Summary
When a subagent (runtime=subagent) calls sessions_spawn, the model routinely includes streamTo: "parent" in the arguments. The server correctly rejects this with:
streamTo is only supported for runtime=acp; got runtime=subagent
The model then retries without streamTo and the second call succeeds. Every subagent-to-specialist delegation costs ~2× tokens (one failed spawn + one successful).
Repro
Any depth-1 subagent that delegates to another agent via sessions_spawn with runtime="subagent" exhibits this. Observed consistently across 20+ sessions in local testing with [email protected] on gpt-5.4.
Sample toolCall args the model emits on the first attempt:
{
"task": "…",
"runtime": "subagent",
"agentId": "ios_agent",
"mode": "run",
"cleanup": "delete",
"sandbox": "inherit",
"streamTo": "parent",
"lightContext": true
}
Tool result:
{"status":"error","error":"streamTo is only supported for runtime=acp; got runtime=subagent"}
The model then re-emits the same args minus streamTo and succeeds.
Root cause
In SessionsSpawnToolSchema (compiled at dist/openclaw-tools-*.js around the src/agents/tools/sessions-spawn-tool.ts region), streamTo is declared without a description:
streamTo: optionalStringEnum(SESSIONS_SPAWN_ACP_STREAM_TARGETS),
Compare to its siblings in the same schema, which both carry runtime hints:
resumeSessionId → "…Requires runtime=\"acp\"."
lightContext → "Only applies to runtime='subagent'."
describeSessionsSpawnTool() in tool-policy also never mentions streamTo. So from the model's vantage point it's an enum-valued "parent" field with no constraint hint — it reasonably includes it whenever streaming semantics feel relevant, even on subagent spawns.
The server-side rejection is correct (subagent spawns are fire-and-forget with push-based auto-announce; there is no bidirectional stream channel). The bug is purely that the schema fails to communicate the constraint, causing a predictable, paid-for retry on every delegation.
Proposed fix
One-line change in src/agents/tools/sessions-spawn-tool.ts:
streamTo: optionalStringEnum(SESSIONS_SPAWN_ACP_STREAM_TARGETS, {
description: "Stream child output to parent turn-by-turn. Requires runtime=\"acp\"; omit for runtime=\"subagent\" (subagents use push-based auto-announce instead)."
}),
Optional: append a sentence to describeSessionsSpawnTool() mirroring the same constraint, and add a unit test asserting the streamTo field description contains runtime="acp" as a regression guard.
Happy to open a PR if you'd like.
Summary
When a subagent (runtime=
subagent) callssessions_spawn, the model routinely includesstreamTo: "parent"in the arguments. The server correctly rejects this with:The model then retries without
streamToand the second call succeeds. Every subagent-to-specialist delegation costs ~2× tokens (one failed spawn + one successful).Repro
Any depth-1 subagent that delegates to another agent via
sessions_spawnwithruntime="subagent"exhibits this. Observed consistently across 20+ sessions in local testing with[email protected]ongpt-5.4.Sample toolCall args the model emits on the first attempt:
{ "task": "…", "runtime": "subagent", "agentId": "ios_agent", "mode": "run", "cleanup": "delete", "sandbox": "inherit", "streamTo": "parent", "lightContext": true }Tool result:
{"status":"error","error":"streamTo is only supported for runtime=acp; got runtime=subagent"}The model then re-emits the same args minus
streamToand succeeds.Root cause
In
SessionsSpawnToolSchema(compiled atdist/openclaw-tools-*.jsaround thesrc/agents/tools/sessions-spawn-tool.tsregion),streamTois declared without a description:Compare to its siblings in the same schema, which both carry runtime hints:
resumeSessionId→"…Requires runtime=\"acp\"."lightContext→"Only applies to runtime='subagent'."describeSessionsSpawnTool()intool-policyalso never mentionsstreamTo. So from the model's vantage point it's an enum-valued"parent"field with no constraint hint — it reasonably includes it whenever streaming semantics feel relevant, even on subagent spawns.The server-side rejection is correct (subagent spawns are fire-and-forget with push-based auto-announce; there is no bidirectional stream channel). The bug is purely that the schema fails to communicate the constraint, causing a predictable, paid-for retry on every delegation.
Proposed fix
One-line change in
src/agents/tools/sessions-spawn-tool.ts:Optional: append a sentence to
describeSessionsSpawnTool()mirroring the same constraint, and add a unit test asserting thestreamTofield description containsruntime="acp"as a regression guard.Happy to open a PR if you'd like.