From 9ebdc68294d400dbebb5109d75c446dc0c337355 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:35:35 -0700 Subject: [PATCH] Check agent execution namespace types (#28370) --- config/ts-nocheck-baseline.txt | 1 - ...ca-runtime-agent-session-operation.test.ts | 107 +++++++++++++++++- .../orca-runtime-create-agent-session.ts | 2 +- .../get-agent-session-execution-namespace.ts} | 32 +++--- 4 files changed, 121 insertions(+), 21 deletions(-) rename src/main/runtime/{orca-runtime-get-agent-session-execution-namespace.ts => session-construction/agent-session/get-agent-session-execution-namespace.ts} (84%) diff --git a/config/ts-nocheck-baseline.txt b/config/ts-nocheck-baseline.txt index 662a805cc15..262b5ac08eb 100644 --- a/config/ts-nocheck-baseline.txt +++ b/config/ts-nocheck-baseline.txt @@ -8,7 +8,6 @@ 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 src/main/runtime/orca-runtime-fit-override-listeners.ts -src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts src/main/runtime/orca-runtime-has-exact-persisted-terminal-surface-identity.ts src/main/runtime/orca-runtime-linear-commands.ts src/main/runtime/orca-runtime-preserved-branch-cleanup.ts diff --git a/src/main/runtime/orca-runtime-agent-session-operation.test.ts b/src/main/runtime/orca-runtime-agent-session-operation.test.ts index 8ab5b5580af..d3662616671 100644 --- a/src/main/runtime/orca-runtime-agent-session-operation.test.ts +++ b/src/main/runtime/orca-runtime-agent-session-operation.test.ts @@ -8,6 +8,7 @@ import type { RuntimeCreateAgentSessionResult } from '../../shared/agent-session-host-authority' import { OrcaRuntimeService } from './orca-runtime' +import type { IPtyProvider } from '../providers/pty-provider-contract' function operationId(now = Date.now()): string { return `${now}-0123456789abcdef0123456789abcdef` @@ -39,10 +40,12 @@ function terminal() { } } -function createRuntime(provider?: { - supportsAgentSessionClaims?: () => boolean - supportsAgentSessionCreateOperations?: () => boolean -}) { +function createRuntime( + provider?: Pick< + IPtyProvider, + 'supportsAgentSessionClaims' | 'supportsAgentSessionCreateOperations' + > +) { const runtime = new OrcaRuntimeService( { getSettings: () => ({ @@ -109,6 +112,102 @@ async function fenceRemoteAgentSessionSpawn(runtime: OrcaRuntimeService) { return { createTerminal, failure, id } } +describe('agent-session execution namespace', () => { + it('retains the original method owner, defaults and native async functions', () => { + const runtime = createRuntime() + let owner = Object.getPrototypeOf(runtime) + while (!Object.hasOwn(owner, 'ensureAgentSession')) { + owner = Object.getPrototypeOf(owner) + } + expect(owner.constructor.name).toBe('OrcaRuntimeWithGetAgentSessionExecutionNamespace') + expect(Object.getOwnPropertyNames(owner)).toEqual([ + 'constructor', + 'getAgentSessionExecutionNamespace', + 'executionOwnerSupportsAgentSessionOperation', + 'toAgentSessionOptions', + 'ensureAgentSession' + ]) + for (const [name, length, constructor] of [ + ['getAgentSessionExecutionNamespace', 2, 'Function'], + ['executionOwnerSupportsAgentSessionOperation', 3, 'AsyncFunction'], + ['toAgentSessionOptions', 1, 'Function'], + ['ensureAgentSession', 1, 'AsyncFunction'] + ]) { + const descriptor = Object.getOwnPropertyDescriptor(owner, name) + expect(descriptor?.value.length).toBe(length) + expect(descriptor?.value.constructor.name).toBe(constructor) + } + }) + + it('calls the selected provider probe with its receiver and caller signal', async () => { + const signal = new AbortController().signal + const provider: Pick = { + async supportsAgentSessionClaims(options) { + expect(this).toBe(provider) + expect(options?.signal).toBe(signal) + return true + } + } + const runtime = createRuntime(provider) + const createTerminal = vi.spyOn(runtime, 'createTerminal').mockResolvedValue(terminal()) + await runtime.ensureAgentSession( + { + kind: 'explicit', + worktree: 'id:worktree-1', + agent: 'codex', + providerSession: { key: 'session_id', id: 'provider-session-1' } + }, + { signal } + ) + expect(createTerminal).toHaveBeenCalledWith( + 'id:worktree-1', + expect.objectContaining({ signal }) + ) + }) + + it('propagates probe getter failure before entering the probe failure fallback', async () => { + const failure = new Error('probe getter failed') + const runtime = createRuntime({ + get supportsAgentSessionClaims(): IPtyProvider['supportsAgentSessionClaims'] { + throw failure + } + }) + const createTerminal = vi.spyOn(runtime, 'createTerminal').mockResolvedValue(terminal()) + await expect( + runtime.ensureAgentSession({ + kind: 'explicit', + worktree: 'id:worktree-1', + agent: 'codex', + providerSession: { key: 'session_id', id: 'provider-session-1' } + }) + ).rejects.toBe(failure) + expect(createTerminal).not.toHaveBeenCalled() + }) + + it('captures the terminal callee before reading launch option getters', async () => { + const runtime = createRuntime() + const replacement = vi.fn(async () => terminal()) + const original = vi.spyOn(runtime, 'createTerminal').mockResolvedValue(terminal()) + const resume = { + kind: 'explicit' as const, + worktree: 'id:worktree-1', + agent: 'codex' as const, + providerSession: { key: 'session_id' as const, id: 'provider-session-1' }, + get terminalKittyKeyboardProtocol() { + runtime.createTerminal = replacement + return true + } + } + await runtime.ensureAgentSession(resume) + expect(original).toHaveBeenCalledOnce() + expect(original.mock.calls[0]).toHaveLength(2) + expect(replacement).not.toHaveBeenCalled() + await runtime.ensureAgentSession(resume) + expect(replacement).toHaveBeenCalledOnce() + expect(replacement.mock.calls[0]).toHaveLength(2) + }) +}) + describe('agent-session create operation ledger', () => { it.each([true, false, undefined])( 'forwards renderer keyboard support on create and resume: %s', diff --git a/src/main/runtime/orca-runtime-create-agent-session.ts b/src/main/runtime/orca-runtime-create-agent-session.ts index 5f18cba3c7f..17acdd80f1a 100644 --- a/src/main/runtime/orca-runtime-create-agent-session.ts +++ b/src/main/runtime/orca-runtime-create-agent-session.ts @@ -1,6 +1,6 @@ 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 { OrcaRuntimeWithGetAgentSessionExecutionNamespace } from './session-construction/agent-session/get-agent-session-execution-namespace' import type { RuntimeAgentSessionRpcCaller, RuntimeCreateAgentSessionRequest, diff --git a/src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts b/src/main/runtime/session-construction/agent-session/get-agent-session-execution-namespace.ts similarity index 84% rename from src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts rename to src/main/runtime/session-construction/agent-session/get-agent-session-execution-namespace.ts index 2fd4be30ede..a2242721c65 100644 --- a/src/main/runtime/orca-runtime-get-agent-session-execution-namespace.ts +++ b/src/main/runtime/session-construction/agent-session/get-agent-session-execution-namespace.ts @@ -1,30 +1,32 @@ -// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests. -import { OrcaRuntimeWithResolveWorktreeRemovalTarget } from './workspaces/workspace-removal-target' -import type { TerminalWorkspaceLaunchScope } from './runtime-legacy-worker-terminal-recovery-types' -import type { TuiAgent } from '../../shared/tui-agent' -import { parseWslUncPath } from '../../shared/wsl-paths' +import { OrcaRuntimeWithResolveWorktreeRemovalTarget } from '../../workspaces/workspace-removal-target' +import type { TerminalWorkspaceLaunchScope } from '../../runtime-legacy-worker-terminal-recovery-types' +import type { TuiAgent } from '../../../../shared/tui-agent' +import { parseWslUncPath } from '../../../../shared/wsl-paths' import type { AgentLaunchPreferences, RuntimeAgentSessionRpcCaller, RuntimeEnsureAgentSessionRequest, RuntimeEnsureAgentSessionResult -} from '../../shared/agent-session-host-authority' -import { canonicalizeAgentSessionIdentity } from './agent-session-claim-identity' -import { isTuiAgentEnabled } from '../../shared/tui-agent-selection' -import { buildAgentResumeStartupPlan } from '../../shared/tui-agent-startup' -import { agentLaunchQuotingShell } from '../../shared/agent-launch-runtime' -import { OrchestrationError } from './orchestration/orchestration-error' -import { withAgentSessionAccountEnv } from './agent-session-account-env' +} from '../../../../shared/agent-session-host-authority' +import { + canonicalizeAgentSessionIdentity, + type ProviderExecutionNamespace +} from '../../agent-session-claim-identity' +import { isTuiAgentEnabled } from '../../../../shared/tui-agent-selection' +import { buildAgentResumeStartupPlan } from '../../../../shared/tui-agent-startup' +import { agentLaunchQuotingShell } from '../../../../shared/agent-launch-runtime' +import { OrchestrationError } from '../../orchestration/orchestration-error' +import { withAgentSessionAccountEnv } from '../../agent-session-account-env' import { resolveTuiAgentLaunchArgs, resolveTuiAgentLaunchEnv -} from '../../shared/tui-agent-launch-defaults' +} from '../../../../shared/tui-agent-launch-defaults' -export class OrcaRuntimeWithGetAgentSessionExecutionNamespace extends OrcaRuntimeWithResolveWorktreeRemovalTarget { +export abstract class OrcaRuntimeWithGetAgentSessionExecutionNamespace extends OrcaRuntimeWithResolveWorktreeRemovalTarget { protected getAgentSessionExecutionNamespace( workspace: TerminalWorkspaceLaunchScope, agent: TuiAgent - ): { machine: string; principal: string; container: string; providerRoot: string } | null { + ): ProviderExecutionNamespace | null { if (workspace.connectionId) { // Why: SSH target ids are not execution-namespace proof. Preserve the // legacy launch until an attested route can safely participate in claims.