mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
refactor(runtime): check terminal creation and splitting (#27708)
This commit is contained in:
@@ -18,9 +18,9 @@ const LAYOUT_AUTHORITIES = [
|
||||
'src/main/persistence/restoring-sessions/session-owner-fields.ts',
|
||||
'src/main/persistence/loading-store/pty-binding-persistence.ts',
|
||||
'src/main/remote-layout-snapshot/index.ts',
|
||||
'src/main/runtime/orca-runtime-split-pty-backed-terminal.ts',
|
||||
'src/main/runtime/terminal/launch/terminal-split-surface.ts',
|
||||
'src/main/runtime/session-tabs/headless-tab-group-moves.ts',
|
||||
'src/main/runtime/orca-runtime-create-terminal.ts',
|
||||
'src/main/runtime/terminal/launch/terminal-create.ts',
|
||||
'src/renderer/src/runtime/mobile-session-tab-close.ts',
|
||||
'src/renderer/src/runtime/runtime-layout-client.ts',
|
||||
'src/renderer/src/store/slices/tabs.ts',
|
||||
@@ -36,7 +36,7 @@ const STACK_WRITERS = [
|
||||
'src/renderer/src/components/terminal-pane/TerminalPane.tsx',
|
||||
'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/terminal/launch/terminal-split.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',
|
||||
|
||||
@@ -65,6 +65,12 @@ const LAYOUT_ORACLE_PREFIXES = [
|
||||
'config/scripts/pr-layout-oracle-route.mjs'
|
||||
]
|
||||
|
||||
const LAUNCH_LAYOUT_WRITERS = [
|
||||
'src/main/runtime/terminal/launch/terminal-create.ts',
|
||||
'src/main/runtime/terminal/launch/terminal-split.ts',
|
||||
'src/main/runtime/terminal/launch/terminal-split-surface.ts'
|
||||
]
|
||||
|
||||
// Runtime matches only the modules that create, move, close, persist or publish layout.
|
||||
const LAYOUT_ORACLE_RUNTIME_MODULE =
|
||||
/^src\/(?:main|renderer\/src)\/runtime\/(?:[^/]+\/)*[^/]*(?:pane-admission|workspace-layout|session-tab|headless-[^/]*tab|layout-client|layout-command-router|layout-projection|create-terminal|split-terminal|split-pty-backed-terminal|terminal-split-layout|spawn-placement|sync-window-graph|runtime-graph|leaf-move)[^/]*\.ts$/
|
||||
@@ -81,6 +87,7 @@ export function isLayoutOracleSource(file) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
LAUNCH_LAYOUT_WRITERS.includes(file) ||
|
||||
LAYOUT_ORACLE_PREFIXES.some((prefix) => file.startsWith(prefix)) ||
|
||||
LAYOUT_ORACLE_RUNTIME_MODULE.test(file)
|
||||
)
|
||||
|
||||
@@ -9,11 +9,6 @@ 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-mobile-session-tab.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
|
||||
src/main/runtime/orca-runtime-create-runtime-owned-mobile-session-terminal.ts
|
||||
src/main/runtime/orca-runtime-create-terminal-desktop.ts
|
||||
src/main/runtime/orca-runtime-create-terminal.ts
|
||||
src/main/runtime/orca-runtime-deliver-pending-messages.ts
|
||||
src/main/runtime/orca-runtime-automation-operations.ts
|
||||
src/main/runtime/orca-runtime-file-commands.ts
|
||||
@@ -33,7 +28,6 @@ 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-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
|
||||
@@ -43,22 +37,17 @@ 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-browser-network-execution-host-for-worktree.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
|
||||
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
|
||||
src/main/runtime/orca-runtime-serialize-agent-prompt-submission.ts
|
||||
src/main/runtime/orca-runtime-sleep-resolved-worktree-terminals.ts
|
||||
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-requested-pty-ids.ts
|
||||
src/main/runtime/orca-runtime-stop-terminals-for-worktree.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-verify-orchestration-compatibility-caller.ts
|
||||
|
||||
@@ -6,6 +6,8 @@ import type { GlobalSettings } from '../../shared/global-settings-types'
|
||||
import type { FloatingTerminalCwdRequest } from '../../shared/ui-chrome-types'
|
||||
import type { Store } from '../persistence'
|
||||
|
||||
type FloatingTerminalSettings = Pick<GlobalSettings, 'floatingTerminalTrustedCwds'>
|
||||
|
||||
const FLOATING_WORKSPACE_DIRNAME = 'floating-workspace'
|
||||
|
||||
function expandHomePath(input: string, home: string): string {
|
||||
@@ -41,7 +43,7 @@ async function canonicalizeAccessibleDirectory(dirPath: string): Promise<string
|
||||
}
|
||||
}
|
||||
|
||||
function getTrustedFloatingWorkspaceDirectories(settings: GlobalSettings): Set<string> {
|
||||
function getTrustedFloatingWorkspaceDirectories(settings: FloatingTerminalSettings): Set<string> {
|
||||
return new Set(
|
||||
(settings.floatingTerminalTrustedCwds ?? [])
|
||||
.map((trustedPath) => trustedPath.trim())
|
||||
@@ -63,7 +65,7 @@ async function getPreservedTrustedFloatingWorkspaceDirectories(
|
||||
|
||||
function isTrustedFloatingWorkspaceDirectory(
|
||||
canonicalDirPath: string,
|
||||
settings: GlobalSettings
|
||||
settings: FloatingTerminalSettings
|
||||
): boolean {
|
||||
return getTrustedFloatingWorkspaceDirectories(settings).has(path.resolve(canonicalDirPath))
|
||||
}
|
||||
@@ -81,7 +83,7 @@ export async function ensureDefaultFloatingWorkspacePath(): Promise<string> {
|
||||
}
|
||||
|
||||
export async function resolveFloatingTerminalCwd(
|
||||
store: Store,
|
||||
store: { getSettings(): FloatingTerminalSettings },
|
||||
args?: FloatingTerminalCwdRequest
|
||||
): Promise<string> {
|
||||
const configuredPath = typeof args?.path === 'string' ? args.path.trim() : ''
|
||||
|
||||
@@ -82,6 +82,62 @@ describe('materializeWorktreePushTargetRemote', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('preserves the legacy null-store failure after existing-remote adoption work', async () => {
|
||||
const commands: string[][] = []
|
||||
gitExecFileAsyncMock.mockImplementation(async (args: string[]) => {
|
||||
commands.push(args)
|
||||
if (args[0] === 'remote' && args[1] === 'get-url') {
|
||||
return { stdout: `${FORK_URL}\n`, stderr: '' }
|
||||
}
|
||||
if (args[0] === 'config' && args[1] === '--get-all') {
|
||||
throw new Error('no such section')
|
||||
}
|
||||
if (args[0] === 'symbolic-ref') {
|
||||
return { stdout: 'contributor/fix\n', stderr: '' }
|
||||
}
|
||||
return { stdout: '', stderr: '' }
|
||||
})
|
||||
await expect(
|
||||
materializeWorktreePushTargetRemote(REPO_PATH, forkTarget(), null, 'repo-1')
|
||||
).rejects.toBeInstanceOf(TypeError)
|
||||
expect(commands).toEqual([
|
||||
['remote', 'get-url', FORK_REMOTE],
|
||||
['config', '--get-all', `remote.${FORK_REMOTE}.fetch`],
|
||||
[
|
||||
'config',
|
||||
'--add',
|
||||
`remote.${FORK_REMOTE}.fetch`,
|
||||
`+refs/heads/contributor/fix*:refs/remotes/${FORK_REMOTE}/contributor/fix*`
|
||||
],
|
||||
['config', `remote.${FORK_REMOTE}.tagOpt`, '--no-tags'],
|
||||
['rev-parse', '--verify', '--quiet', `refs/remotes/${FORK_REMOTE}/contributor/fix`],
|
||||
['symbolic-ref', '--short', 'HEAD'],
|
||||
['branch', '--set-upstream-to', `${FORK_REMOTE}/contributor/fix`, 'contributor/fix']
|
||||
])
|
||||
})
|
||||
|
||||
it('accepts a null store when both persistence identifiers are omitted', async () => {
|
||||
gitExecFileAsyncMock.mockImplementation(async (args: string[]) => {
|
||||
if (args[0] === 'remote' && args[1] === 'get-url') {
|
||||
return { stdout: `${FORK_URL}\n`, stderr: '' }
|
||||
}
|
||||
if (args[0] === 'config' && args[1] === '--get-all') {
|
||||
throw new Error('no such section')
|
||||
}
|
||||
if (args[0] === 'symbolic-ref') {
|
||||
return { stdout: 'contributor/fix\n', stderr: '' }
|
||||
}
|
||||
return { stdout: '', stderr: '' }
|
||||
})
|
||||
const target = forkTarget()
|
||||
|
||||
await expect(materializeWorktreePushTargetRemote(REPO_PATH, target, null)).resolves.toBe(target)
|
||||
expect(gitExecFileAsyncMock).toHaveBeenLastCalledWith(
|
||||
['branch', '--set-upstream-to', `${FORK_REMOTE}/${target.branchName}`, 'contributor/fix'],
|
||||
{ cwd: REPO_PATH }
|
||||
)
|
||||
})
|
||||
|
||||
it('materializes the remote (add + provenance + fetch) when the probe misses', async () => {
|
||||
gitExecFileAsyncMock.mockImplementation(async (args: string[]) => {
|
||||
if (args[0] === 'remote' && args[1] === 'get-url') {
|
||||
|
||||
@@ -641,7 +641,7 @@ async function getSelectedHostedReviewForBranch(
|
||||
export async function prepareWorktreePushTarget(
|
||||
repoPath: string,
|
||||
target: GitPushTarget,
|
||||
store?: WorktreePushTargetStore,
|
||||
store?: WorktreePushTargetStore | null,
|
||||
repoId?: string,
|
||||
gitOptions: { wslDistro?: string } = {}
|
||||
): Promise<GitPushTarget> {
|
||||
@@ -686,7 +686,7 @@ export async function prepareWorktreePushTarget(
|
||||
export async function materializeWorktreePushTargetRemote(
|
||||
repoPath: string,
|
||||
target: GitPushTarget,
|
||||
store?: WorktreePushTargetStore,
|
||||
store?: WorktreePushTargetStore | null,
|
||||
repoId?: string,
|
||||
gitOptions: { wslDistro?: string } = {},
|
||||
worktreeId?: string
|
||||
@@ -757,7 +757,7 @@ async function adoptExistingForkRemoteForBranch(
|
||||
repoPath: string,
|
||||
target: GitPushTarget,
|
||||
gitOptions: { wslDistro?: string },
|
||||
store: WorktreePushTargetStore | undefined,
|
||||
store: WorktreePushTargetStore | null | undefined,
|
||||
repoId: string | undefined,
|
||||
worktreeId: string | undefined
|
||||
): Promise<GitPushTarget> {
|
||||
@@ -791,7 +791,8 @@ async function adoptExistingForkRemoteForBranch(
|
||||
const owned =
|
||||
store !== undefined &&
|
||||
ownerRepoId !== undefined &&
|
||||
isPushTargetRemoteCreatedByKnownWorktree(store, restored, ownerRepoId)
|
||||
// SAFETY: Legacy null keeps this branch's original failure rather than introducing a fallback.
|
||||
isPushTargetRemoteCreatedByKnownWorktree(store!, restored, ownerRepoId)
|
||||
const adopted = owned ? { ...restored, remoteCreated: true } : restored
|
||||
persistMaterializedPushTargetIfCreated(store, worktreeId, adopted)
|
||||
return adopted
|
||||
@@ -831,7 +832,7 @@ function runForkRemoteAdoption<T>(
|
||||
// predates `markRemoteOrcaCreated` (no git-config marker either). `setWorktreeMeta` is
|
||||
// optional on `WorktreePushTargetStore` so narrow test/reconciliation stores keep compiling.
|
||||
function persistMaterializedPushTargetIfCreated(
|
||||
store: WorktreePushTargetStore | undefined,
|
||||
store: WorktreePushTargetStore | null | undefined,
|
||||
worktreeId: string | undefined,
|
||||
target: GitPushTarget
|
||||
): void {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getLatestPtyTitle } from './runtime-worktree-status-projection'
|
||||
import type { PtyLivenessVerdict } from '../../shared/pty-liveness-verdict'
|
||||
|
||||
export abstract class OrcaRuntimeWithAdoptTerminalOrphansFromInventory extends OrcaRuntimeWithSubscribeToTerminalResize {
|
||||
protected abstract assertGraphReady(): void
|
||||
abstract assertGraphReady(): void
|
||||
|
||||
protected async adoptTerminalOrphansFromInventoryUnderMutation(
|
||||
request: RuntimeTerminalOrphanAdoptionRequest,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types'
|
||||
import { OrcaRuntimeWithGetAgentSessionExecutionNamespace } from './orca-runtime-get-agent-session-execution-namespace'
|
||||
import type {
|
||||
RuntimeAgentSessionRpcCaller,
|
||||
@@ -27,7 +28,30 @@ import {
|
||||
import { agentStartedTelemetry } from '../agent-launch/agent-started-telemetry'
|
||||
import { withAgentSessionAccountEnv } from './agent-session-account-env'
|
||||
|
||||
export class OrcaRuntimeWithCreateAgentSession extends OrcaRuntimeWithGetAgentSessionExecutionNamespace {
|
||||
export abstract class OrcaRuntimeWithCreateAgentSession extends OrcaRuntimeWithGetAgentSessionExecutionNamespace {
|
||||
abstract resolveWorkspaceTerminalStartupCwd(
|
||||
workspace: Pick<TerminalWorkspaceLaunchScope, 'path'>,
|
||||
requestedCwd?: string | null
|
||||
): string | undefined
|
||||
|
||||
protected abstract buildTerminalWorkspaceEnv(
|
||||
scope: TerminalWorkspaceLaunchScope,
|
||||
baseEnv: Record<string, string>,
|
||||
paneKey: string,
|
||||
tabId: string,
|
||||
agentTeamsEnv?: Record<string, string>
|
||||
): Promise<Record<string, string>>
|
||||
|
||||
abstract getAuthoritativeWindow(): BrowserWindow
|
||||
|
||||
abstract waitForTerminalHandle(tabId: string, timeoutMs?: number): Promise<string>
|
||||
|
||||
protected abstract reconcileRemoteTerminalCreate(
|
||||
worktreeId: string,
|
||||
terminalHandle: string,
|
||||
connectionId?: string | null
|
||||
): Promise<RuntimeTerminalCreate | null>
|
||||
|
||||
async createAgentSession(
|
||||
request: RuntimeCreateAgentSessionRequest,
|
||||
caller: RuntimeAgentSessionRpcCaller = {}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import * as dependencies from './orca-runtime-create-terminal-dependencies'
|
||||
import type { OrcaRuntimeWithCreateTerminal } from './orca-runtime-create-terminal'
|
||||
import type { OrcaRuntimeWithCreateTerminal } from './terminal/launch/terminal-create'
|
||||
import type { RuntimeTerminalPresentation } from '../../shared/runtime-types'
|
||||
|
||||
// A create with no workspace lands in whatever the window is showing, so only the window can make it.
|
||||
|
||||
@@ -32,7 +32,7 @@ class DefaultTabRuntime extends OrcaRuntimeService {
|
||||
super(store)
|
||||
}
|
||||
|
||||
protected override async resolveTerminalWorkspaceLaunchScope(): Promise<TerminalWorkspaceLaunchScope> {
|
||||
override async resolveTerminalWorkspaceLaunchScope(): Promise<TerminalWorkspaceLaunchScope> {
|
||||
return {
|
||||
id: WORKTREE_ID,
|
||||
path: '/repo/app',
|
||||
|
||||
@@ -35,7 +35,7 @@ class MigratedPaneRuntime extends OrcaRuntimeService {
|
||||
super(store)
|
||||
}
|
||||
|
||||
protected override async resolveTerminalWorkspaceLaunchScope(): Promise<TerminalWorkspaceLaunchScope> {
|
||||
override async resolveTerminalWorkspaceLaunchScope(): Promise<TerminalWorkspaceLaunchScope> {
|
||||
return {
|
||||
id: WORKTREE_ID,
|
||||
path: worktreePath,
|
||||
|
||||
@@ -60,7 +60,7 @@ export abstract class OrcaRuntimeWithPreservedBranchCleanup extends OrcaRuntimeW
|
||||
folderWorkspace: FolderWorkspace
|
||||
): ResolvedWorktree
|
||||
|
||||
protected abstract resolveTerminalWorkspaceLaunchScope(
|
||||
abstract resolveTerminalWorkspaceLaunchScope(
|
||||
selector: string,
|
||||
createdWorktree?: Worktree
|
||||
): Promise<TerminalWorkspaceLaunchScope>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import type { OrcaRuntimeWithCreateTerminal } from './orca-runtime-create-terminal'
|
||||
import type { OrcaRuntimeWithCreateTerminal } from './terminal/launch/terminal-create'
|
||||
import { OrcaRuntimeWithGetTerminalInteractiveWait } from './terminal/wait/terminal-interactive-wait'
|
||||
import type {
|
||||
RuntimeTerminalRead,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithSplitPtyBackedTerminal } from './orca-runtime-split-pty-backed-terminal'
|
||||
import { OrcaRuntimeWithSplitPtyBackedTerminal } from './terminal/launch/terminal-split-surface'
|
||||
import {
|
||||
resolveTerminalSessionWorktreeId,
|
||||
runtimeWorktreeIdsEqual
|
||||
|
||||
@@ -25,7 +25,7 @@ export class OrcaRuntimeWithResolveWaiter extends OrcaRuntimeWithDeliverPendingM
|
||||
return `${tabId}::${leafId}`
|
||||
}
|
||||
|
||||
protected getAuthoritativeWindow(): BrowserWindow {
|
||||
getAuthoritativeWindow(): BrowserWindow {
|
||||
const win = this.getAvailableAuthoritativeWindow()
|
||||
if (!win || win.isDestroyed()) {
|
||||
throw new Error('No renderer window available')
|
||||
|
||||
@@ -289,7 +289,7 @@ export class OrcaRuntimeWithRuntimeId {
|
||||
// ptyId keeps active TUI redraws independent of the total open terminal count.
|
||||
protected leavesByPtyId = new Map<string, RuntimeLeafRecord[]>()
|
||||
|
||||
protected handles = new Map<string, TerminalHandleRecord>()
|
||||
handles = new Map<string, TerminalHandleRecord>()
|
||||
|
||||
protected handleByLeafKey = new Map<string, string>()
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types'
|
||||
import type { RuntimePtyController } from './runtime-pty-controller-contract'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { PtyProcessInfo } from '../providers/pty-provider-contract'
|
||||
import type { RuntimeTerminalCreate } from '../../shared/runtime-types'
|
||||
import type { RuntimeTerminalSplit } from '../../shared/runtime-terminal-contracts'
|
||||
import { OrcaRuntimeService } from './orca-runtime'
|
||||
import { deriveRemoteRuntimeTerminalCreateHandle } from './remote-runtime-terminal-create-identity'
|
||||
import { RemoteRuntimeTerminalCreateIdempotency } from './remote-runtime-terminal-create-idempotency'
|
||||
@@ -380,3 +383,401 @@ describe('terminal create reconciliation scopes inventory to the owning executio
|
||||
expect(listProcesses).toHaveBeenCalledWith('local')
|
||||
})
|
||||
})
|
||||
|
||||
function scope(): TerminalWorkspaceLaunchScope {
|
||||
return {
|
||||
id: 'repo::workspace',
|
||||
path: '/workspace',
|
||||
connectionId: null,
|
||||
repo: null,
|
||||
folderWorkspace: null
|
||||
}
|
||||
}
|
||||
function replacement(): never {
|
||||
throw new Error('an argument getter replaced an already captured runtime method')
|
||||
}
|
||||
|
||||
describe('launch runtime dispatch and allocation phases', () => {
|
||||
it('retains native async method metadata on the original runtime frame', () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
expect(runtime.dedupeTerminalCreate.constructor.name).toBe('AsyncFunction')
|
||||
expect(runtime.dedupeTerminalCreate.length).toBe(5)
|
||||
expect(runtime.dedupeWorktreeCreate.constructor.name).toBe('Function')
|
||||
expect(runtime.dedupeWorktreeCreate.length).toBe(3)
|
||||
})
|
||||
|
||||
it('retains first-Promise behavior when caller operation keys collide', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const firstFactory = vi.fn(async () => ({ worktree: 'first' }))
|
||||
const secondFactory = vi.fn(async () => ({ agentLaunch: 'second' }))
|
||||
const first = runtime.dedupeWorktreeCreate('repo', 'agent.launch:mutation', firstFactory)
|
||||
const second = runtime.dedupeWorktreeCreate('repo', 'agent.launch:mutation', secondFactory)
|
||||
expect(second).toBe(first)
|
||||
await expect(first).resolves.toEqual({ worktree: 'first' })
|
||||
expect(firstFactory).toHaveBeenCalledTimes(1)
|
||||
expect(secondFactory).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('captures private agent-session reconciliation before identity getters', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const result = createdTerminal('reclaimed')
|
||||
const events: string[] = []
|
||||
Object.defineProperty(runtime, 'reconcileRemoteTerminalCreate', {
|
||||
configurable: true,
|
||||
get: () => {
|
||||
events.push('read-reconcile')
|
||||
return async function (this: OrcaRuntimeService, ...args: unknown[]) {
|
||||
expect(this).toBe(runtime)
|
||||
expect(args).toEqual(['worktree-1', 'reclaimed', null])
|
||||
return result
|
||||
}
|
||||
}
|
||||
})
|
||||
const identity = {
|
||||
get worktreeId() {
|
||||
events.push('read-worktree')
|
||||
Object.defineProperty(runtime, 'reconcileRemoteTerminalCreate', { value: replacement })
|
||||
return 'worktree-1'
|
||||
},
|
||||
terminalHandle: 'reclaimed',
|
||||
connectionId: null
|
||||
}
|
||||
await expect(runtime['reclaimFencedAgentSessionSpawn'](identity)).resolves.toBe(result)
|
||||
expect(events).toEqual(['read-reconcile', 'read-worktree'])
|
||||
})
|
||||
|
||||
it('retains direct spawn failure and withdrawal after awaited admission removes spawn', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const entered = Promise.withResolvers<void>()
|
||||
const ready = Promise.withResolvers<void>()
|
||||
const events: string[] = []
|
||||
const spawn = vi.fn(async () => {
|
||||
throw new Error('removed spawn must not run')
|
||||
})
|
||||
const controller: Pick<RuntimePtyController, 'spawn'> = { spawn }
|
||||
const store = {
|
||||
admitTerminalPane: async function () {
|
||||
expect(this).toBe(store)
|
||||
events.push('admit')
|
||||
entered.resolve()
|
||||
await ready.promise
|
||||
delete controller.spawn
|
||||
events.push('removed')
|
||||
return 'admitted'
|
||||
},
|
||||
withdrawTerminalPane: vi.fn(async function (this: typeof store): Promise<void> {
|
||||
expect(this).toBe(store)
|
||||
events.push('withdraw')
|
||||
})
|
||||
}
|
||||
Object.defineProperty(runtime, 'store', { value: store })
|
||||
Object.defineProperty(runtime, 'ptyController', { value: controller })
|
||||
Object.defineProperty(runtime, 'getAvailableAuthoritativeWindow', { value: () => null })
|
||||
Object.defineProperty(runtime, 'resolveTerminalWorkspaceLaunchScope', {
|
||||
value: async () => scope()
|
||||
})
|
||||
Object.defineProperty(runtime, 'resolveAgentTerminalCreateOptions', {
|
||||
value: async (_workspace: unknown, opts: unknown) => opts
|
||||
})
|
||||
Object.defineProperty(runtime, 'buildTerminalWorkspaceEnv', { value: async () => ({}) })
|
||||
const pending = runtime.createTerminal('workspace')
|
||||
await entered.promise
|
||||
expect(events).toEqual(['admit'])
|
||||
expect(store.withdrawTerminalPane).not.toHaveBeenCalled()
|
||||
ready.resolve()
|
||||
await expect(pending).rejects.toBeInstanceOf(TypeError)
|
||||
expect(events).toEqual(['admit', 'removed', 'withdraw'])
|
||||
expect(store.withdrawTerminalPane).toHaveBeenCalledTimes(1)
|
||||
expect(spawn).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reenters the split override and captures it before the PTY argument getter', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const pty = runtime['recordPtyWorktree']('pty', 'repo::workspace')
|
||||
const result: RuntimeTerminalSplit = {
|
||||
handle: 'split',
|
||||
tabId: 'tab',
|
||||
leafId: 'leaf',
|
||||
paneRuntimeId: 1
|
||||
}
|
||||
const events: string[] = []
|
||||
Object.defineProperty(runtime, 'splitPtyBackedTerminal', {
|
||||
configurable: true,
|
||||
get: () => {
|
||||
events.push('read-split')
|
||||
return async function (
|
||||
this: OrcaRuntimeService,
|
||||
actual: unknown
|
||||
): Promise<RuntimeTerminalSplit> {
|
||||
expect(this).toBe(runtime)
|
||||
expect(actual).toBe(pty)
|
||||
return result
|
||||
}
|
||||
}
|
||||
})
|
||||
Object.defineProperty(runtime, 'getLivePtyForHandle', {
|
||||
value: () => ({
|
||||
record: null,
|
||||
get pty() {
|
||||
events.push('read-pty')
|
||||
Object.defineProperty(runtime, 'splitPtyBackedTerminal', { value: replacement })
|
||||
return pty
|
||||
}
|
||||
})
|
||||
})
|
||||
await expect(runtime.splitTerminal('handle')).resolves.toBe(result)
|
||||
expect(events).toEqual(['read-split', 'read-pty'])
|
||||
})
|
||||
|
||||
it('reenters workspace target resolution on the runtime', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const workspace = scope()
|
||||
Object.defineProperty(runtime, 'resolveTerminalWorkspaceLaunchTarget', {
|
||||
value: async function (this: OrcaRuntimeService, selector: string) {
|
||||
expect(this).toBe(runtime)
|
||||
expect(selector).toBe('folder')
|
||||
return { scope: workspace, managedWorktree: null }
|
||||
}
|
||||
})
|
||||
await expect(runtime['resolveTerminalWorkspaceLaunchScope']('folder')).resolves.toBe(workspace)
|
||||
})
|
||||
|
||||
it('reads reconciliation and its canonical cache after workspace resolution', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const ready = Promise.withResolvers<TerminalWorkspaceLaunchScope>()
|
||||
const result: RuntimeTerminalCreate = {
|
||||
handle: 'adopted',
|
||||
worktreeId: 'repo::workspace',
|
||||
title: null
|
||||
}
|
||||
Object.defineProperty(runtime, 'resolveTerminalWorkspaceLaunchScope', {
|
||||
value: () => ready.promise
|
||||
})
|
||||
const run = vi.fn(async () => result)
|
||||
const pending = runtime.dedupeTerminalCreate('device', 'workspace', 'mutation', true, run)
|
||||
const events: string[] = []
|
||||
Object.defineProperty(runtime, 'reconcileRemoteTerminalCreate', {
|
||||
value: async function (
|
||||
this: OrcaRuntimeService,
|
||||
worktreeId: string,
|
||||
_handle: string,
|
||||
connection: null
|
||||
) {
|
||||
expect(this).toBe(runtime)
|
||||
expect(worktreeId).toBe('repo::workspace')
|
||||
expect(connection).toBeNull()
|
||||
events.push('reconcile')
|
||||
return result
|
||||
}
|
||||
})
|
||||
const cache = runtime['terminalCreateIdempotency']
|
||||
Object.defineProperty(runtime, 'terminalCreateIdempotency', {
|
||||
get: () => {
|
||||
events.push('read-cache')
|
||||
return cache
|
||||
}
|
||||
})
|
||||
ready.resolve(scope())
|
||||
await expect(pending).resolves.toBe(result)
|
||||
expect(events).toEqual(['read-cache', 'reconcile'])
|
||||
expect(run).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('captures startup and create overrides before option getters', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const repo = {
|
||||
id: 'repo',
|
||||
path: '/workspace',
|
||||
displayName: 'repo',
|
||||
badgeColor: '#000',
|
||||
addedAt: 0
|
||||
}
|
||||
const terminal: RuntimeTerminalCreate = {
|
||||
handle: 'terminal',
|
||||
worktreeId: 'repo::workspace',
|
||||
title: null
|
||||
}
|
||||
Object.defineProperty(runtime, 'store', {
|
||||
value: { getRepos: () => [repo], getRepo: () => repo }
|
||||
})
|
||||
Object.defineProperty(runtime, 'resolveWorktreeSelector', {
|
||||
value: async () => ({ id: 'repo::workspace', repoId: 'repo', path: '/workspace' })
|
||||
})
|
||||
const events: string[] = []
|
||||
Object.defineProperty(runtime, 'buildStartupForAgent', {
|
||||
configurable: true,
|
||||
get: () => {
|
||||
events.push('read-startup')
|
||||
return function (this: OrcaRuntimeService) {
|
||||
expect(this).toBe(runtime)
|
||||
return { agent: 'codex', startup: { command: 'codex' } }
|
||||
}
|
||||
}
|
||||
})
|
||||
Object.defineProperty(runtime, 'createTerminal', {
|
||||
configurable: true,
|
||||
get: () => {
|
||||
events.push('read-create')
|
||||
return async function (this: OrcaRuntimeService) {
|
||||
expect(this).toBe(runtime)
|
||||
return terminal
|
||||
}
|
||||
}
|
||||
})
|
||||
await expect(
|
||||
runtime.launchAgentTerminal('workspace', {
|
||||
get agent(): 'codex' {
|
||||
events.push('read-agent')
|
||||
Object.defineProperty(runtime, 'buildStartupForAgent', { value: replacement })
|
||||
return 'codex'
|
||||
},
|
||||
prompt: 'prompt',
|
||||
get title(): string {
|
||||
events.push('read-title')
|
||||
Object.defineProperty(runtime, 'createTerminal', { value: replacement })
|
||||
return 'title'
|
||||
}
|
||||
})
|
||||
).resolves.toBe(terminal)
|
||||
expect(events).toEqual(['read-startup', 'read-agent', 'read-create', 'read-title'])
|
||||
})
|
||||
|
||||
it('captures admission store and controller before spawn argument getters', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const expected = new Error('captured controller refusal')
|
||||
const admission = vi.fn(async () => undefined)
|
||||
const originalStore = { admitTerminalPane: admission }
|
||||
let currentStore = originalStore
|
||||
const originalController = {
|
||||
spawn: async () => {
|
||||
throw expected
|
||||
}
|
||||
}
|
||||
let currentController = originalController
|
||||
Object.defineProperty(runtime, 'store', { get: () => currentStore })
|
||||
Object.defineProperty(runtime, 'ptyController', { get: () => currentController })
|
||||
Object.defineProperty(runtime, 'getAvailableAuthoritativeWindow', { value: () => null })
|
||||
Object.defineProperty(runtime, 'resolveTerminalWorkspaceLaunchScope', {
|
||||
value: async () => scope()
|
||||
})
|
||||
Object.defineProperty(runtime, 'resolveAgentTerminalCreateOptions', {
|
||||
value: async (_workspace: unknown, opts: unknown) => opts
|
||||
})
|
||||
Object.defineProperty(runtime, 'buildTerminalWorkspaceEnv', { value: async () => ({}) })
|
||||
await expect(
|
||||
runtime.createTerminal('workspace', {
|
||||
get resumeProviderSession() {
|
||||
currentStore = {
|
||||
admitTerminalPane: vi.fn(async () => {
|
||||
throw new Error('late store')
|
||||
})
|
||||
}
|
||||
currentController = {
|
||||
spawn: async () => {
|
||||
throw new Error('late controller')
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
).rejects.toBe(expected)
|
||||
expect(admission).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('rereads the canonical mobile mutation map after creation and in failure cleanup', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const first = new Map<string, Promise<never>>()
|
||||
const second = new Map<string, Promise<never>>()
|
||||
let current = first
|
||||
const reads: string[] = []
|
||||
Object.defineProperty(runtime, 'mobileTerminalCreateByMutationId', {
|
||||
get: () => {
|
||||
reads.push(current === first ? 'first' : 'second')
|
||||
return current
|
||||
}
|
||||
})
|
||||
const ready = Promise.withResolvers<never>()
|
||||
const expected = new Error('creation refusal')
|
||||
Object.defineProperty(runtime, 'runCreateMobileSessionTerminal', {
|
||||
value: () => {
|
||||
current = second
|
||||
return ready.promise
|
||||
}
|
||||
})
|
||||
const pending = runtime.createMobileSessionTerminal('workspace', {
|
||||
clientMutationId: 'mutation',
|
||||
select: false
|
||||
})
|
||||
expect(first.size).toBe(0)
|
||||
expect(second.size).toBe(1)
|
||||
ready.reject(expected)
|
||||
await expect(pending).rejects.toBe(expected)
|
||||
expect(second.size).toBe(0)
|
||||
expect(reads).toEqual(['first', 'second', 'second', 'second'])
|
||||
})
|
||||
|
||||
it('checks graph readiness on the runtime before resolving its desktop window', async () => {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const expected = new Error('desktop window refusal')
|
||||
const events: string[] = []
|
||||
Object.defineProperty(runtime, 'getAvailableAuthoritativeWindow', { value: () => null })
|
||||
Object.defineProperty(runtime, 'assertGraphReady', {
|
||||
get: () => {
|
||||
events.push('read-ready')
|
||||
return function (this: OrcaRuntimeService) {
|
||||
expect(this).toBe(runtime)
|
||||
events.push('invoke-ready')
|
||||
}
|
||||
}
|
||||
})
|
||||
Object.defineProperty(runtime, 'getAuthoritativeWindow', {
|
||||
value: () => {
|
||||
events.push('read-window')
|
||||
throw expected
|
||||
}
|
||||
})
|
||||
await expect(runtime.createTerminal()).rejects.toBe(expected)
|
||||
expect(events).toEqual(['read-ready', 'invoke-ready', 'read-window'])
|
||||
})
|
||||
|
||||
it('retains the canonical cache allocations after their original preceding clocks', () => {
|
||||
const events: { kind: 'map' | 'clock'; value?: object; stack: string }[] = []
|
||||
const OriginalMap = Map
|
||||
const originalNow = Date.now
|
||||
class TracedMap<Key, Value> extends OriginalMap<Key, Value> {
|
||||
constructor(entries?: Iterable<readonly [Key, Value]> | null) {
|
||||
super(entries)
|
||||
events.push({
|
||||
kind: 'map',
|
||||
value: this,
|
||||
stack: new Error('allocation phase trace').stack ?? ''
|
||||
})
|
||||
}
|
||||
}
|
||||
vi.stubGlobal('Map', TracedMap)
|
||||
const clock = vi.spyOn(Date, 'now').mockImplementation(() => {
|
||||
events.push({ kind: 'clock', stack: new Error('allocation phase trace').stack ?? '' })
|
||||
return originalNow()
|
||||
})
|
||||
try {
|
||||
const runtime = new OrcaRuntimeService(null)
|
||||
const mobileCache = runtime['mobileTerminalCreateByMutationId']
|
||||
const cacheIndex = events.findIndex((event) => event.value === mobileCache)
|
||||
const rootClock = events.findIndex((event) => event.kind === 'clock')
|
||||
expect(cacheIndex).toBeGreaterThanOrEqual(0)
|
||||
expect(cacheIndex).toBeGreaterThan(rootClock)
|
||||
expect(
|
||||
events.filter(
|
||||
(event) =>
|
||||
event.kind === 'map' &&
|
||||
/terminal\/launch\/(?:mobile-terminal-create|terminal-create-identity)\.ts/.test(
|
||||
event.stack.replaceAll('\\', '/')
|
||||
)
|
||||
)
|
||||
).toEqual([])
|
||||
} finally {
|
||||
clock.mockRestore()
|
||||
vi.unstubAllGlobals()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable unicorn/no-useless-spread */
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal } from './orca-runtime-create-runtime-owned-mobile-session-terminal'
|
||||
import { OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal } from './terminal/launch/mobile-terminal-owned-create'
|
||||
import type {
|
||||
RuntimeMobileSessionCreateTerminalResult,
|
||||
RuntimeMobileSessionTerminalClientTab
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function withdrawRuntimePane(
|
||||
*/
|
||||
export async function spawnInAdmittedPane(
|
||||
store: RuntimeStore | null | undefined,
|
||||
controller: Required<Pick<RuntimePtyController, 'spawn'>>,
|
||||
controller: Pick<RuntimePtyController, 'spawn'>,
|
||||
args: AdmittedSpawnArgs
|
||||
): ReturnType<Spawn> {
|
||||
const outcome = await store?.admitTerminalPane?.(admissionFor(args), hostIdOf(args))
|
||||
@@ -72,7 +72,8 @@ export async function spawnInAdmittedPane(
|
||||
const { groupId: _groupId, ...spawnArgs } = args
|
||||
let result: Awaited<ReturnType<Spawn>>
|
||||
try {
|
||||
result = await controller.spawn(spawnArgs)
|
||||
// SAFETY: Callers require spawn; a lost capability during admission keeps the original failure.
|
||||
result = await controller.spawn!(spawnArgs)
|
||||
} catch (error) {
|
||||
if (outcome === 'admitted') {
|
||||
await withdrawRuntimePane(store, args)
|
||||
|
||||
@@ -111,6 +111,8 @@ export type RuntimeStore = {
|
||||
// Read by the launch-line carry rule to name the shell a local line is typed into.
|
||||
terminalDefaultShell?: GlobalSettings['terminalDefaultShell']
|
||||
floatingTerminalEnabled?: GlobalSettings['floatingTerminalEnabled']
|
||||
floatingTerminalCwd?: GlobalSettings['floatingTerminalCwd']
|
||||
floatingTerminalTrustedCwds?: GlobalSettings['floatingTerminalTrustedCwds']
|
||||
agentStatusHooksEnabled?: GlobalSettings['agentStatusHooksEnabled']
|
||||
terminalCopyTrimsGutter?: GlobalSettings['terminalCopyTrimsGutter']
|
||||
experimentalNativeChat?: GlobalSettings['experimentalNativeChat']
|
||||
|
||||
@@ -2,7 +2,10 @@ import { materializeWorktreePushTargetRemote } from '../ipc/worktree-remote'
|
||||
import { getLocalProjectWorktreeGitOptions } from '../project-runtime-git-options'
|
||||
import type { GitPushTarget } from '../../shared/worktree/types'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import type { Store } from '../persistence'
|
||||
import type { WorktreePushTargetStore } from '../ipc/worktree-push-target-cleanup'
|
||||
import type { ProjectRuntimeResolutionStore } from '../local-project-runtime-resolution'
|
||||
|
||||
type TerminalPushTargetStore = WorktreePushTargetStore & ProjectRuntimeResolutionStore
|
||||
|
||||
// Why (#17828): a fork-PR remote deferred at worktree-create time must exist before an
|
||||
// autonomous agent's raw git commands run in a freshly opened terminal -- "sync through
|
||||
@@ -13,7 +16,7 @@ export function triggerTerminalSpawnPushTargetMaterialization(
|
||||
worktreePath: string,
|
||||
pushTarget: GitPushTarget | undefined,
|
||||
repo: Repo | null | undefined,
|
||||
store: Store | undefined,
|
||||
store: TerminalPushTargetStore | null | undefined,
|
||||
repoId?: string,
|
||||
worktreeId?: string
|
||||
): void {
|
||||
@@ -37,7 +40,7 @@ export function triggerTerminalSpawnPushTargetMaterialization(
|
||||
}
|
||||
|
||||
function localGitOptionsForTerminalSpawn(
|
||||
store: Store | undefined,
|
||||
store: ProjectRuntimeResolutionStore | null | undefined,
|
||||
repo: Repo | null | undefined
|
||||
): { wslDistro?: string } {
|
||||
if (!store || !repo) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree as ResolveBrowserNetworkExecutionHostForWorktreeMethods } from '../orca-runtime-resolve-browser-network-execution-host-for-worktree'
|
||||
import type { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree as ResolveBrowserNetworkExecutionHostForWorktreeMethods } from '../terminal/launch/terminal-workspace-scope'
|
||||
import { OrcaRuntimeWithHasTerminalsForWorktree } from './session-graph-reload-lifecycle'
|
||||
import { HEADLESS_RUNTIME_WINDOW_ID } from '../../../shared/runtime-types'
|
||||
import type { FolderWorkspace } from '../../../shared/folder-workspace-types'
|
||||
@@ -65,7 +65,7 @@ export abstract class OrcaRuntimeWithTransitionGraphReloadToTerminalState extend
|
||||
this.markSessionTabsInventoryPublished()
|
||||
}
|
||||
|
||||
protected assertGraphReady(): void {
|
||||
assertGraphReady(): void {
|
||||
if (this.graphStatus !== 'ready') {
|
||||
throw new Error('runtime_unavailable')
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ export abstract class OrcaRuntimeWithRestoreLivePairedRendererSessionOwnedMobile
|
||||
}
|
||||
}
|
||||
|
||||
protected waitForTerminalHandle(tabId: string, timeoutMs = 10_000): Promise<string> {
|
||||
waitForTerminalHandle(tabId: string, timeoutMs = 10_000): Promise<string> {
|
||||
const existing = this.resolveHandleForTab(tabId)
|
||||
if (existing) {
|
||||
return Promise.resolve(existing)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree as ResolveBrowserNetworkExecutionHostForWorktreeMethods } from '../terminal/launch/terminal-workspace-scope'
|
||||
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'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { RuntimePtyController } from '../../runtime-pty-controller-contract'
|
||||
import {
|
||||
addClaudeTeammateModeAuto,
|
||||
addClaudeTeammateModeInProcess,
|
||||
@@ -13,7 +14,10 @@ export async function buildRuntimeAgentTeamsLaunchPlan(args: {
|
||||
claudeAgentTeamsSourceCommand?: string
|
||||
claudeAgentTeamsMode?: ClaudeAgentTeamsMode
|
||||
baseEnv: Record<string, string | undefined>
|
||||
adoptedBeforeLaunch: boolean
|
||||
adoptedBeforeLaunch:
|
||||
| boolean
|
||||
| Awaited<ReturnType<NonNullable<RuntimePtyController['adoptStablePane']>>>
|
||||
| undefined
|
||||
createTeamEnv: (shimDir: string, shimBin: string) => Record<string, string>
|
||||
}): Promise<{
|
||||
plan: Awaited<ReturnType<typeof buildClaudeAgentTeamsLaunchPlan>> | undefined
|
||||
|
||||
+25
-10
@@ -1,14 +1,29 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithCreateTerminal } from './orca-runtime-create-terminal'
|
||||
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../shared/agent-session-resume'
|
||||
import type { RuntimeNavigationTarget } from '../../shared/runtime-navigation'
|
||||
import type { RuntimeMobileSessionCreateTerminalResult } from '../../shared/runtime-types'
|
||||
import { navigationTargetsHost } from '../../shared/runtime-navigation'
|
||||
import { MOBILE_TERMINAL_CREATE_RESULT_TTL_MS } from './orca-runtime-core'
|
||||
import type { MobileTerminalCreateOptions } from './mobile-terminal-renderer-create'
|
||||
import type { RuntimeMobileSessionTabsResult } from '../../../../shared/runtime-session-contracts'
|
||||
import { OrcaRuntimeWithCreateTerminal } from './terminal-create'
|
||||
import type { WorktreeStartupLaunch } from '../../../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../../../shared/agent-session-resume'
|
||||
import type { RuntimeNavigationTarget } from '../../../../shared/runtime-navigation'
|
||||
import type { RuntimeMobileSessionCreateTerminalResult } from '../../../../shared/runtime-types'
|
||||
import { navigationTargetsHost } from '../../../../shared/runtime-navigation'
|
||||
import { MOBILE_TERMINAL_CREATE_RESULT_TTL_MS } from '../../orca-runtime-core'
|
||||
|
||||
export abstract class OrcaRuntimeWithCreateMobileSessionTerminal extends OrcaRuntimeWithCreateTerminal {
|
||||
protected abstract runCreateMobileSessionTerminal(
|
||||
worktreeSelector: string,
|
||||
opts?: MobileTerminalCreateOptions
|
||||
): Promise<RuntimeMobileSessionCreateTerminalResult>
|
||||
|
||||
protected abstract getValidatedExplicitWorktreeIdSelector(
|
||||
selector: string | undefined
|
||||
): string | null
|
||||
|
||||
protected abstract getMobileSessionTabsForWorktree(
|
||||
worktreeId: string,
|
||||
clientNavigationId?: string
|
||||
): RuntimeMobileSessionTabsResult
|
||||
|
||||
export class OrcaRuntimeWithCreateMobileSessionTerminal extends OrcaRuntimeWithCreateTerminal {
|
||||
async createMobileSessionTerminal(
|
||||
worktreeSelector: string,
|
||||
opts: {
|
||||
+31
-30
@@ -1,28 +1,45 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithResolveMobileSessionTerminalCommand } from './orca-runtime-resolve-mobile-session-terminal-command'
|
||||
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../shared/agent-session-resume'
|
||||
import type { AgentLaunchRuntime } from '../../shared/agent-launch-runtime'
|
||||
import { OrcaRuntimeWithResolveMobileSessionTerminalCommand } from './mobile-terminal-startup'
|
||||
import type { WorktreeStartupLaunch } from '../../../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../../../shared/agent-session-resume'
|
||||
import type { AgentLaunchRuntime } from '../../../../shared/agent-launch-runtime'
|
||||
import type {
|
||||
RuntimeMobileSessionCreateTerminalResult,
|
||||
RuntimeMobileSessionTabsSnapshot,
|
||||
RuntimeMobileSessionTerminalTab
|
||||
} from '../../shared/runtime-types'
|
||||
} from '../../../../shared/runtime-types'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { copySleepingAgentLaunchConfig } from './runtime-agent-launch-resolution'
|
||||
import { deriveRemoteRuntimeTerminalCreateHandle } from './remote-runtime-terminal-create-identity'
|
||||
import { parsePaneKey } from '../../shared/stable-pane-id'
|
||||
import { placeCreatedSessionTab } from '../../shared/session-tab-placement'
|
||||
import { copySleepingAgentLaunchConfig } from '../../runtime-agent-launch-resolution'
|
||||
import { deriveRemoteRuntimeTerminalCreateHandle } from '../../remote-runtime-terminal-create-identity'
|
||||
import { parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { placeCreatedSessionTab } from '../../../../shared/session-tab-placement'
|
||||
import {
|
||||
buildHeadlessMobileSessionTabGroups,
|
||||
buildMaterializedHeadlessParentLayout,
|
||||
getHeadlessMobileSessionGroupId
|
||||
} from './mobile-session-layout-projection'
|
||||
} from '../../mobile-session-layout-projection'
|
||||
|
||||
const OWNED_MOBILE_DISPATCH_RECIPE_TTL_MS = 15 * 60_000
|
||||
|
||||
export class OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal extends OrcaRuntimeWithResolveMobileSessionTerminalCommand {
|
||||
export type OwnedMobileTerminalCreateOptions = {
|
||||
command?: string
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
identity?: { tabId: string; leafId: string; sessionId?: string }
|
||||
createMutation?: { clientIdentity: string; id: string }
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
targetGroupId?: string
|
||||
supportsSplitGroupPlacement?: boolean
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchRuntime?: AgentLaunchRuntime
|
||||
signal?: AbortSignal
|
||||
refuseSleptWorktree?: boolean
|
||||
}
|
||||
|
||||
export abstract class OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal extends OrcaRuntimeWithResolveMobileSessionTerminalCommand {
|
||||
private readonly ownedMobileDispatchRecipes = new Map<
|
||||
string,
|
||||
{
|
||||
@@ -46,23 +63,7 @@ export class OrcaRuntimeWithCreateRuntimeOwnedMobileSessionTerminal extends Orca
|
||||
worktreeId: string,
|
||||
activate: boolean,
|
||||
afterTabId?: string,
|
||||
opts: {
|
||||
command?: string
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
identity?: { tabId: string; leafId: string; sessionId?: string }
|
||||
createMutation?: { clientIdentity: string; id: string }
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
targetGroupId?: string
|
||||
supportsSplitGroupPlacement?: boolean
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchRuntime?: AgentLaunchRuntime
|
||||
signal?: AbortSignal
|
||||
refuseSleptWorktree?: boolean
|
||||
} = {}
|
||||
opts: OwnedMobileTerminalCreateOptions = {}
|
||||
): Promise<RuntimeMobileSessionCreateTerminalResult> {
|
||||
this.pruneOwnedMobileDispatchRecipes()
|
||||
const workspace = await this.resolveTerminalWorkspaceLaunchScope(`id:${worktreeId}`)
|
||||
+84
-30
@@ -1,42 +1,96 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithCreateMobileSessionTerminal } from './orca-runtime-create-mobile-session-terminal'
|
||||
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../shared/agent-session-resume'
|
||||
import type { RuntimeMobileSessionCreateTerminalResult } from '../../shared/runtime-types'
|
||||
import type { MobileTerminalCommandOptions, MobileTerminalCommand } from './mobile-terminal-startup'
|
||||
import type { OwnedMobileTerminalCreateOptions } from './mobile-terminal-owned-create'
|
||||
import type { RuntimePtyWorktreeRecord } from '../../runtime-terminal-state-records'
|
||||
import type { TerminalWorkspaceLaunchScope } from '../../runtime-legacy-worker-terminal-recovery-types'
|
||||
import { OrcaRuntimeWithCreateMobileSessionTerminal } from './mobile-terminal-create'
|
||||
import type { WorktreeStartupLaunch } from '../../../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../../../shared/agent-session-resume'
|
||||
import type { RuntimeMobileSessionCreateTerminalResult } from '../../../../shared/runtime-types'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { getRuntimeDesktopSurface } from './runtime-desktop-surface'
|
||||
import { getRuntimeDesktopSurface } from '../../runtime-desktop-surface'
|
||||
import type { IpcMainEvent } from 'electron'
|
||||
import {
|
||||
MOBILE_TERMINAL_READY_FALLBACK_MS,
|
||||
MOBILE_TERMINAL_SURFACE_TIMEOUT_MS,
|
||||
isClientDisconnectedError
|
||||
} from './orca-runtime-core'
|
||||
import { rendererPublicationThrottle } from '../window/renderer-publication-throttle'
|
||||
import { deterministicAgentSessionUuid } from './runtime-agent-launch-resolution'
|
||||
} from '../../orca-runtime-core'
|
||||
import { rendererPublicationThrottle } from '../../../window/renderer-publication-throttle'
|
||||
import { deterministicAgentSessionUuid } from '../../runtime-agent-launch-resolution'
|
||||
|
||||
export type MobileTerminalCreateOptions = {
|
||||
afterTabId?: string
|
||||
targetGroupId?: string
|
||||
command?: string
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
agent?: TuiAgent
|
||||
agentPrompt?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
activate?: boolean
|
||||
clientNavigationId?: string
|
||||
clientMutationId?: string
|
||||
supportsSplitGroupPlacement?: boolean
|
||||
signal?: AbortSignal
|
||||
}
|
||||
|
||||
export abstract class OrcaRuntimeWithRunCreateMobileSessionTerminal extends OrcaRuntimeWithCreateMobileSessionTerminal {
|
||||
protected abstract captureReadyGraphEpoch(): number
|
||||
|
||||
protected abstract resolveMobileSessionTerminalCommand(
|
||||
workspace: TerminalWorkspaceLaunchScope,
|
||||
opts: MobileTerminalCommandOptions
|
||||
): Promise<MobileTerminalCommand>
|
||||
|
||||
protected abstract assertStableReadyGraph(expectedGraphEpoch: number): void
|
||||
|
||||
protected abstract createRuntimeOwnedMobileSessionTerminal(
|
||||
worktreeId: string,
|
||||
activate: boolean,
|
||||
afterTabId?: string,
|
||||
opts?: OwnedMobileTerminalCreateOptions
|
||||
): Promise<RuntimeMobileSessionCreateTerminalResult>
|
||||
|
||||
protected abstract ensurePtyBackedMobileSurfaceForRendererTab(
|
||||
worktreeId: string,
|
||||
tabId: string
|
||||
): RuntimeMobileSessionCreateTerminalResult | null
|
||||
|
||||
protected abstract waitForMobileTerminalSurface(
|
||||
worktreeId: string,
|
||||
parentTabId: string,
|
||||
options?: { timeoutMs?: number; requireReady?: boolean; signal?: AbortSignal }
|
||||
): Promise<RuntimeMobileSessionCreateTerminalResult>
|
||||
|
||||
protected abstract isReadyMobileTerminalSurface(
|
||||
surface: RuntimeMobileSessionCreateTerminalResult | null
|
||||
): boolean
|
||||
|
||||
protected abstract deliverPendingStartupCommandToBareRendererPty(
|
||||
worktreeId: string,
|
||||
tabId: string
|
||||
): void
|
||||
|
||||
protected abstract findMobileTerminalSurface(
|
||||
worktreeId: string,
|
||||
parentTabId: string,
|
||||
options?: { requireReady?: boolean }
|
||||
): RuntimeMobileSessionCreateTerminalResult | null
|
||||
|
||||
protected abstract findLiveRegisteredPtyForRendererTab(
|
||||
worktreeId: string,
|
||||
tabId: string
|
||||
): RuntimePtyWorktreeRecord | null
|
||||
|
||||
protected abstract hasLiveShellForRendererTab(worktreeId: string, tabId: string): boolean
|
||||
|
||||
export class OrcaRuntimeWithRunCreateMobileSessionTerminal extends OrcaRuntimeWithCreateMobileSessionTerminal {
|
||||
protected async runCreateMobileSessionTerminal(
|
||||
worktreeSelector: string,
|
||||
opts: {
|
||||
afterTabId?: string
|
||||
targetGroupId?: string
|
||||
command?: string
|
||||
cwd?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
agent?: TuiAgent
|
||||
agentPrompt?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
viewMode?: 'terminal' | 'chat'
|
||||
activate?: boolean
|
||||
clientNavigationId?: string
|
||||
clientMutationId?: string
|
||||
supportsSplitGroupPlacement?: boolean
|
||||
signal?: AbortSignal
|
||||
} = {}
|
||||
opts: MobileTerminalCreateOptions = {}
|
||||
): Promise<RuntimeMobileSessionCreateTerminalResult> {
|
||||
const pairedCreate = Boolean(opts.clientNavigationId)
|
||||
const graphEpoch = this.captureReadyGraphEpoch()
|
||||
+36
-29
@@ -1,36 +1,43 @@
|
||||
import { OrcaRuntimeWithRunCreateMobileSessionTerminal } from './orca-runtime-run-create-mobile-session-terminal'
|
||||
import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types'
|
||||
import type { WorktreeStartupLaunch } from '../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../shared/agent-session-resume'
|
||||
import { isTuiAgentEnabled } from '../../shared/tui-agent-selection'
|
||||
import { buildAgentStartupPlan } from '../../shared/tui-agent-startup'
|
||||
import { resolveAgentStartupPlanInputs } from '../../shared/agent-startup-plan-inputs'
|
||||
import { agentLaunchQuotingShell, type AgentLaunchRuntime } from '../../shared/agent-launch-runtime'
|
||||
import { OrcaRuntimeWithRunCreateMobileSessionTerminal } from './mobile-terminal-renderer-create'
|
||||
import type { TerminalWorkspaceLaunchScope } from '../../runtime-legacy-worker-terminal-recovery-types'
|
||||
import type { WorktreeStartupLaunch } from '../../../../shared/worktree/launch-types'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import type { SleepingAgentLaunchConfig } from '../../../../shared/agent-session-resume'
|
||||
import { isTuiAgentEnabled } from '../../../../shared/tui-agent-selection'
|
||||
import { buildAgentStartupPlan } from '../../../../shared/tui-agent-startup'
|
||||
import { resolveAgentStartupPlanInputs } from '../../../../shared/agent-startup-plan-inputs'
|
||||
import {
|
||||
agentLaunchQuotingShell,
|
||||
type AgentLaunchRuntime
|
||||
} from '../../../../shared/agent-launch-runtime'
|
||||
|
||||
export type MobileTerminalCommandOptions = {
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
agent?: TuiAgent
|
||||
agentPrompt?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
}
|
||||
|
||||
export type MobileTerminalCommand = {
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
/** What the command was quoted for; the create spawns with it. */
|
||||
launchRuntime?: AgentLaunchRuntime
|
||||
}
|
||||
|
||||
export abstract class OrcaRuntimeWithResolveMobileSessionTerminalCommand extends OrcaRuntimeWithRunCreateMobileSessionTerminal {
|
||||
protected async resolveMobileSessionTerminalCommand(
|
||||
workspace: TerminalWorkspaceLaunchScope,
|
||||
opts: {
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
agent?: TuiAgent
|
||||
agentPrompt?: string
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
}
|
||||
): Promise<{
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
startupCommandDelivery?: WorktreeStartupLaunch['startupCommandDelivery']
|
||||
launchConfig?: SleepingAgentLaunchConfig
|
||||
launchAgent?: TuiAgent
|
||||
/** What the command was quoted for; the create spawns with it. */
|
||||
launchRuntime?: AgentLaunchRuntime
|
||||
}> {
|
||||
opts: MobileTerminalCommandOptions
|
||||
): Promise<MobileTerminalCommand> {
|
||||
if (opts.command || !opts.agent) {
|
||||
return {
|
||||
command: opts.command,
|
||||
+14
-14
@@ -1,21 +1,20 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithCreateAgentSession } from './orca-runtime-create-agent-session'
|
||||
import type { RuntimeTerminalCreate } from '../../shared/runtime-types'
|
||||
import { WORKTREE_CREATE_RESULT_TTL_MS } from './orca-runtime-core'
|
||||
import { deriveRemoteRuntimeTerminalCreateHandle } from './remote-runtime-terminal-create-identity'
|
||||
import { withTimeoutResult } from './runtime-async-boundaries'
|
||||
import { PTY_CONTROLLER_LIST_TIMEOUT_MS } from './orca-runtime-postlude'
|
||||
import { inferWorktreeIdFromPtyId } from './runtime-worktree-path-identity'
|
||||
import { getRegisteredSshState } from '../ssh/ssh-target-registry'
|
||||
import { OrcaRuntimeWithCreateAgentSession } from '../../orca-runtime-create-agent-session'
|
||||
import type { RuntimeTerminalCreate } from '../../../../shared/runtime-types'
|
||||
import { WORKTREE_CREATE_RESULT_TTL_MS } from '../../orca-runtime-core'
|
||||
import { deriveRemoteRuntimeTerminalCreateHandle } from '../../remote-runtime-terminal-create-identity'
|
||||
import { withTimeoutResult } from '../../runtime-async-boundaries'
|
||||
import { PTY_CONTROLLER_LIST_TIMEOUT_MS } from '../../orca-runtime-postlude'
|
||||
import { inferWorktreeIdFromPtyId } from '../../runtime-worktree-path-identity'
|
||||
import { getRegisteredSshState } from '../../../ssh/ssh-target-registry'
|
||||
import {
|
||||
getConnectionExecutionHostId,
|
||||
LOCAL_EXECUTION_HOST_ID,
|
||||
toSshExecutionHostId
|
||||
} from '../../shared/execution-host'
|
||||
import { resolveWorktreeLaunchHost } from './worktree-launch-host-repo'
|
||||
import type { TuiAgent } from '../../shared/tui-agent'
|
||||
} from '../../../../shared/execution-host'
|
||||
import { resolveWorktreeLaunchHost } from '../../worktree-launch-host-repo'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
|
||||
export class OrcaRuntimeWithTerminalCreateDeduplication extends OrcaRuntimeWithCreateAgentSession {
|
||||
export abstract class OrcaRuntimeWithTerminalCreateDeduplication extends OrcaRuntimeWithCreateAgentSession {
|
||||
async dedupeTerminalCreate(
|
||||
clientIdentity: string,
|
||||
worktreeSelector: string | undefined,
|
||||
@@ -126,7 +125,7 @@ export class OrcaRuntimeWithTerminalCreateDeduplication extends OrcaRuntimeWithC
|
||||
}
|
||||
}
|
||||
|
||||
protected getPtyExecutionHostMetadata(
|
||||
getPtyExecutionHostMetadata(
|
||||
ptyId: string | null
|
||||
): Pick<RuntimeTerminalCreate, 'executionHostId' | 'hostPlatform' | 'incarnationId'> {
|
||||
if (!ptyId) {
|
||||
@@ -207,6 +206,7 @@ export class OrcaRuntimeWithTerminalCreateDeduplication extends OrcaRuntimeWithC
|
||||
const key = `${repoSelector}\0${clientMutationId}`
|
||||
const inflight = this.worktreeCreateByMutationId.get(key)
|
||||
if (inflight) {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Callers must use one operation/result type per repo and mutation key.
|
||||
return inflight as Promise<T>
|
||||
}
|
||||
const created = run()
|
||||
+10
-11
@@ -1,14 +1,13 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithTerminalCreateDeduplication } from './orca-runtime-terminal-create-deduplication'
|
||||
import * as dependencies from './orca-runtime-create-terminal-dependencies'
|
||||
import { createDesktopTerminal } from './orca-runtime-create-terminal-desktop'
|
||||
import { buildRuntimeAgentTeamsLaunchPlan } from './terminal/launch/agent-teams-launch-plan'
|
||||
import { createIdempotentCallback } from './orca-runtime-report-pty-spawn-commit'
|
||||
import { recordPtySurface, spawnSurfaceClaimSequence } from './pty-recorded-surface-topology'
|
||||
import { agentLaunchTokenFor } from '../agent-launch/agent-launch-token'
|
||||
import { migratedPaneRestoreHint } from './migrated-pane-restore-cwd'
|
||||
import { OrcaRuntimeWithTerminalCreateDeduplication } from './terminal-create-deduplication'
|
||||
import * as dependencies from '../../orca-runtime-create-terminal-dependencies'
|
||||
import { createDesktopTerminal } from '../../orca-runtime-create-terminal-desktop'
|
||||
import { buildRuntimeAgentTeamsLaunchPlan } from './agent-teams-launch-plan'
|
||||
import { createIdempotentCallback } from '../../orca-runtime-report-pty-spawn-commit'
|
||||
import { recordPtySurface, spawnSurfaceClaimSequence } from '../../pty-recorded-surface-topology'
|
||||
import { agentLaunchTokenFor } from '../../../agent-launch/agent-launch-token'
|
||||
import { migratedPaneRestoreHint } from '../../migrated-pane-restore-cwd'
|
||||
|
||||
export class OrcaRuntimeWithCreateTerminal extends OrcaRuntimeWithTerminalCreateDeduplication {
|
||||
export abstract class OrcaRuntimeWithCreateTerminal extends OrcaRuntimeWithTerminalCreateDeduplication {
|
||||
async createTerminal(
|
||||
worktreeSelector?: string,
|
||||
opts: dependencies.TerminalCreateOptions = {},
|
||||
@@ -85,7 +84,7 @@ export class OrcaRuntimeWithCreateTerminal extends OrcaRuntimeWithTerminalCreate
|
||||
...launchOpts.env,
|
||||
...(launchToken ? { ORCA_AGENT_LAUNCH_TOKEN: launchToken } : {})
|
||||
}
|
||||
let agentTeamsPlan: Awaited<ReturnType<typeof dependencies.buildClaudeAgentTeamsLaunchPlan>>
|
||||
let agentTeamsPlan: Awaited<ReturnType<typeof buildRuntimeAgentTeamsLaunchPlan>>['plan']
|
||||
let sequencedStartupCommand: string | undefined
|
||||
let effectiveLaunchConfig = launchOpts.launchConfig
|
||||
try {
|
||||
+6
-3
@@ -1,7 +1,10 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithRegisterPty } from './orca-runtime-register-pty'
|
||||
import { OrcaRuntimeWithRegisterPty } from '../../orca-runtime-register-pty'
|
||||
|
||||
export abstract class OrcaRuntimeWithPreparePtyExecutionContext extends OrcaRuntimeWithRegisterPty {
|
||||
protected abstract disposeHeadlessTerminal(ptyId: string): void
|
||||
|
||||
protected abstract replaceHeadlessTerminalAfterExecutionContextChange(ptyId: string): void
|
||||
|
||||
export class OrcaRuntimeWithPreparePtyExecutionContext extends OrcaRuntimeWithRegisterPty {
|
||||
preparePtyExecutionContext(
|
||||
ptyId: string,
|
||||
wslDistro: string | null,
|
||||
+34
-24
@@ -1,30 +1,40 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithSplitTerminal } from './orca-runtime-split-terminal'
|
||||
import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records'
|
||||
import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry'
|
||||
import type { RuntimeTerminalSplit } from '../../shared/runtime-types'
|
||||
import { makePaneKey, parsePaneKey } from '../../shared/stable-pane-id'
|
||||
import { recordPtySurface, spawnSurfaceClaimSequence } from './pty-recorded-surface-topology'
|
||||
import { OrcaRuntimeWithSplitTerminal } from './terminal-split'
|
||||
import type { OrcaRuntimeWithResolveTerminalSplitSourceAuthority } from '../../orca-runtime-resolve-terminal-split-source-authority'
|
||||
import type { RuntimePtyWorktreeRecord } from '../../runtime-terminal-state-records'
|
||||
import type { TerminalPaneSplitSource } from '../../../../shared/feature-education-telemetry'
|
||||
import type { RuntimeTerminalSplit } from '../../../../shared/runtime-types'
|
||||
import { makePaneKey, parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { recordPtySurface, spawnSurfaceClaimSequence } from '../../pty-recorded-surface-topology'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { runtimeSplitPlacement } from './runtime-terminal-spawn-placement'
|
||||
import { REJECTED_SPLIT_PTY_STOP_TIMEOUT_MS, ownerSurfacing } from './orca-runtime-core'
|
||||
import type { Worktree } from '../../shared/worktree/types'
|
||||
import { spawnInAdmittedPane, withdrawRuntimePane } from './runtime-pane-admission'
|
||||
import { runtimeSplitPlacement } from '../../runtime-terminal-spawn-placement'
|
||||
import { REJECTED_SPLIT_PTY_STOP_TIMEOUT_MS, ownerSurfacing } from '../../orca-runtime-core'
|
||||
import type { Worktree } from '../../../../shared/worktree/types'
|
||||
import { spawnInAdmittedPane, withdrawRuntimePane } from '../../runtime-pane-admission'
|
||||
|
||||
export type TerminalSplitOptions = {
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
activate?: boolean
|
||||
// Why: same split as createTerminal — adopt the pane without revealing its
|
||||
// workspace, for splits the user never asked to see.
|
||||
surfaceOwner?: false
|
||||
telemetrySource?: TerminalPaneSplitSource
|
||||
}
|
||||
|
||||
export abstract class OrcaRuntimeWithSplitPtyBackedTerminal extends OrcaRuntimeWithSplitTerminal {
|
||||
protected abstract resolveTerminalSplitSourceAuthority(
|
||||
...args: Parameters<
|
||||
OrcaRuntimeWithResolveTerminalSplitSourceAuthority['resolveTerminalSplitSourceAuthority']
|
||||
>
|
||||
): ReturnType<
|
||||
OrcaRuntimeWithResolveTerminalSplitSourceAuthority['resolveTerminalSplitSourceAuthority']
|
||||
>
|
||||
|
||||
export class OrcaRuntimeWithSplitPtyBackedTerminal extends OrcaRuntimeWithSplitTerminal {
|
||||
protected async splitPtyBackedTerminal(
|
||||
pty: RuntimePtyWorktreeRecord,
|
||||
opts: {
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
command?: string
|
||||
env?: Record<string, string>
|
||||
envToDelete?: string[]
|
||||
activate?: boolean
|
||||
// Why: same split as createTerminal — adopt the pane without revealing its
|
||||
// workspace, for splits the user never asked to see.
|
||||
surfaceOwner?: false
|
||||
telemetrySource?: TerminalPaneSplitSource
|
||||
} = {},
|
||||
opts: TerminalSplitOptions = {},
|
||||
createdWorktree?: Worktree
|
||||
): Promise<RuntimeTerminalSplit> {
|
||||
if (!this.ptyController?.spawn) {
|
||||
@@ -57,7 +67,7 @@ export class OrcaRuntimeWithSplitPtyBackedTerminal extends OrcaRuntimeWithSplitT
|
||||
const leafId = randomUUID()
|
||||
const preAllocatedHandle = this.createPreAllocatedTerminalHandle()
|
||||
const paneKey = makePaneKey(parentTabId, leafId)
|
||||
const splitArgs = {
|
||||
const splitArgs: Parameters<typeof spawnInAdmittedPane>[2] = {
|
||||
cols: 120,
|
||||
rows: 40,
|
||||
cwd: workspace.path,
|
||||
+19
-6
@@ -1,11 +1,24 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
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 type { TerminalSplitOptions } from './terminal-split-surface'
|
||||
import type { RuntimePtyWorktreeRecord } from '../../runtime-terminal-state-records'
|
||||
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'
|
||||
import type { Worktree } from '../../shared/worktree/types'
|
||||
import type { Worktree } from '../../../../shared/worktree/types'
|
||||
|
||||
export abstract class OrcaRuntimeWithSplitTerminal extends OrcaRuntimeWithStopExplicitlyClosedTabPtys {
|
||||
protected abstract splitPtyBackedTerminal(
|
||||
pty: RuntimePtyWorktreeRecord,
|
||||
opts?: TerminalSplitOptions,
|
||||
createdWorktree?: Worktree
|
||||
): Promise<RuntimeTerminalSplit>
|
||||
|
||||
protected abstract waitForLeafInTab(
|
||||
tabId: string,
|
||||
leafId: string,
|
||||
timeoutMs?: number
|
||||
): Promise<string>
|
||||
|
||||
export class OrcaRuntimeWithSplitTerminal extends OrcaRuntimeWithStopExplicitlyClosedTabPtys {
|
||||
async splitTerminal(
|
||||
handle: string,
|
||||
opts: {
|
||||
+29
-30
@@ -1,38 +1,37 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { OrcaRuntimeWithTransitionGraphReloadToTerminalState } from './session-graph/session-graph-authority'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import { OrcaRuntimeWithTransitionGraphReloadToTerminalState } from '../../session-graph/session-graph-authority'
|
||||
import type { ExecutionHostId } from '../../../../shared/execution-host'
|
||||
import {
|
||||
floatingWorkspaceToWorktree,
|
||||
isFloatingWorkspaceSelector
|
||||
} from '../../shared/floating-workspace-worktree'
|
||||
import { resolveFloatingTerminalCwd } from '../ipc/floating-workspace-directory'
|
||||
import type { BrowserNetworkExecutionHost } from '../../shared/browser-client-host-protocol'
|
||||
} from '../../../../shared/floating-workspace-worktree'
|
||||
import { resolveFloatingTerminalCwd } from '../../../ipc/floating-workspace-directory'
|
||||
import type { BrowserNetworkExecutionHost } from '../../../../shared/browser-client-host-protocol'
|
||||
import {
|
||||
LOCAL_EXECUTION_HOST_ID,
|
||||
getWorktreeExecutionHostId,
|
||||
parseExecutionHostId
|
||||
} from '../../shared/execution-host'
|
||||
import { resolveRuntimeBrowserNetworkExecutionHost } from './runtime-browser-network-execution-host'
|
||||
import { resolveLocalProjectRuntimeForWorktreeId } from '../local-project-runtime-resolution'
|
||||
import { getRegisteredSshState } from '../ssh/ssh-target-registry'
|
||||
import { resolveWorktreeLaunchHost } from './worktree-launch-host-repo'
|
||||
import { folderWorkspaceKey, parseWorkspaceKey } from '../../shared/workspace-scope'
|
||||
import type { FolderWorkspace } from '../../shared/folder-workspace-types'
|
||||
import type { ResolvedWorktree } from './runtime-worktree-path-identity'
|
||||
import type { Worktree } from '../../shared/worktree/types'
|
||||
import { folderWorkspaceToWorktree } from '../../shared/folder-workspace-worktree'
|
||||
import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types'
|
||||
import { resolveTerminalStartupCwd } from '../../shared/terminal-startup-cwd'
|
||||
import type { ResolvedTerminalWorkspaceLaunchTarget } from './orca-runtime-core'
|
||||
import { AGENT_HOOK_RUNTIME_ENV_KEYS } from './orca-runtime-core'
|
||||
import { ensureJcodeRuntimeDir } from '../../shared/jcode-runtime-dir'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants'
|
||||
} from '../../../../shared/execution-host'
|
||||
import { resolveRuntimeBrowserNetworkExecutionHost } from '../../runtime-browser-network-execution-host'
|
||||
import { resolveLocalProjectRuntimeForWorktreeId } from '../../../local-project-runtime-resolution'
|
||||
import { getRegisteredSshState } from '../../../ssh/ssh-target-registry'
|
||||
import { resolveWorktreeLaunchHost } from '../../worktree-launch-host-repo'
|
||||
import { folderWorkspaceKey, parseWorkspaceKey } from '../../../../shared/workspace-scope'
|
||||
import type { FolderWorkspace } from '../../../../shared/folder-workspace-types'
|
||||
import type { ResolvedWorktree } from '../../runtime-worktree-path-identity'
|
||||
import type { Worktree } from '../../../../shared/worktree/types'
|
||||
import { folderWorkspaceToWorktree } from '../../../../shared/folder-workspace-worktree'
|
||||
import type { TerminalWorkspaceLaunchScope } from '../../runtime-legacy-worker-terminal-recovery-types'
|
||||
import { resolveTerminalStartupCwd } from '../../../../shared/terminal-startup-cwd'
|
||||
import type { ResolvedTerminalWorkspaceLaunchTarget } from '../../orca-runtime-core'
|
||||
import { AGENT_HOOK_RUNTIME_ENV_KEYS } from '../../orca-runtime-core'
|
||||
import { ensureJcodeRuntimeDir } from '../../../../shared/jcode-runtime-dir'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
import { homedir } from 'node:os'
|
||||
import { getExplicitWorktreeIdSelector } from './runtime-worktree-selection'
|
||||
import { WORKTREE_ID_SEPARATOR } from '../../shared/worktree/id'
|
||||
import { WorktreeIdRequiresFullPathError } from './runtime-worktree-lineage-resolution'
|
||||
import { triggerTerminalSpawnPushTargetMaterialization } from './runtime-terminal-spawn-push-target-materialization'
|
||||
import { resolveCreatedWorktreeTerminalTarget } from './runtime-created-worktree-terminal-target'
|
||||
import { getExplicitWorktreeIdSelector } from '../../runtime-worktree-selection'
|
||||
import { WORKTREE_ID_SEPARATOR } from '../../../../shared/worktree/id'
|
||||
import { WorktreeIdRequiresFullPathError } from '../../runtime-worktree-lineage-resolution'
|
||||
import { triggerTerminalSpawnPushTargetMaterialization } from '../../runtime-terminal-spawn-push-target-materialization'
|
||||
import { resolveCreatedWorktreeTerminalTarget } from '../../runtime-created-worktree-terminal-target'
|
||||
|
||||
// Why: folder and floating workspaces have no worktree row, so no parent, children or lineage.
|
||||
function withoutWorktreeLineage(worktree: Worktree): ResolvedWorktree {
|
||||
@@ -51,7 +50,7 @@ function withoutWorktreeLineage(worktree: Worktree): ResolvedWorktree {
|
||||
}
|
||||
}
|
||||
|
||||
export class OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree extends OrcaRuntimeWithTransitionGraphReloadToTerminalState {
|
||||
export abstract class OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree extends OrcaRuntimeWithTransitionGraphReloadToTerminalState {
|
||||
protected resolveBrowserNetworkExecutionHostForWorktree(worktree?: {
|
||||
id: string
|
||||
repoId?: string
|
||||
@@ -105,14 +104,14 @@ export class OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree extend
|
||||
return withoutWorktreeLineage(folderWorkspaceToWorktree(folderWorkspace))
|
||||
}
|
||||
|
||||
protected resolveWorkspaceTerminalStartupCwd(
|
||||
resolveWorkspaceTerminalStartupCwd(
|
||||
workspace: Pick<TerminalWorkspaceLaunchScope, 'path'>,
|
||||
requestedCwd?: string | null
|
||||
): string | undefined {
|
||||
return resolveTerminalStartupCwd(workspace.path, requestedCwd)
|
||||
}
|
||||
|
||||
protected async resolveTerminalWorkspaceLaunchScope(
|
||||
async resolveTerminalWorkspaceLaunchScope(
|
||||
selector: string,
|
||||
createdWorktree?: Worktree
|
||||
): Promise<TerminalWorkspaceLaunchScope> {
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
RuntimePtyTitleTrackerEntry
|
||||
} from '../../runtime-terminal-state-records'
|
||||
import type { RuntimeSyncedLeaf } from '../../../../shared/runtime-types'
|
||||
import { OrcaRuntimeWithPreparePtyExecutionContext } from '../../orca-runtime-prepare-pty-execution-context'
|
||||
import { OrcaRuntimeWithPreparePtyExecutionContext } from '../launch/terminal-execution-context'
|
||||
import { advertisedUrlWatcher } from '../../../ports/advertised-url-watcher'
|
||||
import { appendNormalizedToTailBuffer } from '../../terminal-tail-buffer'
|
||||
import { normalizeTerminalChunk } from '../../terminal-ansi-normalization'
|
||||
|
||||
@@ -22,7 +22,6 @@ import type { TerminalTitleFactMeta } from '../../../../shared/terminal-output-s
|
||||
|
||||
export abstract class OrcaRuntimeWithEmitDaemonPtyTransientFact extends OrcaRuntimeWithScheduleWaitBlockedCheck {
|
||||
protected abstract retirePtyAgentLaunchAuthority(ptyId: string): void
|
||||
protected abstract disposeHeadlessTerminal(ptyId: string): void
|
||||
|
||||
/** A transient fact the daemon detected while it held scan authority —
|
||||
* emitted through the same fact channel as byte-scanned facts. Arrives
|
||||
|
||||
@@ -251,7 +251,7 @@ export abstract class OrcaRuntimeWithResolveWorktreeRemovalTarget extends OrcaRu
|
||||
return { handle, tabId: leaf.tabId, title }
|
||||
}
|
||||
|
||||
protected async resolveAgentTerminalCreateOptions(
|
||||
async resolveAgentTerminalCreateOptions(
|
||||
workspace: TerminalWorkspaceLaunchScope,
|
||||
opts: TerminalCreateOptions
|
||||
): Promise<TerminalCreateOptions> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree } from '../orca-runtime-resolve-browser-network-execution-host-for-worktree'
|
||||
import { OrcaRuntimeWithResolveBrowserNetworkExecutionHostForWorktree } from '../terminal/launch/terminal-workspace-scope'
|
||||
import type { ResolvedWorktree } from '../runtime-worktree-path-identity'
|
||||
import { splitWorktreeIdForFilesystem, worktreeIdComparisonKey } from '../../../shared/worktree/id'
|
||||
import { branchSelectorMatches, runtimePathsEqual } from '../runtime-worktree-path-identity'
|
||||
|
||||
Reference in New Issue
Block a user