diff --git a/config/ts-nocheck-baseline.txt b/config/ts-nocheck-baseline.txt index f8d0c85732f..0be6c009e77 100644 --- a/config/ts-nocheck-baseline.txt +++ b/config/ts-nocheck-baseline.txt @@ -18,11 +18,9 @@ src/main/runtime/orca-runtime-get-runtime-id.ts src/main/runtime/orca-runtime-get-worktree-ps.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-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-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-preserved-branch-cleanup.ts @@ -30,10 +28,7 @@ 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-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 -src/main/runtime/orca-runtime-register-pty.ts 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 diff --git a/src/main/runtime/orca-runtime-tests/terminal-spawn-dispatch.spec.ts b/src/main/runtime/orca-runtime-tests/terminal-spawn-dispatch.spec.ts index ff340199770..7dddc6928bb 100644 --- a/src/main/runtime/orca-runtime-tests/terminal-spawn-dispatch.spec.ts +++ b/src/main/runtime/orca-runtime-tests/terminal-spawn-dispatch.spec.ts @@ -1,6 +1,9 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi, afterEach } from 'vitest' import { OrcaRuntimeService } from '../orca-runtime-test-mocks.spec' import { TEST_WORKTREE_PATH, store } from '../orca-runtime-test-fixtures.spec' +import { OrcaRuntimeWithRuntimeId } from '../orca-runtime-runtime-id' +import { OrcaRuntimeWithStateFields } from '../orca-runtime-state-fields' +import { OrchestrationDb } from '../orchestration/db/orchestration-db' // `agent.launch` settles a launch as failed only when its create threw before this hook ran. describe('OrcaRuntimeService createTerminal spawn dispatch', () => { @@ -68,3 +71,335 @@ describe('OrcaRuntimeService createTerminal spawn dispatch', () => { expect(dispatched).not.toHaveBeenCalled() }) }) + +describe('PTY registration dispatch characterization', () => { + type PtyRegistrationBinding = NonNullable[3]> + const TAB_ID = '11111111-1111-4111-8111-111111111111' + const LEAF_ID = '22222222-2222-4222-8222-222222222222' + const PTY_ID = 'pty-dispatch' + const WORKTREE_ID = 'repo::/tmp/dispatch' + const databases: OrchestrationDb[] = [] + + afterEach(() => { + for (const db of databases.splice(0)) { + db.close() + } + }) + + class DispatchRuntime extends OrcaRuntimeService { + constructor() { + super(null) + this.ensurePtyBackedMobileSurfaceForRendererTab = () => null + this.touchMobileSessionSnapshotsForPty = () => {} + } + + replaceAdmission(assertion: (ptyId: string, incarnationId?: string) => void): void { + this.assertPtyDidNotExitBeforeRegistration = assertion + } + + replaceHostScope( + resolve: () => { kind: 'local'; hostId: 'local'; toJSON(): { kind: 'local' } } | null + ): void { + this.getOrchestrationCompatibilityHostScope = resolve + } + + setDatabase(db: OrchestrationDb | null): void { + this._orchestrationDb = db + } + + get record() { + return this.recordPtyWorktree + } + + set record(value: typeof this.recordPtyWorktree) { + this.recordPtyWorktree = value + } + + get pendingCreates() { + return this.pendingMobileTerminalCreatesByKey + } + + get replacementFences() { + return this.pendingPtyHandleReplacementFences + } + } + + function binding(): PtyRegistrationBinding { + return { tabId: TAB_ID, leafId: LEAF_ID, incarnationId: 'dispatch-incarnation' } + } + + function register(runtime: DispatchRuntime, tab: PtyRegistrationBinding): void { + runtime.registerPty(PTY_ID, WORKTREE_ID, null, tab) + } + + describe('PTY runtime dispatch', () => { + it('constructs supported early layers and keeps lifecycle cells available to the full runtime', () => { + // Earlier constructors already require this browser callback. + abstract class EarlyStateRuntime extends OrcaRuntimeWithStateFields { + protected markHeadlessBrowserSessionTabActive(): void {} + } + const cells: boolean[] = [] + class ConstructorRuntime extends OrcaRuntimeService { + protected get canonicalFetchKeyCache(): ReadonlyMap { + cells.push( + this.stopRequestedPtyIds instanceof Set, + this.ptyExitListenersByPtyId instanceof Map + ) + return super.canonicalFetchKeyCache + } + } + const runtimes: unknown[] = [ + new OrcaRuntimeWithRuntimeId(), + new ConstructorRuntime(), + new ConstructorRuntime(null), + Reflect.construct(EarlyStateRuntime, [null]) + ] + expect(runtimes).toHaveLength(4) + expect(cells).toEqual([true, true, true, true]) + }) + + it('captures the admission method before reading the incarnation', () => { + const runtime = new DispatchRuntime() + runtime.replaceAdmission(function (this: DispatchRuntime) { + expect(this).toBe(runtime) + throw new Error('captured admission') + }) + const tab = binding() + Object.defineProperty(tab, 'incarnationId', { + get() { + runtime.replaceAdmission(() => { + throw new Error('late admission') + }) + return 'dispatch-incarnation' + } + }) + expect(() => register(runtime, tab)).toThrow('captured admission') + }) + + it('captures the record method before evaluating its state object', () => { + const runtime = new DispatchRuntime() + let reads = 0 + runtime.record = function (this: DispatchRuntime) { + expect(this).toBe(runtime) + throw new Error('captured record') + } + const tab = binding() + Object.defineProperty(tab, 'tabId', { + get() { + if (++reads === 3) { + runtime.record = () => { + throw new Error('late record') + } + } + return TAB_ID + } + }) + expect(() => register(runtime, tab)).toThrow('captured record') + }) + + it('skips database payload serialization when no database exists', () => { + const runtime = new DispatchRuntime() + let serializations = 0 + runtime.replaceHostScope(() => ({ + kind: 'local', + hostId: 'local', + toJSON() { + serializations++ + return { kind: 'local' } + } + })) + register(runtime, binding()) + expect(serializations).toBe(0) + }) + + it('skips a throwing database payload when no database exists', () => { + const runtime = new DispatchRuntime() + runtime.replaceHostScope(() => ({ + kind: 'local', + hostId: 'local', + toJSON() { + throw new Error('absent database payload') + } + })) + expect(() => register(runtime, binding())).not.toThrow() + }) + + it('keeps the database method and receiver captured before its payload', () => { + const runtime = new DispatchRuntime() + const captured = new OrchestrationDb(':memory:') + const replacement = new OrchestrationDb(':memory:') + databases.push(captured, replacement) + const calls: string[] = [] + captured.retainReplacedWorkerTerminalResources = function () { + expect(this).toBe(captured) + calls.push('captured database') + return 0 + } + replacement.retainReplacedWorkerTerminalResources = () => calls.push('late database') + runtime.setDatabase(captured) + runtime.replaceHostScope(() => ({ + kind: 'local', + hostId: 'local', + toJSON() { + runtime.setDatabase(replacement) + return { kind: 'local' } + } + })) + register(runtime, binding()) + expect(calls).toEqual(['captured database']) + }) + + it('captures the canonical map method before a binding getter changes it', () => { + const runtime = new DispatchRuntime() + let reads = 0 + const calls: string[] = [] + const map = runtime.pendingCreates + map.has = () => { + calls.push('captured map') + return true + } + const tab = binding() + Object.defineProperty(tab, 'tabId', { + get() { + if (++reads === 3) { + map.has = () => { + calls.push('late map') + return false + } + } + return TAB_ID + } + }) + runtime.replaceHostScope(() => null) + register(runtime, tab) + expect(calls).toEqual(['captured map']) + }) + + it('completes the captured replacement fence without another map lookup', () => { + const runtime = new DispatchRuntime() + let recorded = false + let captured = false + let reads = 0 + const map = runtime.replacementFences + const originalGet = map.get.bind(map) + const first = { + incarnationId: 'first', + staleHandles: new Set(), + pendingRegistration: false + } + const replacement = { + incarnationId: 'second', + staleHandles: new Set(), + pendingRegistration: true + } + map.set(PTY_ID, first) + map.get = function (id) { + reads++ + const value = originalGet(id) + if (recorded) { + captured = true + } + return value + } + const record = runtime.record + runtime.record = function (...args) { + const value = record.apply(runtime, args) + recorded = true + return value + } + runtime.replaceHostScope(() => null) + const tab = binding() + Object.defineProperty(tab, 'incarnationId', { + get() { + if (captured) { + map.set(PTY_ID, replacement) + } + return undefined + } + }) + register(runtime, tab) + expect(reads).toBe(2) + expect(replacement.pendingRegistration).toBe(true) + }) + + it('initializes original lifecycle state before the base constructor getter', () => { + const observations: boolean[] = [] + class ConstructorRuntime extends OrcaRuntimeService { + protected get canonicalFetchKeyCache(): ReadonlyMap { + observations.push( + this.earlyExitedPtyIncarnations instanceof Map, + this.pendingPtyRegistrationIncarnations instanceof Map, + this.ptyLivenessVerdictByPtyId instanceof Map, + this.ptyExitListenersByPtyId instanceof Map, + this.stopRequestedPtyIds instanceof Set, + this.ptyControllerInventoryGenerationByProvider instanceof Map, + this.ptyLivenessObservationSequence === 0, + this.ptyControllerInventorySequence === 0, + this.ptyControllerAggregateInventoryGeneration === 0 + ) + this.markPtyStopRequested(PTY_ID) + expect(this.isPtyStopRequested(PTY_ID)).toBe(true) + return new Map() + } + } + expect(() => new ConstructorRuntime(null)).not.toThrow() + expect(observations).toEqual(Array(9).fill(true)) + }) + }) +}) + +describe('PTY inventory construction characterization', () => { + const PTY_ID = 'pty-constructor-probe' + + describe('PTY methods during runtime construction', () => { + it('keeps registration fences and liveness on the original canonical state', async () => { + const pending: Promise[] = [] + let seamCalls = 0 + class ConstructorRuntime extends OrcaRuntimeService { + protected get canonicalFetchKeyCache(): ReadonlyMap { + seamCalls++ + this.beginPtyRegistration(PTY_ID, 'incarnation') + expect(this.pendingPtyRegistrationIncarnations.get(PTY_ID)).toBe('incarnation') + this.cancelPendingPtyRegistration(PTY_ID, 'incarnation') + expect(this.pendingPtyRegistrationIncarnations.has(PTY_ID)).toBe(false) + this.assertPtyRegistrationAllowed(PTY_ID, 'incarnation') + this.releaseRejectedPtyRegistrationFence(PTY_ID, 'incarnation') + this.acceptPtyIncarnationForExit(PTY_ID, 'incarnation') + this.markPtyStopRequested(PTY_ID) + expect(this.isPtyStopRequested(PTY_ID)).toBe(true) + this.markPtyLivenessLive(PTY_ID) + expect(this.getPtyLivenessVerdict(PTY_ID)).toEqual({ status: 'live', ptyIds: [PTY_ID] }) + this.markPtyLivenessUnverifiable(PTY_ID, 'host unavailable') + expect(this.getPtyLivenessVerdict(PTY_ID)).toEqual({ + status: 'unverifiable', + reason: 'host unavailable' + }) + this.forgetPtyLivenessVerdict(PTY_ID) + expect(this.getPtyLivenessVerdict(PTY_ID)).toBeNull() + expect(this.getTerminalPtyIdForHandle('missing')).toBeNull() + expect(this.getAgentProviderSessionRows()).toEqual([]) + expect(this.getAgentStatusRowsForPane('missing')).toEqual([]) + pending.push( + this.confirmTerminalShellAlone(PTY_ID).then((value) => expect(value).toBe(false)) + ) + pending.push( + this.refreshPtyWorktreeRecordsFromController([]).then((value) => + expect(value).toBeNull() + ) + ) + pending.push( + this.refreshPtyWorktreeRecordsWithControllerInventory([]).then((value) => + expect(value).toBeNull() + ) + ) + this.invalidatePtyControllerInventoryForLifecycle(PTY_ID) + expect(this.ptyControllerInventorySequence).toBe(1) + return super.canonicalFetchKeyCache + } + } + expect(() => new ConstructorRuntime(null)).not.toThrow() + expect(seamCalls).toBe(1) + await Promise.all(pending) + }) + }) +}) diff --git a/src/main/runtime/terminal/launch/terminal-execution-context.ts b/src/main/runtime/terminal/launch/terminal-execution-context.ts index e93e9427bc7..a6dd2a9223f 100644 --- a/src/main/runtime/terminal/launch/terminal-execution-context.ts +++ b/src/main/runtime/terminal/launch/terminal-execution-context.ts @@ -1,4 +1,4 @@ -import { OrcaRuntimeWithRegisterPty } from '../../orca-runtime-register-pty' +import { OrcaRuntimeWithRegisterPty } from '../lifecycle/pty-registration' export abstract class OrcaRuntimeWithPreparePtyExecutionContext extends OrcaRuntimeWithRegisterPty { protected abstract disposeHeadlessTerminal(ptyId: string): void diff --git a/src/main/runtime/terminal/layout/terminal-input-floor.ts b/src/main/runtime/terminal/layout/terminal-input-floor.ts index 2fdf6e838fb..503b9ed510f 100644 --- a/src/main/runtime/terminal/layout/terminal-input-floor.ts +++ b/src/main/runtime/terminal/layout/terminal-input-floor.ts @@ -1,4 +1,4 @@ -import { OrcaRuntimeWithOnPtyExit } from '../../orca-runtime-on-pty-exit' +import { OrcaRuntimeWithOnPtyExit } from '../lifecycle/pty-exit-lifecycle' import type { PtyLivenessVerdict } from '../../../../shared/pty-liveness-verdict' import type { DriverState } from '../../orca-runtime-core' import type { TerminalInputKind } from '../../../../shared/terminal-input-kind' diff --git a/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts b/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts index 6152e4363bc..e788743eb17 100644 --- a/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts +++ b/src/main/runtime/terminal/lifecycle/floating-session-liveness.ts @@ -1,4 +1,4 @@ -import { OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory } from '../../orca-runtime-refresh-pty-worktree-records-with-controller-inventory' +import { OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory } from './pty-controller-inventory-refresh' import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' import { indexFloatingSnapshotPtyBindings } from '../../floating-snapshot-pty-bindings' import type { diff --git a/src/main/runtime/orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts b/src/main/runtime/terminal/lifecycle/pty-controller-inventory-refresh.ts similarity index 90% rename from src/main/runtime/orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts rename to src/main/runtime/terminal/lifecycle/pty-controller-inventory-refresh.ts index 59437a5271b..82fcb5760ae 100644 --- a/src/main/runtime/orca-runtime-refresh-pty-worktree-records-with-controller-inventory.ts +++ b/src/main/runtime/terminal/lifecycle/pty-controller-inventory-refresh.ts @@ -1,39 +1,38 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController } from './orca-runtime-refresh-pty-worktree-records-from-controller' -import type { ResolvedWorktree } from './runtime-worktree-path-identity' -import type * as PtyControllerContract from './runtime-pty-controller-contract' -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants' +import { OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController } from './pty-worktree-refresh' +import type { ResolvedWorktree } from '../../runtime-worktree-path-identity' +import type * as PtyControllerContract from '../../runtime-pty-controller-contract' +import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' import { + type ExecutionHostId, getConnectionExecutionHostId, LOCAL_EXECUTION_HOST_ID, parseExecutionHostId -} from '../../shared/execution-host' +} from '../../../../shared/execution-host' import { PTY_CONTROLLER_LIST_PROVIDER_MARGIN_MS, PTY_CONTROLLER_LIST_TIMEOUT_MS -} from './orca-runtime-postlude' -import type { ExecutionHostId } from '../../shared/execution-host' -import { pruneOldestMapEntry } from './prune-oldest-map-entry' -import { withTimeoutResult } from './runtime-async-boundaries' -import { getPtyExecutionHost } from '../../shared/terminal-execution-host' +} from '../../orca-runtime-postlude' +import { pruneOldestMapEntry } from '../../prune-oldest-map-entry' +import { withTimeoutResult } from '../../runtime-async-boundaries' +import { getPtyExecutionHost } from '../../../../shared/terminal-execution-host' import { createIncrementalResolvedWorktreeLookup, findResolvedWorktreeIdForPath, inferWorktreeIdFromPtyId, runtimeWorktreeIdsEqual -} from './runtime-worktree-path-identity' +} from '../../runtime-worktree-path-identity' import { indexPersistedPtySurfaceBindings, indexPersistedPtyWorktreeBindings, type PersistedPtyBindingIndexes -} from './runtime-worktree-binding-index' -import { parseAppSshPtyId } from '../../shared/ssh-pty-id' -import { indexFloatingSnapshotPtyBindings } from './floating-snapshot-pty-bindings' -import { NO_OBSERVING_PROVIDER_REASON } from '../../shared/pty-liveness-verdict' -import { buildControllerTerminalIdentities } from './orca-runtime-build-controller-terminal-identities' -import { retireOrchestrationAuthorityAbsentFromInventory } from './runtime-restored-orchestration-authority-sweep' +} from '../../runtime-worktree-binding-index' +import { parseAppSshPtyId } from '../../../../shared/ssh-pty-id' +import { indexFloatingSnapshotPtyBindings } from '../../floating-snapshot-pty-bindings' +import { NO_OBSERVING_PROVIDER_REASON } from '../../../../shared/pty-liveness-verdict' +import { buildControllerTerminalIdentities } from '../../orca-runtime-build-controller-terminal-identities' +import { retireOrchestrationAuthorityAbsentFromInventory } from '../../runtime-restored-orchestration-authority-sweep' -export class OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory extends OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController { +export abstract class OrcaRuntimeWithRefreshPtyWorktreeRecordsWithControllerInventory extends OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController { protected async refreshPtyWorktreeRecordsWithControllerInventory( resolvedWorktrees: ResolvedWorktree[], targetWorktreeId: string | null = null, diff --git a/src/main/runtime/orca-runtime-on-pty-exit.ts b/src/main/runtime/terminal/lifecycle/pty-exit-lifecycle.ts similarity index 89% rename from src/main/runtime/orca-runtime-on-pty-exit.ts rename to src/main/runtime/terminal/lifecycle/pty-exit-lifecycle.ts index d316d50f042..768e546cc5b 100644 --- a/src/main/runtime/orca-runtime-on-pty-exit.ts +++ b/src/main/runtime/terminal/lifecycle/pty-exit-lifecycle.ts @@ -1,17 +1,40 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithOnClientDisconnected } from './terminal/layout/terminal-client-disconnect' -import type { PtyIncarnationId } from '../../shared/pty-incarnation' -import type { TerminalExitCause } from '../../shared/terminal-exit-cause' +import { OrcaRuntimeWithOnClientDisconnected } from '../layout/terminal-client-disconnect' +import type { PtyIncarnationId } from '../../../../shared/pty-incarnation' +import type { TerminalExitCause } from '../../../../shared/terminal-exit-cause' import { OPERATOR_CLOSE_EXIT_CAUSE, resolveUnreportedExitCause -} from '../../shared/terminal-exit-cause' -import { SSH_EXIT_UNCONFIRMED_REASON } from '../../shared/pty-liveness-verdict' -import type { RetiredTerminalSurface } from './mobile-session-terminal-retirement' -import { parsePaneKey } from '../../shared/stable-pane-id' -import { advertisedUrlWatcher } from '../ports/advertised-url-watcher' +} from '../../../../shared/terminal-exit-cause' +import { SSH_EXIT_UNCONFIRMED_REASON } from '../../../../shared/pty-liveness-verdict' +import type { RetiredTerminalSurface } from '../../mobile-session-terminal-retirement' +import { parsePaneKey } from '../../../../shared/stable-pane-id' +import { advertisedUrlWatcher } from '../../../ports/advertised-url-watcher' +import type { PtyLivenessVerdict } from '../../../../shared/pty-liveness-verdict' +import type { + RuntimePtyWorktreeRecord, + RuntimeLeafRecord +} from '../../runtime-terminal-state-records' + +export abstract class OrcaRuntimeWithOnPtyExit extends OrcaRuntimeWithOnClientDisconnected { + protected abstract pruneDisconnectedPtyRecords(): void + + protected abstract failActiveDispatchOnExit( + handle: string, + paneKey: string | null, + exitCode: number, + cause: TerminalExitCause + ): void + + protected abstract resolveExitWaiters(leaf: RuntimeLeafRecord): void + + protected abstract pruneDisconnectedPtyTranscript(pty: RuntimePtyWorktreeRecord): void + + protected abstract resolvePtyExitWaiters(pty: RuntimePtyWorktreeRecord, ptyId: string): void + + protected abstract cancelPendingDriverMutations(ptyId: string): void + + protected abstract rememberPtyLivenessVerdict(ptyId: string, verdict: PtyLivenessVerdict): void -export class OrcaRuntimeWithOnPtyExit extends OrcaRuntimeWithOnClientDisconnected { onPtyExit( ptyId: string, exitCode: number, diff --git a/src/main/runtime/orca-runtime-invalidate-all-handles-for-pty.ts b/src/main/runtime/terminal/lifecycle/pty-handle-invalidation.ts similarity index 90% rename from src/main/runtime/orca-runtime-invalidate-all-handles-for-pty.ts rename to src/main/runtime/terminal/lifecycle/pty-handle-invalidation.ts index 96699ebbafe..c56ad56d162 100644 --- a/src/main/runtime/orca-runtime-invalidate-all-handles-for-pty.ts +++ b/src/main/runtime/terminal/lifecycle/pty-handle-invalidation.ts @@ -1,10 +1,17 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithResolveKnownWorkspaceFileTarget } from './orca-runtime-resolve-known-workspace-file-target' -import type { PtyIncarnationId } from '../../shared/pty-incarnation' -import { getPtyExecutionHost } from '../../shared/terminal-execution-host' -import { getConnectionExecutionHostId, parseExecutionHostId } from '../../shared/execution-host' +import { OrcaRuntimeWithResolveKnownWorkspaceFileTarget } from '../../orca-runtime-resolve-known-workspace-file-target' +import type { PtyIncarnationId } from '../../../../shared/pty-incarnation' +import { getPtyExecutionHost } from '../../../../shared/terminal-execution-host' +import { + getConnectionExecutionHostId, + parseExecutionHostId +} from '../../../../shared/execution-host' +import type { RuntimePtyWorktreeRecord } from '../../runtime-terminal-state-records' + +export abstract class OrcaRuntimeWithInvalidateAllHandlesForPty extends OrcaRuntimeWithResolveKnownWorkspaceFileTarget { + protected abstract getOrCreatePtyWorktreeRecord(ptyId: string): RuntimePtyWorktreeRecord | null + + protected abstract rejectWaitersForHandle(handle: string, code: string): void -export class OrcaRuntimeWithInvalidateAllHandlesForPty extends OrcaRuntimeWithResolveKnownWorkspaceFileTarget { protected invalidateAllHandlesForPty(ptyId: string, preserveHandle?: string): Set { const incarnationHandle = this.handleByPtyIncarnation.get(ptyId)?.handle const preallocatedHandle = this.handleByPtyId.get(ptyId) diff --git a/src/main/runtime/orca-runtime-register-pty.ts b/src/main/runtime/terminal/lifecycle/pty-registration.ts similarity index 88% rename from src/main/runtime/orca-runtime-register-pty.ts rename to src/main/runtime/terminal/lifecycle/pty-registration.ts index cdb8542ba60..7d6a662a82c 100644 --- a/src/main/runtime/orca-runtime-register-pty.ts +++ b/src/main/runtime/terminal/lifecycle/pty-registration.ts @@ -1,13 +1,24 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithInvalidateAllHandlesForPty } from './orca-runtime-invalidate-all-handles-for-pty' -import type { PtyIncarnationId } from '../../shared/pty-incarnation' -import type { TuiAgent } from '../../shared/tui-agent' -import { isValidTerminalTabId } from '../../shared/terminal-tab-id' -import { isTerminalLeafId, makePaneKey } from '../../shared/stable-pane-id' -import { isTuiAgent } from '../../shared/tui-agent-config' -import { spawnSurfaceClaimSequence } from './pty-recorded-surface-topology' +import { OrcaRuntimeWithInvalidateAllHandlesForPty } from './pty-handle-invalidation' +import type { PtyIncarnationId } from '../../../../shared/pty-incarnation' +import type { TuiAgent } from '../../../../shared/tui-agent' +import { isValidTerminalTabId } from '../../../../shared/terminal-tab-id' +import { isTerminalLeafId, makePaneKey } from '../../../../shared/stable-pane-id' +import { isTuiAgent } from '../../../../shared/tui-agent-config' +import { spawnSurfaceClaimSequence } from '../../pty-recorded-surface-topology' +import type { RuntimePtyWorktreeRecord } from '../../runtime-terminal-state-records' +import type { OrchestrationCompatibilityTerminalAuthority } from '../../runtime-terminal-contracts' +import type { RuntimeMobileSessionCreateTerminalResult } from '../../../../shared/runtime-session-contracts' + +export abstract class OrcaRuntimeWithRegisterPty extends OrcaRuntimeWithInvalidateAllHandlesForPty { + protected abstract ensurePtyBackedMobileSurfaceForRendererTab( + worktreeId: string, + tabId: string + ): RuntimeMobileSessionCreateTerminalResult | null + + protected abstract getOrchestrationCompatibilityHostScope( + pty: RuntimePtyWorktreeRecord + ): OrchestrationCompatibilityTerminalAuthority['hostScope'] | null -export class OrcaRuntimeWithRegisterPty extends OrcaRuntimeWithInvalidateAllHandlesForPty { registerPty( ptyId: string, worktreeId: string, diff --git a/src/main/runtime/orca-runtime-refresh-pty-worktree-records-from-controller.ts b/src/main/runtime/terminal/lifecycle/pty-worktree-refresh.ts similarity index 57% rename from src/main/runtime/orca-runtime-refresh-pty-worktree-records-from-controller.ts rename to src/main/runtime/terminal/lifecycle/pty-worktree-refresh.ts index 17d3ed13934..4c1db281f21 100644 --- a/src/main/runtime/orca-runtime-refresh-pty-worktree-records-from-controller.ts +++ b/src/main/runtime/terminal/lifecycle/pty-worktree-refresh.ts @@ -1,8 +1,9 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithRecordPtyWorktree } from './terminal/identity/pty-worktree-record' -import type { ResolvedWorktree } from './runtime-worktree-path-identity' +import { OrcaRuntimeWithRecordPtyWorktree } from '../identity/pty-worktree-record' +import type { ResolvedWorktree } from '../../runtime-worktree-path-identity' + +export abstract class OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController extends OrcaRuntimeWithRecordPtyWorktree { + protected abstract refreshFloatingWorkspacePtyLiveness(): Set | null -export class OrcaRuntimeWithRefreshPtyWorktreeRecordsFromController extends OrcaRuntimeWithRecordPtyWorktree { /** Synchronizes PTY tracking records with running daemon sessions, querying their foreground agent states. */ protected async refreshPtyWorktreeRecordsFromController( resolvedWorktrees: ResolvedWorktree[],