refactor(agent): simplify inbox-driven turn admission
This commit is contained in:
@@ -32,9 +32,9 @@ async function unwrap<T>(response: RpcResponse<T>, dispose: () => Promise<void>)
|
||||
|
||||
/**
|
||||
* Consume mux frames until the task turn ends, per the cli-demo runOneShot
|
||||
* correlation precedent: anchor on the first turn/start whose trigger kind is
|
||||
* 'message' (startup-injected turns are skipped), aggregate text from that
|
||||
* turn's assistant/message events (last one wins), finish on its turn/end.
|
||||
* correlation precedent: the stream opens immediately before the prompt, so
|
||||
* its first observed turn/start owns the task. Aggregate text from that turn's
|
||||
* assistant/message events (last one wins), then finish on its turn/end.
|
||||
*/
|
||||
async function consumeUntilTurnEnd(frames: AsyncIterable<RpcRequest<MuxFrame>>, sessionId: SessionId): Promise<TurnOutcome> {
|
||||
let targetTurn: number | undefined
|
||||
|
||||
@@ -92,7 +92,7 @@ async function seedResumeSession(cwd: string): Promise<void> {
|
||||
{ type: 'step/end', seq: 5, time: 1_700_000_000_006, data: { turn: 1, step: 1 } },
|
||||
{ type: 'session/title', seq: 6, time: 1_700_000_000_007, data: { title: 'Resume selector design', messageSeqs: [1], source: { kind: 'fallback' } } },
|
||||
{ type: 'todo/write', seq: 7, time: 1_700_000_000_008, data: { todos: [{ content: 'Preserve restored state', status: 'in_progress' }] } },
|
||||
{ type: 'turn/end', seq: 8, time: 1_700_000_000_009, data: { turn: 1, reason: { kind: 'completed' } } },
|
||||
{ type: 'turn/end', seq: 8, time: 1_700_000_000_009, data: { turn: 1, step: 1, reason: { kind: 'completed' } } },
|
||||
]
|
||||
const file = logPath(join(cwd, '.sessions'), sessionCwd, id, 'none')
|
||||
await mkdir(dirname(file), { recursive: true })
|
||||
|
||||
@@ -509,14 +509,14 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
|
||||
expect(await terminal.snapshot({ includeScrollback: true }))
|
||||
.toContain('/compact — Compact older conversation history')
|
||||
|
||||
const compact = ctx.compact as DeferredSnapshotCompactService
|
||||
const compact = ctx.compact as unknown as DeferredSnapshotCompactService
|
||||
const inbox: string[] = []
|
||||
manualOrder = []
|
||||
ctx.on('agent/inbox/enqueue', (subject, item) => {
|
||||
if (subject === agent) inbox.push(`enqueue:${item.placement}:${item.id}`)
|
||||
ctx.on('agent/inbox/inserted', (subject, { message }) => {
|
||||
if (subject === agent) inbox.push(`inserted:${message.id}`)
|
||||
})
|
||||
ctx.on('agent/inbox/dequeue', (subject, message) => {
|
||||
if (subject === agent) inbox.push(`dequeue:${message.id}`)
|
||||
ctx.on('agent/inbox/claimed', (subject, { message }) => {
|
||||
if (subject === agent) inbox.push(`claimed:${message.id}`)
|
||||
})
|
||||
ctx.on('session/event', (session, event) => {
|
||||
if (session !== agent.session) return
|
||||
@@ -531,7 +531,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
|
||||
if (event.type === 'user/message'
|
||||
&& event.data.source.kind === 'plugin'
|
||||
&& event.data.source.plugin === 'compact') manualOrder?.push('checkpoint')
|
||||
if (event.type === 'turn/start') manualOrder?.push(`turn/start:${event.data.trigger.kind}`)
|
||||
if (event.type === 'turn/start') manualOrder?.push('turn/start')
|
||||
})
|
||||
ctx.on('session/flush', (session) => {
|
||||
if (session === agent.session) manualOrder?.push('flush')
|
||||
@@ -562,7 +562,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
|
||||
content: [{ type: 'text', text: 'Injected while compaction was running.' }],
|
||||
source: { kind: 'plugin', plugin: 'snapshot-injector' },
|
||||
}))
|
||||
expect(inbox[0]).toMatch(/^enqueue:queued:/u)
|
||||
expect(inbox[0]).toMatch(/^inserted:/u)
|
||||
expect(agent.status).toBe('idle')
|
||||
expect(agent.session.events.some(event => event.type === 'user/message'
|
||||
&& event.data.source.kind === 'user'
|
||||
@@ -575,7 +575,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
|
||||
agent.session.events.slice(-12).map(event => event.type).join(',')
|
||||
}`)
|
||||
await settleTerminal(terminal)
|
||||
expect(inbox).toEqual([inbox[0], `dequeue:${inbox[0]?.slice('enqueue:queued:'.length) ?? ''}`])
|
||||
expect(inbox).toEqual([inbox[0], `claimed:${inbox[0]?.slice('inserted:'.length) ?? ''}`])
|
||||
}
|
||||
|
||||
const events: SessionEvent[] = [...agent.session.events]
|
||||
@@ -641,7 +641,7 @@ async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
|
||||
const compactStartIndex = manualTimeline.indexOf('compact/start')
|
||||
const compactEndIndex = manualTimeline.indexOf('compact/end')
|
||||
const firstFlushIndex = manualTimeline.indexOf('flush')
|
||||
const queuedTurnIndex = manualTimeline.indexOf('turn/start:message')
|
||||
const queuedTurnIndex = manualTimeline.indexOf('turn/start')
|
||||
const commandDoneIndex = manualTimeline.indexOf('command/done')
|
||||
expect(manualTimeline.filter(item => item === 'command/run')).toHaveLength(1)
|
||||
expect(manualTimeline.filter(item => item === 'command/done')).toHaveLength(1)
|
||||
|
||||
Reference in New Issue
Block a user