Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
88c4c5d
Finish structured agent readiness before delivering held work
brennanb2025 Oct 12, 2026
1232065
Publish the Codex process before protocol startup
brennanb2025 Oct 12, 2026
699a59a
Publish ACP agent processes before session startup
brennanb2025 Oct 12, 2026
a40ff4d
Merge main into agent startup host readiness
brennanb2025 Oct 12, 2026
914670f
Merge updated startup piece 1 into piece 2
brennanb2025 Oct 12, 2026
aa0ded8
Merge updated startup piece 2 into piece 3
brennanb2025 Oct 12, 2026
1ab0a7d
Merge main into startup host readiness
brennanb2025 Oct 12, 2026
13d3339
Merge updated startup piece 1 into piece 2
brennanb2025 Oct 12, 2026
f7d8d0e
Merge updated startup piece 2 into piece 3
brennanb2025 Oct 12, 2026
765a4ee
Adapt permission fixtures to startup lifecycle
brennanb2025 Oct 12, 2026
60e2f5f
Merge updated startup piece 1 into piece 2
brennanb2025 Oct 12, 2026
bb74ccb
Merge updated startup piece 2 into piece 3
brennanb2025 Oct 12, 2026
0737f63
Keep model discovery for chats before their first start
brennanb2025 Oct 13, 2026
aeba263
Carry catalog startup fix from piece 1 into piece 2
brennanb2025 Oct 13, 2026
641cc89
Carry catalog startup fix from piece 2 into piece 3
brennanb2025 Oct 13, 2026
86d0829
Use one host readiness promise for provider operations
brennanb2025 Oct 13, 2026
2264369
Await provider opening in the execution host
brennanb2025 Oct 13, 2026
57e7bde
Await Claude initialization in host-owned open
brennanb2025 Oct 13, 2026
9711350
Observe Claude readiness through the host status
brennanb2025 Oct 13, 2026
6d3de21
Integrate host-awaited Claude opening
brennanb2025 Oct 13, 2026
9b0ba03
Keep optional readback behind host readiness
brennanb2025 Oct 13, 2026
98bf1ac
Recheck launch permission after host readiness
brennanb2025 Oct 13, 2026
226c2a9
Preserve the awaited child during permission revalidation
brennanb2025 Oct 13, 2026
cdd0155
Carry host readiness into Codex startup
brennanb2025 Oct 13, 2026
cb796af
Await Codex protocol open under host ownership
brennanb2025 Oct 13, 2026
4910b2c
Carry host-awaited Codex opening into ACP startup
brennanb2025 Oct 13, 2026
18c8215
Await ACP protocol open in the host
brennanb2025 Oct 13, 2026
13a0d2d
Derive ACP opening from its protocol session facts
brennanb2025 Oct 13, 2026
fe4ecbe
Exercise unanswered ACP options with an explicit silent route
brennanb2025 Oct 13, 2026
e1cc15d
Record failed owned opening before the first message
brennanb2025 Oct 13, 2026
33bbb96
Carry owned-open failure recording through the startup stack
brennanb2025 Oct 13, 2026
96b3e2c
Carry owned-open failure recording through the startup stack
brennanb2025 Oct 13, 2026
b57b016
Abort a matching provider open when its child exits
brennanb2025 Oct 13, 2026
8e8fcd2
Cover provider exit during an unanswered host open
brennanb2025 Oct 13, 2026
bfd8e50
Cover explicit failure facts on a host-stopped child
brennanb2025 Oct 13, 2026
61b1f4a
Carry owned-open failure recording through the startup stack
brennanb2025 Oct 13, 2026
caf1f63
Carry owned-open failure recording through the startup stack
brennanb2025 Oct 13, 2026
7ba38c7
Keep startup waits cancellable and preserve operation order
brennanb2025 Oct 13, 2026
0b0766c
Carry cancellable host startup and saved picks into Codex opening
brennanb2025 Oct 13, 2026
a5abf73
Derive Codex startup exits from host phase
brennanb2025 Oct 13, 2026
eb1b8fa
Open Codex protocol in background-task test fixtures
brennanb2025 Oct 13, 2026
1a2c2e0
Carry host and Codex startup fixes into ACP opening
brennanb2025 Oct 13, 2026
aad51e8
Keep ACP startup ownership until handshake completes
brennanb2025 Oct 13, 2026
6f0fedd
Run ACP opening catalog fixtures with Node SQLite
brennanb2025 Oct 13, 2026
b1ca041
Verify saved ACP picks without live option writes
brennanb2025 Oct 13, 2026
e68ab05
Keep ACP catalog bookkeeping from failing an options read
brennanb2025 Oct 13, 2026
ed35c02
Keep live choices and delivery order while agents start
brennanb2025 Oct 13, 2026
39cec53
Carry host delivery and live-choice fixes into Codex opening
brennanb2025 Oct 13, 2026
b2e9969
Drop retired startup revision from Codex opening
brennanb2025 Oct 13, 2026
497dc74
Carry host delivery fixes into ACP opening
brennanb2025 Oct 13, 2026
937d236
Keep live-option cancellation until message choices and clarify catal…
brennanb2025 Oct 13, 2026
d25715e
Hold ACP option replies in live-write cancellation regressions
brennanb2025 Oct 13, 2026
24d2a86
Apply startup picks and let parked messages release operations [skip ci]
brennanb2025 Oct 13, 2026
33fef4c
Carry startup picks and parked-message ordering into Codex opening [s…
brennanb2025 Oct 13, 2026
eabdda0
Remove unused Codex opening report field [skip ci]
brennanb2025 Oct 13, 2026
ac91ee8
Carry startup picks and parked-message ordering into ACP opening [ski…
brennanb2025 Oct 13, 2026
34f1349
Keep ACP restoration facts local to the provider [skip ci]
brennanb2025 Oct 13, 2026
349b201
Save a waiting startup pick after Stop ends its opening [skip ci]
brennanb2025 Oct 13, 2026
4de52a8
Carry startup picks through an ended opening [skip ci]
brennanb2025 Oct 13, 2026
612543a
Carry startup picks through an ended opening [skip ci]
brennanb2025 Oct 13, 2026
2b3db6d
Save choices when startup ends or a replacement is still opening
brennanb2025 Oct 13, 2026
6f5bfb1
Merge main into host startup readiness
brennanb2025 Oct 13, 2026
5e87dfa
Carry main sync into 28046
brennanb2025 Oct 13, 2026
5819e7d
Carry main sync into 28047
brennanb2025 Oct 13, 2026
f09f33d
Check saved choices separately from Claude startup reports
brennanb2025 Oct 13, 2026
709ba8f
Carry main sync into 28046
brennanb2025 Oct 13, 2026
8d505b0
Carry main sync into 28047
brennanb2025 Oct 13, 2026
48f2fe6
Release scripted Codex startup before awaiting queued picks
brennanb2025 Oct 13, 2026
d1ebcce
Carry main sync into 28047
brennanb2025 Oct 13, 2026
eae9500
Align startup fixtures and preserve observed Claude exits
brennanb2025 Oct 13, 2026
e6959b7
Carry main sync into 28046
brennanb2025 Oct 13, 2026
f621bd9
Carry main sync into 28047
brennanb2025 Oct 13, 2026
237372a
Open Codex protocol sessions before Stop fixture dispatch
brennanb2025 Oct 13, 2026
a63cbbc
Carry main sync into 28047
brennanb2025 Oct 13, 2026
8ea4b41
Wait for the opened Codex child before runtime notifications
brennanb2025 Oct 13, 2026
92f3753
Carry main sync into 28047
brennanb2025 Oct 13, 2026
7501b4f
Order startup picks before later messages and retain flow regressions
brennanb2025 Oct 13, 2026
29c4a81
Keep reviewed lifecycle contracts while pruning duplicate tests
brennanb2025 Oct 13, 2026
ad1e0d8
Save picks after a proven exit without requiring a child
brennanb2025 Oct 13, 2026
776a0db
Type the startup reply fixture against the Claude control contract
brennanb2025 Oct 13, 2026
9f85142
Carry main sync into 28046
brennanb2025 Oct 13, 2026
f23de30
Keep Codex startup flow contracts and remove overlapping adapter tests
brennanb2025 Oct 13, 2026
0752696
Carry main sync into 28047
brennanb2025 Oct 13, 2026
d7e4f14
Restore the committed dependency lockfile after test tooling
brennanb2025 Oct 13, 2026
c4c84a3
Carry main sync into 28047
brennanb2025 Oct 13, 2026
70dbe14
Honor the before-input callback in the startup reply fixture
brennanb2025 Oct 13, 2026
cc3b9b6
Carry main sync into 28046
brennanb2025 Oct 13, 2026
41a54bd
Carry main sync into 28047
brennanb2025 Oct 13, 2026
85908e2
Keep startup option menus independent and show the saved choice
brennanb2025 Oct 13, 2026
7a4103d
Exercise startup picks across real menus and the runtime
brennanb2025 Oct 13, 2026
07d1745
Carry main sync into 28046
brennanb2025 Oct 13, 2026
e346182
Carry startup picker fix into ACP readiness
brennanb2025 Oct 13, 2026
a4c51a1
Keep saved effort visible when picking a model during startup
brennanb2025 Oct 13, 2026
020cf16
Carry main sync into 28046
brennanb2025 Oct 13, 2026
f6644a5
Carry main sync into 28047
brennanb2025 Oct 13, 2026
0ecfbbe
Merge current main into startup host readiness
brennanb2025 Oct 13, 2026
d467ebd
Include the running-turn scope in repeated Stop fixture
brennanb2025 Oct 13, 2026
df2a984
Use the current attach contract in import fixtures
brennanb2025 Oct 14, 2026
455089c
Carry current main and startup corrections into 28046
brennanb2025 Oct 14, 2026
957cd10
Keep the Codex journal translator within its line limit
brennanb2025 Oct 14, 2026
84c1762
Carry current main and startup corrections into 28047
brennanb2025 Oct 14, 2026
8f6c730
Match ACP startup fixtures to saved picks and late Stop behavior
brennanb2025 Oct 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions config/scripts/vitest-sqlite-runtime-files.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { SQLITE_DATABASE_RUNTIME_INCLUDE } from './vitest-sqlite-database-runtim

// Real SQLite fixtures keep Node publication and close semantics.
export const SQLITE_RUNTIME_INCLUDE = [
'src/main/acp/acp-structured-publish-at-spawn.test.ts',
'tests/e2e/native-chat-startup-option-pickers.unit.test.ts',
...SQLITE_DATABASE_RUNTIME_INCLUDE,
'src/main/acp/acp-structured-host-live-listing.test.ts',
'src/main/acp/acp-structured-host-configured-default.test.ts',
Expand All @@ -22,6 +24,7 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/acp/acp-structured-child-work.test.ts',
'src/main/acp/acp-structured-host-subagent-stop.test.ts',
'src/main/acp/acp-timeline-grok-subagent-retention.test.ts',
'src/main/acp/acp-scripted-child-runtime.test.ts',
'src/main/active-view-persistence-boundary.test.ts',
'src/main/automations/automation-dispatch-host-fence.test.ts',
'src/main/automations/automation-owner-fencing.test.ts',
Expand Down Expand Up @@ -128,6 +131,8 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/native-chat/agent-session-wire/structured-agent-session-provider-restore.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-provider-started-catalog.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-provider-started.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-provider-open.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-ready-operation-order.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-queued-action-receipts.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-queued-gate.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-queued-newer-orca.test.ts',
Expand Down Expand Up @@ -169,6 +174,8 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/native-chat/agent-session-wire/structured-agent-session-start-failure-writer.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-attempt-host.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-attempt-registrations.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-picks.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-permission-mode.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-startup-reconcile-failure.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-child-work.test.ts',
'src/main/native-chat/agent-session-wire/structured-agent-session-status-feed-child.test.ts',
Expand Down Expand Up @@ -246,6 +253,8 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/runtime/structured-agent-session-codex-interrupt-order.test.ts',
'src/main/runtime/structured-agent-session-codex-opening-send-hold.test.ts',
'src/main/runtime/runtime-client-agent-launch-settings-durability.test.ts',
'src/main/runtime/structured-agent-session-codex-scripted-child.test.ts',
'src/main/runtime/structured-agent-session-codex-start-deadline.test.ts',
'src/main/runtime/structured-agent-session-codex-settlement-order.test.ts',
'src/main/runtime/structured-agent-session-integration-replay.test.ts',
'src/main/runtime/structured-agent-session-integration.test.ts',
Expand Down
2 changes: 1 addition & 1 deletion src/main/acp/acp-prompt-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '../../shared/node-bounded-file-reader'
import type { AcpLaunchSpec } from './acp-launch-specs'
import { resolveAcpPeerOptions } from './acp-peer-limits'
import { acpAgentName } from './acp-structured-acquire'
import { acpAgentName } from './acp-structured-agent-definitions'
import type { AcpStructuredConnection } from './acp-structured-connection'
import { acpSessionAcceptsImages } from './acp-structured-options'
import type { ContentBlock } from './generated/acp-protocol.generated'
Expand Down
88 changes: 88 additions & 0 deletions src/main/acp/acp-scripted-child-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { computeAgentSessionPayloadFingerprint } from '../../shared/agent-session-mutation-envelope'
import {
HOST_TEST_SESSION,
hostTestAttachParams,
hostTestMessage
} from '../native-chat/agent-session-wire/structured-agent-session-host-test-data'
import { createStructuredAgentSessionLogger } from '../native-chat/agent-session-wire/structured-agent-session-logger'
import {
ensureStructuredAgentSessionHost,
stopStructuredAgentSessionRuntime
} from '../runtime/structured-agent-session-runtime'
import { acpScriptedChild } from './acp-scripted-child.test-fixture'

let stateDirectory: string | undefined
let operation = 0
const operationId = () => `${Date.now()}-${(++operation).toString(16).padStart(32, '0')}`
const caller = { callerKey: 'scripted-acp-runtime' }

afterEach(async () => {
await stopStructuredAgentSessionRuntime()
if (stateDirectory) {
await rm(stateDirectory, { recursive: true, force: true })
}
stateDirectory = undefined
})

describe.each(['grok', 'opencode'] as const)('%s runtime with a scripted ACP child', (agent) => {
it('attaches and accepts a send while startup is held, then delivers once the protocol session opens', async () => {
stateDirectory = await mkdtemp(join(tmpdir(), 'orca-scripted-acp-'))
const script = acpScriptedChild(agent)()
const host = await ensureStructuredAgentSessionHost({
logger: createStructuredAgentSessionLogger(),
stateDirectory,
hostId: 'local',
claimKeyId: 'key-1',
resolveWorkspacePath: async () => stateDirectory ?? '',
resolveLaunchArgs: () => [],
resolveClaudeAuthPolicy: () => ({ account: 'managed' }),
...script.deps
})
const attach = hostTestAttachParams(null, {
provider: agent,
agent,
providerHandle: undefined,
location: {
executionHostId: 'local',
wslDistro: null,
workspaceId: 'folder-1',
workspaceKind: 'folder'
},
accountHome:
agent === 'grok'
? { variable: 'GROK_HOME', path: stateDirectory }
: { kind: 'opencode', locator: { kind: 'unmanaged' } }
})
attach.envelope.clientOperationId = operationId()
const attached = await host.attach(caller, attach)
expect(attached).toMatchObject({ ok: true })
if (!attached.ok) {
throw new Error(JSON.stringify(attached.refusal))
}
const body = hostTestMessage('message during startup')
const sent = await host.send(caller, {
body,
envelope: {
sessionId: HOST_TEST_SESSION,
clientOperationId: operationId(),
expectedRuntimeFence: attached.value.fence,
payloadFingerprint: computeAgentSessionPayloadFingerprint({
method: 'agentSession.send',
sessionId: HOST_TEST_SESSION,
fields: { body }
})
}
})
expect(sent).toMatchObject({ ok: true })
expect(script.prompts()).toEqual([])
await vi.waitFor(() => expect(script.spawns()).toBe(1))
script.releaseHandshake()
await vi.waitFor(() => expect(script.prompts()).toEqual(['message during startup']))
await host.close(HOST_TEST_SESSION, 'user-close')
expect(script.closes()).toBe(1)
})
})
151 changes: 151 additions & 0 deletions src/main/acp/acp-scripted-child.test-fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { z } from 'zod'
import type {
ScriptedAgentChild,
ScriptedAgentChildFactory
} from '../runtime/structured-agent-scripted-child.test-fixture'
import { FakeAcpChild, GROK_CONFIG_OPTIONS } from './acp-structured-adapter.test-support'
import type { FakeFrame } from './acp-scripted-agent.test-support'
import type { AcpLaunchSpec } from './acp-launch-specs'

const promptParams = z.object({
sessionId: z.string(),
prompt: z.array(z.object({ type: z.string(), text: z.string().optional() })),
_meta: z.object({ promptId: z.string() }).optional()
})
const loadParams = z.object({ sessionId: z.string() })
const configParams = z.object({ configId: z.string(), value: z.string() })

export function acpScriptedChild(agent: AcpLaunchSpec['agent']): ScriptedAgentChildFactory {
return () => {
let current: FakeAcpChild | undefined
let handshake: (() => void) | undefined
let released = false
let opened = 0
let reopened = 0
let closed = 0
const texts: string[] = []
const fixture: ScriptedAgentChild = {
holdHandshakes: true,
completeTurns: true,
deps: {
resolveEnvironment: async () => ({ PATH: '' }),
acpLaunchCommand: {
resolveCommand: () => 'scripted-acp-agent',
probeVersion: async () => true
},
readProcessStartTime: async () => 1_700_000_000_000 + opened,
openAcpConnection: (launch, options) => {
opened += 1
handshake = undefined
released = false
const child = new FakeAcpChild(launch, options)
current = child
let sessionId = `${agent}-scripted-session`
let running: FakeFrame | undefined
let configOptions = GROK_CONFIG_OPTIONS.map((option) => ({ ...option }))
const script = child.agent
script.on('initialize', (frame) =>
script.reply(frame, {
protocolVersion: 1,
agentCapabilities: { loadSession: true }
})
)
const start = (frame: FakeFrame, resumes: boolean): void => {
if (resumes) {
reopened += 1
sessionId = loadParams.parse(frame.params).sessionId
}
const answer = () => {
if (child.closed || child.exited) {
return
}
script.reply(frame, { ...(!resumes ? { sessionId } : {}), configOptions })
}
if (fixture.holdHandshakes && !released) {
handshake = answer
} else {
answer()
}
}
script.on('session/new', (frame) => start(frame, false))
script.on('session/load', (frame) => start(frame, true))
script.on('session/set_config_option', (frame) => {
const { configId, value } = configParams.parse(frame.params)
configOptions = configOptions.map((option) =>
option.id === configId ? { ...option, currentValue: value } : option
)
script.reply(frame, { configOptions })
})
script.on('session/set_model', (frame) => script.reply(frame, {}))
// Grok's subagent-stop probe: the handler refuses the missing id, so stops are offered.
script.on('_x.ai/subagent/cancel', (frame) =>
script.fail(
frame,
-32602,
'Invalid params',
'invalid params: missing field `subagentId`'
)
)
// Its background-task stop probe, answered the same way.
script.on('_x.ai/task/kill', (frame) =>
script.fail(
frame,
-32602,
'Invalid params',
'invalid params: missing field `sessionId`'
)
)
script.on('session/prompt', (frame) => {
const params = promptParams.parse(frame.params)
texts.push(params.prompt.map((block) => block.text ?? '').join(''))
running = frame
script.notify('session/update', {
sessionId,
update: {
sessionUpdate: 'agent_message_chunk',
content: { type: 'text', text: 'Scripted response' }
},
...(params._meta ? { _meta: params._meta } : {})
})
if (fixture.completeTurns) {
script.reply(frame, { stopReason: 'end_turn' })
running = undefined
}
})
script.on('session/cancel', () => {
if (running) {
script.reply(running, { stopReason: 'cancelled' })
}
running = undefined
})
const prove = child.proveClose
child.proveClose = async () => {
if (!child.exited) {
closed += 1
}
return prove()
}
return child
}
},
releaseHandshake: () => {
released = true
handshake?.()
handshake = undefined
},
failHandshake: (message) => {
if (!current) {
throw new Error('no scripted ACP child')
}
current.stderr = message
current.exit()
handshake = undefined
},
prompts: () => texts,
spawns: () => opened,
resumes: () => reopened,
closes: () => closed
}
return fixture
}
}
93 changes: 93 additions & 0 deletions src/main/acp/acp-scripted-connection.test-fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import type { ProviderProcessExit } from '../provider-process/managed-provider-process'
import type { ProviderProcessLaunch } from '../provider-process/provider-process-launch'
import type { AcpAgentConnectionOptions } from './acp-agent-connection'
import { AcpConnectionClosedError } from './acp-errors'
import { AcpSessionRuntime } from './acp-session-runtime'
import type { AcpStructuredConnection } from './acp-structured-connection'
import { AcpScriptedAgent } from './acp-scripted-agent.test-support'
export const PID = 4242

/** A scripted agent behind the connection surface: the real protocol runtime over in-memory stdio,
* with the process lifecycle `AcpAgentConnection` gives it (stdout EOF is not exit; the exit closes
* the protocol with the agent's last words; a protocol failure while it runs is `onClose`). */
export class FakeAcpChild extends AcpSessionRuntime implements AcpStructuredConnection {
readonly agent: AcpScriptedAgent
readonly pid: number | undefined = PID
readonly spawned = Promise.resolve()
stderr = ''
processTreeUnproven = false
private exitListeners: ((exit: ProviderProcessExit) => void)[] = []
private gone = false
private closing = false
private lostWith: Error | undefined
closes = 0

constructor(
readonly launch: ProviderProcessLaunch,
private readonly connectionOptions: AcpAgentConnectionOptions,
agent = new AcpScriptedAgent()
) {
const lifecycle: { child: FakeAcpChild | null } = { child: null }
super(agent.stdout, agent.stdin, {
...connectionOptions,
peer: { ...connectionOptions.peer, closeOnInputEnd: false },
onClose: (error) => lifecycle.child?.lost(error)
})
lifecycle.child = this
this.agent = agent
}

get exited() {
return this.gone
}
onExit(listener: (exit: ProviderProcessExit) => void): void {
if (this.gone) {
listener(FAKE_EXIT)
} else {
this.exitListeners.push(listener)
}
}
stderrTail(): string {
return this.stderr
}
pauseReading(): void {
this.agent.stdout.pause()
}
resumeReading(): void {
this.agent.stdout.resume()
}
/** What a close proves once the protocol is closed; replace it to leave the exit unproven. */
proveClose = async (): Promise<boolean> => {
this.exit()
return true
}
override close(error?: Error): Promise<boolean> {
this.closes += 1
this.closing ||= !this.gone
this.drainNotifications(error)
return this.proveClose().finally(() => super.close(error))
}
/** The agent process ends on its own (or Orca's close landed). */
exit(): void {
if (this.gone) {
return
}
this.gone = true
const error =
this.lostWith ?? new AcpConnectionClosedError(this.stderr || `${this.launch.command} exited`)
super.close(error)
this.connectionOptions.onExit?.(error, { expected: this.closing, exit: FAKE_EXIT })
for (const listener of this.exitListeners.splice(0)) {
listener(FAKE_EXIT)
}
}
private lost(error: Error): void {
this.lostWith ??= error
if (this.closing || this.gone) {
return
}
this.connectionOptions.onClose?.(error)
}
}

const FAKE_EXIT: ProviderProcessExit = { code: 0, signal: null, processless: false }
Loading
Loading