`collab_tool_call` "spawn_agent" item missing from `exec --experimental-json` stream; "wait" item's `receiver_thread_ids` stays empty despite a real, successful delegation underneath

Open 💬 1 comment Opened Jul 23, 2026 by tic-top

What version of Codex CLI is running?

0.144.5 (installed via @openai/codex-sdk / @openai/codex npm packages)

Which model were you using?

gpt-5.6-sol

What platform is your computer?

Linux (Ubuntu 20.04 focal), invoked via @openai/codex-sdk's Codex.startThread().runStreamed(), which shells out to codex exec --experimental-json (see node_modules/@openai/codex-sdk/src/exec.ts).

What happened?

Using native subagent support ([features] multi_agent = true, a custom
agent defined at <CODEX_HOME>/agents/<name>.toml), I asked the root agent
to spawn a named custom agent and relay its exact reply. The
--experimental-json event stream only ever shows:

{"type":"item.started","item":{"id":"item_1","type":"collab_tool_call","tool":"wait","sender_thread_id":"...","receiver_thread_ids":[],"prompt":null,"agents_states":{},"status":"in_progress"}}
{"type":"item.completed","item":{"id":"item_1","type":"collab_tool_call","tool":"wait","sender_thread_id":"...","receiver_thread_ids":[],"prompt":null,"agents_states":{},"status":"completed"}}
{"type":"item.completed","item":{"id":"item_2","type":"agent_message","text":"SPECIALIST SAID: PONG"}}

Note: no collab_tool_call with tool: "spawn_agent" (or similar) ever
appears in this stream, and the wait item's receiver_thread_ids is
always [], even though the final agent_message content is correct.

This reproduced identically across 6 separate runs, with and without
[features.multi_agent_v2].

Reading the FULL session rollout (~/.codex/sessions/**/*.jsonl) for the
same turn tells a different, complete story — it contains the actual
function_call/function_call_output pair for the spawn, a
sub_agent_activity event ("kind": "started"), the child thread's own
rollout file (session_meta subagent.thread_spawn with the correct
agent_path/agent_nickname), and the real agent_message reply routed
back from the child. state_5.sqlite's thread_spawn_edges table also
correctly records the parent→child edge for every one of the 6 runs.

So: delegation genuinely works end-to-end. The bug is that the
--experimental-json stream — the one @openai/codex-sdk consumes and
exposes to SDK users — is missing the spawn_agent collab_tool_call event
entirely, and the wait item it does emit never reflects the real
receiver thread. This is different from #28318 (missing TypeScript type for
collab_tool_call — that reporter's runtime JSON did include a populated
spawn_agent item with real receiver_thread_ids); here the data itself
is missing from this event source, not just its type.

Expected behavior

exec --experimental-json (and therefore @openai/codex-sdk's public
event stream) should include the spawn_agent collab_tool_call item, and
wait's receiver_thread_ids should reflect the actual spawned child
thread(s) — matching what the full session rollout already records
correctly.

Why this matters

Any SDK consumer trying to observe or verify subagent delegation
(logging, tracing, debugging, deciding whether to trust a delegated
result) via the public event stream currently gets a false negative —
delegation looks like a silent no-op even when it succeeded. We only
caught this by cross-referencing the full rollout JSONL and
state_5.sqlite directly, which isn't part of the SDK's public surface.

View original on GitHub ↗

1 Comment

drewmt · 1 month ago

Confirmed on codex-cli 0.145.0-alpha.18 with codex exec --json using one v2 spawn, one wait, and an exact PONG-34919 relay. The public JSONL still emitted only a wait item with receiver_thread_ids: []; the matching rollout contained the spawn_agent function call, a sub_agent_activity started event with the child thread ID, and the child reply. The turn context confirmed multi_agent_version: "v2".

On current main (99744cf), this appears to be the v2 event-shape boundary rather than the JSON serializer itself:

A focused regression would ideally exercise the v2 spawn/wait flow through the exec/app-server boundary and assert that the public JSONL exposes the spawned child ID, rather than only testing the synthetic CollabAgentToolCall mapper. Happy to prepare that patch if the maintainers would like an external PR.