Detailed reference for tool-call dispatch, response submission, retries, and fallback handling.
Use with remote-skills.md when you need reliable tool-call completion behavior.
Primary realtime path:
respond_to_signal(signal)forapp_server.request.item.tool.call
Polling fallback path:
drain_pending_calls()respond_to_pending_call(call)
Both paths execute registered Python handlers and submit responses through codex-manager API.
dispatch_tool_call(...) produces a RemoteSkillDispatch record containing:
handled- normalized
tool argumentsrequest_idand optionalcall_idresponse_payload- optional
error
Missing skill names produce deterministic failure payloads (success: false).
Submission API:
POST /api/tool-calls/:requestId/response
SDK wrapper:
client.tool_calls.respond(request_id=..., response=...)
Submission classification handles server outcomes (accepted, retryable, idempotent, terminal error).
Malformed response payloads (including dict payloads missing status) are treated as retryable submission failures, not accepted outcomes.
Default submission retry behavior:
max_submit_attempts=3retry_delay_seconds=0.05(linear per-attempt)
You can override both per call:
skills.respond_to_signal(signal, max_submit_attempts=5, retry_delay_seconds=0.1)The registry tracks locally handled request ids.
Duplicate paths are tagged as idempotent outcomes:
local_duplicate- server-level
404 not_found/409 in_flightcan also be classified idempotently
This prevents repeated side effects when listeners reconnect or receive duplicate work windows. The cache is bounded and trimmed while preserving immediate duplicate protection for the most recently handled request id.
Each session registry enforces one dispatch mode at a time:
- signal mode (
respond_to_signal(...)) - polling mode (
drain_pending_calls()/respond_to_pending_call(...))
Mixing modes on the same session handle raises a dispatch-mode conflict until reset_dispatch_mode() is called.
No-op paths (ignored non-tool signals or polling with no actionable rows) remain mode-neutral.
matches_signal(signal) and pending-call session extraction prevent cross-session accidental dispatch.
Guideline:
- prefer the session-scoped handle returned by
remote_skills.create_session(...)/remote_skills.lifecycle(...) - register handler on global stream, but dispatch only through session-scoped
skillsobject
Reliable pattern:
- run websocket handler with
respond_to_signal(...) - run periodic fallback task calling
drain_pending_calls() - rely on idempotency classification to avoid duplicate mutations
This handles websocket lag/disconnect windows without overcomplicating end-user code.
Treat failures in three classes:
- handler execution failures (bad arguments, runtime exceptions)
- submission transport failures (HTTP exceptions/timeouts)
- terminal server outcomes (malformed request id, policy rejection)
Record submission_status, submission_code, and submission_attempts for diagnostics.
- Remote skills overview:
remote-skills.md - Lifecycle and catalog controls:
remote-skills-lifecycle-and-catalog.md - Streaming reliability patterns:
streaming-reliability-patterns.md