Skip to content

fix(agents): a resumed agent's chat is readable on the phone before its first new turn - #29664

Merged
nwparker merged 4 commits into
mainfrom
nwparker/mobile-chat-provider-session
Oct 16, 2026
Merged

nwparker merged 4 commits into
mainfrom
nwparker/mobile-chat-provider-session

Conversation

@nwparker

@nwparker nwparker commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​512 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​511
Prod 7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​137 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​136

ELI5

When Orca resumes an old Codex conversation in a terminal, the phone's chat view showed nothing until you typed a new message. Orca started that resume itself, so it already knew which conversation the terminal holds. It now says so right away, and the phone (and anything else that reads agent status) can open the saved conversation immediately.

What Changed

Before: a resumed Codex session (desktop "Resume", a restart's cold restore, or any terminal.create that carries a resume session) was listed to the phone with no agent session at all. Phone chat had no transcript to ask for and stayed on its empty state until the user sent a message from the terminal. Claude did not have this gap, because Claude reports its session as soon as it starts.

After: the moment Orca spawns a terminal that resumes a known session, the host records that session in its agent-status store as an identity-only row (the same kind of row Orca already keeps for a finished agent's resume identity). The phone's session.tabs.list row carries agentType and agentStatus.providerSession straight away, and nativeChat.readSession returns the saved conversation. The agent's own first hook replaces the row as usual.

Mechanism:

  • src/main/ipc/pty/pane/resume-launch-identity.ts is called from both spawn paths, the renderer pty:spawn commit and the runtime-controller commit. It seeds only for a fresh local spawn of a resumable agent with a valid resume session. It skips reattached processes, SSH panes (their relay owns those rows), Codex resumes that fell back to a fresh session, and Pi-family resumes that have no transcript file.
  • AgentHookServer.recordLaunchResumeIdentity (server-ingest-terminal.ts) writes the row with observation origin launch. It never overwrites a row the agent reported after the launch, and it marks the row so that a host restart before the first turn keeps it. The seed runs only after the spawn has committed, so a rejected spawn leaves nothing behind. Terminal evidence (OSC or process) that starts the first turn keeps the seeded session, because the seed's done is a placeholder, not a finished turn.
  • terminal.ensureAgentSession (a paired desktop or web client choosing "Resume" against a remote server, which is [Bug]: "Resume in Worktree" opens in Terminal despite Default view = Native, and chat view stays empty until a message is sent #12904's path) now passes its session to the spawn as resumeProviderSession, as the other host-built resume launches already did. That lets this path seed too, and gives its Codex resume the same Codex-home selection the local resume gets.

Root cause: Codex runs its SessionStart hook only when its first turn begins (run_pending_session_start_hooks in codex-rs core/src/session/turn.rs), and every other Codex hook is turn-scoped. So a resumed Codex sends nothing until the user types. Meanwhile the resume session Orca passed to the spawn (resumeProviderSession) was used only to choose the Codex home (spawn-preflight.ts, spawn-env-codex.ts). Nothing recorded it as the pane's identity.

Wire compatibility: no wire change. The host publishes an existing optional field (agentStatus.providerSession) earlier than before. Older phones already read it.

Why

The execution host owns agent status in one store, and every reader (phone tab list, native chat, worktree ps) joins on it. Seeding that store at the one place that knows the resume, which is the spawn, fixes every reader at once and follows the existing identity-only row model. Alternatives considered:

  • Guessing the session from the Codex process command line or the newest rollout file. That breaks for codex resume --last and cannot tell apart two panes in one folder.
  • Teaching the phone to remember the session it asked to resume. That only covers phone-started resumes, and an old phone would still show nothing.

Linked Issue

Part of #10923 (also the "empty until you send one" symptom in #12904). Not a full fix; see Notes.

Visual Proof

N/A. This changes host-published status data, not UI. The phone renders the same chat view once the identity arrives; the e2e spec checks the phone's wire.

Testing

Reproduced first on fresh origin/main with a scripted phone client paired to an isolated headless orca serve:

  • A typed Codex or Claude session (fake agents posting real Orca hook scripts, and also real Codex 0.162.1) already gets providerSession on main, and nativeChat.readSession works. The original headless-serve report is already fixed on main by the hook-row fallback.
  • A real codex resume <id> (and --last) leaves the tab with no agentStatus until a message is sent. This is the failure fixed here.

Tests:

  • tests/e2e/headless-serve-mobile-native-chat-identity.spec.ts: a phone paired to headless serve reads Codex and Claude chats, and reads a resumed Codex chat before its first turn. The resume case fails on main and passes with this change.
  • src/main/agent-hooks/server-launch-resume-identity.test.ts: seeds the row, yields to the agent's own report, is replaced by the first hook, and survives a host restart. The restart case fails without the retention marker.
  • src/main/ipc/pty/pane/resume-launch-identity.test.ts: the skip rules (reattach, SSH, dropped Codex resume, bad id, non-resumable agent, Pi without a file).
  • terminal-creation-and-readiness-part-02.spec.ts: ensureAgentSession passes the session to the spawn.
  • Unit folders src/main/agent-hooks, src/main/ipc, src/main/runtime/session-construction and orca-runtime.test.ts all pass single-worker. The static-analysis job steps were also run locally.

Verified live, each in an isolated temp HOME and profile, background launch, no windows:

  • macOS: real Codex 0.162.1 typed session plus host-launched codex resume. The phone list carries agentType: codex and the session id before any input, and nativeChat.readSession returns the transcript.
  • Linux (openclaw, under Xvfb): the same real-Codex flow and both e2e tests pass.

Only in tests: the desktop renderer path and the restart path. Windows was not run.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Not covered here, still open under #10923:

  • A codex resume the user types by hand: Orca did not launch it, so it does not know the session.
  • A phone-started AI Vault resume: the phone does not send resumeProviderSession yet. The field already exists on terminal.create, so a later phone change can opt in.
  • The desktop's own chat view: its renderer still drops non-Pi identity-only rows. This needs a separate change to the sleeping-session records.
  • The OMP status extension resetting its session id, and the SSH-relay report from today. These are separate producers and paths.

Codex review (gpt-6.1-sol, high), four rounds:

  • Fixed: a Codex resume that fell back to a fresh session no longer seeds. The seed survives a host restart. Seeding runs after the spawn commits, with no orphan on a failed commit. Terminal evidence starting the first turn keeps the seed.
  • Dismissed, edge cases that only fall back to today's behaviour (identity appears at the agent's first hook) and never show a wrong session:
    • Terminal-only status (OSC or process) that lands between launch and commit. The commit runs immediately after spawn, and Codex and Claude report through hooks.
    • An idle OSC repaint, or a restart, followed by terminal-only working. Fixing it would need a new persisted marker on status rows.
  • Dismissed as out of scope: the desktop renderer's identity-only receiver (see Notes).

SSH: SSH panes are skipped on purpose, because the relay on that host owns their status. Folder workspaces are covered because the seed is keyed by pane, not by git worktree.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 47 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3b3776cf-969c-49ba-89af-2b14a6078aa5
📥 Commits

Reviewing files that changed from the base of the PR and between a93fe5e and d9ba992.

📒 Files selected for processing: 11
  • src/main/agent-hooks/server-launch-resume-identity.test.ts
  • src/main/agent-hooks/server/server-ingest-terminal.ts
  • src/main/ipc/pty/ipc/spawn-commit.ts
  • src/main/ipc/pty/pane/resume-launch-identity.test.ts
  • src/main/ipc/pty/pane/resume-launch-identity.ts
  • src/main/ipc/pty/runtime/spawn-commit.ts
  • src/main/ipc/pty/runtime/spawn-preflight.ts
  • src/main/ipc/pty/runtime/spawn-state.ts
  • src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-02.spec.ts
  • src/main/runtime/session-construction/agent-session/get-agent-session-execution-namespace.ts
  • tests/e2e/headless-serve-mobile-native-chat-identity.spec.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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