Repository navigation
Description
Describe the bug
In ACP mode (copilot --acp), when the agent starts a shell with the bash tool in mode: "async", the session/prompt response is withheld until that shell exits, even though the agent's foreground work finished long before. If the async shell is a dev server or another long-running process, the prompt never completes. ACP clients have no other end-of-turn signal on protocol v1, so they show the session as busy indefinitely.
The same prompt with detach: true (bash args {"mode":"async","detach":true}) behaves as expected: session/prompt returns end_turn as soon as the foreground work ends, and the later shell-completion wake (system.notification → read_bash → new turn) still happens.
This makes ACP clients look stuck. It was reported downstream in T3 Code: pingdotgg/t3code#27301
Affected version
1.0.105
Steps to reproduce the behavior
- Start
copilot --acpand sendinitialize(protocolVersion 1; optionally subscribe to raw events withclientCapabilities._meta["github.com/copilot"].events = ["assistant.turn_end", "session.usage_checkpoint", "system.notification"]). session/new, thensession/promptwith: Use the bash tool with mode "async" (NOT detached) to runsleep 25, do not wait or read its output, then immediately reply with just the word ok. Do nothing else. Allow the permission request.- Note when the
session/promptresponse arrives. - Repeat asking for
detach: truefor comparison, and check the bash arguments in~/.copilot/session-state/<sessionId>/events.jsonl: the bug needs{"mode":"async","detach":false}or nodetachfield.
Observed (seconds from start):
async, not detached ({"mode":"async"}):
3.22 tool_call bash "sleep 25"
3.94 sessionEvent assistant.turn_end
3.94 sessionEvent session.usage_checkpoint <- foreground work done, "ok" already sent
28.27 sessionEvent system.notification <- shell completed
28.27 tool_call read_bash
29.41 sessionEvent assistant.turn_end
29.42 session/prompt response {"stopReason":"end_turn"} <- held ~25 s
async + detach ({"mode":"async","detach":true}):
4.18 tool_call bash "sleep 25"
5.17 sessionEvent assistant.turn_end
5.18 sessionEvent session.usage_checkpoint
5.18 session/prompt response {"stopReason":"end_turn"} <- immediate
29.87 sessionEvent system.notification <- wake still delivered
In a real session the agent started a local dev server with mode: "async". The client showed the session as busy for about 10 minutes after the agent finished. The response to the next prompt came 0.5 s after I killed the dev server. Sending session/cancel also releases the held prompt without killing the async shell.
Expected behavior
session/prompt should resolve with end_turn when the agent's foreground work ends, as it does for detached shells, even if an async shell started in that prompt is still running. If async shells must keep the prompt open by design, please provide a documented signal that foreground work has ended, such as ACP state_update idle (protocol v2) or a documented sessionEvent, so clients can show the session as idle with background work running.
Additional context
- macOS 27.0.1, arm64
- Client: ACP over stdio (T3 Code; also reproduced with a minimal standalone JSON-RPC client)
- Models: claude-sonnet-5.5 (repro), gpt-6.1-sol (original session)
Also reproduced inside T3 Code with {"command":"sleep 90","mode":"async","detach":false}: the agent replied "ok" and emitted session.usage_checkpoint 4.3 s after the prompt, but the session/prompt response arrived 100 s after the prompt, only after the shell exited and the agent ran a second internal turn.
Activity
devm33 commentedon Nov 3, 2026
Confirmed on CLI 1.0.106-6: an attached async shell keeps
session/promptpending until it exits. There is already a foreground-completion signal available through the opt-in event extension:assistant.idlearrives while that shell is still running.To track foreground activity separately from the outstanding prompt request, subscribe during initialization:
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": 1, "clientCapabilities": { "_meta": { "github.com/copilot": { "events": ["assistant.turn_start", "assistant.idle", "session.idle"] } } } } }These arrive as
github.com/copilot/sessionEventnotifications, with the event type inparams.typeand the session inparams.sessionId. Ignore events carryingparams.agentIdwhen updating the parent agent's foreground indicator.assistant.turn_start: foreground activity is starting/resuming.assistant.idle: foreground work has settled; attached background work may remain and the prompt response may still be pending.session.idle: the whole session is quiescent, including attached work.assistant.idleis not a task-completion guarantee: background notifications or Autopilot can start further work.assistant.turn_endonly ends an individual model turn, andsession.usage_checkpointis optional accounting data, so neither substitutes forassistant.idle.The CLI help documentation for this contract is currently in review. Leaving this issue open until that change is merged.
Generated by Copilot