From 86cd327749dc624dd0181f18b4967fdbabbbac40 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 4 Sep 2026 17:11:41 -0700 Subject: [PATCH] Answer the structured-session support probe without installing the host (#18695) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Answer the structured-session support probe without installing the host `getStructuredAgentSessionCreateSupport` called `ensureStructuredAgentSessionHost()` before answering, so a read-only "can you create a Codex session here?" question performed the create route's lifecycle work: the first install opens the durable agent-session record store, attaches the PTY write-gate record lookup and starts the orphan-child reaper. Ask the pure predicate instead. `supportsCreate` on the installed host resolves to `adapterSupportsCreate`, which for the Codex adapter is exactly `agent === 'codex' && supportsCodexStructuredLocation(location)` — no adapter instance is needed to answer it. Nothing is lost: the create/attach route still installs via `ensureStructuredHostInstalled`, and startup restoration still installs and reconciles when a store is already persisted. * test(runtime): cover structured support probe parity --------- Co-authored-by: Merge Sim --- ...lve-recovered-structured-tui-transcript.ts | 8 +- ...ctured-agent-session-support-probe.test.ts | 174 ++++++++++++++++++ 2 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 src/main/runtime/structured-agent-session-support-probe.test.ts diff --git a/src/main/runtime/orca-runtime-resolve-recovered-structured-tui-transcript.ts b/src/main/runtime/orca-runtime-resolve-recovered-structured-tui-transcript.ts index cfb8b421966..aef04bde6bc 100644 --- a/src/main/runtime/orca-runtime-resolve-recovered-structured-tui-transcript.ts +++ b/src/main/runtime/orca-runtime-resolve-recovered-structured-tui-transcript.ts @@ -3,6 +3,8 @@ import { OrcaRuntimeWithStopStructuredSessionProcess } from './orca-runtime-stop import type { AgentSessionOwnerBinding } from '../../shared/agent-session-host-authority' import { agentSessionOwnerBindingsEqual } from '../../shared/claimed-agent-pty-owner-snapshot' import { resolvePinnedCodexRolloutProof } from '../codex/codex-tui-rollout-proof' +import { supportsCodexStructuredLocation } from '../codex/codex-structured-location-support' +import { supportsClaudeStructuredLocation } from '../claude/claude-structured-location-support' import { getStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry' import { resolveStructuredAgentSessionCreateSupport } from '../native-chat/structured-agent-session-create-support' import { LOCAL_EXECUTION_HOST_ID } from '../../shared/execution-host' @@ -51,13 +53,13 @@ export class OrcaRuntimeWithResolveRecoveredStructuredTuiTranscript extends Orca agent: 'claude' | 'codex' ): Promise<{ supported: boolean; reason?: 'agent' | 'remote' | 'wsl' }> { const location = await this.resolveStructuredAgentSessionLocation(worktreeSelector) - await this.ensureStructuredAgentSessionHost() - // The verdict lives in a typechecked module; this file is @ts-nocheck. return resolveStructuredAgentSessionCreateSupport({ agent, location, adapterSupportsCreate: - getStructuredAgentSessionHost()?.supportsCreate(location, agent) === true, + agent === 'claude' + ? supportsClaudeStructuredLocation(location) + : supportsCodexStructuredLocation(location), getSettings: () => this.requireStore().getSettings() }) } diff --git a/src/main/runtime/structured-agent-session-support-probe.test.ts b/src/main/runtime/structured-agent-session-support-probe.test.ts new file mode 100644 index 00000000000..e393e41f3a4 --- /dev/null +++ b/src/main/runtime/structured-agent-session-support-probe.test.ts @@ -0,0 +1,174 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { OrcaRuntimeService } from './orca-runtime' +import { + getStructuredAgentSessionHost, + setStructuredAgentSessionHost +} from '../native-chat/agent-session-wire/structured-agent-session-registry' +import { agentSessionPtyWriteGate } from './agent-session-pty-write-gate' + +type InstallEffects = { + storeOpened: boolean + writeGateAttached: boolean + reaperStarted: boolean +} + +/** Stands in for `install()` by performing the three effects it performs, so a probe that + * reinstalls the host is caught by what the install *does*, not by a call count alone. */ +function stubStructuredHostInstall(runtime: OrcaRuntimeService): { + effects: InstallEffects + ensure: ReturnType +} { + const effects: InstallEffects = { + storeOpened: false, + writeGateAttached: false, + reaperStarted: false + } + // `supportsCreate` answers as the real Codex adapter would, so a probe that reinstalls the host + // still returns the right answer and fails on the install effects alone. + const host = { + reconcileRestartLeases: vi.fn(async () => {}), + supportsCreate: (location: { executionHostId: string; wslDistro: string | null }) => + location.executionHostId === 'local' && location.wslDistro === null + } + const ensure = vi.fn(async () => { + effects.storeOpened = true + effects.reaperStarted = true + agentSessionPtyWriteGate.attachRecordLookup(() => null) + effects.writeGateAttached = true + setStructuredAgentSessionHost(host as never) + }) + vi.spyOn(runtime, 'ensureStructuredAgentSessionHost').mockImplementation(ensure) + return { effects, ensure } +} + +type TestLocation = { + executionHostId: string + wslDistro: string | null + workspaceKind?: 'folder' | 'git-worktree' +} + +type SupportResult = { + supported: boolean + reason?: 'agent' | 'remote' | 'wsl' +} + +function createRuntime(location: TestLocation): OrcaRuntimeService { + const runtime = new OrcaRuntimeService({ getSettings: () => ({}) } as never) + const internal = runtime as unknown as { + resolveStructuredAgentSessionLocation: () => Promise + } + internal.resolveStructuredAgentSessionLocation = vi.fn(async () => ({ + executionHostId: location.executionHostId, + wslDistro: location.wslDistro, + workspaceId: 'workspace-1', + workspaceKind: location.workspaceKind ?? 'git-worktree' + })) + return runtime +} + +async function expectSupportWithoutInstall(input: { + agent: 'claude' | 'codex' + location: TestLocation + expected: SupportResult + repetitions?: number +}): Promise { + const runtime = createRuntime(input.location) + const { effects, ensure } = stubStructuredHostInstall(runtime) + + const answers: SupportResult[] = [] + for (let index = 0; index < (input.repetitions ?? 1); index += 1) { + answers.push( + await runtime.getStructuredAgentSessionCreateSupport('id:workspace-1', input.agent) + ) + } + + expect(answers).toEqual(Array(input.repetitions ?? 1).fill(input.expected)) + expect(ensure).not.toHaveBeenCalled() + expect(effects).toEqual({ + storeOpened: false, + writeGateAttached: false, + reaperStarted: false + }) + expect(getStructuredAgentSessionHost()).toBeNull() +} + +describe('structured agent-session create-support probe', () => { + afterEach(() => { + setStructuredAgentSessionHost(null) + agentSessionPtyWriteGate.detachRecordLookup() + vi.restoreAllMocks() + }) + + it.each(['codex', 'claude'] as const)( + 'answers %s support repeatedly without installing the host', + async (agent) => { + await expectSupportWithoutInstall({ + agent, + location: { executionHostId: 'local', wslDistro: null }, + expected: { supported: true }, + repetitions: 3 + }) + } + ) + + it.each(['codex', 'claude'] as const)( + 'still reports an unsupported remote %s location without installing the host', + async (agent) => { + await expectSupportWithoutInstall({ + agent, + location: { executionHostId: 'ssh-host-1', wslDistro: null }, + expected: { supported: false, reason: 'remote' } + }) + } + ) + + it.each(['codex', 'claude'] as const)( + 'still reports an unsupported WSL %s location without installing the host', + async (agent) => { + await expectSupportWithoutInstall({ + agent, + location: { executionHostId: 'local', wslDistro: 'Ubuntu' }, + expected: { supported: false, reason: 'wsl' } + }) + } + ) + + it.each(['codex', 'claude'] as const)( + 'supports a local folder workspace for %s without installing the host', + async (agent) => { + await expectSupportWithoutInstall({ + agent, + location: { + executionHostId: 'local', + wslDistro: null, + workspaceKind: 'folder' + }, + expected: { supported: true } + }) + } + ) + + it('still installs and reconciles on startup when a store is already persisted', async () => { + const runtime = createRuntime({ executionHostId: 'local', wslDistro: null }) + const { effects, ensure } = stubStructuredHostInstall(runtime) + const internal = runtime as unknown as { + hasPersistedStructuredAgentSessionStore: () => boolean + refreshMobileSessionPtyRecords: () => Promise + } + internal.hasPersistedStructuredAgentSessionStore = () => true + internal.refreshMobileSessionPtyRecords = vi.fn(async () => {}) + + await runtime.prepareStructuredAgentSessionStartupRestoration() + + expect(ensure).toHaveBeenCalledTimes(1) + expect(effects).toEqual({ + storeOpened: true, + writeGateAttached: true, + reaperStarted: true + }) + expect( + (getStructuredAgentSessionHost() as unknown as { reconcileRestartLeases: () => void }) + .reconcileRestartLeases + ).toHaveBeenCalledTimes(1) + }) +})