Check agent execution namespace types (#28370)

This commit is contained in:
Neil
2026-10-14 23:35:35 -07:00
committed by GitHub
parent 215bdbd6b0
commit 9ebdc68294
4 changed files with 121 additions and 21 deletions
-1
View File
@@ -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
@@ -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<IPtyProvider, 'supportsAgentSessionClaims'> = {
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',
@@ -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,
@@ -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.