|
|
@@ -12,6 +12,13 @@ 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. */
|
|
|
@@ -323,6 +330,31 @@ 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/queued', (agent, _content, 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
|