Repository navigation
fix(native-chat): phone Stop reaches a message the agent hasn't answered; unreadable task list reports unconfirmed - #28243
Conversation
…k list is unconfirmed The phone named the running turn on every Stop, so a Stop pressed before the agent echoed the message said "Stop not sent" and the agent ran on. Against a host advertising agent-session.conversation-stop.v1 the phone now sends the Stop that names no turn, as the desktop already does; the host stops whatever the chat has in flight. A card's Cancel still names its turn, and older hosts keep the turn-named Stop. A background-task Stop whose host could not read the session's task list read the failure as "no tasks" and answered cancelled:false. It now refuses as outcome unknown, the refusal a timed-out background stop already returns. Adds host tests pinning the Stop-before-echo races on the unnamed Stop: Codex turns admitted or started before the echo, a later send just before the Stop, a retried Stop, and a Stop after a failed one. All pass on main unchanged.
…ts turn The phone offered Stop only once a turn existed, so the Stop that names no turn was unreachable while a send waited for the agent's echo. Against a host that advertises conversation stop, a working chat now offers Stop. A card's Cancel chose the untargeted form from the prompt after the prompt-cancel capability filtered it out, so against a host with conversation stop but no prompt cancel it sent a Stop naming nothing. It now always names the card's turn. The race test for an admitted turn that cannot take an interrupt accepts success only once the host has let go of the agent, and otherwise requires the unconfirmed refusal.
Review summary (head
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
726aee90-f4b4-4b5a-960b-a04dc17da7f6
📒 Files selected for processing (16)
config/scripts/vitest-sqlite-runtime-files.mjsmobile/src/session/mobile-native-chat-merged-snapshot-parity-hooks.test.tsxmobile/src/session/mobile-structured-agent-session-cancel.tsmobile/src/session/mobile-structured-agent-session-host-support.test.tsmobile/src/session/mobile-structured-agent-session-host-support.tsmobile/src/session/mobile-structured-agent-session-unnamed-stop.test.tsmobile/src/session/mobile-structured-operation-id-retirement.test.tsmobile/src/session/use-mobile-native-chat-controller.test.tsmobile/src/session/use-mobile-native-chat-controller.tsmobile/src/session/use-mobile-structured-agent-session-prompt-cancel.test.tsxmobile/src/session/use-mobile-structured-agent-session-queued.test-fixture.tsmobile/src/session/use-mobile-structured-agent-session.tsmobile/src/session/use-mobile-structured-turn-indicator.test.tsxsrc/main/native-chat/agent-session-wire/structured-agent-session-background-stop-unreadable.test.tssrc/main/native-chat/agent-session-wire/structured-agent-session-background-stop.tssrc/main/native-chat/agent-session-wire/structured-agent-session-unnamed-stop-races.test.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.
…-stop-close-ref # Conflicts: # src/main/native-chat/agent-session-wire/structured-agent-session-background-stop.ts
ELI5
You press Stop and expect the agent to stop. Over an SSH host there were two places where that went wrong:
Now the phone sends the same Stop the desktop already sends, and the host stops whatever the chat has in flight, including a message the agent hasn't answered yet. A task Stop that the host can't check says "unconfirmed" instead of claiming success.
What Changed
Before → after, as you see it:
Mechanism:
agent-session.conversation-stop.v1, a working chat shows Stop before the agent opens a turn, and the phone's Stop names no turn. That is the form the desktop already uses, and it means "stop whatever the chat has in flight". The host already counts a message the agent hasn't echoed yet as work in flight. A question or permission card's Cancel still names the turn that raised the card, whether or not the host takes the card's own identity. An older host without the capability gets today's turn-named Stop.cancelled: falseas if the task had already ended.No new capability, no new wire field, no new message type.
Why
The common pattern is that the execution host decides what a Stop reaches, and the work has an identity from the moment a message is accepted. Orca's host already works this way. It accepts a Stop that names nothing, and it treats a send the agent hasn't answered as work in flight. Codex's opening turn is waited for and interrupted, and agents whose Stop ends their session end their process.
#26461 instead had clients name the turn, waiting message or task they saw, and added host-side rules for proving that work had ended. That cost about 770 production lines and a new capability. This PR keeps the existing mechanism, adds tests showing it already covers those races, and fixes only the two gaps above. #26461 will be closed in favour of this one.
Differences from the common pattern:
agent-session.conversation-stop.v1. It goes away once no supported host lacks it.session.tabs.closestops the agent even when the hidden-tab record can't be written (refactor(native-chat): keep agent-session records in the chat journal database #24006). The reason is that a database written by a newer Orca opens read-only. That only happens after a downgrade, which users don't do, so this is a candidate to make strict (no stop, report the failure) in a follow-up.agentSession.closewaits for the agent to stop before it answers. The common pattern answers once the close is recorded and lets the stop finish on its own. Candidate follow-up.Linked Issue
Coordinator's structured SSH chat fix plan, U21 and U23 (replaces #26461). No separate public issue.
Visual Proof
Live check on the phone (iOS Simulator running this branch's Orca Mobile, paired to a desktop Orca built from this branch, real signed-in Claude), head
06506a29542.1. Right after sending, before any reply: the phone shows Working and a Stop button. On
mainthe phone has no turn yet here, so Stop answered "Stop not sent" and Claude kept going. (The numbers above are from an earlier turn.)2. Stop pressed about 1 s after sending: the turn is interrupted with no reply. The desktop shows the same.
3. Stop while numbers are streaming still stops it (stopped at 248 of 400).
4. A question card's Cancel from the phone still settles that card (it names its own turn, not "everything in flight").
Not run live: an SSH host, Windows/Linux desktops, Android.
Testing
On macOS:
structured-agent-session-background-stop-unreadable.test.ts: the one-task and every-task Stop with an unreadable list.mobile-structured-agent-session-unnamed-stop.test.ts: the phone's Stop before the first reply, its Stop that names no turn, and a card's Cancel that keeps its turn when the host can't take the card's identity.use-mobile-native-chat-controller.test.ts: the controller offers Stop for a working chat before a turn opens on a capable host, while a card still offers no Cancel without a turn.structured-agent-session-unnamed-stop-races.test.ts, 7 tests): all pass on main unchanged. One uses the real Codex adapter over a scripted Codex app-server.tc:node,tc:web, the mobile typecheck and the mobile test-typecheck ratchet all pass.orca-ci-checks: 21 of 23 pass. The two failures are not from this change:Script not found "check". Thetsctypechecks above pass, and CI runs its own.Not run: a live SSH host, the phone, or Windows/Linux desktops. The change is host logic and a phone request shape, both covered by the tests above.
Review
Draft.
Agent skill upstream boundary
Notes
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)