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

@@ -4,6 +4,7 @@ import Loader from '@cordisjs/plugin-loader'
import type { Agent } from '@deepseek-ai/dsh-agent'
import CommandService, { type CommandResult } from '@deepseek-ai/dsh-commands'
import {
CompactionId,
CompactService,
ManualCompactionError,
type CompactAgentContext,
@@ -14,7 +15,10 @@ import {
import { Session, SessionId } from '@deepseek-ai/dsh-session'
import * as commandCompact from '@deepseek-ai/dsh-command-compact'
const COMPACTION_ID = CompactionId('command-compact-test')
const RESULT: CompactionResult = {
compactionId: COMPACTION_ID,
startSeq: 1,
summarySeq: 2,
endSeq: 3,
@@ -45,18 +49,28 @@ class StubCompactService extends CompactService {
override compactNow(
agent: ManualCompactAgentContext,
signal: AbortSignal,
sourceCommandId?: Parameters<CompactService['compactNow']>[2],
): Promise<CompactionResult | null> {
this.calls.push({ agent, signal })
if (this.operation !== undefined) return this.operation()
return this.failure === undefined
? Promise.resolve(this.result === null ? null : this.appendResult(agent, this.result))
? Promise.resolve(this.result === null ? null : this.appendResult(agent, this.result, sourceCommandId))
// oxlint-disable-next-line typescript/prefer-promise-reject-errors -- exercise arbitrary backend rejection values.
: Promise.reject(this.failure)
}
private appendResult(agent: ManualCompactAgentContext, result: CompactionResult): CompactionResult {
agent.session.append('compact/start', { turn: null })
private appendResult(
agent: ManualCompactAgentContext,
result: CompactionResult,
sourceCommandId: Parameters<CompactService['compactNow']>[2],
): CompactionResult {
const provenance = {
compactionId: result.compactionId,
...sourceCommandId === undefined ? {} : { sourceCommandId },
}
agent.session.append('compact/start', { ...provenance, turn: null })
agent.session.append('compact/summary', {
...provenance,
summary: result.summary,
shadowedRange: result.shadowedRange,
shadowedSeqs: result.shadowedSeqs,
@@ -64,8 +78,8 @@ class StubCompactService extends CompactService {
provider: 'command-test',
model: 'command-test',
})
agent.session.append('compact/end', { turn: null })
return result
agent.session.append('compact/end', { ...provenance, turn: null })
return { ...result, ...provenance }
}
}

View File

@@ -9,6 +9,7 @@ import Include from '@cordisjs/plugin-include'
import type { Agent } from '@deepseek-ai/dsh-agent'
import CommandService from '@deepseek-ai/dsh-commands'
import {
CompactionId,
CompactService,
type CompactAgentContext,
type CompactionResult,
@@ -18,7 +19,10 @@ import {
import * as commandCompact from '@deepseek-ai/dsh-command-compact'
import { Session, SessionId } from '@deepseek-ai/dsh-session'
const COMPACTION_ID = CompactionId('loader-command-compact-test')
const RESULT: CompactionResult = {
compactionId: COMPACTION_ID,
startSeq: 1,
summarySeq: 2,
endSeq: 3,
@@ -44,9 +48,15 @@ class LoaderCompactService extends CompactService {
override compactNow(
agent: ManualCompactAgentContext,
_signal: AbortSignal,
sourceCommandId?: Parameters<CompactService['compactNow']>[2],
): Promise<CompactionResult | null> {
agent.session.append('compact/start', { turn: null })
const provenance = {
compactionId: RESULT.compactionId,
...sourceCommandId === undefined ? {} : { sourceCommandId },
}
agent.session.append('compact/start', { ...provenance, turn: null })
agent.session.append('compact/summary', {
...provenance,
summary: RESULT.summary,
shadowedRange: RESULT.shadowedRange,
shadowedSeqs: RESULT.shadowedSeqs,
@@ -54,8 +64,8 @@ class LoaderCompactService extends CompactService {
provider: 'loader-test',
model: 'loader-test',
})
agent.session.append('compact/end', { turn: null })
return Promise.resolve(RESULT)
agent.session.append('compact/end', { ...provenance, turn: null })
return Promise.resolve({ ...RESULT, ...provenance })
}
}
@@ -132,11 +142,17 @@ describe('command-compact real Loader composition', () => {
},
{
type: 'compact/start',
data: { turn: null },
data: {
compactionId: COMPACTION_ID,
sourceCommandId: execution.commandId,
turn: null,
},
},
{
type: 'compact/summary',
data: {
compactionId: COMPACTION_ID,
sourceCommandId: execution.commandId,
summary: RESULT.summary,
shadowedRange: RESULT.shadowedRange,
shadowedSeqs: RESULT.shadowedSeqs,
@@ -147,7 +163,11 @@ describe('command-compact real Loader composition', () => {
},
{
type: 'compact/end',
data: { turn: null },
data: {
compactionId: COMPACTION_ID,
sourceCommandId: execution.commandId,
turn: null,
},
},
{
type: 'command/done',

View File

@@ -4,7 +4,7 @@ import BasicCompactService from '@deepseek-ai/dsh-compact-basic'
import type { BasicCompactConfig } from '@deepseek-ai/dsh-compact-basic'
import { selectCompactableRange } from '@deepseek-ai/dsh-compact-basic/src/region.ts'
import type { SummarizationInput, SummaryResult } from '@deepseek-ai/dsh-compact-basic/src/summarizer.ts'
import { toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compact'
import { CompactionId, toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compact'
import {
resolveCompactSpec,
resolveConfig,
@@ -945,7 +945,10 @@ describe('compaction region transaction', () => {
)).rejects.toThrow(/no open turn/)
const locked = conversation(1)
locked.append('compact/start', { turn: 2 })
locked.append('compact/start', {
compactionId: CompactionId('locked-compaction'),
turn: 2,
})
const lockedNodes = locked.surface.nodes
await expect(compact.compactRegion(
lockedNodes[0]!,
@@ -1623,6 +1626,7 @@ describe('automatic listener and loader composition', () => {
const compact = new TestCompactService(ctx)
const session = conversation(2)
const fakeResult: CompactionResult = {
compactionId: CompactionId('fake-compaction'),
startSeq: 1,
summarySeq: 2,
endSeq: 3,

View File

@@ -3,13 +3,14 @@ import { Context } from 'cordis'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import InvariantService from '@deepseek-ai/dsh-invariants'
import { CommandId } from '@deepseek-ai/dsh-commands/brand'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
import * as AgentInvariant from '@deepseek-ai/dsh-agent/invariant'
import * as AgentLoopInvariant from '@deepseek-ai/dsh-agent-loop/invariant'
import * as CompactInvariant from '@deepseek-ai/dsh-compact/invariant'
import * as CompactBasicInvariant from '@deepseek-ai/dsh-compact-basic/invariant'
import { BasicCompactService } from '@deepseek-ai/dsh-compact-basic'
import { isCompactCheckpointSource, ManualCompactionError } from '@deepseek-ai/dsh-compact'
import { CompactionId, isCompactCheckpointSource, ManualCompactionError } from '@deepseek-ai/dsh-compact'
import type { CompactionResult } from '@deepseek-ai/dsh-compact'
import {
createAssistantMessage,
@@ -23,7 +24,7 @@ import type {
StreamChunk,
TokenUsage,
} from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import SessionStore, { Session, SessionId, type SessionEvent } from '@deepseek-ai/dsh-session'
import LlmService from '@deepseek-ai/dsh-llm'
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
import type { Agent } from '@deepseek-ai/dsh-agent'
@@ -394,22 +395,35 @@ describe('compactNow transaction and failure classification', () => {
const { compact, flushes } = detachedService()
const session = closedConversation(2, 7)
const agent = fakeAgent(session, () => () => undefined)
const commandId = CommandId('manual-compact-command')
const result = await compact.compactNow(agent, SIGNAL)
const result = await compact.compactNow(agent, SIGNAL, commandId)
expect(result).not.toBeNull()
expect(result?.sourceCommandId).toBe(commandId)
expect(flushes()).toBe(1)
expect(session.events.filter(event => event.type === 'turn/start').at(-1)?.data.turn).toBe(7)
expect(session.events.findLast(event => event.type === 'compact/start')?.data)
.toEqual({ turn: null })
expect(session.events.findLast(event => event.type === 'compact/end')?.data)
.toEqual({ turn: null })
const start = session.events.findLast(event => event.type === 'compact/start')
const summaryEvent = session.events.findLast(event => event.type === 'compact/summary')
const checkpoint = session.events.findLast(
(event): event is SessionEvent<'user/message'> => event.type === 'user/message'
&& isCompactCheckpointSource(event.data.source),
)
const end = session.events.findLast(event => event.type === 'compact/end')
const correlated = { compactionId: result?.compactionId, sourceCommandId: commandId }
expect(start?.data).toEqual({ ...correlated, turn: null })
expect(summaryEvent?.data.sourceCommandId).toBe(commandId)
expect(checkpoint?.data.source).toMatchObject(correlated)
expect(end?.data).toEqual({ ...correlated, turn: null })
})
it('reports a live unmatched bracket as busy without summarizing', async () => {
const { compact } = detachedService()
const session = closedConversation(2)
session.append('compact/start', { turn: null })
session.append('compact/start', {
compactionId: CompactionId('live-manual-compaction'),
turn: null,
})
const agent = fakeAgent(session, () => () => undefined)
const error = await rejection(() => compact.compactNow(agent, SIGNAL))
@@ -421,7 +435,10 @@ describe('compactNow transaction and failure classification', () => {
it('ignores an unmatched bracket inherited before a later end-seed marker', async () => {
const { compact } = detachedService()
const original = closedConversation(2)
original.append('compact/start', { turn: null })
original.append('compact/start', {
compactionId: CompactionId('stale-manual-compaction'),
turn: null,
})
const reloaded = Session.create(SessionId('stale-orphan'), [...original.events])
const boundary = reloaded.events.findLast(event => event.type === 'session/end-seed')
const orphan = reloaded.events.find(event => event.type === 'compact/start')
@@ -435,7 +452,10 @@ describe('compactNow transaction and failure classification', () => {
it('scans a stale orphan independently of later repaired turn state', async () => {
const { compact } = detachedService()
const original = closedConversation(2)
original.append('compact/start', { turn: null })
original.append('compact/start', {
compactionId: CompactionId('reloaded-manual-compaction'),
turn: null,
})
original.append('turn/start', { turn: 3 })
original.append('turn/end', { turn: 3, reason: { kind: 'interrupted' } })
const reloaded = Session.create(SessionId('reloaded-orphan'), [...original.events])
@@ -664,7 +684,7 @@ describe('compactNow transaction and failure classification', () => {
expect(result).not.toBeNull()
expect(session.events.some(event => event.type === 'turn/start')).toBe(false)
expect(session.events.find(event => event.type === 'compact/start')?.data)
.toEqual({ turn: null })
.toEqual({ compactionId: result?.compactionId, turn: null })
})
it('classifies a durability failure after the standalone bracket committed', async () => {
@@ -676,8 +696,9 @@ describe('compactNow transaction and failure classification', () => {
expect((await rejection(compact.compactNow(agent, SIGNAL))).code).toBe('persistence')
vi.restoreAllMocks()
expect(session.events.some(event => event.type === 'compact/summary')).toBe(true)
expect(session.events.findLast(event => event.type === 'compact/end')?.data)
.toEqual({ turn: null })
const start = session.events.findLast(event => event.type === 'compact/start')
const end = session.events.findLast(event => event.type === 'compact/end')
expect(end?.data).toEqual({ compactionId: start?.data.compactionId, turn: null })
})
it('lets a pre-aborted signal win before reservation, measurement, or summarization', async () => {

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()