test: update conversation assembly coverage and docs
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user