Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1a07fc6
Every agent opens its chat at launch, and one Stop rule (rebuilt on m…
brennanb2025 Oct 9, 2026
ad5d2cc
Keep an agent's own sign-in words on a refused start; let a picker re…
brennanb2025 Oct 9, 2026
a6c3a0f
Take the model-catalog start hold back out
brennanb2025 Oct 9, 2026
58f3e6f
Merge origin/main: a chat's learned default is reported at its proven…
brennanb2025 Oct 10, 2026
d7acf9a
Merge remote-tracking branch 'origin/main' into brennanb2025/agent-pu…
brennanb2025 Oct 10, 2026
2cade43
Merge remote-tracking branch 'origin/main' into brennanb2025/agent-pu…
brennanb2025 Oct 10, 2026
20832cb
Merge remote-tracking branch 'origin/main' into brennanb2025/agent-pu…
brennanb2025 Oct 10, 2026
2a7fa4e
fix(native-chat): a Codex or Pi start has only the host's start deadline
brennanb2025 Oct 10, 2026
4ad891d
Merge remote-tracking branch 'origin/main' into brennanb2025/agent-pu…
brennanb2025 Oct 10, 2026
5c66c69
fix(native-chat): settle a signed-out Pi's first message once; keep h…
brennanb2025 Oct 10, 2026
05ccab6
fix(native-chat): keep a chat's picks shown while its next runtime st…
brennanb2025 Oct 10, 2026
fc15e0a
Merge remote-tracking branch 'origin/main' into brennanb2025/agent-pu…
brennanb2025 Oct 11, 2026
70febbb
Prove Codex workload exit and keep failed-start option reads passive
brennanb2025 Oct 11, 2026
e7fb001
Report unobserved Codex workload exit as unverifiable
brennanb2025 Oct 11, 2026
18ec576
Keep connection mocks in tests and extract the scripted server fixture
brennanb2025 Oct 11, 2026
34e5388
Fix the queued Stop test clock in CI
brennanb2025 Oct 11, 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: 8 additions & 1 deletion config/scripts/vitest-sqlite-runtime-files.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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 @@ -164,7 +165,7 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/native-chat/agent-session-wire/structured-agent-session-stale-turn-open.test.ts',
'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',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
fd -i 'structured-agent-session-startup-attempt-registrations.test.ts'
fd -i 'structured-agent-session-startup-picks.test.ts'
rg -n 'SQLITE_RUNTIME_INCLUDE|SQLITE_DATABASE_RUNTIME_INCLUDE' --type=js --type=ts -g '!node_modules' -C3
for f in $(fd -i -e ts 'startup-attempt-registrations|acp-structured-host-start-failure|acp-structured-publish-at-spawn|acp-structured-host-lifecycle'); do
  echo "== $f"; rg -n 'openTestAgentSessionRecordStore|openTestJournalHostDatabase|openHostRig|openProviderTimelineRig' "$f" | head
  rg -n "$(basename "$f")" config/scripts/vitest-sqlite-runtime-files.mjs
done

Repository: stablyai/orca

Length of output: 3515


🏁 Script executed:

set -eu
printf '%s\n' '== revision availability and changed paths =='
git cat-file -e a6c3a0fada321cce276ed6d75f3717bbe3162fce^{commit}
git cat-file -e 2cb2bef67e3a8c9435c8ebd30ce6f2f0b4dadf08^{commit}
git diff --name-status 2cb2bef67e3a8c9435c8ebd30ce6f2f0b4dadf08 a6c3a0fada321cce276ed6d75f3717bbe3162fce -- \
  config/scripts/vitest-sqlite-runtime-files.mjs \
  'src/main/**/structured-agent-session-startup-attempt-registrations.test.ts' \
  'src/main/**/structured-agent-session-startup-picks.test.ts' \
  'src/main/acp/acp-structured-host-start-failure.test.ts' \
  'src/main/acp/acp-structured-publish-at-spawn.test.ts' \
  'src/main/acp/acp-structured-host-lifecycle.test.ts'
printf '%s\n' '== current runtime include file =='
nl -ba config/scripts/vitest-sqlite-runtime-files.mjs
printf '%s\n' '== relevant files in reviewed head =='
for f in \
  src/main/native-chat/agent-session-wire/structured-agent-session-startup-picks.test.ts \
  src/main/acp/acp-structured-host-start-failure.test.ts \
  src/main/acp/acp-structured-publish-at-spawn.test.ts \
  src/main/acp/acp-structured-host-lifecycle.test.ts; do
  if git cat-file -e "a6c3a0fada321cce276ed6d75f3717bbe3162fce:$f" 2>/dev/null; then
    echo "== $f"
    git show "a6c3a0fada321cce276ed6d75f3717bbe3162fce:$f" | nl -ba | sed -n '1,140p'
  else
    echo "MISSING $f"
  fi
done
printf '%s\n' '== old test in base and head =='
for rev in 2cb2bef67e3a8c9435c8ebd30ce6f2f0b4dadf08 a6c3a0fada321cce276ed6d75f3717bbe3162fce; do
  echo "REV $rev"
  git ls-tree -r --name-only "$rev" | grep -F 'structured-agent-session-startup-attempt-registrations.test.ts' || :
done

Repository: stablyai/orca

Length of output: 42058


🏁 Script executed:

set -eu
printf '%s\n' '== runtime include membership =='
for f in \
  config/scripts/vitest-sqlite-runtime-files.mjs \
  config/scripts/vitest-sqlite-database-runtime-files.mjs \
  config/scripts/vitest-node-runtime-files.mjs; do
  echo "== $f"
  rg -n -F -- \
    'acp-structured-host-start-failure.test.ts' \
    'acp-structured-publish-at-spawn.test.ts' \
    'acp-structured-host-lifecycle.test.ts' \
    'structured-agent-session-startup-attempt-registrations.test.ts' \
    'structured-agent-session-startup-picks.test.ts' "$f" || :
done
printf '%s\n' '== runtime selection references =='
rg -n -F -- 'SQLITE_RUNTIME_INCLUDE' config package.json pnpm-workspace.yaml . 2>/dev/null | head -80 || :
printf '%s\n' '== helper definitions =='
rg -n -F -- 'export async function openHostRig' src/main/acp 'export async function openAcpAdapterRig' src/main/acp || :
rg -n -F -- 'function openHostRig' src/main/acp 'function openAcpAdapterRig' src/main/acp || :
printf '%s\n' '== host rig support =='
for f in src/main/acp/acp-structured-host.test-support.ts src/main/acp/acp-structured-adapter.test-support.ts; do
  echo "== $f"
  rg -n -C6 -F -- 'openHostRig' "$f" || :
  rg -n -C6 -F -- 'openAcpAdapterRig' "$f" || :
done

Repository: stablyai/orca

Length of output: 5345


🏁 Script executed:

set -eu
printf '%s\n' '== exact runtime include membership =='
for name in \
  acp-structured-host-start-failure.test.ts \
  acp-structured-publish-at-spawn.test.ts \
  acp-structured-host-lifecycle.test.ts \
  structured-agent-session-startup-attempt-registrations.test.ts \
  structured-agent-session-startup-picks.test.ts; do
  echo "== $name"
  for f in \
    config/scripts/vitest-sqlite-runtime-files.mjs \
    config/scripts/vitest-sqlite-database-runtime-files.mjs \
    config/scripts/vitest-node-runtime-files.mjs; do
    rg -n -F -- "$name" "$f" || :
  done
done
printf '%s\n' '== runtime selector =='
rg -n -C5 -F -- 'SQLITE_RUNTIME_INCLUDE' config/scripts package.json
printf '%s\n' '== host rig implementation =='
sed -n '1,125p' src/main/acp/acp-structured-host.test-support.ts
printf '%s\n' '== adapter rig implementation =='
sed -n '1,145p' src/main/acp/acp-structured-adapter.test-support.ts

Repository: stablyai/orca

Length of output: 13392


Add the new ACP tests to the SQLite runtime include.

The startup-attempt registrations test was deleted, so it does not need to be restored. However, the three new ACP tests are absent from the SQLite runtime lists. Their rigs use real record-store or journal fixtures. Without these entries, the tests can run under the wrong Vitest runtime.

🐛 Suggested fix
   'src/main/acp/acp-structured-host-configured-default.test.ts',
+  'src/main/acp/acp-structured-host-start-failure.test.ts',
+  'src/main/acp/acp-structured-host-lifecycle.test.ts',
+  'src/main/acp/acp-structured-publish-at-spawn.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 @@ -232,13 +233,19 @@ export const SQLITE_RUNTIME_INCLUDE = [
'src/main/runtime/runtime-orchestration-sender-names.test.ts',
'src/main/runtime/runtime-worktree-agent-rows-verdict.test.ts',
'src/main/runtime/runtime-worktree-structured-agent-rows-liveness.test.ts',
'src/main/runtime/structured-agent-registration-lifecycle-matrix.test.ts',
'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/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',
'src/main/runtime/structured-agent-session-mobile-restore.test.ts',
'src/main/runtime/structured-agent-session-paired-caller.test.ts',
'src/main/runtime/structured-agent-session-pi-signed-out-start.test.ts',
'src/main/runtime/structured-agent-session-pi-startup.test.ts',
'src/main/runtime/structured-agent-session-provider-operations-while-starting.test.ts',
'src/main/runtime/structured-agent-session-rollback-compatibility.test.ts',
'src/main/runtime/structured-agent-session-spawn-token-descendants.test.ts',
'src/main/runtime/structured-agent-session-startup-reconcile.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