From 1bdbc2364801a30d33b62c49247dc22b93b497e1 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:11:03 -0700 Subject: [PATCH] refactor(runtime): check session graphs and tab lifecycle (#27694) --- config/main-layout-writer-baseline.txt | 6 +- .../pr-e2e-layout-oracle-routing.test.mjs | 5 +- config/scripts/pr-layout-oracle-route.mjs | 4 + config/ts-nocheck-baseline.txt | 17 - src/main/agent-workspace-trust-spawn.ts | 2 +- .../terminal-stale-pane-tree-client.test.ts | 2 +- .../runtime/orca-runtime-attach-window.ts | 98 ------ ...ld-headless-mobile-session-browser-tabs.ts | 2 +- .../runtime/orca-runtime-file-commands.ts | 2 +- .../orca-runtime-list-managed-worktrees.ts | 2 +- ...runtime-persist-headless-terminal-title.ts | 2 +- ...le-headless-mobile-session-browser-tabs.ts | 2 +- ...ser-network-execution-host-for-worktree.ts | 2 +- ...me-schedule-mobile-session-tabs-changed.ts | 2 +- .../runtime/orca-runtime-split-terminal.ts | 2 +- .../worktree-removal-execution-host.spec.ts | 2 +- .../orca-runtime-wait-for-leaf-pty-id.ts | 2 +- ...runtime-write-orchestration-pointer-pty.ts | 2 +- ...rer-browser-session-reconciliation.test.ts | 2 +- ...ime-migration-catalog-capabilities.test.ts | 6 +- src/main/runtime/runtime-store-contract.ts | 2 + .../session-graph-authority.ts} | 38 +- .../session-graph/session-graph-promotion.ts | 192 ++++++++++ .../session-graph-reload-lifecycle.ts} | 60 +++- .../session-window-graph.ts} | 14 +- .../explicit-terminal-close-stops.ts} | 17 +- .../session-tabs/headless-tab-properties.ts | 2 +- .../headless-terminal-tab-close.ts} | 59 +++- .../mobile-session-hydration.ts} | 86 ++++- .../mobile-session-preservation.ts} | 22 +- ...obile-session-publication-preservation.ts} | 25 +- .../mobile-session-reconciliation.ts} | 45 ++- .../renderer-session-terminal-rescue.ts} | 18 +- .../session-tabs/restored-chat-tab-changes.ts | 30 ++ .../session-tab-prototype-dispatch.test.ts | 328 +++++++++++++++++- .../session-tabs-inventory-publication.ts | 2 +- .../session-tabs-inventory.ts} | 85 ++++- .../structured-session-create-support.ts} | 52 +-- .../structured-session-tab-close.ts} | 53 ++- .../structured-session-tab-restoration.ts} | 77 ++-- .../terminal-surface-retirements.ts} | 27 +- .../lifecycle/floating-session-liveness.ts} | 16 +- .../wait/terminal-interactive-wait.ts | 2 +- .../runtime/workspaces/workspace-selectors.ts | 2 +- src/shared/tui-agent-launch-defaults.ts | 8 +- 45 files changed, 1091 insertions(+), 335 deletions(-) delete mode 100644 src/main/runtime/orca-runtime-attach-window.ts rename src/main/runtime/{orca-runtime-transition-graph-reload-to-terminal-state.ts => session-graph/session-graph-authority.ts} (78%) create mode 100644 src/main/runtime/session-graph/session-graph-promotion.ts rename src/main/runtime/{orca-runtime-has-terminals-for-worktree.ts => session-graph/session-graph-reload-lifecycle.ts} (67%) rename src/main/runtime/{orca-runtime-sync-window-graph.ts => session-graph/session-window-graph.ts} (96%) rename src/main/runtime/{orca-runtime-stop-explicitly-closed-tab-ptys.ts => session-tabs/explicit-terminal-close-stops.ts} (93%) rename src/main/runtime/{orca-runtime-close-headless-mobile-terminal-tab.ts => session-tabs/headless-terminal-tab-close.ts} (75%) rename src/main/runtime/{orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session.ts => session-tabs/mobile-session-hydration.ts} (76%) rename src/main/runtime/{orca-runtime-merge-preserved-headless-mobile-session-tabs.ts => session-tabs/mobile-session-preservation.ts} (88%) rename src/main/runtime/{orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab.ts => session-tabs/mobile-session-publication-preservation.ts} (86%) rename src/main/runtime/{orca-runtime-sync-mobile-session-tabs.ts => session-tabs/mobile-session-reconciliation.ts} (83%) rename src/main/runtime/{orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts => session-tabs/renderer-session-terminal-rescue.ts} (87%) create mode 100644 src/main/runtime/session-tabs/restored-chat-tab-changes.ts rename src/main/runtime/{orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts => session-tabs/session-tabs-inventory.ts} (61%) rename src/main/runtime/{orca-runtime-get-structured-agent-session-create-support.ts => session-tabs/structured-session-create-support.ts} (86%) rename src/main/runtime/{orca-runtime-close-structured-agent-session-tab.ts => session-tabs/structured-session-tab-close.ts} (80%) rename src/main/runtime/{orca-runtime-restore-structured-agent-session-tabs-once.ts => session-tabs/structured-session-tab-restoration.ts} (82%) rename src/main/runtime/{orca-runtime-persist-terminal-surface-retirements.ts => session-tabs/terminal-surface-retirements.ts} (84%) rename src/main/runtime/{orca-runtime-refresh-floating-workspace-pty-liveness.ts => terminal/lifecycle/floating-session-liveness.ts} (90%) diff --git a/config/main-layout-writer-baseline.txt b/config/main-layout-writer-baseline.txt index 7da39e86bb9..2df2e2cc215 100644 --- a/config/main-layout-writer-baseline.txt +++ b/config/main-layout-writer-baseline.txt @@ -54,12 +54,12 @@ 3 src/main/runtime/mobile-session-layout-projection.ts 2 src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts 2 src/main/runtime/orca-runtime-apply-mobile-session-tab-navigation.ts -2 src/main/runtime/orca-runtime-attach-window.ts +2 src/main/runtime/session-graph/session-graph-promotion.ts 2 src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts -1 src/main/runtime/orca-runtime-close-structured-agent-session-tab.ts +1 src/main/runtime/session-tabs/structured-session-tab-close.ts 1 src/main/runtime/orca-runtime-get-runtime-id.ts 2 src/main/runtime/orca-runtime-persist-headless-terminal-title.ts -1 src/main/runtime/orca-runtime-persist-terminal-surface-retirements.ts +1 src/main/runtime/session-tabs/terminal-surface-retirements.ts 1 src/main/runtime/orca-runtime-pty-foreground-process-reads.ts 3 src/main/runtime/orca-runtime-stop-terminals-for-worktree.ts 1 src/main/runtime/orca-runtime-terminal-drivers.ts diff --git a/config/scripts/pr-e2e-layout-oracle-routing.test.mjs b/config/scripts/pr-e2e-layout-oracle-routing.test.mjs index 41b27c68e21..a7944d3eec6 100644 --- a/config/scripts/pr-e2e-layout-oracle-routing.test.mjs +++ b/config/scripts/pr-e2e-layout-oracle-routing.test.mjs @@ -37,7 +37,10 @@ const STACK_WRITERS = [ 'src/renderer/src/runtime/sync-runtime-graph.ts', 'src/renderer/src/runtime/web-session-tabs-sync/store-patch.ts', 'src/main/runtime/orca-runtime-split-terminal.ts', - 'src/main/runtime/orca-runtime-sync-window-graph.ts', + 'src/main/runtime/session-graph/session-window-graph.ts', + 'src/main/runtime/session-tabs/mobile-session-hydration.ts', + 'src/main/runtime/session-tabs/mobile-session-reconciliation.ts', + 'src/main/runtime/session-tabs/mobile-session-preservation.ts', 'src/main/runtime/mobile-session-layout-projection.ts', 'src/main/runtime/headless-runtime-graph.ts', 'src/main/persistence/loading-store/session-host-partitions.ts', diff --git a/config/scripts/pr-layout-oracle-route.mjs b/config/scripts/pr-layout-oracle-route.mjs index ccf472417bc..8af3645833f 100644 --- a/config/scripts/pr-layout-oracle-route.mjs +++ b/config/scripts/pr-layout-oracle-route.mjs @@ -38,6 +38,10 @@ const LAYOUT_ORACLE_PREFIXES = [ 'src/main/runtime/terminal-surface-close.ts', 'src/main/runtime/terminal/output/terminal-derived-agent-facts.ts', 'src/main/runtime/terminal/output/terminal-output-generation.ts', + 'src/main/runtime/session-graph/session-window-graph.ts', + 'src/main/runtime/session-tabs/mobile-session-hydration.ts', + 'src/main/runtime/session-tabs/mobile-session-reconciliation.ts', + 'src/main/runtime/session-tabs/mobile-session-preservation.ts', 'src/renderer/src/components/terminal/terminal-tab-actions.ts', 'src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts', 'src/shared/rpc-contract/workspace-layout-params.ts', diff --git a/config/ts-nocheck-baseline.txt b/config/ts-nocheck-baseline.txt index cf4eb2ba233..35b0643c8b3 100644 --- a/config/ts-nocheck-baseline.txt +++ b/config/ts-nocheck-baseline.txt @@ -5,13 +5,9 @@ # Regenerate/prune: pnpm check:ts-nocheck-ratchet --prune (removes stale entries only) src/main/runtime/orca-runtime-adopt-terminal-orphans-from-inventory.ts src/main/runtime/orca-runtime-apply-mobile-session-tab-navigation.ts -src/main/runtime/orca-runtime-attach-window.ts src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts src/main/runtime/orca-runtime-build-pty-terminal-summary.ts -src/main/runtime/orca-runtime-close-headless-mobile-terminal-tab.ts src/main/runtime/orca-runtime-close-mobile-session-tab.ts -src/main/runtime/orca-runtime-close-structured-agent-session-tab.ts -src/main/runtime/orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts src/main/runtime/orca-runtime-controller-knows-pty-is-live.ts src/main/runtime/orca-runtime-create-agent-session.ts src/main/runtime/orca-runtime-create-mobile-session-terminal.ts @@ -26,29 +22,23 @@ src/main/runtime/orca-runtime-focus-terminal.ts src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts src/main/runtime/orca-runtime-get-pty-record-for-pane-key.ts src/main/runtime/orca-runtime-get-runtime-id.ts -src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts src/main/runtime/orca-runtime-get-worktree-ps.ts src/main/runtime/orca-runtime-get-worktree-terminal-provisioning-host.ts src/main/runtime/orca-runtime-has-exact-persisted-terminal-surface-identity.ts src/main/runtime/orca-runtime-has-live-or-persisted-serve-or-ssh-owned-pty-binding.ts -src/main/runtime/orca-runtime-has-terminals-for-worktree.ts -src/main/runtime/orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session.ts src/main/runtime/orca-runtime-invalidate-all-handles-for-pty.ts src/main/runtime/orca-runtime-linear-commands.ts src/main/runtime/orca-runtime-list-managed-worktrees.ts -src/main/runtime/orca-runtime-merge-preserved-headless-mobile-session-tabs.ts src/main/runtime/orca-runtime-notify-ssh-state-changed.ts src/main/runtime/orca-runtime-on-pty-exit.ts src/main/runtime/orca-runtime-perform-mobile-session-pty-records-refresh.ts src/main/runtime/orca-runtime-persist-headless-terminal-title.ts -src/main/runtime/orca-runtime-persist-terminal-surface-retirements.ts src/main/runtime/orca-runtime-prepare-pty-execution-context.ts src/main/runtime/orca-runtime-preserved-branch-cleanup.ts src/main/runtime/orca-runtime-prune-mobile-session-tab-group-layout.ts src/main/runtime/orca-runtime-pty-foreground-process-reads.ts src/main/runtime/orca-runtime-publish-pty-backed-mobile-session-terminal.ts src/main/runtime/orca-runtime-reconcile-headless-mobile-session-browser-tabs.ts -src/main/runtime/orca-runtime-refresh-floating-workspace-pty-liveness.ts src/main/runtime/orca-runtime-refresh-pty-worktree-records-from-controller.ts src/main/runtime/orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts src/main/runtime/orca-runtime-refuse-unattributed-mobile-session-tab-close.ts @@ -57,8 +47,6 @@ src/main/runtime/orca-runtime-resolve-browser-network-execution-host-for-worktre src/main/runtime/orca-runtime-resolve-known-workspace-file-target.ts src/main/runtime/orca-runtime-resolve-terminal-pane.ts src/main/runtime/orca-runtime-resolve-terminal-split-source-authority.ts -src/main/runtime/orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts -src/main/runtime/orca-runtime-restore-structured-agent-session-tabs-once.ts src/main/runtime/orca-runtime-run-create-mobile-session-terminal.ts src/main/runtime/orca-runtime-runtime-id.ts src/main/runtime/orca-runtime-schedule-mobile-session-tabs-changed.ts @@ -68,16 +56,11 @@ src/main/runtime/orca-runtime-split-pty-backed-terminal.ts src/main/runtime/orca-runtime-split-terminal.ts src/main/runtime/orca-runtime-state-fields.ts src/main/runtime/orca-runtime-stop-exact-terminals-for-worktree.ts -src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts src/main/runtime/orca-runtime-stop-requested-pty-ids.ts src/main/runtime/orca-runtime-stop-terminals-for-worktree.ts -src/main/runtime/orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab.ts -src/main/runtime/orca-runtime-sync-mobile-session-tabs.ts -src/main/runtime/orca-runtime-sync-window-graph.ts src/main/runtime/orca-runtime-terminal-create-deduplication.ts src/main/runtime/orca-runtime-terminal-drivers.ts src/main/runtime/orca-runtime-touch-mobile-session-tabs-for-worktree.ts -src/main/runtime/orca-runtime-transition-graph-reload-to-terminal-state.ts src/main/runtime/orca-runtime-verify-orchestration-compatibility-caller.ts src/main/runtime/orca-runtime-wait-for-leaf-pty-id.ts src/main/runtime/orca-runtime-wait-for-mobile-terminal-surface.ts diff --git a/src/main/agent-workspace-trust-spawn.ts b/src/main/agent-workspace-trust-spawn.ts index f893529b53b..8d901951bbb 100644 --- a/src/main/agent-workspace-trust-spawn.ts +++ b/src/main/agent-workspace-trust-spawn.ts @@ -9,7 +9,7 @@ import { import { AGENT_TRUST_KEYED_BY_START_FOLDER } from './execution-host-workspace-trust' type AgentWorkspaceTrustSetting = - | Pick + | Partial> | null | undefined diff --git a/src/main/persistence/terminal-topology/terminal-stale-pane-tree-client.test.ts b/src/main/persistence/terminal-topology/terminal-stale-pane-tree-client.test.ts index 61a06bac146..8393d03a622 100644 --- a/src/main/persistence/terminal-topology/terminal-stale-pane-tree-client.test.ts +++ b/src/main/persistence/terminal-topology/terminal-stale-pane-tree-client.test.ts @@ -6,7 +6,7 @@ import { LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' import type { TerminalPaneLayoutNode } from '../../../shared/terminal-tab-types' import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' import { TEST_LEAF_1, TEST_LEAF_2 } from '../../persistence-session-fixtures' -import type { OrcaRuntimeWithCloseHeadlessMobileTerminalTab } from '../../runtime/orca-runtime-close-headless-mobile-terminal-tab' +import type { OrcaRuntimeWithCloseHeadlessMobileTerminalTab } from '../../runtime/session-tabs/headless-terminal-tab-close' import { OrcaRuntimeService } from '../../runtime/orca-runtime' import type { Store } from '../loading-store/store' import { closeLeafOrTab } from './terminal-topology-commit' diff --git a/src/main/runtime/orca-runtime-attach-window.ts b/src/main/runtime/orca-runtime-attach-window.ts deleted file mode 100644 index fea1683adb0..00000000000 --- a/src/main/runtime/orca-runtime-attach-window.ts +++ /dev/null @@ -1,98 +0,0 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithNotifySshStateChanged } from './orca-runtime-notify-ssh-state-changed' -import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-types' -import type { ExecutionHostId } from '../../shared/execution-host' -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' -import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records' - -export class OrcaRuntimeWithAttachWindow extends OrcaRuntimeWithNotifySshStateChanged { - attachWindow(windowId: number): void { - if (this.authoritativeWindowId === HEADLESS_RUNTIME_WINDOW_ID) { - if ( - this.pendingHeadlessPromotionWindowId !== null && - windowId !== this.pendingHeadlessPromotionWindowId - ) { - return - } - // Why: promotion is a renderer reload of the same graph owner, not a new - // runtime; stale handles must transition before the real window publishes. - this.persistWindowlessPtyBindingsForDesktopAttach() - this.pendingHeadlessPromotionWindowId = windowId - this.authoritativeWindowId = windowId - this.beginGraphReload(windowId) - return - } - if (this.authoritativeWindowId === null) { - // Why: a promoted serve can close and later reopen its window while new - // background PTYs keep arriving; every windowless gap needs this handoff. - this.persistWindowlessPtyBindingsForDesktopAttach() - this.authoritativeWindowId = windowId - } - } - - protected persistWindowlessPtyBindingsForDesktopAttach(): void { - if (!this.store?.getWorkspaceSession || !this.store.setWorkspaceSession) { - return - } - const partitions = new Map< - ExecutionHostId, - { session: WorkspaceSessionState; ptys: RuntimePtyWorktreeRecord[] } - >() - for (const pty of this.ptysById.values()) { - if (!pty.connected || !pty.tabId) { - continue - } - const hostId = this.getWorkspaceSessionHostIdForWorktree(pty.worktreeId) - const session = this.store.getWorkspaceSession(hostId) - const tab = session.tabsByWorktree[pty.worktreeId]?.find( - (candidate) => candidate.id === pty.tabId - ) - if (!tab) { - continue - } - const layoutPtyIds = Object.values( - session.terminalLayoutsByTabId[pty.tabId]?.ptyIdsByLeafId ?? {} - ) - if (tab.ptyId !== pty.ptyId && !layoutPtyIds.includes(pty.ptyId)) { - continue - } - const partition = partitions.get(hostId) ?? { session, ptys: [] } - partition.ptys.push(pty) - partitions.set(hostId, partition) - } - - for (const [hostId, { session, ptys }] of partitions) { - // Why: windowless SSH PTYs must be handed to the desktop through their SSH partition, never the local session. - const activeWorktreeIdsOnShutdown = [ - ...new Set([ - ...(session.activeWorktreeIdsOnShutdown ?? []), - ...ptys.map((pty) => pty.worktreeId) - ]) - ] - const activeConnectionIdsAtShutdown = [ - ...new Set([ - ...(session.activeConnectionIdsAtShutdown ?? []), - ...ptys - .map((pty) => pty.connectionId) - .filter((connectionId): connectionId is string => connectionId !== null) - ]) - ] - const remoteSessionIdsByTabId = { ...session.remoteSessionIdsByTabId } - for (const pty of ptys) { - if (pty.connectionId && pty.tabId) { - remoteSessionIdsByTabId[pty.tabId] = pty.ptyId - } - } - - this.store.setWorkspaceSession( - { - ...session, - activeWorktreeIdsOnShutdown, - ...(activeConnectionIdsAtShutdown.length > 0 ? { activeConnectionIdsAtShutdown } : {}), - ...(Object.keys(remoteSessionIdsByTabId).length > 0 ? { remoteSessionIdsByTabId } : {}) - }, - hostId - ) - } - } -} diff --git a/src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts b/src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts index f74c1154008..e314ccff4b1 100644 --- a/src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts +++ b/src/main/runtime/orca-runtime-build-headless-mobile-session-browser-tabs.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithPersistTerminalSurfaceRetirements } from './orca-runtime-persist-terminal-surface-retirements' +import { OrcaRuntimeWithPersistTerminalSurfaceRetirements } from './session-tabs/terminal-surface-retirements' import type { RuntimeMobileSessionBrowserTab, RuntimeMobileSessionTabsResult, diff --git a/src/main/runtime/orca-runtime-file-commands.ts b/src/main/runtime/orca-runtime-file-commands.ts index fc9a386b3d1..00d0b04c807 100644 --- a/src/main/runtime/orca-runtime-file-commands.ts +++ b/src/main/runtime/orca-runtime-file-commands.ts @@ -33,7 +33,7 @@ import type { ResolvedWorktree } from './runtime-worktree-path-identity' import type { Repo } from '../../shared/repo-types' export abstract class OrcaRuntimeWithFileCommands extends OrcaRuntimeWithPreservedBranchCleanup { - protected abstract requireStore(): RuntimeStore + abstract requireStore(): RuntimeStore protected abstract resolveWorktreeSelector(selector: string): Promise diff --git a/src/main/runtime/orca-runtime-list-managed-worktrees.ts b/src/main/runtime/orca-runtime-list-managed-worktrees.ts index aa8fbe17e97..9d7fd618d5d 100644 --- a/src/main/runtime/orca-runtime-list-managed-worktrees.ts +++ b/src/main/runtime/orca-runtime-list-managed-worktrees.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce } from './orca-runtime-restore-structured-agent-session-tabs-once' +import { OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce } from './session-tabs/structured-session-tab-restoration' import { DEFAULT_WORKTREE_LIST_LIMIT } from './orca-runtime-postlude' import type { RuntimeWorktreeListResult } from '../../shared/runtime-types' import type { DetectedWorktreeListResult, Worktree } from '../../shared/worktree/types' diff --git a/src/main/runtime/orca-runtime-persist-headless-terminal-title.ts b/src/main/runtime/orca-runtime-persist-headless-terminal-title.ts index 301b2a35d77..33bb80e240e 100644 --- a/src/main/runtime/orca-runtime-persist-headless-terminal-title.ts +++ b/src/main/runtime/orca-runtime-persist-headless-terminal-title.ts @@ -194,7 +194,7 @@ export abstract class OrcaRuntimeWithPersistHeadlessTerminalTitle extends Runtim // Why: same defect as `resolveRuntimeGitTarget` above, in ~30 filesystem dispatches. `getRepo(id)` // is host-blind and never read `worktree.hostId`, and the `connectionId` it returned spelled // "runtime host", "unresolved" and "genuinely local" all as `undefined` (#11163). - protected async resolveRuntimeFileTarget(worktreeSelector: string): Promise<{ + async resolveRuntimeFileTarget(worktreeSelector: string): Promise<{ worktree: ResolvedWorktree executionHostId: ExecutionHostId }> { diff --git a/src/main/runtime/orca-runtime-reconcile-headless-mobile-session-browser-tabs.ts b/src/main/runtime/orca-runtime-reconcile-headless-mobile-session-browser-tabs.ts index 096aebf7bf0..1ed5c9c6fc2 100644 --- a/src/main/runtime/orca-runtime-reconcile-headless-mobile-session-browser-tabs.ts +++ b/src/main/runtime/orca-runtime-reconcile-headless-mobile-session-browser-tabs.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithHydrateHeadlessMobileSessionTabsFromWorkspaceSession } from './orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session' +import { OrcaRuntimeWithHydrateHeadlessMobileSessionTabsFromWorkspaceSession } from './session-tabs/mobile-session-hydration' import type { RuntimeMobileSessionBrowserTab, RuntimeMobileSessionSnapshotTab, diff --git a/src/main/runtime/orca-runtime-resolve-browser-network-execution-host-for-worktree.ts b/src/main/runtime/orca-runtime-resolve-browser-network-execution-host-for-worktree.ts index b3a33f356f1..1073782474b 100644 --- a/src/main/runtime/orca-runtime-resolve-browser-network-execution-host-for-worktree.ts +++ b/src/main/runtime/orca-runtime-resolve-browser-network-execution-host-for-worktree.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithTransitionGraphReloadToTerminalState } from './orca-runtime-transition-graph-reload-to-terminal-state' +import { OrcaRuntimeWithTransitionGraphReloadToTerminalState } from './session-graph/session-graph-authority' import type { ExecutionHostId } from '../../shared/execution-host' import { floatingWorkspaceToWorktree, diff --git a/src/main/runtime/orca-runtime-schedule-mobile-session-tabs-changed.ts b/src/main/runtime/orca-runtime-schedule-mobile-session-tabs-changed.ts index 17e0e2872c3..75acde4d898 100644 --- a/src/main/runtime/orca-runtime-schedule-mobile-session-tabs-changed.ts +++ b/src/main/runtime/orca-runtime-schedule-mobile-session-tabs-changed.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab } from './orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab' +import { OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab } from './session-tabs/mobile-session-publication-preservation' import type { BrowserTabInfo, RuntimeMobileSessionClientTab, diff --git a/src/main/runtime/orca-runtime-split-terminal.ts b/src/main/runtime/orca-runtime-split-terminal.ts index 7dc006ef24e..a782c515e39 100644 --- a/src/main/runtime/orca-runtime-split-terminal.ts +++ b/src/main/runtime/orca-runtime-split-terminal.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithStopExplicitlyClosedTabPtys } from './orca-runtime-stop-explicitly-closed-tab-ptys' +import { OrcaRuntimeWithStopExplicitlyClosedTabPtys } from './session-tabs/explicit-terminal-close-stops' import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry' import type { RuntimeTerminalSplit } from '../../shared/runtime-types' import { randomUUID } from 'node:crypto' diff --git a/src/main/runtime/orca-runtime-tests/worktree-removal-execution-host.spec.ts b/src/main/runtime/orca-runtime-tests/worktree-removal-execution-host.spec.ts index e3a7c88f169..8395212c755 100644 --- a/src/main/runtime/orca-runtime-tests/worktree-removal-execution-host.spec.ts +++ b/src/main/runtime/orca-runtime-tests/worktree-removal-execution-host.spec.ts @@ -149,7 +149,7 @@ class RemovalRuntime extends OrcaRuntimeService { return super.localRemovalJobHost(runtimeStore) } - protected override requireStore(): RuntimeStore { + override requireStore(): RuntimeStore { if (this.stopAtStore) { throw this.helperFailure } diff --git a/src/main/runtime/orca-runtime-wait-for-leaf-pty-id.ts b/src/main/runtime/orca-runtime-wait-for-leaf-pty-id.ts index 18a300c117e..3c6c728c4c5 100644 --- a/src/main/runtime/orca-runtime-wait-for-leaf-pty-id.ts +++ b/src/main/runtime/orca-runtime-wait-for-leaf-pty-id.ts @@ -1,5 +1,5 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals } from './orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals' +import { OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals } from './session-tabs/renderer-session-terminal-rescue' import type { TerminalOscLinkRange } from '../../shared/terminal-osc-link-ranges' export class OrcaRuntimeWithWaitForLeafPtyId extends OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals { diff --git a/src/main/runtime/orca-runtime-write-orchestration-pointer-pty.ts b/src/main/runtime/orca-runtime-write-orchestration-pointer-pty.ts index 93e5927d126..2237d61f61c 100644 --- a/src/main/runtime/orca-runtime-write-orchestration-pointer-pty.ts +++ b/src/main/runtime/orca-runtime-write-orchestration-pointer-pty.ts @@ -1,6 +1,6 @@ // @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. import { isRemoteRuntimePtyId } from '../../shared/remote-runtime-pty-id' -import { OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness } from './orca-runtime-refresh-floating-workspace-pty-liveness' +import { OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness } from './terminal/lifecycle/floating-session-liveness' import { writeOrchestrationPointerWithSettlement } from './orchestration/mailbox-pointer-pty-write' import type { WriteSettlement } from '../../shared/pty-write-settlement' import type { RuntimeLeafRecord } from './runtime-terminal-state-records' diff --git a/src/main/runtime/renderer-browser-session-reconciliation.test.ts b/src/main/runtime/renderer-browser-session-reconciliation.test.ts index 7dc929c231d..a11874046fa 100644 --- a/src/main/runtime/renderer-browser-session-reconciliation.test.ts +++ b/src/main/runtime/renderer-browser-session-reconciliation.test.ts @@ -3,7 +3,7 @@ import type { RuntimeMobileSessionBrowserTab, RuntimeMobileSessionTabsSnapshot } from '../../shared/runtime-types' -import { OrcaRuntimeWithCloseStructuredAgentSessionTab } from './orca-runtime-close-structured-agent-session-tab' +import { OrcaRuntimeWithCloseStructuredAgentSessionTab } from './session-tabs/structured-session-tab-close' import { OrcaRuntimeWithReconcileHeadlessMobileSessionBrowserTabs } from './orca-runtime-reconcile-headless-mobile-session-browser-tabs' const rendererPage: RuntimeMobileSessionBrowserTab = { diff --git a/src/main/runtime/runtime-migration-catalog-capabilities.test.ts b/src/main/runtime/runtime-migration-catalog-capabilities.test.ts index 76f079eb116..df24611a040 100644 --- a/src/main/runtime/runtime-migration-catalog-capabilities.test.ts +++ b/src/main/runtime/runtime-migration-catalog-capabilities.test.ts @@ -28,7 +28,7 @@ describe('runtime migration catalog capabilities', () => { it('re-enters requireStore after checking the captured store capabilities', () => { const seen: OrcaRuntimeService[] = [] class Probe extends OrcaRuntimeService { - protected override requireStore(): RuntimeStore { + override requireStore(): RuntimeStore { seen.push(this) return super.requireStore() } @@ -41,7 +41,7 @@ describe('runtime migration catalog capabilities', () => { it('retains the native missing-method error on a different returned store', () => { const seen: OrcaRuntimeService[] = [] class Probe extends OrcaRuntimeService { - protected override requireStore(): RuntimeStore { + override requireStore(): RuntimeStore { seen.push(this) return store } @@ -54,7 +54,7 @@ describe('runtime migration catalog capabilities', () => { it('mutates the returned store before its native missing-flush rejection', async () => { const stage = vi.fn(() => staged) class Probe extends OrcaRuntimeService { - protected override requireStore(): RuntimeStore { + override requireStore(): RuntimeStore { return { ...store, stageOrcadMigrationCatalog: stage } } } diff --git a/src/main/runtime/runtime-store-contract.ts b/src/main/runtime/runtime-store-contract.ts index 9528b78bf88..035a0499d94 100644 --- a/src/main/runtime/runtime-store-contract.ts +++ b/src/main/runtime/runtime-store-contract.ts @@ -88,6 +88,8 @@ export type RuntimeStore = { getMobileClientTabSelections?: Store['getMobileClientTabSelections'] setMobileClientTabSelections?: Store['setMobileClientTabSelections'] getSettings(): { + agentWorkspaceTrustEnabled?: GlobalSettings['agentWorkspaceTrustEnabled'] + codexManagedAccounts?: GlobalSettings['codexManagedAccounts'] nestedWorkerMaxDepth?: GlobalSettings['nestedWorkerMaxDepth'] workspaceDir: string nestWorkspaces: boolean diff --git a/src/main/runtime/orca-runtime-transition-graph-reload-to-terminal-state.ts b/src/main/runtime/session-graph/session-graph-authority.ts similarity index 78% rename from src/main/runtime/orca-runtime-transition-graph-reload-to-terminal-state.ts rename to src/main/runtime/session-graph/session-graph-authority.ts index 05701907f64..fe3faee892a 100644 --- a/src/main/runtime/orca-runtime-transition-graph-reload-to-terminal-state.ts +++ b/src/main/runtime/session-graph/session-graph-authority.ts @@ -1,23 +1,31 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithHasTerminalsForWorktree } from './orca-runtime-has-terminals-for-worktree' -import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-types' -import type { FolderWorkspace } from '../../shared/folder-workspace-types' +import type { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree as ResolveBrowserNetworkExecutionHostForWorktreeMethods } from '../orca-runtime-resolve-browser-network-execution-host-for-worktree' +import { OrcaRuntimeWithHasTerminalsForWorktree } from './session-graph-reload-lifecycle' +import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-types' +import type { FolderWorkspace } from '../../../shared/folder-workspace-types' import { assertFolderWorkspacePathUsable, getFolderWorkspacePathStatus, inferFolderWorkspacePathConnection -} from '../project-groups/folder-workspace-path-status' -import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types' -import { folderWorkspaceKey, parseWorkspaceKey } from '../../shared/workspace-scope' -import type { ResolvedWorktree } from './runtime-worktree-path-identity' -import type { BrowserExecutionHostKeyResolution } from './runtime-browser-client-page-adoption' -import { browserNetworkExecutionHostKey } from '../browser/browser-network-execution-route' -import type { ClientHostedBrowserRpcRoute } from './runtime-browser-client-automation' -import { routeRuntimeBrowserClientAutomation } from './runtime-browser-client-automation' -import { getRuntimeBrowserPageRegistry } from './runtime-browser-page-registry' -import { getBrowserHostLeaseRegistry } from './browser-host-lease-registry-instance' +} from '../../project-groups/folder-workspace-path-status' +import type { TerminalWorkspaceLaunchScope } from '../runtime-legacy-worker-terminal-recovery-types' +import { folderWorkspaceKey, parseWorkspaceKey } from '../../../shared/workspace-scope' +import type { ResolvedWorktree } from '../runtime-worktree-path-identity' +import type { BrowserExecutionHostKeyResolution } from '../runtime-browser-client-page-adoption' +import { browserNetworkExecutionHostKey } from '../../browser/browser-network-execution-route' +import type { ClientHostedBrowserRpcRoute } from '../runtime-browser-client-automation' +import { routeRuntimeBrowserClientAutomation } from '../runtime-browser-client-automation' +import { getRuntimeBrowserPageRegistry } from '../runtime-browser-page-registry' +import { getBrowserHostLeaseRegistry } from '../browser-host-lease-registry-instance' + +export abstract class OrcaRuntimeWithTransitionGraphReloadToTerminalState extends OrcaRuntimeWithHasTerminalsForWorktree { + protected abstract resolveBrowserNetworkExecutionHostForWorktree( + ...args: Parameters< + ResolveBrowserNetworkExecutionHostForWorktreeMethods['resolveBrowserNetworkExecutionHostForWorktree'] + > + ): ReturnType< + ResolveBrowserNetworkExecutionHostForWorktreeMethods['resolveBrowserNetworkExecutionHostForWorktree'] + > -export class OrcaRuntimeWithTransitionGraphReloadToTerminalState extends OrcaRuntimeWithHasTerminalsForWorktree { protected transitionGraphReloadToTerminalState(windowId: number): void { if (this.shouldRestoreHeadlessGraph(windowId)) { this.restoreHeadlessGraphAuthority() diff --git a/src/main/runtime/session-graph/session-graph-promotion.ts b/src/main/runtime/session-graph/session-graph-promotion.ts new file mode 100644 index 00000000000..0c6151edea7 --- /dev/null +++ b/src/main/runtime/session-graph/session-graph-promotion.ts @@ -0,0 +1,192 @@ +import type { OrcaRuntimeWithHasTerminalsForWorktree as HasTerminalsForWorktreeMethods } from './session-graph-reload-lifecycle' +import type { OrcaRuntimeWithBindPtyIncarnationHandle as BindPtyIncarnationHandleMethods } from '../terminal/identity/terminal-handle-bindings' +import type { OrcaRuntimeWithResolveExitWaiters as ResolveExitWaitersMethods } from '../terminal/wait/terminal-wait-settlements' +import type { OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees as CollectMobileVisibleGraphChangedWorktreesMethods } from '../session-tabs/session-tabs-inventory' +import type { OrcaRuntimeWithGetPtyRecordForPaneKey as GetPtyRecordForPaneKeyMethods } from '../orca-runtime-get-pty-record-for-pane-key' +import type { OrcaRuntimeWithResolveWaiter as ResolveWaiterMethods } from '../orca-runtime-resolve-waiter' +import type { OrcaRuntimeWithMarkPtyLivenessUnverifiable as MarkPtyLivenessUnverifiableMethods } from '../terminal/layout/terminal-input-floor' +import type { OrcaRuntimeWithRecordPtyWorktree as RecordPtyWorktreeMethods } from '../terminal/identity/pty-worktree-record' +import type { RuntimeSessionTabsInventoryPublication as SessionTabsInventoryPublicationMethods } from '../session-tabs/session-tabs-inventory-publication' +import type { OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness as RefreshFloatingWorkspacePtyLivenessMethods } from '../terminal/lifecycle/floating-session-liveness' +import type { OrcaRuntimeWithTouchMobileSessionTabsForWorktree as TouchMobileSessionTabsForWorktreeMethods } from '../orca-runtime-touch-mobile-session-tabs-for-worktree' +import type { OrcaRuntimeWithScheduleMobileSessionTabsChanged as ScheduleMobileSessionTabsChangedMethods } from '../orca-runtime-schedule-mobile-session-tabs-changed' +import type { OrcaRuntimeWithSyncMobileSessionTabs as SyncMobileSessionTabsMethods } from '../session-tabs/mobile-session-reconciliation' +import { OrcaRuntimeWithNotifySshStateChanged } from '../orca-runtime-notify-ssh-state-changed' +import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-types' +import type { ExecutionHostId } from '../../../shared/execution-host' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' +import type { RuntimePtyWorktreeRecord } from '../runtime-terminal-state-records' + +export abstract class OrcaRuntimeWithAttachWindow extends OrcaRuntimeWithNotifySshStateChanged { + protected abstract beginGraphReload( + ...args: Parameters + ): ReturnType + + protected abstract adoptFirstPtyForLeafHandle( + ...args: Parameters + ): ReturnType + + protected abstract adoptPreAllocatedHandle( + ...args: Parameters + ): ReturnType + + protected abstract checkDeliverySettledAndArmRecheck( + ...args: Parameters + ): ReturnType + + protected abstract collectMobileVisibleGraphChangedWorktrees( + ...args: Parameters< + CollectMobileVisibleGraphChangedWorktreesMethods['collectMobileVisibleGraphChangedWorktrees'] + > + ): ReturnType< + CollectMobileVisibleGraphChangedWorktreesMethods['collectMobileVisibleGraphChangedWorktrees'] + > + + protected abstract deliverPendingMessagesForLeaf( + ...args: Parameters + ): ReturnType + + protected abstract getLeafKey( + ...args: Parameters + ): ReturnType + + abstract getPtyLivenessVerdict( + ...args: Parameters + ): ReturnType + + protected abstract invalidateLeafHandle( + ...args: Parameters + ): ReturnType + + protected abstract makeRuntimePaneKey( + ...args: Parameters + ): ReturnType + + abstract markGraphReady( + ...args: Parameters + ): ReturnType + + protected abstract markSessionTabsInventoryPublished( + ...args: Parameters + ): ReturnType + + protected abstract nextTitleObservationSequence( + ...args: Parameters + ): ReturnType + + protected abstract rebuildLeafPtyIndex( + ...args: Parameters + ): ReturnType + + protected abstract reconcileMobileSessionRetirementFences( + ...args: Parameters< + TouchMobileSessionTabsForWorktreeMethods['reconcileMobileSessionRetirementFences'] + > + ): ReturnType + + protected abstract reconcilePtyIncarnationHandles( + ...args: Parameters + ): ReturnType + + protected abstract recordPtyWorktree( + ...args: Parameters + ): ReturnType + + protected abstract scheduleMobileSessionTabsChanged( + ...args: Parameters + ): ReturnType + + protected abstract syncMobileSessionTabs( + ...args: Parameters + ): ReturnType + + attachWindow(windowId: number): void { + if (this.authoritativeWindowId === HEADLESS_RUNTIME_WINDOW_ID) { + if ( + this.pendingHeadlessPromotionWindowId !== null && + windowId !== this.pendingHeadlessPromotionWindowId + ) { + return + } + // Why: promotion is a renderer reload of the same graph owner, not a new + // runtime; stale handles must transition before the real window publishes. + this.persistWindowlessPtyBindingsForDesktopAttach() + this.pendingHeadlessPromotionWindowId = windowId + this.authoritativeWindowId = windowId + this.beginGraphReload(windowId) + return + } + if (this.authoritativeWindowId === null) { + // Why: a promoted serve can close and later reopen its window while new + // background PTYs keep arriving; every windowless gap needs this handoff. + this.persistWindowlessPtyBindingsForDesktopAttach() + this.authoritativeWindowId = windowId + } + } + + protected persistWindowlessPtyBindingsForDesktopAttach(): void { + if (!this.store?.getWorkspaceSession || !this.store.setWorkspaceSession) { + return + } + const partitions = new Map< + ExecutionHostId, + { session: WorkspaceSessionState; ptys: RuntimePtyWorktreeRecord[] } + >() + for (const pty of this.ptysById.values()) { + if (!pty.connected || !pty.tabId) { + continue + } + const hostId = this.getWorkspaceSessionHostIdForWorktree(pty.worktreeId) + const session = this.store.getWorkspaceSession(hostId) + const tab = session.tabsByWorktree[pty.worktreeId]?.find( + (candidate) => candidate.id === pty.tabId + ) + if (!tab) { + continue + } + const layoutPtyIds = Object.values( + session.terminalLayoutsByTabId[pty.tabId]?.ptyIdsByLeafId ?? {} + ) + if (tab.ptyId !== pty.ptyId && !layoutPtyIds.includes(pty.ptyId)) { + continue + } + const partition = partitions.get(hostId) ?? { session, ptys: [] } + partition.ptys.push(pty) + partitions.set(hostId, partition) + } + + for (const [hostId, { session, ptys }] of partitions) { + // Why: windowless SSH PTYs must be handed to the desktop through their SSH partition, never the local session. + const activeWorktreeIdsOnShutdown = [ + ...new Set([ + ...(session.activeWorktreeIdsOnShutdown ?? []), + ...ptys.map((pty) => pty.worktreeId) + ]) + ] + const activeConnectionIdsAtShutdown = [ + ...new Set([ + ...(session.activeConnectionIdsAtShutdown ?? []), + ...ptys + .map((pty) => pty.connectionId) + .filter((connectionId): connectionId is string => connectionId !== null) + ]) + ] + const remoteSessionIdsByTabId = { ...session.remoteSessionIdsByTabId } + for (const pty of ptys) { + if (pty.connectionId && pty.tabId) { + remoteSessionIdsByTabId[pty.tabId] = pty.ptyId + } + } + + this.store.setWorkspaceSession( + { + ...session, + activeWorktreeIdsOnShutdown, + ...(activeConnectionIdsAtShutdown.length > 0 ? { activeConnectionIdsAtShutdown } : {}), + ...(Object.keys(remoteSessionIdsByTabId).length > 0 ? { remoteSessionIdsByTabId } : {}) + }, + hostId + ) + } + } +} diff --git a/src/main/runtime/orca-runtime-has-terminals-for-worktree.ts b/src/main/runtime/session-graph/session-graph-reload-lifecycle.ts similarity index 67% rename from src/main/runtime/orca-runtime-has-terminals-for-worktree.ts rename to src/main/runtime/session-graph/session-graph-reload-lifecycle.ts index ebedfb362fc..6114be9f2cd 100644 --- a/src/main/runtime/orca-runtime-has-terminals-for-worktree.ts +++ b/src/main/runtime/session-graph/session-graph-reload-lifecycle.ts @@ -1,9 +1,59 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithStopExactTerminalsForWorktree } from './orca-runtime-stop-exact-terminals-for-worktree' -import type { RuntimeRendererReloadFence } from './orca-runtime-core' -import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-types' +import type { OrcaRuntimeWithTransitionGraphReloadToTerminalState as TransitionGraphReloadToTerminalStateMethods } from './session-graph-authority' +import type { OrcaRuntimeWithBindPtyIncarnationHandle as BindPtyIncarnationHandleMethods } from '../terminal/identity/terminal-handle-bindings' +import type { OrcaRuntimeWithResolveWaiter as ResolveWaiterMethods } from '../orca-runtime-resolve-waiter' +import { OrcaRuntimeWithStopExactTerminalsForWorktree } from '../orca-runtime-stop-exact-terminals-for-worktree' +import type { RuntimeRendererReloadFence } from '../orca-runtime-core' +import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-types' + +export abstract class OrcaRuntimeWithHasTerminalsForWorktree extends OrcaRuntimeWithStopExactTerminalsForWorktree { + protected abstract assertStableReadyGraph( + ...args: Parameters + ): ReturnType + + protected abstract captureReadyGraphEpoch( + ...args: Parameters + ): ReturnType + + protected abstract clearPtyIncarnationHandles( + ...args: Parameters + ): ReturnType + + protected abstract invalidatePtyIncarnationHandle( + ...args: Parameters + ): ReturnType + + protected abstract refreshWritableFlags( + ...args: Parameters + ): ReturnType + + protected abstract rejectAllWaiters( + ...args: Parameters + ): ReturnType + + protected abstract rejectWaitersForHandle( + ...args: Parameters + ): ReturnType + + protected abstract rememberDetachedPreAllocatedLeaves( + ...args: Parameters + ): ReturnType + + protected abstract restoreHeadlessGraphAuthority( + ...args: Parameters< + TransitionGraphReloadToTerminalStateMethods['restoreHeadlessGraphAuthority'] + > + ): ReturnType + + protected abstract shouldRestoreHeadlessGraph( + ...args: Parameters + ): ReturnType + + protected abstract transitionGraphReloadToTerminalState( + ...args: Parameters< + TransitionGraphReloadToTerminalStateMethods['transitionGraphReloadToTerminalState'] + > + ): ReturnType -export class OrcaRuntimeWithHasTerminalsForWorktree extends OrcaRuntimeWithStopExactTerminalsForWorktree { async hasTerminalsForWorktree(worktreeSelector: string): Promise { const graphEpoch = this.captureReadyGraphEpoch() const worktree = await this.resolveWorktreeSelector(worktreeSelector) diff --git a/src/main/runtime/orca-runtime-sync-window-graph.ts b/src/main/runtime/session-graph/session-window-graph.ts similarity index 96% rename from src/main/runtime/orca-runtime-sync-window-graph.ts rename to src/main/runtime/session-graph/session-window-graph.ts index d244e0ff253..b02d8eee566 100644 --- a/src/main/runtime/orca-runtime-sync-window-graph.ts +++ b/src/main/runtime/session-graph/session-window-graph.ts @@ -1,14 +1,13 @@ /* eslint-disable unicorn/no-useless-spread */ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithAttachWindow } from './orca-runtime-attach-window' +import { OrcaRuntimeWithAttachWindow } from './session-graph-promotion' import type { RuntimeRendererSyncWindowGraph, RuntimeSyncedTab, RuntimeSyncWindowGraph, RuntimeSyncWindowGraphResult -} from '../../shared/runtime-types' -import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-types' -import type { RuntimeLeafRecord } from './runtime-terminal-state-records' +} from '../../../shared/runtime-session-contracts' +import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-types' +import type { RuntimeLeafRecord } from '../runtime-terminal-state-records' /** The runtime indexes graph tabs by bare id, so duplicate ids cannot be routed safely. */ function assertUniqueRuntimeGraphTabIds(tabs: readonly RuntimeSyncedTab[]): void { @@ -21,7 +20,10 @@ function assertUniqueRuntimeGraphTabIds(tabs: readonly RuntimeSyncedTab[]): void } } -export class OrcaRuntimeWithSyncWindowGraph extends OrcaRuntimeWithAttachWindow { +export abstract class OrcaRuntimeWithSyncWindowGraph extends OrcaRuntimeWithAttachWindow { + declare protected terminalGroupGridSizes?: RuntimeSyncWindowGraph['terminalGroupGridSizes'] + declare protected singleGroupWorkspaceGridSize?: RuntimeSyncWindowGraph['singleGroupWorkspaceGridSize'] + shouldRelayTerminalBrowserOpens(): boolean { return this.authoritativeWindowId === HEADLESS_RUNTIME_WINDOW_ID } diff --git a/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts b/src/main/runtime/session-tabs/explicit-terminal-close-stops.ts similarity index 93% rename from src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts rename to src/main/runtime/session-tabs/explicit-terminal-close-stops.ts index 9cdc7b96441..5a1be77ce3d 100644 --- a/src/main/runtime/orca-runtime-stop-explicitly-closed-tab-ptys.ts +++ b/src/main/runtime/session-tabs/explicit-terminal-close-stops.ts @@ -1,18 +1,17 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithFocusTerminal } from './orca-runtime-focus-terminal' -import { EXPLICIT_TERMINAL_CLOSE_STOP_TIMEOUT_MS } from './orca-runtime-core' -import { SSH_PROVIDER_UNREGISTERED_REASON } from '../../shared/pty-liveness-verdict' -import type { RuntimeTerminalClose } from '../../shared/runtime-types' -import type { RuntimePtyTabCloseAuthority } from './runtime-terminal-state-records' -import { parsePaneKey } from '../../shared/stable-pane-id' -import { errorMessage } from '../../shared/error-message' +import { OrcaRuntimeWithFocusTerminal } from '../orca-runtime-focus-terminal' +import { EXPLICIT_TERMINAL_CLOSE_STOP_TIMEOUT_MS } from '../orca-runtime-core' +import { SSH_PROVIDER_UNREGISTERED_REASON } from '../../../shared/pty-liveness-verdict' +import type { RuntimeTerminalClose } from '../../../shared/runtime-types' +import type { RuntimePtyTabCloseAuthority } from '../runtime-terminal-state-records' +import { parsePaneKey } from '../../../shared/stable-pane-id' +import { errorMessage } from '../../../shared/error-message' /** How an explicit close's stop of its addressed PTY ended. */ type ExplicitCloseStop = { stopped: boolean } const NO_STOP: ExplicitCloseStop = { stopped: false } -export class OrcaRuntimeWithStopExplicitlyClosedTabPtys extends OrcaRuntimeWithFocusTerminal { +export abstract class OrcaRuntimeWithStopExplicitlyClosedTabPtys extends OrcaRuntimeWithFocusTerminal { protected async stopExplicitlyClosedTabPtys( ptyIds: readonly string[], addressedPtyId: string diff --git a/src/main/runtime/session-tabs/headless-tab-properties.ts b/src/main/runtime/session-tabs/headless-tab-properties.ts index d4ae383e615..0fc43393ecf 100644 --- a/src/main/runtime/session-tabs/headless-tab-properties.ts +++ b/src/main/runtime/session-tabs/headless-tab-properties.ts @@ -1,4 +1,4 @@ -import { OrcaRuntimeWithCloseHeadlessMobileTerminalTab } from '../orca-runtime-close-headless-mobile-terminal-tab' +import { OrcaRuntimeWithCloseHeadlessMobileTerminalTab } from './headless-terminal-tab-close' import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' import type { RuntimeMobileSessionTabsSnapshot } from '../../../shared/runtime-session-contracts' import type { RuntimeMobileSessionSnapshotTab } from '../../../shared/runtime-mobile-session-tab-contracts' diff --git a/src/main/runtime/orca-runtime-close-headless-mobile-terminal-tab.ts b/src/main/runtime/session-tabs/headless-terminal-tab-close.ts similarity index 75% rename from src/main/runtime/orca-runtime-close-headless-mobile-terminal-tab.ts rename to src/main/runtime/session-tabs/headless-terminal-tab-close.ts index 5ef34314e42..04482de7438 100644 --- a/src/main/runtime/orca-runtime-close-headless-mobile-terminal-tab.ts +++ b/src/main/runtime/session-tabs/headless-terminal-tab-close.ts @@ -1,19 +1,58 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithCloseStructuredAgentSessionTab } from './orca-runtime-close-structured-agent-session-tab' +import type { RuntimeHeadlessTabProperties as HeadlessTabPropertiesMethods } from './headless-tab-properties' +import type { RuntimeHeadlessTabGroups as HeadlessTabGroupsMethods } from './headless-tab-group-moves' +import type { OrcaRuntimeWithPersistHeadlessTerminalTitle as PersistHeadlessTerminalTitleMethods } from '../orca-runtime-persist-headless-terminal-title' +import type { OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals as RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods } from './renderer-session-terminal-rescue' +import { OrcaRuntimeWithCloseStructuredAgentSessionTab } from './structured-session-tab-close' import type { RuntimeMobileSessionTabMove, RuntimeMobileSessionTabMoveResult, RuntimeMobileSessionTabsSnapshot, RuntimeMobileSessionTerminalTab -} from '../../shared/runtime-types' -import { parseAppSshPtyId } from '../../shared/ssh-pty-id' -import { buildHeadlessMobileSessionTabGroups } from './mobile-session-layout-projection' -import { appendRetiredTerminalSurfaceProofs } from './mobile-session-terminal-retirement-proof' -import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records' -import type { TerminalPaneLayoutNode } from '../../shared/terminal-tab-types' -import type { RuntimeSessionTabCloseReason } from '../../shared/runtime-session-contracts' +} from '../../../shared/runtime-types' +import { parseAppSshPtyId } from '../../../shared/ssh-pty-id' +import { buildHeadlessMobileSessionTabGroups } from '../mobile-session-layout-projection' +import { appendRetiredTerminalSurfaceProofs } from '../mobile-session-terminal-retirement-proof' +import type { RuntimePtyWorktreeRecord } from '../runtime-terminal-state-records' +import type { TerminalPaneLayoutNode } from '../../../shared/terminal-tab-types' +import type { RuntimeSessionTabCloseReason } from '../../../shared/runtime-session-contracts' + +export abstract class OrcaRuntimeWithCloseHeadlessMobileTerminalTab extends OrcaRuntimeWithCloseStructuredAgentSessionTab { + protected abstract applyHeadlessSessionTabPropsToSnapshot( + ...args: Parameters + ): ReturnType + + protected abstract applyHeadlessTerminalPaneLayoutToSnapshot( + ...args: Parameters + ): ReturnType + + protected abstract moveHeadlessMobileSessionTab( + ...args: Parameters + ): ReturnType + + protected abstract normalizeMobileSessionTabOrder( + ...args: Parameters + ): ReturnType + + protected abstract persistHeadlessSessionTabProps( + ...args: Parameters + ): ReturnType + + protected abstract persistHeadlessTerminalPaneLayout( + ...args: Parameters + ): ReturnType + + protected abstract resolveMobileSessionHostTabId( + ...args: Parameters + ): ReturnType + + protected abstract setPairedRendererSessionOwnership( + ...args: Parameters< + RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods['setPairedRendererSessionOwnership'] + > + ): ReturnType< + RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods['setPairedRendererSessionOwnership'] + > -export class OrcaRuntimeWithCloseHeadlessMobileTerminalTab extends OrcaRuntimeWithCloseStructuredAgentSessionTab { protected async closeHeadlessMobileTerminalTab( worktreeId: string, snapshot: RuntimeMobileSessionTabsSnapshot, diff --git a/src/main/runtime/orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session.ts b/src/main/runtime/session-tabs/mobile-session-hydration.ts similarity index 76% rename from src/main/runtime/orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session.ts rename to src/main/runtime/session-tabs/mobile-session-hydration.ts index 5b3c6dceb8f..b584f7fecbd 100644 --- a/src/main/runtime/orca-runtime-hydrate-headless-mobile-session-tabs-from-workspace-session.ts +++ b/src/main/runtime/session-tabs/mobile-session-hydration.ts @@ -1,35 +1,97 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. +import type { OrcaRuntimeWithBuildHeadlessMobileSessionBrowserTabs as BuildHeadlessMobileSessionBrowserTabsMethods } from '../orca-runtime-build-headless-mobile-session-browser-tabs' +import type { OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab as StoredMobileSnapshotHasStalePreservedTabMethods } from './mobile-session-publication-preservation' +import type { OrcaRuntimeWithReconcileHeadlessMobileSessionBrowserTabs as ReconcileHeadlessMobileSessionBrowserTabsMethods } from '../orca-runtime-reconcile-headless-mobile-session-browser-tabs' import type { BrowserWindow } from 'electron' -import { RuntimeSessionTabsInventoryPublication } from './session-tabs/session-tabs-inventory-publication' -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' -import { getRuntimeBrowserPageRegistry } from './runtime-browser-page-registry' -import { splitWorktreeIdForFilesystem } from '../../shared/worktree/id' -import { buildHeadlessMobileSessionTerminalTabs } from './mobile-session-terminal-projection' -import { buildHeadlessMobileSessionEditorTabs } from './mobile-session-editor-projection' +import { RuntimeSessionTabsInventoryPublication } from './session-tabs-inventory-publication' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' +import { getRuntimeBrowserPageRegistry } from '../runtime-browser-page-registry' +import { splitWorktreeIdForFilesystem } from '../../../shared/worktree/id' +import { buildHeadlessMobileSessionTerminalTabs } from '../mobile-session-terminal-projection' +import { buildHeadlessMobileSessionEditorTabs } from '../mobile-session-editor-projection' import type { RuntimeMobileSessionSnapshotTab, RuntimeMobileSessionTabGroup, RuntimeMobileSessionTabsSnapshot, RuntimeMobileSessionTerminalTab -} from '../../shared/runtime-types' +} from '../../../shared/runtime-types' import { collectHeadlessParentTabOrder, distributeHeadlessTabsAcrossGroups, getHeadlessMobileSessionGroupId, pickHeadlessActiveTerminalTab, pickRestoredActiveGroupId -} from './mobile-session-layout-projection' +} from '../mobile-session-layout-projection' import { mergeMobileSessionSnapshotTabs, mergeMobileSessionTabGroups -} from './mobile-session-tab-merge' +} from '../mobile-session-tab-merge' import { appendBrowserTabOrder, collectBrowserGroupAssignment -} from './mobile-session-browser-group-projection' -import { headlessMobileSnapshotContentUnchanged } from './mobile-session-snapshot-equality' +} from '../mobile-session-browser-group-projection' +import { headlessMobileSnapshotContentUnchanged } from '../mobile-session-snapshot-equality' export abstract class OrcaRuntimeWithHydrateHeadlessMobileSessionTabsFromWorkspaceSession extends RuntimeSessionTabsInventoryPublication { + protected abstract buildHeadlessMobileSessionBrowserTabs( + ...args: Parameters< + BuildHeadlessMobileSessionBrowserTabsMethods['buildHeadlessMobileSessionBrowserTabs'] + > + ): ReturnType< + BuildHeadlessMobileSessionBrowserTabsMethods['buildHeadlessMobileSessionBrowserTabs'] + > + + protected abstract getMergedMobileSessionPublicationEpoch( + ...args: Parameters< + StoredMobileSnapshotHasStalePreservedTabMethods['getMergedMobileSessionPublicationEpoch'] + > + ): ReturnType< + StoredMobileSnapshotHasStalePreservedTabMethods['getMergedMobileSessionPublicationEpoch'] + > + + protected abstract hasRecentExpiredSshLeasePane( + ...args: Parameters< + ReconcileHeadlessMobileSessionBrowserTabsMethods['hasRecentExpiredSshLeasePane'] + > + ): ReturnType + + protected abstract hasServeOrSshOwnedBinding( + ...args: Parameters< + ReconcileHeadlessMobileSessionBrowserTabsMethods['hasServeOrSshOwnedBinding'] + > + ): ReturnType + + protected abstract isHeadlessBuiltMobileSessionPublicationBase( + ...args: Parameters< + StoredMobileSnapshotHasStalePreservedTabMethods['isHeadlessBuiltMobileSessionPublicationBase'] + > + ): ReturnType< + StoredMobileSnapshotHasStalePreservedTabMethods['isHeadlessBuiltMobileSessionPublicationBase'] + > + + protected abstract reconcileHeadlessMobileSessionBrowserTabs( + ...args: Parameters< + ReconcileHeadlessMobileSessionBrowserTabsMethods['reconcileHeadlessMobileSessionBrowserTabs'] + > + ): ReturnType< + ReconcileHeadlessMobileSessionBrowserTabsMethods['reconcileHeadlessMobileSessionBrowserTabs'] + > + + protected abstract workspaceSessionHasRuntimeOwnedPtyCandidate( + ...args: Parameters< + ReconcileHeadlessMobileSessionBrowserTabsMethods['workspaceSessionHasRuntimeOwnedPtyCandidate'] + > + ): ReturnType< + ReconcileHeadlessMobileSessionBrowserTabsMethods['workspaceSessionHasRuntimeOwnedPtyCandidate'] + > + + protected abstract workspaceSessionWorktreeHasRuntimeOwnedPtyCandidate( + ...args: Parameters< + ReconcileHeadlessMobileSessionBrowserTabsMethods['workspaceSessionWorktreeHasRuntimeOwnedPtyCandidate'] + > + ): ReturnType< + ReconcileHeadlessMobileSessionBrowserTabsMethods['workspaceSessionWorktreeHasRuntimeOwnedPtyCandidate'] + > + protected abstract getAvailableAuthoritativeWindow(): BrowserWindow | null protected hydrateHeadlessMobileSessionTabsFromWorkspaceSession( diff --git a/src/main/runtime/orca-runtime-merge-preserved-headless-mobile-session-tabs.ts b/src/main/runtime/session-tabs/mobile-session-preservation.ts similarity index 88% rename from src/main/runtime/orca-runtime-merge-preserved-headless-mobile-session-tabs.ts rename to src/main/runtime/session-tabs/mobile-session-preservation.ts index 2ea80067093..bb349791dec 100644 --- a/src/main/runtime/orca-runtime-merge-preserved-headless-mobile-session-tabs.ts +++ b/src/main/runtime/session-tabs/mobile-session-preservation.ts @@ -1,20 +1,28 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithSyncMobileSessionTabs } from './orca-runtime-sync-mobile-session-tabs' +import type { OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab as StoredMobileSnapshotHasStalePreservedTabMethods } from './mobile-session-publication-preservation' +import { OrcaRuntimeWithSyncMobileSessionTabs } from './mobile-session-reconciliation' import type { RuntimeMobileSessionSnapshotTab, RuntimeMobileSessionTabGroup, RuntimeMobileSessionTabsSnapshot, RuntimeMobileSessionTerminalTab -} from '../../shared/runtime-types' +} from '../../../shared/runtime-types' import { getMobileSessionSnapshotTabIdentityKeys, mergeMobileSessionSnapshotTabs, mergeMobileSessionTabGroups -} from './mobile-session-tab-merge' -import { terminalLayoutContainsLeaf } from '../../shared/workspace-session-pane-ownership' -import { getHeadlessMobileSessionGroupId } from './mobile-session-layout-projection' +} from '../mobile-session-tab-merge' +import { terminalLayoutContainsLeaf } from '../../../shared/workspace-session-pane-ownership' +import { getHeadlessMobileSessionGroupId } from '../mobile-session-layout-projection' + +export abstract class OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs extends OrcaRuntimeWithSyncMobileSessionTabs { + protected abstract collectPreservedHeadlessMobileSessionTabs( + ...args: Parameters< + StoredMobileSnapshotHasStalePreservedTabMethods['collectPreservedHeadlessMobileSessionTabs'] + > + ): ReturnType< + StoredMobileSnapshotHasStalePreservedTabMethods['collectPreservedHeadlessMobileSessionTabs'] + > -export class OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs extends OrcaRuntimeWithSyncMobileSessionTabs { protected mergePreservedHeadlessMobileSessionTabs( snapshot: RuntimeMobileSessionTabsSnapshot, existing: RuntimeMobileSessionTabsSnapshot | undefined diff --git a/src/main/runtime/orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab.ts b/src/main/runtime/session-tabs/mobile-session-publication-preservation.ts similarity index 86% rename from src/main/runtime/orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab.ts rename to src/main/runtime/session-tabs/mobile-session-publication-preservation.ts index 726d3973aef..03e6c289e51 100644 --- a/src/main/runtime/orca-runtime-stored-mobile-snapshot-has-stale-preserved-tab.ts +++ b/src/main/runtime/session-tabs/mobile-session-publication-preservation.ts @@ -1,16 +1,25 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs } from './orca-runtime-merge-preserved-headless-mobile-session-tabs' +import type { OrcaRuntimeWithScheduleMobileSessionTabsChanged as ScheduleMobileSessionTabsChangedMethods } from '../orca-runtime-schedule-mobile-session-tabs-changed' +import { OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs } from './mobile-session-preservation' import type { RuntimeMobileSessionSnapshotTab, RuntimeMobileSessionTabsRemovedResult, RuntimeMobileSessionTabsSnapshot -} from '../../shared/runtime-types' -import { getMobileSessionSnapshotTabIdentityKeys } from './mobile-session-tab-merge' -import { getRuntimeBrowserPageRegistry } from './runtime-browser-page-registry' -import { sameRuntimeBrowserPlacement } from '../../shared/runtime-browser-placement' -import type { ClientHostedBrowserRowsEvent } from '../../shared/client-hosted-browser-rows' +} from '../../../shared/runtime-types' +import { getMobileSessionSnapshotTabIdentityKeys } from '../mobile-session-tab-merge' +import { getRuntimeBrowserPageRegistry } from '../runtime-browser-page-registry' +import { sameRuntimeBrowserPlacement } from '../../../shared/runtime-browser-placement' +import type { ClientHostedBrowserRowsEvent } from '../../../shared/client-hosted-browser-rows' + +export abstract class OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab extends OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs { + protected abstract getListedBrowserTabsByPageId( + ...args: Parameters + ): ReturnType + protected abstract notifyMobileSessionTabsChangedNow( + ...args: Parameters< + ScheduleMobileSessionTabsChangedMethods['notifyMobileSessionTabsChangedNow'] + > + ): ReturnType -export class OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab extends OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs { // Why: the accepted-revision no-op gate must not fossilize preserved runtime // tabs. A stored merged snapshot's tabs absent from the accepted renderer // publication exist only via preservation; if any such tab no longer diff --git a/src/main/runtime/orca-runtime-sync-mobile-session-tabs.ts b/src/main/runtime/session-tabs/mobile-session-reconciliation.ts similarity index 83% rename from src/main/runtime/orca-runtime-sync-mobile-session-tabs.ts rename to src/main/runtime/session-tabs/mobile-session-reconciliation.ts index 51997577179..24dcd5d627d 100644 --- a/src/main/runtime/orca-runtime-sync-mobile-session-tabs.ts +++ b/src/main/runtime/session-tabs/mobile-session-reconciliation.ts @@ -1,11 +1,44 @@ +import type { OrcaRuntimeWithMergePreservedHeadlessMobileSessionTabs as MergePreservedHeadlessMobileSessionTabsMethods } from './mobile-session-preservation' +import type { OrcaRuntimeWithStoredMobileSnapshotHasStalePreservedTab as StoredMobileSnapshotHasStalePreservedTabMethods } from './mobile-session-publication-preservation' /* eslint-disable unicorn/no-useless-spread */ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithWriteOrchestrationPointerPty } from './orca-runtime-write-orchestration-pointer-pty' -import type { RuntimeMobileSessionTabsSnapshot } from '../../shared/runtime-types' -import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types' -import { getMobileSessionSnapshotTabIdentityKeys } from './mobile-session-tab-merge' +import { OrcaRuntimeWithWriteOrchestrationPointerPty } from '../orca-runtime-write-orchestration-pointer-pty' +import type { RuntimeMobileSessionTabsSnapshot } from '../../../shared/runtime-types' +import type { WorkspaceSessionState } from '../../../shared/workspace-session-state-types' +import { getMobileSessionSnapshotTabIdentityKeys } from '../mobile-session-tab-merge' + +export abstract class OrcaRuntimeWithSyncMobileSessionTabs extends OrcaRuntimeWithWriteOrchestrationPointerPty { + protected abstract buildPreservedHeadlessMobileSessionSnapshot( + ...args: Parameters< + MergePreservedHeadlessMobileSessionTabsMethods['buildPreservedHeadlessMobileSessionSnapshot'] + > + ): ReturnType< + MergePreservedHeadlessMobileSessionTabsMethods['buildPreservedHeadlessMobileSessionSnapshot'] + > + + protected abstract mergePreservedHeadlessMobileSessionTabs( + ...args: Parameters< + MergePreservedHeadlessMobileSessionTabsMethods['mergePreservedHeadlessMobileSessionTabs'] + > + ): ReturnType< + MergePreservedHeadlessMobileSessionTabsMethods['mergePreservedHeadlessMobileSessionTabs'] + > + + protected abstract releaseRuntimeSessionOwnershipForRendererRetiredTabs( + ...args: Parameters< + MergePreservedHeadlessMobileSessionTabsMethods['releaseRuntimeSessionOwnershipForRendererRetiredTabs'] + > + ): ReturnType< + MergePreservedHeadlessMobileSessionTabsMethods['releaseRuntimeSessionOwnershipForRendererRetiredTabs'] + > + + protected abstract storedMobileSnapshotHasStalePreservedTab( + ...args: Parameters< + StoredMobileSnapshotHasStalePreservedTabMethods['storedMobileSnapshotHasStalePreservedTab'] + > + ): ReturnType< + StoredMobileSnapshotHasStalePreservedTabMethods['storedMobileSnapshotHasStalePreservedTab'] + > -export class OrcaRuntimeWithSyncMobileSessionTabs extends OrcaRuntimeWithWriteOrchestrationPointerPty { // Returns the worktrees whose stored snapshot object changed during this // sync, so the caller can fan out only actually-changed worktrees. protected syncMobileSessionTabs( diff --git a/src/main/runtime/orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts b/src/main/runtime/session-tabs/renderer-session-terminal-rescue.ts similarity index 87% rename from src/main/runtime/orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts rename to src/main/runtime/session-tabs/renderer-session-terminal-rescue.ts index 8e9ad65dc32..684e109d62a 100644 --- a/src/main/runtime/orca-runtime-restore-live-paired-renderer-session-owned-mobile-terminals.ts +++ b/src/main/runtime/session-tabs/renderer-session-terminal-rescue.ts @@ -1,11 +1,15 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithWaitForMobileTerminalSurface } from './orca-runtime-wait-for-mobile-terminal-surface' -import { runtimeWorktreeIdsEqual } from './runtime-worktree-path-identity' -import { parsePaneKey } from '../../shared/stable-pane-id' -import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records' -import type { RuntimeMobileSessionCreateTerminalResult } from '../../shared/runtime-types' +import type { OrcaRuntimeWithWaitForLeafPtyId as WaitForLeafPtyIdMethods } from '../orca-runtime-wait-for-leaf-pty-id' +import { OrcaRuntimeWithWaitForMobileTerminalSurface } from '../orca-runtime-wait-for-mobile-terminal-surface' +import { runtimeWorktreeIdsEqual } from '../runtime-worktree-path-identity' +import { parsePaneKey } from '../../../shared/stable-pane-id' +import type { RuntimePtyWorktreeRecord } from '../runtime-terminal-state-records' +import type { RuntimeMobileSessionCreateTerminalResult } from '../../../shared/runtime-types' + +export abstract class OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals extends OrcaRuntimeWithWaitForMobileTerminalSurface { + protected abstract resolveHandleForTab( + ...args: Parameters + ): ReturnType -export class OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals extends OrcaRuntimeWithWaitForMobileTerminalSurface { protected restoreLivePairedRendererSessionOwnedMobileTerminals( worktreeId: string | null, options: { missingSnapshotOnly?: boolean; notify?: boolean } = {} diff --git a/src/main/runtime/session-tabs/restored-chat-tab-changes.ts b/src/main/runtime/session-tabs/restored-chat-tab-changes.ts new file mode 100644 index 00000000000..24a7d9a54d0 --- /dev/null +++ b/src/main/runtime/session-tabs/restored-chat-tab-changes.ts @@ -0,0 +1,30 @@ +import { chatTabChangesMatchingIndex } from '../structured-agent-session-tab-index-seed' +import { chatTabId, type ChatTabChange } from '../agent-session-tab-layout' +import { structuredSessionTabRetired } from '../structured-worker-custody' +import type { getStructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-registry' + +type ChatTabOpen = Extract['open'] + +/** Chat-tab changes that make each worktree's saved tabs match the restored sessions. */ +export function restoredChatTabChanges( + savedTabsByWorktree: Parameters[0], + host: ReturnType, + restored: readonly { workspaceId: string; sessionId: string; agent: ChatTabOpen['agent'] }[] +): Map { + return chatTabChangesMatchingIndex( + savedTabsByWorktree, + restored.map((session) => ({ + worktreeId: session.workspaceId, + open: { + sessionId: session.sessionId, + agent: session.agent, + tabId: chatTabId(session.sessionId) + } + })), + // A chat a newer Orca wrote is only unreadable here, so it keeps its tab. + (sessionId) => + !!host && + structuredSessionTabRetired(host, sessionId) && + !host.deps?.store?.isSessionUnreadable?.(sessionId) + ) +} diff --git a/src/main/runtime/session-tabs/session-tab-prototype-dispatch.test.ts b/src/main/runtime/session-tabs/session-tab-prototype-dispatch.test.ts index 96732fb61a9..570a4677f7c 100644 --- a/src/main/runtime/session-tabs/session-tab-prototype-dispatch.test.ts +++ b/src/main/runtime/session-tabs/session-tab-prototype-dispatch.test.ts @@ -1,5 +1,6 @@ -import { describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import type { + RuntimeMobileSessionAgentTab, RuntimeMobileSessionTabGroup, RuntimeMobileSessionTabMove, RuntimeMobileSessionTabMoveResult, @@ -8,6 +9,8 @@ import type { import type { RuntimeMobileSessionSnapshotTab } from '../../../shared/runtime-mobile-session-tab-contracts' import type { TabGroupLayoutNode } from '../../../shared/tab-types' import { OrcaRuntimeService } from '../orca-runtime' +import { setStructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-registry' +import type { ResolvedWorktree } from '../runtime-worktree-path-identity' function snapshot(): RuntimeMobileSessionTabsSnapshot { return { @@ -283,3 +286,326 @@ describe('original session tab runtime prototypes', () => { expect(topLevelId).toBe('tab') }) }) + +class SessionRuntime extends OrcaRuntimeService { + readonly refreshArgumentCounts: number[] = [] + readonly reloadRequests: number[] = [] + restoreRequests = 0 + restoreResult = Promise.resolve() + + override async resolveWorktreeSelector(): Promise { + const git = { + path: 'workspace', + head: '', + branch: 'selected', + isBare: false, + isMainWorktree: false + } + return { + ...git, + git, + id: 'workspace', + repoId: 'repo', + displayName: '', + comment: '', + linkedIssue: null, + linkedPR: null, + linkedLinearIssue: null, + isArchived: false, + isUnread: false, + isPinned: false, + sortOrder: 0, + lastActivityAt: 0, + parentWorktreeId: null, + childWorktreeIds: [], + lineage: null + } + } + + protected override refreshMobileSessionPtyRecords( + ...args: Parameters + ): Promise | null> { + this.refreshArgumentCounts.push(args.length) + return Promise.resolve(null) + } + + protected override hydrateHeadlessMobileSessionTabsFromWorkspaceSession(): Set { + return new Set() + } + + protected override restoreLivePairedRendererSessionOwnedMobileTerminals(): void {} + + protected override beginGraphReload(windowId: number): number { + this.reloadRequests.push(windowId) + return 77 + } + + protected override hasPersistedStructuredAgentSessionStore(): boolean { + return true + } + + override prepareStructuredAgentSessionStartupRestoration(): Promise { + return Promise.resolve() + } + + protected override restoreStructuredAgentSessionTabsOnce(): Promise { + this.restoreRequests += 1 + return this.restoreResult + } + + seedHeadlessSnapshot(snapshot: RuntimeMobileSessionTabsSnapshot): void { + this.mobileSessionTabsByWorktree.set(snapshot.worktree, snapshot) + } + + setHostTabResolver( + resolver: (snapshot: RuntimeMobileSessionTabsSnapshot, tabId: string) => string | null + ): void { + this.resolveMobileSessionHostTabId = resolver + } + + restoreTabs(): Promise { + return this.restoreStructuredAgentSessionTabs() + } + + closeTab(tab: RuntimeMobileSessionAgentTab): Promise { + return this.closeStructuredAgentSessionTab(tab, 'user-close') + } +} + +describe('session runtime reentry', () => { + afterEach(() => setStructuredAgentSessionHost(null)) + + it('keeps omitted refresh arguments omitted after asynchronous workspace resolution', async () => { + const runtime = new SessionRuntime() + + await runtime.listMobileSessionTabs('selected') + + expect(runtime.refreshArgumentCounts).toEqual([0]) + }) + + it('uses public methods replaced after construction', async () => { + const runtime = new SessionRuntime() + const list = vi.spyOn(runtime, 'listAllMobileSessionTabsWithChangeSequence').mockResolvedValue({ + snapshots: [], + changeSequence: 73 + }) + const project = vi + .spyOn(runtime, 'projectStructuredAgentSessionTab') + .mockImplementation(() => {}) + const input = { + workspaceId: 'workspace', + sessionId: 'session', + agent: 'claude', + activate: false + } satisfies Parameters[0] + + await expect(runtime.listAllMobileSessionTabs('client')).resolves.toEqual([]) + await runtime.publishStructuredAgentSessionTab(input) + + expect(list).toHaveBeenCalledExactlyOnceWith('client') + expect(project).toHaveBeenCalledExactlyOnceWith(input) + }) + + it('re-enters the protected graph reload override', () => { + const runtime = new SessionRuntime() + runtime.attachWindow(1) + runtime.syncWindowGraph(1, { tabs: [], leaves: [] }) + + expect(runtime.markRendererReloading(1)).toEqual({ revision: 77, recovery: 'renderer' }) + expect(runtime.reloadRequests).toEqual([1]) + }) + + it('shares the protected asynchronous restoration operation', async () => { + const runtime = new SessionRuntime() + const gate = Promise.withResolvers() + runtime.restoreResult = gate.promise + + const first = runtime.restoreTabs() + const second = runtime.restoreTabs() + + expect(first).toBe(second) + expect(runtime.restoreRequests).toBe(1) + gate.resolve() + await first + }) + + it('captures the headless callback before evaluating move arguments', async () => { + const runtime = new SessionRuntime() + const snapshot: RuntimeMobileSessionTabsSnapshot = { + worktree: 'workspace', + publicationEpoch: 'epoch', + snapshotVersion: 1, + activeGroupId: null, + activeTabId: null, + activeTabType: null, + tabs: [] + } + runtime.seedHeadlessSnapshot(snapshot) + const first = vi.fn(function ( + this: OrcaRuntimeService, + received: RuntimeMobileSessionTabsSnapshot, + tabId: string + ) { + expect(this).toBe(runtime) + expect(received).toBe(snapshot) + expect(tabId).toBe('tab') + expect(arguments.length).toBe(2) + return null + }) + const replacement = vi.fn(function ( + this: OrcaRuntimeService, + received: RuntimeMobileSessionTabsSnapshot, + tabId: string + ) { + expect(this).toBe(runtime) + expect(received).toBe(snapshot) + expect(tabId).toBe('tab') + return null + }) + runtime.setHostTabResolver(first) + + await expect( + runtime.moveMobileSessionTab('selected', { + kind: 'reorder', + get tabId() { + runtime.setHostTabResolver(replacement) + return 'tab' + }, + targetGroupId: 'group', + tabOrder: ['tab'] + }) + ).rejects.toThrow('tab_not_found') + + expect(first).toHaveBeenCalledExactlyOnceWith(snapshot, 'tab') + expect(replacement).not.toHaveBeenCalled() + await expect( + runtime.moveMobileSessionTab('selected', { + kind: 'reorder', + tabId: 'tab', + targetGroupId: 'group', + tabOrder: ['tab'] + }) + ).rejects.toThrow('tab_not_found') + expect(replacement).toHaveBeenCalledExactlyOnceWith(snapshot, 'tab') + }) + + it('captures the runtime callee before a tab getter replaces it', async () => { + const runtime = new SessionRuntime() + const first = vi.fn(function (this: OrcaRuntimeService, sessionId: string) { + expect(this).toBe(runtime) + expect(sessionId).toBe('session') + return false + }) + const replacement = vi.fn(() => false) + runtime.retireStructuredAgentSessionTabFromSnapshot = first + const tab: RuntimeMobileSessionAgentTab = { + type: 'agent-session', + id: 'agent-session:session', + title: '', + agent: 'claude', + isActive: false, + get sessionId() { + runtime.retireStructuredAgentSessionTabFromSnapshot = replacement + return 'session' + } + } + + await runtime.closeTab(tab) + + expect(first).toHaveBeenCalledOnce() + expect(replacement).not.toHaveBeenCalled() + await runtime.closeTab(tab) + expect(replacement).toHaveBeenCalledExactlyOnceWith('session') + }) +}) + +class GraphRuntime extends OrcaRuntimeService { + get graphTabs() { + return this.tabs + } + get graphLeaves() { + return this.leaves + } + get graphLeavesByPtyId() { + return this.leavesByPtyId + } + get graphAvailability() { + return this.graphStatus + } + get graphEpoch() { + return this.rendererGraphEpoch + } + get detachedLeaves() { + return this.detachedPreAllocatedLeaves + } + get syncCallbacks() { + return this.graphSyncCallbacks + } +} + +const graph = { + tabs: [{ tabId: 'tab', worktreeId: 'workspace', activeLeafId: 'leaf', layout: null, title: '' }], + leaves: [ + { + tabId: 'tab', + worktreeId: 'workspace', + leafId: 'leaf', + paneRuntimeId: 1, + ptyId: 'pty', + paneTitle: null + } + ] +} + +describe('runtime graph ownership', () => { + it('shares detached preallocated leaves with terminal identity during renderer reload', async () => { + const runtime = new GraphRuntime() + const handle = runtime.preAllocateHandleForPty('pty') + runtime.attachWindow(1) + runtime.syncWindowGraph(1, graph) + const leaf = runtime.graphLeaves.get('tab::leaf') + + runtime.markRendererReloading(1) + + expect(runtime.detachedLeaves.get('pty')).toBe(leaf) + runtime.syncWindowGraph(1, { tabs: [], leaves: [] }) + runtime.onPtyData('pty', 'after omitted leaf\n', 100) + await expect(runtime.readTerminal(handle)).resolves.toMatchObject({ + tail: ['after omitted leaf'] + }) + }) + + it('keeps the installed graph and its PTY index in one per-runtime authority', () => { + const first = new GraphRuntime() + const second = new GraphRuntime() + first.attachWindow(1) + first.syncWindowGraph(1, graph) + const leaf = first.graphLeavesByPtyId.get('pty')?.[0] + expect(leaf).toBe(first.graphLeaves.get('tab::leaf')) + expect(first.graphLeavesByPtyId.has('pty')).toBe(true) + expect(second.graphLeavesByPtyId.has('pty')).toBe(false) + expect(second.graphAvailability).toBe('unavailable') + + first.markGraphUnavailable(1) + expect(first.graphTabs.size).toBe(0) + expect(first.graphLeaves.size).toBe(0) + expect(first.graphLeavesByPtyId.has('pty')).toBe(false) + expect(second.graphEpoch).toBe(0) + }) + + it('drains the captured callback list when a callback changes registration', () => { + const runtime = new GraphRuntime() + runtime.attachWindow(1) + const later = vi.fn() + const original = vi.fn() + runtime.syncCallbacks.push(() => { + runtime.syncCallbacks.splice(1, 1) + runtime.syncCallbacks.push(later) + }, original) + + runtime.syncWindowGraph(1, { tabs: [], leaves: [] }) + + expect(original).toHaveBeenCalledOnce() + expect(later).not.toHaveBeenCalled() + }) +}) diff --git a/src/main/runtime/session-tabs/session-tabs-inventory-publication.ts b/src/main/runtime/session-tabs/session-tabs-inventory-publication.ts index 9ee5c62918a..3ecf7b8df73 100644 --- a/src/main/runtime/session-tabs/session-tabs-inventory-publication.ts +++ b/src/main/runtime/session-tabs/session-tabs-inventory-publication.ts @@ -1,4 +1,4 @@ -import { OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees } from '../orca-runtime-collect-mobile-visible-graph-changed-worktrees' +import { OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees } from './session-tabs-inventory' export abstract class RuntimeSessionTabsInventoryPublication extends OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees { protected waitForSessionTabsInventoryPublication(signal?: AbortSignal): Promise { diff --git a/src/main/runtime/orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts b/src/main/runtime/session-tabs/session-tabs-inventory.ts similarity index 61% rename from src/main/runtime/orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts rename to src/main/runtime/session-tabs/session-tabs-inventory.ts index 391a33f0e24..81b580f1f39 100644 --- a/src/main/runtime/orca-runtime-collect-mobile-visible-graph-changed-worktrees.ts +++ b/src/main/runtime/session-tabs/session-tabs-inventory.ts @@ -1,11 +1,82 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithSyncWindowGraph } from './orca-runtime-sync-window-graph' -import type { RuntimeMobileSessionTabsResult, RuntimeSyncedTab } from '../../shared/runtime-types' -import type { RuntimeLeafRecord } from './runtime-terminal-state-records' -import type { PtyControllerInventory } from './runtime-pty-controller-contract' -import { parseExecutionHostId } from '../../shared/execution-host' +import type { RuntimeSessionTabsInventoryPublication as SessionTabsInventoryPublicationMethods } from './session-tabs-inventory-publication' +import type { OrcaRuntimeWithScheduleMobileSessionTabsChanged as ScheduleMobileSessionTabsChangedMethods } from '../orca-runtime-schedule-mobile-session-tabs-changed' +import type { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree as ResolveBrowserNetworkExecutionHostForWorktreeMethods } from '../orca-runtime-resolve-browser-network-execution-host-for-worktree' +import type { OrcaRuntimeWithHydrateHeadlessMobileSessionTabsFromWorkspaceSession as HydrateHeadlessMobileSessionTabsFromWorkspaceSessionMethods } from './mobile-session-hydration' +import type { OrcaRuntimeWithBuildHeadlessMobileSessionBrowserTabs as BuildHeadlessMobileSessionBrowserTabsMethods } from '../orca-runtime-build-headless-mobile-session-browser-tabs' +import type { OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobileTerminals as RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods } from './renderer-session-terminal-rescue' +import type { OrcaRuntimeWithPruneMobileSessionTabGroupLayout as PruneMobileSessionTabGroupLayoutMethods } from '../orca-runtime-prune-mobile-session-tab-group-layout' +import { OrcaRuntimeWithSyncWindowGraph } from '../session-graph/session-window-graph' +import type { + RuntimeMobileSessionTabsResult, + RuntimeSyncedTab +} from '../../../shared/runtime-types' +import type { RuntimeLeafRecord } from '../runtime-terminal-state-records' +import type { PtyControllerInventory } from '../runtime-pty-controller-contract' +import { parseExecutionHostId } from '../../../shared/execution-host' + +export abstract class OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees extends OrcaRuntimeWithSyncWindowGraph { + protected abstract getAuthoritativeSessionTabsInventoryEpoch( + ...args: Parameters< + SessionTabsInventoryPublicationMethods['getAuthoritativeSessionTabsInventoryEpoch'] + > + ): ReturnType + + protected abstract getMobileSessionTabsForWorktree( + ...args: Parameters + ): ReturnType + + protected abstract getValidatedExplicitWorktreeIdSelector( + ...args: Parameters< + ResolveBrowserNetworkExecutionHostForWorktreeMethods['getValidatedExplicitWorktreeIdSelector'] + > + ): ReturnType< + ResolveBrowserNetworkExecutionHostForWorktreeMethods['getValidatedExplicitWorktreeIdSelector'] + > + + protected abstract hydrateHeadlessMobileSessionTabsFromWorkspaceSession( + ...args: Parameters< + HydrateHeadlessMobileSessionTabsFromWorkspaceSessionMethods['hydrateHeadlessMobileSessionTabsFromWorkspaceSession'] + > + ): ReturnType< + HydrateHeadlessMobileSessionTabsFromWorkspaceSessionMethods['hydrateHeadlessMobileSessionTabsFromWorkspaceSession'] + > + + protected abstract projectMobileSessionTabsForClient( + ...args: Parameters< + BuildHeadlessMobileSessionBrowserTabsMethods['projectMobileSessionTabsForClient'] + > + ): ReturnType + + protected abstract refreshMobileSessionPtyInventory( + ...args: Parameters< + BuildHeadlessMobileSessionBrowserTabsMethods['refreshMobileSessionPtyInventory'] + > + ): ReturnType + + protected abstract refreshMobileSessionPtyRecords( + ...args: Parameters< + BuildHeadlessMobileSessionBrowserTabsMethods['refreshMobileSessionPtyRecords'] + > + ): ReturnType + + protected abstract restoreLivePairedRendererSessionOwnedMobileTerminals( + ...args: Parameters< + RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods['restoreLivePairedRendererSessionOwnedMobileTerminals'] + > + ): ReturnType< + RestoreLivePairedRendererSessionOwnedMobileTerminalsMethods['restoreLivePairedRendererSessionOwnedMobileTerminals'] + > + + protected abstract toMobileSessionTabsResult( + ...args: Parameters + ): ReturnType + + protected abstract waitForSessionTabsInventoryPublication( + ...args: Parameters< + SessionTabsInventoryPublicationMethods['waitForSessionTabsInventoryPublication'] + > + ): ReturnType -export class OrcaRuntimeWithCollectMobileVisibleGraphChangedWorktrees extends OrcaRuntimeWithSyncWindowGraph { // Why: toMobileSessionTabsResult resolves handles/titles from this.tabs and // this.leaves, so any tab/leaf delta a graph sync installs can flip the // client payload (pending-handle → ready, tab title) with zero change to the diff --git a/src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts b/src/main/runtime/session-tabs/structured-session-create-support.ts similarity index 86% rename from src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts rename to src/main/runtime/session-tabs/structured-session-create-support.ts index c4ad2e6c116..7a947b738d5 100644 --- a/src/main/runtime/orca-runtime-get-structured-agent-session-create-support.ts +++ b/src/main/runtime/session-tabs/structured-session-create-support.ts @@ -1,34 +1,42 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { agentSessionRefusalError } from '../../shared/agent-session-wire-refusals' -import { OrcaRuntimeWithGetWorktreePs } from './orca-runtime-get-worktree-ps' -import { getStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry' +import type { OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce as RestoreStructuredAgentSessionTabsOnceMethods } from './structured-session-tab-restoration' +import { agentSessionRefusalError } from '../../../shared/agent-session-wire-refusals' +import { OrcaRuntimeWithGetWorktreePs } from '../orca-runtime-get-worktree-ps' +import { getStructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-registry' import { resolveCommittedStructuredAgentSessionAdoptionIntent, resolveStructuredAgentSessionAdoptionForCreate -} from './structured-agent-session-create-adoption' -import { isNativeLocalLocation, LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host' -import { getLocalProjectWorktreeGitOptions } from '../project-runtime-git-options' -import type { AgentSessionAttachParams } from '../native-chat/agent-session-wire/structured-agent-session-attach' +} from '../structured-agent-session-create-adoption' +import { isNativeLocalLocation, LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' +import { getLocalProjectWorktreeGitOptions } from '../../project-runtime-git-options' +import type { AgentSessionAttachParams } from '../../native-chat/agent-session-wire/structured-agent-session-attach' import { resolveHostStructuredAgentCreateSupport, resolveHostStructuredAgentLaunchEnvironment -} from './structured-agent-launch-support' -import { structuredAgentRuntimeRegistration } from './structured-agent-runtime-registrations' -import { structuredLaunchSeedOptions } from '../../shared/native-chat-session-option-defaults' -import { hasPersistedStructuredAgentSessionStore as hasPersistedStructuredAgentSessionStoreOnDisk } from './structured-agent-session-runtime' -import { ensureStructuredAgentSessionHostUnlessRefused } from './structured-agent-session-host-refusal' -import { getProfileUserDataPath } from '../orca-profiles/profile-storage-paths' -import { parseWslUncPath } from '../../shared/wsl-paths' +} from '../structured-agent-launch-support' +import { structuredAgentRuntimeRegistration } from '../structured-agent-runtime-registrations' +import { structuredLaunchSeedOptions } from '../../../shared/native-chat-session-option-defaults' +import { hasPersistedStructuredAgentSessionStore as hasPersistedStructuredAgentSessionStoreOnDisk } from '../structured-agent-session-runtime' +import { ensureStructuredAgentSessionHostUnlessRefused } from '../structured-agent-session-host-refusal' +import { getProfileUserDataPath } from '../../orca-profiles/profile-storage-paths' +import { parseWslUncPath } from '../../../shared/wsl-paths' import { isFloatingWorkspaceId, isFloatingWorkspaceSelector -} from '../../shared/floating-workspace-worktree' -import { parseWorkspaceKey } from '../../shared/workspace-scope' -import type { AgentSessionAccountHome } from '../../shared/agent-session-account-home' -import type { StructuredAgentId } from '../../shared/agent-session-provider-handle' -import { readPiSessionHeader, samePiDirectory } from '../pi/rpc-launch-resolution' +} from '../../../shared/floating-workspace-worktree' +import { parseWorkspaceKey } from '../../../shared/workspace-scope' +import type { AgentSessionAccountHome } from '../../../shared/agent-session-account-home' +import type { StructuredAgentId } from '../../../shared/agent-session-provider-handle' +import { readPiSessionHeader, samePiDirectory } from '../../pi/rpc-launch-resolution' + +export abstract class OrcaRuntimeWithGetStructuredAgentSessionCreateSupport extends OrcaRuntimeWithGetWorktreePs { + protected abstract restoreStructuredAgentSessionTabsOnce( + ...args: Parameters< + RestoreStructuredAgentSessionTabsOnceMethods['restoreStructuredAgentSessionTabsOnce'] + > + ): ReturnType< + RestoreStructuredAgentSessionTabsOnceMethods['restoreStructuredAgentSessionTabsOnce'] + > -export class OrcaRuntimeWithGetStructuredAgentSessionCreateSupport extends OrcaRuntimeWithGetWorktreePs { async getStructuredAgentSessionCreateSupport( worktreeSelector: string, agent: StructuredAgentId @@ -270,7 +278,7 @@ export class OrcaRuntimeWithGetStructuredAgentSessionCreateSupport extends OrcaR providerHandle: { transport: definition.handleTransport, agent: definition.agent, - nativeId: transcriptImport.handleNamesTranscript + nativeId: transcriptImport!.handleNamesTranscript ? adoption.transcriptPath : input.resumeFrom.providerSessionId }, diff --git a/src/main/runtime/orca-runtime-close-structured-agent-session-tab.ts b/src/main/runtime/session-tabs/structured-session-tab-close.ts similarity index 80% rename from src/main/runtime/orca-runtime-close-structured-agent-session-tab.ts rename to src/main/runtime/session-tabs/structured-session-tab-close.ts index eafb9f55883..aa95e6cc18f 100644 --- a/src/main/runtime/orca-runtime-close-structured-agent-session-tab.ts +++ b/src/main/runtime/session-tabs/structured-session-tab-close.ts @@ -1,27 +1,46 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithCloseMobileSessionTab } from './orca-runtime-close-mobile-session-tab' +import type { OrcaRuntimeWithPruneMobileSessionTabGroupLayout as PruneMobileSessionTabGroupLayoutMethods } from '../orca-runtime-prune-mobile-session-tab-group-layout' +import type { RuntimeHeadlessTabGroups as HeadlessTabGroupsMethods } from './headless-tab-group-moves' +import { OrcaRuntimeWithCloseMobileSessionTab } from '../orca-runtime-close-mobile-session-tab' import type { + BrowserTabInfo, RuntimeMobileSessionAgentTab, RuntimeMobileSessionBrowserTab, RuntimeMobileSessionRetiredTerminalSurface, RuntimeMobileSessionTabsSnapshot, RuntimeMobileSessionTerminalTab -} from '../../shared/runtime-types' -import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records' -import { getMobileSessionSnapshotTabIdentityKeys } from './mobile-session-tab-merge' -import type { BrowserSessionTabSelectionOptions } from './browser-tab-create-publication' -import { getRuntimeBrowserPageRegistry } from './runtime-browser-page-registry' -import { applyBrowserSessionTabSelection } from './browser-session-tab-selection-snapshot' -import { getStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry' -import type { StructuredAgentSessionCloseCause } from '../native-chat/agent-session-wire/structured-agent-session-host-lifetime' -import { retireStructuredAgentSessionTabFrom } from './structured-agent-session-tab-retirement' -import { chatTabId, withChatTabChanges, type ChatTabChange } from './agent-session-tab-layout' -import { savedSessionId } from './saved-structured-agent-session-restoration' -import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host' -import { structuredSessionTabRetired } from './structured-worker-custody' -import type { StructuredAgentId } from '../../shared/agent-session-provider-handle' +} from '../../../shared/runtime-types' +import type { RuntimePtyWorktreeRecord } from '../runtime-terminal-state-records' +import { getMobileSessionSnapshotTabIdentityKeys } from '../mobile-session-tab-merge' +import type { BrowserSessionTabSelectionOptions } from '../browser-tab-create-publication' +import { getRuntimeBrowserPageRegistry } from '../runtime-browser-page-registry' +import { applyBrowserSessionTabSelection } from '../browser-session-tab-selection-snapshot' +import { getStructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-registry' +import type { StructuredAgentSessionCloseCause } from '../../native-chat/agent-session-wire/structured-agent-session-host-lifetime' +import { retireStructuredAgentSessionTabFrom } from '../structured-agent-session-tab-retirement' +import { chatTabId, withChatTabChanges, type ChatTabChange } from '../agent-session-tab-layout' +import { savedSessionId } from '../saved-structured-agent-session-restoration' +import { LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' +import { structuredSessionTabRetired } from '../structured-worker-custody' +import type { StructuredAgentId } from '../../../shared/agent-session-provider-handle' + +export abstract class OrcaRuntimeWithCloseStructuredAgentSessionTab extends OrcaRuntimeWithCloseMobileSessionTab { + protected abstract getListedBrowserTabsByPageId(worktreeId: string): Map + protected abstract findPtyForMobileTerminalTab( + ...args: Parameters + ): ReturnType + + protected abstract getMobileTerminalLeafPtyIds( + ...args: Parameters + ): ReturnType + + protected abstract getMobileTerminalPaneKey( + ...args: Parameters + ): ReturnType + + protected abstract persistHeadlessTabGroups( + ...args: Parameters + ): ReturnType -export class OrcaRuntimeWithCloseStructuredAgentSessionTab extends OrcaRuntimeWithCloseMobileSessionTab { protected async closeStructuredAgentSessionTab( tab: RuntimeMobileSessionAgentTab, cause: StructuredAgentSessionCloseCause diff --git a/src/main/runtime/orca-runtime-restore-structured-agent-session-tabs-once.ts b/src/main/runtime/session-tabs/structured-session-tab-restoration.ts similarity index 82% rename from src/main/runtime/orca-runtime-restore-structured-agent-session-tabs-once.ts rename to src/main/runtime/session-tabs/structured-session-tab-restoration.ts index 46e7c3350f7..91a46c8c617 100644 --- a/src/main/runtime/orca-runtime-restore-structured-agent-session-tabs-once.ts +++ b/src/main/runtime/session-tabs/structured-session-tab-restoration.ts @@ -1,46 +1,46 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { defaultAgentChatLabel } from '../../shared/agent-session-chat-label' -import { OrcaRuntimeWithGetStructuredAgentSessionCreateSupport } from './orca-runtime-get-structured-agent-session-create-support' -import { getStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry' -import { collectSavedStructuredAgentSessionIds } from './saved-structured-agent-session-restoration' -import { - chatTabChangesMatchingIndex, - seedStructuredAgentSessionTabIndex -} from './structured-agent-session-tab-index-seed' -import { chatTabId } from './agent-session-tab-layout' -import { structuredSessionTabRetired } from './structured-worker-custody' -import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host' +import type { OrcaRuntimeWithScheduleMobileSessionTabsChanged as ScheduleMobileSessionTabsChangedMethods } from '../orca-runtime-schedule-mobile-session-tabs-changed' +import { defaultAgentChatLabel } from '../../../shared/agent-session-chat-label' +import { OrcaRuntimeWithGetStructuredAgentSessionCreateSupport } from './structured-session-create-support' +import { getStructuredAgentSessionHost } from '../../native-chat/agent-session-wire/structured-agent-session-registry' +import { collectSavedStructuredAgentSessionIds } from '../saved-structured-agent-session-restoration' +import { LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' +import { seedStructuredAgentSessionTabIndex } from '../structured-agent-session-tab-index-seed' +import { restoredChatTabChanges } from './restored-chat-tab-changes' import type { RuntimeMobileSessionAgentTab, RuntimeMobileSessionTabsSnapshot, RuntimeRepoSearchRefs -} from '../../shared/runtime-types' -import { getHeadlessMobileSessionGroupId } from './mobile-session-layout-projection' -import { DEFAULT_REPO_SEARCH_REFS_LIMIT } from './orca-runtime-postlude' -import type { Repo } from '../../shared/repo-types' -import type { GitAdmissionTier } from '../git/command-runner/git-exec-options' +} from '../../../shared/runtime-types' +import { getHeadlessMobileSessionGroupId } from '../mobile-session-layout-projection' +import { DEFAULT_REPO_SEARCH_REFS_LIMIT } from '../orca-runtime-postlude' +import type { Repo } from '../../../shared/repo-types' +import type { GitAdmissionTier } from '../../git/command-runner/git-exec-options' import { getLocalProjectGhExecOptions, type LocalProjectGhExecOptions -} from '../project-runtime-git-options' +} from '../../project-runtime-git-options' import { agentLaunchPlatformForScope, newWorktreeLaunchScope, resolveAgentLaunchRuntime, resolveStartupAgentLaunchRuntime -} from './agent-launch-scope-runtime' -import type { TuiAgent } from '../../shared/tui-agent' -import type { AgentLaunchRuntime } from '../../shared/agent-launch-runtime' -import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types' -import { parseAppSshPtyId } from '../../shared/ssh-pty-id' -import type { PtyProcessInspection } from '../providers/pty-process-inspection' -import type { StructuredAgentId } from '../../shared/agent-session-provider-handle' +} from '../agent-launch-scope-runtime' +import type { TuiAgent } from '../../../shared/tui-agent' +import type { AgentLaunchRuntime } from '../../../shared/agent-launch-runtime' +import type { TerminalWorkspaceLaunchScope } from '../runtime-legacy-worker-terminal-recovery-types' +import { parseAppSshPtyId } from '../../../shared/ssh-pty-id' +import type { PtyProcessInspection } from '../../providers/pty-process-inspection' +import type { StructuredAgentId } from '../../../shared/agent-session-provider-handle' export type AgentLaunchScope = Parameters< OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce['getAgentLaunchPlatformForWorkspace'] >[0] -export class OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce extends OrcaRuntimeWithGetStructuredAgentSessionCreateSupport { +export abstract class OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce extends OrcaRuntimeWithGetStructuredAgentSessionCreateSupport { + protected abstract notifyMobileSessionTabSnapshots( + ...args: Parameters + ): ReturnType + protected async restoreStructuredAgentSessionTabsOnce(): Promise { await this.prepareStructuredAgentSessionStartupRestoration() const host = getStructuredAgentSessionHost() @@ -73,21 +73,10 @@ export class OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce extends OrcaRu targets, restored.map((session) => session.sessionId) ) - const changes = chatTabChangesMatchingIndex( + const changes = restoredChatTabChanges( this.store?.getWorkspaceSession?.(LOCAL_EXECUTION_HOST_ID)?.unifiedTabs ?? {}, - restored.map((session) => ({ - worktreeId: session.workspaceId, - open: { - sessionId: session.sessionId, - agent: session.agent, - tabId: chatTabId(session.sessionId) - } - })), - // A chat a newer Orca wrote is only unreadable here, so it keeps its tab. - (sessionId) => - !!host && - structuredSessionTabRetired(host, sessionId) && - !host.deps?.store?.isSessionUnreadable?.(sessionId) + host, + restored ) for (const [worktreeId, worktreeChanges] of changes) { this.changeWindowlessChatTabs(worktreeId, worktreeChanges) @@ -301,7 +290,13 @@ export class OrcaRuntimeWithRestoreStructuredAgentSessionTabsOnce extends OrcaRu connectionId?: string | null } ): NodeJS.Platform { - return agentLaunchPlatformForScope(this.requireStore(), scope) + // Orchestration passes a repo-only scope; the platform never reads its id or folder. + return agentLaunchPlatformForScope(this.requireStore(), { + id: '', + folderWorkspace: null, + ...scope, + connectionId: scope.connectionId ?? null + }) } protected resolveAgentLaunchRuntime( diff --git a/src/main/runtime/orca-runtime-persist-terminal-surface-retirements.ts b/src/main/runtime/session-tabs/terminal-surface-retirements.ts similarity index 84% rename from src/main/runtime/orca-runtime-persist-terminal-surface-retirements.ts rename to src/main/runtime/session-tabs/terminal-surface-retirements.ts index 5458543815a..5b264340788 100644 --- a/src/main/runtime/orca-runtime-persist-terminal-surface-retirements.ts +++ b/src/main/runtime/session-tabs/terminal-surface-retirements.ts @@ -1,14 +1,18 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithTouchMobileSessionTabsForWorktree } from './orca-runtime-touch-mobile-session-tabs-for-worktree' -import type { RetiredTerminalSurface } from './mobile-session-terminal-retirement' -import type { RuntimeMobileSessionRetiredTerminalSurface } from '../../shared/runtime-types' -import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host' -import { retireTerminalSurfaceFromPersistence } from './mobile-session-terminal-persistence-retirement' -import { retireTerminalSurfacesFromSnapshot } from './mobile-session-terminal-retirement' -import { attachRetirementProofsToSnapshot } from './mobile-session-terminal-retirement-proof' -import { getRepoIdFromWorktreeId } from '../../shared/worktree/id' +import type { OrcaRuntimeWithBindPtyIncarnationHandle as BindPtyIncarnationHandleMethods } from '../terminal/identity/terminal-handle-bindings' +import { OrcaRuntimeWithTouchMobileSessionTabsForWorktree } from '../orca-runtime-touch-mobile-session-tabs-for-worktree' +import type { RetiredTerminalSurface } from '../mobile-session-terminal-retirement' +import type { RuntimeMobileSessionRetiredTerminalSurface } from '../../../shared/runtime-types' +import { LOCAL_EXECUTION_HOST_ID } from '../../../shared/execution-host' +import { retireTerminalSurfaceFromPersistence } from '../mobile-session-terminal-persistence-retirement' +import { retireTerminalSurfacesFromSnapshot } from '../mobile-session-terminal-retirement' +import { attachRetirementProofsToSnapshot } from '../mobile-session-terminal-retirement-proof' +import { getRepoIdFromWorktreeId } from '../../../shared/worktree/id' + +export abstract class OrcaRuntimeWithPersistTerminalSurfaceRetirements extends OrcaRuntimeWithTouchMobileSessionTabsForWorktree { + protected abstract findHandleForPtyRecord( + ...args: Parameters + ): ReturnType -export class OrcaRuntimeWithPersistTerminalSurfaceRetirements extends OrcaRuntimeWithTouchMobileSessionTabsForWorktree { /** * Retires each surface in the in-memory session partition of the host that owns its worktree. * Why: an SSH pane's durable surface lives in that connection's partition; retiring it @@ -34,7 +38,8 @@ export class OrcaRuntimeWithPersistTerminalSurfaceRetirements extends OrcaRuntim continue } try { - this.store.setWorkspaceSession(next, hostId) + // The existing catch handles a missing store setter. + this.store!.setWorkspaceSession!(next, hostId) accepted.push(surface) } catch (error) { // Why: the process is gone whether or not the profile admits the write (quit, maintenance). diff --git a/src/main/runtime/orca-runtime-refresh-floating-workspace-pty-liveness.ts b/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts similarity index 90% rename from src/main/runtime/orca-runtime-refresh-floating-workspace-pty-liveness.ts rename to src/main/runtime/terminal/lifecycle/floating-session-liveness.ts index 2ff33658b92..6152e4363bc 100644 --- a/src/main/runtime/orca-runtime-refresh-floating-workspace-pty-liveness.ts +++ b/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts @@ -1,11 +1,13 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory } from './orca-runtime-refresh-pty-worktree-records-with-controller-inventory' -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants' -import { indexFloatingSnapshotPtyBindings } from './floating-snapshot-pty-bindings' -import type { RuntimeLeafRecord, RuntimePtyWorktreeRecord } from './runtime-terminal-state-records' -import { DISCONNECTED_PTY_RECORD_MAX } from './orca-runtime-postlude' +import { OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory } from '../../orca-runtime-refresh-pty-worktree-records-with-controller-inventory' +import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' +import { indexFloatingSnapshotPtyBindings } from '../../floating-snapshot-pty-bindings' +import type { + RuntimeLeafRecord, + RuntimePtyWorktreeRecord +} from '../../runtime-terminal-state-records' +import { DISCONNECTED_PTY_RECORD_MAX } from '../../orca-runtime-postlude' -export class OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness extends OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory { +export abstract class OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness extends OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory { protected refreshFloatingWorkspacePtyLiveness(): Set | null { const controller = this.ptyController if (!controller?.hasPty) { diff --git a/src/main/runtime/terminal/wait/terminal-interactive-wait.ts b/src/main/runtime/terminal/wait/terminal-interactive-wait.ts index 94621cbd134..9d0d52fceb9 100644 --- a/src/main/runtime/terminal/wait/terminal-interactive-wait.ts +++ b/src/main/runtime/terminal/wait/terminal-interactive-wait.ts @@ -11,7 +11,7 @@ import type { TerminalWaitStatusEvidence } from './terminal-wait-permission' -import type { AgentLaunchScope } from '../../orca-runtime-restore-structured-agent-session-tabs-once' +import type { AgentLaunchScope } from '../../session-tabs/structured-session-tab-restoration' import { withTimeout } from '../../runtime-async-boundaries' import { TERMINAL_INTERACTIVE_WAIT_PROBE_TIMEOUT_MS } from '../../orca-runtime-core' import { parsePaneKey } from '../../../../shared/stable-pane-id' diff --git a/src/main/runtime/workspaces/workspace-selectors.ts b/src/main/runtime/workspaces/workspace-selectors.ts index 5837bac2127..ddd3e7c00b4 100644 --- a/src/main/runtime/workspaces/workspace-selectors.ts +++ b/src/main/runtime/workspaces/workspace-selectors.ts @@ -186,7 +186,7 @@ export abstract class RuntimeWorkspaceSelectors extends OrcaRuntimeWithResolveBr throw new Error('repo_not_found') } - protected requireStore(): RuntimeStore { + requireStore(): RuntimeStore { if (!this.store) { throw new Error('runtime_unavailable') } diff --git a/src/shared/tui-agent-launch-defaults.ts b/src/shared/tui-agent-launch-defaults.ts index 9db70ce4c83..8230e30ff9f 100644 --- a/src/shared/tui-agent-launch-defaults.ts +++ b/src/shared/tui-agent-launch-defaults.ts @@ -16,7 +16,7 @@ const UNSUPPORTED_TUI_AGENT_ARGS: Partial> = export const DEFAULT_TUI_AGENT_ARGS: Partial> = YOLO_TUI_AGENT_ARGS -export const DEFAULT_TUI_AGENT_ENV: Partial>> = +export const DEFAULT_TUI_AGENT_ENV: Partial>> = YOLO_TUI_AGENT_ENV function argPattern(arg: string): RegExp { @@ -111,7 +111,7 @@ export function getTuiAgentDefaultArgs(agent: TuiAgent): string { return DEFAULT_TUI_AGENT_ARGS[agent] ?? '' } -export function getTuiAgentDefaultEnv(agent: TuiAgent): Record { +export function getTuiAgentDefaultEnv(agent: string): Record { return { ...DEFAULT_TUI_AGENT_ENV[agent] } } @@ -160,8 +160,8 @@ export function resolvedTuiAgentArgsBypassPermissions( } export function resolveTuiAgentLaunchEnv( - agent: TuiAgent, - configuredEnv: Partial>> | null | undefined + agent: string, + configuredEnv: Partial>> | null | undefined ): Record { if (configuredEnv && Object.hasOwn(configuredEnv, agent)) { return { ...configuredEnv[agent] }