91ee264e16
Move COMPACT_CHECKPOINT_SOURCE and isCompactCheckpointSource into a cordis-free src/checkpoint.ts leaf, re-exported from the root so every host-side consumer keeps its import. The client can then type-import the leaf without reaching dsh-session's root, whose Context merge declares the host sessions service and collides with the client's -- the dsh-commands/brand shape. Renaming the plugin id now fails the client typecheck. Also: keep recoverable summary text when a compact/summary mixes text with other block types, and capture the seeded-history provenance seqs from the pushes that produce them instead of deriving them by arithmetic.
51 lines
2.3 KiB
TypeScript
51 lines
2.3 KiB
TypeScript
/**
|
|
* Behavioral half of the compaction-checkpoint drift trap.
|
|
*
|
|
* `TranscriptAdapter` pins its plugin literal to the seam's own declaration at
|
|
* compile time through a type-only import of `dsh-compact/checkpoint`, so
|
|
* renaming the seam's plugin already fails `tsc`. This spec covers the same
|
|
* drift from the other side — end to end through the adapter, driving it with a
|
|
* checkpoint built from the canonical `COMPACT_CHECKPOINT_SOURCE` **value** and
|
|
* checking the seam's own predicate agrees. It runs in the client TEST program,
|
|
* which can value-import the package root; a `packages/client/*` package
|
|
* program cannot, because that root reaches `dsh-session`'s root and collides
|
|
* the host `Context.sessions` merge (`TS2717`).
|
|
*/
|
|
|
|
import { COMPACT_CHECKPOINT_SOURCE, isCompactCheckpointSource } from '@deepseek-ai/dsh-compact'
|
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
|
import { describe, expect, it } from 'vitest'
|
|
import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
|
|
import { TranscriptAdapter } from '../src/client/sessions/transcript-adapter.ts'
|
|
|
|
/** A replacement user message stamped with the seam's own canonical source. */
|
|
function canonicalCheckpoint(seq: number): SessionEvent {
|
|
return {
|
|
type: 'user/message',
|
|
seq,
|
|
time: 1_700_000_000_000 + seq,
|
|
surfaceOp: { op: 'replace', start: 0, end: 0 },
|
|
sourceEventSeqs: [0],
|
|
data: createUserMessage({
|
|
content: [{ type: 'text', text: '<context_checkpoint>model only</context_checkpoint>' }],
|
|
source: COMPACT_CHECKPOINT_SOURCE,
|
|
}),
|
|
} as unknown as SessionEvent
|
|
}
|
|
|
|
describe('compaction checkpoint recognition', () => {
|
|
it('recognizes a checkpoint carrying the seam-canonical source', () => {
|
|
const adapter = new TranscriptAdapter()
|
|
adapter.reset([canonicalCheckpoint(1)])
|
|
expect(adapter.nodes()).toEqual([{ kind: 'compaction', seq: 1, time: 1_700_000_000_001, summary: null }])
|
|
})
|
|
|
|
it('agrees with the seam s own predicate on the source it recognizes', () => {
|
|
// Both sides answer the same question about the same value: if the seam
|
|
// renames its plugin, this equality is what breaks.
|
|
const checkpoint = canonicalCheckpoint(1)
|
|
expect(checkpoint.type === 'user/message' && isCompactCheckpointSource(checkpoint.data.source)).toBe(true)
|
|
expect(COMPACT_CHECKPOINT_SOURCE).toEqual({ kind: 'plugin', plugin: 'compact' })
|
|
})
|
|
})
|