mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
Compose the shared release branch PTC rename with the structural system-head migration in the existing V2-to-V3 stage. Audit source payloads and remap local coordinates before renaming dispatch vocabulary and owned plugin attribution; retain original message identities and generation-qualified references. Native V3 validation combines the same system and PTC lifecycle semantics through a private released-validation view. Reuse the V3 row-admission owner in current JSONL scanning before recoverable-tail suppression, so malformed rows cannot hide retired required PTC tags or header.system. The new corruption-prefix regressions and all173 existing JSONL scanner tests pass. Combined migration/catalog/persistence suite:300 tests; executable V3 migration source has100% coverage. No released generation, prior migration implementation, shared release branch, benchmark or CI workflow is modified by this integration.
259 lines
13 KiB
TypeScript
259 lines
13 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import type { SessionFormatEvent } from '@deepseek-ai/dsh-session-format'
|
|
import { sessionFormatCatalog } from '../src/index.ts'
|
|
|
|
function deepFreeze<T>(value: T): T {
|
|
if (value !== null && typeof value === 'object') {
|
|
for (const child of Object.values(value)) deepFreeze(child)
|
|
Object.freeze(value)
|
|
}
|
|
return value
|
|
}
|
|
|
|
describe('first-party Session format catalog', () => {
|
|
it('statically owns the complete adjacent v0 to v3 chain', () => {
|
|
const header = {
|
|
type: 'session',
|
|
version: 0,
|
|
id: 'catalog',
|
|
createdAt: 1,
|
|
seedLength: 0,
|
|
delegationDepth: 0,
|
|
}
|
|
|
|
expect(sessionFormatCatalog.currentVersion).toBe(3)
|
|
expect(sessionFormatCatalog.readHeader(header)).toEqual({
|
|
status: 'migration-required',
|
|
storedVersion: 0,
|
|
targetVersion: 3,
|
|
header: {
|
|
version: 3,
|
|
id: 'catalog',
|
|
createdAt: 1,
|
|
isSeeded: true,
|
|
delegationDepth: 0,
|
|
},
|
|
})
|
|
|
|
const v1Header = { ...header, version: 1 }
|
|
const restore = sessionFormatCatalog.createRestore(v1Header, {
|
|
recovery: 'strict', validation: 'current',
|
|
})
|
|
restore.decodeRow({ type: 'turn/start', seq: 0, time: 2, data: { turn: 1 } })
|
|
expect(restore.finish()).toMatchObject({
|
|
header: { version: 3, id: 'catalog' },
|
|
})
|
|
})
|
|
|
|
it('restores the installed current vocabulary without freezing ordinary payload additions', () => {
|
|
const header = {
|
|
type: 'session', version: 3, id: 'current-growth', createdAt: 1, isSeeded: false, delegationDepth: 0,
|
|
}
|
|
const restore = (rows: readonly unknown[]) => {
|
|
const current = sessionFormatCatalog.createRestore(header, {
|
|
recovery: 'strict', validation: 'current',
|
|
})
|
|
for (const row of rows) current.decodeRow(row)
|
|
return current.finish()
|
|
}
|
|
const extended = restore([{
|
|
type: 'turn/start', seq: 0, time: 1, data: { turn: 1, postReleaseMember: true },
|
|
}])
|
|
expect(extended.events).toEqual([{
|
|
type: 'turn/start', seq: 0, time: 1, data: { turn: 1, postReleaseMember: true },
|
|
}])
|
|
|
|
expect(() => restore([{
|
|
type: 'ordinary/not-installed', seq: 0, time: 1, data: 'future',
|
|
}])).toThrow(/unknown event type/)
|
|
|
|
const extension = restore([{
|
|
type: 'ordinary/external', seq: 0, time: 1, data: null, ignorable: true,
|
|
}])
|
|
expect(extension.events).toEqual([{
|
|
type: 'ordinary/external', seq: 0, time: 1, data: null, ignorable: true,
|
|
}])
|
|
})
|
|
|
|
it.each([0, 1])('restores v%i empty and non-empty inherited prefixes through every adjacent edge', (version) => {
|
|
for (const seedLength of [0, 1]) {
|
|
const sourceHeader = {
|
|
type: 'session', version, id: 'seed-chain', createdAt: 1,
|
|
parentSession: 'parent', seedLength, delegationDepth: 0,
|
|
}
|
|
const restore = sessionFormatCatalog.createRestore(sourceHeader, { recovery: 'strict', validation: 'current' })
|
|
if (seedLength > 0) {
|
|
restore.decodeRow({ type: 'feedback/record', seq: 0, time: 1, data: { text: 'inherited' } })
|
|
}
|
|
const artifact = restore.finish()
|
|
expect(artifact.header.version).toBe(3)
|
|
expect(artifact.inheritedEventCount).toBe(seedLength)
|
|
expect(artifact.events.at(-1)).toEqual({
|
|
type: 'session/end-seed', seq: seedLength, time: 1, data: { inherited: true },
|
|
})
|
|
expect(sourceHeader.version).toBe(version)
|
|
}
|
|
})
|
|
|
|
it.each([false, true])('inserts the v3 system head and remaps the inherited cut (seeded=%s)', (isSeeded) => {
|
|
const header = { type: 'session', version: 2, id: 'v2-identity', createdAt: 1, isSeeded, delegationDepth: 0 }
|
|
const rows = [
|
|
{ type: 'turn/start', seq: 0, time: 1, data: { turn: 1 } },
|
|
{ type: 'step/start', seq: 1, time: 2, data: { turn: 1, step: 1 } },
|
|
{ type: 'feedback/record', seq: 2, time: 3, data: { text: 'unchanged' } },
|
|
...(isSeeded ? [{ type: 'session/end-seed', seq: 3, time: 4, data: { inherited: true } }] : []),
|
|
]
|
|
const before = JSON.stringify({ header, rows })
|
|
const restore = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation: 'current' })
|
|
for (const row of rows) restore.decodeRow(row)
|
|
expect(restore.finish()).toEqual({
|
|
header: { version: 3, id: 'v2-identity', createdAt: 1, isSeeded, delegationDepth: 0 },
|
|
inheritedEventCount: isSeeded ? 4 : 0,
|
|
events: [
|
|
{ type: 'turn/start', seq: 0, time: 1, data: { turn: 1 } },
|
|
{ type: 'step/start', seq: 1, time: 2, data: { turn: 1, step: 1 } },
|
|
{
|
|
type: 'system/message', seq: 2, time: 2, surfaceOp: 'append',
|
|
data: {
|
|
turn: 1, step: 1,
|
|
message: {
|
|
id: 'v2-to-v3-system-9673c4ed630de6c21ea6bd6b573094ea8e5e216843a1b572a68657499ad9667b',
|
|
role: 'system', source: { kind: 'plugin', plugin: '@deepseek-ai/dsh-system-prompt' }, content: [],
|
|
},
|
|
},
|
|
},
|
|
{ type: 'feedback/record', seq: 3, time: 3, data: { text: 'unchanged' } },
|
|
...(isSeeded ? [{ type: 'session/end-seed', seq: 4, time: 4, data: { inherited: true } }] : []),
|
|
],
|
|
})
|
|
expect(JSON.stringify({ header, rows })).toBe(before)
|
|
})
|
|
|
|
it.each(['current', 'transformed'] as const)('refuses unclassified ignorable v2 events (%s)', (validation) => {
|
|
const header = { type: 'session', version: 2, id: 'v2-unknown', createdAt: 1, isSeeded: false, delegationDepth: 0 }
|
|
const row = { type: 'external/event', seq: 0, time: 1, data: { extra: ['unchanged'] }, ignorable: true }
|
|
const before = JSON.stringify({ header, row })
|
|
const restore = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
expect(() => { restore.decodeRow(row) }).toThrow(/cannot safely transform unclassified event external\/event/)
|
|
expect(JSON.stringify({ header, row })).toBe(before)
|
|
})
|
|
|
|
it.each([0, 1, 2])('migrates frozen v%i PTC records and reopens the actual current representation without rewriting IDs', (version) => {
|
|
const sourceHeader = deepFreeze({
|
|
type: 'session', version, id: 'tools-code-mode:session', createdAt: 1, delegationDepth: 0,
|
|
...(version === 2 ? { isSeeded: false } : {}),
|
|
})
|
|
const message = (id: string) => ({
|
|
id, role: 'user', source: { kind: 'plugin', plugin: 'tools-code-mode' },
|
|
content: [{ type: 'text', text: 'tool/code-dispatch-start tools-code-mode tools-ptc' }],
|
|
})
|
|
const oldId = 'tools-code-mode:message'
|
|
const newId = 'tools-ptc:message'
|
|
const rootId = 'tools-code-mode:root'
|
|
const childId = 'tools-code-mode:child'
|
|
const dispatch = {
|
|
rootCallId: rootId, parentCallId: rootId, subCallId: childId, name: 'read',
|
|
arguments: { text: 'tools-code-mode', nested: { type: 'tool/code-dispatch' } },
|
|
}
|
|
const rows: SessionFormatEvent[] = deepFreeze([
|
|
{ type: 'turn/start', seq: 0, time: 10, data: { turn: 1 } },
|
|
{ type: 'step/start', seq: 1, time: 10, data: { turn: 1, step: 1 } },
|
|
{ type: 'user/message', seq: 2, time: 11, data: message(oldId), surfaceOp: 'append' },
|
|
{ type: 'user/message', seq: 3, time: 12, data: message(newId), surfaceOp: 'append' },
|
|
{ type: 'agent/inbox/spliced', seq: 4, time: 13, data: { target: 'next-turn', start: 0, inserted: [message(oldId), message(newId)] } },
|
|
{ type: 'tool/code-dispatch-start', seq: 5, time: 14, data: dispatch },
|
|
{ type: 'tool/code-dispatch', seq: 6, time: 15, data: { ...dispatch, isError: false, content: [{ type: 'text', text: childId }] } },
|
|
{ type: 'user/message', seq: 7, time: 16, data: message('tools-code-mode:replacement'), sourceEventSeqs: [2, 3], surfaceOp: { op: 'replace', start: 2, end: 3 } },
|
|
{ type: 'step/end', seq: 8, time: 17, data: { turn: 1, step: 1 } },
|
|
{ type: 'turn/end', seq: 9, time: 17, data: { turn: 1, reason: { kind: 'completed' } } },
|
|
])
|
|
const before = JSON.stringify({ sourceHeader, rows })
|
|
const restore = sessionFormatCatalog.createRestore(sourceHeader, { recovery: 'strict', validation: 'current' })
|
|
for (const row of rows) restore.decodeRow(row)
|
|
const artifact = restore.finish()
|
|
const renamedMessage = (id: string) => ({ ...message(id), source: { kind: 'plugin', plugin: 'tools-ptc' } })
|
|
const expected = [
|
|
rows[0], rows[1],
|
|
expect.objectContaining({ type: 'system/message', seq: 2 }),
|
|
{ ...rows[2], seq: 3, data: renamedMessage(oldId) },
|
|
{ ...rows[3], seq: 4, data: renamedMessage(newId) },
|
|
{ ...rows[4], seq: 5, data: { target: 'next-turn', start: 0, inserted: [renamedMessage(oldId), renamedMessage(newId)] } },
|
|
{ ...rows[5], seq: 6, type: 'tool/ptc-dispatch-start' },
|
|
{ ...rows[6], seq: 7, type: 'tool/ptc-dispatch' },
|
|
{ ...rows[7], seq: 8, sourceEventSeqs: [3, 4], surfaceOp: { op: 'replace', start: 3, end: 4 }, data: renamedMessage('tools-code-mode:replacement') },
|
|
{ ...rows[8], seq: 9 }, { ...rows[9], seq: 10 },
|
|
]
|
|
expect(artifact).toEqual({
|
|
header: { version: 3, id: sourceHeader.id, createdAt: 1, isSeeded: false, delegationDepth: 0 },
|
|
inheritedEventCount: 0, events: expected,
|
|
})
|
|
const currentHeader = deepFreeze(sessionFormatCatalog.encodeCurrentHeader(artifact.header, artifact.inheritedEventCount))
|
|
const currentRows = deepFreeze(artifact.events.map(event => sessionFormatCatalog.encodeCurrentEvent(event)))
|
|
const encodedBefore = JSON.stringify({ currentHeader, currentRows })
|
|
const reopened = sessionFormatCatalog.createRestore(currentHeader, { recovery: 'strict', validation: 'current' })
|
|
for (const row of currentRows) reopened.decodeRow(row)
|
|
expect(reopened.finish()).toEqual(artifact)
|
|
expect(JSON.stringify({ sourceHeader, rows })).toBe(before)
|
|
expect(JSON.stringify({ currentHeader, currentRows })).toBe(encodedBefore)
|
|
})
|
|
|
|
it.each(['current', 'transformed'] as const)('rejects native v3 obsolete required tags with %s validation and retains ignorable tags', (validation) => {
|
|
const header = deepFreeze({ type: 'session', version: 3, id: 'native-ptc', createdAt: 1, isSeeded: false, delegationDepth: 0 })
|
|
for (const type of ['tool/code-dispatch-start', 'tool/code-dispatch']) {
|
|
const required = deepFreeze({ type, seq: 0, time: 1, data: null })
|
|
const rejected = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
expect(() => { rejected.decodeRow(required) }).toThrow(/unknown event type/)
|
|
const ignorable = deepFreeze({ ...required, ignorable: true, data: { text: 'tools-code-mode', source: { kind: 'plugin', plugin: 'tools-code-mode' } } })
|
|
const accepted = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
accepted.decodeRow(ignorable)
|
|
expect(accepted.finish().events).toEqual([ignorable])
|
|
}
|
|
})
|
|
|
|
it.each(['current', 'transformed'] as const)('refuses V3 dispatch tag collisions in V2 input with %s validation', (validation) => {
|
|
const header = deepFreeze({ type: 'session', version: 2, id: 'v2-collision', createdAt: 1, isSeeded: false, delegationDepth: 0 })
|
|
for (const type of ['tool/ptc-dispatch-start', 'tool/ptc-dispatch']) {
|
|
for (const ignorable of [false, true]) {
|
|
const restore = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
const row = deepFreeze({ type, seq: 0, time: 1, data: null, ...(ignorable ? { ignorable: true } : {}) })
|
|
expect(() => { restore.decodeRow(row) }).toThrow(/format v2.*ptc-dispatch/)
|
|
}
|
|
}
|
|
})
|
|
|
|
it.each(['current', 'transformed'] as const)('renames V2-owned ignorable dispatch tags with %s validation', (validation) => {
|
|
const header = deepFreeze({ type: 'session', version: 2, id: 'v2-owned', createdAt: 1, isSeeded: false, delegationDepth: 0 })
|
|
const data = { rootCallId: 'root', parentCallId: 'root', subCallId: 'child', name: 'read', arguments: {} }
|
|
const rows = deepFreeze([
|
|
{ type: 'turn/start', seq: 0, time: 1, data: { turn: 1 } },
|
|
{ type: 'tool/code-dispatch-start', seq: 1, time: 2, data, ignorable: true },
|
|
{ type: 'tool/code-dispatch', seq: 2, time: 3, data: { ...data, isError: false, content: [] }, ignorable: true },
|
|
])
|
|
const restore = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
for (const row of rows) restore.decodeRow(row)
|
|
expect(restore.finish().events).toEqual([
|
|
rows[0], { ...rows[1], type: 'tool/ptc-dispatch-start' }, { ...rows[2], type: 'tool/ptc-dispatch' },
|
|
])
|
|
})
|
|
|
|
it.each(['current', 'transformed'] as const)('refuses a v3 delivery marker in v2 input (%s)', (validation) => {
|
|
const header = { type: 'session', version: 2, id: 'future-delivery', createdAt: 1, isSeeded: false, delegationDepth: 0 }
|
|
const restore = sessionFormatCatalog.createRestore(header, { recovery: 'strict', validation })
|
|
restore.decodeRow({ type: 'feedback/record', seq: 0, time: 1, data: { text: 'unaccepted' } })
|
|
expect(() => {
|
|
restore.decodeRow({ type: 'session-log-deepseek/delivery-accepted', seq: 1, time: 2,
|
|
data: { sessionId: header.id, throughSeq: 0, sessionFormatVersion: 3 } })
|
|
restore.finish()
|
|
}).toThrow(/format v2 delivery marker claims target format v3/)
|
|
})
|
|
|
|
it('validates complete relationships after streaming migration', () => {
|
|
const stream = sessionFormatCatalog.createRestore({
|
|
type: 'session', version: 1, id: 'invalid-stream', createdAt: 1, delegationDepth: 0,
|
|
}, { recovery: 'strict', validation: 'current' })
|
|
stream.decodeRow({ type: 'step/start', seq: 0, time: 2, data: { turn: 1, step: 1 } })
|
|
|
|
expect(() => stream.finish()).toThrow(/open turn/)
|
|
})
|
|
})
|