fix(chat): refuse chat.send on sessions live in tmux and register the omo runtime - #47
Merged
Conversation
… omo runtime chat.send routed every message to spawnFns[provider] without checking whether the target transcript is currently owned by a live tmux pane, so a second headless CLI resumed the same session id: two processes interleaved one JSONL and the live agent never saw the message (#44, observed on omo). - New live-spawn-guard service: before starting a run, chat.send resolves the fresh external-CLI roster and refuses with SESSION_LIVE_IN_TMUX when a pane of the same provider owns the exact provider session id. Fail-open on unavailable discovery evidence: a false block would break the core chat path, a false allow merely restores pre-guard behavior. - Guarded providers are the six headless-resume CLIs (claude, codex, cursor, opencode, omp, omo). gjc is exempt: its live sessions are reached through the SDK connect lane, which attaches instead of forking. - With the guard in place, spawnOmo/abortOmoSession are registered, closing the omo send gap left by #43. The pi runtime abort-handle capture and the chat.abort failure branch now include omo alongside gjc/omp. Verified end to end on this host: a real omo pane started with --session-id <id> in tmux blocks the spawn with the owning session name, an unrelated id and the same id under omp pass, and killing the pane releases the guard. Closes #44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #44.
chat.sendrouted every message tospawnFns[provider]without checking whether the target transcript is currently owned by a live tmux pane, so a second headless CLI resumed the same session id: two processes interleaved one JSONL and the live agent never saw the message.How
live-spawn-guard.service.ts: before starting a run,chat.sendresolves the fresh external-CLI roster (getExternalCliSessionsDetailedFresh, same evidence grade as request-time relay authorization) and refuses with aSESSION_LIVE_IN_TMUXprotocol error naming the owning tmux session. The client already renders protocol errors in the conversation and stops the spinner.spawnOmo/abortOmoSession), closing the gap fix(providers): register omo in the runtime lists the type system cannot see #43 deliberately left pending exactly this guard. The pi abort-handle capture andchat.abortfailure branch include omo alongside gjc/omp.Verification
live-spawn-guard.test.ts): exact provider+id match blocks, unresolved panes / other providers / other ids never block, gjc exempt.omo --session-id <id>in a tmux pane →findLiveTmuxSpawnBlock('omo', id)returned{"tmuxName":"guard-e2e"}; unrelated id and same id under omp returned null; killing the pane released the guard.