fix(ci): repair main's line limits and a stale test import (#33675)

* test(native-chat): split startup report coverage below line limit

* test(native-chat): keep startup status reads synchronous

* refactor(native-chat): move status child fields below line limit

* test(native-chat): annotate exported startup mock types

* test(chat-rename): import launch persistence from its current path
This commit is contained in:
Brennan Benson
2026-10-25 19:53:28 -04:00
committed by GitHub
parent d37c04c82c
commit 5a4dd30048
6 changed files with 400 additions and 327 deletions
@@ -0,0 +1,225 @@
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, expect, vi, type Mock } from 'vitest'
import type { AgentJournalSubmission } from '../../../shared/agent-session-journal-types'
import { computeAgentSessionPayloadFingerprint } from '../../../shared/agent-session-mutation-envelope'
import { codexProviderHandle } from '../../../shared/agent-session-provider-handle-encoding'
import type { AgentSessionRecordStore } from '../../runtime/agent-session-record-store'
import { openTestAgentSessionRecordStore } from '../../runtime/agent-session-record-store-test-harness'
import { openTestJournalHostDatabase } from '../agent-session-journal/journal-host-database-test-support'
import type {
AgentSessionAcquisition,
StructuredAgentSessionAcquireInput,
StructuredAgentSessionAdapter
} from './structured-agent-session-adapter'
import {
claudeAndCodexDeclared,
NO_STRUCTURED_AGENTS
} from './structured-agent-session-adapter-router-test-support'
import { StructuredAgentSessionHost } from './structured-agent-session-host'
import {
HOST_TEST_NOW as NOW,
HOST_TEST_SESSION as SESSION,
HOST_TEST_THREAD as THREAD,
hostTestAttachParams,
hostTestMessage,
hostTestOperationId,
resetHostTestOperationIds
} from './structured-agent-session-host-test-data'
import { createStructuredAgentSessionLogger } from './structured-agent-session-logger'
import type { StructuredAgentSessionStartupLimits } from './structured-agent-session-startup-attempt-contract'
export const CALLER = { callerKey: 'client-1' }
export const eventually = (assertion: () => void | Promise<void>, interval = 0): Promise<void> =>
vi.waitFor(assertion, { timeout: 10_000, interval })
let root: string
export let store: AgentSessionRecordStore
export let host: StructuredAgentSessionHost
export let acquire: Mock<StructuredAgentSessionAdapter['acquire']>
export let dispatch: Mock<StructuredAgentSessionAdapter['dispatch']>
export let closeSession: Mock<NonNullable<StructuredAgentSessionAdapter['closeSession']>>
let setOption: Mock<StructuredAgentSessionAdapter['setOption']>
export const providerStarted: Mock = vi.fn()
export const recordLiveListing: Mock = vi.fn()
const openGates = new Map<string, (model: string) => void>()
export async function proveOpen(model = 'default'): Promise<void> {
const generation = `generation-${acquire.mock.calls.length}`
await eventually(() => expect(openGates.has(generation)).toBe(true))
openGates.get(generation)!(model)
openGates.delete(generation)
}
export function acquisition(input: StructuredAgentSessionAcquireInput): AgentSessionAcquisition {
return {
process: {
hostId: 'local',
pid: 4242,
processStartTimeMs: 1_700_000_000_000,
spawnToken: input.spawnToken
},
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
link: {
linkId: `link-${input.fence}`,
handle: codexProviderHandle(THREAD),
origin: store.getRecord(SESSION)?.providerHandleChain.length ? 'resumed' : 'created',
mintedAtFence: input.fence,
observedAt: NOW
}
}
}
export const publishFirst: StructuredAgentSessionAdapter['acquire'] = async (input) =>
acquisition(input)
type StartupHostOptions = {
startupLimits?: Partial<StructuredAgentSessionStartupLimits>
supportsGoals?: boolean
}
async function startHost({ startupLimits, supportsGoals }: StartupHostOptions = {}): Promise<void> {
host = new StructuredAgentSessionHost({
agents: supportsGoals ? claudeAndCodexDeclared() : NO_STRUCTURED_AGENTS,
logger: createStructuredAgentSessionLogger(),
store,
adapter: {
open: ({ acquisitionGeneration }) =>
new Promise((resolve) => {
openGates.set(acquisitionGeneration!, (model) =>
resolve({ reportedOptions: { model, reportedPermissionMode: 'ask' } })
)
}),
acquire,
dispatch,
closeSession,
releaseAcquisition: vi.fn(async () => true),
cancelTurn: vi.fn(async () => ({ cancelled: false })),
answerPrompt: vi.fn(async () => undefined),
setOption
},
modelCatalog: {
peek: () => null,
read: vi.fn(async () => ({ origin: 'unknown' as const })),
recordLiveListing,
retainedLiveModelIds: vi.fn(async () => []),
prewarm: vi.fn(async () => {}),
stop: vi.fn(),
providerStarted
},
journalDatabase: openTestJournalHostDatabase(root),
claimKeyId: 'key-1',
mintSpawnToken: () => `spawn-${acquire.mock.calls.length}`,
idleSweep: { intervalMs: 60 * 60_000, idleMs: 60 * 60_000 },
...(startupLimits === undefined ? {} : { startupLimits }),
now: () => NOW
})
}
export async function restartWith(
next: StructuredAgentSessionAdapter['acquire'],
options: StartupHostOptions = {}
): Promise<void> {
await host.close(SESSION, 'evict')
await host.flushAllStreamedEvents()
acquire.mockImplementation(next)
closeSession.mockClear()
await startHost(options)
acquire.mockClear()
}
export function registerStartupAttemptHostTests(): void {
beforeEach(async () => {
vi.useFakeTimers({
toFake: ['Date', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']
})
root = await mkdtemp(join(tmpdir(), 'orca-startup-attempt-'))
resetHostTestOperationIds()
providerStarted.mockReset()
recordLiveListing.mockReset()
openGates.clear()
acquire = vi.fn(async (input) => acquisition(input))
dispatch = vi.fn(async () => ({
state: 'accepted' as const,
providerIdentity: {
provider: 'codex' as const,
threadId: THREAD,
turnId: `turn-${dispatch.mock.calls.length}`,
ordinal: dispatch.mock.calls.length
}
}))
closeSession = vi.fn(async () => true)
setOption = vi.fn(async () => undefined)
store = await openTestAgentSessionRecordStore(root)
await startHost()
expect(await host.attach(CALLER, hostTestAttachParams(null))).toMatchObject({ ok: true })
})
afterEach(async () => {
try {
await host.flushAllStreamedEvents()
await rm(root, { recursive: true, force: true })
} finally {
vi.useRealTimers()
}
})
}
function sendParams(text: string, clientOperationId = hostTestOperationId()) {
const body = hostTestMessage(text)
return {
envelope: {
sessionId: SESSION,
clientOperationId,
expectedRuntimeFence: 1,
payloadFingerprint: computeAgentSessionPayloadFingerprint({
method: 'agentSession.send',
sessionId: SESSION,
fields: { body }
})
},
body
}
}
export async function accept(text: string, clientOperationId?: string): Promise<string> {
const params = sendParams(text, clientOperationId)
expect(await host.send(CALLER, params)).toMatchObject({
ok: true,
value: { submission: { dispatchState: 'pending', handoverRecorded: true } }
})
return params.envelope.clientOperationId
}
export async function submission(id: string): Promise<AgentJournalSubmission | undefined> {
return (await host.journalSnapshot(SESSION)).submissions.find(
(entry) => entry.clientMessageId === id
)
}
export async function heldBehindStart(text: string, interval = 0): Promise<string> {
const id = await accept(text)
await eventually(
async () => expect(host.readStatusSummary(SESSION)?.hostExecutionPhase).toBe('starting'),
interval
)
return id
}
export function stop() {
return host.cancel(CALLER, {
envelope: {
sessionId: SESSION,
clientOperationId: hostTestOperationId(),
expectedRuntimeFence: null,
payloadFingerprint: computeAgentSessionPayloadFingerprint({
method: 'agentSession.cancel',
sessionId: SESSION,
fields: { turnId: 'turn-none' }
})
},
turnId: 'turn-none'
})
}
@@ -1,230 +1,52 @@
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { CodexAppServerRequestError } from '../../codex/app-server/codex-app-server-request-error'
import { withProviderRequestTimeout } from '../../provider-process/provider-request-timeout'
import { AgentSessionAcquisitionRefusal } from './structured-agent-session-adapter'
import { agentSessionFailureFact } from '../../../shared/agent-session-failure'
import { agentSessionFailureWords } from '../../../shared/agent-session-failure-words'
import type { AgentJournalSubmission } from '../../../shared/agent-session-journal-types'
import { AGENT_JOURNAL_THREAD_SCOPE } from '../../../shared/agent-session-journal-types'
import { computeAgentSessionPayloadFingerprint } from '../../../shared/agent-session-mutation-envelope'
import { codexProviderHandle } from '../../../shared/agent-session-provider-handle-encoding'
import { DISPATCH_REJECTED_CANCELLED } from '../../../shared/structured-agent-session-dispatch-rejection'
import { NATIVE_CHAT_STOPPED_BEFORE_START_TEXT } from '../../../shared/native-chat-stopped-before-start'
import { projectStructuredAgentSessionMessages } from '../../../shared/structured-agent-session-message-projection'
import type { AgentSessionRecordStore } from '../../runtime/agent-session-record-store'
import { openTestAgentSessionRecordStore } from '../../runtime/agent-session-record-store-test-harness'
import { openTestJournalHostDatabase } from '../agent-session-journal/journal-host-database-test-support'
import type {
AgentSessionAcquisition,
StructuredAgentSessionAcquireInput,
StructuredAgentSessionAdapter
} from './structured-agent-session-adapter'
import type { StructuredAgentSessionAdapter } from './structured-agent-session-adapter'
import {
claudeAndCodexDeclared,
NO_STRUCTURED_AGENTS
} from './structured-agent-session-adapter-router-test-support'
import { StructuredAgentSessionHost } from './structured-agent-session-host'
import {
HOST_TEST_NOW as NOW,
HOST_TEST_SESSION as SESSION,
HOST_TEST_THREAD as THREAD,
hostTestAttachParams,
hostTestMessage,
hostTestOperationId,
resetHostTestOperationIds
hostTestOperationId
} from './structured-agent-session-host-test-data'
import { createStructuredAgentSessionLogger } from './structured-agent-session-logger'
import type { StructuredAgentSessionStartupLimits } from './structured-agent-session-startup-attempt-contract'
import {
CALLER,
accept,
acquisition,
acquire,
closeSession,
dispatch,
eventually,
heldBehindStart,
host,
proveOpen,
providerStarted,
publishFirst,
restartWith as restartHostWith,
stop,
store,
submission,
registerStartupAttemptHostTests
} from './structured-agent-session-startup-attempt-host-test-support'
const CALLER = { callerKey: 'client-1' }
const eventually = (assertion: () => void | Promise<void>, interval = 0): Promise<void> =>
vi.waitFor(assertion, { timeout: 10_000, interval })
let root: string
let store: AgentSessionRecordStore
let host: StructuredAgentSessionHost
let acquire: Mock<StructuredAgentSessionAdapter['acquire']>
let dispatch: Mock<StructuredAgentSessionAdapter['dispatch']>
let closeSession: Mock<NonNullable<StructuredAgentSessionAdapter['closeSession']>>
let setOption: Mock<StructuredAgentSessionAdapter['setOption']>
let startupLimits: Partial<StructuredAgentSessionStartupLimits> | undefined
let supportsGoals = false
const providerStarted = vi.fn()
const recordLiveListing = vi.fn()
const openGates = new Map<string, (model: string) => void>()
async function proveOpen(model = 'default'): Promise<void> {
const generation = `generation-${acquire.mock.calls.length}`
await eventually(() => expect(openGates.has(generation)).toBe(true))
openGates.get(generation)!(model)
openGates.delete(generation)
}
function acquisition(input: StructuredAgentSessionAcquireInput): AgentSessionAcquisition {
return {
process: {
hostId: 'local',
pid: 4242,
processStartTimeMs: 1_700_000_000_000,
spawnToken: input.spawnToken
},
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
link: {
linkId: `link-${input.fence}`,
handle: codexProviderHandle(THREAD),
origin: store.getRecord(SESSION)?.providerHandleChain.length ? 'resumed' : 'created',
mintedAtFence: input.fence,
observedAt: NOW
}
}
}
const publishFirst: StructuredAgentSessionAdapter['acquire'] = async (input) => acquisition(input)
async function startHost(): Promise<void> {
host = new StructuredAgentSessionHost({
agents: supportsGoals ? claudeAndCodexDeclared() : NO_STRUCTURED_AGENTS,
logger: createStructuredAgentSessionLogger(),
store,
adapter: {
open: ({ acquisitionGeneration }) =>
new Promise((resolve) => {
openGates.set(acquisitionGeneration!, (model) =>
resolve({ reportedOptions: { model, reportedPermissionMode: 'ask' } })
)
}),
acquire,
dispatch,
closeSession,
releaseAcquisition: vi.fn(async () => true),
cancelTurn: vi.fn(async () => ({ cancelled: false })),
answerPrompt: vi.fn(async () => undefined),
setOption
},
modelCatalog: {
peek: () => null,
read: vi.fn(async () => ({ origin: 'unknown' as const })),
recordLiveListing,
retainedLiveModelIds: vi.fn(async () => []),
prewarm: vi.fn(async () => {}),
stop: vi.fn(),
providerStarted
},
journalDatabase: openTestJournalHostDatabase(root),
claimKeyId: 'key-1',
mintSpawnToken: () => `spawn-${acquire.mock.calls.length}`,
idleSweep: { intervalMs: 60 * 60_000, idleMs: 60 * 60_000 },
...(startupLimits === undefined ? {} : { startupLimits }),
now: () => NOW
})
}
async function restartWith(next: StructuredAgentSessionAdapter['acquire']): Promise<void> {
await host.close(SESSION, 'evict')
await host.flushAllStreamedEvents()
acquire.mockImplementation(next)
closeSession.mockClear()
await startHost()
acquire.mockClear()
}
beforeEach(async () => {
vi.useFakeTimers({
toFake: ['Date', 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']
})
root = await mkdtemp(join(tmpdir(), 'orca-startup-attempt-'))
resetHostTestOperationIds()
beforeEach(() => {
startupLimits = undefined
supportsGoals = false
providerStarted.mockReset()
recordLiveListing.mockReset()
openGates.clear()
acquire = vi.fn(async (input) => acquisition(input))
dispatch = vi.fn(async () => ({
state: 'accepted' as const,
providerIdentity: {
provider: 'codex' as const,
threadId: THREAD,
turnId: `turn-${dispatch.mock.calls.length}`,
ordinal: dispatch.mock.calls.length
}
}))
closeSession = vi.fn(async () => true)
setOption = vi.fn(async () => undefined)
store = await openTestAgentSessionRecordStore(root)
await startHost()
expect(await host.attach(CALLER, hostTestAttachParams(null))).toMatchObject({ ok: true })
})
registerStartupAttemptHostTests()
afterEach(async () => {
try {
await host.flushAllStreamedEvents()
await rm(root, { recursive: true, force: true })
} finally {
vi.useRealTimers()
}
})
function sendParams(text: string, clientOperationId = hostTestOperationId()) {
const body = hostTestMessage(text)
return {
envelope: {
sessionId: SESSION,
clientOperationId,
expectedRuntimeFence: 1,
payloadFingerprint: computeAgentSessionPayloadFingerprint({
method: 'agentSession.send',
sessionId: SESSION,
fields: { body }
})
},
body
}
}
async function accept(text: string, clientOperationId?: string): Promise<string> {
const params = sendParams(text, clientOperationId)
expect(await host.send(CALLER, params)).toMatchObject({
ok: true,
value: { submission: { dispatchState: 'pending', handoverRecorded: true } }
})
return params.envelope.clientOperationId
}
async function submission(id: string): Promise<AgentJournalSubmission | undefined> {
return (await host.journalSnapshot(SESSION)).submissions.find(
(entry) => entry.clientMessageId === id
)
}
async function heldBehindStart(text: string, interval = 0): Promise<string> {
const id = await accept(text)
await eventually(
async () =>
expect((await host.readStatusSummary(SESSION))?.hostExecutionPhase).toBe('starting'),
interval
)
return id
}
function stop() {
return host.cancel(CALLER, {
envelope: {
sessionId: SESSION,
clientOperationId: hostTestOperationId(),
expectedRuntimeFence: null,
payloadFingerprint: computeAgentSessionPayloadFingerprint({
method: 'agentSession.cancel',
sessionId: SESSION,
fields: { turnId: 'turn-none' }
})
},
turnId: 'turn-none'
})
async function restartWith(next: StructuredAgentSessionAdapter['acquire']): Promise<void> {
await restartHostWith(next, { startupLimits, supportsGoals })
}
const HOST_STOPPED = agentSessionFailureWords(agentSessionFailureFact('hostStopped'), {
@@ -272,59 +94,6 @@ describe('a send while the agent starts', () => {
})
})
it.each([false, true])(
'keeps current-generation reports across readiness (replaced=%s)',
async (replaced) => {
await store.replaceSessionOptions({
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
permissionMode: 'own',
now: NOW
})
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
const fence = store.getRecord(SESSION)!.lease.runtimeFence
const acquisitionGeneration = `generation-${acquire.mock.calls.length}`
const report = (
reportedOptions: { reportedPermissionMode?: string | null },
generation = acquisitionGeneration,
reportFence = fence
) =>
host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: reportFence,
acquisitionGeneration: generation,
reportedOptions
})
const early = report({ reportedPermissionMode: 'accept-edits' })
expect(host.readStatusSummary(SESSION)?.runtimePermission).toBeNull()
if (replaced) {
await host.close(SESSION, 'evict')
await early
await proveOpen()
await host.flushStreamedEvents(SESSION)
expect(host.readStatusSummary(SESSION)?.runtimePermission).toBeNull()
return
}
await proveOpen()
await early
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
const current = () => host.readStatusSummary(SESSION)?.runtimePermission
expect(current()).toEqual({ fence, mode: 'accept-edits' })
await report({ reportedPermissionMode: 'full-access' }, 'foreign')
await report({ reportedPermissionMode: 'full-access' }, acquisitionGeneration, fence + 1)
await report({})
expect(current()).toEqual({ fence, mode: 'accept-edits' })
await report({ reportedPermissionMode: null })
expect(current()).toEqual({ fence, mode: null })
expect(store.getRecord(SESSION)?.permissionMode).toBe('own')
expect(dispatch).toHaveBeenCalledTimes(1)
await host.close(SESSION, 'evict')
expect(current()).toBeNull()
}
)
describe('a start that ends before it proves itself', () => {
it.each([false, true])(
'settles only its own message after startup failure (acquisition refusal: %s)',
@@ -486,64 +255,6 @@ describe('a start that ends before it proves itself', () => {
})
})
describe('what a started child reports', () => {
it('keeps provider reports out of the saved next-message choice', async () => {
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
await proveOpen('reported')
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
await host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'from-settings' }
})
await host.flushStreamedEvents(SESSION)
expect(store.getRecord(SESSION)?.options).toBeUndefined()
})
it('preserves a later accepted pick when an older child reports options', async () => {
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
await store.replaceSessionOptions({
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
options: { model: 'picked' },
now: NOW
})
await proveOpen('reported')
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
await host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'from-settings' }
})
await host.flushStreamedEvents(SESSION)
expect(store.getRecord(SESSION)?.options).toEqual({ model: 'picked' })
})
it('saves a settings readback that arrives before open proves the start', async () => {
await restartWith(publishFirst)
await heldBehindStart('hello')
const listing = { models: [], configuredDefault: { modelId: 'opus' } }
const reported = host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'opus' },
catalogListing: listing
})
await proveOpen()
await reported
await eventually(() => expect(recordLiveListing).toHaveBeenCalledWith(SESSION, listing))
})
})
function startupAttemptOpen(): boolean {
return host.collaboratorsForTests().runtimeState.startupAttempts.isOpen(SESSION)
}
@@ -0,0 +1,132 @@
import { describe, expect, it } from 'vitest'
import {
HOST_TEST_NOW as NOW,
HOST_TEST_SESSION as SESSION
} from './structured-agent-session-host-test-data'
import {
acquire,
dispatch,
eventually,
heldBehindStart,
host,
proveOpen,
publishFirst,
recordLiveListing,
restartWith,
store,
submission,
registerStartupAttemptHostTests
} from './structured-agent-session-startup-attempt-host-test-support'
registerStartupAttemptHostTests()
it.each([false, true])(
'keeps current-generation reports across readiness (replaced=%s)',
async (replaced) => {
await store.replaceSessionOptions({
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
permissionMode: 'own',
now: NOW
})
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
const fence = store.getRecord(SESSION)!.lease.runtimeFence
const acquisitionGeneration = `generation-${acquire.mock.calls.length}`
const report = (
reportedOptions: { reportedPermissionMode?: string | null },
generation = acquisitionGeneration,
reportFence = fence
) =>
host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: reportFence,
acquisitionGeneration: generation,
reportedOptions
})
const early = report({ reportedPermissionMode: 'accept-edits' })
expect(host.readStatusSummary(SESSION)?.runtimePermission).toBeNull()
if (replaced) {
await host.close(SESSION, 'evict')
await early
await proveOpen()
await host.flushStreamedEvents(SESSION)
expect(host.readStatusSummary(SESSION)?.runtimePermission).toBeNull()
return
}
await proveOpen()
await early
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
const current = () => host.readStatusSummary(SESSION)?.runtimePermission
expect(current()).toEqual({ fence, mode: 'accept-edits' })
await report({ reportedPermissionMode: 'full-access' }, 'foreign')
await report({ reportedPermissionMode: 'full-access' }, acquisitionGeneration, fence + 1)
await report({})
expect(current()).toEqual({ fence, mode: 'accept-edits' })
await report({ reportedPermissionMode: null })
expect(current()).toEqual({ fence, mode: null })
expect(store.getRecord(SESSION)?.permissionMode).toBe('own')
expect(dispatch).toHaveBeenCalledTimes(1)
await host.close(SESSION, 'evict')
expect(current()).toBeNull()
}
)
describe('what a started child reports', () => {
it('keeps provider reports out of the saved next-message choice', async () => {
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
await proveOpen('reported')
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
await host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'from-settings' }
})
await host.flushStreamedEvents(SESSION)
expect(store.getRecord(SESSION)?.options).toBeUndefined()
})
it('preserves a later accepted pick when an older child reports options', async () => {
await restartWith(publishFirst)
const id = await heldBehindStart('hello')
await store.replaceSessionOptions({
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
options: { model: 'picked' },
now: NOW
})
await proveOpen('reported')
await eventually(async () => expect((await submission(id))?.dispatchState).toBe('accepted'))
await host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'from-settings' }
})
await host.flushStreamedEvents(SESSION)
expect(store.getRecord(SESSION)?.options).toEqual({ model: 'picked' })
})
it('saves a settings readback that arrives before open proves the start', async () => {
await restartWith(publishFirst)
await heldBehindStart('hello')
const listing = { models: [], configuredDefault: { modelId: 'opus' } }
const reported = host.handleAdapterEvent({
type: 'options-reported',
sessionId: SESSION,
fence: store.getRecord(SESSION)!.lease.runtimeFence,
acquisitionGeneration: `generation-${acquire.mock.calls.length}`,
reportedOptions: { model: 'opus' },
catalogListing: listing
})
await proveOpen()
await reported
await eventually(() => expect(recordLiveListing).toHaveBeenCalledWith(SESSION, listing))
})
})
@@ -31,6 +31,20 @@ export function structuredStatusChildWork(
return { children, ...(tasks ? { backgroundTasks: tasks } : {}) }
}
/** Exclude per-tick usage from global broadcasts; chat background channels retain it. */
export function structuredStatusChildWorkFields(
views: readonly AgentChildWorkView[] | undefined,
provider: StructuredAgentId
): StructuredStatusChildWork {
const { children, backgroundTasks } = structuredStatusChildWork(views, provider)
return {
...(backgroundTasks && backgroundTasks.length > 0
? { backgroundTasks: backgroundTasks.map(({ totalTokens: _tokens, ...task }) => task) }
: {}),
...(children ? { children } : {})
}
}
export function structuredStatusChildrenEqual(
a: readonly AgentChildWorkView[] | undefined,
b: readonly AgentChildWorkView[] | undefined
@@ -22,7 +22,7 @@ import {
retainedStructuredStatusSummaries,
structuredAgentSessionStatusSummary
} from './structured-agent-session-status-summary'
import { structuredStatusChildWork } from './structured-agent-session-status-child-work'
import { structuredStatusChildWorkFields } from './structured-agent-session-status-child-work'
import {
StructuredAgentSessionJournalProjections,
type StructuredAgentSessionJournalProjection
@@ -244,16 +244,7 @@ export class StructuredAgentSessionStatusFeed {
sessionId: string,
provider: StatusFeedSession['params']['provider']
): Pick<AgentSessionStatusSummary, 'children' | 'backgroundTasks'> {
const { children, backgroundTasks } = structuredStatusChildWork(
this.readChildWork(sessionId),
provider
)
return {
...(backgroundTasks && backgroundTasks.length > 0
? { backgroundTasks: backgroundTasks.map(({ totalTokens: _tokens, ...task }) => task) }
: {}),
...(children ? { children } : {})
}
return structuredStatusChildWorkFields(this.readChildWork(sessionId), provider)
}
/** Publish evidence then refold parent/summary; sink failure must not block other publications. */
@@ -41,7 +41,7 @@ import type { RuntimeMobileSessionTabsResult } from '../../../shared/runtime-typ
import { applyWebSessionTabsSnapshot } from './web-session-tabs-sync/snapshot-api'
import { applyWebSessionTabsStorePatch } from './web-session-tabs-sync/store-patch'
import { applyLocalStructuredSessionTabSnapshots } from './local-structured-session-tabs-sync/snapshot-apply'
import { readStructuredAgentLaunchRecord } from '@/lib/structured-agent-session-launch-persistence'
import { readStructuredAgentLaunchRecord } from '@/lib/agent-session/launch/structured-agent-session-launch-persistence'
const mock = vi.hoisted(() => ({
rpc: vi.fn(),