test: update conversation assembly coverage and docs

This commit is contained in:
imccyu
2026-08-09 15:48:56 +08:00
parent 6d09b3168d
commit 522acc7432
101 changed files with 3970 additions and 2295 deletions

View File

@@ -2,8 +2,9 @@ import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import {
COMPACT_CHECKPOINT_SOURCE,
CompactionId,
CompactService,
compactCheckpointSource,
isCompactCheckpointSource,
} from '@deepseek-ai/dsh-compact'
import type { CompactionResult, CompactionTrigger } from '@deepseek-ai/dsh-compact'
@@ -52,9 +53,11 @@ class StubCompactService extends CompactService {
const endIndex = surface.indexOf(end)
if (startIndex < 0 || endIndex < startIndex) throw new Error('stub compact range is invalid')
const shadowedSeqs = surface.slice(startIndex, endIndex + 1)
const compactionId = CompactionId('stub-compaction')
// Minimal stub honoring the lock + log-only event contract.
const startEvent = session.append('compact/start', { turn: 0 })
const startEvent = session.append('compact/start', { compactionId, turn: 0 })
const summaryEvent = session.append('compact/summary', {
compactionId,
summary,
shadowedRange: { start, end },
shadowedSeqs,
@@ -64,13 +67,14 @@ class StubCompactService extends CompactService {
})
session.append('user/message', createUserMessage({
content: summary,
source: COMPACT_CHECKPOINT_SOURCE,
source: compactCheckpointSource(compactionId),
}), {
surfaceOp: { op: 'replace', start, end },
sourceEventSeqs: [startEvent.seq, summaryEvent.seq, ...shadowedSeqs],
})
const endEvent = session.append('compact/end', { turn: 0 })
const endEvent = session.append('compact/end', { compactionId, turn: 0 })
return {
compactionId,
startSeq: startEvent.seq,
summarySeq: summaryEvent.seq,
endSeq: endEvent.seq,
@@ -139,7 +143,8 @@ describe('CompactService seam', () => {
expect(result.shadowedSeqs).toEqual([original.seq])
const checkpoint = session.events.find(event => event.type === 'user/message'
&& isCompactCheckpointSource(event.data.source))
expect(checkpoint?.type === 'user/message' && checkpoint.data.source).toEqual(COMPACT_CHECKPOINT_SOURCE)
expect(checkpoint?.type === 'user/message' && checkpoint.data.source)
.toEqual(compactCheckpointSource(result.compactionId))
expect(isCompactCheckpointSource({ kind: 'plugin', plugin: 'other' })).toBe(false)
expect(isCompactCheckpointSource({ kind: 'user' })).toBe(false)
expect(session.events.filter(e => e.type.startsWith('compact/')).map(e => e.type))

View File

@@ -1,7 +1,10 @@
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { CompactionId, compactCheckpointSource } from '@deepseek-ai/dsh-compact'
import * as CompactInvariant from '@deepseek-ai/dsh-compact/invariant'
import { CommandId } from '@deepseek-ai/dsh-commands/brand'
import InvariantService from '@deepseek-ai/dsh-invariants'
async function setup(): Promise<Context> {
@@ -12,7 +15,13 @@ async function setup(): Promise<Context> {
return ctx
}
const TEST_COMPACTION_ID = CompactionId('test-compaction')
const NEXT_COMPACTION_ID = CompactionId('next-test-compaction')
const TEST_COMMAND_ID = CommandId('test-command')
const NEXT_COMMAND_ID = CommandId('next-test-command')
const summary = (overrides: Record<string, unknown> = {}) => ({
compactionId: TEST_COMPACTION_ID,
summary: [{ type: 'text' as const, text: 'short' }],
shadowedRange: { start: 2, end: 4 },
shadowedSeqs: [2, 3, 4],
@@ -31,33 +40,33 @@ describe('compaction invariants', () => {
const ctx = await setup()
const success = ctx.sessions.create()
startTurn(success)
success.append('compact/start', { turn: 1 })
success.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
success.append('compact/summary', summary())
success.append('compact/end', { turn: 1 })
success.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 1 })
const failed = ctx.sessions.create()
startTurn(failed, 2)
failed.append('compact/start', { turn: 2 })
failed.append('compact/end', { turn: 2, error: 'provider failed' })
failed.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 2 })
failed.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 2, error: 'provider failed' })
})
it('accepts standalone successful and failed compaction lifecycles between turns', async () => {
const ctx = await setup()
const success = ctx.sessions.create()
success.append('compact/start', { turn: null })
success.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
success.append('compact/summary', summary())
success.append('compact/end', { turn: null })
success.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: null })
const failed = ctx.sessions.create()
failed.append('compact/start', { turn: null })
failed.append('compact/end', { turn: null, error: 'provider failed' })
failed.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
failed.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: null, error: 'provider failed' })
})
it('clears an inherited open compaction trace at end-seed during replay', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
const source = Session.create(SessionId('stale-compaction-source'))
source.append('compact/start', { turn: null })
source.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
const replayed = ctx.sessions.create(SessionId('stale-compaction-replay'), {
seed: source.events,
})
@@ -68,8 +77,8 @@ describe('compaction invariants', () => {
await ctx.plugin(CompactInvariant)
expect(() => {
replayed.append('compact/start', { turn: null })
replayed.append('compact/end', { turn: null, error: 'new attempt failed' })
replayed.append('compact/start', { compactionId: NEXT_COMPACTION_ID, turn: null })
replayed.append('compact/end', { compactionId: NEXT_COMPACTION_ID, turn: null, error: 'new attempt failed' })
}).not.toThrow()
})
@@ -78,7 +87,7 @@ describe('compaction invariants', () => {
await ctx.plugin(SessionStore)
const source = Session.create(SessionId('stale-numbered-compaction-source'))
startTurn(source)
source.append('compact/start', { turn: 1 })
source.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
const replayed = ctx.sessions.create(SessionId('stale-numbered-compaction-replay'), {
seed: source.events,
})
@@ -98,7 +107,7 @@ describe('compaction invariants', () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
const source = Session.create(SessionId('stale-repaired-compaction-source'))
source.append('compact/start', { turn: null })
source.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
startTurn(source)
source.append('turn/end', { turn: 1, reason: { kind: 'interrupted' } })
const replayed = ctx.sessions.create(SessionId('stale-repaired-compaction-replay'), {
@@ -124,10 +133,10 @@ describe('compaction invariants', () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
const source = Session.create(SessionId('closed-nested-compaction-source'))
source.append('compact/start', { turn: null })
source.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
startTurn(source)
source.append('turn/end', { turn: 1, reason: { kind: 'interrupted' } })
source.append('compact/end', { turn: null, error: 'failed after crossing turn' })
source.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: null, error: 'failed after crossing turn' })
const replayed = ctx.sessions.create(SessionId('closed-nested-compaction-replay'), {
seed: source.events,
})
@@ -143,10 +152,14 @@ describe('compaction invariants', () => {
await ctx.plugin(SessionStore)
const session = ctx.sessions.create()
session.append('turn/start', { turn: 1 })
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
await ctx.plugin(InvariantService)
await ctx.plugin(CompactInvariant)
expect(() => session.append('compact/end', { turn: 1, error: 'resume failed' })).not.toThrow()
expect(() => session.append('compact/end', {
compactionId: TEST_COMPACTION_ID,
turn: 1,
error: 'resume failed',
})).not.toThrow()
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
})
@@ -162,7 +175,8 @@ describe('compaction invariants', () => {
type: 'step/start', seq: 1, time: 1, data: { turn: 1, step: 1 },
})
ctx.emit('session/event', session, {
type: 'compact/start', seq: 2, time: 2, data: { turn: 1 },
type: 'compact/start', seq: 2, time: 2,
data: { compactionId: TEST_COMPACTION_ID, turn: 1 },
})
}).not.toThrow()
})
@@ -170,28 +184,30 @@ describe('compaction invariants', () => {
it('rejects compaction outside or for a different open turn', async () => {
const ctx = await setup()
const session = ctx.sessions.create()
expect(() => session.append('compact/start', { turn: 1 })).toThrow(/outside any open turn/)
expect(() => session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 }))
.toThrow(/outside any open turn/)
startTurn(session)
expect(() => session.append('compact/start', { turn: 2 })).toThrow(/but open turn is 1/)
expect(() => session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 2 }))
.toThrow(/but open turn is 1/)
})
it('rejects a standalone bracket while a turn is open and a numbered bracket between turns', async () => {
const ctx = await setup()
const open = ctx.sessions.create()
startTurn(open)
expect(() => open.append('compact/start', { turn: null }))
expect(() => open.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null }))
.toThrow(/standalone but turn 1 is open/)
const idle = ctx.sessions.create()
expect(() => idle.append('compact/start', { turn: 1 }))
expect(() => idle.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 }))
.toThrow(/outside any open turn/)
})
it('attributes a nested standalone start to the standalone owner', async () => {
const ctx = await setup()
const session = ctx.sessions.create()
session.append('compact/start', { turn: null })
expect(() => session.append('compact/start', { turn: null }))
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
expect(() => session.append('compact/start', { compactionId: NEXT_COMPACTION_ID, turn: null }))
.toThrow(/standalone compaction is still compacting/)
})
@@ -201,7 +217,7 @@ describe('compaction invariants', () => {
const session = ctx.sessions.create()
startTurn(session)
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
await ctx.plugin(InvariantService)
await expect(ctx.plugin(CompactInvariant).then(() => undefined)).rejects.toThrow(/outside any open turn/)
})
@@ -209,10 +225,10 @@ describe('compaction invariants', () => {
it('rejects turn boundaries that cross live standalone or numbered compaction brackets', async () => {
const ctx = await setup()
const standalone = ctx.sessions.create()
standalone.append('compact/start', { turn: null })
standalone.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: null })
expect(() => { startTurn(standalone) })
.toThrow(/turn\/start cannot cross an open standalone compaction/)
standalone.append('compact/end', { turn: null, error: 'cancelled' })
standalone.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: null, error: 'cancelled' })
expect(() => {
startTurn(standalone)
standalone.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
@@ -220,53 +236,151 @@ describe('compaction invariants', () => {
const numbered = ctx.sessions.create()
startTurn(numbered)
numbered.append('compact/start', { turn: 1 })
numbered.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
expect(() => numbered.append(
'turn/end',
{ turn: 1, reason: { kind: 'completed' } },
)).toThrow(/turn\/end cannot cross an open compaction for turn 1/)
numbered.append('compact/end', { turn: 1, error: 'cancelled' })
numbered.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 1, error: 'cancelled' })
expect(() => numbered.append(
'turn/end',
{ turn: 1, reason: { kind: 'completed' } },
)).not.toThrow()
})
it('rejects a replacement checkpoint for another compaction transaction', async () => {
const ctx = await setup()
const session = ctx.sessions.create()
const original = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'original' }],
source: { kind: 'user' },
}), { surfaceOp: 'append' })
startTurn(session)
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary())
expect(() => session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'checkpoint' }],
source: compactCheckpointSource(NEXT_COMPACTION_ID),
}), {
surfaceOp: { op: 'replace', start: original.seq, end: original.seq },
sourceEventSeqs: [original.seq],
})).toThrow(/compaction checkpoint id .* does not match compact\/start id/)
})
it('requires checkpoint provenance to name an open transaction', async () => {
const ctx = await setup()
const withoutStart = ctx.sessions.create()
const original = withoutStart.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'original' }],
source: { kind: 'user' },
}), { surfaceOp: 'append' })
expect(() => withoutStart.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'checkpoint' }],
source: compactCheckpointSource(TEST_COMPACTION_ID),
}), {
surfaceOp: { op: 'replace', start: original.seq, end: original.seq },
sourceEventSeqs: [original.seq],
})).toThrow(/no matching compact\/start/)
const emptyCommand = ctx.sessions.create()
const replaced = emptyCommand.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'original' }],
source: { kind: 'user' },
}), { surfaceOp: 'append' })
startTurn(emptyCommand)
emptyCommand.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
expect(() => emptyCommand.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'checkpoint' }],
source: compactCheckpointSource(TEST_COMPACTION_ID, CommandId('')),
}), {
surfaceOp: { op: 'replace', start: replaced.seq, end: replaced.seq },
sourceEventSeqs: [replaced.seq],
})).toThrow(/checkpoint sourceCommandId must be a non-empty string/)
})
it.each([
['empty start id', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { compactionId: CompactionId(''), turn: 1 })
}, /compact\/start compactionId must be a non-empty string/],
['empty start source command id', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', {
compactionId: TEST_COMPACTION_ID,
sourceCommandId: CommandId(''),
turn: 1,
})
}, /compact\/start sourceCommandId must be a non-empty string/],
['summary without start', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/summary', summary())
}, /no matching compact\/start/],
['nested start', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/start', { turn: 2 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/start', { compactionId: NEXT_COMPACTION_ID, turn: 2 })
}, /still compacting/],
['repeated summary', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary())
session.append('compact/summary', summary())
}, /repeated within one compaction/],
['summary for another compaction', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary({ compactionId: NEXT_COMPACTION_ID }))
}, /compact\/summary id .* does not match compact\/start id/],
['summary for another source command', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', {
compactionId: TEST_COMPACTION_ID,
sourceCommandId: TEST_COMMAND_ID,
turn: 1,
})
session.append('compact/summary', summary({ sourceCommandId: NEXT_COMMAND_ID }))
}, /compact\/summary sourceCommandId .* does not match compact\/start sourceCommandId/],
['empty shadow set', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary({ shadowedSeqs: [] }))
}, /shadowedSeqs must be non-empty/],
['wrong endpoints', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary({ shadowedRange: { start: 1, end: 4 } }))
}, /shadowedRange must match/],
['invalid token count', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/summary', summary({ shadowedTokenCount: -1 }))
}, /non-negative safe integer/],
['end without start', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/end', { turn: 1, error: 'failed' })
session.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 1, error: 'failed' })
}, /no matching compact\/start/],
['wrong end turn', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/end', { turn: 2, error: 'failed' })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 2, error: 'failed' })
}, /does not match/],
['end for another compaction', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/end', { compactionId: NEXT_COMPACTION_ID, turn: 1, error: 'failed' })
}, /compact\/end id .* does not match compact\/start id/],
['end missing the source command', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', {
compactionId: TEST_COMPACTION_ID,
sourceCommandId: TEST_COMMAND_ID,
turn: 1,
})
session.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 1, error: 'failed' })
}, /compact\/end sourceCommandId .* does not match compact\/start sourceCommandId/],
['empty end source command id', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', {
compactionId: TEST_COMPACTION_ID,
sourceCommandId: TEST_COMMAND_ID,
turn: 1,
})
session.append('compact/end', {
compactionId: TEST_COMPACTION_ID,
sourceCommandId: CommandId(''),
turn: 1,
error: 'failed',
})
}, /compact\/end sourceCommandId must be a non-empty string/],
['success without summary', (session: ReturnType<Context['sessions']['create']>) => {
session.append('compact/start', { turn: 1 })
session.append('compact/end', { turn: 1 })
session.append('compact/start', { compactionId: TEST_COMPACTION_ID, turn: 1 })
session.append('compact/end', { compactionId: TEST_COMPACTION_ID, turn: 1 })
}, /requires one compact\/summary/],
])('rejects %s', async (_name, action, message) => {
const ctx = await setup()