refactor(agent-loop): simplify message machine

This commit is contained in:
_Kerman
2026-07-30 13:49:57 +08:00
parent d554ae3019
commit f2e20c1ef0
212 changed files with 1326 additions and 2382 deletions
@@ -12,13 +12,6 @@ import { SessionId } from '@deepseek-ai/dsh-session'
import type { TurnEndReason } from '@deepseek-ai/dsh-session'
import * as goalSession from '../src/index.ts'
declare module '@deepseek-ai/dsh-session' {
interface TurnTriggerMap {
/** Test-only plugin turn with no message source. */
'test-metadata': { kind: 'test-metadata' }
}
}
type ScriptEntry = StreamChunk[] | Error | 'hang' | ((options: GenerateOptions) => StreamChunk[])
/** Small request-recording adapter with controllable failure and cancellation. */
@@ -334,31 +327,6 @@ describe('same-session goal driving', () => {
expect(requestText(test.adapter.requests[1]!)).toContain('<goal_round>')
})
it('ignores plugin-owned turn triggers while a goal round is queued', async () => {
const test = await harness([textResponse('goal answer')])
const warnings: string[] = []
test.ctx.logger.warn = ((message: unknown) => { warnings.push(String(message)) }) as typeof test.ctx.logger.warn
let inserted = false
test.ctx.on('agent/inbox/enqueue', (agent, info) => {
if (agent !== test.agent || info.source.kind !== 'goal' || inserted) return
inserted = true
const lastStart = agent.session.events.findLast(event => event.type === 'turn/start')
const turn = (lastStart?.data.turn ?? 0) + 1
agent.session.append('turn/start', {
turn,
trigger: { kind: 'test-metadata' },
})
agent.session.append('turn/end', { turn, reason: { kind: 'completed' } })
})
test.ctx.goals.create(test.agent, { objective: 'ignore metadata', maxGoalRounds: 1 })
await waitForGoal(test.ctx, test.agent, goal => goal?.phase === 'blocked')
expect(inserted).toBe(true)
expect(test.adapter.requests).toHaveLength(1)
expect(warnings.some(warning => warning.includes('session/event listener threw'))).toBe(false)
})
it('makes a reserved round stale when a listener queues human work behind it', async () => {
const test = await harness([textResponse('human batch'), textResponse('later goal')])
let inserted = false
@@ -908,8 +876,7 @@ describe('same-session goal driving', () => {
let queued = false
test.ctx.on('session/event', (session, event) => {
if (session !== test.agent.session || queued) return
if (event.type === 'turn/start' && event.data.trigger.kind === 'message'
&& event.data.trigger.source.kind === 'goal') {
if (event.type === 'user/message' && event.data.source.kind === 'goal') {
queued = true
test.agent.followup(createUserMessage({ content: [{ type: 'text', text: 'human interleaved' }], source: { kind: 'user' } }))
}
@@ -997,7 +964,6 @@ describe('same-session goal driving', () => {
const orphan = test.ctx.sessions.create(SessionId('goal-session-orphan'))
orphan.append('turn/start', {
turn: 1,
trigger: { kind: 'injection', source: { kind: 'plugin', plugin: 'test' } },
})
orphan.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
@@ -41,7 +41,7 @@ function view(roundsStarted: number): GoalView {
}
function appendChange(session: Session): void {
session.append('turn/start', { turn: 1, trigger: { kind: 'injection', source: changeSource } })
session.append('turn/start', { turn: 1 })
session.append('user/message', createUserMessage({
content: renderGoalChange(change),
source: changeSource,
@@ -51,7 +51,7 @@ function appendChange(session: Session): void {
function appendRound(session: Session, turn: number, content = renderGoalRoundPrompt(view(turn - 2), turn - 1)): void {
const source = { kind: 'goal', goalId: change.goal.id, revision: 1, round: turn - 1 } as const
session.append('turn/start', { turn, trigger: { kind: 'message', source } })
session.append('turn/start', { turn })
session.append('user/message', createUserMessage({
content, source,
}), { surfaceOp: 'append' })
@@ -82,7 +82,7 @@ describe('goal-session prompt invariants', () => {
ctx.sessions.create(SessionId('goal-session-invariant-dispatch'))
const userSource = { kind: 'user' } as const
session.append('turn/start', { turn: 4, trigger: { kind: 'message', source: userSource } })
session.append('turn/start', { turn: 4 })
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'ordinary human message' }],
source: userSource,
@@ -90,7 +90,7 @@ describe('goal-session prompt invariants', () => {
session.append('turn/end', { turn: 4, reason: { kind: 'completed' } })
const stateSource = { ...changeSource, round: 0 } as const
session.append('turn/start', { turn: 5, trigger: { kind: 'message', source: stateSource } })
session.append('turn/start', { turn: 5 })
expect(() => {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'round zero is not a driver continuation' }],
@@ -114,7 +114,7 @@ describe('goal-session prompt invariants', () => {
it('rejects a goal round without a reconstructable active goal', async () => {
const { session } = await mount()
const source = { kind: 'goal', goalId: change.goal.id, revision: 1, round: 1 } as const
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source } })
session.append('turn/start', { turn: 1 })
expect(() => {
session.append('user/message', createUserMessage({
@@ -128,7 +128,7 @@ describe('goal-session prompt invariants', () => {
it('attributes an invalid durable prefix during late loading', async () => {
const { ctx, session } = await mount(true)
session.append('turn/start', { turn: 1, trigger: { kind: 'injection', source: changeSource } })
session.append('turn/start', { turn: 1 })
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'counterfeit goal state' }],
source: changeSource,