Summary
runAgentTool() forwards every child chunk to the parent's clients (_forwardAgentToolStream → _broadcastAgentToolEvent), and Think's child side also routes each chunk to the root. For a headless parent (a Slack agent with no browser attached) running a long child, that is one client-facing broadcast per streamed token with no consumer. There is no way to ask for lifecycle events only.
Seen on agents@0.23.0 with @cloudflare/think@0.18.0.
Measured
One awaited child run (Honeycomb, sample rate 1): child-window broadcasts fell from 9,217 to 17 once the child's intermediate chunks were no longer re-forwarded; the child still persisted its full transcript and the parent still tailed it to completion. Root traffic per chunk is tracked separately in #2252.
Suggestion
An opt-in on runAgentTool:
eventDelivery?: "full" | "terminal"; // default "full"
"terminal" emits only the lifecycle events (started, progress, finished) to clients while the child keeps persisting its complete stream and the parent keeps tailing for completion, cancellation, progress observation and recovery. Detached runs would reject "terminal" (they already have their own completion contract).
What we carry as a local patch, for reference: the option is validated in runAgentTool, persisted in cf_agent_tool_runs.event_delivery (a TEXT NOT NULL DEFAULT 'full' column, schema version bump), passed to the child adapter's startAgentToolRun(input, { runId, eventDelivery }), and honoured by _forwardAgentToolStream / _broadcastAgentToolChunks; cancellation drains the child turn before clearing delivery state, and recovered streams keep increasing sequence numbers so replay does not skip retained chunks. Think's startAgentToolRun gets the matching flag so it stops routing child chunks to the root.
Happy to open a PR if this shape is acceptable.
Summary
runAgentTool()forwards every child chunk to the parent's clients (_forwardAgentToolStream→_broadcastAgentToolEvent), and Think's child side also routes each chunk to the root. For a headless parent (a Slack agent with no browser attached) running a long child, that is one client-facing broadcast per streamed token with no consumer. There is no way to ask for lifecycle events only.Seen on
agents@0.23.0with@cloudflare/think@0.18.0.Measured
One awaited child run (Honeycomb, sample rate 1): child-window broadcasts fell from 9,217 to 17 once the child's intermediate chunks were no longer re-forwarded; the child still persisted its full transcript and the parent still tailed it to completion. Root traffic per chunk is tracked separately in #2252.
Suggestion
An opt-in on
runAgentTool:"terminal"emits only the lifecycle events (started, progress, finished) to clients while the child keeps persisting its complete stream and the parent keeps tailing for completion, cancellation, progress observation and recovery. Detached runs would reject"terminal"(they already have their own completion contract).What we carry as a local patch, for reference: the option is validated in
runAgentTool, persisted incf_agent_tool_runs.event_delivery(aTEXT NOT NULL DEFAULT 'full'column, schema version bump), passed to the child adapter'sstartAgentToolRun(input, { runId, eventDelivery }), and honoured by_forwardAgentToolStream/_broadcastAgentToolChunks; cancellation drains the child turn before clearing delivery state, and recovered streams keep increasing sequence numbers so replay does not skip retained chunks. Think'sstartAgentToolRungets the matching flag so it stops routing child chunks to the root.Happy to open a PR if this shape is acceptable.