Summary
There is currently no way for an SDK consumer to receive incremental stdout/stderr from a long-running tool (e.g. Bash) while it is still executing. Tool output is delivered exactly once, as a single complete block, when the command finishes. This makes it impossible to build a responsive UI for long-running commands — device-flow login URLs, build progress, "server ready on :3000" messages, etc. all stay hidden until the process exits.
This was surfaced downstream in the ACP adapter: agentclientprotocol/claude-agent-acp#739. The adapter maintainer confirmed the adapter has nothing to stream because the SDK doesn't expose this:
"If the SDK starts surfacing this information we can show it. but as far as I know, there isn't a way to get this information."
Filing here because the fix has to originate in the SDK.
What I verified (against @anthropic-ai/claude-agent-sdk@0.3.160)
I went through the published sdk.d.ts looking for any surface that exposes in-flight tool output or lets a consumer own tool execution. There isn't one:
CanUseTool / PermissionResult — the allow branch is { behavior: 'allow'; updatedInput?; updatedPermissions? }. It can gate or rewrite input, but has no field to return output and no way to delegate/take over execution. The CLI always runs the tool itself.
PreToolUse hook — same story: permissionDecision + updatedInput + additionalContext. No output, no delegation.
MessageDisplay hook — streams assistant message text line-deltas ("display-only"), never tool stdout.
includePartialMessages / SDKPartialAssistantMessage — partial assistant messages (text/thinking deltas) during generation. The tool_result still arrives as one complete block at completion.
So a consumer can see the model typing, but not the tool running.
Why the existing workaround isn't enough
The only current path is model-driven run_in_background + polling a BashOutput-style tool. That requires the model to choose to background the command, and the output arrives via polling rather than as a stream — it can't be relied on for transparent, real-time tool cards.
Proposed options
Either would unblock downstream clients (ACP/Zed and others):
- Emit incremental tool-output events. A streaming event (gated behind a flag like
includePartialMessages, or a new includePartialToolOutput) carrying { tool_use_id, stdout/stderr delta } as a long-running tool produces output — mirroring how MessageDisplay flushes assistant-text line-deltas today.
- Let a consumer own execution. Extend the permission/tool model so
CanUseTool (or a tool handler) can take over a tool call and stream its own output back — letting clients run the command in a client-owned terminal and stream natively.
Option 1 is the smaller, more general change and maps cleanly onto the adapter's existing terminal_output lifecycle.
Environment
@anthropic-ai/claude-agent-sdk@0.3.160
- Downstream:
@agentclientprotocol/claude-agent-acp@0.40.0, Zed client
Summary
There is currently no way for an SDK consumer to receive incremental stdout/stderr from a long-running tool (e.g.
Bash) while it is still executing. Tool output is delivered exactly once, as a single complete block, when the command finishes. This makes it impossible to build a responsive UI for long-running commands — device-flow login URLs, build progress, "server ready on :3000" messages, etc. all stay hidden until the process exits.This was surfaced downstream in the ACP adapter: agentclientprotocol/claude-agent-acp#739. The adapter maintainer confirmed the adapter has nothing to stream because the SDK doesn't expose this:
Filing here because the fix has to originate in the SDK.
What I verified (against
@anthropic-ai/claude-agent-sdk@0.3.160)I went through the published
sdk.d.tslooking for any surface that exposes in-flight tool output or lets a consumer own tool execution. There isn't one:CanUseTool/PermissionResult— theallowbranch is{ behavior: 'allow'; updatedInput?; updatedPermissions? }. It can gate or rewrite input, but has no field to return output and no way to delegate/take over execution. The CLI always runs the tool itself.PreToolUsehook — same story:permissionDecision+updatedInput+additionalContext. No output, no delegation.MessageDisplayhook — streams assistant message text line-deltas ("display-only"), never tool stdout.includePartialMessages/SDKPartialAssistantMessage— partial assistant messages (text/thinking deltas) during generation. Thetool_resultstill arrives as one complete block at completion.So a consumer can see the model typing, but not the tool running.
Why the existing workaround isn't enough
The only current path is model-driven
run_in_background+ polling aBashOutput-style tool. That requires the model to choose to background the command, and the output arrives via polling rather than as a stream — it can't be relied on for transparent, real-time tool cards.Proposed options
Either would unblock downstream clients (ACP/Zed and others):
includePartialMessages, or a newincludePartialToolOutput) carrying{ tool_use_id, stdout/stderr delta }as a long-running tool produces output — mirroring howMessageDisplayflushes assistant-text line-deltas today.CanUseTool(or a tool handler) can take over a tool call and stream its own output back — letting clients run the command in a client-owned terminal and stream natively.Option 1 is the smaller, more general change and maps cleanly onto the adapter's existing
terminal_outputlifecycle.Environment
@anthropic-ai/claude-agent-sdk@0.3.160@agentclientprotocol/claude-agent-acp@0.40.0, Zed client