Repository navigation
Conversation
…le they wait Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — ACP extension handlers can now wait for user answers without freezing other session traffic. Protocol-scoped lifetimes and retained termination guards keep the change bounded, with no changes to permissions or product defaults. You can add or adjust custom eligibility rules. Learn more. |
Findings
|
| Layer / File(s) | Summary |
|---|---|
Normalize protocol termination packages/effect-acp/src/protocol.ts, packages/effect-acp/src/protocol.test.ts |
A shared helper normalizes causes for termination, and the reader checks that the protocol is active before routing messages. Tests check termination after an incoming-request observer fails and confirm that a stdout write failure stops processing later requests. |
Run extension handlers in protocol scope packages/effect-acp/src/protocol.ts, packages/effect-acp/src/protocol.test.ts |
The protocol captures its scope and forks extension-request handlers in that scope. Parse errors remain request errors, interrupt-only causes are ignored, and other failures terminate the protocol. Tests check the response and confirm the reader processes a session update while a handler waits. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant ACP transport
participant Protocol reader
participant Extension request handler
participant Session update observer
ACP transport->>Protocol reader: Send extension request
Protocol reader->>Extension request handler: Fork handler in protocol scope
ACP transport->>Protocol reader: Send session update
Protocol reader->>Session update observer: Process session update
Extension request handler-->>ACP transport: Emit extension response
Possibly related issues
pingdotgg/t3code#28697: Extension handlers run separately from the reader, so the reader can process incoming messages while a handler waits. The PR does not modify the Codex package.
Suggested reviewers: hieu-lee
Merge Risk: 
Low · up to 087b1
-
A request may run after the connection fails without receiving a response. The risk is limited to this timing race.
Pre-merge checks: 
4
-
Full details: Description check
-
Explanation
The description includes complete Problem, Change, Scope and approval, and Verification sections. It explains the failure mode, implementation, scope rationale, focused tests, broader checks, observed results, and the untested live-session scenario.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Usage-based review receipt
- Mode: Continue automatically
- Reviewed files: 2
- Charged: $0.50
- View usage details
Note
This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 4879f19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 087b1d7
Problem
When Grok asks a question (
x.ai/ask_user_question), T3 stops reading anything else Grok sends until the user answers. The ACP protocol reader handled extension requests inline and waited for the handler to finish before reading the next line. Grok's question handler finishes only when the user replies.While the question is open, later
session/updatenotifications sit unread. So do responses to T3's own requests and core requests such as permission prompts or terminal and file calls from parallel or background work. The thread looks frozen, and that work stalls until the question is answered.Core client requests (
session/request_permission,fs/*,terminal/*) don't have this problem because they go through the RpcServer queue and run concurrently.Change
packages/effect-acp/src/protocol.tsnow forks the extension request chain into the protocol's scope withstartImmediately: true. The reader keeps going while a handler waits.onIncomingRequeststill completes on the reader before the next line is read, as on the core-request path, and only the handler chain is forked. This follows the Codex app-server sibling (effect-codex-app-serverhandleRequest), which forks request handlers and routes failures tohandleTermination.Before this change, any failure or defect in that chain ended the reader, and the reader then terminated the connection. That behavior stays: the reader's cause-to-
AcpErrornormalization moves into a smallterminateWithCausehelper, and both the reader and the forked chain use it. Plain scope interruption is ignored. A forked chain that terminates the connection does not fail the reader, so the reader checks the protocol's existingensureActivetermination guard before routing each message. After termination it stops reading instead of runningonIncomingRequestor queuing later requests (CodeRabbit finding).Scope and approval
Very small, focused fix for an obvious bug, so no prior issue: an extension request handler that waits (as Grok's question handler does by design) should not stop the connection from reading. About 20 non-test lines changed in one file.
Overlap search: the local PR index and
gh search("extension request" acp, ask_user_question, grok question stuck) turned up nothing that touches this. The closest:Verification
keeps reading while an extension request handler waits(packages/effect-acp/src/protocol.test.ts). On main it fails withError: Test timed out in 5000ms.because the latersession/updateis never delivered. It passes with the fix.terminates when a callback on the reader diestest with an extension request whoseonIncomingRequestdies. With a typed-only catch it fails (Test timed out in 5000ms.); with this change it passes. A third case uses a stdout writer that always fails, so extension request 9 terminates the connection, then sends request 10. Without the reader guard it fails withexpected '$t3:jsonrpc:number:10' to equal 'stopped'because the observer still sees request 10. With the guard, the reader stops and the observer never sees it.vp test runeffect-acp (all 6 files): 70 passed. provider-grok: 83 passed. provider-acp: 92 passed. apps/serverGrokAdapterV2.test.ts: 10 passed.AntigravityTextGeneration.test.ts: 32 passed.CI=true vp run check:changed: all pass (format, lint, knip, typecheck of 14 packages, tests).Reviewed with sol-loop: 5 rounds with GPT-6.1 Sol on high. Sol verdict: approved. Round 1 found that defects in the forked chain no longer terminated the connection, which is fixed above. Round 3 approved moving
onIncomingRequestout of the fork (CodeRabbit finding). Round 4 found that the termination guard first evaluated routing eagerly, so a synchronousonIncomingRequeststill ran after termination. That is now fixed, and round 5 approved it.Created with Claude Opus 5.5 in Claude Code (T3 Code).
🤖 Generated with Claude Code