Repository navigation
report tool calls while their arguments stream (LSP progress and ACP preview) #53
Description
`
Summary
In agent mode, a client learns nothing about a tool call until the model has finished generating all of its arguments. For file-writing tools this means a long silence. A large create_file (about 12.5k characters) leaves the client idle for 30–40 s, with no indication that a file is being written or which one. The server already receives the arguments in pieces from the model; it just doesn't forward them. We're asking for an opt-in way to receive them, in LSP mode and in the ACP preview.
Environment
@github/copilot-language-server1.551.2 (latest on npm), macOS arm64- LSP mode (
--stdio):conversation/createwithchatMode: "Agent", client tools registered withconversation/registerTools - Model: Claude Sonnet 5.5 (similar behaviour with GPT models)
What we observed (LSP mode)
We logged every JSON-RPC message in both directions, with millisecond timestamps, for a prompt that writes one large index.html through a client create_file tool:
| Time | Message |
|---|---|
| +0.000 s | → conversation/create |
| +0.686 s | ← $/progress begin |
| +2.533 s | ← window/logMessage "[fetchChat] … finished with 200 status" (model response started) |
| +2.534 s → +39.226 s | no messages of any kind, in either direction (36.7 s) |
| +39.227 s | ← $/progress report: editAgentRounds[0].toolCalls[0] = { name: "create_file", status: "running", input: { filePath, content: <all 12,505 chars> } }. This is the first message naming the tool or the file |
| +39.228 s | ← conversation/invokeClientTool with the same complete input |
Reply text does stream live in the same turn. Only the tool-call arguments are held back.
Why (from reading the published dist/main.js)
- The stream handlers for the Messages, Responses and Chat Completions APIs already produce
beginToolCalls(name and id, atcontent_block_start/output_item.added) andcopilotToolCallStreamUpdates(the arguments so far, on eachinput_json_delta/function_call_arguments.delta). - The only consumer of these fields appears to be
FetchStreamRecorder(timing). The agent loop's stream callback (ConversationFinishCallback.isFinishedAfter) forwards text, annotations, references, errors and thinking, but not tool-call starts or argument deltas. AgentToolCalls.initsets the status"not started", but that status is never reported. The first status a client sees is"running", with the arguments already complete.- We found no
copilotCapabilitiesflag, initialization option or feature flag that changes this.
ACP (preview)
- Through the server's
--acprelay to the Copilot CLI (1.0.93, observed with a scripted model stand-in): atool_callwithstatus: "pending"arrives about 7 ms after the model finishes the arguments. It carries the title ("Creating …/index.html") and the fullrawInput. During the 18.6 s of argument streaming, nosession/updatewas sent. - The internal ACP pipeline (
GITHUB_COPILOT_ACP_USE_CLI=0) builds itstool_callfrom the same progress reports, so we expect the same timing. We read this from the code but couldn't run it live. - The Copilot CLI's own SDK stream already exposes this (
assistant.tool_call_deltawithtoolCallId,toolName,inputDelta), so the data and the event shape exist. Since ACP is in preview, this looks like a good time to carry it through ACP too.
Request (opt-in, no change for existing clients)
For example initializationOptions.copilotCapabilities.toolCallStreaming: true:
- Tool-call start. When the model starts a tool call, report it right away:
- LSP: an
editAgentRounds[].toolCalls[]entry withid,nameandstatus: "not started"(or a new"streaming"). - ACP: a
tool_callwithstatus: "pending"and the tool's title.
- LSP: an
- Argument progress. Send throttled updates (for example every 100–250 ms) carrying the arguments accumulated so far, as raw partial JSON:
- LSP: a
partialInputfield. - ACP: a
tool_call_updatewithrawInput. That lets clients show the file path as soon as it appears, and the content growing.
- LSP: a
- Same id throughout, so the call carries on into
running/conversation/invokeClientTooland thencompleted.
Why it matters
Users see an idle spinner for the entire time a large file is being written, and it looks like the agent is stuck. Other agent hosts show "Creating <file>" and a growing line count during this phase. This change would let LSP and ACP clients do the same, with data the server already has.
`
Activity