Skip to content

fix(remote): keep resumed agent tabs stable on headed hosts - #11448

Merged
OrcaWin merged 4 commits into
mainfrom
brennanb2025/windows-agent-resume-blank
Jul 31, 2026
Merged

OrcaWin merged 4 commits into
mainfrom
brennanb2025/windows-agent-resume-blank

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • keep headed runtime-session terminals when a renderer snapshot temporarily omits them
  • track ownership through structured runtime creates, renderer-backed creates, and splits
  • clear ownership on explicit close, headless retirement, and PTY exit
  • require the exact worktree/tab/leaf PTY binding before preserving a tab
  • remap host agent status to the paired client's web-terminal-* identity and retain the correct provider identity without restoring an older turn

Root cause

A headed desktop runtime can create a local PTY on behalf of a paired client. The PTY remains live and authoritative in main, but the host renderer can omit its synthetic session tab during reconciliation. The previous preservation predicate only recognized headless, serve-*, and SSH-owned PTYs, so a live local PTY created through the runtime-session path could be dropped from the publication while its process remained alive.

The runtime now records whether a PTY is runtime-session-owned and preserves it only while its exact worktree, parent tab, and leaf binding is still live. Splits inherit ownership. Explicit close, retirement, and exit clear it, so this does not turn stale or rebound PTYs into durable tabs.

The renderer-side reconciliation also keeps the paired client's remapped ownership/provider metadata and prevents an older host done snapshot from restoring a previous provider session onto a newer client working turn.

Compatibility and performance

  • Local, WSL, SSH, and folder-workspace routing is unchanged.
  • Existing serve-*, SSH, and headless preservation remains intact.
  • The hot preservation check uses the already-bound terminal lookup plus a boolean ownership flag; it adds no polling, provider inventories, or broad PTY scans.
  • Cleanup work is bounded to the tab's published leaves and runs only on close/retirement/exit paths.

Validation

Headed Electron paired-runtime validation on final source

Validated commit 59db88d257 on macOS with an isolated headed Electron host and a separate paired Electron client. The harness rejected local scope and required:

activeRuntimeEnvironmentId = 660bdb1b-b0cc-4b73-a185-5b938ccbb686
hostId = runtime:660bdb1b-b0cc-4b73-a185-5b938ccbb686

A structured Codex resume fixture reported through Orca's real hook and OSC status paths. The client switched to a second worktree for 15 seconds (past the 10-second close-intent TTL), returned, and confirmed:

Assertion Result
Mirrored tab/pane remapped to web-terminal-* PASS
Provider identity retained PASS (session_id: pr11448-electron-resume)
working state remains on the resumed tab PASS
Exact authoritative PTY and handle remain connected PASS
Agent process spawn count PASS (exactly 1)

This run covers the renderer-backed headed-host topology that previously dropped locally owned runtime PTYs. It is isolated same-machine pairing, not a physical remote-server, Windows, or Linux rerun. The earlier Windows 11 real-Codex validation still covers the original paired-runtime reproduction and renderer identity behavior, but the final ownership fix was not rerun on that rig.

Automated checks

  • Same-model review loop; one exact-leaf-binding issue fixed, follow-up review clean
  • Focused Vitest: 986 passed, 1 skipped
  • Broader related matrix: 1,064 passed, 1 skipped
  • pnpm run typecheck:node
  • pnpm run typecheck:web
  • touched-file oxlint, native-plugin audit, and type-aware audit
  • touched-file oxfmt --check
  • pnpm run check:max-lines-ratchet
  • pnpm run check:reliability-gates
  • git diff --check
  • final E2E Electron and web builds

Screenshots

Resumed agent working before the worktree round trip:

pr11448-runtime-resume-before-roundtrip.png

The same resumed tab and working agent after 15 seconds away and re-entry:

pr11448-runtime-resume-after-15s-reentry.png

Review history

  • Same-model review found that the first ownership predicate did not verify the exact leaf binding. The fix now uses the authoritative terminal lookup, with PTY-rebinding and two-PTY split-close regressions. Follow-up review returned clean.
  • Greptile's provider-identity loss finding was fixed in d644677a8c.
  • Greptile's stale-attribution edge was fixed in a73e37c032.
  • CodeRabbit reported no code-level findings on the earlier renderer changes.

Security

No authentication, authorization, credential, filesystem, shell, or network-routing behavior changed. The new state is an in-memory PTY ownership marker used only by session-tab preservation and cleanup.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Runtime-owned PTYs are tracked through creation, persistence, renderer-tab rescue, mobile terminal assignment, teardown, and exit. Mobile terminals remain listed when renderer snapshots omit runtime-owned tabs and are removed after closure or synchronized absence. Agent status mirroring now remaps tab and worktree attribution, preserves fresher state timestamps, reconciles provider sessions, and includes provider-session equivalence in comparisons. Tests cover attribution repair, epoch updates, and obsolete session clearing.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary fix: keeping resumed agent tabs stable on headed hosts.
Description check ✅ Passed The description is detailed and covers the change, testing, screenshots, review history, security considerations, and platform compatibility.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@greptile-apps

greptile-apps Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR fixes two renderer-side state-reconciliation bugs in web-session-tabs-sync.ts for paired runtime clients, and adds a runtimeSessionOwned tracking mechanism in orca-runtime.ts to preserve tabs created via runtime sessions.

  • Agent-status attribution remapping: remapHostAgentStatus now converts the host tab ID to the client-visible web-terminal-* form via toWebTerminalSurfaceTabId, so resume ownership checks can match mirrored tabs.
  • Provider identity preservation: buildMirroredAgentStatusPatch now correctly carries providerSession from the host snapshot when the existing entry has none, guarded by hostIdentityPredatesCurrentTurn to avoid applying stale turn identity; agentStatusEntryEqual gains a providerSession comparison so same-state identity updates are no longer silently discarded.
  • Runtime session ownership: RuntimePtyWorktreeRecord gains a runtimeSessionOwned flag set/cleared across createTerminal, splitTerminal, PTY exit, and closeMobileSessionTab; shouldPreserveMobileSessionTab checks this flag as a new preservation path for runtime-scope paired tabs.

Confidence Score: 5/5

Renderer-side reconciliation fix with no tab-lifecycle side effects; the runtime ownership flag has clean set/clear paths and is exercised by new tests.

The three changed functions in web-session-tabs-sync.ts have narrow, well-tested scopes and the previously flagged provider-session loss on the fresher-OSC path is now correctly handled by the explicit providerSession assignment. The runtimeSessionOwned flag in orca-runtime.ts has consistent lifecycle coverage across createTerminal, splitTerminal propagation, PTY exit, and explicit tab close.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
src/renderer/src/runtime/web-session-tabs-sync.ts Core renderer-side fix: adds tabId remapping in remapHostAgentStatus, correct providerSession preservation in buildMirroredAgentStatusPatch, and providerSession equality check in agentStatusEntryEqual. Logic is sound and the previously-flagged provider-session loss on the fresher-OSC path is addressed.
src/renderer/src/runtime/web-session-tabs-sync.test.ts Significantly expanded test coverage: attribution repair, fresher-OSC identity retention, same-state identity update, and next-turn providerSession isolation are all exercised within the renamed test.
src/main/runtime/orca-runtime.ts Adds runtimeSessionOwned flag to RuntimePtyWorktreeRecord with consistent set/clear lifecycle and integrates the flag into shouldPreserveMobileSessionTab as a new preservation path.
src/main/runtime/orca-runtime.test.ts Adds a test for live headed runtime-owned tab preservation until explicit close, and extends an existing test across multiple syncWindowGraph updates.

Sequence Diagram

sequenceDiagram
    participant Host as Host Runtime
    participant Client as Paired Client
    participant Sync as web-session-tabs-sync
    participant State as AgentStatusByPaneKey

    Host->>Client: Snapshot with agentStatus (tabId: host-tab-1, paneKey: host-tab-1:leaf)
    Client->>Sync: remapHostAgentStatus(surface)
    Note over Sync: paneKey to web-terminal-host-tab-1:leaf, tabId to web-terminal-host-tab-1 (NEW)
    Sync->>State: entry with remapped paneKey + tabId

    alt OSC 9999 fresher
        Note over Sync: Keep existing visible state, take host ownership metadata (NEW)
        Sync->>State: nextEntry merges existing state with host paneKey/worktreeId/tabId/providerSession
    else Host snapshot is fresher
        Sync->>State: "nextEntry = remapped entry"
    end

    Note over Sync: agentStatusEntryEqual now checks agentProviderSessionsEqual() (NEW)
    alt providerSession changed
        Sync->>State: Write nextEntry
    else providerSession equal
        Sync->>State: Skip no-op
    end
Loading

Reviews (4): Last reviewed commit: "fix(remote): preserve headed runtime age..." | Re-trigger Greptile

@OrcaWin OrcaWin changed the title fix(remote): keep resumed agent tabs stable fix(remote): correct mirrored agent-status attribution and provider identity Jul 30, 2026
@brennanb2025 brennanb2025 changed the title fix(remote): correct mirrored agent-status attribution and provider identity fix(remote): keep resumed agent tabs stable on headed hosts Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/runtime/orca-runtime.ts (1)

27856-27856: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Preservation logic OK; minor duplication between the two ownership-check helpers.

hasLiveRuntimeSessionOwnedPtyBinding and hasLiveOrPersistedServeOrSshOwnedPtyBinding are both consulted here and both walk PTY bindings for the same tab via similar lookups. Not a correctness issue — could be worth folding into one combined preservation predicate later for readability, but not essential now.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 13d0e17e-568e-4f49-ad3b-0be507faedea

📥 Commits

Reviewing files that changed from the base of the PR and between 807a283 and 59db88d.

📒 Files selected for processing (4)
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/renderer/src/runtime/web-session-tabs-sync.test.ts
  • src/renderer/src/runtime/web-session-tabs-sync.ts

Comment on lines +28898 to +28947
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 3,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 4,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})

expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert retention after snapshot version 3.

Without an assertion between versions 3 and 4, immediate pruning at version 3 still passes the final empty-list assertion. Assert the terminal remains after version 3, then verify removal after version 4.

Proposed test addition
     runtime.syncWindowGraph(1, {
       tabs: [],
       leaves: [
         {
           tabId: 'tab-renderer',
           worktreeId: TEST_WORKTREE_ID,
           leafId: HEADLESS_SECOND_LEAF_ID,
           paneRuntimeId: 1,
           ptyId: 'pty-renderer',
           paneTitle: null
         }
       ],
       mobileSessionTabs: [
         {
           worktree: TEST_WORKTREE_ID,
           publicationEpoch: 'epoch-1',
           snapshotVersion: 3,
           activeGroupId: 'group-1',
           activeTabId: null,
           activeTabType: null,
           tabs: []
         }
       ]
     })
+    expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([
+      expect.objectContaining({ parentTabId: 'tab-renderer', ptyId: 'pty-renderer' })
+    ])
     runtime.syncWindowGraph(1, {

Based on PR objectives: renderer-omission handling must preserve tabs until synchronized absence.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 3,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 4,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([])
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 3,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([
expect.objectContaining({ parentTabId: 'tab-renderer', ptyId: 'pty-renderer' })
])
runtime.syncWindowGraph(1, {
tabs: [],
leaves: [
{
tabId: 'tab-renderer',
worktreeId: TEST_WORKTREE_ID,
leafId: HEADLESS_SECOND_LEAF_ID,
paneRuntimeId: 1,
ptyId: 'pty-renderer',
paneTitle: null
}
],
mobileSessionTabs: [
{
worktree: TEST_WORKTREE_ID,
publicationEpoch: 'epoch-1',
snapshotVersion: 4,
activeGroupId: 'group-1',
activeTabId: null,
activeTabType: null,
tabs: []
}
]
})
expect((await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs).toEqual([])

@OrcaWin

OrcaWin commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Native Windows verification of 59db88d25

Re-ran the reference failing configuration on Windows 11 Pro 10.0.26200 — the topology where this bug was originally reported and the only place the failure had been reproduced. Both host and client on 59db88d25 (the fix is main-process, so the host must carry it), paired in runtime scope, real Codex CLI 0.145.0 resume from Agent Session History.

Previously this configuration dropped the resumed tab in 3/3 attempts. Now:

Check Result
Resumed tab survives 3 × 15 s switch-away cycles PASS
PTY stays bound across every cycle PASS
Agent status attached to the resumed tab (web-terminal-0306131b-…) PASS
Provider session + transcript path correct PASS (019fb10c-…)
Exactly one agent process spawned by the resume PASS

Dwell was 15 s deliberately — CLOSE_INTENT_TTL_MS is 10_000, and the earlier 1.5 s validation passed precisely because it never reached the failure window.

Also re-ran the two changed suites locally: 987 passed.

On the fix itself: it adds a narrow runtimeSessionOwned flag rather than widening hasLiveOrPersistedServeOrSshOwnedPtyBinding, which avoids the tab-resurrection hazard that predicate's comment warns about. All four set-sites are gated on runtime-session provenance — notably ensurePtyBackedMobileSurfaceForRendererTab early-returns unless a runtime-session create is pending, so ordinary renderer tabs never acquire the flag — and it is cleared on renderer close and PTY exit.

@OrcaWin
OrcaWin merged commit 0281496 into main Jul 31, 2026
79 of 81 checks passed
gal064 pushed a commit to gal064/orca that referenced this pull request Jul 31, 2026
…#11448)

* fix(remote): keep resumed agent tabs stable

* fix(remote): retain resume identity from older snapshots

* fix(remote): refresh mirrored resume attribution

* fix(remote): preserve headed runtime agent tabs
gal064 pushed a commit to gal064/orca that referenced this pull request Aug 1, 2026
A ledger of what this branch carries on top of stablyai/orca, with the upstream
issue/PR to watch for each entry so a change can be dropped once upstream lands
its own fix. Flags the stablyai#11448 cherry-pick as redundant from v1.4.164-rc.0.

Claude-Session: https://claude.ai/code/session_01S765eBk3XW7WmsvBxF17V9
gal064 pushed a commit to gal064/orca that referenced this pull request Aug 2, 2026
Drops the stablyai#11448 entry (upstream's copy arrived with the merge) and the skill
entry (local tooling, not application divergence). Folds the agent-status
retention fix into the remote-tab entry and records the remote notification gap
as known-open, including why it must be built on top of the status fix.

Claude-Session: https://claude.ai/code/session_01S765eBk3XW7WmsvBxF17V9
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…#11448)

* fix(remote): keep resumed agent tabs stable

* fix(remote): retain resume identity from older snapshots

* fix(remote): refresh mirrored resume attribution

* fix(remote): preserve headed runtime agent tabs
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.

2 participants