1
/** V4 framing with native tool-role admission and released physical rows. */3
import { SessionFormatError, isSessionFormatJsonObject } from '@deepseek-ai/dsh-session-format'4
import type { SessionFormatCodec, SessionFormatCurrentEncoder, SessionFormatHeader, SessionFormatEvent } from '@deepseek-ai/dsh-session-format'5
import { releasedV2SessionFormatCodec } from '@deepseek-ai/dsh-session-format-v2-to-v3'6
import { assertV4SourceRowAdmission } from './message-sources.ts'7
import { assertV4RetiredSyntax } from './retired-syntax.ts'8
import { assertV4SystemMessageFields } from './system-message.ts'9
import { assertV4DeveloperData } from './developer.ts'10
import { assertV4ForkResult } from './fork-result.ts'11
import { assertV4ToolResultMessage } from './tool-role.ts'12
import { assertReleasedV4Header } from './validation.ts'14
function physicalV2(value: unknown): SessionFormatHeader {15
if (!isSessionFormatJsonObject(value) || value['version'] !== 4) throw new SessionFormatError('expected format v4 physical header')16
return { ...value, version: 2 } as SessionFormatHeader17
}19
/**20
* V4 physical encoder and decoder retain the released row framing while21
* validating the native tool-role message directly.22
*/23
export const releasedV4SessionFormatCodec = Object.freeze({24
version: 4,25
decodeHeader(value: unknown) {26
return { ...releasedV2SessionFormatCodec.decodeHeader(physicalV2(value)), version: 4 }27
},28
createDecoder(value, recovery) {29
const decoder = releasedV2SessionFormatCodec.createDecoder(physicalV2(value), recovery)30
return {31
...decoder,32
header: { ...decoder.header, version: 4 },33
decodeRow(row, context) {34
assertV4RowAdmission(row)35
decoder.decodeRow(row, {36
emitRun: context.emitRun.bind(context),37
emitEvent: context.emitEvent.bind(context),38
})39
},40
}41
},42
encodeHeader(header, inheritedEventCount) {43
assertReleasedV4Header(header)44
return { ...releasedV2SessionFormatCodec.encodeHeader({ ...header, version: 2 }, inheritedEventCount), version: 4 }45
},46
encodeEvent(event: SessionFormatEvent) {47
if (event.type === 'developer/message' && event['ignorable'] === true) {48
assertV4DeveloperData(event)49
assertV4RetiredSyntax(event)50
}51
assertV4RowAdmission(event)52
return releasedV2SessionFormatCodec.encodeEvent(event)53
},54
} satisfies SessionFormatCodec & SessionFormatCurrentEncoder)56
/**57
* Apply native V4 admission before a scanner discards a recoverable suffix.58
* Ignorable developer payloads require reader vocabulary; physical decoding defers them.59
* @param row - parsed physical row before framing and source-event range decoding.60
* @param knownEventTypes - installed event types, supplied by native readers before tail recovery.61
*/62
export function assertV4RowAdmission(row: unknown, knownEventTypes?: ReadonlySet<string>): void {63
if (isSessionFormatJsonObject(row)) {64
if (row['type'] === 'developer/message' && row['ignorable'] === true65
&& knownEventTypes?.has('developer/message') !== true) return66
assertV4DeveloperData(row as unknown as SessionFormatEvent)67
}68
assertV4SourceRowAdmission(row)69
assertV4RetiredSyntax(row)70
assertV4SystemMessageFields(row)71
if (!isSessionFormatJsonObject(row) || row['type'] !== 'tool/result') return72
const event = row as unknown as SessionFormatEvent73
assertV4ToolResultMessage(event)74
assertV4ForkResult(event)75
}