@deepseek-ai/dsh-session-format-v0-to-v1 0.1.3-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,2585 @@
1
+ import { SessionFormatError, SessionFormatUnsupportedMigrationError, defineSessionFormatMigration, isSessionFormatJsonObject, sessionFormatCount, sessionFormatSafeInteger, snapshotSessionFormatJson } from "@deepseek-ai/dsh-session-format";
2
+ import { isAbsolute } from "node:path";
3
+ import { deepEqualJson, isJsonValue } from "@deepseek-ai/dsh-util-values";
4
+ //#region lib/types/dispositions.js
5
+ /**
6
+ * Freeze one exact released payload-member disposition for adjacent format validators.
7
+ * @param required - members that must be present.
8
+ * @param optional - additional admitted members.
9
+ * @param opaque - members retained as lossless JSON without nested semantic inspection.
10
+ * @returns the detached frozen disposition.
11
+ */
12
+ function defineReleasedPayloadDisposition(required, optional = [], opaque = []) {
13
+ return Object.freeze({
14
+ required: Object.freeze([...required]),
15
+ optional: Object.freeze([...optional]),
16
+ opaque: Object.freeze([...opaque])
17
+ });
18
+ }
19
+ const disposition = defineReleasedPayloadDisposition;
20
+ /**
21
+ * Frozen released-v0 event and payload-member inventory.
22
+ * Every listed member is preserved by the identity edge. Members in `opaque`
23
+ * remain lossless JSON without nested Session-sequence interpretation. Nested
24
+ * merge-extensible discriminants validate known variants and preserve
25
+ * unknown variants as owner-opaque JSON.
26
+ */
27
+ const RELEASED_V0_EVENT_DISPOSITIONS = Object.freeze({
28
+ "agent-preset/selected": disposition(["agentPreset"]),
29
+ "agent/inbox/spliced": disposition([
30
+ "target",
31
+ "start",
32
+ "inserted"
33
+ ], ["removedCount", "outcome"]),
34
+ "approval/asked": disposition(["id", "toolName"], ["callId", "reason"]),
35
+ "approval/decided": disposition(["id", "outcome"]),
36
+ "approval/policy": disposition(["policy"], ["source"]),
37
+ "assistant/chunk": disposition([
38
+ "turn",
39
+ "step",
40
+ "chunk"
41
+ ]),
42
+ "assistant/message": disposition([
43
+ "turn",
44
+ "step",
45
+ "message"
46
+ ], ["usage", "interrupted"]),
47
+ "command/done": disposition(["commandId", "kind"], ["text", "sourceEventSeq"]),
48
+ "command/run": disposition([
49
+ "commandId",
50
+ "name",
51
+ "source"
52
+ ], ["args"]),
53
+ "compaction/end": disposition(["compactionId", "turn"], ["sourceCommandId", "error"]),
54
+ "compaction/prune": disposition([
55
+ "shadowedRange",
56
+ "shadowedSeqs",
57
+ "shadowedTokenCount"
58
+ ]),
59
+ "compaction/start": disposition(["compactionId", "turn"], ["sourceCommandId"]),
60
+ "compaction/summary": disposition([
61
+ "compactionId",
62
+ "summary",
63
+ "shadowedRange",
64
+ "shadowedSeqs",
65
+ "shadowedTokenCount",
66
+ "provider",
67
+ "model"
68
+ ], [
69
+ "sourceCommandId",
70
+ "maxTokens",
71
+ "usage",
72
+ "rawOutput",
73
+ "llmStreamCall"
74
+ ]),
75
+ "feedback/record": disposition(["text"]),
76
+ "goal/change": disposition([
77
+ "kind",
78
+ "version",
79
+ "operation"
80
+ ], [
81
+ "goal",
82
+ "roundsStarted",
83
+ "createdAt",
84
+ "updatedAt",
85
+ "cleared",
86
+ "clearedAt"
87
+ ]),
88
+ "hook/invoked": disposition([
89
+ "turn",
90
+ "point",
91
+ "dialect",
92
+ "handlerId"
93
+ ], ["matcher"]),
94
+ "hook/result": disposition([
95
+ "turn",
96
+ "point",
97
+ "handlerId",
98
+ "decision",
99
+ "durationMs"
100
+ ], ["exitCode", "stderrSummary"]),
101
+ "llm/retry": disposition([
102
+ "retryId",
103
+ "turn",
104
+ "step",
105
+ "provider",
106
+ "mode",
107
+ "policyKey",
108
+ "retry",
109
+ "delayMs",
110
+ "failure"
111
+ ], ["maxRetries"]),
112
+ "llm/retry-started": disposition([
113
+ "retryId",
114
+ "turn",
115
+ "step",
116
+ "retry"
117
+ ]),
118
+ "model/selection": disposition(["provider", "model"], ["reasoningEffort"]),
119
+ "permission/preset": disposition(["preset"]),
120
+ "plan/mode": disposition(["active"]),
121
+ "request/context": disposition(["provider", "model"], ["contextWindow"]),
122
+ "request/header": disposition(["header", "reason"], ["startsSeries"]),
123
+ "sandbox/mode": disposition(["mode"], ["source"]),
124
+ "schedule/change": disposition(["version", "operation"], [
125
+ "schedule",
126
+ "id",
127
+ "acceptedAt"
128
+ ]),
129
+ "session-log-deepseek/delivery-accepted": disposition(["sessionId", "throughSeq"]),
130
+ "session/end-seed": disposition([]),
131
+ "session/title": disposition([
132
+ "title",
133
+ "messageSeqs",
134
+ "source"
135
+ ]),
136
+ "session/title-llm-request": disposition([
137
+ "titleProvider",
138
+ "messageSeqs",
139
+ "route",
140
+ "system",
141
+ "messages",
142
+ "maxTokens"
143
+ ]),
144
+ "step/end": disposition(["turn", "step"]),
145
+ "step/start": disposition(["turn", "step"]),
146
+ "subagent/descriptor": disposition([
147
+ "mode",
148
+ "version",
149
+ "provider"
150
+ ], [
151
+ "label",
152
+ "agentProvider",
153
+ "agentModel",
154
+ "agentReasoningEffort",
155
+ "persona",
156
+ "toolFilter"
157
+ ]),
158
+ "subagent/model-selection-policy": disposition(["allowedModels"]),
159
+ "team/member": disposition([
160
+ "version",
161
+ "teamId",
162
+ "member"
163
+ ]),
164
+ "team/message/delivered": disposition([
165
+ "version",
166
+ "teamId",
167
+ "messageId",
168
+ "targetId"
169
+ ]),
170
+ "team/message/queued": disposition([
171
+ "version",
172
+ "teamId",
173
+ "message"
174
+ ]),
175
+ "team/task": disposition([
176
+ "version",
177
+ "teamId",
178
+ "task"
179
+ ]),
180
+ "todo/write": disposition(["todos"]),
181
+ "tool-workflow/agent-end": disposition([
182
+ "runId",
183
+ "seq",
184
+ "outcome"
185
+ ]),
186
+ "tool-workflow/agent-start": disposition([
187
+ "runId",
188
+ "seq",
189
+ "label",
190
+ "childId"
191
+ ], ["phase"]),
192
+ "tool-workflow/run-end": disposition(["runId", "stopReason"]),
193
+ "tool-workflow/run-start": disposition(["runId", "name"]),
194
+ "tool/call": disposition([
195
+ "turn",
196
+ "step",
197
+ "callId",
198
+ "name",
199
+ "arguments"
200
+ ]),
201
+ "tool/code-dispatch": disposition([
202
+ "rootCallId",
203
+ "parentCallId",
204
+ "subCallId",
205
+ "name",
206
+ "arguments",
207
+ "isError",
208
+ "content"
209
+ ], [], ["arguments"]),
210
+ "tool/code-dispatch-start": disposition([
211
+ "rootCallId",
212
+ "parentCallId",
213
+ "subCallId",
214
+ "name",
215
+ "arguments"
216
+ ], [], ["arguments"]),
217
+ "tool/result": disposition([
218
+ "turn",
219
+ "step",
220
+ "message"
221
+ ], ["error", "meta"], ["meta"]),
222
+ "turn/end": disposition(["turn", "reason"]),
223
+ "turn/start": disposition(["turn"]),
224
+ "user/message": disposition([
225
+ "role",
226
+ "id",
227
+ "content",
228
+ "source"
229
+ ]),
230
+ "web/deepseek-search-llm-request": disposition([
231
+ "endpoint",
232
+ "apiVersion",
233
+ "body"
234
+ ])
235
+ });
236
+ /** Stable sorted released-v0 event inventory. */
237
+ const RELEASED_V0_EVENT_TYPES = Object.freeze(Object.keys(RELEASED_V0_EVENT_DISPOSITIONS).sort((left, right) => left.localeCompare(right, "en")));
238
+ //#endregion
239
+ //#region lib/types/validation-helpers.js
240
+ /**
241
+ * Require one plain JSON object.
242
+ * @param value - candidate JSON value.
243
+ * @param label - diagnostic subject.
244
+ * @returns validated object record.
245
+ */
246
+ function releasedV0Record(value, label) {
247
+ if (!isSessionFormatJsonObject(value)) throw new SessionFormatError(`${label} must be a JSON object`);
248
+ return value;
249
+ }
250
+ /**
251
+ * Require every named member and no member outside the optional list.
252
+ * @param record - candidate object.
253
+ * @param required - members that must exist.
254
+ * @param optional - additional admitted members.
255
+ * @param label - diagnostic subject.
256
+ */
257
+ function assertReleasedV0Keys(record, required, optional = [], label) {
258
+ const allowed = new Set([...required, ...optional]);
259
+ const unexpected = Object.keys(record).find((key) => !allowed.has(key));
260
+ if (unexpected !== void 0) throw new SessionFormatError(`${label} has unexpected member ${JSON.stringify(unexpected)}`);
261
+ const missing = required.find((key) => !Object.hasOwn(record, key));
262
+ if (missing !== void 0) throw new SessionFormatError(`${label} lacks required member ${JSON.stringify(missing)}`);
263
+ }
264
+ //#endregion
265
+ //#region lib/types/payload-validation.js
266
+ /**
267
+ * Validate nested released payload semantics for one known event.
268
+ * @param event - known event with exact top-level members.
269
+ * @param version - source or current payload generation.
270
+ */
271
+ function assertReleasedPayloadSemantics(event, version) {
272
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
273
+ const label = `${event.type} ${event.seq}`;
274
+ switch (event.type) {
275
+ case "agent-preset/selected":
276
+ stringValue(data["agentPreset"], `${label} agentPreset`);
277
+ return;
278
+ case "agent/inbox/spliced":
279
+ literalValue(data["target"], ["next-turn", "next-step"], `${label} target`);
280
+ countValue(data["start"], `${label} start`);
281
+ if (data["removedCount"] !== void 0) countValue(data["removedCount"], `${label} removedCount`);
282
+ arrayValue(data["inserted"], `${label} inserted`, (value) => {
283
+ messageValue(value, `${label} inserted message`, version, "user");
284
+ });
285
+ if (data["outcome"] !== void 0) literalValue(data["outcome"], ["canceled"], `${label} outcome`);
286
+ return;
287
+ case "approval/asked":
288
+ nonEmptyString(data["id"], `${label} id`);
289
+ nonEmptyString(data["toolName"], `${label} toolName`);
290
+ if (data["callId"] !== void 0) nonEmptyString(data["callId"], `${label} callId`);
291
+ if (data["reason"] !== void 0) stringValue(data["reason"], `${label} reason`);
292
+ return;
293
+ case "approval/decided":
294
+ nonEmptyString(data["id"], `${label} id`);
295
+ literalValue(data["outcome"], [
296
+ "allowed-once",
297
+ "rejected",
298
+ "cancelled",
299
+ "unavailable"
300
+ ], `${label} outcome`);
301
+ return;
302
+ case "approval/policy":
303
+ literalValue(data["policy"], ["ask", "never"], `${label} policy`);
304
+ if (data["source"] !== void 0) literalValue(data["source"], ["delegation"], `${label} source`);
305
+ return;
306
+ case "assistant/chunk":
307
+ coordinatePair(data, label);
308
+ streamChunkValue(data["chunk"], `${label} chunk`);
309
+ return;
310
+ case "assistant/message":
311
+ coordinatePair(data, label);
312
+ messageValue(data["message"], `${label} message`, version, "assistant");
313
+ if (data["usage"] !== void 0) tokenUsageValue(data["usage"], `${label} usage`);
314
+ if (data["interrupted"] !== void 0) literalValue(data["interrupted"], [true], `${label} interrupted`);
315
+ return;
316
+ case "command/done":
317
+ nonEmptyString(data["commandId"], `${label} commandId`);
318
+ literalValue(data["kind"], ["success", "error"], `${label} kind`);
319
+ if (data["text"] !== void 0) stringValue(data["text"], `${label} text`);
320
+ if (data["sourceEventSeq"] !== void 0) earlierSeq(data["sourceEventSeq"], event.seq, `${label} sourceEventSeq`);
321
+ return;
322
+ case "command/run":
323
+ nonEmptyString(data["commandId"], `${label} commandId`);
324
+ nonEmptyString(data["name"], `${label} name`);
325
+ if (data["args"] !== void 0) stringValue(data["args"], `${label} args`);
326
+ literalValue(exactRecord(data["source"], `${label} source`, ["kind"])["kind"], ["user"], `${label} source kind`);
327
+ return;
328
+ case "compaction/start":
329
+ case "compaction/end":
330
+ nonEmptyString(data["compactionId"], `${label} compactionId`);
331
+ if (data["sourceCommandId"] !== void 0) nonEmptyString(data["sourceCommandId"], `${label} sourceCommandId`);
332
+ nullableValue(data["turn"], `${label} turn`, countValue);
333
+ if (data["error"] !== void 0) stringValue(data["error"], `${label} error`);
334
+ return;
335
+ case "compaction/prune":
336
+ shadowedValue(data, event.seq, label);
337
+ return;
338
+ case "compaction/summary":
339
+ if (data["llmStreamCall"] === true && data["rawOutput"] === void 0) throw new SessionFormatError(`${label} llmStreamCall requires rawOutput`);
340
+ nonEmptyString(data["compactionId"], `${label} compactionId`);
341
+ if (data["sourceCommandId"] !== void 0) nonEmptyString(data["sourceCommandId"], `${label} sourceCommandId`);
342
+ contentBlocksValue(data["summary"], `${label} summary`, version);
343
+ shadowedValue(data, event.seq, label);
344
+ nonEmptyString(data["provider"], `${label} provider`);
345
+ nonEmptyString(data["model"], `${label} model`);
346
+ if (data["maxTokens"] !== void 0) countValue(data["maxTokens"], `${label} maxTokens`);
347
+ if (data["usage"] !== void 0) tokenUsageValue(data["usage"], `${label} usage`);
348
+ if (data["rawOutput"] !== void 0) contentBlocksValue(data["rawOutput"], `${label} rawOutput`, version);
349
+ if (data["llmStreamCall"] !== void 0) literalValue(data["llmStreamCall"], [true], `${label} llmStreamCall`);
350
+ return;
351
+ case "feedback/record":
352
+ nonEmptyString(data["text"], `${label} text`);
353
+ return;
354
+ case "goal/change":
355
+ goalChangeValue(data, label);
356
+ return;
357
+ case "hook/invoked":
358
+ countValue(data["turn"], `${label} turn`);
359
+ nonEmptyString(data["point"], `${label} point`);
360
+ literalValue(data["dialect"], ["claude-code", "codex"], `${label} dialect`);
361
+ if (data["matcher"] !== void 0) stringValue(data["matcher"], `${label} matcher`);
362
+ nonEmptyString(data["handlerId"], `${label} handlerId`);
363
+ return;
364
+ case "hook/result":
365
+ countValue(data["turn"], `${label} turn`);
366
+ nonEmptyString(data["point"], `${label} point`);
367
+ nonEmptyString(data["handlerId"], `${label} handlerId`);
368
+ nonEmptyString(data["decision"], `${label} decision`);
369
+ if (data["exitCode"] !== void 0) safeIntegerValue(data["exitCode"], `${label} exitCode`);
370
+ if (data["stderrSummary"] !== void 0) stringValue(data["stderrSummary"], `${label} stderrSummary`);
371
+ if (finiteNumberValue(data["durationMs"], `${label} durationMs`) < 0) throw new SessionFormatError(`${label} durationMs must be non-negative`);
372
+ return;
373
+ case "llm/retry":
374
+ nonEmptyString(data["retryId"], `${label} retryId`);
375
+ coordinatePair(data, label);
376
+ nonEmptyString(data["provider"], `${label} provider`);
377
+ literalValue(data["mode"], ["normal", "always"], `${label} mode`);
378
+ nonEmptyString(data["policyKey"], `${label} policyKey`);
379
+ positiveIntegerValue(data["retry"], `${label} retry`);
380
+ if (data["mode"] === "normal") {
381
+ const maxRetries = positiveIntegerValue(data["maxRetries"], `${label} maxRetries`);
382
+ if (data["retry"] > maxRetries) throw new SessionFormatError(`${label} retry exceeds maxRetries`);
383
+ } else if (data["maxRetries"] !== void 0) throw new SessionFormatError(`${label} always mode must omit maxRetries`);
384
+ const delayMs = finiteNumberValue(data["delayMs"], `${label} delayMs`);
385
+ if (delayMs < 0) throw new SessionFormatError(`${label} delayMs must be non-negative`);
386
+ if (delayMs > 2147483647) throw new SessionFormatError(`${label} delayMs exceeds the timer range`);
387
+ llmFailureValue(data["failure"], `${label} failure`);
388
+ return;
389
+ case "llm/retry-started":
390
+ nonEmptyString(data["retryId"], `${label} retryId`);
391
+ coordinatePair(data, label);
392
+ positiveIntegerValue(data["retry"], `${label} retry`);
393
+ return;
394
+ case "model/selection":
395
+ nonEmptyString(data["provider"], `${label} provider`);
396
+ nonEmptyString(data["model"], `${label} model`);
397
+ if (data["reasoningEffort"] !== void 0) nonEmptyString(data["reasoningEffort"], `${label} reasoningEffort`);
398
+ return;
399
+ case "permission/preset":
400
+ nonEmptyString(data["preset"], `${label} preset`);
401
+ return;
402
+ case "plan/mode":
403
+ booleanValue(data["active"], `${label} active`);
404
+ return;
405
+ case "request/context":
406
+ nonEmptyString(data["provider"], `${label} provider`);
407
+ nonEmptyString(data["model"], `${label} model`);
408
+ if (data["contextWindow"] !== void 0) positiveIntegerValue(data["contextWindow"], `${label} contextWindow`);
409
+ return;
410
+ case "request/header":
411
+ requestHeaderValue(data["header"], `${label} header`);
412
+ literalValue(data["reason"], [
413
+ "initial",
414
+ "resume",
415
+ "change",
416
+ "series"
417
+ ], `${label} reason`);
418
+ if (data["startsSeries"] !== void 0) literalValue(data["startsSeries"], [true], `${label} startsSeries`);
419
+ return;
420
+ case "sandbox/mode":
421
+ literalValue(data["mode"], [
422
+ "read-only",
423
+ "workspace-write",
424
+ "danger-full-access"
425
+ ], `${label} mode`);
426
+ if (data["source"] !== void 0) literalValue(data["source"], ["delegation"], `${label} source`);
427
+ return;
428
+ case "schedule/change":
429
+ scheduleChangeValue(data, label);
430
+ return;
431
+ case "session-log-deepseek/delivery-accepted":
432
+ if ((data["sessionFormatVersion"] === void 0 ? 0 : countValue(data["sessionFormatVersion"], `${label} sessionFormatVersion`)) !== version) return;
433
+ nonEmptyString(data["sessionId"], `${label} sessionId`);
434
+ earlierSeq(data["throughSeq"], event.seq, `${label} throughSeq`);
435
+ return;
436
+ case "session/end-seed": return;
437
+ case "session/title":
438
+ nonEmptyString(data["title"], `${label} title`);
439
+ seqArray(data["messageSeqs"], event.seq, `${label} messageSeqs`, false);
440
+ titleSourceValue(data["source"], `${label} source`);
441
+ return;
442
+ case "session/title-llm-request":
443
+ nonEmptyString(data["titleProvider"], `${label} titleProvider`);
444
+ seqArray(data["messageSeqs"], event.seq, `${label} messageSeqs`, true);
445
+ modelRouteValue(data["route"], `${label} route`);
446
+ stringValue(data["system"], `${label} system`);
447
+ arrayValue(data["messages"], `${label} messages`, (value) => {
448
+ messageValue(value, `${label} message`, version);
449
+ });
450
+ positiveIntegerValue(data["maxTokens"], `${label} maxTokens`);
451
+ return;
452
+ case "step/end":
453
+ case "step/start":
454
+ coordinatePair(data, label);
455
+ return;
456
+ case "subagent/descriptor":
457
+ subagentDescriptorValue(data, label);
458
+ return;
459
+ case "subagent/model-selection-policy":
460
+ allowedModelsValue(data["allowedModels"], `${label} allowedModels`);
461
+ return;
462
+ case "team/member":
463
+ teamSelector(data, label);
464
+ teamMemberValue(data["member"], `${label} member`);
465
+ return;
466
+ case "team/message/delivered":
467
+ teamSelector(data, label);
468
+ nonEmptyString(data["messageId"], `${label} messageId`);
469
+ nonEmptyString(data["targetId"], `${label} targetId`);
470
+ return;
471
+ case "team/message/queued":
472
+ teamSelector(data, label);
473
+ teamMessageValue(data["message"], `${label} message`, version);
474
+ return;
475
+ case "team/task":
476
+ teamSelector(data, label);
477
+ teamTaskValue(data["task"], `${label} task`);
478
+ return;
479
+ case "todo/write":
480
+ arrayValue(data["todos"], `${label} todos`, (value, itemLabel) => {
481
+ const item = exactRecord(value, itemLabel, ["content", "status"]);
482
+ stringValue(item["content"], `${itemLabel} content`);
483
+ literalValue(item["status"], [
484
+ "pending",
485
+ "in_progress",
486
+ "completed"
487
+ ], `${itemLabel} status`);
488
+ });
489
+ return;
490
+ case "tool-workflow/agent-end":
491
+ workflowIdentity(data, label);
492
+ literalValue(data["outcome"], [
493
+ "completed",
494
+ "failed",
495
+ "cancelled"
496
+ ], `${label} outcome`);
497
+ return;
498
+ case "tool-workflow/agent-start":
499
+ workflowIdentity(data, label);
500
+ stringValue(data["label"], `${label} label`);
501
+ if (data["phase"] !== void 0) stringValue(data["phase"], `${label} phase`);
502
+ nonEmptyString(data["childId"], `${label} childId`);
503
+ return;
504
+ case "tool-workflow/run-end":
505
+ nonEmptyString(data["runId"], `${label} runId`);
506
+ literalValue(data["stopReason"], [
507
+ "completed",
508
+ "cancelled",
509
+ "error"
510
+ ], `${label} stopReason`);
511
+ return;
512
+ case "tool-workflow/run-start":
513
+ nonEmptyString(data["runId"], `${label} runId`);
514
+ nonEmptyString(data["name"], `${label} name`);
515
+ return;
516
+ case "tool/call":
517
+ coordinatePair(data, label);
518
+ nonEmptyString(data["callId"], `${label} callId`);
519
+ nonEmptyString(data["name"], `${label} name`);
520
+ stringValue(data["arguments"], `${label} arguments`);
521
+ return;
522
+ case "tool/code-dispatch":
523
+ case "tool/code-dispatch-start":
524
+ nonEmptyString(data["rootCallId"], `${label} rootCallId`);
525
+ nonEmptyString(data["parentCallId"], `${label} parentCallId`);
526
+ nonEmptyString(data["subCallId"], `${label} subCallId`);
527
+ nonEmptyString(data["name"], `${label} name`);
528
+ if (event.type === "tool/code-dispatch") {
529
+ booleanValue(data["isError"], `${label} isError`);
530
+ contentBlocksValue(data["content"], `${label} content`, version);
531
+ }
532
+ return;
533
+ case "tool/result":
534
+ coordinatePair(data, label);
535
+ messageValue(data["message"], `${label} message`, version, "tool");
536
+ if (data["error"] !== void 0) {
537
+ const error = exactRecord(data["error"], `${label} error`, ["name", "code"]);
538
+ nonEmptyString(error["name"], `${label} error name`);
539
+ nonEmptyString(error["code"], `${label} error code`);
540
+ }
541
+ return;
542
+ case "turn/end":
543
+ countValue(data["turn"], `${label} turn`);
544
+ turnEndReasonValue(data["reason"], `${label} reason`);
545
+ return;
546
+ case "turn/start":
547
+ countValue(data["turn"], `${label} turn`);
548
+ return;
549
+ case "user/message":
550
+ messageValue(data, label, version, "user");
551
+ return;
552
+ case "web/deepseek-search-llm-request":
553
+ nonEmptyString(data["endpoint"], `${label} endpoint`);
554
+ nonEmptyString(data["apiVersion"], `${label} apiVersion`);
555
+ deepSeekSearchBodyValue(data["body"], `${label} body`);
556
+ return;
557
+ /* v8 ignore next -- the frozen disposition rejects unknown types before semantic dispatch. */
558
+ default: throw new SessionFormatError(`released payload validator is missing event ${JSON.stringify(event.type)}`);
559
+ }
560
+ }
561
+ function exactRecord(value, label, required, optional = []) {
562
+ const record = releasedV0Record(value, label);
563
+ assertReleasedV0Keys(record, required, optional, label);
564
+ return record;
565
+ }
566
+ function stringValue(value, label) {
567
+ if (typeof value !== "string") throw new SessionFormatError(`${label} must be a string`);
568
+ }
569
+ function nonEmptyString(value, label) {
570
+ if (typeof value !== "string" || value.length === 0) throw new SessionFormatError(`${label} must be a non-empty string`);
571
+ }
572
+ function booleanValue(value, label) {
573
+ if (typeof value !== "boolean") throw new SessionFormatError(`${label} must be a boolean`);
574
+ }
575
+ function safeIntegerValue(value, label) {
576
+ return sessionFormatSafeInteger(value, label);
577
+ }
578
+ function countValue(value, label) {
579
+ return sessionFormatCount(value, label);
580
+ }
581
+ function positiveIntegerValue(value, label) {
582
+ const result = countValue(value, label);
583
+ if (result === 0) throw new SessionFormatError(`${label} must be positive`);
584
+ return result;
585
+ }
586
+ function finiteNumberValue(value, label) {
587
+ if (typeof value !== "number" || !Number.isFinite(value) || Object.is(value, -0)) throw new SessionFormatError(`${label} must be a finite number`);
588
+ return value;
589
+ }
590
+ function literalValue(value, allowed, label) {
591
+ if (!allowed.some((candidate) => candidate === value)) throw new SessionFormatError(`${label} must be one of ${allowed.map(String).join(", ")}`);
592
+ }
593
+ function nullableValue(value, label, validate) {
594
+ if (value !== null) validate(value, label);
595
+ }
596
+ function arrayValue(value, label, validate) {
597
+ if (!Array.isArray(value)) throw new SessionFormatError(`${label} must be an array`);
598
+ const members = value;
599
+ members.forEach((member, index) => {
600
+ validate(member, `${label}[${index}]`);
601
+ });
602
+ return members;
603
+ }
604
+ function coordinatePair(data, label) {
605
+ countValue(data["turn"], `${label} turn`);
606
+ countValue(data["step"], `${label} step`);
607
+ }
608
+ function earlierSeq(value, eventSeq, label) {
609
+ const seq = countValue(value, label);
610
+ if (seq >= eventSeq) throw new SessionFormatError(`${label} must identify an earlier event`);
611
+ return seq;
612
+ }
613
+ function seqArray(value, eventSeq, label, requireNonEmpty) {
614
+ const seen = /* @__PURE__ */ new Set();
615
+ const values = arrayValue(value, label, (member, memberLabel) => {
616
+ const seq = earlierSeq(member, eventSeq, memberLabel);
617
+ if (seen.has(seq)) throw new SessionFormatError(`${label} repeats seq ${seq}`);
618
+ seen.add(seq);
619
+ });
620
+ if (requireNonEmpty && values.length === 0) throw new SessionFormatError(`${label} must be non-empty`);
621
+ return values;
622
+ }
623
+ function llmFailureValue(value, label) {
624
+ const failure = exactRecord(value, label, ["message", "code"], [
625
+ "status",
626
+ "providerRetryAfterMs",
627
+ "requestId"
628
+ ]);
629
+ nonEmptyString(failure["message"], `${label} message`);
630
+ nonEmptyString(failure["code"], `${label} code`);
631
+ if (failure["status"] !== void 0) {
632
+ const status = safeIntegerValue(failure["status"], `${label} status`);
633
+ if (status < 100 || status > 599) throw new SessionFormatError(`${label} status must be 100 through 599`);
634
+ }
635
+ if (failure["providerRetryAfterMs"] !== void 0 && finiteNumberValue(failure["providerRetryAfterMs"], `${label} providerRetryAfterMs`) <= 0) throw new SessionFormatError(`${label} providerRetryAfterMs must be positive`);
636
+ if (failure["requestId"] !== void 0) nonEmptyString(failure["requestId"], `${label} requestId`);
637
+ }
638
+ function tokenUsageValue(value, label) {
639
+ const usage = exactRecord(value, label, ["inputTokens", "outputTokens"], [
640
+ "totalTokens",
641
+ "cacheReadTokens",
642
+ "cacheWriteTokens",
643
+ "reasoningTokens"
644
+ ]);
645
+ for (const key of Object.keys(usage)) countValue(usage[key], `${label} ${key}`);
646
+ }
647
+ function contentBlocksValue(value, label, version) {
648
+ arrayValue(value, label, (member, memberLabel) => {
649
+ contentBlockValue(member, memberLabel, version);
650
+ });
651
+ }
652
+ function contentBlockValue(value, label, version) {
653
+ const block = releasedV0Record(value, label);
654
+ switch (block["type"]) {
655
+ case "text":
656
+ case "reasoning":
657
+ assertReleasedV0Keys(block, ["type", "text"], [], label);
658
+ stringValue(block["text"], `${label} text`);
659
+ return;
660
+ case "image":
661
+ assertReleasedV0Keys(block, ["type", "attachment"], [], label);
662
+ imageAttachmentValue(block["attachment"], `${label} attachment`);
663
+ return;
664
+ case "tool-call":
665
+ assertReleasedV0Keys(block, [
666
+ "type",
667
+ "id",
668
+ "name",
669
+ "arguments"
670
+ ], [], label);
671
+ nonEmptyString(block["id"], `${label} id`);
672
+ nonEmptyString(block["name"], `${label} name`);
673
+ stringValue(block["arguments"], `${label} arguments`);
674
+ return;
675
+ case "tool-result":
676
+ assertReleasedV0Keys(block, [
677
+ "type",
678
+ "toolCallId",
679
+ "content"
680
+ ], ["isError"], label);
681
+ nonEmptyString(block["toolCallId"], `${label} toolCallId`);
682
+ contentBlocksValue(block["content"], `${label} content`, version);
683
+ if (block["isError"] !== void 0) booleanValue(block["isError"], `${label} isError`);
684
+ return;
685
+ default:
686
+ nonEmptyString(block["type"], `${label} type`);
687
+ return;
688
+ }
689
+ }
690
+ function imageAttachmentValue(value, label) {
691
+ const attachment = exactRecord(value, label, [
692
+ "attachmentId",
693
+ "mediaType",
694
+ "bytes",
695
+ "width",
696
+ "height"
697
+ ], ["name", "originalDimensions"]);
698
+ nonEmptyString(attachment["attachmentId"], `${label} attachmentId`);
699
+ literalValue(attachment["mediaType"], [
700
+ "image/png",
701
+ "image/jpeg",
702
+ "image/webp",
703
+ "image/gif"
704
+ ], `${label} mediaType`);
705
+ countValue(attachment["bytes"], `${label} bytes`);
706
+ positiveIntegerValue(attachment["width"], `${label} width`);
707
+ positiveIntegerValue(attachment["height"], `${label} height`);
708
+ if (attachment["name"] !== void 0) stringValue(attachment["name"], `${label} name`);
709
+ if (attachment["originalDimensions"] !== void 0) {
710
+ const dimensions = exactRecord(attachment["originalDimensions"], `${label} originalDimensions`, ["width", "height"]);
711
+ positiveIntegerValue(dimensions["width"], `${label} original width`);
712
+ positiveIntegerValue(dimensions["height"], `${label} original height`);
713
+ }
714
+ }
715
+ function messageValue(value, label, version, expected) {
716
+ const message = exactRecord(value, label, [
717
+ "id",
718
+ "role",
719
+ "content",
720
+ "source"
721
+ ]);
722
+ nonEmptyString(message["id"], `${label} id`);
723
+ const role = expected === "assistant" ? "assistant" : expected === "user" || expected === "tool" ? "user" : void 0;
724
+ if (role === void 0) literalValue(message["role"], [
725
+ "system",
726
+ "user",
727
+ "assistant"
728
+ ], `${label} role`);
729
+ else literalValue(message["role"], [role], `${label} role`);
730
+ contentBlocksValue(message["content"], `${label} content`, version);
731
+ const source = releasedV0Record(message["source"], `${label} source`);
732
+ if (version < 2 && expected === "user" && source["kind"] === "goal" && source["change"] !== void 0) legacyGoalMessageValue(message, source, label);
733
+ else messageSourceValue(source, `${label} source`, version, expected);
734
+ if (expected === "tool") {
735
+ const content = message["content"];
736
+ const block = Array.isArray(content) && content.length === 1 ? releasedV0Record(content[0], `${label} tool result`) : void 0;
737
+ const source = releasedV0Record(message["source"], `${label} source`);
738
+ if (block?.["type"] !== "tool-result" || block["toolCallId"] !== source["callId"]) throw new SessionFormatError(`${label} must contain exactly one tool-result block`);
739
+ }
740
+ }
741
+ function legacyGoalMessageValue(message, source, label) {
742
+ assertReleasedV0Keys(source, [
743
+ "kind",
744
+ "goalId",
745
+ "revision",
746
+ "round",
747
+ "change"
748
+ ], [], `${label} source`);
749
+ nonEmptyString(source["goalId"], `${label} source goalId`);
750
+ positiveIntegerValue(source["revision"], `${label} source revision`);
751
+ if (source["round"] !== 0) throw new SessionFormatError(`${label} legacy goal source round must be 0`);
752
+ const change = releasedV0Record(source["change"], `${label} source change`);
753
+ goalChangeValue(change, `${label} source change`);
754
+ const ref = releasedV0Record(change["operation"] === "clear" ? change["cleared"] : change["goal"], `${label} source change ref`);
755
+ if (source["goalId"] !== ref["id"] || source["revision"] !== ref["revision"]) throw new SessionFormatError(`${label} legacy goal source does not match its change`);
756
+ const payload = change["operation"] === "clear" ? {
757
+ cleared: change["cleared"],
758
+ clearedAt: change["clearedAt"]
759
+ } : {
760
+ goal: change["goal"],
761
+ roundsStarted: change["roundsStarted"],
762
+ createdAt: change["createdAt"],
763
+ updatedAt: change["updatedAt"]
764
+ };
765
+ const expected = [{
766
+ type: "text",
767
+ text: `<goal_state>${JSON.stringify(payload)}</goal_state>`
768
+ }];
769
+ if (!deepEqualJson(message["content"], expected)) throw new SessionFormatError(`${label} legacy goal content does not match its change`);
770
+ }
771
+ function messageSourceValue(value, label, version, expected) {
772
+ const source = releasedV0Record(value, label);
773
+ if (expected === "assistant" && source["kind"] !== "model") throw new SessionFormatError(`${label} must be model source`);
774
+ if (expected === "tool" && source["kind"] !== "tool") throw new SessionFormatError(`${label} must be tool source`);
775
+ switch (source["kind"]) {
776
+ case "user":
777
+ assertReleasedV0Keys(source, ["kind"], ["rpcId", "clientTimeZone"], label);
778
+ if (source["rpcId"] !== void 0) nonEmptyString(source["rpcId"], `${label} rpcId`);
779
+ if (source["clientTimeZone"] !== void 0) nonEmptyString(source["clientTimeZone"], `${label} clientTimeZone`);
780
+ return;
781
+ case "plugin":
782
+ pluginSourceValue(source, label);
783
+ return;
784
+ case "model":
785
+ assertReleasedV0Keys(source, [
786
+ "kind",
787
+ "provider",
788
+ "model"
789
+ ], ["replayState"], label);
790
+ nonEmptyString(source["provider"], `${label} provider`);
791
+ nonEmptyString(source["model"], `${label} model`);
792
+ return;
793
+ case "tool":
794
+ assertReleasedV0Keys(source, ["kind", "callId"], [], label);
795
+ nonEmptyString(source["callId"], `${label} callId`);
796
+ return;
797
+ case "agent-instructions":
798
+ assertReleasedV0Keys(source, [
799
+ "kind",
800
+ "form",
801
+ "changes"
802
+ ], ["baseline", "baselineIdentity"], label);
803
+ literalValue(source["form"], ["instructions"], `${label} form`);
804
+ if (source["baseline"] !== void 0) literalValue(source["baseline"], [true], `${label} baseline`);
805
+ if (source["baselineIdentity"] !== void 0) nonEmptyString(source["baselineIdentity"], `${label} baselineIdentity`);
806
+ arrayValue(source["changes"], `${label} changes`, (member, memberLabel) => {
807
+ const change = exactRecord(member, memberLabel, [
808
+ "action",
809
+ "scope",
810
+ "path"
811
+ ], ["digest"]);
812
+ literalValue(change["action"], [
813
+ "set",
814
+ "replace",
815
+ "remove"
816
+ ], `${memberLabel} action`);
817
+ stringValue(change["scope"], `${memberLabel} scope`);
818
+ stringValue(change["path"], `${memberLabel} path`);
819
+ if (change["digest"] !== void 0) stringValue(change["digest"], `${memberLabel} digest`);
820
+ });
821
+ return;
822
+ case "session-reference":
823
+ sessionReferenceSourceValue(source, label, version);
824
+ return;
825
+ case "team-message":
826
+ assertReleasedV0Keys(source, [
827
+ "kind",
828
+ "teamId",
829
+ "messageId",
830
+ "senderId",
831
+ "senderName"
832
+ ], [], label);
833
+ for (const key of [
834
+ "teamId",
835
+ "messageId",
836
+ "senderId"
837
+ ]) nonEmptyString(source[key], `${label} ${key}`);
838
+ stringValue(source["senderName"], `${label} senderName`);
839
+ return;
840
+ case "goal":
841
+ assertReleasedV0Keys(source, [
842
+ "kind",
843
+ "goalId",
844
+ "revision",
845
+ "round"
846
+ ], [], label);
847
+ nonEmptyString(source["goalId"], `${label} goalId`);
848
+ positiveIntegerValue(source["revision"], `${label} revision`);
849
+ positiveIntegerValue(source["round"], `${label} round`);
850
+ return;
851
+ case "skill-invocation":
852
+ assertReleasedV0Keys(source, [
853
+ "kind",
854
+ "name",
855
+ "form"
856
+ ], [], label);
857
+ nonEmptyString(source["name"], `${label} name`);
858
+ literalValue(source["form"], ["instructions"], `${label} form`);
859
+ return;
860
+ case "skill-catalog":
861
+ assertReleasedV0Keys(source, [
862
+ "kind",
863
+ "form",
864
+ "entries"
865
+ ], ["update"], label);
866
+ literalValue(source["form"], ["catalog"], `${label} form`);
867
+ if (source["update"] !== void 0) literalValue(source["update"], [true], `${label} update`);
868
+ arrayValue(source["entries"], `${label} entries`, (member, memberLabel) => {
869
+ const entry = exactRecord(member, memberLabel, ["name", "description"]);
870
+ nonEmptyString(entry["name"], `${memberLabel} name`);
871
+ stringValue(entry["description"], `${memberLabel} description`);
872
+ });
873
+ return;
874
+ case "coordinator":
875
+ case "subagent-report":
876
+ assertReleasedV0Keys(source, [
877
+ "kind",
878
+ "form",
879
+ "senderSessionId"
880
+ ], [], label);
881
+ literalValue(source["form"], ["relay"], `${label} form`);
882
+ nonEmptyString(source["senderSessionId"], `${label} senderSessionId`);
883
+ return;
884
+ case "subagent-settled":
885
+ assertReleasedV0Keys(source, [
886
+ "kind",
887
+ "form",
888
+ "summary",
889
+ "senderSessionId"
890
+ ], [], label);
891
+ literalValue(source["form"], ["notice"], `${label} form`);
892
+ stringValue(source["summary"], `${label} summary`);
893
+ nonEmptyString(source["senderSessionId"], `${label} senderSessionId`);
894
+ return;
895
+ case "webhook":
896
+ assertReleasedV0Keys(source, [
897
+ "kind",
898
+ "provider",
899
+ "source",
900
+ "deliveryId",
901
+ "ruleId",
902
+ "form",
903
+ "summary"
904
+ ], [], label);
905
+ for (const key of [
906
+ "provider",
907
+ "source",
908
+ "deliveryId",
909
+ "ruleId"
910
+ ]) nonEmptyString(source[key], `${label} ${key}`);
911
+ literalValue(source["form"], ["notice"], `${label} form`);
912
+ stringValue(source["summary"], `${label} summary`);
913
+ return;
914
+ default:
915
+ nonEmptyString(source["kind"], `${label} kind`);
916
+ return;
917
+ }
918
+ }
919
+ function pluginSourceValue(source, label) {
920
+ const optional = [
921
+ "form",
922
+ "sections",
923
+ "summary"
924
+ ];
925
+ if (source["plugin"] === "compact") optional.push("compactionId", "sourceCommandId");
926
+ assertReleasedV0Keys(source, ["kind", "plugin"], optional, label);
927
+ nonEmptyString(source["plugin"], `${label} plugin`);
928
+ if (source["plugin"] === "compact") {
929
+ nonEmptyString(source["compactionId"], `${label} compactionId`);
930
+ if (source["sourceCommandId"] !== void 0) nonEmptyString(source["sourceCommandId"], `${label} sourceCommandId`);
931
+ }
932
+ const form = source["form"];
933
+ if (form === void 0) return;
934
+ literalValue(form, [
935
+ "instructions",
936
+ "catalog",
937
+ "snapshot",
938
+ "notice",
939
+ "relay",
940
+ "recall"
941
+ ], `${label} form`);
942
+ if (form === "snapshot") arrayValue(source["sections"], `${label} sections`, (member, memberLabel) => {
943
+ const section = exactRecord(member, memberLabel, ["name", "text"]);
944
+ nonEmptyString(section["name"], `${memberLabel} name`);
945
+ stringValue(section["text"], `${memberLabel} text`);
946
+ });
947
+ else if (source["sections"] !== void 0) throw new SessionFormatError(`${label} sections require snapshot form`);
948
+ if (form === "notice") stringValue(source["summary"], `${label} summary`);
949
+ else if (source["summary"] !== void 0) throw new SessionFormatError(`${label} summary requires notice form`);
950
+ }
951
+ function sessionReferenceSourceValue(source, label, version) {
952
+ assertReleasedV0Keys(source, [
953
+ "kind",
954
+ "form",
955
+ "version",
956
+ "references"
957
+ ], [], label);
958
+ literalValue(source["form"], ["recall"], `${label} form`);
959
+ literalValue(source["version"], [1], `${label} version`);
960
+ let expectedInputIndex = 0;
961
+ const sessionIds = /* @__PURE__ */ new Set();
962
+ if (arrayValue(source["references"], `${label} references`, (member, memberLabel) => {
963
+ const reference = exactRecord(member, memberLabel, [
964
+ "sessionId",
965
+ "label",
966
+ "capturedThroughSeq",
967
+ "compacted",
968
+ "originalMessages",
969
+ "retainedMessages",
970
+ "omittedMessages",
971
+ "omittedBytes",
972
+ "truncated",
973
+ "inputIndex"
974
+ ], version >= 1 ? ["capturedFormatVersion"] : []);
975
+ nonEmptyString(reference["sessionId"], `${memberLabel} sessionId`);
976
+ stringValue(reference["label"], `${memberLabel} label`);
977
+ if (reference["capturedThroughSeq"] !== null) countValue(reference["capturedThroughSeq"], `${memberLabel} capturedThroughSeq`);
978
+ if (reference["capturedFormatVersion"] !== void 0) {
979
+ const capturedVersion = countValue(reference["capturedFormatVersion"], `${memberLabel} capturedFormatVersion`);
980
+ if (capturedVersion < 1 || capturedVersion > version) throw new SessionFormatError(`${memberLabel} capturedFormatVersion must be between 1 and ${version}`);
981
+ }
982
+ booleanValue(reference["compacted"], `${memberLabel} compacted`);
983
+ const original = countValue(reference["originalMessages"], `${memberLabel} originalMessages`);
984
+ const retained = countValue(reference["retainedMessages"], `${memberLabel} retainedMessages`);
985
+ const omitted = countValue(reference["omittedMessages"], `${memberLabel} omittedMessages`);
986
+ const omittedBytes = countValue(reference["omittedBytes"], `${memberLabel} omittedBytes`);
987
+ const inputIndex = countValue(reference["inputIndex"], `${memberLabel} inputIndex`);
988
+ const truncated = reference["truncated"];
989
+ booleanValue(truncated, `${memberLabel} truncated`);
990
+ if (retained > original || omitted !== original - retained) throw new SessionFormatError(`${memberLabel} message counts are inconsistent`);
991
+ if (truncated !== (omitted > 0 || omittedBytes > 0)) throw new SessionFormatError(`${memberLabel} truncated disagrees with omitted content`);
992
+ if (inputIndex !== expectedInputIndex) throw new SessionFormatError(`${label} inputIndex must match reference position`);
993
+ expectedInputIndex += 1;
994
+ const sessionId = reference["sessionId"];
995
+ if (sessionIds.has(sessionId)) throw new SessionFormatError(`${label} repeats sessionId ${sessionId}`);
996
+ sessionIds.add(sessionId);
997
+ }).length === 0) throw new SessionFormatError(`${label} references must be non-empty`);
998
+ }
999
+ function streamChunkValue(value, label) {
1000
+ const chunk = releasedV0Record(value, label);
1001
+ switch (chunk["type"]) {
1002
+ case "block-start":
1003
+ assertReleasedV0Keys(chunk, [
1004
+ "type",
1005
+ "index",
1006
+ "blockType"
1007
+ ], [], label);
1008
+ countValue(chunk["index"], `${label} index`);
1009
+ nonEmptyString(chunk["blockType"], `${label} blockType`);
1010
+ return;
1011
+ case "text-delta":
1012
+ case "reasoning-delta":
1013
+ assertReleasedV0Keys(chunk, [
1014
+ "type",
1015
+ "index",
1016
+ "text"
1017
+ ], [], label);
1018
+ countValue(chunk["index"], `${label} index`);
1019
+ stringValue(chunk["text"], `${label} text`);
1020
+ return;
1021
+ case "tool-call-delta":
1022
+ assertReleasedV0Keys(chunk, [
1023
+ "type",
1024
+ "index",
1025
+ "id",
1026
+ "argumentsDelta"
1027
+ ], ["name"], label);
1028
+ countValue(chunk["index"], `${label} index`);
1029
+ nonEmptyString(chunk["id"], `${label} id`);
1030
+ if (chunk["name"] !== void 0) stringValue(chunk["name"], `${label} name`);
1031
+ stringValue(chunk["argumentsDelta"], `${label} argumentsDelta`);
1032
+ return;
1033
+ case "block-end":
1034
+ assertReleasedV0Keys(chunk, [
1035
+ "type",
1036
+ "index",
1037
+ "block"
1038
+ ], [], label);
1039
+ countValue(chunk["index"], `${label} index`);
1040
+ contentBlockValue(chunk["block"], `${label} block`, 1);
1041
+ return;
1042
+ case "usage":
1043
+ assertReleasedV0Keys(chunk, ["type", "usage"], [], label);
1044
+ tokenUsageValue(chunk["usage"], `${label} usage`);
1045
+ return;
1046
+ case "finish":
1047
+ assertReleasedV0Keys(chunk, ["type", "reason"], ["replayState"], label);
1048
+ finishReasonValue(chunk["reason"], `${label} reason`);
1049
+ if (chunk["replayState"] !== void 0) replayEnvelopeValue(chunk["replayState"], `${label} replayState`);
1050
+ return;
1051
+ default: throw new SessionFormatError(`${label} has unknown stream chunk type ${JSON.stringify(chunk["type"])}`);
1052
+ }
1053
+ }
1054
+ function finishReasonValue(value, label) {
1055
+ const reason = releasedV0Record(value, label);
1056
+ if (reason["kind"] === "aborted" || reason["kind"] === "error") {
1057
+ assertReleasedV0Keys(reason, ["kind", "failure"], [], label);
1058
+ llmFailureValue(reason["failure"], `${label} failure`);
1059
+ return;
1060
+ }
1061
+ if (reason["kind"] === "stop" || reason["kind"] === "tool-calls" || reason["kind"] === "max-tokens") assertReleasedV0Keys(reason, ["kind"], [], label);
1062
+ nonEmptyString(reason["kind"], `${label} kind`);
1063
+ }
1064
+ function replayEnvelopeValue(value, label) {
1065
+ const replay = exactRecord(value, label, ["response"], ["blocks"]);
1066
+ if (replay["blocks"] !== void 0 && !Array.isArray(replay["blocks"])) throw new SessionFormatError(`${label} blocks must be an array`);
1067
+ }
1068
+ function turnEndReasonValue(value, label) {
1069
+ const reason = releasedV0Record(value, label);
1070
+ switch (reason["kind"]) {
1071
+ case "completed":
1072
+ case "blocked":
1073
+ case "max-tokens":
1074
+ case "interrupted":
1075
+ assertReleasedV0Keys(reason, ["kind"], [], label);
1076
+ return;
1077
+ case "aborted": {
1078
+ assertReleasedV0Keys(reason, ["kind", "reason"], [], label);
1079
+ const cause = releasedV0Record(reason["reason"], `${label} abort cause`);
1080
+ if (cause["kind"] === "hook") {
1081
+ assertReleasedV0Keys(cause, ["kind", "reason"], [], `${label} abort cause`);
1082
+ stringValue(cause["reason"], `${label} abort reason`);
1083
+ } else {
1084
+ assertReleasedV0Keys(cause, ["kind"], [], `${label} abort cause`);
1085
+ literalValue(cause["kind"], [
1086
+ "user",
1087
+ "parent",
1088
+ "disposed",
1089
+ "legacy"
1090
+ ], `${label} abort kind`);
1091
+ }
1092
+ return;
1093
+ }
1094
+ case "error":
1095
+ assertReleasedV0Keys(reason, ["kind", "error"], [], label);
1096
+ llmFailureValue(reason["error"], `${label} error`);
1097
+ return;
1098
+ default:
1099
+ nonEmptyString(reason["kind"], `${label} kind`);
1100
+ return;
1101
+ }
1102
+ }
1103
+ function requestHeaderValue(value, label) {
1104
+ const header = exactRecord(value, label, ["config"], [
1105
+ "adapterDefaults",
1106
+ "system",
1107
+ "tools"
1108
+ ]);
1109
+ const config = exactRecord(header["config"], `${label} config`, ["provider", "model"], [
1110
+ "reasoningEffort",
1111
+ "temperature",
1112
+ "maxTokens",
1113
+ "stop"
1114
+ ]);
1115
+ nonEmptyString(config["provider"], `${label} provider`);
1116
+ nonEmptyString(config["model"], `${label} model`);
1117
+ if (config["reasoningEffort"] !== void 0) nonEmptyString(config["reasoningEffort"], `${label} reasoningEffort`);
1118
+ if (config["temperature"] !== void 0) finiteNumberValue(config["temperature"], `${label} temperature`);
1119
+ if (config["maxTokens"] !== void 0) positiveIntegerValue(config["maxTokens"], `${label} maxTokens`);
1120
+ if (config["stop"] !== void 0) arrayValue(config["stop"], `${label} stop`, stringValue);
1121
+ if (header["adapterDefaults"] !== void 0) {
1122
+ const defaults = exactRecord(header["adapterDefaults"], `${label} adapterDefaults`, [], ["reasoningEffort", "maxTokens"]);
1123
+ for (const [key, marker] of Object.entries(defaults)) {
1124
+ literalValue(marker, [true], `${label} adapterDefaults ${key}`);
1125
+ if (!Object.hasOwn(config, key)) throw new SessionFormatError(`${label} adapter default ${key} lacks config value`);
1126
+ }
1127
+ }
1128
+ if (header["system"] !== void 0) stringValue(header["system"], `${label} system`);
1129
+ if (header["tools"] !== void 0) arrayValue(header["tools"], `${label} tools`, toolSchemaValue);
1130
+ }
1131
+ function toolSchemaValue(value, label) {
1132
+ const schema = exactRecord(value, label, [
1133
+ "name",
1134
+ "description",
1135
+ "parameters"
1136
+ ]);
1137
+ nonEmptyString(schema["name"], `${label} name`);
1138
+ stringValue(schema["description"], `${label} description`);
1139
+ releasedV0Record(schema["parameters"], `${label} parameters`);
1140
+ }
1141
+ function shadowedValue(data, eventSeq, label) {
1142
+ const range = exactRecord(data["shadowedRange"], `${label} shadowedRange`, ["start", "end"]);
1143
+ const start = earlierSeq(range["start"], eventSeq, `${label} shadowedRange start`);
1144
+ const end = earlierSeq(range["end"], eventSeq, `${label} shadowedRange end`);
1145
+ const seqs = seqArray(data["shadowedSeqs"], eventSeq, `${label} shadowedSeqs`, true);
1146
+ if (seqs[0] !== start || seqs.at(-1) !== end) throw new SessionFormatError(`${label} shadowedRange must match shadowedSeqs endpoints`);
1147
+ countValue(data["shadowedTokenCount"], `${label} shadowedTokenCount`);
1148
+ }
1149
+ function goalChangeValue(data, label) {
1150
+ literalValue(data["kind"], ["goal/change"], `${label} kind`);
1151
+ literalValue(data["version"], [1], `${label} version`);
1152
+ if (data["operation"] === "clear") {
1153
+ assertReleasedV0Keys(data, [
1154
+ "kind",
1155
+ "version",
1156
+ "operation",
1157
+ "cleared",
1158
+ "clearedAt"
1159
+ ], [], `${label} data`);
1160
+ goalRefValue(data["cleared"], `${label} cleared`);
1161
+ countValue(data["clearedAt"], `${label} clearedAt`);
1162
+ return;
1163
+ }
1164
+ assertReleasedV0Keys(data, [
1165
+ "kind",
1166
+ "version",
1167
+ "operation",
1168
+ "goal",
1169
+ "roundsStarted",
1170
+ "createdAt",
1171
+ "updatedAt"
1172
+ ], [], `${label} data`);
1173
+ literalValue(data["operation"], [
1174
+ "create",
1175
+ "edit",
1176
+ "pause",
1177
+ "resume",
1178
+ "complete",
1179
+ "block"
1180
+ ], `${label} operation`);
1181
+ goalSnapshotValue(data["goal"], `${label} goal`);
1182
+ countValue(data["roundsStarted"], `${label} roundsStarted`);
1183
+ countValue(data["createdAt"], `${label} createdAt`);
1184
+ countValue(data["updatedAt"], `${label} updatedAt`);
1185
+ }
1186
+ function goalRefValue(value, label) {
1187
+ const ref = exactRecord(value, label, ["id", "revision"]);
1188
+ nonEmptyString(ref["id"], `${label} id`);
1189
+ positiveIntegerValue(ref["revision"], `${label} revision`);
1190
+ }
1191
+ function goalSnapshotValue(value, label) {
1192
+ const goal = exactRecord(value, label, [
1193
+ "id",
1194
+ "revision",
1195
+ "objective",
1196
+ "phase",
1197
+ "maxGoalRounds"
1198
+ ], ["blockedReason"]);
1199
+ nonEmptyString(goal["id"], `${label} id`);
1200
+ positiveIntegerValue(goal["revision"], `${label} revision`);
1201
+ nonEmptyString(goal["objective"], `${label} objective`);
1202
+ literalValue(goal["phase"], [
1203
+ "active",
1204
+ "paused",
1205
+ "blocked",
1206
+ "complete"
1207
+ ], `${label} phase`);
1208
+ positiveIntegerValue(goal["maxGoalRounds"], `${label} maxGoalRounds`);
1209
+ if (goal["phase"] === "blocked") {
1210
+ const reason = exactRecord(goal["blockedReason"], `${label} blockedReason`, ["code", "message"]);
1211
+ nonEmptyString(reason["code"], `${label} blocked code`);
1212
+ nonEmptyString(reason["message"], `${label} blocked message`);
1213
+ } else if (goal["blockedReason"] !== void 0) throw new SessionFormatError(`${label} blockedReason requires blocked phase`);
1214
+ }
1215
+ function scheduleChangeValue(data, label) {
1216
+ literalValue(data["version"], [1], `${label} version`);
1217
+ if (data["operation"] === "create") {
1218
+ assertReleasedV0Keys(data, [
1219
+ "version",
1220
+ "operation",
1221
+ "schedule"
1222
+ ], [], `${label} data`);
1223
+ scheduleRecordValue(data["schedule"], `${label} schedule`);
1224
+ return;
1225
+ }
1226
+ assertReleasedV0Keys(data, [
1227
+ "version",
1228
+ "operation",
1229
+ "id"
1230
+ ], data["operation"] === "dispatch" ? ["acceptedAt"] : [], `${label} data`);
1231
+ literalValue(data["operation"], ["delete", "dispatch"], `${label} operation`);
1232
+ scheduleIdValue(data["id"], `${label} id`);
1233
+ if (data["acceptedAt"] !== void 0) instantValue(data["acceptedAt"], `${label} acceptedAt`);
1234
+ }
1235
+ function scheduleRecordValue(value, label) {
1236
+ const record = releasedV0Record(value, label);
1237
+ if (record["kind"] === "after") {
1238
+ assertReleasedV0Keys(record, [
1239
+ "id",
1240
+ "kind",
1241
+ "prompt",
1242
+ "afterSeconds",
1243
+ "scheduledAt"
1244
+ ], [], label);
1245
+ positiveIntegerValue(record["afterSeconds"], `${label} afterSeconds`);
1246
+ } else if (record["kind"] === "at") assertReleasedV0Keys(record, [
1247
+ "id",
1248
+ "kind",
1249
+ "prompt",
1250
+ "scheduledAt"
1251
+ ], [], label);
1252
+ else if (record["kind"] === "every") {
1253
+ assertReleasedV0Keys(record, [
1254
+ "id",
1255
+ "kind",
1256
+ "prompt",
1257
+ "everySeconds",
1258
+ "scheduledAt"
1259
+ ], [], label);
1260
+ if (positiveIntegerValue(record["everySeconds"], `${label} everySeconds`) < 300) throw new SessionFormatError(`${label} everySeconds must be at least 300`);
1261
+ } else throw new SessionFormatError(`${label} has unknown schedule kind`);
1262
+ scheduleIdValue(record["id"], `${label} id`);
1263
+ nonEmptyString(record["prompt"], `${label} prompt`);
1264
+ instantValue(record["scheduledAt"], `${label} scheduledAt`);
1265
+ }
1266
+ function scheduleIdValue(value, label) {
1267
+ nonEmptyString(value, label);
1268
+ if (value.trim() !== value) throw new SessionFormatError(`${label} must not have surrounding whitespace`);
1269
+ }
1270
+ function instantValue(value, label) {
1271
+ if (typeof value !== "string" || !/^(?!0000)\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d\.\d{3}Z$/u.test(value) || !Number.isFinite(Date.parse(value)) || new Date(Date.parse(value)).toISOString() !== value) throw new SessionFormatError(`${label} must be a canonical UTC instant`);
1272
+ }
1273
+ function titleSourceValue(value, label) {
1274
+ const source = releasedV0Record(value, label);
1275
+ if (source["kind"] === "provider") {
1276
+ assertReleasedV0Keys(source, ["kind", "provider"], ["model"], label);
1277
+ nonEmptyString(source["provider"], `${label} provider`);
1278
+ if (source["model"] !== void 0) modelRouteValue(source["model"], `${label} model`);
1279
+ return;
1280
+ }
1281
+ assertReleasedV0Keys(source, ["kind"], [], label);
1282
+ literalValue(source["kind"], ["fallback", "user"], `${label} kind`);
1283
+ }
1284
+ function modelRouteValue(value, label) {
1285
+ const route = exactRecord(value, label, ["provider", "model"]);
1286
+ nonEmptyString(route["provider"], `${label} provider`);
1287
+ nonEmptyString(route["model"], `${label} model`);
1288
+ }
1289
+ function subagentDescriptorValue(data, label) {
1290
+ literalValue(data["version"], [3], `${label} version`);
1291
+ nonEmptyString(data["provider"], `${label} provider`);
1292
+ if (data["mode"] === "one-shot") {
1293
+ assertReleasedV0Keys(data, [
1294
+ "mode",
1295
+ "version",
1296
+ "provider"
1297
+ ], ["label"], `${label} data`);
1298
+ if (data["label"] !== void 0) stringValue(data["label"], `${label} label`);
1299
+ return;
1300
+ }
1301
+ literalValue(data["mode"], ["continuable"], `${label} mode`);
1302
+ nonEmptyString(data["label"], `${label} label`);
1303
+ for (const key of [
1304
+ "agentProvider",
1305
+ "agentModel",
1306
+ "agentReasoningEffort",
1307
+ "persona"
1308
+ ]) if (data[key] !== void 0) nonEmptyString(data[key], `${label} ${key}`);
1309
+ if (data["agentProvider"] === void 0 !== (data["agentModel"] === void 0)) throw new SessionFormatError(`${label} agentProvider and agentModel must be paired`);
1310
+ if (data["toolFilter"] !== void 0) {
1311
+ const filter = exactRecord(data["toolFilter"], `${label} toolFilter`, [], ["allow", "deny"]);
1312
+ if (filter["allow"] === void 0 && filter["deny"] === void 0) throw new SessionFormatError(`${label} toolFilter requires allow or deny`);
1313
+ if (filter["allow"] !== void 0) arrayValue(filter["allow"], `${label} allow`, nonEmptyString);
1314
+ if (filter["deny"] !== void 0) arrayValue(filter["deny"], `${label} deny`, nonEmptyString);
1315
+ }
1316
+ }
1317
+ function allowedModelsValue(value, label) {
1318
+ const seen = /* @__PURE__ */ new Set();
1319
+ if (arrayValue(value, label, (member, memberLabel) => {
1320
+ const route = exactRecord(member, memberLabel, ["provider", "model"]);
1321
+ nonEmptyString(route["provider"], `${memberLabel} provider`);
1322
+ nonEmptyString(route["model"], `${memberLabel} model`);
1323
+ const key = `${route["provider"]}\0${route["model"]}`;
1324
+ if (seen.has(key)) throw new SessionFormatError(`${label} repeats route ${key}`);
1325
+ seen.add(key);
1326
+ }).length === 0) throw new SessionFormatError(`${label} must be non-empty`);
1327
+ }
1328
+ function teamSelector(data, label) {
1329
+ literalValue(data["version"], [1], `${label} version`);
1330
+ nonEmptyString(data["teamId"], `${label} teamId`);
1331
+ }
1332
+ function teamMemberValue(value, label) {
1333
+ const member = exactRecord(value, label, [
1334
+ "id",
1335
+ "name",
1336
+ "description",
1337
+ "provider",
1338
+ "context",
1339
+ "phase"
1340
+ ], ["error"]);
1341
+ nonEmptyString(member["id"], `${label} id`);
1342
+ stringValue(member["name"], `${label} name`);
1343
+ stringValue(member["description"], `${label} description`);
1344
+ stringValue(member["provider"], `${label} provider`);
1345
+ literalValue(member["context"], ["fresh", "fork"], `${label} context`);
1346
+ literalValue(member["phase"], [
1347
+ "provisioning",
1348
+ "active",
1349
+ "failed"
1350
+ ], `${label} phase`);
1351
+ if (member["error"] !== void 0) stringValue(member["error"], `${label} error`);
1352
+ }
1353
+ function teamTaskValue(value, label) {
1354
+ const task = exactRecord(value, label, [
1355
+ "id",
1356
+ "revision",
1357
+ "subject",
1358
+ "description",
1359
+ "status",
1360
+ "blockedBy",
1361
+ "writeScopes"
1362
+ ], ["ownerId"]);
1363
+ nonEmptyString(task["id"], `${label} id`);
1364
+ positiveIntegerValue(task["revision"], `${label} revision`);
1365
+ stringValue(task["subject"], `${label} subject`);
1366
+ stringValue(task["description"], `${label} description`);
1367
+ literalValue(task["status"], [
1368
+ "pending",
1369
+ "in_progress",
1370
+ "completed",
1371
+ "deleted"
1372
+ ], `${label} status`);
1373
+ if (task["ownerId"] !== void 0) nonEmptyString(task["ownerId"], `${label} ownerId`);
1374
+ arrayValue(task["blockedBy"], `${label} blockedBy`, nonEmptyString);
1375
+ arrayValue(task["writeScopes"], `${label} writeScopes`, stringValue);
1376
+ }
1377
+ function teamMessageValue(value, label, version) {
1378
+ const message = exactRecord(value, label, [
1379
+ "id",
1380
+ "senderId",
1381
+ "senderName",
1382
+ "targetId",
1383
+ "delivery",
1384
+ "content"
1385
+ ]);
1386
+ for (const key of [
1387
+ "id",
1388
+ "senderId",
1389
+ "targetId"
1390
+ ]) nonEmptyString(message[key], `${label} ${key}`);
1391
+ stringValue(message["senderName"], `${label} senderName`);
1392
+ literalValue(message["delivery"], ["quiet", "wakeup"], `${label} delivery`);
1393
+ contentBlocksValue(message["content"], `${label} content`, version);
1394
+ }
1395
+ function workflowIdentity(data, label) {
1396
+ nonEmptyString(data["runId"], `${label} runId`);
1397
+ positiveIntegerValue(data["seq"], `${label} seq`);
1398
+ }
1399
+ function deepSeekSearchBodyValue(value, label) {
1400
+ const body = exactRecord(value, label, [
1401
+ "model",
1402
+ "max_tokens",
1403
+ "messages",
1404
+ "tools"
1405
+ ]);
1406
+ nonEmptyString(body["model"], `${label} model`);
1407
+ positiveIntegerValue(body["max_tokens"], `${label} max_tokens`);
1408
+ if (arrayValue(body["messages"], `${label} messages`, (member, memberLabel) => {
1409
+ const message = exactRecord(member, memberLabel, ["role", "content"]);
1410
+ literalValue(message["role"], ["user"], `${memberLabel} role`);
1411
+ if (arrayValue(message["content"], `${memberLabel} content`, (block, blockLabel) => {
1412
+ const text = exactRecord(block, blockLabel, ["type", "text"]);
1413
+ literalValue(text["type"], ["text"], `${blockLabel} type`);
1414
+ stringValue(text["text"], `${blockLabel} text`);
1415
+ }).length !== 1) throw new SessionFormatError(`${memberLabel} content must contain one text block`);
1416
+ }).length !== 1) throw new SessionFormatError(`${label} messages must contain one user message`);
1417
+ if (arrayValue(body["tools"], `${label} tools`, (member, memberLabel) => {
1418
+ const tool = exactRecord(member, memberLabel, [
1419
+ "type",
1420
+ "name",
1421
+ "max_uses"
1422
+ ]);
1423
+ literalValue(tool["type"], ["web_search_20250305"], `${memberLabel} type`);
1424
+ literalValue(tool["name"], ["web_search"], `${memberLabel} name`);
1425
+ positiveIntegerValue(tool["max_uses"], `${memberLabel} max_uses`);
1426
+ }).length !== 1) throw new SessionFormatError(`${label} tools must contain one web search tool`);
1427
+ }
1428
+ //#endregion
1429
+ //#region lib/types/validation.js
1430
+ const HEADER_REQUIRED = [
1431
+ "version",
1432
+ "id",
1433
+ "createdAt",
1434
+ "isSeeded",
1435
+ "delegationDepth"
1436
+ ];
1437
+ const HEADER_OPTIONAL = [
1438
+ "cwd",
1439
+ "parentSession",
1440
+ "origin",
1441
+ "agentPreset"
1442
+ ];
1443
+ const EVENT_REQUIRED = [
1444
+ "type",
1445
+ "seq",
1446
+ "time",
1447
+ "data"
1448
+ ];
1449
+ const SURFACE_EVENT_TYPES = new Set([
1450
+ "user/message",
1451
+ "assistant/message",
1452
+ "tool/result"
1453
+ ]);
1454
+ const SURFACE_OPTIONAL = [
1455
+ "ignorable",
1456
+ "sourceEventSeqs",
1457
+ "surfaceOp"
1458
+ ];
1459
+ const LOG_OPTIONAL = ["ignorable"];
1460
+ const LEGACY_SOURCE_TYPES = new Set([
1461
+ "steering/message",
1462
+ "request/header-delta",
1463
+ "mode/set",
1464
+ "compact/start",
1465
+ "compact/summary",
1466
+ "compact/end",
1467
+ "compact/prune"
1468
+ ]);
1469
+ /**
1470
+ * Validate the logical header shared by released v0 and v1.
1471
+ * @param header - detached logical header.
1472
+ * @param version - exact expected generation.
1473
+ */
1474
+ function assertReleasedSessionFormatHeader(header, version) {
1475
+ const record = releasedV0Record(header, `format v${version} header`);
1476
+ assertReleasedV0Keys(record, HEADER_REQUIRED, HEADER_OPTIONAL, `format v${version} header`);
1477
+ if (record["version"] !== version) throw new SessionFormatError(`expected format v${version} header`);
1478
+ if (typeof record["id"] !== "string") throw new SessionFormatError(`format v${version} header id must be a string`);
1479
+ sessionFormatCount(record["createdAt"], `format v${version} header createdAt`);
1480
+ if (typeof record["isSeeded"] !== "boolean") throw new SessionFormatError(`format v${version} header isSeeded must be a boolean`);
1481
+ sessionFormatCount(record["delegationDepth"], `format v${version} header delegationDepth`);
1482
+ for (const key of [
1483
+ "cwd",
1484
+ "parentSession",
1485
+ "agentPreset"
1486
+ ]) if (record[key] !== void 0 && typeof record[key] !== "string") throw new SessionFormatError(`format v${version} header ${key} must be a string`);
1487
+ if (typeof record["cwd"] === "string" && !isAbsolute(record["cwd"])) throw new SessionFormatError(`format v${version} header cwd must be absolute`);
1488
+ if (record["origin"] !== void 0 && record["origin"] !== "subagent") throw new SessionFormatError(`format v${version} header origin must be "subagent"`);
1489
+ }
1490
+ /**
1491
+ * Validate one released-v1 logical header.
1492
+ * @param header - detached logical header.
1493
+ */
1494
+ function assertReleasedV1Header(header) {
1495
+ assertReleasedSessionFormatHeader(header, 1);
1496
+ }
1497
+ /**
1498
+ * Restore v1 against the installed build's ordinary event vocabulary without freezing payload additions.
1499
+ * @param artifact - vocabulary-neutral released-v1 physical decode.
1500
+ * @param knownEventTypes - event types understood by the installed current Session package.
1501
+ * @returns the same validated detached artifact.
1502
+ */
1503
+ function restoreReleasedV1Artifact(artifact, knownEventTypes) {
1504
+ assertReleasedV1Header(artifact.header);
1505
+ assertReleasedArtifactCoordinates(artifact, false, knownEventTypes);
1506
+ return artifact;
1507
+ }
1508
+ /**
1509
+ * Validate released-v0/v1 artifact coordinates under an explicit vocabulary policy.
1510
+ * @param artifact - logical artifact to validate.
1511
+ * @param allowLegacySteering - whether to accept the retired steering event name.
1512
+ * @param knownEventTypes - installed event vocabulary, when vocabulary-aware.
1513
+ * @param vocabularyNeutral - whether unknown event types remain opaque.
1514
+ * @param frozenEnvelope - whether event envelopes must already be frozen.
1515
+ */
1516
+ function assertReleasedArtifactCoordinates(artifact, allowLegacySteering, knownEventTypes, vocabularyNeutral = false, frozenEnvelope = false) {
1517
+ const inheritedEventCount = sessionFormatCount(artifact.inheritedEventCount, "Session inheritedEventCount");
1518
+ if (inheritedEventCount > artifact.events.length) throw new SessionFormatError("Session inheritedEventCount exceeds its event count");
1519
+ if (!artifact.header.isSeeded && inheritedEventCount !== 0) throw new SessionFormatError("unseeded Session inheritedEventCount must be 0");
1520
+ for (let index = 0; index < artifact.events.length; index += 1) {
1521
+ const event = artifact.events[index];
1522
+ const record = releasedV0Record(event, `Session event ${index}`);
1523
+ const type = record["type"];
1524
+ if (typeof type !== "string") throw new SessionFormatError(`Session event ${index} type must be a string`);
1525
+ const disposition = RELEASED_V0_EVENT_DISPOSITIONS[type];
1526
+ const legacy = allowLegacySteering && LEGACY_SOURCE_TYPES.has(type);
1527
+ const currentKnown = knownEventTypes?.has(type) === true;
1528
+ const ignorableCurrent = !allowLegacySteering && !currentKnown && record["ignorable"] === true;
1529
+ if (!currentKnown && !legacy && !ignorableCurrent && !vocabularyNeutral) {
1530
+ if (allowLegacySteering) throw new SessionFormatUnsupportedMigrationError(`format v0 contains unknown historical event type ${JSON.stringify(type)} at seq ${index}; migration refuses unknown historical events even when ignorable`);
1531
+ throw new SessionFormatUnsupportedMigrationError(`format v1 contains unknown required event type ${JSON.stringify(type)} at seq ${index}`);
1532
+ }
1533
+ const surface = disposition !== void 0 ? SURFACE_EVENT_TYPES.has(type) : type === "steering/message";
1534
+ assertReleasedV0Keys(record, EVENT_REQUIRED, frozenEnvelope ? surface ? SURFACE_OPTIONAL : LOG_OPTIONAL : SURFACE_OPTIONAL, `Session event ${index}`);
1535
+ if (record["seq"] !== index) throw new SessionFormatError(`Session event ${index} has non-dense seq ${JSON.stringify(record["seq"])}`);
1536
+ sessionFormatSafeInteger(record["time"], `Session event ${index} time`);
1537
+ if (record["ignorable"] !== void 0 && record["ignorable"] !== true) throw new SessionFormatError(`Session event ${index} ignorable must be true when present`);
1538
+ if (frozenEnvelope && surface) assertReleasedSurfaceMetadata(record, index, type, "allow-empty-assistant");
1539
+ }
1540
+ }
1541
+ /**
1542
+ * Validate shared-layout surface references for one released generation.
1543
+ * @param record - exact event envelope.
1544
+ * @param seq - event position used for earlier-reference checks.
1545
+ * @param type - surface event type used in diagnostics.
1546
+ * @param assistantSources - whether this generation admits empty Assistant chunk provenance.
1547
+ */
1548
+ function assertReleasedSurfaceMetadata(record, seq, type, assistantSources) {
1549
+ const sources = record["sourceEventSeqs"];
1550
+ if (type === "assistant/message" && sources !== void 0 && assistantSources === "forbid-assistant") throw new SessionFormatError(`assistant/message ${seq} retains obsolete chunk provenance`);
1551
+ if (sources !== void 0) {
1552
+ if (!Array.isArray(sources)) throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be an array`);
1553
+ const seen = /* @__PURE__ */ new Set();
1554
+ for (const source of sources) {
1555
+ const current = sessionFormatCount(source, `${type} ${seq} sourceEventSeqs member`);
1556
+ if (current >= seq || seen.has(current)) throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be unique earlier seqs`);
1557
+ seen.add(current);
1558
+ }
1559
+ if (sources.length === 0 && type !== "assistant/message") throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be non-empty`);
1560
+ }
1561
+ const operation = record["surfaceOp"];
1562
+ if (operation === void 0 || operation === "append") return;
1563
+ const replacement = releasedV0Record(operation, `${type} ${seq} surfaceOp`);
1564
+ assertReleasedV0Keys(replacement, [
1565
+ "op",
1566
+ "start",
1567
+ "end"
1568
+ ], [], `${type} ${seq} surfaceOp`);
1569
+ if (replacement["op"] !== "replace") throw new SessionFormatError(`${type} ${seq} surfaceOp must replace`);
1570
+ const start = sessionFormatCount(replacement["start"], `${type} ${seq} surface start`);
1571
+ const end = sessionFormatCount(replacement["end"], `${type} ${seq} surface end`);
1572
+ if (start >= seq || end >= seq) throw new SessionFormatError(`${type} ${seq} has an invalid surface replacement`);
1573
+ }
1574
+ /**
1575
+ * Validate one exact known payload after legacy normalization.
1576
+ * @param event - known event to validate.
1577
+ * @param version - payload generation controlling versioned members.
1578
+ */
1579
+ function assertReleasedEventPayload(event, version) {
1580
+ const disposition = RELEASED_V0_EVENT_DISPOSITIONS[event.type];
1581
+ /* v8 ignore next -- artifact coordinate validation admits only the frozen inventory before payload validation. */
1582
+ if (disposition === void 0) throw new SessionFormatUnsupportedMigrationError(`format v0 contains unknown historical event type ${JSON.stringify(event.type)} at seq ${event.seq}; migration refuses unknown historical events even when ignorable`);
1583
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
1584
+ if (event.type === "subagent/descriptor" && data["version"] !== 3) {
1585
+ const descriptorVersion = sessionFormatCount(data["version"], `${event.type} ${event.seq} version`);
1586
+ if (version === 0) throw new SessionFormatUnsupportedMigrationError(`${event.type} ${event.seq} uses unsupported descriptor version ${descriptorVersion}`);
1587
+ return;
1588
+ }
1589
+ const versionOptional = version === 1 && event.type === "session-log-deepseek/delivery-accepted" ? [...disposition.optional, "sessionFormatVersion"] : disposition.optional;
1590
+ assertReleasedV0Keys(data, disposition.required, versionOptional, `${event.type} ${event.seq} data`);
1591
+ for (const key of disposition.opaque) if (Object.hasOwn(data, key) && !isJsonValue(data[key])) throw new SessionFormatError(`${event.type} ${event.seq} opaque ${key} is not lossless JSON`);
1592
+ assertReleasedPayloadSemantics(event, version);
1593
+ }
1594
+ //#endregion
1595
+ //#region lib/types/codec.js
1596
+ const PHYSICAL_HEADER_REQUIRED = [
1597
+ "type",
1598
+ "version",
1599
+ "id",
1600
+ "createdAt",
1601
+ "delegationDepth"
1602
+ ];
1603
+ const PHYSICAL_HEADER_OPTIONAL = [
1604
+ "cwd",
1605
+ "parentSession",
1606
+ "seedLength",
1607
+ "origin",
1608
+ "agentPreset"
1609
+ ];
1610
+ const PACKED_TAGS = new Set([
1611
+ "text-chunks",
1612
+ "reasoning-chunks",
1613
+ "tool-call-chunks"
1614
+ ]);
1615
+ /**
1616
+ * Test whether a compact migration item is a released packed Assistant row.
1617
+ * @param run - compact migration item to classify.
1618
+ * @returns whether the item carries the released Assistant chunk representation.
1619
+ */
1620
+ function isReleasedAssistantChunkRun(run) {
1621
+ return run.runType === "released-assistant-chunks";
1622
+ }
1623
+ /** Frozen physical JSON codec for the released v0 layout. */
1624
+ const releasedV0SessionFormatCodec = createReleasedCodec(0);
1625
+ /** Frozen physical JSON codec for the shared-layout released v1 format. */
1626
+ const releasedV1SessionFormatCodec = createReleasedCodec(1);
1627
+ function createReleasedCodec(version) {
1628
+ return Object.freeze({
1629
+ version,
1630
+ decodeHeader: (value) => decodeHeader(value, version),
1631
+ createDecoder(headerValue, recovery) {
1632
+ const physical = decodePhysicalHeader(headerValue, version);
1633
+ const scanner = scanRows(recovery === "recoverable");
1634
+ return {
1635
+ header: physical.header,
1636
+ headerInheritedEventCount: physical.inheritedEventCount,
1637
+ decodeRow: (rowValue, context) => {
1638
+ scanner.decodeRow(rowValue, context);
1639
+ },
1640
+ finish(_context) {
1641
+ scanner.finish(physical.inheritedEventCount);
1642
+ return physical.inheritedEventCount;
1643
+ }
1644
+ };
1645
+ }
1646
+ });
1647
+ }
1648
+ function scanRows(recoverable) {
1649
+ let rowIndex = 0;
1650
+ let eventCount = 0;
1651
+ let issue;
1652
+ return {
1653
+ decodeRow(rowValue, context) {
1654
+ const currentRow = rowIndex;
1655
+ rowIndex += 1;
1656
+ let packed = false;
1657
+ let decoded;
1658
+ try {
1659
+ const record = releasedV0Record(rowValue, `released Session row ${currentRow}`);
1660
+ const type = record["type"];
1661
+ if (typeof type === "string" && PACKED_TAGS.has(type)) {
1662
+ packed = true;
1663
+ decoded = decodePackedRun(record, type, currentRow);
1664
+ } else decoded = decodeEvent(record, currentRow);
1665
+ } catch (error) {
1666
+ const current = error instanceof SessionFormatError ? error : new SessionFormatError(`released Session row ${currentRow} is malformed`, { cause: error });
1667
+ if (!recoverable) throw current;
1668
+ issue ??= current;
1669
+ return;
1670
+ }
1671
+ if (issue !== void 0) {
1672
+ if (!packed && decoded.type === "turn/end") throw issue;
1673
+ return;
1674
+ }
1675
+ const seq = packed ? decoded.firstSeq : decoded.seq;
1676
+ if (seq !== eventCount) {
1677
+ const gap = new SessionFormatError(`released Session row ${currentRow} has seq gap (expected ${eventCount}, got ${seq})`);
1678
+ if (!recoverable) throw gap;
1679
+ issue = gap;
1680
+ if (!packed && decoded.type === "turn/end") throw gap;
1681
+ return;
1682
+ }
1683
+ if (packed) {
1684
+ const run = decoded;
1685
+ eventCount += run.eventCount;
1686
+ context.emitRun(run);
1687
+ } else {
1688
+ eventCount += 1;
1689
+ context.emitEvent(decoded);
1690
+ }
1691
+ },
1692
+ finish(inheritedEventCount) {
1693
+ if (inheritedEventCount > eventCount) throw new SessionFormatError("Session inheritedEventCount exceeds its event count");
1694
+ }
1695
+ };
1696
+ }
1697
+ function decodeHeader(value, version) {
1698
+ return decodePhysicalHeader(value, version).header;
1699
+ }
1700
+ function decodePhysicalHeader(value, version) {
1701
+ const record = releasedV0Record(snapshotSessionFormatJson(value, `released v${version} physical header`), `released v${version} physical header`);
1702
+ assertReleasedV0Keys(record, PHYSICAL_HEADER_REQUIRED, PHYSICAL_HEADER_OPTIONAL, `released v${version} physical header`);
1703
+ if (record["type"] !== "session" || record["version"] !== version) throw new SessionFormatError(`expected released v${version} physical Session header`);
1704
+ if (typeof record["id"] !== "string") throw new SessionFormatError(`released v${version} header id must be a string`);
1705
+ const createdAt = sessionFormatCount(record["createdAt"], `released v${version} header createdAt`);
1706
+ const delegationDepth = sessionFormatCount(record["delegationDepth"], `released v${version} header delegationDepth`);
1707
+ const seedLength = record["seedLength"] === void 0 ? 0 : sessionFormatCount(record["seedLength"], `released v${version} header seedLength`);
1708
+ for (const key of [
1709
+ "cwd",
1710
+ "parentSession",
1711
+ "agentPreset"
1712
+ ]) if (record[key] !== void 0 && typeof record[key] !== "string") throw new SessionFormatError(`released v${version} header ${key} must be a string`);
1713
+ if (record["origin"] !== void 0 && record["origin"] !== "subagent") throw new SessionFormatError(`released v${version} header origin must be "subagent"`);
1714
+ const header = {
1715
+ version,
1716
+ id: record["id"],
1717
+ createdAt,
1718
+ ...record["cwd"] === void 0 ? {} : { cwd: record["cwd"] },
1719
+ ...record["parentSession"] === void 0 ? {} : { parentSession: record["parentSession"] },
1720
+ isSeeded: record["seedLength"] !== void 0,
1721
+ ...record["origin"] === void 0 ? {} : { origin: record["origin"] },
1722
+ delegationDepth,
1723
+ ...record["agentPreset"] === void 0 ? {} : { agentPreset: record["agentPreset"] }
1724
+ };
1725
+ assertReleasedSessionFormatHeader(header, version);
1726
+ return {
1727
+ header,
1728
+ inheritedEventCount: seedLength
1729
+ };
1730
+ }
1731
+ function decodeEvent(record, rowIndex) {
1732
+ if (record["sourceEventSeqs"] !== void 0) {
1733
+ const seq = sessionFormatCount(record["seq"], `released Session row ${rowIndex} seq`);
1734
+ return {
1735
+ ...record,
1736
+ sourceEventSeqs: decodeSeqRanges(record["sourceEventSeqs"], seq)
1737
+ };
1738
+ }
1739
+ return record;
1740
+ }
1741
+ function decodePackedRun(row, type, rowIndex) {
1742
+ const label = `released ${type} row ${rowIndex}`;
1743
+ assertReleasedV0Keys(row, [
1744
+ "type",
1745
+ "seq0",
1746
+ "time0",
1747
+ "data"
1748
+ ], [], label);
1749
+ const seq0 = sessionFormatCount(row["seq0"], `${label} seq0`);
1750
+ const time0 = sessionFormatSafeInteger(row["time0"], `${label} time0`);
1751
+ const data = releasedV0Record(row["data"], `${label} data`);
1752
+ const isTool = type === "tool-call-chunks";
1753
+ assertReleasedV0Keys(data, isTool ? [
1754
+ "turn",
1755
+ "step",
1756
+ "index",
1757
+ "id",
1758
+ "dt",
1759
+ "args"
1760
+ ] : [
1761
+ "turn",
1762
+ "step",
1763
+ "index",
1764
+ "dt",
1765
+ "texts"
1766
+ ], isTool ? ["name"] : [], `${label} data`);
1767
+ const payload = data[isTool ? "args" : "texts"];
1768
+ if (!Array.isArray(payload) || payload.length === 0 || payload.some((member) => typeof member !== "string")) throw new SessionFormatError(`${label} payload must be a non-empty string array`);
1769
+ const gaps = data["dt"];
1770
+ if (!Array.isArray(gaps) || gaps.length !== payload.length - 1) throw new SessionFormatError(`${label} dt length must match its payload`);
1771
+ let lastTime = time0;
1772
+ for (const gap of gaps) {
1773
+ const validGap = sessionFormatSafeInteger(gap, `${label} dt member`);
1774
+ lastTime = sessionFormatSafeInteger(lastTime + validGap, `${label} member time`);
1775
+ }
1776
+ const turn = sessionFormatCount(data["turn"], `${label} turn`);
1777
+ const step = sessionFormatCount(data["step"], `${label} step`);
1778
+ const chunkIndex = sessionFormatCount(data["index"], `${label} index`);
1779
+ if (isTool && (typeof data["id"] !== "string" || data["id"].length === 0 || data["name"] !== void 0 && typeof data["name"] !== "string")) throw new SessionFormatError(`${label} id and optional name must be strings`);
1780
+ const lastSeq = sessionFormatCount(seq0 + payload.length - 1, `${label} final seq`);
1781
+ const stream = type === "tool-call-chunks" ? {
1782
+ type,
1783
+ time0,
1784
+ index: chunkIndex,
1785
+ dt: gaps,
1786
+ id: data["id"],
1787
+ ...data["name"] === void 0 ? {} : { name: data["name"] },
1788
+ args: payload
1789
+ } : {
1790
+ type,
1791
+ time0,
1792
+ index: chunkIndex,
1793
+ dt: gaps,
1794
+ texts: payload
1795
+ };
1796
+ const run = {
1797
+ runType: "released-assistant-chunks",
1798
+ firstSeq: seq0,
1799
+ eventCount: payload.length,
1800
+ turn,
1801
+ step,
1802
+ lastSeq,
1803
+ lastTime,
1804
+ stream,
1805
+ expand: () => expandAssistantChunkRun(run)
1806
+ };
1807
+ return run;
1808
+ }
1809
+ function* expandAssistantChunkRun(run) {
1810
+ const stream = run.stream;
1811
+ const gaps = stream["dt"];
1812
+ const members = stream["type"] === "tool-call-chunks" ? stream["args"] : stream["texts"];
1813
+ let time = run.stream["time0"];
1814
+ for (let index = 0; index < members.length; index += 1) {
1815
+ if (index > 0) time += gaps[index - 1];
1816
+ const member = members[index];
1817
+ const chunk = stream["type"] === "text-chunks" ? {
1818
+ type: "text-delta",
1819
+ index: stream["index"],
1820
+ text: member
1821
+ } : stream["type"] === "reasoning-chunks" ? {
1822
+ type: "reasoning-delta",
1823
+ index: stream["index"],
1824
+ text: member
1825
+ } : {
1826
+ type: "tool-call-delta",
1827
+ index: stream["index"],
1828
+ id: stream["id"],
1829
+ ...stream["name"] === void 0 ? {} : { name: stream["name"] },
1830
+ argumentsDelta: member
1831
+ };
1832
+ yield {
1833
+ type: "assistant/chunk",
1834
+ seq: run.firstSeq + index,
1835
+ time,
1836
+ data: {
1837
+ turn: run.turn,
1838
+ step: run.step,
1839
+ chunk
1840
+ }
1841
+ };
1842
+ }
1843
+ }
1844
+ function decodeSeqRanges(value, maxEntries) {
1845
+ if (!Array.isArray(value)) throw new SessionFormatError("sourceEventSeqs must be an array");
1846
+ const output = [];
1847
+ let hasRange = false;
1848
+ for (const entry of value) {
1849
+ if (typeof entry === "number") {
1850
+ if (output.length >= maxEntries) throw new SessionFormatError("sourceEventSeqs exceeds its event seq");
1851
+ output.push(sessionFormatCount(entry, "sourceEventSeqs member"));
1852
+ continue;
1853
+ }
1854
+ if (!Array.isArray(entry) || entry.length !== 2) throw new SessionFormatError("sourceEventSeqs range must be a [start, end] pair");
1855
+ const start = sessionFormatCount(entry[0], "sourceEventSeqs range start");
1856
+ const end = sessionFormatCount(entry[1], "sourceEventSeqs range end");
1857
+ if (end < start || end - start + 1 > maxEntries - output.length) throw new SessionFormatError("sourceEventSeqs range exceeds its event seq");
1858
+ for (let seq = start; seq <= end; seq += 1) output.push(seq);
1859
+ hasRange = true;
1860
+ }
1861
+ if (hasRange && output.some((member, index) => index > 0 && member <= output[index - 1])) throw new SessionFormatError("sourceEventSeqs ranges must be strictly increasing");
1862
+ return output;
1863
+ }
1864
+ //#endregion
1865
+ //#region lib/types/migration.js
1866
+ /** Identity format edge that promotes released v0 into released v1. */
1867
+ const sessionFormatV0ToV1 = defineSessionFormatMigration({
1868
+ name: "@deepseek-ai/dsh-session-format-v0-to-v1",
1869
+ fromVersion: 0,
1870
+ toVersion: 1,
1871
+ migrateHeader(header) {
1872
+ assertHeaderVersion(header, 0);
1873
+ return {
1874
+ ...header,
1875
+ version: 1
1876
+ };
1877
+ },
1878
+ createStage(input) {
1879
+ return new ReleasedV0ToV1Stage(input);
1880
+ },
1881
+ validateTargetHeader: assertReleasedV1Header
1882
+ });
1883
+ var ReleasedV0ToV1Stage = class {
1884
+ input;
1885
+ headerInheritedEventCount;
1886
+ state = {
1887
+ messageIds: /* @__PURE__ */ new Map(),
1888
+ retryIds: /* @__PURE__ */ new Map()
1889
+ };
1890
+ constructor(input) {
1891
+ this.input = input;
1892
+ assertHeaderVersion(input.sourceHeader, 0);
1893
+ this.headerInheritedEventCount = input.sourceInheritedEventCount;
1894
+ }
1895
+ transformEvent(event, context) {
1896
+ const normalized = normalizeReleasedV0Event(event, this.input.sourceHeader.id, this.state);
1897
+ assertSourceDeliveryMarker(normalized, this.input);
1898
+ context.emitEvent(normalized);
1899
+ }
1900
+ transformRun(run, context) {
1901
+ if (isReleasedAssistantChunkRun(run)) {
1902
+ context.emitRun(run);
1903
+ return;
1904
+ }
1905
+ for (const event of run.expand()) this.transformEvent(event, context);
1906
+ }
1907
+ finish(_context) {
1908
+ return this.input.sourceInheritedEventCount;
1909
+ }
1910
+ };
1911
+ function assertHeaderVersion(header, version) {
1912
+ if (header.version !== version) throw new SessionFormatError(`expected format v${version} header`);
1913
+ }
1914
+ function normalizeReleasedV0Event(event, sessionId, state) {
1915
+ const named = normalizeLegacyCompactionType(event);
1916
+ assertSupportedLegacyType(named, sessionId);
1917
+ const message = normalizeLegacyMessage(normalizeLegacyCompaction(normalizeLegacyRetry(normalizeLegacySteering(normalizeLegacyRequestHeader(normalizeLegacyTurnEnd(normalizeLegacyTurnStart(named, sessionId), sessionId), sessionId), sessionId), sessionId, state.retryIds), sessionId, state), sessionId, state.messageIds);
1918
+ if (message.type !== "assistant/chunk") assertReleasedEventPayload(message, 0);
1919
+ const messageId = eventMessageId(message);
1920
+ if (messageId !== void 0) state.messageIds.set(message.seq, messageId);
1921
+ return message;
1922
+ }
1923
+ function normalizeLegacyCompactionType(event) {
1924
+ switch (event.type) {
1925
+ case "compact/start": return {
1926
+ ...event,
1927
+ type: "compaction/start"
1928
+ };
1929
+ case "compact/summary": return {
1930
+ ...event,
1931
+ type: "compaction/summary"
1932
+ };
1933
+ case "compact/end": return {
1934
+ ...event,
1935
+ type: "compaction/end"
1936
+ };
1937
+ case "compact/prune": return {
1938
+ ...event,
1939
+ type: "compaction/prune"
1940
+ };
1941
+ default: return event;
1942
+ }
1943
+ }
1944
+ function assertSourceDeliveryMarker(event, input) {
1945
+ if (event.type !== "session-log-deepseek/delivery-accepted") return;
1946
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
1947
+ const acceptedVersion = data["sessionFormatVersion"] ?? 0;
1948
+ const inherited = input.sourceHeader.parentSession !== void 0 && event.seq < input.sourceInheritedEventCount;
1949
+ if (acceptedVersion === 0 && !inherited && data["sessionId"] !== input.sourceHeader.id) throw new SessionFormatError("current-generation delivery marker names the wrong Session");
1950
+ }
1951
+ function normalizeLegacyRetry(event, sessionId, retryIds) {
1952
+ if (event.type !== "llm/retry") return event;
1953
+ const data = releasedV0Record(event.data, `llm/retry ${event.seq} data`);
1954
+ const chain = [
1955
+ data["turn"],
1956
+ data["step"],
1957
+ data["provider"],
1958
+ data["policyKey"]
1959
+ ].map((value) => JSON.stringify(value)).join("\0");
1960
+ const retryId = data["retryId"];
1961
+ if (typeof retryId === "string" && retryId.length > 0) {
1962
+ retryIds.set(chain, retryId);
1963
+ return event;
1964
+ }
1965
+ if (Object.hasOwn(data, "retryId")) return event;
1966
+ const migratedRetryId = retryIds.get(chain) ?? `legacy-retry:${sessionId}:${event.seq}`;
1967
+ retryIds.set(chain, migratedRetryId);
1968
+ return {
1969
+ ...event,
1970
+ data: {
1971
+ ...data,
1972
+ retryId: migratedRetryId
1973
+ }
1974
+ };
1975
+ }
1976
+ function normalizeLegacyCompaction(event, sessionId, state) {
1977
+ if (event.type === "session/end-seed") {
1978
+ delete state.compactionId;
1979
+ return event;
1980
+ }
1981
+ if (event.type === "compaction/start") {
1982
+ const data = releasedV0Record(event.data, `compaction/start ${event.seq} data`);
1983
+ const existing = data["compactionId"];
1984
+ if (typeof existing === "string" && existing.length > 0) {
1985
+ state.compactionId = existing;
1986
+ return event;
1987
+ }
1988
+ if (Object.hasOwn(data, "compactionId")) return event;
1989
+ const id = `legacy-compaction:${sessionId}:${event.seq}`;
1990
+ state.compactionId = id;
1991
+ return {
1992
+ ...event,
1993
+ data: {
1994
+ ...data,
1995
+ compactionId: id
1996
+ }
1997
+ };
1998
+ }
1999
+ const compactionId = state.compactionId;
2000
+ if (compactionId === void 0) return event;
2001
+ if (event.type === "compaction/summary" || event.type === "compaction/end") {
2002
+ const normalized = addLegacyCompactionId(event, compactionId);
2003
+ if (event.type === "compaction/end") delete state.compactionId;
2004
+ return normalized;
2005
+ }
2006
+ if (event.type !== "user/message") return event;
2007
+ const data = releasedV0Record(event.data, `user/message ${event.seq} data`);
2008
+ const source = data["source"];
2009
+ if (!releasedIsRecord(source) || source["kind"] !== "plugin" || source["plugin"] !== "compact" || Object.hasOwn(source, "compactionId")) return event;
2010
+ return {
2011
+ ...event,
2012
+ data: {
2013
+ ...data,
2014
+ source: {
2015
+ ...source,
2016
+ compactionId
2017
+ }
2018
+ }
2019
+ };
2020
+ }
2021
+ function addLegacyCompactionId(event, compactionId) {
2022
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
2023
+ if (Object.hasOwn(data, "compactionId")) return event;
2024
+ return {
2025
+ ...event,
2026
+ data: {
2027
+ ...data,
2028
+ compactionId
2029
+ }
2030
+ };
2031
+ }
2032
+ function normalizeLegacyRequestHeader(event, sessionId) {
2033
+ if (event.type !== "request/header") return event;
2034
+ const data = releasedV0Record(event.data, `request/header ${event.seq} data`);
2035
+ const header = releasedV0Record(data["header"], `request/header ${event.seq} header`);
2036
+ if (!Object.hasOwn(header, "messagePrefix")) return event;
2037
+ if (!Array.isArray(header["messagePrefix"])) throw new SessionFormatError(`session ${JSON.stringify(sessionId)} contains malformed request/header messagePrefix at seq ${event.seq}`);
2038
+ const { messagePrefix: _messagePrefix, ...currentHeader } = header;
2039
+ return {
2040
+ ...event,
2041
+ data: {
2042
+ ...data,
2043
+ header: currentHeader
2044
+ }
2045
+ };
2046
+ }
2047
+ function assertSupportedLegacyType(event, sessionId) {
2048
+ if (event.type === "request/header-delta" || event.type === "mode/set") throw new SessionFormatUnsupportedMigrationError(`session ${JSON.stringify(sessionId)} contains unsupported legacy ${event.type} event at seq ${event.seq}`);
2049
+ if (event.type === "request/header") {
2050
+ if (releasedV0Record(event.data, `request/header ${event.seq} data`)["reason"] === "fallback") throw new SessionFormatUnsupportedMigrationError(`session ${JSON.stringify(sessionId)} contains unsupported request/header reason "fallback" at seq ${event.seq}`);
2051
+ }
2052
+ }
2053
+ function normalizeLegacySteering(event, sessionId) {
2054
+ if (event.type !== "steering/message") return event;
2055
+ const data = releasedV0Record(event.data, `steering/message ${event.seq} data`);
2056
+ const wrapped = data["message"];
2057
+ if (wrapped !== void 0) {
2058
+ assertReleasedV0Keys(data, ["turn", "message"], [], `steering/message ${event.seq} data`);
2059
+ sessionFormatCount(data["turn"], `steering/message ${event.seq} turn`);
2060
+ return {
2061
+ ...event,
2062
+ type: "user/message",
2063
+ data: wrapped
2064
+ };
2065
+ }
2066
+ assertReleasedV0Keys(data, [
2067
+ "turn",
2068
+ "content",
2069
+ "source"
2070
+ ], [], `steering/message ${event.seq} data`);
2071
+ sessionFormatCount(data["turn"], `steering/message ${event.seq} turn`);
2072
+ const { turn: _turn, ...message } = data;
2073
+ return {
2074
+ ...event,
2075
+ type: "user/message",
2076
+ data: {
2077
+ ...message,
2078
+ id: legacyMessageId(sessionId, event.seq),
2079
+ role: "user"
2080
+ }
2081
+ };
2082
+ }
2083
+ function normalizeLegacyTurnStart(event, sessionId) {
2084
+ if (event.type !== "turn/start") return event;
2085
+ const data = releasedV0Record(event.data, `turn/start ${event.seq} data`);
2086
+ if (!Object.hasOwn(data, "trigger")) return event;
2087
+ assertReleasedV0Keys(data, ["turn", "trigger"], [], `turn/start ${event.seq} data`);
2088
+ const turn = sessionFormatCount(data["turn"], `turn/start ${event.seq} turn`);
2089
+ const trigger = releasedV0Record(data["trigger"], `turn/start ${event.seq} trigger`);
2090
+ if (turn < 1 || typeof trigger["kind"] !== "string" || trigger["kind"].length === 0) throw malformedLegacy(sessionId, "turn/start", event.seq);
2091
+ return {
2092
+ ...event,
2093
+ data: { turn }
2094
+ };
2095
+ }
2096
+ function normalizeLegacyTurnEnd(event, sessionId) {
2097
+ if (event.type !== "turn/end") return event;
2098
+ const data = releasedV0Record(event.data, `turn/end ${event.seq} data`);
2099
+ assertReleasedV0Keys(data, ["turn", "reason"], [], `turn/end ${event.seq} data`);
2100
+ if (sessionFormatCount(data["turn"], `turn/end ${event.seq} turn`) < 1) throw malformedLegacy(sessionId, "turn/end", event.seq);
2101
+ const reason = releasedV0Record(data["reason"], `turn/end ${event.seq} reason`);
2102
+ if (typeof reason["kind"] !== "string") throw malformedLegacy(sessionId, "turn/end", event.seq);
2103
+ let current;
2104
+ switch (reason["kind"]) {
2105
+ case "completed":
2106
+ case "blocked":
2107
+ case "max-tokens":
2108
+ case "interrupted":
2109
+ assertReleasedV0Keys(reason, ["kind"], [], `turn/end ${event.seq} reason`);
2110
+ return event;
2111
+ case "aborted":
2112
+ if (Object.hasOwn(reason, "reason")) return event;
2113
+ assertReleasedV0Keys(reason, ["kind"], [], `turn/end ${event.seq} reason`);
2114
+ current = {
2115
+ kind: "aborted",
2116
+ reason: { kind: "legacy" }
2117
+ };
2118
+ break;
2119
+ case "disposed":
2120
+ assertReleasedV0Keys(reason, ["kind"], [], `turn/end ${event.seq} reason`);
2121
+ current = {
2122
+ kind: "aborted",
2123
+ reason: { kind: "disposed" }
2124
+ };
2125
+ break;
2126
+ case "error":
2127
+ if (Object.hasOwn(reason, "error")) return event;
2128
+ current = normalizeLegacyErrorReason(reason, event.seq, sessionId);
2129
+ break;
2130
+ default: return event;
2131
+ }
2132
+ return {
2133
+ ...event,
2134
+ data: {
2135
+ ...data,
2136
+ reason: current
2137
+ }
2138
+ };
2139
+ }
2140
+ function normalizeLegacyErrorReason(reason, seq, sessionId) {
2141
+ sessionFormatCount(reason["step"], `turn/end ${seq} error step`);
2142
+ const failure = reason["failure"];
2143
+ if (failure !== void 0) {
2144
+ assertReleasedV0Keys(reason, [
2145
+ "kind",
2146
+ "step",
2147
+ "failure"
2148
+ ], [], `turn/end ${seq} reason`);
2149
+ const record = releasedV0Record(failure, `turn/end ${seq} failure`);
2150
+ assertReleasedV0Keys(record, ["message", "code"], [
2151
+ "status",
2152
+ "providerRetryAfterMs",
2153
+ "requestId"
2154
+ ], `turn/end ${seq} failure`);
2155
+ if (typeof record["message"] !== "string" || typeof record["code"] !== "string") throw malformedLegacy(sessionId, "turn/end", seq);
2156
+ return {
2157
+ kind: "error",
2158
+ error: record
2159
+ };
2160
+ }
2161
+ assertReleasedV0Keys(reason, [
2162
+ "kind",
2163
+ "step",
2164
+ "message"
2165
+ ], ["code"], `turn/end ${seq} reason`);
2166
+ if (typeof reason["message"] !== "string" || reason["code"] !== void 0 && typeof reason["code"] !== "string") throw malformedLegacy(sessionId, "turn/end", seq);
2167
+ return {
2168
+ kind: "error",
2169
+ error: {
2170
+ message: reason["message"],
2171
+ code: typeof reason["code"] === "string" ? reason["code"] : "UNKNOWN"
2172
+ }
2173
+ };
2174
+ }
2175
+ function normalizeLegacyMessage(event, sessionId, messageIds) {
2176
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
2177
+ switch (event.type) {
2178
+ case "user/message":
2179
+ if (Object.hasOwn(data, "id") || Object.hasOwn(data, "role") || Object.hasOwn(data, "message") || !Object.hasOwn(data, "content") || !Object.hasOwn(data, "source")) return event;
2180
+ return {
2181
+ ...event,
2182
+ data: {
2183
+ ...data,
2184
+ id: legacyMessageId(sessionId, event.seq),
2185
+ role: "user"
2186
+ }
2187
+ };
2188
+ case "assistant/message": {
2189
+ if (Object.hasOwn(data, "message") || !Object.hasOwn(data, "content") || !Object.hasOwn(data, "provenance")) return event;
2190
+ const { content, provenance, ...eventData } = data;
2191
+ const source = releasedV0Record(provenance, `assistant/message ${event.seq} provenance`);
2192
+ return {
2193
+ ...event,
2194
+ data: {
2195
+ ...eventData,
2196
+ message: {
2197
+ id: legacyMessageId(sessionId, event.seq),
2198
+ role: "assistant",
2199
+ content,
2200
+ source: {
2201
+ ...source,
2202
+ kind: "model"
2203
+ }
2204
+ }
2205
+ }
2206
+ };
2207
+ }
2208
+ case "tool/result": {
2209
+ if (Object.hasOwn(data, "message") || !Object.hasOwn(data, "callId") || !Object.hasOwn(data, "content") || !Object.hasOwn(data, "isError")) return event;
2210
+ const { callId, content, isError, ...eventData } = data;
2211
+ if (typeof callId !== "string" || typeof isError !== "boolean" || content === void 0) return event;
2212
+ const inheritedId = replacementStart(event);
2213
+ const messageId = inheritedId === void 0 ? legacyMessageId(sessionId, event.seq) : messageIds.get(inheritedId);
2214
+ if (messageId === void 0) throw new SessionFormatError(`tool/result ${event.seq} replacement cites a message without identity`);
2215
+ return {
2216
+ ...event,
2217
+ data: {
2218
+ ...eventData,
2219
+ message: {
2220
+ id: messageId,
2221
+ role: "user",
2222
+ content: [{
2223
+ type: "tool-result",
2224
+ toolCallId: callId,
2225
+ content,
2226
+ isError
2227
+ }],
2228
+ source: {
2229
+ kind: "tool",
2230
+ callId
2231
+ }
2232
+ }
2233
+ }
2234
+ };
2235
+ }
2236
+ default: return event;
2237
+ }
2238
+ }
2239
+ function replacementStart(event) {
2240
+ const operation = event["surfaceOp"];
2241
+ if (operation === void 0 || !releasedIsRecord(operation) || operation["op"] !== "replace") return void 0;
2242
+ return operation["start"];
2243
+ }
2244
+ function eventMessageId(event) {
2245
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
2246
+ const message = event.type === "user/message" ? data : releasedIsRecord(data["message"]) ? data["message"] : void 0;
2247
+ return typeof message?.["id"] === "string" ? message["id"] : void 0;
2248
+ }
2249
+ function releasedIsRecord(value) {
2250
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2251
+ }
2252
+ function legacyMessageId(sessionId, seq) {
2253
+ return `legacy-message:${sessionId}:${seq}`;
2254
+ }
2255
+ function malformedLegacy(sessionId, type, seq) {
2256
+ return new SessionFormatError(`session ${JSON.stringify(sessionId)} contains malformed pre-react-loop ${type} at seq ${seq}`);
2257
+ }
2258
+ //#endregion
2259
+ //#region lib/types/relationships.js
2260
+ const SURFACE_TYPES = new Set([
2261
+ "user/message",
2262
+ "assistant/message",
2263
+ "tool/result"
2264
+ ]);
2265
+ /**
2266
+ * Validate cross-event relationships required to construct one current Session safely.
2267
+ * @param artifact - complete normalized v0 or exact current v1 artifact.
2268
+ * @param extensions - later-generation event roles interpreted by the calling format owner.
2269
+ */
2270
+ function assertReleasedArtifactRelationships(artifact, extensions = {}) {
2271
+ let openTurn = null;
2272
+ let openStep = null;
2273
+ let openStepProvider;
2274
+ let nextTurn = 1;
2275
+ let nextStep = 1;
2276
+ let surface = [];
2277
+ let openCompaction;
2278
+ const staleCompactionStarts = inheritedOrphanCompactionStarts(artifact.events);
2279
+ const retries = [];
2280
+ const retryStarts = /* @__PURE__ */ new Set();
2281
+ const ptcRoots = /* @__PURE__ */ new Map();
2282
+ const ptcStarts = /* @__PURE__ */ new Map();
2283
+ const toolLifecycles = /* @__PURE__ */ new Map();
2284
+ const commandRuns = /* @__PURE__ */ new Set();
2285
+ for (const event of artifact.events) {
2286
+ const extensionStepEvent = extensions.stepEvents?.has(event.type) === true;
2287
+ if (RELEASED_V0_EVENT_DISPOSITIONS[event.type] === void 0 && !extensionStepEvent) continue;
2288
+ const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
2289
+ if (SURFACE_TYPES.has(event.type)) surface = applySurface(surface, event);
2290
+ if ((event.type === "turn/start" || event.type === "turn/end") && openCompaction !== void 0 && !staleCompactionStarts.has(openCompaction.startSeq)) throw new SessionFormatError(`${event.type} crosses an open compaction`);
2291
+ if (extensionStepEvent) {
2292
+ requireOpenStep(event, data, openTurn, openStep);
2293
+ continue;
2294
+ }
2295
+ switch (event.type) {
2296
+ case "turn/start": {
2297
+ const previous = artifact.events[event.seq - 1];
2298
+ if (extensions.legacyInterruptedTurnRestart === true && openTurn !== null && openStep === null && data["turn"] === openTurn + 1 && nextTurn === openTurn && previous?.type === "agent/inbox/spliced") {
2299
+ const splice = releasedV0Record(previous.data, `agent/inbox/spliced ${previous.seq} data`);
2300
+ if (splice["target"] === "next-turn" && Array.isArray(splice["inserted"]) && splice["inserted"].length > 0) {
2301
+ openTurn = null;
2302
+ nextTurn += 1;
2303
+ }
2304
+ }
2305
+ if (openTurn !== null || data["turn"] !== nextTurn) throw new SessionFormatError(`turn/start ${JSON.stringify(data["turn"])} does not open expected turn ${nextTurn}`);
2306
+ openTurn = data["turn"];
2307
+ openStep = null;
2308
+ toolLifecycles.clear();
2309
+ nextStep = 1;
2310
+ break;
2311
+ }
2312
+ case "turn/end":
2313
+ if (openTurn !== data["turn"]) throw new SessionFormatError(`turn/end ${JSON.stringify(data["turn"])} has no matching open turn`);
2314
+ assertNoUnresolvedTools(toolLifecycles, "turn/end");
2315
+ if (openStep !== null) throw new SessionFormatError(`turn/end ${JSON.stringify(data["turn"])} crosses an open step`);
2316
+ openTurn = null;
2317
+ nextTurn += 1;
2318
+ break;
2319
+ case "step/start":
2320
+ if (openTurn !== data["turn"] || openStep !== null || data["step"] !== nextStep) throw new SessionFormatError(`${event.type} does not match the open turn and next step`);
2321
+ openStep = data["step"];
2322
+ break;
2323
+ case "step/end":
2324
+ requireOpenStep(event, data, openTurn, openStep);
2325
+ assertNoUnresolvedTools(toolLifecycles, "step/end");
2326
+ toolLifecycles.clear();
2327
+ openStep = null;
2328
+ nextStep += 1;
2329
+ break;
2330
+ case "assistant/chunk":
2331
+ requireOpenStep(event, data, openTurn, openStep);
2332
+ break;
2333
+ case "assistant/message": {
2334
+ requireOpenStep(event, data, openTurn, openStep);
2335
+ const content = releasedV0Record(data["message"], `assistant/message ${event.seq} message`)["content"];
2336
+ for (const block of content) {
2337
+ if (block["type"] !== "tool-call") continue;
2338
+ const callId = block["id"];
2339
+ if (toolLifecycles.has(callId)) throw new SessionFormatError(`assistant/message repeats advertised tool call ${callId}`);
2340
+ toolLifecycles.set(callId, {
2341
+ name: block["name"],
2342
+ arguments: block["arguments"],
2343
+ state: "advertised"
2344
+ });
2345
+ }
2346
+ break;
2347
+ }
2348
+ case "tool/call": {
2349
+ requireOpenStep(event, data, openTurn, openStep);
2350
+ const callId = data["callId"];
2351
+ const lifecycle = toolLifecycles.get(callId);
2352
+ if (lifecycle === void 0 || lifecycle.state !== "advertised" || lifecycle.name !== data["name"] || lifecycle.arguments !== data["arguments"]) throw new SessionFormatError(`tool/call ${callId} does not match one advertised tool call`);
2353
+ lifecycle.state = "started";
2354
+ break;
2355
+ }
2356
+ case "tool/result":
2357
+ if (event["surfaceOp"] === "append") {
2358
+ requireOpenStep(event, data, openTurn, openStep);
2359
+ const message = releasedV0Record(data["message"], `tool/result ${event.seq} message`);
2360
+ const callId = releasedV0Record(message["source"], `tool/result ${event.seq} source`)["callId"];
2361
+ const content = message["content"];
2362
+ const error = data["error"] === void 0 ? void 0 : releasedV0Record(data["error"], `tool/result ${event.seq} error`);
2363
+ const lifecycle = toolLifecycles.get(callId);
2364
+ if (lifecycle === void 0) throw new SessionFormatError(`tool/result ${callId} has no advertised tool lifecycle`);
2365
+ if (lifecycle.state === "advertised" && !isExactToolNotStartedRepair(event, content, error)) throw new SessionFormatError(`tool/result ${callId} is not the exact TOOL_NOT_STARTED repair`);
2366
+ toolLifecycles.delete(callId);
2367
+ } else if (openTurn === null) throw new SessionFormatError("tool/result replacement is outside an open turn");
2368
+ break;
2369
+ case "request/header":
2370
+ if (openTurn === null) throw new SessionFormatError(`${event.type} is outside an open turn`);
2371
+ openStepProvider = data["header"]["config"]["provider"];
2372
+ break;
2373
+ case "request/context":
2374
+ if (openTurn === null) throw new SessionFormatError(`${event.type} is outside an open turn`);
2375
+ break;
2376
+ case "tool/code-dispatch-start":
2377
+ case "tool/code-dispatch": {
2378
+ if (openTurn === null) throw new SessionFormatError(`${event.type} is outside an open turn`);
2379
+ const root = data["rootCallId"];
2380
+ const parent = data["parentCallId"];
2381
+ const child = data["subCallId"];
2382
+ const known = ptcRoots.get(child);
2383
+ if (known !== void 0 && known !== root) throw new SessionFormatError(`${event.type} changes its rootCallId`);
2384
+ if (parent !== root && ptcRoots.get(parent) !== root) throw new SessionFormatError(`${event.type} parentCallId does not belong to rootCallId`);
2385
+ if (event.type === "tool/code-dispatch-start") {
2386
+ if (ptcStarts.has(child)) throw new SessionFormatError("tool/code-dispatch-start repeats subCallId");
2387
+ ptcStarts.set(child, {
2388
+ root,
2389
+ parent,
2390
+ name: data["name"],
2391
+ arguments: data["arguments"],
2392
+ settled: false
2393
+ });
2394
+ } else {
2395
+ const start = ptcStarts.get(child);
2396
+ if (start === void 0 || start.settled) throw new SessionFormatError("tool/code-dispatch has no unique start");
2397
+ if (start.root !== root || start.parent !== parent || start.name !== data["name"] || !deepEqualJson(start.arguments, data["arguments"])) throw new SessionFormatError("tool/code-dispatch does not match its start");
2398
+ start.settled = true;
2399
+ }
2400
+ ptcRoots.set(child, root);
2401
+ break;
2402
+ }
2403
+ case "llm/retry":
2404
+ if (openTurn !== data["turn"] || data["step"] !== (openStep ?? nextStep - 1) || openTurn === null) throw new SessionFormatError("llm/retry does not match the current turn and step");
2405
+ if (data["provider"] !== openStepProvider) throw new SessionFormatError("llm/retry provider does not match the open request/header");
2406
+ assertRetryChain(retries, data);
2407
+ retries.push(event);
2408
+ break;
2409
+ case "llm/retry-started": {
2410
+ const scheduled = retries.find((candidate) => {
2411
+ const prior = candidate.data;
2412
+ return prior["retryId"] === data["retryId"] && prior["retry"] === data["retry"];
2413
+ });
2414
+ if (scheduled === void 0) throw new SessionFormatError("llm/retry-started pairs no prior scheduled attempt");
2415
+ const prior = scheduled.data;
2416
+ if (prior["turn"] !== data["turn"] || prior["step"] !== data["step"]) throw new SessionFormatError("llm/retry-started does not match its scheduled turn and step");
2417
+ const key = `${JSON.stringify(data["retryId"])}\0${JSON.stringify(data["retry"])}`;
2418
+ if (retryStarts.has(key)) throw new SessionFormatError("llm/retry-started repeats one scheduled attempt");
2419
+ retryStarts.add(key);
2420
+ break;
2421
+ }
2422
+ case "session/title":
2423
+ case "session/title-llm-request":
2424
+ assertTitleSources(artifact.events, event, data, extensions.preservedSourceTitleRequestText !== true);
2425
+ break;
2426
+ case "command/run": {
2427
+ const id = data["commandId"];
2428
+ if (commandRuns.has(id)) throw new SessionFormatError(`command/run repeats commandId ${id}`);
2429
+ commandRuns.add(id);
2430
+ break;
2431
+ }
2432
+ case "command/done": {
2433
+ const id = data["commandId"];
2434
+ if (!commandRuns.has(id)) throw new SessionFormatError(`command/done ${id} has no prior command/run`);
2435
+ const sourceSeq = data["sourceEventSeq"];
2436
+ if (sourceSeq !== void 0) {
2437
+ const source = artifact.events[sourceSeq];
2438
+ if (data["kind"] !== "success" || source?.type === "command/run" || source?.type === "command/done") throw new SessionFormatError(`command/done ${id} has invalid sourceEventSeq`);
2439
+ }
2440
+ break;
2441
+ }
2442
+ case "session-log-deepseek/delivery-accepted":
2443
+ if ((data["sessionFormatVersion"] ?? 0) === artifact.header.version) {
2444
+ if (!(artifact.header.parentSession !== void 0 && event.seq < artifact.inheritedEventCount) && data["sessionId"] !== artifact.header.id) throw new SessionFormatError("current-generation delivery marker names the wrong Session");
2445
+ }
2446
+ break;
2447
+ case "compaction/start":
2448
+ if (openCompaction !== void 0) throw new SessionFormatError("compaction/start overlaps an open compaction");
2449
+ assertCompactionTurn(data["turn"], openTurn, "compaction/start");
2450
+ openCompaction = {
2451
+ id: data["compactionId"],
2452
+ ...data["sourceCommandId"] === void 0 ? {} : { sourceCommandId: data["sourceCommandId"] },
2453
+ turn: data["turn"],
2454
+ startSeq: event.seq,
2455
+ summarized: false
2456
+ };
2457
+ break;
2458
+ case "compaction/summary":
2459
+ assertCompactionOwner(openCompaction, data, "compaction/summary");
2460
+ assertCompactionTurn(openCompaction?.turn, openTurn, "compaction/summary");
2461
+ if (openCompaction?.summarized === true) throw new SessionFormatError("compaction/summary repeats");
2462
+ assertCurrentSurfaceSpan(surface, data, "compaction/summary");
2463
+ openCompaction = {
2464
+ ...openCompaction,
2465
+ summarized: true
2466
+ };
2467
+ break;
2468
+ case "compaction/end":
2469
+ assertCompactionOwner(openCompaction, data, "compaction/end");
2470
+ if (data["turn"] !== openCompaction?.turn) throw new SessionFormatError("compaction/end changes its owner turn");
2471
+ assertCompactionTurn(openCompaction?.turn, openTurn, "compaction/end");
2472
+ if (data["error"] === void 0 && openCompaction?.summarized !== true) throw new SessionFormatError("successful compaction/end requires one summary");
2473
+ openCompaction = void 0;
2474
+ break;
2475
+ case "compaction/prune":
2476
+ assertCurrentSurfaceSpan(surface, data, "compaction/prune");
2477
+ break;
2478
+ case "user/message": {
2479
+ const source = releasedV0Record(data["source"], `user/message ${event.seq} source`);
2480
+ if (event["surfaceOp"] !== "append" && source["kind"] === "plugin" && source["plugin"] === "compact") assertCompactionOwner(openCompaction, source, `compaction checkpoint at seq ${event.seq}`);
2481
+ break;
2482
+ }
2483
+ case "session/end-seed":
2484
+ openCompaction = void 0;
2485
+ break;
2486
+ }
2487
+ }
2488
+ }
2489
+ function inheritedOrphanCompactionStarts(events) {
2490
+ const stale = /* @__PURE__ */ new Set();
2491
+ let open;
2492
+ for (const event of events) if (event.type === "compaction/start") open = event.seq;
2493
+ else if (event.type === "compaction/end") open = void 0;
2494
+ else if (event.type === "session/end-seed") {
2495
+ if (open !== void 0) stale.add(open);
2496
+ open = void 0;
2497
+ }
2498
+ return stale;
2499
+ }
2500
+ function assertRetryChain(retries, data) {
2501
+ const prior = [...retries].reverse().find((candidate) => {
2502
+ const value = candidate.data;
2503
+ return value["turn"] === data["turn"] && value["step"] === data["step"] && value["provider"] === data["provider"] && value["policyKey"] === data["policyKey"];
2504
+ });
2505
+ const expected = (prior?.data?.["retry"] ?? 0) + 1;
2506
+ if (data["retry"] !== expected) throw new SessionFormatError(`llm/retry must use retry ${expected}`);
2507
+ if (prior !== void 0 && prior.data["retryId"] !== data["retryId"]) throw new SessionFormatError("llm/retry must preserve retryId across one policy chain");
2508
+ if (prior === void 0 && retries.some((candidate) => candidate.data["retryId"] === data["retryId"])) throw new SessionFormatError(`llm/retry reuses retryId ${JSON.stringify(data["retryId"])} across policy chains`);
2509
+ }
2510
+ function requireOpenStep(event, data, openTurn, openStep) {
2511
+ if (data["turn"] !== openTurn || data["step"] !== openStep || openTurn === null || openStep === null) throw new SessionFormatError(`${event.type} does not match an open turn and step`);
2512
+ }
2513
+ function assertNoUnresolvedTools(lifecycles, boundary) {
2514
+ const unresolved = lifecycles.keys().next().value;
2515
+ if (unresolved !== void 0) throw new SessionFormatError(`${boundary} leaves unresolved tool call ${unresolved}`);
2516
+ }
2517
+ function isExactToolNotStartedRepair(event, content, error) {
2518
+ const message = event.data["message"];
2519
+ const callId = message["source"]["callId"];
2520
+ const block = content[0];
2521
+ const repairContent = block?.["content"];
2522
+ return error?.["name"] === "ToolNotStartedError" && error["code"] === "TOOL_NOT_STARTED" && event["sourceEventSeqs"] === void 0 && message["id"] === `interrupted-tool-result-${callId}-${event.seq}` && block?.["isError"] === true && repairContent?.length === 1 && repairContent[0]?.["type"] === "text" && repairContent[0]["text"] === "The tool call was interrupted before the Harness recorded it as started. Retry it if it is still needed.";
2523
+ }
2524
+ function applySurface(surface, event) {
2525
+ const operation = event["surfaceOp"];
2526
+ if (operation === void 0) throw new SessionFormatError(`${event.type} requires a surfaceOp marker`);
2527
+ if (operation === "append") return [...surface, event.seq];
2528
+ const replace = operation;
2529
+ const start = surface.indexOf(replace.start);
2530
+ const end = surface.indexOf(replace.end);
2531
+ if (start < 0 || end < start) throw new SessionFormatError(`${event.type} replacement range is not on the current surface`);
2532
+ const shadowed = surface.slice(start, end + 1);
2533
+ const sources = new Set(Array.isArray(event["sourceEventSeqs"]) ? event["sourceEventSeqs"] : []);
2534
+ if (shadowed.some((seq) => !sources.has(seq))) throw new SessionFormatError(`${event.type} replacement sourceEventSeqs omit a shadowed surface node`);
2535
+ return [
2536
+ ...surface.slice(0, start),
2537
+ event.seq,
2538
+ ...surface.slice(end + 1)
2539
+ ];
2540
+ }
2541
+ function assertTitleSources(events, event, data, validateFramedText) {
2542
+ const seqs = data["messageSeqs"];
2543
+ if (event.type === "session/title") {
2544
+ const titleSource = releasedV0Record(data["source"], `session/title ${event.seq} source`);
2545
+ if (seqs.length === 0 !== (titleSource["kind"] === "user")) throw new SessionFormatError(`session/title ${event.seq} messageSeqs must be empty exactly for a user title`);
2546
+ }
2547
+ const selected = [];
2548
+ for (const seq of seqs) {
2549
+ const source = events[seq];
2550
+ if (source?.type !== "user/message") throw new SessionFormatError(`${event.type} ${event.seq} messageSeqs must cite earlier human user/message events`);
2551
+ const sourceData = releasedV0Record(source.data, `${source.type} ${seq} data`);
2552
+ if (releasedV0Record(sourceData["source"], `${source.type} ${seq} source`)["kind"] !== "user") throw new SessionFormatError(`${event.type} ${event.seq} messageSeqs must cite earlier human user/message events`);
2553
+ const content = sourceData["content"];
2554
+ selected.push({
2555
+ seq,
2556
+ text: content.flatMap((block) => block["type"] === "text" && typeof block["text"] === "string" ? [block["text"]] : []).join("\n")
2557
+ });
2558
+ }
2559
+ if (event.type === "session/title-llm-request") {
2560
+ const messages = data["messages"];
2561
+ const expected = `Generate the session title from this JSON array of human messages:\n${JSON.stringify(selected)}`;
2562
+ const message = messages[0];
2563
+ const content = message?.["content"];
2564
+ const source = message === void 0 ? void 0 : releasedV0Record(message["source"], "session/title-llm-request message source");
2565
+ if (messages.length !== 1 || message?.["role"] !== "user" || content?.length !== 1 || source?.["kind"] !== "plugin" || source["plugin"] !== "dsh-session-title-llm") throw new SessionFormatError("session/title-llm-request messages do not represent messageSeqs");
2566
+ const framed = content[0];
2567
+ if (framed === void 0 || framed["type"] !== "text" || validateFramedText && framed["text"] !== expected) throw new SessionFormatError("session/title-llm-request messages do not represent messageSeqs");
2568
+ }
2569
+ }
2570
+ function assertCompactionOwner(open, data, type) {
2571
+ if (open === void 0 || data["compactionId"] !== open.id || data["sourceCommandId"] !== open.sourceCommandId) throw new SessionFormatError(`${type} has no matching compaction/start`);
2572
+ }
2573
+ function assertCompactionTurn(owner, openTurn, type) {
2574
+ if (owner === null ? openTurn !== null : owner !== openTurn) throw new SessionFormatError(`${type} does not match the open turn`);
2575
+ }
2576
+ function assertCurrentSurfaceSpan(surface, data, type) {
2577
+ const range = data["shadowedRange"];
2578
+ const seqs = data["shadowedSeqs"];
2579
+ const start = surface.indexOf(range.start);
2580
+ const end = surface.indexOf(range.end);
2581
+ const expected = start < 0 || end < start ? [] : surface.slice(start, end + 1);
2582
+ if (expected.length !== seqs.length || expected.some((seq, index) => seq !== seqs[index])) throw new SessionFormatError(`${type} shadowedSeqs do not name an exact current surface span`);
2583
+ }
2584
+ //#endregion
2585
+ export { RELEASED_V0_EVENT_DISPOSITIONS, RELEASED_V0_EVENT_TYPES, assertReleasedArtifactRelationships, assertReleasedEventPayload, assertReleasedPayloadSemantics, assertReleasedSurfaceMetadata, assertReleasedV1Header, defineReleasedPayloadDisposition, isReleasedAssistantChunkRun, releasedV0SessionFormatCodec, releasedV1SessionFormatCodec, restoreReleasedV1Artifact, sessionFormatV0ToV1 };