Skip to content

fix(client): restore running state on reconnect + keyboard interrupt#353

Merged
dimakis merged 1 commit into
mainfrom
fix/reattach-running-state
May 22, 2026
Merged

fix(client): restore running state on reconnect + keyboard interrupt#353
dimakis merged 1 commit into
mainfrom
fix/reattach-running-state

Conversation

@dimakis
Copy link
Copy Markdown
Owner

@dimakis dimakis commented May 22, 2026

Summary

  • Running state lost on reconnect: protocol-parser.ts only dispatched SET_RUNNING for running === false, silently dropping the true case. After iOS WS reconnect, the UI showed idle send button instead of interrupt/queue/stop — so messages queued via sendToChat() with no stopTask() cascade, leaving subagents stuck indefinitely.
  • iOS keyboard bypass: The keyboard Send/Enter key always called handleSend(), bypassing the on-screen interrupt/queue/stop buttons. Now handleKeyDown routes through handleInterrupt() when the session is running.

Test plan

  • protocol-parser.test.ts updated — reconnected with running: true now asserts SET_RUNNING: true + setWsRunning called
  • Manual: background iOS app while subagent is running → reopen → verify interrupt/stop buttons appear
  • Manual: type message + tap iOS keyboard Send while agent is running → verify it interrupts (not queues)

🤖 Generated with Claude Code

…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>
@dimakis
Copy link
Copy Markdown
Owner Author

dimakis commented May 22, 2026

Centaur Review

Found 2 issue(s).

packages/client/__tests__/protocol-parser.test.ts

Clean, correct fix: restores running state on reconnect and routes Enter to interrupt when running. Both changes are well-tested at the protocol level; minor gaps in negative assertions and ChatInput test coverage.

  • 🔵 missing_tests (L619): The test for running: false on reconnect does not assert that setWsRunning is NOT called. The implementation asymmetrically calls setWsRunning only for true but not false. A negative assertion (expect(setWsRunning).not.toHaveBeenCalled()) in the existing running: false test would document this intentional asymmetry. [fixable]

frontend/src/components/ChatInput.tsx

Clean, correct fix: restores running state on reconnect and routes Enter to interrupt when running. Both changes are well-tested at the protocol level; minor gaps in negative assertions and ChatInput test coverage.

  • 🔵 missing_tests (L154): No unit/integration tests exist for ChatInput. The new Enter-key routing (interrupt vs send based on running + onInterrupt) is untested. Key edge case: running=true but onInterrupt undefined should still fall through to handleSend. [fixable]

@dimakis dimakis merged commit eaea1ed into main May 22, 2026
1 check passed
Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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]

Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Centaur Review

LGTM — no issues found.

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