Skip to content

Comments

fix(cli): fix YOLO mode message handling and sync wedge#702

Open
seibe wants to merge 3 commits intoslopus:mainfrom
seibe:fix/yolo-mode
Open

fix(cli): fix YOLO mode message handling and sync wedge#702
seibe wants to merge 3 commits intoslopus:mainfrom
seibe:fix/yolo-mode

Conversation

@seibe
Copy link

@seibe seibe commented Feb 21, 2026

Summary

Fixes a sync wedge (deadlock) between the CLI and App that occurred specifically in YOLO (bypass permissions) mode. Three interrelated issues caused messages to stop syncing.

Related issues

Addresses: #521, #446, #29, #206 — YOLO mode not working, permissions still being asked.
Related: #648 — YOLO preference not persisted (addressed separately by #689).

Comparison with similar PRs

Root causes addressed

  1. AskUserQuestion auto-approved in YOLO mode: Claude could call AskUserQuestion while in bypass-permissions mode. The tool was silently auto-approved, but no real user existed to answer, causing the App to hang waiting for input that would never come. Fix: disallow AskUserQuestion in the SDK's disallowedTools list when in YOLO mode, and add a guard in permissionHandler.ts to exclude it from auto-approval.

  2. Messages not forwarded until iterator advanced: opts.onMessage() was called inside the for await loop over Claude's output, but the loop could be blocked waiting for nextMessage() (user input). In YOLO auto-continuation mode, this meant assistant messages from completed turns were not synced to the App until the next turn started. Fix: introduce an onMessageReceived callback that fires immediately when messages are parsed from stdout, decoupled from the iterator consumer.

  3. Race condition in outgoing message queue: Tool-call release and next-message enqueue were separate operations under separate lock acquisitions. A released delayed message could be sent before the new message was enqueued, causing ordering issues. Fix: consolidate releaseToolCallIds into the enqueue() call for atomic execution within a single lock.

  4. Turn-end sent before messages flushed: The onReady callback signaled turn completion before the outgoing message queue was fully flushed. Fix: make onReady async and await messageQueue.flush() before closing the turn.

  5. Outbox batch overflow: flushOutbox sent all pending messages in a single request. During fast YOLO auto-continuations, the outbox could accumulate more messages than practical for a single POST. Fix: drain the outbox in a loop with MAX_BATCH_SIZE = 100.

Changes by file

  • claudeRemote.ts: Add AskUserQuestion to disallowedTools in bypass mode; introduce onMessageReceived for immediate forwarding; widen onReady to async
  • claudeRemoteLauncher.ts: Atomic tool-call release via releaseToolCallIds on enqueue(); async onReady with queue flush
  • query.ts: New onMessageReceived callback on Query class, fired during stdout parsing
  • OutgoingMessageQueue.ts: Add releaseToolCallIds option to enqueue() for atomic release-and-enqueue
  • permissionHandler.ts: Exclude AskUserQuestion from bypass auto-approval
  • apiSession.ts: Drain outbox in batched loop with MAX_BATCH_SIZE = 100

Test plan

  • Start a session in YOLO mode → Claude does not attempt AskUserQuestion
  • YOLO mode auto-continuation → messages appear in App in real-time without delay
  • Multi-turn YOLO session → no orphan turns, messages arrive in correct order
  • Large batch of messages in quick succession → outbox drains completely

… auto-approval

Three interconnected bugs caused the App to permanently stop showing CLI messages
when AskUserQuestion was auto-approved in Yolo (bypassPermissions) mode:

A) Exclude AskUserQuestion from bypassPermissions so the App can present choices
   to the user instead of silently auto-selecting the first option
B) Flush OutgoingMessageQueue before closing turn to prevent turn-end from
   arriving at the App before delayed tool call messages (orphan turns)
C) Make tool call release atomic with enqueue to prevent head-of-line blocking
   race conditions in the message queue
- Add MAX_BATCH_SIZE (100) to flushOutbox to prevent oversized POST requests
- Disallow AskUserQuestion tool when bypassPermissions mode is active
…ived

In YOLO mode, the for-await loop blocks at nextMessage() after result
messages while Claude auto-continues. Messages accumulated in the stream
buffer but were never forwarded to the app. Added onMessageReceived
callback that fires as soon as messages are read from stdout, bypassing
the blocked iterator.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant