→ {"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":false,"auth":{"terminal":false}},"clientInfo":{"name":"codex","title":"Codex CLI","version":"0.0.0"}},"id":"d48faf1b-4977-455b-a03a-946eac6cb430"}
← {"jsonrpc":"2.0","id":"d48faf1b-4977-455b-a03a-946eac6cb430","result":{"protocolVersion":1,"agentCapabilities":{"_meta":{"claudeCode":{"promptQueueing":true}},"promptCapabilities":{"image":true,"embeddedContext":true},"mcpCapabilities":{"http":true,"sse":true},"loadSession":true,"sessionCapabilities":{"fork":{},"list":{},"resume":{},"close":{}}},"agentInfo":{"name":"@zed-industries/claude-agent-acp","title":"Claude Agent","version":"0.23.1"},"authMethods":[]}}
→ {"jsonrpc":"2.0","method":"session/new","params":{"cwd":"/home/clifford/Documents/source/nori/cli/.worktrees/plan-session-update-support","mcpServers":[]},"id":"4a20bf17-08e7-459b-a393-c3499020022a"}
← {"jsonrpc":"2.0","id":"4a20bf17-08e7-459b-a393-c3499020022a","result":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","models":{"availableModels":[{"modelId":"default","name":"Default (recommended)","description":"Opus 4.6 with 1M context · Most capable for complex work"},{"modelId":"sonnet","name":"Sonnet","description":"Sonnet 4.6 · Best for everyday tasks"},{"modelId":"sonnet[1m]","name":"Sonnet (1M context)","description":"Sonnet 4.6 with 1M context · Billed as extra usage · $3/$15 per Mtok"},{"modelId":"haiku","name":"Haiku","description":"Haiku 4.5 · Fastest for quick answers"}],"currentModelId":"default"},"modes":{"currentModeId":"default","availableModes":[{"id":"default","name":"Default","description":"Standard behavior, prompts for dangerous operations"},{"id":"acceptEdits","name":"Accept Edits","description":"Auto-accept file edit operations"},{"id":"plan","name":"Plan Mode","description":"Planning mode, no actual tool execution"},{"id":"dontAsk","name":"Don't Ask","description":"Don't prompt for permissions, deny if not pre-approved"},{"id":"bypassPermissions","name":"Bypass Permissions","description":"Bypass all permission checks"}]},"configOptions":[{"id":"mode","name":"Mode","description":"Session permission mode","category":"mode","type":"select","currentValue":"default","options":[{"value":"default","name":"Default","description":"Standard behavior, prompts for dangerous operations"},{"value":"acceptEdits","name":"Accept Edits","description":"Auto-accept file edit operations"},{"value":"plan","name":"Plan Mode","description":"Planning mode, no actual tool execution"},{"value":"dontAsk","name":"Don't Ask","description":"Don't prompt for permissions, deny if not pre-approved"},{"value":"bypassPermissions","name":"Bypass Permissions","description":"Bypass all permission checks"}]},{"id":"model","name":"Model","description":"AI model to use","category":"model","type":"select","currentValue":"default","options":[{"value":"default","name":"Default (recommended)","description":"Opus 4.6 with 1M context · Most capable for complex work"},{"value":"sonnet","name":"Sonnet","description":"Sonnet 4.6 · Best for everyday tasks"},{"value":"sonnet[1m]","name":"Sonnet (1M context)","description":"Sonnet 4.6 with 1M context · Billed as extra usage · $3/$15 per Mtok"},{"value":"haiku","name":"Haiku","description":"Haiku 4.5 · Fastest for quick answers"}]}]}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"available_commands_update","availableCommands":[]}}}
→ {"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":false,"auth":{"terminal":false}},"clientInfo":{"name":"codex","title":"Codex CLI","version":"0.0.0"}},"id":"963d59ae-5561-488a-b1a4-6e0fcfb77905"}
→ {"jsonrpc":"2.0","method":"session/prompt","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","prompt":[{"type":"text","text":"I'm testing something. Just run a foreground sleep 30 task, then say 'all done!'"}]},"id":"c08f997e-8794-4e83-aa41-4c5ae3d3be94"}
← {"jsonrpc":"2.0","id":"963d59ae-5561-488a-b1a4-6e0fcfb77905","result":{"protocolVersion":1,"agentCapabilities":{"_meta":{"claudeCode":{"promptQueueing":true}},"promptCapabilities":{"image":true,"embeddedContext":true},"mcpCapabilities":{"http":true,"sse":true},"loadSession":true,"sessionCapabilities":{"fork":{},"list":{},"resume":{},"close":{}}},"agentInfo":{"name":"@zed-industries/claude-agent-acp","title":"Claude Agent","version":"0.23.1"},"authMethods":[]}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":""}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The user wants me to run a `sleep 30` command in the foreground and then say \"all done!"}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\"."}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"toolCallId":"toolu_01TbtohBfxwZB1ZdC5XzaitP","sessionUpdate":"tool_call","rawInput":{},"status":"pending","title":"Terminal","kind":"execute","content":[]}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"_meta":{"claudeCode":{"toolName":"Bash"}},"toolCallId":"toolu_01TbtohBfxwZB1ZdC5XzaitP","sessionUpdate":"tool_call_update","rawInput":{"command":"sleep 30","description":"Sleep for 30 seconds"},"title":"sleep 30","kind":"execute","content":[{"type":"content","content":{"type":"text","text":"Sleep for 30 seconds"}}]}}}
→ {"jsonrpc":"2.0","method":"session/cancel","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf"}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"usage_update","used":24422,"size":1000000,"cost":{"amount":0.15535875,"currency":"USD"}}}}
← {"jsonrpc":"2.0","id":"c08f997e-8794-4e83-aa41-4c5ae3d3be94","result":{"stopReason":"cancelled"}}
→ {"jsonrpc":"2.0","method":"session/prompt","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","prompt":[{"type":"text","text":"what have you finished?"}]},"id":"daa0362c-edd8-47b7-ab22-091bc10a2040"}
← {"jsonrpc":"2.0","id":"daa0362c-edd8-47b7-ab22-091bc10a2040","result":{"stopReason":"end_turn","usage":{"inputTokens":0,"outputTokens":0,"cachedReadTokens":0,"cachedWriteTokens":0,"totalTokens":0}}}
→ {"jsonrpc":"2.0","method":"session/prompt","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","prompt":[{"type":"text","text":"."}]},"id":"39d50f73-65bf-4eee-9bc9-0d05527b376e"}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":""}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"Nothing"}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":" — I hadn't started any work yet. You asked me to run"}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":" `sleep 30`, but the command was rejected before it executed"}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"."}}}}
← {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"9cbea40d-85a1-4b71-8fc9-584be559bbbf","update":{"sessionUpdate":"usage_update","used":24602,"size":1000000,"cost":{"amount":0.169686,"currency":"USD"}}}}
← {"jsonrpc":"2.0","id":"39d50f73-65bf-4eee-9bc9-0d05527b376e","result":{"stopReason":"end_turn","usage":{"inputTokens":3,"outputTokens":32,"cachedReadTokens":24387,"cachedWriteTokens":211,"totalTokens":24633}}}
(Not written by AI)
I created some edge-case logic myself, to handle this bug (behavior?) in my own client: tilework-tech/nori-cli#442
I'm not totally certain if this is a bug or not, but from my best read of the ACP docs it looks like technically permissible? It never explicitly says the agent should send only one stop reason, but it does seem to imply that.
https://agentclientprotocol.com/protocol/prompt-turn#cancellation
However with the Claude ACP adapter specifically, any cancelled prompt turn I'm seeing will always send a second stop reason when it's prompted again.
Exact repro ACP wire during cancellation:
Should both of these stop reasons actually come through from the agent? I would have thought just the first one.
Full ACP Wire
However, this issue doesn't repro with Zed. Not sure if it actually is intended behavior somehow, or maybe Zed just has the same edge case fix that I made in Nori.