fix(client): restore running state on reconnect + keyboard interrupt#353
Conversation
…nterrupt On v2 reconnect, the protocol parser only dispatched SET_RUNNING for the false case, silently dropping running=true. After a WS reconnect (common on iOS backgrounding), the UI showed the idle send button instead of interrupt/queue/stop — so user messages queued via sendToChat() with no stopTask cascade, leaving subagents stuck indefinitely. Additionally, the iOS keyboard Send button fires Enter → handleKeyDown → handleSend(), bypassing the on-screen interrupt/queue/stop buttons entirely. Now handleKeyDown routes through handleInterrupt() when the session is running. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Centaur ReviewFound 2 issue(s).
|
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 2 issue(s).
packages/client/__tests__/protocol-parser.test.ts
Clean, well-tested fix. Restores running state on reconnect and routes Enter key to interrupt — both changes are correct with good test coverage including edge cases.
- 🔵 missing_tests: No test for the case where currentSessionId is set but the session is not found in the reconnected sessions list (active === undefined). The .find() returns undefined and the if(active) guard skips SET_RUNNING — this is correct behavior but worth a test to document the contract.
[fixable]
packages/client/src/protocol-parser.ts
Clean, well-tested fix. Restores running state on reconnect and routes Enter key to interrupt — both changes are correct with good test coverage including edge cases.
- 🔵 style (L144): setWsRunning is only called when active.running is true, not when false — asymmetric with other message handlers (e.g. reattach_failed calls setWsRunning(false)). Since setWsRunning is deprecated and SET_RUNNING handles both directions, this is functionally fine, but could be simplified to
callbacks.setWsRunning?.(poolKey, active.running)for symmetry. Low priority given the deprecation.[fixable]
| result.messagesActions.push({ type: 'SET_RUNNING', running: false }); | ||
| if (active) { | ||
| result.messagesActions.push({ type: 'SET_RUNNING', running: active.running }); | ||
| if (active.running) callbacks.setWsRunning?.(poolKey, true); |
There was a problem hiding this comment.
🔵 style: setWsRunning is only called when active.running is true, not when false — asymmetric with other message handlers (e.g. reattach_failed calls setWsRunning(false)). Since setWsRunning is deprecated and SET_RUNNING handles both directions, this is functionally fine, but could be simplified to callbacks.setWsRunning?.(poolKey, active.running) for symmetry. Low priority given the deprecation. [fixable]
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
LGTM — no issues found.
Summary
protocol-parser.tsonly dispatchedSET_RUNNINGforrunning === false, silently dropping thetruecase. After iOS WS reconnect, the UI showed idle send button instead of interrupt/queue/stop — so messages queued viasendToChat()with nostopTask()cascade, leaving subagents stuck indefinitely.handleSend(), bypassing the on-screen interrupt/queue/stop buttons. NowhandleKeyDownroutes throughhandleInterrupt()when the session is running.Test plan
protocol-parser.test.tsupdated —reconnectedwithrunning: truenow assertsSET_RUNNING: true+setWsRunningcalled🤖 Generated with Claude Code