Repository navigation
feat(sidebar): main runs workspace sleep; the window follows (P3b) - #31564
Conversation
…lip) The fixes found while the sidebar ran from main's feed, without turning that on: ORCA_SIDEBAR_INPUTS_IS_MAIN stays false, and none of flip-a's stop-gaps come along. - Main stops counting a closed tab's terminal once it was asked to stop and no pane shows it; a failed or refused stop takes the request back, and a host out of contact stays unverifiable. - Main fetches a paired server's rows with the transport's normal bound, so a server slower than 5 s still lists. - A deferred unread raise flips the folder row it was decided for, and a read while the owner check is out cancels it. - Phone: no worktree.ps flash before the feed; a host's last installed feed stands in while a fresh read is out; it highlights its own last-opened workspace and a fold never scrolls; projects file under their project group with the desktop's indent; the collapse spinner shows only after 500 ms; "Reopen to update" keeps the rows on screen. - e2e helpers and journeys for the switch-on suite.
…d prune pending unread raises - A stop-and-wait that throws takes its stop request back, like one that fails. - A final stop the legacy relay still owes stays marked, since the relay retries it. - Pending unread raises are tracked only while one waits, so the map no longer grows. - The paired-server fallback poll test gives its third fetch a reply and asserts live.
…stores; detected, view and live-content domains (inert)
…n metadata writes, no phone wait on detected checkouts
…gress hints (switch-gated) Create, delete, terminal.closeAll, terminal.sleep and worktree.sleep now run through one first-in, first-out queue per host-qualified workspace: strict order, no merging, delete cancels what has not started and waits for what has, an unconfirmed outcome pauses until the host's own answer. Hints publish in a new inProgress feed domain (no reader yet); the creations domain is its projection. A finished create or delete keeps its hint until a scan started after it applies, in the same revision. Adds the paired-server metadata serializer (saving domain, no caller yet). Replaces the creation registry.
The desktop registers how sleep reaches paired servers; a headless serve has none. Co-Authored-By: Claude <noreply@anthropic.com>
…spend stub Co-Authored-By: Claude <noreply@anthropic.com>
- A command issued while paused waits behind the pause; when the owner answers again main re-reads terminal liveness and the listing, then carries on. A forced delete runs past it. - Commands keep their arrival order across a workspace lookup; deletes by name key by the resolved id, so they wait for that workspace's running sleep. - A second delete with different options is refused as already in progress. - Close and delete read lost contact as sleep does. - A create always frees its own queue entry, even after a retry took over its id. - Drops the unused opId, and the paired-server write serializer and its feed section, which land with their first caller. Co-Authored-By: Claude <noreply@anthropic.com>
… sidebar-p3b-main-lifecycle # Conflicts: # src/main/runtime/workspaces/workspace-creation.ts # src/main/runtime/workspaces/workspace-lifecycle-commands.ts # src/main/runtime/workspaces/workspace-lifecycle-queue.ts
…p classifies only lost contact as unconfirmed - The window no longer captures wake records for a sleep main runs; it takes main's records from main's answer, which its wake and tab-open resume then read. - A workspace reopened during its sleep keeps its VM running (the window and a phone's activation tell main). - Only lost contact pauses: a definite chat-stop failure or a server's still-live answer is a plain failure with today's toast. - A paired server's chats are asked with the Sleep mark, and again without it when an older server refuses the field, instead of reading an unprobed status. - A phone's worktree.sleep is answered on acceptance and its activation no longer waits on a queued wake, so neither can outlast the phone's request timeout. The web client passes markChatsAsleep and keeps the receipts. - One desktop adapter, passed where the desktop builds the runtime, replaces the two module slots; a headless server gets none. Co-Authored-By: Claude <noreply@anthropic.com>
…int fixes Co-Authored-By: Claude <noreply@anthropic.com>
…the window's copies The window no longer sleeps a paired server's terminals, so its convergence and order tests for that path move to main (remote-workspace-terminal-sleep.test.ts) or follow main's answer. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Also: split the workspace-wide terminal stops into their own RPC module (line limit), and let a pending name lookup hold other workspaces' commands only briefly. Co-Authored-By: Claude <noreply@anthropic.com>
… sidebar-p3b-main-lifecycle # Conflicts: # src/main/runtime/workspaces/managed-workspace-operations.ts
…attaches - Main claims and starts each slept pane through its windowless resume path, with or without a window: the "window present" check is gone. - A phone's open queues main's wake, which finishes only once the resumes have started, and always notes the reopen so a running sleep leaves the VM up. - The window no longer relaunches a workspace sleep's records in fresh tabs; opening a slept pane asks main to resume it and the pane's spawn attaches to main's terminal. - A window save cannot overwrite main's workspace-sleep records. - A VM that would not suspend no longer reads as "kept open": the terminals slept, and the toast says the machine is still running.
… sidebar-p3b-main-lifecycle # Conflicts: # src/main/runtime/workspaces/workspace-activation.ts
…eadless wake report counts only real sleeps
…cycle # Conflicts: # src/main/runtime/rpc/methods/worktree-sleep-method.ts # src/main/runtime/sidebar-inputs/orca-runtime-sidebar-temporal.ts # src/main/runtime/sidebar-inputs/sidebar-retention-owner.ts # src/main/runtime/workspaces/workspace-creation.ts # src/main/runtime/workspaces/workspace-lifecycle-commands.ts # src/main/runtime/workspaces/workspace-lifecycle-queue.test.ts # src/main/runtime/workspaces/workspace-lifecycle-queue.ts
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 Walkthrough
Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments: 1
tests/e2e/workspace-sleep-in-main.spec.ts: 1
95-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCheck the visible result in the page, not only the store and runtime counts.
readSleepStatereadswindow.__storeandterminal.listand compares their counts. These checks prove the host stopped its terminals. They do not prove the window shows the slept workspace correctly. A broken follower that hides nothing, or that leaves a dead pane mounted, still passes. Keep the host liveness check. Also check the page itself, for example the slept row's state in the sidebar or that no terminal pane forsecondis mounted, usinggetByRole,toBeVisible, ortoHaveCount.As per coding guidelines: "Use
window.__storefor setup; assert user-visible behavior through the DOM (getByRole,toBeVisible,toHaveText). Writing state and reading it back cannot catch broken rendering."Also applies to: 119-119
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
6f6f8af9-1e65-480f-a430-2c6dc4c6f686
⛔ Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing: 129
src/main/ephemeral-vm-workspace-suspend.tssrc/main/ipc/ephemeral-vm-managed-ssh.test.tssrc/main/ipc/ephemeral-vm-runtime-handlers.tssrc/main/ipc/ephemeral-vm.test.tssrc/main/ipc/pty-launch-parity.test-fixture.tssrc/main/ipc/session.tssrc/main/ipc/worktrees-create-metadata-persistence.test.tssrc/main/ipc/worktrees.tssrc/main/ipc/worktrees/sleep/register-worktree-sleep-handlers.tssrc/main/persistence/window-save-sleep-records.test.tssrc/main/persistence/window-save-sleep-records.tssrc/main/runtime/acknowledged-terminal-tab-retirement-fixture.tssrc/main/runtime/construction/runtime-construction-dependencies.tssrc/main/runtime/construction/runtime-construction.tssrc/main/runtime/fit-override-integration.test.tssrc/main/runtime/mobile-presence-lock.test.tssrc/main/runtime/mobile-subscribe-integration.spec.tssrc/main/runtime/multi-client-navigation-isolation.integration.test.tssrc/main/runtime/orca-runtime-default-tab-agent-status.test.tssrc/main/runtime/orca-runtime-layout-owner-start-fixture.tssrc/main/runtime/orca-runtime-terminal-cwd.test.tssrc/main/runtime/orca-runtime-terminal-rename-retention.test.tssrc/main/runtime/orca-runtime-test-scenario-builders.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-headless-wake-report.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-02.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-03.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-07.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-09.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-12.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-part-13.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-04.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-05.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.tssrc/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.tssrc/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation-part-04.spec.tssrc/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-02.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-03.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-04.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-and-startup.spec.tssrc/main/runtime/orca-runtime-tests/worktree-setup-single-creator.spec.tssrc/main/runtime/owned-session-teardown-policy.test.tssrc/main/runtime/owned-session-teardown-policy.tssrc/main/runtime/paired-host-terminal-close.test.tssrc/main/runtime/qoder-mobile-resume-adoption.test.tssrc/main/runtime/remote-desktop-driver.test.tssrc/main/runtime/rpc/methods/worktree-sleep-method.tssrc/main/runtime/runtime-created-worktree-terminal-target.test.tssrc/main/runtime/runtime-notifier-contract.tssrc/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.tssrc/main/runtime/session-lifecycle/projection/restored-pane-materialize.tssrc/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.tssrc/main/runtime/sidebar-inputs/orca-runtime-sidebar-temporal.tssrc/main/runtime/sidebar-inputs/sidebar-retention-owner.test.tssrc/main/runtime/sidebar-inputs/sidebar-retention-owner.tssrc/main/runtime/workspaces/managed-workspace-operations.tssrc/main/runtime/workspaces/managed-worktree-sleep-layout-owner.test.tssrc/main/runtime/workspaces/remote-workspace-terminal-sleep.test.tssrc/main/runtime/workspaces/remote-workspace-terminal-sleep.tssrc/main/runtime/workspaces/workspace-activation.tssrc/main/runtime/workspaces/workspace-creation.tssrc/main/runtime/workspaces/workspace-lifecycle-commands.tssrc/main/runtime/workspaces/workspace-lifecycle-queue.test.tssrc/main/runtime/workspaces/workspace-lifecycle-queue.tssrc/main/runtime/workspaces/workspace-sleep-execution.test.tssrc/main/runtime/workspaces/workspace-sleep-execution.tssrc/main/runtime/workspaces/workspace-sleep-own-worktree.test.tssrc/main/runtime/workspaces/workspace-sleep-platform.tssrc/main/runtime/workspaces/workspace-sleep-runtime.tssrc/main/startup/main-process-runtime-service.tssrc/main/window/runtime-window-lifecycle.tssrc/preload/api/ephemeral-vm-api.tssrc/preload/api/ephemeral-vm-bridge.tssrc/preload/api/ui-bridge-clipboard-and-window-controls.tssrc/preload/api/ui-command-event-api.tssrc/preload/api/worktree-api.tssrc/preload/api/worktrees-bridge.tssrc/renderer/src/components/sidebar/sleep-workspace-chat-stop.tssrc/renderer/src/components/sidebar/sleep-worktree-activation-race.test.tssrc/renderer/src/components/sidebar/sleep-worktree-admission.test.tssrc/renderer/src/components/sidebar/sleep-worktree-flow.test.tssrc/renderer/src/components/sidebar/sleep-worktree-flow.tssrc/renderer/src/components/sidebar/sleep-worktree-test-main.tssrc/renderer/src/components/sidebar/workspace-sleep-follower.tssrc/renderer/src/components/sidebar/workspace-sleep-target.tssrc/renderer/src/components/sidebar/worktree-list/listing/use-sleeping-sweep-retention.main-parity.test.tsxsrc/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.tssrc/renderer/src/components/terminal-pane/pty-connection/main-resumed-sleep-pane.tssrc/renderer/src/hooks/ipc-events-agent-status-window-test-fixtures.tssrc/renderer/src/hooks/ipc-events-close-routing-test-harness.tssrc/renderer/src/hooks/ipc-events-terminal-create-window-test-fixtures.tssrc/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge-reveal.test.tssrc/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge.test.tssrc/renderer/src/hooks/ipc-events/mobile-terminal-close-ipc-bridge.tssrc/renderer/src/hooks/useIpcEvents-browser-tab-create.test.tssrc/renderer/src/hooks/useIpcEvents-cli-worktree-activation.test.tssrc/renderer/src/hooks/useIpcEvents-close-routing-browser-pages.test.tssrc/renderer/src/hooks/useIpcEvents-ssh-disconnect-cleanup.test.tssrc/renderer/src/hooks/useIpcEvents-updater-status.test.tssrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/lib/pi-session-resume-wake.test.tssrc/renderer/src/lib/resume-sleeping-agent-session-live-ssh-dedupe.test.tssrc/renderer/src/lib/resume-sleeping-agent-session-provider-claim.test.tssrc/renderer/src/lib/resume-sleeping-agent-session.test.tssrc/renderer/src/lib/resume-sleeping-agent-session.tssrc/renderer/src/lib/settled-worker-wake-policy.test.tssrc/renderer/src/lib/wake-sleeping-agents-in-background.tssrc/renderer/src/store/host-twin-isolation.test.tssrc/renderer/src/store/slices/agent-status-manual-sleep-capture.test.tssrc/renderer/src/store/slices/store-sleep-runtime-convergence.test.tssrc/renderer/src/store/slices/store-sleep-shutdown-order.test.tssrc/renderer/src/store/slices/store-sleep-twin-host-owner.test.tssrc/renderer/src/store/terminals/terminal-actions.tssrc/renderer/src/store/terminals/terminal-shutdown.tssrc/renderer/src/store/terminals/terminal-sleep-layout-owner.tssrc/renderer/src/web/preload-api/web-ui-api.tssrc/renderer/src/web/preload-api/web-workspace-sleep-notices.tssrc/renderer/src/web/preload-api/web-worktrees-api.tssrc/shared/rpc-contract/worktree-params.tssrc/shared/workspace-sleep.tssrc/shared/worktree-sleep-request.tstests/e2e/ssh-orcad-sleep-disconnected.spec.tstests/e2e/workspace-sleep-in-main.spec.tstests/e2e/workspace-sleep-main-resume.spec.tstests/e2e/worktree-lineage.spec.ts
💤 Files with no reviewable changes: 41
- src/renderer/src/components/sidebar/sleep-worktree-admission.test.ts
- src/shared/worktree-sleep-request.ts
- src/main/ipc/pty-launch-parity.test-fixture.ts
- src/preload/api/ephemeral-vm-bridge.ts
- tests/e2e/worktree-lineage.spec.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-12.spec.ts
- src/main/runtime/mobile-subscribe-integration.spec.ts
- src/main/runtime/mobile-presence-lock.test.ts
- src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
- src/main/runtime/orca-runtime-tests/worktree-setup-single-creator.spec.ts
- src/preload/api/ephemeral-vm-api.ts
- src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation.spec.ts
- src/main/runtime/multi-client-navigation-isolation.integration.test.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-03.spec.ts
- src/main/runtime/qoder-mobile-resume-adoption.test.ts
- src/main/runtime/paired-host-terminal-close.test.ts
- src/renderer/src/components/sidebar/sleep-workspace-chat-stop.ts
- src/main/runtime/fit-override-integration.test.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-05.spec.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-07.spec.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-03.spec.ts
- src/main/runtime/remote-desktop-driver.test.ts
- src/main/runtime/orca-runtime-terminal-rename-retention.test.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-13.spec.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-12.spec.ts
- src/main/runtime/orca-runtime-default-tab-agent-status.test.ts
- src/main/runtime/orca-runtime-layout-owner-start-fixture.ts
- src/main/runtime/orca-runtime-tests/worktree-setup-and-startup.spec.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-09.spec.ts
- src/main/runtime/orca-runtime-terminal-cwd.test.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-04.spec.ts
- src/main/runtime/orca-runtime-tests/worktree-removal-and-reconciliation-part-04.spec.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness.spec.ts
- src/renderer/src/store/slices/store-sleep-twin-host-owner.test.ts
- src/main/runtime/orca-runtime-tests/terminal-creation-and-readiness-part-08.spec.ts
- src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-04.spec.ts
- src/main/runtime/orca-runtime-test-scenario-builders.spec.ts
- src/main/runtime/orca-runtime-tests/mobile-session-tabs-part-02.spec.ts
- src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-02.spec.ts
- src/main/runtime/orca-runtime-tests/worktree-setup-and-startup-part-03.spec.ts
- src/main/runtime/workspaces/workspace-creation.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.
| if (teardownStops('sleep', 'structured-chat')) { | ||
| chats = await stopChats(deps, target, markAsleep) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
set -e
printf '%s\n' '--- target function and surrounding source ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-execution.ts | sed -n '1,280p'
printf '%s\n' '--- failure and outcome references ---'
rg -n -F --glob '*.{ts,tsx}' -e 'WorkspaceSleepOutcome' -e 'outcome.failure' -e 'sleepWorkspaces(' -e 'stopChats(' src/main src | head -240
printf '%s\n' '--- relevant workspace sleep files ---'
rg --files src/main src | rg 'workspace-(sleep|wake)|sleep.*workspace|workspace.*sleep'Repository: stablyai/orca
Length of output: 27904
🏁 Script executed:
set -e
printf '%s\n' '--- runtime and shared outcome ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-runtime.ts | sed -n '1,135p'
nl -ba src/shared/workspace-sleep.ts | sed -n '1,90p'
printf '%s\n' '--- renderer outcome handling ---'
nl -ba src/renderer/src/components/sidebar/sleep-worktree-flow.ts | sed -n '70,205p'
printf '%s\n' '--- execution tests around terminal failure and chat stopping ---'
nl -ba src/main/runtime/workspaces/workspace-sleep-execution.test.ts | sed -n '1,235p'Repository: stablyai/orca
Length of output: 27510
Skip chat shutdown when terminal sleep fails.
failure marks the workspace as kept open, but stopChats still runs and can mark chats asleep. Guard the chat stop with !failure so an unsuccessful workspace sleep does not stop its chats.
🐛 Suggested fix
--- "a/src/main/runtime/workspaces/workspace-sleep-execution.ts"
+++ "b/src/main/runtime/workspaces/workspace-sleep-execution.ts"
@@ -204,9 +204,9 @@
? error.stoppedPtyIds.filter((id): id is string => typeof id === 'string')
: null
}
- if (teardownStops('sleep', 'structured-chat')) {
+ if (!failure && teardownStops('sleep', 'structured-chat')) {
chats = await stopChats(deps, target, markAsleep)
}
} finally {
batch.chatsSettled()
}📝 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.
| if (teardownStops('sleep', 'structured-chat')) { | |
| chats = await stopChats(deps, target, markAsleep) | |
| } | |
| if (!failure && teardownStops('sleep', 'structured-chat')) { | |
| chats = await stopChats(deps, target, markAsleep) | |
| } |
| ended(true) | ||
| return { | ||
| worktreeId, | ||
| chatFailed: reply?.chatFailed === true, | ||
| receipts: Array.isArray(reply?.receipts) ? reply.receipts : [] | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Pass vmSuspendFailure through from the host's reply.
A web client is not mobile, so worktree.sleep waits for the whole sleep. It returns sleepOwnWorktree's full outcome, and that outcome includes vmSuspendFailure. This mapping keeps only chatFailed and receipts. sleepWorktreesReportingFailures therefore never adds the workspace to vmStillRunningIds. A web user who sleeps a VM workspace whose suspend failed gets no "machine is still running" warning. The VM keeps running without the user knowing.
🐛 Proposed fix
--- "a/src/renderer/src/web/preload-api/web-worktrees-api.ts"
+++ "b/src/renderer/src/web/preload-api/web-worktrees-api.ts"
@@ -175,12 +175,15 @@
// Why: the host stops the workspace's chats too, which can take up to two minutes.
WEB_WORKSPACE_SLEEP_TIMEOUT_MS
)
ended(true)
return {
worktreeId,
chatFailed: reply?.chatFailed === true,
- receipts: Array.isArray(reply?.receipts) ? reply.receipts : []
+ receipts: Array.isArray(reply?.receipts) ? reply.receipts : [],
+ ...(typeof reply?.vmSuspendFailure === 'string'
+ ? { vmSuspendFailure: reply.vmSuspendFailure }
+ : {})
}
} catch (error) {
ended(false)
const failure = error instanceof Error ? error.message : String(error)📝 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.
| ended(true) | |
| return { | |
| worktreeId, | |
| chatFailed: reply?.chatFailed === true, | |
| receipts: Array.isArray(reply?.receipts) ? reply.receipts : [] | |
| } | |
| ended(true) | |
| return { | |
| worktreeId, | |
| chatFailed: reply?.chatFailed === true, | |
| receipts: Array.isArray(reply?.receipts) ? reply.receipts : [], | |
| ...(typeof reply?.vmSuspendFailure === 'string' | |
| ? { vmSuspendFailure: reply.vmSuspendFailure } | |
| : {}) | |
| } |
… a slept workspace's mounted agents on phone open A workspace sleep stopped each terminal through main's stop-and-verify, which polled for a further second after the exit it had already seen. And a phone opening a slept workspace no longer reached the window's mounted panes of agents that had finished before the sleep, so they stayed asleep until the window opened their tab.
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:
19eabcdd-6c0f-49be-bec1-6d0cdeb1b575
📒 Files selected for processing: 5
src/main/ipc/pty-daemon-controller-teardown.test.tssrc/main/ipc/pty/provider/liveness.tssrc/main/ipc/pty/runtime/kill.tssrc/renderer/src/lib/wake-sleeping-agents-in-background.test.tssrc/renderer/src/lib/wake-sleeping-agents-in-background.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.
| } | ||
| } | ||
| try { | ||
| if (!(await verifyPtyStopped(provider, ptyId, opts))) { | ||
| if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) { | ||
| runtime?.markPtyLivenessLive?.(ptyId) | ||
| return false | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '230,325p' src/main/ipc/pty/runtime/kill.ts
sed -n '45,115p' src/main/ipc/pty/provider/liveness.tsRepository: stablyai/orca
Length of output: 7000
🏁 Script executed:
set +e
printf '%s\n' '--- definitions and references ---'
rg -n -F --glob 'src/**/*.ts' -e 'shutdownProviderAndDetectExit' -e 'PtyExitProof' -e 'providerExitObserved' -e 'verifyPtyStopped' -e 'KEEP_HISTORY_STOP' src
printf '%s\n' '--- changed hunk and helper context ---'
git diff 154307498b7757f718ce7b914aea8eab365597f8 7dd89729e0a5801ec4f7ceddb25ebda3f0eec9c1 -- src/main/ipc/pty/runtime/kill.ts
printf '%s\n' '--- kill.ts top-level context ---'
sed -n '1,240p' src/main/ipc/pty/runtime/kill.ts
printf '%s\n' '--- tests mentioning stop/exit/keepHistory ---'
rg -n -F --glob 'src/**/*.test.ts' --glob 'src/**/*.spec.ts' --glob 'test/**/*.ts' --glob 'tests/**/*.ts' -e 'stopAndWaitPty' -e 'verifyPtyStopped' -e 'keepHistory' -e 'exit-observed' -e 'incarnationId' .
exit 0Repository: stablyai/orca
Length of output: 41386
🏁 Script executed:
printf '%s\n' '--- shutdown producer ---'
nl -ba src/main/ipc/pty/provider/shutdown-detect.ts
printf '%s\n' '--- provider contract and exit listener bindings ---'
rg -n -F --glob 'src/main/ipc/pty/**/*.ts' -e 'interface IPtyProvider' -e 'type IPtyProvider' -e 'onExit' -e 'listProcesses' -e 'shutdown(' -e 'kill(' -e 'waitForPtyExitRecord'
printf '%s\n' '--- provider type/implementation files ---'
rg --files src/main/ipc/pty/provider src/main/ipc/pty | rg '(provider|pty-provider|bind-listeners|local-configure|session|controller)'
printf '%s\n' '--- focused respawn and teardown tests ---'
nl -ba src/main/ipc/pty/runtime/kill-same-id-respawn.test.ts
nl -ba src/main/ipc/pty-daemon-controller-teardown.test.ts | sed -n '1,230p'
nl -ba src/main/ipc/pty-daemon-controller-teardown.test.ts | sed -n '240,350p'
printf '%s\n' '--- exact PR diff for relevant files ---'
git diff --unified=80 154307498b7757f718ce7b914aea8eab365597f8 7dd89729e0a5801ec4f7ceddb25ebda3f0eec9c1 -- src/main/ipc/pty/provider/shutdown-detect.ts src/main/ipc/pty/provider/liveness.ts src/main/ipc/pty/runtime/kill.ts src/main/ipc/pty-daemon-controller-teardown.test.ts src/main/ipc/pty/runtime/kill-same-id-respawn.test.tsRepository: stablyai/orca
Length of output: 41933
🏁 Script executed:
printf '%s\n' '--- ownership state and incarnation lifecycle ---'
nl -ba src/main/ipc/pty/provider/ownership-state.ts
rg -n -F --glob 'src/**/*.ts' -e 'ptyIncarnationById.set' -e 'ptyIncarnationById.delete' -e 'ptyIncarnationById.get' -e 'registerPty(' -e 'onPtySpawned' src/main/ipc/pty src/main/runtime | head -240
printf '%s\n' '--- provider contract and event payload ---'
nl -ba src/providers/pty-provider-contract.ts
nl -ba src/main/ipc/pty/provider/bind-listeners.ts | sed -n '70,135p'
nl -ba src/main/ipc/pty/provider/local-configure.ts | sed -n '115,150p'
printf '%s\n' '--- stop entrypoints and no-incarnation tests ---'
rg -n -F --glob 'src/**/*.ts' -e 'stopAndWaitPtyFromRuntimeController' -e 'stopAndWait(' -e 'stopAndWaitPty(' -e 'incarnationId: undefined' -e 'incarnationId: null' src/main/ipc/pty src/main/runtime | head -300
printf '%s\n' '--- exact relevant test blocks ---'
rg -n -F --glob 'src/main/**/*.test.ts' -e 'uses inventory, not an incarnation-less exit' -e 'skips the keep-history settle poll' -e 'incarnation-less exit' -e 'stopAndWait' src/main/ipc/pty-daemon-controller-teardown.test.ts src/main/ipc/pty/runtime src/main/ipc/pty | head -120Repository: stablyai/orca
Length of output: 41857
🏁 Script executed:
printf '%s\n' '--- locate provider contract and cleanup ---'
rg --files . | rg 'pty-provider-contract|state-cleanup|pty-provider'
nl -ba src/main/ipc/pty/provider/state-cleanup.ts
printf '%s\n' '--- registerPty implementation and spawn event ordering ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(' -e 'onPtySpawned(' -e 'ptyController.stopAndWait' -e 'stopAndWait(' | head -180
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty' -e 'onPtySpawned' | rg 'function|registerPty|onPtySpawned' | head -120
printf '%s\n' '--- stop workflows around sleep and terminal stop ---'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '130,190p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-stop.ts | sed -n '190,235p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-stop-targets.ts | sed -n '40,80p'
printf '%s\n' '--- runtime registration and spawn lifecycle definitions ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(id' -e 'registerPty(' -e 'onPtySpawned(id' -e 'onPtySpawned(' | head -160Repository: stablyai/orca
Length of output: 41749
🏁 Script executed:
printf '%s\n' '--- runtime registration owner ---'
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'class OrcaRuntimeService' -e 'registerPty(' -e 'onPtySpawned(' -e 'ptyIncarnation' | head -220
rg -n -F --glob 'src/main/runtime/**/*.ts' -e 'registerPty(' | rg 'pty-registration|pty-lifecycle|runtime-service|orca-runtime' | head -80
printf '%s\n' '--- launch flows around registration and spawn ---'
nl -ba src/main/runtime/terminal/launch/terminal-create.ts | sed -n '180,255p'
nl -ba src/main/runtime/terminal/launch/terminal-split-surface.ts | sed -n '80,130p'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '140,182p'
printf '%s\n' '--- lifecycle implementation files ---'
rg --files src/main/runtime | rg 'pty-registration|pty-lifecycle|runtime-service|orca-runtime'Repository: stablyai/orca
Length of output: 41826
🏁 Script executed:
printf '%s\n' '--- pty registration implementation ---'
nl -ba src/main/runtime/terminal/lifecycle/pty-registration.ts
printf '%s\n' '--- sleep live-record construction ---'
nl -ba src/main/runtime/terminal/stop/worktree-terminal-sleep.ts | sed -n '95,175p'
printf '%s\n' '--- normal launch registration exact context ---'
nl -ba src/main/runtime/terminal/launch/terminal-create.ts | sed -n '210,245p'
nl -ba src/main/runtime/terminal/launch/terminal-split-in-main.ts | sed -n '105,140p'
printf '%s\n' '--- restore/reattach registration exact context ---'
nl -ba src/main/runtime/terminal/identity/pty-worktree-record.ts | sed -n '105,145p'
nl -ba src/main/runtime/terminal/lifecycle/pty-handle-invalidation.ts | sed -n '110,175p'Repository: stablyai/orca
Length of output: 24466
Require an incarnation match before skipping keep-history settlement.
When ptyIncarnationById has no entry, shutdownProviderAndDetectExit accepts any exit with the target ID. stopAndWaitPty then skips the keepHistory settle poll after one absent inventory result. A late exit from an older same-ID PTY can therefore confirm the stop before the current incarnation settles. A different PTY ID cannot trigger this path because the producer checks payload.id === id.
Keep the immediate path for exits that match a captured incarnation. Use the settle poll when no incarnation was captured.
Suggested fix
}
try {
- if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) {
+ const exitObservedForCurrentIncarnation =
+ stoppingIncarnationId !== undefined && providerExitObserved
+ if (!(await verifyPtyStopped(provider, ptyId, opts, exitObservedForCurrentIncarnation))) {
runtime?.markPtyLivenessLive?.(ptyId)
return false
}📝 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.
| } | |
| } | |
| try { | |
| if (!(await verifyPtyStopped(provider, ptyId, opts))) { | |
| if (!(await verifyPtyStopped(provider, ptyId, opts, providerExitObserved))) { | |
| runtime?.markPtyLivenessLive?.(ptyId) | |
| return false | |
| } | |
| } | |
| } | |
| try { | |
| const exitObservedForCurrentIncarnation = | |
| stoppingIncarnationId !== undefined && providerExitObserved | |
| if (!(await verifyPtyStopped(provider, ptyId, opts, exitObservedForCurrentIncarnation))) { | |
| runtime?.markPtyLivenessLive?.(ptyId) | |
| return false | |
| } |
…cycle # Conflicts: # src/main/ipc/ephemeral-vm-runtime-handlers.ts # src/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.ts # src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts # src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts # src/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.ts
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:
26c8355a-731c-45cb-9802-0836bc74736d
⛔ Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing: 13
src/main/ephemeral-vm/workspace-suspend.tssrc/main/ipc/ephemeral-vm-managed-ssh.test.tssrc/main/ipc/ephemeral-vm-runtime-handlers.tssrc/main/ipc/ephemeral-vm.test.tssrc/main/runtime/acknowledged-terminal-tab-retirement-fixture.tssrc/main/runtime/orca-runtime-tests/mobile-session-tabs-windowless-sleep-resume.spec.tssrc/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.tssrc/main/runtime/session-lifecycle/projection/restored-pane-materialize.tssrc/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.test.tssrc/main/runtime/session-lifecycle/projection/sleep-record-pane-claim.tssrc/main/startup/main-process-runtime-service.tssrc/renderer/src/components/terminal-pane/pty-connection/deferred-cold-restore-and-snapshot.tssrc/renderer/src/i18n/locales/en.json
💤 Files with no reviewable changes: 1
- src/main/runtime/acknowledged-terminal-tab-retirement-fixture.ts
🚧 Files skipped from review as they are similar to previous changes: 1
- src/renderer/src/i18n/locales/en.json
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Resolve numeric pane keys during workspace wake. · restored-pane-materialize.ts:112-128
src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts:112-128
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winResolve numeric pane keys during workspace wake.
When a legacy numeric
paneKeyis present,resumeSleptWorkspaceAgentsselects the record butparsePaneKeyrejects it. TheflatMapthen drops the record, so its agent does not resume.Use
findPaneSleepingRecordfor the runtime panes. That existing path resolves numeric aliases and prevents one record from being assigned to multiple split panes.Suggested fix
-import { parsePaneKey } from '../../../../shared/stable-pane-id' +import { findPaneSleepingRecord } from './pane-sleeping-record' ... async resumeSleptWorkspaceAgents(worktreeId: string): Promise<void> { - const records = Object.values( - this.getWorkspaceSessionForWorktree(worktreeId)?.sleepingAgentSessionsByPaneKey ?? {} - ).filter( + const session = this.getWorkspaceSessionForWorktree(worktreeId) + if (!session) return + const records = new Set( + Object.values(session.sleepingAgentSessionsByPaneKey ?? {}).filter( (record) => worktreeIdsEqual(record.worktreeId, worktreeId) && isUnclaimedRunningSleepRecord(record) + ) ) - const panes = records.flatMap((record) => { - const pane = parsePaneKey(record.paneKey) - return pane ? [{ worktreeId, tabId: pane.tabId, leafId: pane.leafId }] : [] - }) + const panes = buildHeadlessMobileSessionTerminalTabs( + worktreeId, + session.tabsByWorktree?.[worktreeId] ?? [], + session + ).flatMap(({ parentTabId, leafId }) => { + const found = findPaneSleepingRecord(session, worktreeId, { parentTabId, leafId }) + return found && records.has(found.record) + ? [{ worktreeId, tabId: parentTabId, leafId }] + : [] + })
ℹ️ Review info
⚙️ Run configuration
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
a1de15c8-0d95-4415-84c3-e876ca6356a3
📒 Files selected for processing: 1
src/main/runtime/session-lifecycle/projection/restored-pane-materialize.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.
…op sleep adapter, and open the workspace after its sleep starts
…later specs share
…cycle # Conflicts: # src/main/runtime/owned-session-teardown-policy.test.ts # src/main/runtime/session-lifecycle/projection/apply-mobile-session-tab-navigation.ts
…and main typechecks again (#32168) #32018 pushed headless-terminal-tab-close.ts over the 300-line lint limit, and its new test passed sleepWorktree to RuntimeNotifier after #31564 removed it. setMobileSessionTabProps moves unchanged into RuntimeHeadlessTabProperties, which already owns the helpers it calls and directly extends the close class.
ELI5
Putting a workspace to sleep used to be run by the desktop window: it stopped the terminals, chats and VM itself, and a phone's sleep had to ask the window to do it. Now the desktop's main process runs every sleep, whoever asked (sidebar, phone or CLI), and the window follows along by hiding the workspace's panes and browsers.
Waking works the same way. Main records which agents to resume and is the only thing that resumes them, each in its own pane. The window just connects to the terminal main started. The window's own sleep code and its "relaunch in a new tab" wake are deleted.
The menu and toasts are unchanged, including the "kept open" message when the host cannot confirm the terminals stopped. A phone's or the CLI's failed sleep still shows that toast on the desktop. One message is new: when only the VM fails to suspend, the toast now says the terminals stopped but the machine is still running, instead of "kept open".
What Changed
Main runs sleep (
workspace-sleep-execution.ts,workspace-sleep-runtime.ts)worktrees:sleep, plusworktrees:sleepReopened(the user opened the workspace mid-sleep, so its VM keeps running). A phone's activation sends the same signal to main.Wake has one executor: main (
restored-pane-materialize.ts,apply-mobile-session-tab-navigation.ts,main-resumed-sleep-pane.ts)worktrees:resumeSleptPane). The pane's own start then connects to main's terminal instead of launching anything.Failure messages
The window follows (
workspace-sleep-follower.ts)Phone, CLI and web
worktree.sleepis answered as soon as the sleep is accepted. The phone never reads the reply, and stopping chats can take longer than the phone's 30-second request timeout. A phone's activation no longer waits on a queued wake.worktree.sleeptakes an optionalmarkChatsAsleepand replies with the chat receipts; older hosts ignore the field. The web client passes it, keeps the receipts, and waits past the two-minute chat stop.Deleted
terminal-shutdown.tsand its tests (ported toremote-workspace-terminal-sleep.test.tsin main), andsleep-workspace-chat-stop.ts.ephemeralVm:suspendWorkspace).worktree-sleep-request-relay.ts,shared/worktree-sleep-request.ts).sleepWorktreenotifier and the admission test for the old flow.remote-worktree-sleep.tsmoved from the renderer into main.Other
Why
Linked Issue
Part of the sidebar one-owner design (P3b). Based on main, now that #31553 (P3) has merged. The sleeping glyph and Hide sleeping move onto main's feed in a follow-up PR stacked on this one.
Visual Proof
N/A for new UI: the menu, toasts and copy are unchanged. The disconnected-SSH e2e checks the kept-open toast on screen.
Testing
pnpm tcandpnpm typecheck:e2epass. The headless server build (build:orcad-template) passes. It caught Electron leaking into the server's sleep path, which is fixed here.worktree-active-delete-scroll-positionfails, and it also fails on the base.This PR still shows no immediate hint on sleep. The follow-up PR hides the row when the sleep is requested.
An earlier version was slower: main's stop waited a further second after it had already seen each terminal exit. That wait now runs only when no exit was seen. The phone reply arrives in about 25 ms on both.
--dangerously-skip-permissions). The base dropped them on resume.agent-descendant-process-killalternated base and this PR on one machine, 10 runs each: base 10/10, this PR 9/10. The one failure was a detached child that survived the kill. That kill path has no changes in this PR, and 20 more runs of this PR with tracing all passed.What runs with the switch off:
Only the queue and the queued phone wake are behind the switch.
Wake e2e (new,
workspace-sleep-main-resume.spec.ts): a slept workspace with two agent panes and a window open. The visible pane resumes once in place, the hidden one only when opened, no new tab appears, and main still holds both records. The same spec fails on the base: the window resumes from its own copy and its save erases main's record.Unit tests cover the window save that omits or clears the field, the headless wake report by record kind, and a slept pane opened twice and after a window reload (one resume, the second open attaches).
Wake-related e2e (12 tests) and disconnected SSH pass again after the wake change. The related unit set passes: 71 files and 720 tests.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Not done
Notes
SSH: a paired server that cannot be reached leaves the sleep unconfirmed; it never counts as stopped. Older paired servers without the new sleep method fall back to the old one. Folder workspaces sleep the same way.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)