You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Authorship note: This issue was investigated, drafted, and filed by Claude (Anthropic's AI assistant, running in Claude Code) at the request of and on behalf of this account's owner. The diagnosis below comes from reading the code and real session logs on the owner's machine.
Summary
Since PR #1332 (commit dafbd97, "fix(claude): preserve local process on remote handoff"), a message sent from the mobile app or webapp to a session that is running local interactive Claude Code no longer takes over the session. It is queued silently: nothing happens in the app, nothing is shown in the terminal, and the message is only picked up after the local Claude process exits, i.e. when the user presses Ctrl+C in the terminal.
For phone-first usage (start happy claude on the desktop, then drive the session from the phone) this breaks the core flow: the app appears hung, and the user has to walk back to the terminal and exit Claude before anything happens.
Environment
happy-cli 1.1.10-beta.10, built from a fork merge of upstream main at 17937dd; the relevant code path is unchanged on current upstream main (ff7a10d)
Linux x64, Node v26
Reproduced with both the mobile app and the webapp (self-hosted server, but nothing here is self-host specific)
Steps to reproduce
Run happy claude in a terminal; leave the interactive Claude UI sitting idle at the prompt.
From the app, send a message to this session.
Observe: nothing happens. The message is queued (MessageQueue2.push(), doSwitch only sets a flag), local Claude keeps running, and there is no feedback in either the app or the terminal.
Press Ctrl+C in the terminal.
Only now does the loop enter remote mode, collect the queued message, and process it.
Log evidence
From a real session (timestamps preserved): the message was pushed at 14:51:25 and only collected at 15:20:29, after the terminal Claude was exited 29 minutes later.
[14:51:25.174] [MessageQueue2] push() called with mode hash: ac1744...
[14:51:25.174] [local]: doSwitch
[14:51:25.174] [MessageQueue2] push() completed. Queue size: 1
... no further activity; local interactive Claude keeps running idle ...
[15:20:29.498] [loop] Iteration with mode: remote <- after Ctrl+C in the terminal
[15:20:29.531] [MessageQueue2] Collected batch of 1 messages with mode hash: ac1744...
[15:20:29.538] [claudeRemote] Thinking state changed to: true
Root cause
dafbd97 changed doSwitch in packages/happy-cli/src/claude/claudeLocalLauncher.ts from "abort the local process and switch to remote" to "set switchRequested and wait for the local process to exit naturally":
session.queue.setOnMessage((_message: string,_mode)=>{// Remote messages request control from the app, but must not kill// the active local Claude Code process. The message remains queued// and is picked up by remote mode after local exits naturally.voiddoSwitch();});
In interactive use the local process only exits "naturally" when the user exits it, so the queued message can wait forever.
The tradeoff
The old behavior had a real problem too: #1331 (an app message SIGTERMed local Claude and killed its running shell/monitor tasks), and before #974 the lost switch intent even presented as a crash (#982). So a plain revert just trades one bug for the other.
Proposed fix
Defer the handoff only while the local Claude is actually busy, and add feedback:
If local Claude is idle (no active turn), abort and switch immediately, as before dafbd97. The CLI already tracks this state: claudeLocal reports it via onThinkingChange.
emit a session event so the app can show something like "message queued - a terminal session is active", instead of silence, and
hand off when the current turn finishes rather than waiting for process exit.
That preserves the #1331 fix while restoring the phone-first flow.
Workaround
We currently run a local revert of the dafbd97 behavior (immediate abort-and-switch; the queued message survives the handoff because the queue is not reset on switch). Happy to turn the proposal above into a PR if maintainers agree on a direction.
Summary
Since PR #1332 (commit dafbd97, "fix(claude): preserve local process on remote handoff"), a message sent from the mobile app or webapp to a session that is running local interactive Claude Code no longer takes over the session. It is queued silently: nothing happens in the app, nothing is shown in the terminal, and the message is only picked up after the local Claude process exits, i.e. when the user presses Ctrl+C in the terminal.
For phone-first usage (start
happy claudeon the desktop, then drive the session from the phone) this breaks the core flow: the app appears hung, and the user has to walk back to the terminal and exit Claude before anything happens.Environment
Steps to reproduce
happy claudein a terminal; leave the interactive Claude UI sitting idle at the prompt.MessageQueue2.push(),doSwitchonly sets a flag), local Claude keeps running, and there is no feedback in either the app or the terminal.Log evidence
From a real session (timestamps preserved): the message was pushed at 14:51:25 and only collected at 15:20:29, after the terminal Claude was exited 29 minutes later.
Root cause
dafbd97 changed
doSwitchinpackages/happy-cli/src/claude/claudeLocalLauncher.tsfrom "abort the local process and switch to remote" to "setswitchRequestedand wait for the local process to exit naturally":In interactive use the local process only exits "naturally" when the user exits it, so the queued message can wait forever.
The tradeoff
The old behavior had a real problem too: #1331 (an app message SIGTERMed local Claude and killed its running shell/monitor tasks), and before #974 the lost switch intent even presented as a crash (#982). So a plain revert just trades one bug for the other.
Proposed fix
Defer the handoff only while the local Claude is actually busy, and add feedback:
claudeLocalreports it viaonThinkingChange.That preserves the #1331 fix while restoring the phone-first flow.
Workaround
We currently run a local revert of the dafbd97 behavior (immediate abort-and-switch; the queued message survives the handoff because the queue is not reset on switch). Happy to turn the proposal above into a PR if maintainers agree on a direction.