@deepseek-ai/dsh-session-persistence-jsonl 0.1.6-alpha.2 → 0.1.7-alpha.1
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/README.i18n.yaml +2 -2
- package/README.md +6 -2
- package/README.zh.md +6 -2
- package/lib/index.js +210 -45
- package/lib/types/catalog-migration.d.ts +28 -0
- package/lib/types/generation.d.ts +16 -0
- package/lib/types/index.d.ts +5 -1
- package/lib/worker.cjs +1456 -313
- package/package.json +13 -13
package/lib/worker.cjs
CHANGED
|
@@ -264,12 +264,6 @@ function offloadMessageImages(message, indexes) {
|
|
|
264
264
|
selected += 1;
|
|
265
265
|
}
|
|
266
266
|
imageIndex += 1;
|
|
267
|
-
} else if (block.type === "tool-result") {
|
|
268
|
-
const content = visit(block.content);
|
|
269
|
-
if (content !== block.content) projected = {
|
|
270
|
-
...block,
|
|
271
|
-
content
|
|
272
|
-
};
|
|
273
267
|
}
|
|
274
268
|
if (projected !== block) next ??= blocks.slice(0, index);
|
|
275
269
|
next?.push(projected);
|
|
@@ -356,6 +350,45 @@ function defineProperty(object, key, value) {
|
|
|
356
350
|
});
|
|
357
351
|
}
|
|
358
352
|
//#endregion
|
|
353
|
+
//#region ../../../vendor/cosmokit/src/volatile.ts
|
|
354
|
+
/** Shared config references used by schema validators and plugin runtimes. */
|
|
355
|
+
const write = Symbol.for("cosmokit.volatile.write");
|
|
356
|
+
function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
357
|
+
if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
|
|
358
|
+
if (value === null || typeof value !== "object") return value;
|
|
359
|
+
if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
|
|
360
|
+
ancestors.add(value);
|
|
361
|
+
try {
|
|
362
|
+
if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
|
|
363
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
|
|
364
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
|
|
365
|
+
} finally {
|
|
366
|
+
ancestors.delete(value);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Create a detached reference containing an immutable copy of the supplied data.
|
|
371
|
+
* @param value - validated config data; class instances and functions are unsupported.
|
|
372
|
+
* @returns a reference whose value is updated only by its owning runtime.
|
|
373
|
+
*/
|
|
374
|
+
function createVolatile(value) {
|
|
375
|
+
let current = snapshot(value);
|
|
376
|
+
return Object.freeze({
|
|
377
|
+
get: () => current,
|
|
378
|
+
[write]: (value) => {
|
|
379
|
+
current = value;
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Identify references across ESM/CJS copies of the shared library.
|
|
385
|
+
* @param value - a parsed config value.
|
|
386
|
+
* @returns whether the value implements the shared reference protocol.
|
|
387
|
+
*/
|
|
388
|
+
function isVolatile(value) {
|
|
389
|
+
return typeof value === "object" && value !== null && write in value;
|
|
390
|
+
}
|
|
391
|
+
//#endregion
|
|
359
392
|
//#region ../../../vendor/cosmokit/src/types.ts
|
|
360
393
|
/** Test values using `instanceof` with a `toStringTag` fallback. */
|
|
361
394
|
function is(type, value) {
|
|
@@ -436,26 +469,47 @@ function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
|
436
469
|
}
|
|
437
470
|
return result;
|
|
438
471
|
}
|
|
439
|
-
/**
|
|
472
|
+
/**
|
|
473
|
+
* Compare values recursively, treating two volatile references as equal regardless of value.
|
|
474
|
+
* Strict comparison distinguishes null/undefined, treats opaque objects by identity,
|
|
475
|
+
* compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
|
|
476
|
+
* @param a - first value.
|
|
477
|
+
* @param b - second value.
|
|
478
|
+
* @param strict - whether to require strict data equality outside volatile references.
|
|
479
|
+
* @returns whether the values compare equal.
|
|
480
|
+
*/
|
|
440
481
|
function deepEqual(a, b, strict) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
482
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
483
|
+
function compare(a, b) {
|
|
484
|
+
if (a === b) return true;
|
|
485
|
+
if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
|
|
486
|
+
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
487
|
+
if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
|
|
488
|
+
if (ancestors.has(a)) return false;
|
|
489
|
+
function check(test, then) {
|
|
490
|
+
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
491
|
+
}
|
|
492
|
+
ancestors.add(a);
|
|
493
|
+
try {
|
|
494
|
+
return check(Array.isArray, (a, b) => {
|
|
495
|
+
if (a.length !== b.length) return false;
|
|
496
|
+
for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
|
|
497
|
+
return true;
|
|
498
|
+
}) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("URL"), (a, b) => a.href === b.href) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
|
|
499
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
500
|
+
const viewA = new Uint8Array(a);
|
|
501
|
+
const viewB = new Uint8Array(b);
|
|
502
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
503
|
+
return true;
|
|
504
|
+
}) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
|
|
505
|
+
...a,
|
|
506
|
+
...b
|
|
507
|
+
}).every((key) => compare(a[key], b[key])));
|
|
508
|
+
} finally {
|
|
509
|
+
ancestors.delete(a);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return compare(a, b);
|
|
459
513
|
}
|
|
460
514
|
//#endregion
|
|
461
515
|
//#region ../../../vendor/cosmokit/src/string.ts
|
|
@@ -2462,6 +2516,7 @@ const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
2462
2516
|
"compaction/start",
|
|
2463
2517
|
"compaction/summary",
|
|
2464
2518
|
"deliverables/presented",
|
|
2519
|
+
"developer/message",
|
|
2465
2520
|
"feedback/message-delete",
|
|
2466
2521
|
"feedback/message-put",
|
|
2467
2522
|
"feedback/record",
|
|
@@ -2514,6 +2569,7 @@ const MESSAGE_PROJECTION_EVENT_TYPES = new Set(["image/offload"]);
|
|
|
2514
2569
|
/** Runtime counterpart of the message-producing event union. */
|
|
2515
2570
|
const SURFACE_EVENT_TYPES = new Set([
|
|
2516
2571
|
"system/message",
|
|
2572
|
+
"developer/message",
|
|
2517
2573
|
"user/message",
|
|
2518
2574
|
"assistant/message",
|
|
2519
2575
|
"tool/result"
|
|
@@ -2521,7 +2577,7 @@ const SURFACE_EVENT_TYPES = new Set([
|
|
|
2521
2577
|
/**
|
|
2522
2578
|
* Whether an event type can join the model-visible surface.
|
|
2523
2579
|
* @param type - event type to test.
|
|
2524
|
-
* @returns true for one of the
|
|
2580
|
+
* @returns true for one of the message-producing event types.
|
|
2525
2581
|
*/
|
|
2526
2582
|
function isSurfaceEligibleType(type) {
|
|
2527
2583
|
return SURFACE_EVENT_TYPES.has(type);
|
|
@@ -2529,7 +2585,7 @@ function isSurfaceEligibleType(type) {
|
|
|
2529
2585
|
/**
|
|
2530
2586
|
* Project a single event into the LLM message it derives to, or null when it
|
|
2531
2587
|
* produces none — a non-surface event (attempt, boundary, log-only record) or an
|
|
2532
|
-
* empty-content
|
|
2588
|
+
* empty-content system, developer, or assistant message. A caller
|
|
2533
2589
|
* reconstructing model input supplies the same prefix's `projectedMessages`
|
|
2534
2590
|
* from {@link foldSurface}; without that map this function reads original
|
|
2535
2591
|
* event content. Session instance methods apply the live projection. Messages
|
|
@@ -2544,6 +2600,7 @@ function deriveEventMessage(event, projectedMessages) {
|
|
|
2544
2600
|
switch (event.type) {
|
|
2545
2601
|
case "user/message": return event.data;
|
|
2546
2602
|
case "system/message":
|
|
2603
|
+
case "developer/message":
|
|
2547
2604
|
case "assistant/message":
|
|
2548
2605
|
if (event.data.message.content.length === 0) return null;
|
|
2549
2606
|
return event.data.message;
|
|
@@ -2556,14 +2613,33 @@ function isRecord(value) {
|
|
|
2556
2613
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2557
2614
|
}
|
|
2558
2615
|
/**
|
|
2559
|
-
* Reject noncanonical request-header fields and contradictory tool failure metadata.
|
|
2616
|
+
* Reject noncanonical request-header fields, developer roles/content, and contradictory tool failure metadata.
|
|
2560
2617
|
* This does not validate complete event payloads or embedded provider streams.
|
|
2561
2618
|
* @param event - event whose locally related payload fields are inspected.
|
|
2562
2619
|
* @param subject - event location to include in validation errors.
|
|
2563
|
-
* @throws when request
|
|
2620
|
+
* @throws when request-header fields, developer roles/content, or tool failure metadata are invalid.
|
|
2564
2621
|
*/
|
|
2565
2622
|
function validateSessionEventData(event, subject) {
|
|
2566
2623
|
const data = event.data;
|
|
2624
|
+
if (SURFACE_EVENT_TYPES.has(event.type) && isRecord(data)) {
|
|
2625
|
+
const message = event.type === "user/message" ? data : data["message"];
|
|
2626
|
+
if (isRecord(message)) {
|
|
2627
|
+
if (event.type === "developer/message" !== (message["role"] === "developer")) throw new Error(`${subject} developer/message and developer role must occur together`);
|
|
2628
|
+
if (message["role"] !== "developer" && Array.isArray(message["content"]) && message["content"].some((block) => isRecord(block) && (block["type"] === "tool-addition" || block["type"] === "tool-removal"))) throw new Error(`${subject} tool-change blocks require developer role`);
|
|
2629
|
+
if (event.type === "developer/message" && Array.isArray(message["content"])) {
|
|
2630
|
+
let hasAdditions = false;
|
|
2631
|
+
for (const block of message["content"]) {
|
|
2632
|
+
if (!isRecord(block) || block["type"] !== "tool-addition" && block["type"] !== "tool-removal") continue;
|
|
2633
|
+
if (typeof block["toolName"] !== "string" || block["toolName"].length === 0) throw new Error(`${subject} ${block["type"]} requires a nonempty toolName`);
|
|
2634
|
+
if (block["type"] === "tool-addition") {
|
|
2635
|
+
hasAdditions = true;
|
|
2636
|
+
if (Object.hasOwn(block, "tool")) throw new Error(`${subject} tool-addition must omit inline tool definitions`);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
if (hasAdditions ? !isEventSeq(data["headerSeq"]) : Object.hasOwn(data, "headerSeq")) throw new Error(`${subject} requires headerSeq exactly when tool additions are present`);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2567
2643
|
if (event.type === "request/header") {
|
|
2568
2644
|
if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
|
|
2569
2645
|
const header = data["header"];
|
|
@@ -2576,9 +2652,7 @@ function validateSessionEventData(event, subject) {
|
|
|
2576
2652
|
if (!isRecord(data)) throw new Error(`${subject} data must be an object`);
|
|
2577
2653
|
if (data["error"] === void 0) return;
|
|
2578
2654
|
const message = data["message"];
|
|
2579
|
-
|
|
2580
|
-
const block = Array.isArray(content) ? content[0] : void 0;
|
|
2581
|
-
if (!isRecord(block) || block["isError"] !== true) throw new Error(`${subject} error requires message content[0].isError === true`);
|
|
2655
|
+
if (!isRecord(message) || message["isError"] !== true) throw new Error(`${subject} error requires message.isError === true`);
|
|
2582
2656
|
}
|
|
2583
2657
|
}
|
|
2584
2658
|
/** Create an empty surface fold state. */
|
|
@@ -2636,6 +2710,23 @@ function assertSourceEventReferences(event, shadowedSeqs) {
|
|
|
2636
2710
|
const missing = shadowedSeqs.filter((seq) => !sources.has(seq));
|
|
2637
2711
|
if (missing.length > 0) throw new Error(`surface replace: sourceEventSeqs must include every shadowed surface node; missing ${missing.join(", ")}`);
|
|
2638
2712
|
}
|
|
2713
|
+
/** Resolve tool additions against their immutable historical request header. */
|
|
2714
|
+
function assertDeveloperHeader(event, events, baseSeq) {
|
|
2715
|
+
if (event.type !== "developer/message") return;
|
|
2716
|
+
validateSessionEventData(event, `developer/message at seq ${event.seq}`);
|
|
2717
|
+
if (event.data.headerSeq === void 0) return;
|
|
2718
|
+
const headerSeq = event.data.headerSeq;
|
|
2719
|
+
const headerEvent = events[headerSeq - baseSeq];
|
|
2720
|
+
if (headerSeq >= event.seq || headerEvent?.type !== "request/header") throw new Error("developer/message headerSeq must reference an earlier request/header");
|
|
2721
|
+
for (const block of event.data.message.content) {
|
|
2722
|
+
if (block.type !== "tool-addition") continue;
|
|
2723
|
+
const definitions = headerEvent.data.header.tools?.filter((tool) => tool.name === block.toolName) ?? [];
|
|
2724
|
+
if (definitions.length !== 1) throw new Error(`developer/message tool-addition "${block.toolName}" must name exactly one tool in headerSeq ${headerSeq}`);
|
|
2725
|
+
const definition = definitions[0];
|
|
2726
|
+
if (typeof definition.description !== "string" || !isRecord(definition.parameters)) throw new Error(`developer/message tool-addition "${block.toolName}" requires a complete tool definition in headerSeq ${headerSeq}`);
|
|
2727
|
+
if (Object.hasOwn(definition, "deferLoading") && definition.deferLoading !== true) throw new Error("developer/message referenced tool deferLoading must be true when present");
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2639
2730
|
/**
|
|
2640
2731
|
* Validate one event's surface metadata without checking membership in a log or surface.
|
|
2641
2732
|
* @param event - event whose marker and source sequence values are inspected.
|
|
@@ -2688,21 +2779,13 @@ function assertToolResultRewrite(event, shadowedSeqs, events, baseSeq) {
|
|
|
2688
2779
|
if (original?.type !== "tool/result") throw new Error("tool/result surface replacement must target a current tool/result");
|
|
2689
2780
|
const originalRest = { ...original.data };
|
|
2690
2781
|
const replacementRest = { ...event.data };
|
|
2691
|
-
const originalResult = original.data.message.content[0];
|
|
2692
|
-
const replacementResult = event.data.message.content[0];
|
|
2693
2782
|
originalRest["message"] = {
|
|
2694
2783
|
...original.data.message,
|
|
2695
|
-
content:
|
|
2696
|
-
...originalResult,
|
|
2697
|
-
content: null
|
|
2698
|
-
}]
|
|
2784
|
+
content: null
|
|
2699
2785
|
};
|
|
2700
2786
|
replacementRest["message"] = {
|
|
2701
2787
|
...event.data.message,
|
|
2702
|
-
content:
|
|
2703
|
-
...replacementResult,
|
|
2704
|
-
content: null
|
|
2705
|
-
}]
|
|
2788
|
+
content: null
|
|
2706
2789
|
};
|
|
2707
2790
|
if (!isDeepEqualJson(originalRest, replacementRest)) throw new Error("tool/result surface replacement may change only content");
|
|
2708
2791
|
}
|
|
@@ -2722,6 +2805,7 @@ function assertSystemHeadRewrite(event, state, startIdx, shadowedSeqs, events, b
|
|
|
2722
2805
|
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
|
|
2723
2806
|
if (event.seq !== expectedSeq) throw new Error(`session event seq ${event.seq} is not contiguous; expected ${expectedSeq}`);
|
|
2724
2807
|
const surfaceOp = validateSurfaceMetadata(event);
|
|
2808
|
+
assertDeveloperHeader(event, events, baseSeq);
|
|
2725
2809
|
const projection = projections.find((item) => item.type === event.type);
|
|
2726
2810
|
if (projection !== void 0) return {
|
|
2727
2811
|
kind: "project",
|
|
@@ -2906,7 +2990,10 @@ function isTypertRemoteSegment(value) {
|
|
|
2906
2990
|
}
|
|
2907
2991
|
const REMOTE_METHOD_DESCRIPTOR = "@deepseek-ai/dsh-typert-protocol/remote-methods";
|
|
2908
2992
|
/**
|
|
2909
|
-
* Bind one visible Service field to a Cordis key and Remote namespace.
|
|
2993
|
+
* Bind one visible Service field to a Cordis key and Remote namespace. A
|
|
2994
|
+
* service that owns a Cordis Context also gives its tree `ctx.invocation`,
|
|
2995
|
+
* `undefined` outside a Remote call, so no `TypertRemoteService` is needed for
|
|
2996
|
+
* a Host composition to read it.
|
|
2910
2997
|
* @param service - owning Service instance, normally `this`.
|
|
2911
2998
|
* @param serviceKey - exact Cordis service key.
|
|
2912
2999
|
* @param options - optional distinct wire namespace.
|
|
@@ -2916,6 +3003,8 @@ function bindTypertRemote(service, serviceKey, options = {}) {
|
|
|
2916
3003
|
validateName("service key", serviceKey);
|
|
2917
3004
|
const namespace = options.namespace ?? serviceKey;
|
|
2918
3005
|
validateName("namespace", namespace);
|
|
3006
|
+
const ctx = Reflect.get(service, "ctx");
|
|
3007
|
+
if (ctx instanceof Context) provideInvocationAccessor(ctx);
|
|
2919
3008
|
return Object.freeze({
|
|
2920
3009
|
service,
|
|
2921
3010
|
serviceKey,
|
|
@@ -2937,6 +3026,16 @@ var TypertRemoteService = class extends Service {
|
|
|
2937
3026
|
this.typertRemote = bindTypertRemote(this, this.name, options);
|
|
2938
3027
|
}
|
|
2939
3028
|
};
|
|
3029
|
+
/**
|
|
3030
|
+
* Make `ctx.invocation` read as `undefined` outside a Remote call instead of the
|
|
3031
|
+
* reflect service's "cannot get property" error; a call-derived Context shadows
|
|
3032
|
+
* the accessor with its own property. The first Remote Service constructed in a
|
|
3033
|
+
* tree registers it on the root, where it outlives any one Service.
|
|
3034
|
+
*/
|
|
3035
|
+
function provideInvocationAccessor(ctx) {
|
|
3036
|
+
if (Object.hasOwn(ctx.root.reflect.props, "invocation")) return;
|
|
3037
|
+
ctx.root.accessor("invocation", { get: () => void 0 });
|
|
3038
|
+
}
|
|
2940
3039
|
function Remote(methodExportOrOptions, context) {
|
|
2941
3040
|
if (typeof methodExportOrOptions === "string") {
|
|
2942
3041
|
validateName("Remote export name", methodExportOrOptions);
|
|
@@ -3036,9 +3135,24 @@ function freezeMessage(message) {
|
|
|
3036
3135
|
* @returns an immutable message with a fresh stable identity.
|
|
3037
3136
|
*/
|
|
3038
3137
|
function createMessage(input) {
|
|
3039
|
-
return
|
|
3138
|
+
return deepFreeze(structuredClone({
|
|
3040
3139
|
...input,
|
|
3041
3140
|
id: brandString(randomUUID())
|
|
3141
|
+
}));
|
|
3142
|
+
}
|
|
3143
|
+
/**
|
|
3144
|
+
* Create one identified model-produced assistant message and freeze it before publication.
|
|
3145
|
+
* @param input - complete content plus the provider, model, and optional replay state for a new assistant message.
|
|
3146
|
+
* @returns an immutable assistant message with fixed role/source tags and a fresh stable identity.
|
|
3147
|
+
*/
|
|
3148
|
+
function createAssistantMessage(input) {
|
|
3149
|
+
return createMessage({
|
|
3150
|
+
role: "assistant",
|
|
3151
|
+
content: input.content,
|
|
3152
|
+
source: {
|
|
3153
|
+
kind: "model",
|
|
3154
|
+
...input.source
|
|
3155
|
+
}
|
|
3042
3156
|
});
|
|
3043
3157
|
}
|
|
3044
3158
|
//#endregion
|
|
@@ -3218,6 +3332,7 @@ Schema.prototype.pattern = function pattern(regexp) {
|
|
|
3218
3332
|
return schema;
|
|
3219
3333
|
};
|
|
3220
3334
|
Schema.prototype.simplify = function simplify(value) {
|
|
3335
|
+
if (isVolatile(value)) value = value.get();
|
|
3221
3336
|
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
3222
3337
|
if (isNullable(value)) return value;
|
|
3223
3338
|
if (this.type === "object" || this.type === "dict") {
|
|
@@ -3274,12 +3389,49 @@ for (const key of [
|
|
|
3274
3389
|
};
|
|
3275
3390
|
return schema;
|
|
3276
3391
|
} });
|
|
3392
|
+
Schema.prototype.volatile = function volatile() {
|
|
3393
|
+
if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
|
|
3394
|
+
return this.extra("volatile", true);
|
|
3395
|
+
};
|
|
3277
3396
|
const resolvers = {};
|
|
3397
|
+
const checkedVolatile = Symbol("checked-volatile-schema");
|
|
3398
|
+
function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
|
|
3399
|
+
const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
|
|
3400
|
+
if (states.has(blocked)) return;
|
|
3401
|
+
states.add(blocked);
|
|
3402
|
+
seen.set(schema, states);
|
|
3403
|
+
if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
|
|
3404
|
+
const nested = blocked || !!schema.meta?.volatile;
|
|
3405
|
+
if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
|
|
3406
|
+
if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
|
|
3407
|
+
if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
|
|
3408
|
+
if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
|
|
3409
|
+
}
|
|
3278
3410
|
Schema.extend = function extend(type, resolve) {
|
|
3279
3411
|
resolvers[type] = resolve;
|
|
3280
3412
|
};
|
|
3281
3413
|
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
3282
3414
|
if (!schema) return [data];
|
|
3415
|
+
if (!options[checkedVolatile]) {
|
|
3416
|
+
validateVolatileSchema(schema, options.path);
|
|
3417
|
+
options = {
|
|
3418
|
+
...options,
|
|
3419
|
+
[checkedVolatile]: true
|
|
3420
|
+
};
|
|
3421
|
+
}
|
|
3422
|
+
if (schema.meta?.volatile) {
|
|
3423
|
+
const inner = Schema(schema);
|
|
3424
|
+
inner.meta = {
|
|
3425
|
+
...schema.meta,
|
|
3426
|
+
volatile: false
|
|
3427
|
+
};
|
|
3428
|
+
const [value, adapted] = Schema.resolve(data, inner, options, strict);
|
|
3429
|
+
try {
|
|
3430
|
+
return [createVolatile(value), adapted];
|
|
3431
|
+
} catch (error) {
|
|
3432
|
+
throw new ValidationError(error instanceof Error ? error.message : String(error), options);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3283
3435
|
if (options.ignore?.(data, schema)) return [data];
|
|
3284
3436
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
3285
3437
|
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
@@ -3382,6 +3534,7 @@ Schema.extend("lazy", (data, schema, options, strict) => {
|
|
|
3382
3534
|
...schema.meta,
|
|
3383
3535
|
...schema.inner.meta
|
|
3384
3536
|
};
|
|
3537
|
+
validateVolatileSchema(schema.inner, options.path, true);
|
|
3385
3538
|
}
|
|
3386
3539
|
return Schema.resolve(data, schema.inner, options, strict);
|
|
3387
3540
|
});
|
|
@@ -3481,7 +3634,7 @@ function property(data, key, schema, options) {
|
|
|
3481
3634
|
} catch (e) {
|
|
3482
3635
|
if (!options?.autofix) throw e;
|
|
3483
3636
|
delete data[key];
|
|
3484
|
-
return schema.meta.default;
|
|
3637
|
+
return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
|
|
3485
3638
|
}
|
|
3486
3639
|
}
|
|
3487
3640
|
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
@@ -3898,25 +4051,23 @@ function textOnlyImageText(ref) {
|
|
|
3898
4051
|
return `[image omitted because this model accepts text only; attachment sha256:${String(ref.attachmentId).slice(7, 15)}]`;
|
|
3899
4052
|
}
|
|
3900
4053
|
/**
|
|
3901
|
-
* True when typed model content contains an image block
|
|
3902
|
-
*
|
|
3903
|
-
*
|
|
3904
|
-
* survey), so a consumer cannot silently diverge on nesting depth.
|
|
4054
|
+
* True when typed model content contains an image block. This is the one image
|
|
4055
|
+
* walk shared by every image policy (capability gating, text-only
|
|
4056
|
+
* serialization, compaction survey), so a consumer cannot silently diverge.
|
|
3905
4057
|
* @param content - typed model content blocks.
|
|
3906
|
-
* @returns whether any
|
|
4058
|
+
* @returns whether any block is an image.
|
|
3907
4059
|
*/
|
|
3908
4060
|
function contentHasImage(content) {
|
|
3909
|
-
return content.some((block) => block.type === "image"
|
|
4061
|
+
return content.some((block) => block.type === "image");
|
|
3910
4062
|
}
|
|
3911
4063
|
/**
|
|
3912
|
-
* True when typed model content contains a file block
|
|
3913
|
-
* tool-result content on the same recursion every file policy shares.
|
|
4064
|
+
* True when typed model content contains a file block.
|
|
3914
4065
|
* Reads current content on every call without retaining scan results.
|
|
3915
4066
|
* @param content - typed model content blocks.
|
|
3916
|
-
* @returns whether any
|
|
4067
|
+
* @returns whether any block is a file.
|
|
3917
4068
|
*/
|
|
3918
4069
|
function contentHasFile(content) {
|
|
3919
|
-
for (const block of content) if (block.type === "file"
|
|
4070
|
+
for (const block of content) if (block.type === "file") return true;
|
|
3920
4071
|
return false;
|
|
3921
4072
|
}
|
|
3922
4073
|
/**
|
|
@@ -3933,7 +4084,7 @@ function fileHandleText(ref, readonlyPath) {
|
|
|
3933
4084
|
if (readonlyPath === void 0) return `[${identity} was uploaded, but the current execution environment cannot access a readable path. Report that limitation if its contents are needed; do not claim to have read it.]`;
|
|
3934
4085
|
return `[${identity}: verbatim read-only copy saved at ${quoted(readonlyPath)}. Read that path with your file tools when its contents are needed; copy it to a writable location before modifying it. When delegating file work, include this saved path in the delegation prompt; only subagents sharing this execution environment can read it.]`;
|
|
3935
4086
|
}
|
|
3936
|
-
/** Replace every file occurrence
|
|
4087
|
+
/** Replace every file occurrence with handle text. */
|
|
3937
4088
|
function replaceFilesWithHandles(blocks, resolvePath) {
|
|
3938
4089
|
let next;
|
|
3939
4090
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -3945,29 +4096,10 @@ function replaceFilesWithHandles(blocks, resolvePath) {
|
|
|
3945
4096
|
});
|
|
3946
4097
|
continue;
|
|
3947
4098
|
}
|
|
3948
|
-
if (block.type === "tool-result") {
|
|
3949
|
-
const content = replaceFilesWithHandles(block.content, resolvePath);
|
|
3950
|
-
if (content !== block.content) {
|
|
3951
|
-
next ??= blocks.slice(0, index);
|
|
3952
|
-
next.push({
|
|
3953
|
-
...block,
|
|
3954
|
-
content
|
|
3955
|
-
});
|
|
3956
|
-
continue;
|
|
3957
|
-
}
|
|
3958
|
-
}
|
|
3959
4099
|
next?.push(block);
|
|
3960
4100
|
}
|
|
3961
4101
|
return next ?? blocks;
|
|
3962
4102
|
}
|
|
3963
|
-
/**
|
|
3964
|
-
* Project durable file history into deterministic handle text for every model
|
|
3965
|
-
* route. Unlike images, no provider receives file blocks natively, so this
|
|
3966
|
-
* projection is unconditional in request assembly.
|
|
3967
|
-
* @param messages - complete request history.
|
|
3968
|
-
* @param resolvePath - resolve one reference's current execution-world read path.
|
|
3969
|
-
* @returns the original list without files, otherwise shallow message copies with handle text.
|
|
3970
|
-
*/
|
|
3971
4103
|
function projectFilesToText(messages, resolvePath) {
|
|
3972
4104
|
if (!messages.some((message) => contentHasFile(message.content))) return messages;
|
|
3973
4105
|
return messages.map((message) => {
|
|
@@ -3978,7 +4110,7 @@ function projectFilesToText(messages, resolvePath) {
|
|
|
3978
4110
|
};
|
|
3979
4111
|
});
|
|
3980
4112
|
}
|
|
3981
|
-
/** Replace every image occurrence
|
|
4113
|
+
/** Replace every image occurrence for a text-only model. */
|
|
3982
4114
|
function replaceImagesForTextModel(blocks) {
|
|
3983
4115
|
let next;
|
|
3984
4116
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -3990,26 +4122,10 @@ function replaceImagesForTextModel(blocks) {
|
|
|
3990
4122
|
});
|
|
3991
4123
|
continue;
|
|
3992
4124
|
}
|
|
3993
|
-
if (block.type === "tool-result") {
|
|
3994
|
-
const content = replaceImagesForTextModel(block.content);
|
|
3995
|
-
if (content !== block.content) {
|
|
3996
|
-
next ??= blocks.slice(0, index);
|
|
3997
|
-
next.push({
|
|
3998
|
-
...block,
|
|
3999
|
-
content
|
|
4000
|
-
});
|
|
4001
|
-
continue;
|
|
4002
|
-
}
|
|
4003
|
-
}
|
|
4004
4125
|
next?.push(block);
|
|
4005
4126
|
}
|
|
4006
4127
|
return next ?? blocks;
|
|
4007
4128
|
}
|
|
4008
|
-
/**
|
|
4009
|
-
* Project durable image history into deterministic text for an exact text-only model.
|
|
4010
|
-
* @param messages - complete request history.
|
|
4011
|
-
* @returns the original list without images, otherwise shallow message copies with stable placeholders.
|
|
4012
|
-
*/
|
|
4013
4129
|
function projectImagesForTextModel(messages) {
|
|
4014
4130
|
if (!messages.some((message) => contentHasImage(message.content))) return messages;
|
|
4015
4131
|
return messages.map((message) => {
|
|
@@ -4212,15 +4328,11 @@ var BlockAssembler = class {
|
|
|
4212
4328
|
}
|
|
4213
4329
|
/**
|
|
4214
4330
|
* The assembled assistant message.
|
|
4215
|
-
* @param source -
|
|
4331
|
+
* @param source - provider/model attribution (without the `kind` tag) for the assembled message.
|
|
4216
4332
|
* @returns a frozen assistant-role message over `blocks()` (same open-block assembly rules).
|
|
4217
4333
|
*/
|
|
4218
|
-
message(source
|
|
4219
|
-
|
|
4220
|
-
plugin: "dsh-llm/assembler"
|
|
4221
|
-
}) {
|
|
4222
|
-
return createMessage({
|
|
4223
|
-
role: "assistant",
|
|
4334
|
+
message(source) {
|
|
4335
|
+
return createAssistantMessage({
|
|
4224
4336
|
content: this.blocks(),
|
|
4225
4337
|
source
|
|
4226
4338
|
});
|
|
@@ -5054,8 +5166,9 @@ var LlmError = class extends HarnessError {
|
|
|
5054
5166
|
/** Remove replay state whose historical route is owned by another adapter. */
|
|
5055
5167
|
forAdapter(options, adapter) {
|
|
5056
5168
|
const messages = options.messages.map((message) => {
|
|
5169
|
+
if (message.role !== "assistant") return message;
|
|
5057
5170
|
const source = message.source;
|
|
5058
|
-
if (
|
|
5171
|
+
if (source.replayState === void 0) return message;
|
|
5059
5172
|
if (this.adapters.get(source.provider)?.adapter === adapter) return message;
|
|
5060
5173
|
return freezeMessage({
|
|
5061
5174
|
...message,
|
|
@@ -5232,7 +5345,7 @@ function validateSessionHeader(id, input) {
|
|
|
5232
5345
|
if (input === null || typeof input !== "object" || Array.isArray(input)) throw new Error("session header is not a plain JSON record");
|
|
5233
5346
|
const record = input;
|
|
5234
5347
|
if (Object.hasOwn(record, "seedLength")) throw new Error("session header has invalid field \"seedLength\"");
|
|
5235
|
-
if (record.version !==
|
|
5348
|
+
if (record.version !== 4) throw new Error(`session header version must be 4, got ${String(record.version)}`);
|
|
5236
5349
|
if (record.id !== id) throw new Error(`session header id "${String(record.id)}" does not match session id "${id}"`);
|
|
5237
5350
|
if (typeof record.createdAt !== "number" || !Number.isSafeInteger(record.createdAt) || record.createdAt < 0) throw new Error("session header createdAt must be a non-negative safe integer");
|
|
5238
5351
|
if (record.cwd !== void 0) {
|
|
@@ -5257,7 +5370,7 @@ function validateRestoredSessionHeader(id, input) {
|
|
|
5257
5370
|
/** Detach, validate, and freeze the creation metadata published by a session. */
|
|
5258
5371
|
function snapshotSessionHeader(id, source) {
|
|
5259
5372
|
const snapshot = snapshotJsonValue(source === void 0 ? {
|
|
5260
|
-
version:
|
|
5373
|
+
version: 4,
|
|
5261
5374
|
id,
|
|
5262
5375
|
createdAt: Date.now(),
|
|
5263
5376
|
isSeeded: false
|
|
@@ -5282,6 +5395,7 @@ function adoptSessionEvent(event) {
|
|
|
5282
5395
|
case "user/message":
|
|
5283
5396
|
deepFreeze(event.data);
|
|
5284
5397
|
break;
|
|
5398
|
+
case "developer/message":
|
|
5285
5399
|
case "system/message":
|
|
5286
5400
|
case "assistant/message":
|
|
5287
5401
|
case "tool/result":
|
|
@@ -5312,6 +5426,7 @@ function assertSessionEventEnvelope(value, index) {
|
|
|
5312
5426
|
validateSessionEventData(event, `seed ${type} at index ${index}`);
|
|
5313
5427
|
switch (type) {
|
|
5314
5428
|
case "request/header":
|
|
5429
|
+
case "developer/message":
|
|
5315
5430
|
case "system/message":
|
|
5316
5431
|
case "user/message":
|
|
5317
5432
|
case "assistant/attempt":
|
|
@@ -5360,15 +5475,16 @@ function assertAdapterDefaults(value, config, index) {
|
|
|
5360
5475
|
const defaults = value;
|
|
5361
5476
|
if (Object.keys(defaults).some((key) => !allowedAdapterKeys.has(key)) || Object.values(defaults).some((marker) => marker !== true) || defaults["reasoningEffort"] === true && config["reasoningEffort"] === void 0 || defaults["maxTokens"] === true && config["maxTokens"] === void 0) throw new Error(`seed request/header at index ${index} has invalid adapterDefaults`);
|
|
5362
5477
|
}
|
|
5363
|
-
/** The
|
|
5478
|
+
/** The surface event types whose payload carries an identified message. */
|
|
5364
5479
|
function isMessageEventType(type) {
|
|
5365
|
-
return type === "system/message" || type === "user/message" || type === "assistant/message" || type === "tool/result";
|
|
5480
|
+
return type === "developer/message" || type === "system/message" || type === "user/message" || type === "assistant/message" || type === "tool/result";
|
|
5366
5481
|
}
|
|
5367
5482
|
const MESSAGE_ROLE_BY_TYPE = {
|
|
5368
5483
|
"system/message": "system",
|
|
5484
|
+
"developer/message": "developer",
|
|
5369
5485
|
"user/message": "user",
|
|
5370
5486
|
"assistant/message": "assistant",
|
|
5371
|
-
"tool/result": "
|
|
5487
|
+
"tool/result": "tool"
|
|
5372
5488
|
};
|
|
5373
5489
|
/** Validate only the event-specific invariants needed to safely replay a message. */
|
|
5374
5490
|
function assertMessageEventShape(event, subject) {
|
|
@@ -5386,7 +5502,7 @@ function assertMessageEventShape(event, subject) {
|
|
|
5386
5502
|
if (!Array.isArray(messageRecord["content"])) throw new Error(`${subject} message has invalid content`);
|
|
5387
5503
|
const sourceRecord = source;
|
|
5388
5504
|
if (type === "system/message") {
|
|
5389
|
-
if (sourceRecord["kind"] !== "
|
|
5505
|
+
if (sourceRecord["kind"] !== "system-prompt") throw new Error(`${subject} message must have system-prompt source`);
|
|
5390
5506
|
return;
|
|
5391
5507
|
}
|
|
5392
5508
|
if (type === "assistant/message") {
|
|
@@ -5395,10 +5511,7 @@ function assertMessageEventShape(event, subject) {
|
|
|
5395
5511
|
}
|
|
5396
5512
|
if (type !== "tool/result") return;
|
|
5397
5513
|
if (sourceRecord["kind"] !== "tool" || typeof sourceRecord["callId"] !== "string" || sourceRecord["callId"] === "") throw new Error(`${subject} message must have tool source`);
|
|
5398
|
-
|
|
5399
|
-
const block = content[0];
|
|
5400
|
-
if (content.length !== 1 || typeof block !== "object" || block === null || block["type"] !== "tool-result" || !Array.isArray(block["content"])) throw new Error(`${subject} message must contain one tool-result block`);
|
|
5401
|
-
if (block["toolCallId"] !== sourceRecord["callId"]) throw new Error(`${subject} message has mismatched tool call ids`);
|
|
5514
|
+
if (messageRecord["toolCallId"] !== sourceRecord["callId"]) throw new Error(`${subject} message has mismatched tool call ids`);
|
|
5402
5515
|
}
|
|
5403
5516
|
/** Whether an unknown value carries the current provider/model pair. */
|
|
5404
5517
|
function hasProviderModel(value) {
|
|
@@ -5455,30 +5568,26 @@ var Session = class Session {
|
|
|
5455
5568
|
return this.header.id;
|
|
5456
5569
|
}
|
|
5457
5570
|
/**
|
|
5458
|
-
* The
|
|
5459
|
-
*
|
|
5460
|
-
*
|
|
5461
|
-
*
|
|
5462
|
-
* the constructor-input boundary for lifecycle ownership and persistence
|
|
5463
|
-
* adoption; consumers that need complete canonical history still start at
|
|
5464
|
-
* seq 0. Distinct from {@link inheritedEventCount}, the DURABLE
|
|
5465
|
-
* fork-lineage cut: a resumed session's constructor seed is its full stored
|
|
5466
|
-
* log, while the inherited count keeps the original fork value — this field is the
|
|
5467
|
-
* in-process construction fact.
|
|
5571
|
+
* The constructor seed length (0 without one), before any marker appended
|
|
5572
|
+
* during construction. Seed events never publish on `session/event`. A
|
|
5573
|
+
* marker appended before the store attaches occupies this seq without
|
|
5574
|
+
* publishing either; otherwise this seq is available for the next append.
|
|
5468
5575
|
*
|
|
5469
|
-
*
|
|
5470
|
-
*
|
|
5471
|
-
*
|
|
5472
|
-
*
|
|
5473
|
-
*
|
|
5474
|
-
*
|
|
5475
|
-
*
|
|
5476
|
-
* When this lifecycle appends the marker, it occupies this seq before the
|
|
5477
|
-
* store attaches and therefore does not publish either. Otherwise this seq
|
|
5478
|
-
* holds an ordinary published write.
|
|
5576
|
+
* This in-process offset is not persisted. A fork seed can already contain
|
|
5577
|
+
* the child's inherited marker and synthetic closers, so its child-owned
|
|
5578
|
+
* history starts at {@link inheritedEventCount}, before this offset. A
|
|
5579
|
+
* resumed Session's seed contains its full stored log, while its inherited
|
|
5580
|
+
* count keeps the durable fork cut. Consumers needing complete canonical
|
|
5581
|
+
* history start at seq 0.
|
|
5479
5582
|
*/
|
|
5480
5583
|
firstLiveSeq;
|
|
5481
5584
|
/**
|
|
5585
|
+
* First event produced for this object lifecycle. A new fork includes its
|
|
5586
|
+
* child-owned seed marker and closers; a restored Session starts after its
|
|
5587
|
+
* complete stored prefix. This in-process capture offset is not persisted.
|
|
5588
|
+
*/
|
|
5589
|
+
firstLifecycleSeq;
|
|
5590
|
+
/**
|
|
5482
5591
|
* Create a detached session by validating and snapshotting borrowed seed
|
|
5483
5592
|
* events and storage metadata.
|
|
5484
5593
|
* @param id - session identity.
|
|
@@ -5532,10 +5641,14 @@ var Session = class Session {
|
|
|
5532
5641
|
const inheritedEventCount = SessionLogOffset(suppliedInheritedEventCount ?? 0);
|
|
5533
5642
|
if (!this.header.isSeeded && inheritedEventCount !== 0) throw new Error("unseeded session inherited event count must be 0");
|
|
5534
5643
|
if (inheritedEventCount > this.log.length) throw new Error("session inherited event count exceeds its event log");
|
|
5535
|
-
|
|
5644
|
+
const seedMarker = this.log[inheritedEventCount];
|
|
5645
|
+
const markedSeed = seedMarker?.type === "session/end-seed" && seedMarker.data.inherited === true;
|
|
5646
|
+
if (mode === "snapshot" && this.header.isSeeded && inheritedEventCount !== this.log.length && !markedSeed) throw new Error("seeded session constructor seed must equal its inherited prefix or mark its inherited cut");
|
|
5647
|
+
if (markedSeed && this.log.slice(inheritedEventCount + 1).some((event) => event.type === "session/end-seed" && event.data.inherited === true)) throw new Error("session inherited event count must identify the final inherited marker");
|
|
5536
5648
|
this.inheritedEventCount = inheritedEventCount;
|
|
5537
|
-
|
|
5538
|
-
|
|
5649
|
+
this.firstLifecycleSeq = mode === "snapshot" && this.header.isSeeded ? inheritedEventCount : this.firstLiveSeq;
|
|
5650
|
+
if (seed !== void 0 && mode === "snapshot" && this.header.isSeeded && !markedSeed) this.append("session/end-seed", { inherited: true });
|
|
5651
|
+
else if (seed !== void 0 && !(mode === "snapshot" && this.header.isSeeded) && this.log.at(-1)?.type !== "session/end-seed") this.append("session/end-seed", {});
|
|
5539
5652
|
}
|
|
5540
5653
|
/** Cached immutable full snapshot of the private append-only log. */
|
|
5541
5654
|
eventsSnapshot;
|
|
@@ -5750,101 +5863,6 @@ var Session = class Session {
|
|
|
5750
5863
|
}
|
|
5751
5864
|
};
|
|
5752
5865
|
//#endregion
|
|
5753
|
-
//#region ../session-persistence/src/errors.ts
|
|
5754
|
-
/**
|
|
5755
|
-
* Stable failures exposed by the session-persistence service and its handles,
|
|
5756
|
-
* including the format refusals shared by every backend: a stored log this
|
|
5757
|
-
* build cannot faithfully interpret is refused, never misread, and the
|
|
5758
|
-
* refusal points at the raw artifact when the backend keeps one per session.
|
|
5759
|
-
* @module @deepseek-ai/dsh-session-persistence/errors
|
|
5760
|
-
*/
|
|
5761
|
-
/** Durable session contents failed validation after a successful backend read. */
|
|
5762
|
-
var SessionPersistenceCorruptionError = class extends Error {
|
|
5763
|
-
/**
|
|
5764
|
-
* @param message - stable corruption context.
|
|
5765
|
-
* @param options - original validation failure.
|
|
5766
|
-
*/
|
|
5767
|
-
constructor(message, options) {
|
|
5768
|
-
super(message, options);
|
|
5769
|
-
this.name = "SessionPersistenceCorruptionError";
|
|
5770
|
-
}
|
|
5771
|
-
};
|
|
5772
|
-
/**
|
|
5773
|
-
* The stored log is intact but this runtime cannot faithfully interpret it:
|
|
5774
|
-
* the header carries an unsupported format version, or an event's type is
|
|
5775
|
-
* unknown to this build. Distinct from {@link SessionPersistenceCorruptionError}
|
|
5776
|
-
* — nothing is damaged; the raw log remains readable at {@link location} when
|
|
5777
|
-
* the backend keeps one artifact per session.
|
|
5778
|
-
*/
|
|
5779
|
-
var SessionFormatUnsupportedError = class extends Error {
|
|
5780
|
-
location;
|
|
5781
|
-
/**
|
|
5782
|
-
* @param message - stable reason the log cannot be interpreted, already
|
|
5783
|
-
* including the raw-log path when one exists.
|
|
5784
|
-
* @param location - the backend's artifact location, when one exists.
|
|
5785
|
-
*/
|
|
5786
|
-
constructor(message, location) {
|
|
5787
|
-
super(message);
|
|
5788
|
-
this.location = location;
|
|
5789
|
-
this.name = "SessionFormatUnsupportedError";
|
|
5790
|
-
}
|
|
5791
|
-
};
|
|
5792
|
-
/**
|
|
5793
|
-
* Direction-aware refusal text for a stored session whose format version this
|
|
5794
|
-
* build does not read. Shared by load-time checks and by backends that must
|
|
5795
|
-
* refuse BEFORE decoding version-dependent structure (a future format may not
|
|
5796
|
-
* satisfy this build's structural checks at all, and the user must see
|
|
5797
|
-
* "upgrade the harness", never "corrupt").
|
|
5798
|
-
* @param id - the stored session id, for message context.
|
|
5799
|
-
* @param version - the stored format version.
|
|
5800
|
-
* @returns the stable refusal text, without a raw-log path suffix.
|
|
5801
|
-
*/
|
|
5802
|
-
function sessionFormatVersionRefusal(id, version) {
|
|
5803
|
-
return version > 3 ? `session "${id}" uses log format v${version}, but this harness reads only v3: the log was written by a newer harness — upgrade the harness to open it` : `session "${id}" uses log format v${version}, older than the supported v3, and this build ships no upgrade path for it`;
|
|
5804
|
-
}
|
|
5805
|
-
//#endregion
|
|
5806
|
-
//#region ../session-persistence/src/storage-contract.ts
|
|
5807
|
-
/**
|
|
5808
|
-
* Backend-shared storage validation: the version gate, the fail-closed event
|
|
5809
|
-
* vocabulary, append-batch materialization, and contiguity — one place so
|
|
5810
|
-
* every backend refuses the same inputs identically.
|
|
5811
|
-
* @module @deepseek-ai/dsh-session-persistence/storage-contract
|
|
5812
|
-
*/
|
|
5813
|
-
/** Build a format refusal that points at the raw artifact when the backend has one. */
|
|
5814
|
-
function unsupported(reason, location) {
|
|
5815
|
-
return new SessionFormatUnsupportedError(location === void 0 ? reason : `${reason} (raw log: ${location.path})`, location);
|
|
5816
|
-
}
|
|
5817
|
-
/**
|
|
5818
|
-
* Validate one exclusively owned stored event array in place: adopt each
|
|
5819
|
-
* record (validating and freezing it) and refuse any event type this build
|
|
5820
|
-
* does not know, unless its writer marked it `ignorable: true` — silently
|
|
5821
|
-
* skipping an unknown required event could reconstruct a wrong session (the
|
|
5822
|
-
* envelope contract on `SessionEvent.ignorable`). Unknown required types and
|
|
5823
|
-
* retired pre-release shapes refuse here; this validator performs no migration.
|
|
5824
|
-
* @param meta - the stored header the events belong to.
|
|
5825
|
-
* @param events - exclusively owned decoded events; validated in place.
|
|
5826
|
-
* @param location - the backend's artifact location for refusals, when one exists.
|
|
5827
|
-
* @returns the same array, validated and frozen.
|
|
5828
|
-
* @throws {SessionFormatUnsupportedError} for unknown event types.
|
|
5829
|
-
* @throws {SessionPersistenceCorruptionError} for records that fail validation.
|
|
5830
|
-
*/
|
|
5831
|
-
function validateStoredEvents(meta, events, location) {
|
|
5832
|
-
for (const event of events) {
|
|
5833
|
-
if (!KNOWN_SESSION_EVENT_TYPES.has(event.type) && event.ignorable !== true) throw unsupported(`session "${meta.id}" contains event type "${event.type}" (seq ${event.seq}) unknown to this harness and not marked ignorable; refusing to interpret the log — it was likely written by a newer harness`, location);
|
|
5834
|
-
if (event.type === "request/header") {
|
|
5835
|
-
const data = event.data;
|
|
5836
|
-
if (typeof data === "object" && data !== null && data["reason"] === "fallback") throw unsupported(`session "${meta.id}" contains a request/header event (seq ${event.seq}) with the unsupported legacy reason "fallback"; refusing to interpret the log — it was written by a retired pre-release harness`, location);
|
|
5837
|
-
}
|
|
5838
|
-
}
|
|
5839
|
-
try {
|
|
5840
|
-
for (const [index, event] of events.entries()) events[index] = adoptSessionEvent(event);
|
|
5841
|
-
} catch (error) {
|
|
5842
|
-
if (error instanceof SessionFormatUnsupportedError) throw error;
|
|
5843
|
-
throw new SessionPersistenceCorruptionError(`stored session "${meta.id}" failed validation: ${String(error)}`, { cause: error });
|
|
5844
|
-
}
|
|
5845
|
-
return events;
|
|
5846
|
-
}
|
|
5847
|
-
//#endregion
|
|
5848
5866
|
//#region ../session-format/src/error.ts
|
|
5849
5867
|
/** Error raised when a durable Session artifact cannot be restored or migrated losslessly. */
|
|
5850
5868
|
var SessionFormatError = class extends Error {
|
|
@@ -6319,6 +6337,101 @@ function sessionFormatLogFilename(version) {
|
|
|
6319
6337
|
return generation === 0 ? "session.jsonl" : `session.v${generation}.jsonl`;
|
|
6320
6338
|
}
|
|
6321
6339
|
//#endregion
|
|
6340
|
+
//#region ../session-persistence/src/errors.ts
|
|
6341
|
+
/**
|
|
6342
|
+
* Stable failures exposed by the session-persistence service and its handles,
|
|
6343
|
+
* including the format refusals shared by every backend: a stored log this
|
|
6344
|
+
* build cannot faithfully interpret is refused, never misread, and the
|
|
6345
|
+
* refusal points at the raw artifact when the backend keeps one per session.
|
|
6346
|
+
* @module @deepseek-ai/dsh-session-persistence/errors
|
|
6347
|
+
*/
|
|
6348
|
+
/** Durable session contents failed validation after a successful backend read. */
|
|
6349
|
+
var SessionPersistenceCorruptionError = class extends Error {
|
|
6350
|
+
/**
|
|
6351
|
+
* @param message - stable corruption context.
|
|
6352
|
+
* @param options - original validation failure.
|
|
6353
|
+
*/
|
|
6354
|
+
constructor(message, options) {
|
|
6355
|
+
super(message, options);
|
|
6356
|
+
this.name = "SessionPersistenceCorruptionError";
|
|
6357
|
+
}
|
|
6358
|
+
};
|
|
6359
|
+
/**
|
|
6360
|
+
* The stored log is intact but this runtime cannot faithfully interpret it:
|
|
6361
|
+
* the header carries an unsupported format version, or an event's type is
|
|
6362
|
+
* unknown to this build. Distinct from {@link SessionPersistenceCorruptionError}
|
|
6363
|
+
* — nothing is damaged; the raw log remains readable at {@link location} when
|
|
6364
|
+
* the backend keeps one artifact per session.
|
|
6365
|
+
*/
|
|
6366
|
+
var SessionFormatUnsupportedError = class extends Error {
|
|
6367
|
+
location;
|
|
6368
|
+
/**
|
|
6369
|
+
* @param message - stable reason the log cannot be interpreted, already
|
|
6370
|
+
* including the raw-log path when one exists.
|
|
6371
|
+
* @param location - the backend's artifact location, when one exists.
|
|
6372
|
+
*/
|
|
6373
|
+
constructor(message, location) {
|
|
6374
|
+
super(message);
|
|
6375
|
+
this.location = location;
|
|
6376
|
+
this.name = "SessionFormatUnsupportedError";
|
|
6377
|
+
}
|
|
6378
|
+
};
|
|
6379
|
+
/**
|
|
6380
|
+
* Direction-aware refusal text for a stored session whose format version this
|
|
6381
|
+
* build does not read. Shared by load-time checks and by backends that must
|
|
6382
|
+
* refuse BEFORE decoding version-dependent structure (a future format may not
|
|
6383
|
+
* satisfy this build's structural checks at all, and the user must see
|
|
6384
|
+
* "upgrade the harness", never "corrupt").
|
|
6385
|
+
* @param id - the stored session id, for message context.
|
|
6386
|
+
* @param version - the stored format version.
|
|
6387
|
+
* @returns the stable refusal text, without a raw-log path suffix.
|
|
6388
|
+
*/
|
|
6389
|
+
function sessionFormatVersionRefusal(id, version) {
|
|
6390
|
+
return version > 4 ? `session "${id}" uses log format v${version}, but this harness reads only v4: the log was written by a newer harness — upgrade the harness to open it` : `session "${id}" uses log format v${version}, older than the supported v4, and this build ships no upgrade path for it`;
|
|
6391
|
+
}
|
|
6392
|
+
//#endregion
|
|
6393
|
+
//#region ../session-persistence/src/storage-contract.ts
|
|
6394
|
+
/**
|
|
6395
|
+
* Backend-shared storage validation: the version gate, the fail-closed event
|
|
6396
|
+
* vocabulary, append-batch materialization, and contiguity — one place so
|
|
6397
|
+
* every backend refuses the same inputs identically.
|
|
6398
|
+
* @module @deepseek-ai/dsh-session-persistence/storage-contract
|
|
6399
|
+
*/
|
|
6400
|
+
/** Build a format refusal that points at the raw artifact when the backend has one. */
|
|
6401
|
+
function unsupported(reason, location) {
|
|
6402
|
+
return new SessionFormatUnsupportedError(location === void 0 ? reason : `${reason} (raw log: ${location.path})`, location);
|
|
6403
|
+
}
|
|
6404
|
+
/**
|
|
6405
|
+
* Validate one exclusively owned stored event array in place: adopt each
|
|
6406
|
+
* record (validating and freezing it) and refuse any event type this build
|
|
6407
|
+
* does not know, unless its writer marked it `ignorable: true` — silently
|
|
6408
|
+
* skipping an unknown required event could reconstruct a wrong session (the
|
|
6409
|
+
* envelope contract on `SessionEvent.ignorable`). Unknown required types and
|
|
6410
|
+
* retired pre-release shapes refuse here; this validator performs no migration.
|
|
6411
|
+
* @param meta - the stored header the events belong to.
|
|
6412
|
+
* @param events - exclusively owned decoded events; validated in place.
|
|
6413
|
+
* @param location - the backend's artifact location for refusals, when one exists.
|
|
6414
|
+
* @returns the same array, validated and frozen.
|
|
6415
|
+
* @throws {SessionFormatUnsupportedError} for unknown event types.
|
|
6416
|
+
* @throws {SessionPersistenceCorruptionError} for records that fail validation.
|
|
6417
|
+
*/
|
|
6418
|
+
function validateStoredEvents(meta, events, location) {
|
|
6419
|
+
for (const event of events) {
|
|
6420
|
+
if (!KNOWN_SESSION_EVENT_TYPES.has(event.type) && event.ignorable !== true) throw unsupported(`session "${meta.id}" contains event type "${event.type}" (seq ${event.seq}) unknown to this harness and not marked ignorable; refusing to interpret the log — it was likely written by a newer harness`, location);
|
|
6421
|
+
if (event.type === "request/header") {
|
|
6422
|
+
const data = event.data;
|
|
6423
|
+
if (typeof data === "object" && data !== null && data["reason"] === "fallback") throw unsupported(`session "${meta.id}" contains a request/header event (seq ${event.seq}) with the unsupported legacy reason "fallback"; refusing to interpret the log — it was written by a retired pre-release harness`, location);
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
try {
|
|
6427
|
+
for (const [index, event] of events.entries()) events[index] = adoptSessionEvent(event);
|
|
6428
|
+
} catch (error) {
|
|
6429
|
+
if (error instanceof SessionFormatUnsupportedError) throw error;
|
|
6430
|
+
throw new SessionPersistenceCorruptionError(`stored session "${meta.id}" failed validation: ${String(error)}`, { cause: error });
|
|
6431
|
+
}
|
|
6432
|
+
return events;
|
|
6433
|
+
}
|
|
6434
|
+
//#endregion
|
|
6322
6435
|
//#region ../session-format-catalog/src/current.ts
|
|
6323
6436
|
/** Current installed Session validation used after vocabulary-aware format restoration. */
|
|
6324
6437
|
/**
|
|
@@ -6327,7 +6440,7 @@ function sessionFormatLogFilename(version) {
|
|
|
6327
6440
|
* @returns nothing after successful validation.
|
|
6328
6441
|
*/
|
|
6329
6442
|
function validateInstalledCurrentSessionHeader(header) {
|
|
6330
|
-
if (header.version !==
|
|
6443
|
+
if (header.version !== 4) throw new Error(`installed Session format is v4, got v${header.version}`);
|
|
6331
6444
|
Session.fromRestore(SessionId(header.id), [], header, SessionLogOffset(0), "detached");
|
|
6332
6445
|
}
|
|
6333
6446
|
/**
|
|
@@ -6336,7 +6449,7 @@ function validateInstalledCurrentSessionHeader(header) {
|
|
|
6336
6449
|
* @returns nothing after successful validation.
|
|
6337
6450
|
*/
|
|
6338
6451
|
function validateInstalledCurrentSessionArtifact(artifact) {
|
|
6339
|
-
if (artifact.header.version !==
|
|
6452
|
+
if (artifact.header.version !== 4) throw new Error(`installed Session format is v4, got v${artifact.header.version}`);
|
|
6340
6453
|
Session.fromRestore(SessionId(artifact.header.id), artifact.events, artifact.header, SessionLogOffset(artifact.inheritedEventCount), "detached", currentSessionMessageProjections);
|
|
6341
6454
|
}
|
|
6342
6455
|
//#endregion
|
|
@@ -8529,7 +8642,7 @@ function malformedLegacy(sessionId, type, seq) {
|
|
|
8529
8642
|
}
|
|
8530
8643
|
//#endregion
|
|
8531
8644
|
//#region ../session-format-v0-to-v1/src/relationships.ts
|
|
8532
|
-
const SURFACE_TYPES$
|
|
8645
|
+
const SURFACE_TYPES$3 = new Set([
|
|
8533
8646
|
"user/message",
|
|
8534
8647
|
"assistant/message",
|
|
8535
8648
|
"tool/result"
|
|
@@ -8558,7 +8671,7 @@ function assertReleasedArtifactRelationships(artifact, extensions = {}) {
|
|
|
8558
8671
|
const extensionStepEvent = extensions.stepEvents?.has(event.type) === true;
|
|
8559
8672
|
if (RELEASED_V0_EVENT_DISPOSITIONS[event.type] === void 0 && !extensionStepEvent) continue;
|
|
8560
8673
|
const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
|
|
8561
|
-
if (SURFACE_TYPES$
|
|
8674
|
+
if (SURFACE_TYPES$3.has(event.type)) surface = applySurface(surface, event);
|
|
8562
8675
|
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`);
|
|
8563
8676
|
if (extensionStepEvent) {
|
|
8564
8677
|
requireOpenStep(event, data, openTurn, openStep);
|
|
@@ -8895,7 +9008,7 @@ const EVENT_REQUIRED$1 = [
|
|
|
8895
9008
|
"time",
|
|
8896
9009
|
"data"
|
|
8897
9010
|
];
|
|
8898
|
-
const SURFACE_TYPES$
|
|
9011
|
+
const SURFACE_TYPES$2 = new Set([
|
|
8899
9012
|
"user/message",
|
|
8900
9013
|
"assistant/message",
|
|
8901
9014
|
"tool/result"
|
|
@@ -8941,7 +9054,7 @@ function validateReleasedV2Artifact(artifact, mode, knownEventTypes, relationshi
|
|
|
8941
9054
|
const installed = knownEventTypes?.has(type) === true;
|
|
8942
9055
|
const ignorableUnknown = disposition === void 0 && record["ignorable"] === true;
|
|
8943
9056
|
if (mode === "current" && disposition === void 0 && !installed && !ignorableUnknown) throw new SessionFormatUnsupportedMigrationError(`format v2 contains unknown event type ${JSON.stringify(type)} at seq ${index}`);
|
|
8944
|
-
const surface = disposition !== void 0 && SURFACE_TYPES$
|
|
9057
|
+
const surface = disposition !== void 0 && SURFACE_TYPES$2.has(type);
|
|
8945
9058
|
assertReleasedV2Keys(record, EVENT_REQUIRED$1, mode === "physical" || disposition === void 0 ? SURFACE_OPTIONAL : surface ? SURFACE_OPTIONAL : LOG_OPTIONAL, `format v2 event ${index}`);
|
|
8946
9059
|
if (record["seq"] !== index) throw new SessionFormatError(`format v2 event ${index} is not dense`);
|
|
8947
9060
|
sessionFormatSafeInteger(record["time"], `format v2 event ${index} time`);
|
|
@@ -9268,7 +9381,7 @@ function transformReleasedEvent(state, event, context) {
|
|
|
9268
9381
|
if (event.type === "assistant/chunk") assertChunkEnvelope(event);
|
|
9269
9382
|
if (RELEASED_V0_EVENT_DISPOSITIONS[event.type] === void 0) throw refusal(`format v1 contains unknown event type ${JSON.stringify(event.type)} at seq ${event.seq}`);
|
|
9270
9383
|
const interrupted = legacyInterruptedTurn(state.legacyTurns, event);
|
|
9271
|
-
if (event.type === "turn/start" && state.legacyTurns.openTurn !== null && interrupted === void 0) throw refusal(`turn/start ${JSON.stringify(record$
|
|
9384
|
+
if (event.type === "turn/start" && state.legacyTurns.openTurn !== null && interrupted === void 0) throw refusal(`turn/start ${JSON.stringify(record$3(event.data)["turn"])} does not close the prior turn`);
|
|
9272
9385
|
assertSourceDeliveryMarker(state, event);
|
|
9273
9386
|
observeLegacyTurn(state.legacyTurns, event);
|
|
9274
9387
|
state.lastTime = event.time;
|
|
@@ -9310,7 +9423,7 @@ function assertChunkEnvelope(event) {
|
|
|
9310
9423
|
}
|
|
9311
9424
|
function assertSourceDeliveryMarker(state, event) {
|
|
9312
9425
|
if (event.type !== "session-log-deepseek/delivery-accepted") return;
|
|
9313
|
-
const data = record$
|
|
9426
|
+
const data = record$3(event.data);
|
|
9314
9427
|
const inherited = state.sourceHeader.parentSession !== void 0 && event.seq < state.sourceCut;
|
|
9315
9428
|
if (data["sessionFormatVersion"] === 1 && !inherited && data["sessionId"] !== state.sourceHeader.id) throw refusal("current-generation delivery marker names the wrong Session");
|
|
9316
9429
|
}
|
|
@@ -9347,10 +9460,10 @@ function finishMigration(state, context) {
|
|
|
9347
9460
|
return state.targetCut;
|
|
9348
9461
|
}
|
|
9349
9462
|
function transformChunk(state, event, context) {
|
|
9350
|
-
const data = record$
|
|
9463
|
+
const data = record$3(event.data);
|
|
9351
9464
|
const turn = coordinate(data["turn"]);
|
|
9352
9465
|
const step = coordinate(data["step"]);
|
|
9353
|
-
const chunk = record$
|
|
9466
|
+
const chunk = record$3(data["chunk"]);
|
|
9354
9467
|
if (state.pending !== void 0 && (state.pending.group.terminal || state.pending.group.turn !== turn || state.pending.group.step !== step)) finishAttempt(state, context);
|
|
9355
9468
|
else if (state.pending !== void 0) flushBuffered(state, state.pending, context);
|
|
9356
9469
|
state.pending ??= {
|
|
@@ -9367,7 +9480,7 @@ function transformChunk(state, event, context) {
|
|
|
9367
9480
|
if (chunk["type"] === "finish") state.pending.group.terminal = true;
|
|
9368
9481
|
}
|
|
9369
9482
|
function transformMessage(state, event, context) {
|
|
9370
|
-
const data = record$
|
|
9483
|
+
const data = record$3(event.data);
|
|
9371
9484
|
const turn = coordinate(data["turn"]);
|
|
9372
9485
|
const step = coordinate(data["step"]);
|
|
9373
9486
|
const sources = event.sourceEventSeqs;
|
|
@@ -9447,8 +9560,8 @@ function legacyTurnState() {
|
|
|
9447
9560
|
};
|
|
9448
9561
|
}
|
|
9449
9562
|
function legacyInterruptedTurn(state, event) {
|
|
9450
|
-
if (event.type !== "turn/start" || state.openTurn === null || state.openStep !== null || coordinate(record$
|
|
9451
|
-
const splice = record$
|
|
9563
|
+
if (event.type !== "turn/start" || state.openTurn === null || state.openStep !== null || coordinate(record$3(event.data)["turn"]) !== state.openTurn + 1 || state.previous?.type !== "agent/inbox/spliced") return void 0;
|
|
9564
|
+
const splice = record$3(state.previous.data);
|
|
9452
9565
|
if (splice["target"] !== "next-turn" || !Array.isArray(splice["inserted"]) || splice["inserted"].length === 0) return;
|
|
9453
9566
|
return {
|
|
9454
9567
|
type: "turn/end",
|
|
@@ -9461,7 +9574,7 @@ function legacyInterruptedTurn(state, event) {
|
|
|
9461
9574
|
};
|
|
9462
9575
|
}
|
|
9463
9576
|
function observeLegacyTurn(state, event) {
|
|
9464
|
-
const data = record$
|
|
9577
|
+
const data = record$3(event.data);
|
|
9465
9578
|
if (event.type === "turn/start") {
|
|
9466
9579
|
state.openTurn = coordinate(data["turn"]);
|
|
9467
9580
|
state.openStep = null;
|
|
@@ -9474,8 +9587,8 @@ function observeLegacyTurn(state, event) {
|
|
|
9474
9587
|
}
|
|
9475
9588
|
function splitLegacyGoalChange(event) {
|
|
9476
9589
|
if (event.type !== "user/message") return void 0;
|
|
9477
|
-
const data = record$
|
|
9478
|
-
const source = record$
|
|
9590
|
+
const data = record$3(event.data);
|
|
9591
|
+
const source = record$3(data["source"]);
|
|
9479
9592
|
if (source["kind"] !== "goal" || source["change"] === void 0) return void 0;
|
|
9480
9593
|
return {
|
|
9481
9594
|
change: {
|
|
@@ -9594,7 +9707,7 @@ function streamOf(group) {
|
|
|
9594
9707
|
return group.stream.map(({ record }) => record);
|
|
9595
9708
|
}
|
|
9596
9709
|
function messageEvent(source, group) {
|
|
9597
|
-
const data = record$
|
|
9710
|
+
const data = record$3(source.data);
|
|
9598
9711
|
const { sourceEventSeqs: _sourceEventSeqs, ...event } = source;
|
|
9599
9712
|
return {
|
|
9600
9713
|
...event,
|
|
@@ -9621,7 +9734,7 @@ function remapReferences(source, targetSeq, mapping) {
|
|
|
9621
9734
|
const sources = sourceEventSeqs === void 0 ? {} : { sourceEventSeqs: mapList(numberArray(sourceEventSeqs), mapping, `${source.type} ${source.seq} sources`) };
|
|
9622
9735
|
let operation = surfaceOp;
|
|
9623
9736
|
if (surfaceOp !== void 0 && surfaceOp !== "append") {
|
|
9624
|
-
const replacement = record$
|
|
9737
|
+
const replacement = record$3(surfaceOp);
|
|
9625
9738
|
operation = {
|
|
9626
9739
|
op: "replace",
|
|
9627
9740
|
start: mapOne(coordinate(replacement["start"]), mapping, `${source.type} ${source.seq} surface start`),
|
|
@@ -9637,7 +9750,7 @@ function remapReferences(source, targetSeq, mapping) {
|
|
|
9637
9750
|
};
|
|
9638
9751
|
}
|
|
9639
9752
|
function remapPayloadReferences(event, mapping) {
|
|
9640
|
-
const data = record$
|
|
9753
|
+
const data = record$3(event.data);
|
|
9641
9754
|
switch (event.type) {
|
|
9642
9755
|
case "command/done": return data["sourceEventSeq"] === void 0 ? data : {
|
|
9643
9756
|
...data,
|
|
@@ -9645,7 +9758,7 @@ function remapPayloadReferences(event, mapping) {
|
|
|
9645
9758
|
};
|
|
9646
9759
|
case "compaction/prune":
|
|
9647
9760
|
case "compaction/summary": {
|
|
9648
|
-
const range = record$
|
|
9761
|
+
const range = record$3(data["shadowedRange"]);
|
|
9649
9762
|
return {
|
|
9650
9763
|
...data,
|
|
9651
9764
|
shadowedRange: {
|
|
@@ -9671,7 +9784,7 @@ function mapOne(value, mapping, label) {
|
|
|
9671
9784
|
if (mapped === void 0) throw refusal(`${label} targets consumed assistant/chunk ${value}`);
|
|
9672
9785
|
return mapped;
|
|
9673
9786
|
}
|
|
9674
|
-
function record$
|
|
9787
|
+
function record$3(value) {
|
|
9675
9788
|
return value;
|
|
9676
9789
|
}
|
|
9677
9790
|
function numberArray(value) {
|
|
@@ -9687,7 +9800,7 @@ function refusal(message) {
|
|
|
9687
9800
|
//#region ../session-format-v2-to-v3/src/payload.ts
|
|
9688
9801
|
/** Audited V2 migration admission and V3 payload validation, independent of installed core Session types. */
|
|
9689
9802
|
/** Audited surface event names; all other admitted events are log-only. */
|
|
9690
|
-
const SURFACE_TYPES = new Set([
|
|
9803
|
+
const SURFACE_TYPES$1 = new Set([
|
|
9691
9804
|
"system/message",
|
|
9692
9805
|
"user/message",
|
|
9693
9806
|
"assistant/message",
|
|
@@ -9716,7 +9829,7 @@ const SOURCE_KINDS = new Set([
|
|
|
9716
9829
|
* @param label - diagnostic subject.
|
|
9717
9830
|
* @returns the narrowed object.
|
|
9718
9831
|
*/
|
|
9719
|
-
function record(value, label) {
|
|
9832
|
+
function record$2(value, label) {
|
|
9720
9833
|
if (!isSessionFormatJsonObject(value)) throw new SessionFormatError(label + " must be an object");
|
|
9721
9834
|
return value;
|
|
9722
9835
|
}
|
|
@@ -9746,7 +9859,7 @@ function assertEvent(event, version) {
|
|
|
9746
9859
|
const disposition = RELEASED_V2_EVENT_DISPOSITIONS[event.type];
|
|
9747
9860
|
const feedback = event.type === "feedback/message-put" || event.type === "feedback/message-delete";
|
|
9748
9861
|
if (disposition === void 0 && !feedback) throw new SessionFormatUnsupportedMigrationError("format v2 to v3 cannot safely transform unclassified event " + event.type);
|
|
9749
|
-
const surface = SURFACE_TYPES.has(event.type);
|
|
9862
|
+
const surface = SURFACE_TYPES$1.has(event.type);
|
|
9750
9863
|
keys(event, [
|
|
9751
9864
|
"type",
|
|
9752
9865
|
"seq",
|
|
@@ -9764,7 +9877,7 @@ function assertEvent(event, version) {
|
|
|
9764
9877
|
assertReleasedSurfaceMetadata(event, event.seq, event.type, "forbid-assistant");
|
|
9765
9878
|
if (event["surfaceOp"] === void 0) throw new SessionFormatError(event.type + " requires surfaceOp");
|
|
9766
9879
|
}
|
|
9767
|
-
const data = record(event.data, event.type + " data");
|
|
9880
|
+
const data = record$2(event.data, event.type + " data");
|
|
9768
9881
|
if (feedback) {
|
|
9769
9882
|
assertFeedback(event.type, data);
|
|
9770
9883
|
return;
|
|
@@ -9778,10 +9891,10 @@ function assertEvent(event, version) {
|
|
|
9778
9891
|
}
|
|
9779
9892
|
if (event.type === "session/end-seed" && data["inherited"] !== void 0 && data["inherited"] !== true) throw new SessionFormatError("session/end-seed inherited must be true");
|
|
9780
9893
|
if (event.type === "user/message") assertSource(data);
|
|
9781
|
-
if (event.type === "assistant/message" || event.type === "tool/result") assertSource(record(data["message"], "message"));
|
|
9894
|
+
if (event.type === "assistant/message" || event.type === "tool/result") assertSource(record$2(data["message"], "message"));
|
|
9782
9895
|
if (event.type === "tool/result" && isSessionFormatJsonObject(data["error"]) && data["error"]["code"] === "TOOL_NOT_STARTED") {
|
|
9783
|
-
const message = record(data["message"], "tool result message");
|
|
9784
|
-
const source = record(message["source"], "tool result source");
|
|
9896
|
+
const message = record$2(data["message"], "tool result message");
|
|
9897
|
+
const source = record$2(message["source"], "tool result source");
|
|
9785
9898
|
if (!isRepairIdentity(message["id"], source["callId"])) throw new SessionFormatError("TOOL_NOT_STARTED repair requires its canonical historical message id");
|
|
9786
9899
|
}
|
|
9787
9900
|
if (event.type === "agent/inbox/spliced" || event.type === "session/title-llm-request") {
|
|
@@ -9803,7 +9916,7 @@ function isRepairIdentity(id, callId) {
|
|
|
9803
9916
|
return /^(0|[1-9]\d*)$/.test(suffix) && Number.isSafeInteger(Number(suffix));
|
|
9804
9917
|
}
|
|
9805
9918
|
function assertSource(message) {
|
|
9806
|
-
const source = record(message["source"], "message source");
|
|
9919
|
+
const source = record$2(message["source"], "message source");
|
|
9807
9920
|
if (typeof source["kind"] !== "string" || !SOURCE_KINDS.has(source["kind"])) throw new SessionFormatUnsupportedMigrationError("cannot safely transform unclassified message source");
|
|
9808
9921
|
if (source["kind"] === "agent-message") {
|
|
9809
9922
|
keys(source, [
|
|
@@ -9836,14 +9949,14 @@ function assertOwnedContent(event, data) {
|
|
|
9836
9949
|
case "assistant/message":
|
|
9837
9950
|
case "tool/result":
|
|
9838
9951
|
case "team/message/queued":
|
|
9839
|
-
assertContentKinds(record(data["message"], label + ".message")["content"], label + ".message.content");
|
|
9952
|
+
assertContentKinds(record$2(data["message"], label + ".message")["content"], label + ".message.content");
|
|
9840
9953
|
break;
|
|
9841
9954
|
case "agent/inbox/spliced":
|
|
9842
9955
|
case "session/title-llm-request": {
|
|
9843
9956
|
const field = event.type === "agent/inbox/spliced" ? "inserted" : "messages";
|
|
9844
9957
|
for (const [index, value] of contentArray(data[field], label + "." + field).entries()) {
|
|
9845
9958
|
const path = label + "." + field + "[" + String(index) + "]";
|
|
9846
|
-
assertContentKinds(record(value, path)["content"], path + ".content");
|
|
9959
|
+
assertContentKinds(record$2(value, path)["content"], path + ".content");
|
|
9847
9960
|
}
|
|
9848
9961
|
break;
|
|
9849
9962
|
}
|
|
@@ -9854,9 +9967,9 @@ function assertOwnedContent(event, data) {
|
|
|
9854
9967
|
}
|
|
9855
9968
|
if (event.type === "assistant/message" || event.type === "assistant/attempt") for (const [index, value] of contentArray(data["stream"], label + ".stream").entries()) {
|
|
9856
9969
|
const path = label + ".stream[" + String(index) + "]";
|
|
9857
|
-
const entry = record(value, path);
|
|
9970
|
+
const entry = record$2(value, path);
|
|
9858
9971
|
if (entry["type"] !== "chunk") continue;
|
|
9859
|
-
const chunk = record(entry["chunk"], path + ".chunk");
|
|
9972
|
+
const chunk = record$2(entry["chunk"], path + ".chunk");
|
|
9860
9973
|
if (chunk["type"] === "block-end") assertContentBlock(chunk["block"], path + ".chunk.block");
|
|
9861
9974
|
if (chunk["type"] === "block-start") assertContentKind(chunk["blockType"], path + ".chunk.blockType");
|
|
9862
9975
|
}
|
|
@@ -9868,7 +9981,7 @@ function assertContentKinds(content, label) {
|
|
|
9868
9981
|
for (const [index, value] of contentArray(content, label).entries()) assertContentBlock(value, label + "[" + String(index) + "]");
|
|
9869
9982
|
}
|
|
9870
9983
|
function assertContentBlock(value, label) {
|
|
9871
|
-
const block = record(value, label);
|
|
9984
|
+
const block = record$2(value, label);
|
|
9872
9985
|
assertContentKind(block["type"], label);
|
|
9873
9986
|
if (block["type"] === "tool-result") {
|
|
9874
9987
|
if (!Array.isArray(block["content"])) throw new SessionFormatError(label + ".content: invalid message content kind \"tool-result\": content must be an array");
|
|
@@ -9876,7 +9989,7 @@ function assertContentBlock(value, label) {
|
|
|
9876
9989
|
}
|
|
9877
9990
|
if (block["type"] === "file") {
|
|
9878
9991
|
keys(block, ["type", "attachment"], [], label + " kind \"file\"");
|
|
9879
|
-
const attachment = record(block["attachment"], label + " kind \"file\" attachment");
|
|
9992
|
+
const attachment = record$2(block["attachment"], label + " kind \"file\" attachment");
|
|
9880
9993
|
keys(attachment, [
|
|
9881
9994
|
"attachmentId",
|
|
9882
9995
|
"name",
|
|
@@ -9914,10 +10027,10 @@ function assertV3StructuralRow(value) {
|
|
|
9914
10027
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
9915
10028
|
const row = value;
|
|
9916
10029
|
if (row["type"] === "request/header") {
|
|
9917
|
-
const data = record(row["data"], "request/header data");
|
|
9918
|
-
if (Object.hasOwn(record(data["header"], "request header"), "system")) throw new SessionFormatUnsupportedMigrationError("format v3 request/header rejects retired header.system");
|
|
10030
|
+
const data = record$2(row["data"], "request/header data");
|
|
10031
|
+
if (Object.hasOwn(record$2(data["header"], "request header"), "system")) throw new SessionFormatUnsupportedMigrationError("format v3 request/header rejects retired header.system");
|
|
9919
10032
|
} else if (row["type"] === "system/message") {
|
|
9920
|
-
const data = record(row["data"], "system/message data");
|
|
10033
|
+
const data = record$2(row["data"], "system/message data");
|
|
9921
10034
|
assertSystem({
|
|
9922
10035
|
type: "system/message",
|
|
9923
10036
|
seq: 0,
|
|
@@ -9933,7 +10046,7 @@ function assertSystem(event, data) {
|
|
|
9933
10046
|
"message"
|
|
9934
10047
|
], [], "system/message data");
|
|
9935
10048
|
for (const coordinate of ["turn", "step"]) if (sessionFormatCount(data[coordinate], coordinate) === 0) throw new SessionFormatError(coordinate + " must be positive");
|
|
9936
|
-
const message = record(data["message"], "system message");
|
|
10049
|
+
const message = record$2(data["message"], "system message");
|
|
9937
10050
|
keys(message, [
|
|
9938
10051
|
"id",
|
|
9939
10052
|
"role",
|
|
@@ -9941,7 +10054,7 @@ function assertSystem(event, data) {
|
|
|
9941
10054
|
"content"
|
|
9942
10055
|
], [], "system message");
|
|
9943
10056
|
if (typeof message["id"] !== "string" || message["id"].length === 0 || message["role"] !== "system") throw new SessionFormatError("system message requires an id and system role");
|
|
9944
|
-
const source = record(message["source"], "system source");
|
|
10057
|
+
const source = record$2(message["source"], "system source");
|
|
9945
10058
|
if (source["kind"] !== "plugin" || typeof source["plugin"] !== "string" || source["plugin"].length === 0) throw new SessionFormatError("system message requires plugin source");
|
|
9946
10059
|
assertReleasedPayloadSemantics({
|
|
9947
10060
|
...event,
|
|
@@ -9959,7 +10072,7 @@ function assertFeedback(type, data) {
|
|
|
9959
10072
|
if (typeof data["messageId"] !== "string") throw new SessionFormatError("feedback messageId must be a string");
|
|
9960
10073
|
return;
|
|
9961
10074
|
}
|
|
9962
|
-
const item = record(data["item"], "feedback item");
|
|
10075
|
+
const item = record$2(data["item"], "feedback item");
|
|
9963
10076
|
keys(item, [
|
|
9964
10077
|
"messageId",
|
|
9965
10078
|
"rating",
|
|
@@ -9980,15 +10093,15 @@ function assertFeedback(type, data) {
|
|
|
9980
10093
|
* @param knownEventTypes - additional installed event types whose envelopes are interpreted.
|
|
9981
10094
|
*/
|
|
9982
10095
|
function assertV3Event(event, knownEventTypes) {
|
|
9983
|
-
const value = record(event, "format v3 event");
|
|
10096
|
+
const value = record$2(event, "format v3 event");
|
|
9984
10097
|
const subject = `format v3 ${event.type} at seq ${event.seq}`;
|
|
9985
|
-
const opaque = !(!(event.type === "tool/code-dispatch-start" || event.type === "tool/code-dispatch") && (SURFACE_TYPES.has(event.type) || RELEASED_V2_EVENT_DISPOSITIONS[event.type] !== void 0 || event.type === "tool/ptc-dispatch-start" || event.type === "tool/ptc-dispatch" || event.type === "feedback/message-put" || event.type === "feedback/message-delete" || knownEventTypes?.has(event.type) === true));
|
|
10098
|
+
const opaque = !(!(event.type === "tool/code-dispatch-start" || event.type === "tool/code-dispatch") && (SURFACE_TYPES$1.has(event.type) || RELEASED_V2_EVENT_DISPOSITIONS[event.type] !== void 0 || event.type === "tool/ptc-dispatch-start" || event.type === "tool/ptc-dispatch" || event.type === "feedback/message-put" || event.type === "feedback/message-delete" || knownEventTypes?.has(event.type) === true));
|
|
9986
10099
|
keys(value, [
|
|
9987
10100
|
"type",
|
|
9988
10101
|
"seq",
|
|
9989
10102
|
"time",
|
|
9990
10103
|
"data"
|
|
9991
|
-
], SURFACE_TYPES.has(event.type) || opaque ? [
|
|
10104
|
+
], SURFACE_TYPES$1.has(event.type) || opaque ? [
|
|
9992
10105
|
"ignorable",
|
|
9993
10106
|
"surfaceOp",
|
|
9994
10107
|
"sourceEventSeqs"
|
|
@@ -9997,11 +10110,11 @@ function assertV3Event(event, knownEventTypes) {
|
|
|
9997
10110
|
sessionFormatCount(event.seq, `${subject} seq`);
|
|
9998
10111
|
sessionFormatSafeInteger(event.time, `${subject} time`);
|
|
9999
10112
|
if (Object.hasOwn(value, "ignorable") && value["ignorable"] !== true) throw new SessionFormatError(`${subject} ignorable must be true when present`);
|
|
10000
|
-
if (SURFACE_TYPES.has(event.type)) {
|
|
10113
|
+
if (SURFACE_TYPES$1.has(event.type)) {
|
|
10001
10114
|
const operation = value["surfaceOp"];
|
|
10002
10115
|
if (operation === void 0) throw new SessionFormatError(`${subject} requires a surfaceOp marker`);
|
|
10003
10116
|
if (operation !== "append") {
|
|
10004
|
-
const replace = record(operation, `${subject} surfaceOp`);
|
|
10117
|
+
const replace = record$2(operation, `${subject} surfaceOp`);
|
|
10005
10118
|
if (Object.keys(replace).length !== 3 || replace["op"] !== "replace" || !Object.hasOwn(replace, "startSeq") || !Object.hasOwn(replace, "endSeq")) throw new SessionFormatError(`${subject} requires exact replace fields op/startSeq/endSeq`);
|
|
10006
10119
|
for (const key of ["startSeq", "endSeq"]) if (sessionFormatCount(replace[key], `${subject} surfaceOp ${key}`) >= event.seq) throw new SessionFormatError(`${subject} replacement endpoints must reference earlier events`);
|
|
10007
10120
|
}
|
|
@@ -10023,13 +10136,13 @@ function assertV3Event(event, knownEventTypes) {
|
|
|
10023
10136
|
function assertCanonicalPayload(event) {
|
|
10024
10137
|
const subject = `format v3 ${event.type} at seq ${event.seq}`;
|
|
10025
10138
|
if (event.type === "request/header") {
|
|
10026
|
-
const header = record(record(event.data, `${subject} data`)["header"], `${subject} header`);
|
|
10139
|
+
const header = record$2(record$2(event.data, `${subject} data`)["header"], `${subject} header`);
|
|
10027
10140
|
if (Array.isArray(header["tools"]) && header["tools"].length === 0 || isSessionFormatJsonObject(header["adapterDefaults"]) && Object.keys(header["adapterDefaults"]).length === 0) throw new SessionFormatError(`${subject} empty optional header fields must be omitted`);
|
|
10028
10141
|
}
|
|
10029
10142
|
if (event.type !== "tool/result") return;
|
|
10030
|
-
const data = record(event.data, `${subject} data`);
|
|
10143
|
+
const data = record$2(event.data, `${subject} data`);
|
|
10031
10144
|
if (data["error"] === void 0) return;
|
|
10032
|
-
const content = record(data["message"], `${subject} message`)["content"];
|
|
10145
|
+
const content = record$2(data["message"], `${subject} message`)["content"];
|
|
10033
10146
|
if (!Array.isArray(content) || content.length !== 1 || !isSessionFormatJsonObject(content[0]) || content[0]["type"] !== "tool-result" || content[0]["isError"] !== true) throw new SessionFormatError(`${subject} carries error metadata for a non-error tool result`);
|
|
10034
10147
|
}
|
|
10035
10148
|
/**
|
|
@@ -10041,7 +10154,7 @@ function canonicalizeTransformedEvent(event) {
|
|
|
10041
10154
|
let target = event;
|
|
10042
10155
|
const operation = event["surfaceOp"];
|
|
10043
10156
|
if (operation !== void 0 && operation !== "append") {
|
|
10044
|
-
const replace = record(operation, `format v2 ${event.type} at seq ${event.seq} surfaceOp`);
|
|
10157
|
+
const replace = record$2(operation, `format v2 ${event.type} at seq ${event.seq} surfaceOp`);
|
|
10045
10158
|
if (Object.keys(replace).length !== 3 || replace["op"] !== "replace" || !Object.hasOwn(replace, "start") || !Object.hasOwn(replace, "end")) throw new SessionFormatError(`format v2 ${event.type} at seq ${event.seq} requires exact replace fields op/start/end`);
|
|
10046
10159
|
target = {
|
|
10047
10160
|
...event,
|
|
@@ -10053,8 +10166,8 @@ function canonicalizeTransformedEvent(event) {
|
|
|
10053
10166
|
};
|
|
10054
10167
|
}
|
|
10055
10168
|
if (event.type === "request/header") {
|
|
10056
|
-
const data = record(event.data, `format v2 request/header at seq ${event.seq} data`);
|
|
10057
|
-
const header = record(data["header"], `format v2 request/header at seq ${event.seq} header`);
|
|
10169
|
+
const data = record$2(event.data, `format v2 request/header at seq ${event.seq} data`);
|
|
10170
|
+
const header = record$2(data["header"], `format v2 request/header at seq ${event.seq} header`);
|
|
10058
10171
|
const empty = Object.keys(header).filter((key) => key === "tools" && Array.isArray(header[key]) && header[key].length === 0 || key === "adapterDefaults" && isSessionFormatJsonObject(header[key]) && Object.keys(header[key]).length === 0);
|
|
10059
10172
|
if (empty.length > 0) {
|
|
10060
10173
|
const canonical = Object.fromEntries(Object.entries(header).filter(([key]) => !empty.includes(key)));
|
|
@@ -10101,37 +10214,37 @@ function restoreReleasedV3Artifact(artifact, knownEventTypes) {
|
|
|
10101
10214
|
assertV3Event(event, knownEventTypes);
|
|
10102
10215
|
const system = event.type === "system/message";
|
|
10103
10216
|
if (event.type === "step/start") {
|
|
10104
|
-
const data = record(event.data, event.type);
|
|
10217
|
+
const data = record$2(event.data, event.type);
|
|
10105
10218
|
step = {
|
|
10106
10219
|
turn: data["turn"],
|
|
10107
10220
|
step: data["step"]
|
|
10108
10221
|
};
|
|
10109
10222
|
} else if (event.type === "step/end" || event.type === "turn/end") step = void 0;
|
|
10110
10223
|
if (system) {
|
|
10111
|
-
const data = record(event.data, "system/message");
|
|
10224
|
+
const data = record$2(event.data, "system/message");
|
|
10112
10225
|
if (step === void 0 || step.turn !== data["turn"] || step.step !== data["step"]) throw new SessionFormatError("system/message does not match an open step");
|
|
10113
10226
|
const operation = event["surfaceOp"];
|
|
10114
10227
|
if (hasSurface && head === void 0) throw new SessionFormatError("system/message requires a protected first surface head");
|
|
10115
10228
|
if (operation === "append") {
|
|
10116
10229
|
if (!hasSurface) head = event.seq;
|
|
10117
10230
|
} else {
|
|
10118
|
-
const replace = record(operation, "system replacement");
|
|
10231
|
+
const replace = record$2(operation, "system replacement");
|
|
10119
10232
|
if (replace["startSeq"] === head || replace["endSeq"] === head) {
|
|
10120
10233
|
if (replace["startSeq"] !== head || replace["endSeq"] !== head) throw new SessionFormatError("system/message must replace exactly the current system head");
|
|
10121
10234
|
head = event.seq;
|
|
10122
10235
|
}
|
|
10123
10236
|
}
|
|
10124
|
-
} else if (SURFACE_TYPES.has(event.type) && event["surfaceOp"] !== "append") {
|
|
10125
|
-
const replace = record(event["surfaceOp"], "surface replacement");
|
|
10237
|
+
} else if (SURFACE_TYPES$1.has(event.type) && event["surfaceOp"] !== "append") {
|
|
10238
|
+
const replace = record$2(event["surfaceOp"], "surface replacement");
|
|
10126
10239
|
if (replace["startSeq"] === head || replace["endSeq"] === head) throw new SessionFormatError("surface replacement cannot shadow the protected system head");
|
|
10127
10240
|
}
|
|
10128
10241
|
if (event.type === "compaction/prune" || event.type === "compaction/summary") {
|
|
10129
|
-
const seqs = record(event.data, event.type)["shadowedSeqs"];
|
|
10242
|
+
const seqs = record$2(event.data, event.type)["shadowedSeqs"];
|
|
10130
10243
|
if (Array.isArray(seqs) && seqs.some((seq) => seq === head)) throw new SessionFormatError("compaction cannot shadow the protected system head");
|
|
10131
10244
|
}
|
|
10132
|
-
if (SURFACE_TYPES.has(event.type)) hasSurface = true;
|
|
10245
|
+
if (SURFACE_TYPES$1.has(event.type)) hasSurface = true;
|
|
10133
10246
|
const projected = relationshipEvent(event);
|
|
10134
|
-
if (!SURFACE_TYPES.has(event.type) || event["surfaceOp"] === "append") return projected;
|
|
10247
|
+
if (!SURFACE_TYPES$1.has(event.type) || event["surfaceOp"] === "append") return projected;
|
|
10135
10248
|
const replacement = event["surfaceOp"];
|
|
10136
10249
|
return {
|
|
10137
10250
|
...projected,
|
|
@@ -10178,7 +10291,7 @@ function relationshipEvent(event) {
|
|
|
10178
10291
|
};
|
|
10179
10292
|
}
|
|
10180
10293
|
if (event.type === "system/message") {
|
|
10181
|
-
const message = record(record(event.data, "system data")["message"], "system message");
|
|
10294
|
+
const message = record$2(record$2(event.data, "system data")["message"], "system message");
|
|
10182
10295
|
return {
|
|
10183
10296
|
...event,
|
|
10184
10297
|
type: "user/message",
|
|
@@ -10189,11 +10302,11 @@ function relationshipEvent(event) {
|
|
|
10189
10302
|
};
|
|
10190
10303
|
}
|
|
10191
10304
|
if (event.type !== "tool/result") return event;
|
|
10192
|
-
const data = record(event.data, "tool result");
|
|
10305
|
+
const data = record$2(event.data, "tool result");
|
|
10193
10306
|
if (data["error"] === void 0) return event;
|
|
10194
|
-
if (record(data["error"], "tool error")["code"] !== "TOOL_NOT_STARTED") return event;
|
|
10195
|
-
const message = record(data["message"], "tool message");
|
|
10196
|
-
const callId = record(message["source"], "tool source")["callId"];
|
|
10307
|
+
if (record$2(data["error"], "tool error")["code"] !== "TOOL_NOT_STARTED") return event;
|
|
10308
|
+
const message = record$2(data["message"], "tool message");
|
|
10309
|
+
const callId = record$2(message["source"], "tool source")["callId"];
|
|
10197
10310
|
const id = message["id"];
|
|
10198
10311
|
if (!isRepairIdentity(id, callId)) return event;
|
|
10199
10312
|
const prefix = "interrupted-tool-result-" + callId + "-";
|
|
@@ -10314,14 +10427,14 @@ function remapEvent(event, seq, mapping) {
|
|
|
10314
10427
|
return value.map(one);
|
|
10315
10428
|
};
|
|
10316
10429
|
const range = (value) => {
|
|
10317
|
-
const source = record(value, "sequence range");
|
|
10430
|
+
const source = record$2(value, "sequence range");
|
|
10318
10431
|
return {
|
|
10319
10432
|
...source,
|
|
10320
10433
|
start: one(source["start"]),
|
|
10321
10434
|
end: one(source["end"])
|
|
10322
10435
|
};
|
|
10323
10436
|
};
|
|
10324
|
-
let data = record(event.data, event.type);
|
|
10437
|
+
let data = record$2(event.data, event.type);
|
|
10325
10438
|
switch (event.type) {
|
|
10326
10439
|
case "command/done":
|
|
10327
10440
|
if (data["sourceEventSeq"] !== void 0) data = {
|
|
@@ -10399,9 +10512,9 @@ var ReleasedV2ToV3Stage = class {
|
|
|
10399
10512
|
assertEvent(event, 2);
|
|
10400
10513
|
this.observeMessageIds(event);
|
|
10401
10514
|
let source = event;
|
|
10402
|
-
const data = record(event.data, event.type);
|
|
10515
|
+
const data = record$2(event.data, event.type);
|
|
10403
10516
|
if (event.type === "request/header") {
|
|
10404
|
-
const { system, ...header } = record(data["header"], "request header");
|
|
10517
|
+
const { system, ...header } = record$2(data["header"], "request header");
|
|
10405
10518
|
const prompt = typeof system === "string" ? system : "";
|
|
10406
10519
|
if (prompt !== this.prompt) this.emitSystem(prompt, event, context);
|
|
10407
10520
|
source = {
|
|
@@ -10412,7 +10525,7 @@ var ReleasedV2ToV3Stage = class {
|
|
|
10412
10525
|
}
|
|
10413
10526
|
};
|
|
10414
10527
|
}
|
|
10415
|
-
if (SURFACE_TYPES.has(event.type) && this.head === void 0) throw new SessionFormatUnsupportedMigrationError("format v2 surface before first step cannot acquire a system head without changing chronology");
|
|
10528
|
+
if (SURFACE_TYPES$1.has(event.type) && this.head === void 0) throw new SessionFormatUnsupportedMigrationError("format v2 surface before first step cannot acquire a system head without changing chronology");
|
|
10416
10529
|
if (event.type === "session/end-seed" && data["inherited"] === true) {
|
|
10417
10530
|
if (!this.input.sourceHeader.isSeeded) throw new SessionFormatError("format v2 unseeded Session contains an inherited end-seed marker");
|
|
10418
10531
|
this.sourceCut = event.seq;
|
|
@@ -10443,8 +10556,8 @@ var ReleasedV2ToV3Stage = class {
|
|
|
10443
10556
|
return sessionFormatCount(this.targetCut, "format v3 inherited event count");
|
|
10444
10557
|
}
|
|
10445
10558
|
observeMessageIds(event) {
|
|
10446
|
-
const data = record(event.data, event.type);
|
|
10447
|
-
const messages = event.type === "user/message" ? [data] : event.type === "assistant/message" || event.type === "tool/result" ? [record(data["message"], "message")] : event.type === "agent/inbox/spliced" ? data["inserted"] : event.type === "session/title-llm-request" ? data["messages"] : [];
|
|
10559
|
+
const data = record$2(event.data, event.type);
|
|
10560
|
+
const messages = event.type === "user/message" ? [data] : event.type === "assistant/message" || event.type === "tool/result" ? [record$2(data["message"], "message")] : event.type === "agent/inbox/spliced" ? data["inserted"] : event.type === "session/title-llm-request" ? data["messages"] : [];
|
|
10448
10561
|
for (const message of messages) {
|
|
10449
10562
|
const id = message["id"];
|
|
10450
10563
|
if (this.generatedIds.has(id)) throw new SessionFormatUnsupportedMigrationError("source message id collides with a generated system message id");
|
|
@@ -10549,40 +10662,1067 @@ function renameMessageSource(message) {
|
|
|
10549
10662
|
};
|
|
10550
10663
|
}
|
|
10551
10664
|
//#endregion
|
|
10552
|
-
//#region ../session-format-
|
|
10665
|
+
//#region ../session-format-v3-to-v4/src/sources.ts
|
|
10666
|
+
/** Released V3 plugin-source conversion and declared message traversal. */
|
|
10667
|
+
/**
|
|
10668
|
+
* Visit only messages carried by first-party event payloads.
|
|
10669
|
+
* @param event - source or target logical event.
|
|
10670
|
+
* @param transform - message transform preserving unchanged identity.
|
|
10671
|
+
* @returns the event sharing all untouched payloads.
|
|
10672
|
+
*/
|
|
10673
|
+
function mapEventMessages(event, transform) {
|
|
10674
|
+
const data = event.data;
|
|
10675
|
+
if (!isSessionFormatJsonObject(data)) return event;
|
|
10676
|
+
if (event.type === "user/message") {
|
|
10677
|
+
const message = transform(data);
|
|
10678
|
+
return message === data ? event : {
|
|
10679
|
+
...event,
|
|
10680
|
+
data: message
|
|
10681
|
+
};
|
|
10682
|
+
}
|
|
10683
|
+
if (event.type === "developer/message" || event.type === "system/message" || event.type === "assistant/message" || event.type === "tool/result") {
|
|
10684
|
+
if (!isSessionFormatJsonObject(data["message"])) throw new SessionFormatError(event.type + " requires a message");
|
|
10685
|
+
const message = transform(data["message"]);
|
|
10686
|
+
return message === data["message"] ? event : {
|
|
10687
|
+
...event,
|
|
10688
|
+
data: {
|
|
10689
|
+
...data,
|
|
10690
|
+
message
|
|
10691
|
+
}
|
|
10692
|
+
};
|
|
10693
|
+
}
|
|
10694
|
+
const key = event.type === "agent/inbox/spliced" ? "inserted" : event.type === "session/title-llm-request" ? "messages" : void 0;
|
|
10695
|
+
if (key === void 0) return event;
|
|
10696
|
+
const messages = data[key];
|
|
10697
|
+
if (!Array.isArray(messages)) throw new SessionFormatError(event.type + " requires message array");
|
|
10698
|
+
const mapped = messages.map((message) => {
|
|
10699
|
+
if (!isSessionFormatJsonObject(message)) throw new SessionFormatError(event.type + " requires message objects");
|
|
10700
|
+
return transform(message);
|
|
10701
|
+
});
|
|
10702
|
+
return mapped.every((message, index) => message === messages[index]) ? event : {
|
|
10703
|
+
...event,
|
|
10704
|
+
data: {
|
|
10705
|
+
...data,
|
|
10706
|
+
[key]: mapped
|
|
10707
|
+
}
|
|
10708
|
+
};
|
|
10709
|
+
}
|
|
10710
|
+
Object.freeze({
|
|
10711
|
+
"compact": "compact-checkpoint",
|
|
10712
|
+
"tools-code-mode": "ptc-mode",
|
|
10713
|
+
"tools-ptc": "ptc-mode",
|
|
10714
|
+
"dsh-compaction-basic": "compact-basic",
|
|
10715
|
+
"@deepseek-ai/dsh-system-prompt": "runtime-context"
|
|
10716
|
+
});
|
|
10717
|
+
//#endregion
|
|
10718
|
+
//#region ../session-format-v3-to-v4/src/message-sources.ts
|
|
10719
|
+
/** Native source admission preserves unknown attribution and refuses retired plugin wrappers. */
|
|
10720
|
+
function source(message) {
|
|
10721
|
+
const value = message["source"];
|
|
10722
|
+
if (!isSessionFormatJsonObject(value) || typeof value["kind"] !== "string" || value["kind"].length === 0 || value["kind"] === "plugin") throw new SessionFormatError("format v4 message requires a producer-owned source kind");
|
|
10723
|
+
}
|
|
10724
|
+
/**
|
|
10725
|
+
* Validate producer attribution in every declared durable message slot.
|
|
10726
|
+
* @param event - complete native V4 event before Session adoption.
|
|
10727
|
+
*/
|
|
10728
|
+
function assertV4MessageSources(event) {
|
|
10729
|
+
mapEventMessages(event, (message) => {
|
|
10730
|
+
source(message);
|
|
10731
|
+
return message;
|
|
10732
|
+
});
|
|
10733
|
+
}
|
|
10734
|
+
/**
|
|
10735
|
+
* Refuse retired source syntax even after a malformed recoverable row; leave incomplete messages to decoding.
|
|
10736
|
+
* @param row - parsed row whose incomplete values have not been classified by the scanner.
|
|
10737
|
+
*/
|
|
10738
|
+
function assertV4SourceRowAdmission(row) {
|
|
10739
|
+
if (!isSessionFormatJsonObject(row) || !isSessionFormatJsonObject(row["data"])) return;
|
|
10740
|
+
const data = row["data"];
|
|
10741
|
+
const messages = row["type"] === "user/message" ? [data] : row["type"] === "system/message" || row["type"] === "assistant/message" || row["type"] === "tool/result" ? [data["message"]] : row["type"] === "agent/inbox/spliced" ? data["inserted"] : row["type"] === "session/title-llm-request" ? data["messages"] : [];
|
|
10742
|
+
if (!Array.isArray(messages)) return;
|
|
10743
|
+
for (const message of messages) {
|
|
10744
|
+
if (!isSessionFormatJsonObject(message)) continue;
|
|
10745
|
+
const value = message["source"];
|
|
10746
|
+
if (isSessionFormatJsonObject(value) && value["kind"] === "plugin") source(message);
|
|
10747
|
+
}
|
|
10748
|
+
}
|
|
10749
|
+
//#endregion
|
|
10750
|
+
//#region ../session-format-v3-to-v4/src/retired-syntax.ts
|
|
10751
|
+
/** Retired native syntax remains a hard refusal even after a recoverable physical-row failure. */
|
|
10752
|
+
function assertBlock(block, subject) {
|
|
10753
|
+
if (isSessionFormatJsonObject(block) && block["type"] === "tool-result") throw new SessionFormatError(`${subject} must not contain a released tool-result wrapper`);
|
|
10754
|
+
}
|
|
10755
|
+
function assertContent$1(content, subject) {
|
|
10756
|
+
if (Array.isArray(content)) content.forEach((block) => {
|
|
10757
|
+
assertBlock(block, subject);
|
|
10758
|
+
});
|
|
10759
|
+
}
|
|
10760
|
+
function assertMessageContent(message, subject) {
|
|
10761
|
+
if (isSessionFormatJsonObject(message)) assertContent$1(message["content"], subject);
|
|
10762
|
+
}
|
|
10763
|
+
/**
|
|
10764
|
+
* Refuse retired headers, PTC tags, and tool-result blocks in interpreted content slots.
|
|
10765
|
+
* Tool arguments, replay state, schema parameters, and nested extension values remain opaque.
|
|
10766
|
+
* @param row - parsed physical row or complete native logical event.
|
|
10767
|
+
*/
|
|
10768
|
+
function assertV4RetiredSyntax(row) {
|
|
10769
|
+
if (!isSessionFormatJsonObject(row)) return;
|
|
10770
|
+
if ((row["type"] === "tool/code-dispatch-start" || row["type"] === "tool/code-dispatch") && row["ignorable"] !== true) throw new SessionFormatUnsupportedMigrationError(`format v4 rejects retired event type ${row["type"]}`);
|
|
10771
|
+
const data = row["data"];
|
|
10772
|
+
if (row["type"] === "request/header") {
|
|
10773
|
+
if (!isSessionFormatJsonObject(data) || !isSessionFormatJsonObject(data["header"])) throw new SessionFormatError("format v4 request/header requires data and header objects");
|
|
10774
|
+
if (Object.hasOwn(data["header"], "system")) throw new SessionFormatError("format v4 request/header rejects retired header.system");
|
|
10775
|
+
return;
|
|
10776
|
+
}
|
|
10777
|
+
if (!isSessionFormatJsonObject(data)) return;
|
|
10778
|
+
const subject = `format v4 ${String(row["type"])} at seq ${String(row["seq"])} content`;
|
|
10779
|
+
switch (row["type"]) {
|
|
10780
|
+
case "user/message":
|
|
10781
|
+
assertContent$1(data["content"], subject);
|
|
10782
|
+
break;
|
|
10783
|
+
case "developer/message":
|
|
10784
|
+
case "assistant/message":
|
|
10785
|
+
case "team/message/queued":
|
|
10786
|
+
assertMessageContent(data["message"], subject);
|
|
10787
|
+
break;
|
|
10788
|
+
case "agent/inbox/spliced":
|
|
10789
|
+
case "session/title-llm-request": {
|
|
10790
|
+
const messages = data[row["type"] === "agent/inbox/spliced" ? "inserted" : "messages"];
|
|
10791
|
+
if (Array.isArray(messages)) messages.forEach((message) => {
|
|
10792
|
+
assertMessageContent(message, subject);
|
|
10793
|
+
});
|
|
10794
|
+
break;
|
|
10795
|
+
}
|
|
10796
|
+
case "compaction/summary":
|
|
10797
|
+
assertContent$1(data["summary"], subject);
|
|
10798
|
+
assertContent$1(data["rawOutput"], subject);
|
|
10799
|
+
break;
|
|
10800
|
+
case "tool/ptc-dispatch":
|
|
10801
|
+
assertContent$1(data["content"], subject);
|
|
10802
|
+
break;
|
|
10803
|
+
}
|
|
10804
|
+
if ((row["type"] === "assistant/message" || row["type"] === "assistant/attempt") && Array.isArray(data["stream"])) for (const entry of data["stream"]) {
|
|
10805
|
+
if (!isSessionFormatJsonObject(entry) || entry["type"] !== "chunk") continue;
|
|
10806
|
+
const chunk = entry["chunk"];
|
|
10807
|
+
if (!isSessionFormatJsonObject(chunk)) continue;
|
|
10808
|
+
if (chunk["type"] === "block-end") assertBlock(chunk["block"], subject);
|
|
10809
|
+
if (chunk["type"] === "block-start" && chunk["blockType"] === "tool-result") throw new SessionFormatError(`${subject} must not contain a released tool-result wrapper`);
|
|
10810
|
+
}
|
|
10811
|
+
}
|
|
10812
|
+
//#endregion
|
|
10813
|
+
//#region ../session-format-v3-to-v4/src/system-message.ts
|
|
10814
|
+
/** Native system-message fields checked before physical recovery can discard a row. */
|
|
10815
|
+
function record$1(value, subject) {
|
|
10816
|
+
if (!isSessionFormatJsonObject(value)) throw new SessionFormatError(`format v4 ${subject} requires an object`);
|
|
10817
|
+
return value;
|
|
10818
|
+
}
|
|
10819
|
+
function string(value, subject, nonempty = false) {
|
|
10820
|
+
if (typeof value !== "string" || nonempty && value.length === 0) throw new SessionFormatError(`format v4 ${subject} requires a string${nonempty ? " with content" : ""}`);
|
|
10821
|
+
}
|
|
10822
|
+
function positive(value, subject) {
|
|
10823
|
+
if (sessionFormatCount(value, subject) === 0) throw new SessionFormatError(`format v4 ${subject} must be positive`);
|
|
10824
|
+
}
|
|
10825
|
+
function image(value) {
|
|
10826
|
+
const attachment = record$1(value, "system image attachment");
|
|
10827
|
+
string(attachment["attachmentId"], "system image attachmentId", true);
|
|
10828
|
+
if (![
|
|
10829
|
+
"image/png",
|
|
10830
|
+
"image/jpeg",
|
|
10831
|
+
"image/webp",
|
|
10832
|
+
"image/gif"
|
|
10833
|
+
].includes(attachment["mediaType"])) throw new SessionFormatError("format v4 system image requires a supported mediaType");
|
|
10834
|
+
sessionFormatCount(attachment["bytes"], "system image bytes");
|
|
10835
|
+
positive(attachment["width"], "system image width");
|
|
10836
|
+
positive(attachment["height"], "system image height");
|
|
10837
|
+
if (attachment["name"] !== void 0) string(attachment["name"], "system image name");
|
|
10838
|
+
if (attachment["originalDimensions"] !== void 0) {
|
|
10839
|
+
const original = record$1(attachment["originalDimensions"], "system image originalDimensions");
|
|
10840
|
+
positive(original["width"], "system image original width");
|
|
10841
|
+
positive(original["height"], "system image original height");
|
|
10842
|
+
}
|
|
10843
|
+
}
|
|
10844
|
+
/**
|
|
10845
|
+
* Validate system fields independently of historical message representations; preserve additional JSON fields.
|
|
10846
|
+
* @param row - complete logical event or parsed physical row before corruption recovery.
|
|
10847
|
+
*/
|
|
10848
|
+
function assertV4SystemMessageFields(row) {
|
|
10849
|
+
if (!isSessionFormatJsonObject(row) || row["type"] !== "system/message") return;
|
|
10850
|
+
const data = record$1(row["data"], "system/message data");
|
|
10851
|
+
positive(data["turn"], "system/message turn");
|
|
10852
|
+
positive(data["step"], "system/message step");
|
|
10853
|
+
const message = record$1(data["message"], "system message");
|
|
10854
|
+
string(message["id"], "system message id", true);
|
|
10855
|
+
if (message["role"] !== "system") throw new SessionFormatError("format v4 system message requires system role");
|
|
10856
|
+
const content = message["content"];
|
|
10857
|
+
if (!Array.isArray(content)) throw new SessionFormatError("format v4 system message requires content array");
|
|
10858
|
+
for (const value of content) {
|
|
10859
|
+
const block = record$1(value, "system content block");
|
|
10860
|
+
string(block["type"], "system content type", true);
|
|
10861
|
+
switch (block["type"]) {
|
|
10862
|
+
case "text":
|
|
10863
|
+
case "reasoning":
|
|
10864
|
+
string(block["text"], "system content text");
|
|
10865
|
+
break;
|
|
10866
|
+
case "tool-call":
|
|
10867
|
+
string(block["id"], "system tool-call id", true);
|
|
10868
|
+
string(block["name"], "system tool-call name", true);
|
|
10869
|
+
string(block["arguments"], "system tool-call arguments");
|
|
10870
|
+
break;
|
|
10871
|
+
case "image":
|
|
10872
|
+
image(block["attachment"]);
|
|
10873
|
+
break;
|
|
10874
|
+
case "tool-result": throw new SessionFormatError("format v4 system content rejects retired tool-result wrappers");
|
|
10875
|
+
default: break;
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10879
|
+
//#endregion
|
|
10880
|
+
//#region ../session-format-v3-to-v4/src/developer.ts
|
|
10881
|
+
/** Native V4 developer-message and deferred-tool-schema validation. */
|
|
10882
|
+
function assertToolChange(block, developer) {
|
|
10883
|
+
if (!isSessionFormatJsonObject(block) || block["type"] !== "tool-addition" && block["type"] !== "tool-removal") return;
|
|
10884
|
+
if (!developer) throw new SessionFormatError(`format v4 ${block["type"]} requires developer role`);
|
|
10885
|
+
if (typeof block["toolName"] !== "string" || block["toolName"].length === 0) throw new SessionFormatError(`format v4 ${block["type"]} requires a nonempty toolName`);
|
|
10886
|
+
if (block["type"] === "tool-addition" && Object.hasOwn(block, "tool")) throw new SessionFormatError("format v4 tool-addition must omit inline tool definitions");
|
|
10887
|
+
}
|
|
10888
|
+
function assertContent(value, developer = false) {
|
|
10889
|
+
if (Array.isArray(value)) value.forEach((block) => {
|
|
10890
|
+
assertToolChange(block, developer);
|
|
10891
|
+
});
|
|
10892
|
+
}
|
|
10893
|
+
function assertDeveloperMessage(message) {
|
|
10894
|
+
assertContent(message["content"], true);
|
|
10895
|
+
const source = message["source"];
|
|
10896
|
+
if (typeof message["id"] !== "string" || message["id"].length === 0 || !Array.isArray(message["content"]) || !isSessionFormatJsonObject(source) || typeof source["kind"] !== "string" || source["kind"].length === 0 || source["kind"] === "plugin") throw new SessionFormatError("format v4 developer message requires id, role, content, and a producer-owned source");
|
|
10897
|
+
}
|
|
10898
|
+
/** Ordinary-message corruption belongs to the decoder; recognized V4-only values are refused here. */
|
|
10899
|
+
function assertOrdinaryMessage(value) {
|
|
10900
|
+
if (!isSessionFormatJsonObject(value)) return;
|
|
10901
|
+
if (value["role"] === "developer") throw new SessionFormatError("format v4 developer/message and developer role must occur together");
|
|
10902
|
+
assertContent(value["content"]);
|
|
10903
|
+
}
|
|
10553
10904
|
/**
|
|
10554
|
-
*
|
|
10555
|
-
*
|
|
10905
|
+
* Validate native V4 developer messages, tool-change blocks, and deferred tool schemas.
|
|
10906
|
+
* Additional developer JSON fields are preserved. Malformed legacy message
|
|
10907
|
+
* slots remain available to recoverable decoding.
|
|
10908
|
+
* @param event - logical event or physical row before source-event range decoding.
|
|
10556
10909
|
*/
|
|
10910
|
+
function assertV4DeveloperData(event) {
|
|
10911
|
+
const data = event.data;
|
|
10912
|
+
if (!isSessionFormatJsonObject(data)) {
|
|
10913
|
+
if (event.type === "developer/message") throw new SessionFormatError("format v4 developer/message data must be an object");
|
|
10914
|
+
return;
|
|
10915
|
+
}
|
|
10916
|
+
if (event.type === "developer/message") {
|
|
10917
|
+
const message = data["message"];
|
|
10918
|
+
if (!isSessionFormatJsonObject(message) || message["role"] !== "developer") throw new SessionFormatError("format v4 developer/message requires turn, step, and a developer message");
|
|
10919
|
+
for (const field of ["turn", "step"]) if (sessionFormatCount(data[field], `developer/message ${field}`) === 0) throw new SessionFormatError(`developer/message ${field} must be positive`);
|
|
10920
|
+
assertDeveloperMessage(message);
|
|
10921
|
+
if (message["content"].some((block) => isSessionFormatJsonObject(block) && block["type"] === "tool-addition")) sessionFormatCount(data["headerSeq"], "developer/message headerSeq");
|
|
10922
|
+
else if (Object.hasOwn(data, "headerSeq")) throw new SessionFormatError("format v4 developer/message must omit headerSeq without tool additions");
|
|
10923
|
+
} else switch (event.type) {
|
|
10924
|
+
case "user/message":
|
|
10925
|
+
assertOrdinaryMessage(data);
|
|
10926
|
+
break;
|
|
10927
|
+
case "system/message":
|
|
10928
|
+
case "assistant/message":
|
|
10929
|
+
case "tool/result":
|
|
10930
|
+
assertOrdinaryMessage(data["message"]);
|
|
10931
|
+
break;
|
|
10932
|
+
case "agent/inbox/spliced":
|
|
10933
|
+
case "session/title-llm-request": {
|
|
10934
|
+
const messages = data[event.type === "agent/inbox/spliced" ? "inserted" : "messages"];
|
|
10935
|
+
if (Array.isArray(messages)) messages.forEach(assertOrdinaryMessage);
|
|
10936
|
+
break;
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
if (event.type === "compaction/summary") {
|
|
10940
|
+
assertContent(data["summary"]);
|
|
10941
|
+
assertContent(data["rawOutput"]);
|
|
10942
|
+
}
|
|
10943
|
+
if ((event.type === "assistant/message" || event.type === "assistant/attempt") && Array.isArray(data["stream"])) for (const entry of data["stream"]) {
|
|
10944
|
+
if (!isSessionFormatJsonObject(entry) || entry["type"] !== "chunk") continue;
|
|
10945
|
+
const chunk = entry["chunk"];
|
|
10946
|
+
if (!isSessionFormatJsonObject(chunk)) continue;
|
|
10947
|
+
if (chunk["type"] === "block-end") assertToolChange(chunk["block"], false);
|
|
10948
|
+
if (chunk["type"] === "block-start" && (chunk["blockType"] === "tool-addition" || chunk["blockType"] === "tool-removal")) throw new SessionFormatError("format v4 tool-change blocks require developer role");
|
|
10949
|
+
}
|
|
10950
|
+
if (event.type === "request/header" && isSessionFormatJsonObject(data["header"])) {
|
|
10951
|
+
const tools = data["header"]["tools"];
|
|
10952
|
+
if (Array.isArray(tools)) for (const tool of tools) {
|
|
10953
|
+
if (!isSessionFormatJsonObject(tool) || !Object.hasOwn(tool, "deferLoading")) continue;
|
|
10954
|
+
if (tool["deferLoading"] !== true) throw new SessionFormatError("format v4 tool deferLoading must be true when present");
|
|
10955
|
+
}
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
//#endregion
|
|
10959
|
+
//#region ../session-format-v3-to-v4/src/fork-result.ts
|
|
10960
|
+
/** Native V4 fork-result identity and not-started error validation. */
|
|
10961
|
+
/**
|
|
10962
|
+
* Validate fork-generated not-started results without changing their identity or text.
|
|
10963
|
+
* Append results carry their own sequence; replacement results retain an earlier identity.
|
|
10964
|
+
* @param row - logical event or physical event row.
|
|
10965
|
+
*/
|
|
10966
|
+
function assertV4ForkResult(row) {
|
|
10967
|
+
if (!isSessionFormatJsonObject(row) || row["type"] !== "tool/result") return;
|
|
10968
|
+
const data = row["data"];
|
|
10969
|
+
if (!isSessionFormatJsonObject(data)) return;
|
|
10970
|
+
const error = data["error"];
|
|
10971
|
+
const message = data["message"];
|
|
10972
|
+
if (!isSessionFormatJsonObject(error) || error["code"] !== "TOOL_NOT_STARTED" || !isSessionFormatJsonObject(message) || typeof message["id"] !== "string" || !message["id"].startsWith("forked-tool-result-")) return;
|
|
10973
|
+
const source = message["source"];
|
|
10974
|
+
const callId = isSessionFormatJsonObject(source) ? source["callId"] : void 0;
|
|
10975
|
+
const prefix = `forked-tool-result-${String(callId)}-`;
|
|
10976
|
+
const suffix = message["id"].slice(prefix.length);
|
|
10977
|
+
const operation = row["surfaceOp"];
|
|
10978
|
+
const replacement = isSessionFormatJsonObject(operation) && operation["op"] === "replace";
|
|
10979
|
+
const sequence = Number(suffix);
|
|
10980
|
+
const content = message["content"];
|
|
10981
|
+
const text = Array.isArray(content) && content.length === 1 ? content[0] : void 0;
|
|
10982
|
+
const sourceEventSeqs = row["sourceEventSeqs"];
|
|
10983
|
+
if (typeof callId !== "string" || !message["id"].startsWith(prefix) || !/^(0|[1-9]\d*)$/.test(suffix) || !Number.isSafeInteger(sequence) || (replacement ? typeof row["seq"] !== "number" || sequence >= row["seq"] : sequence !== row["seq"]) || error["name"] !== "ToolNotStartedError" || !replacement && (sourceEventSeqs !== void 0 || operation !== "append") || replacement && (!Array.isArray(sourceEventSeqs) || sourceEventSeqs.length !== 1 || sourceEventSeqs[0] !== sequence) || message["role"] !== "tool" || message["isError"] !== true || message["toolCallId"] !== callId || !isSessionFormatJsonObject(source) || source["kind"] !== "tool" || !isSessionFormatJsonObject(text) || text["type"] !== "text" || typeof text["text"] !== "string") throw new SessionFormatError("invalid V4 not-started fork result");
|
|
10984
|
+
}
|
|
10985
|
+
//#endregion
|
|
10986
|
+
//#region ../session-format-v3-to-v4/src/tool-role.ts
|
|
10987
|
+
/** First-class tool-role messages in the V4 representation. */
|
|
10988
|
+
/**
|
|
10989
|
+
* Validate the native V4 first-class tool-role message of one tool/result row.
|
|
10990
|
+
* @param event - canonical V4 event.
|
|
10991
|
+
* @throws {SessionFormatError} when the row is not an exact first-class tool result.
|
|
10992
|
+
*/
|
|
10993
|
+
function assertV4ToolResultMessage(event) {
|
|
10994
|
+
if (event.type !== "tool/result") return;
|
|
10995
|
+
const subject = `format v4 ${event.type} at seq ${event.seq}`;
|
|
10996
|
+
const data = event.data;
|
|
10997
|
+
if (!isSessionFormatJsonObject(data)) throw new SessionFormatError(`${subject} data must be an object`);
|
|
10998
|
+
const message = data["message"];
|
|
10999
|
+
if (!isSessionFormatJsonObject(message)) throw new SessionFormatError(`${subject} message must be an object`);
|
|
11000
|
+
const id = message["id"];
|
|
11001
|
+
const role = message["role"];
|
|
11002
|
+
const toolCallId = message["toolCallId"];
|
|
11003
|
+
const source = message["source"];
|
|
11004
|
+
const isError = message["isError"];
|
|
11005
|
+
const content = message["content"];
|
|
11006
|
+
const sourceCallId = isSessionFormatJsonObject(source) ? source["callId"] : void 0;
|
|
11007
|
+
if (typeof id !== "string" || id.length === 0) throw new SessionFormatError(`${subject} requires a first-class message with a string id`);
|
|
11008
|
+
if (role !== "tool") throw new SessionFormatError(`${subject} requires a tool-role message`);
|
|
11009
|
+
if (typeof toolCallId !== "string" || toolCallId.length === 0 || sourceCallId !== toolCallId) throw new SessionFormatError(`${subject} requires toolCallId matching its tool source`);
|
|
11010
|
+
if (!isSessionFormatJsonObject(source) || source["kind"] !== "tool") throw new SessionFormatError(`${subject} requires a tool source`);
|
|
11011
|
+
if (!Array.isArray(content)) throw new SessionFormatError(`${subject} requires array content`);
|
|
11012
|
+
if (content.some((block) => isSessionFormatJsonObject(block) && block["type"] === "tool-result")) throw new SessionFormatError(`${subject} content must not contain a released tool-result wrapper`);
|
|
11013
|
+
if (isError !== void 0 && typeof isError !== "boolean") throw new SessionFormatError(`${subject} isError must be boolean when present`);
|
|
11014
|
+
if (data["error"] !== void 0 && isError !== true) throw new SessionFormatError(`${subject} carries error metadata for a non-error tool result`);
|
|
11015
|
+
}
|
|
11016
|
+
//#endregion
|
|
11017
|
+
//#region ../session-format-v3-to-v4/src/relationships.ts
|
|
11018
|
+
/** Mandatory native V4 relationships; incomplete tails retain their open transactions. */
|
|
11019
|
+
const SURFACE_TYPES = new Set([
|
|
11020
|
+
"system/message",
|
|
11021
|
+
"user/message",
|
|
11022
|
+
"developer/message",
|
|
11023
|
+
"assistant/message",
|
|
11024
|
+
"tool/result"
|
|
11025
|
+
]);
|
|
11026
|
+
const STEP_EVENT_TYPES = new Set([
|
|
11027
|
+
"system/message",
|
|
11028
|
+
"developer/message",
|
|
11029
|
+
"assistant/attempt"
|
|
11030
|
+
]);
|
|
11031
|
+
const RELATIONSHIP_TYPES = new Set([
|
|
11032
|
+
...SURFACE_TYPES,
|
|
11033
|
+
...STEP_EVENT_TYPES,
|
|
11034
|
+
"turn/start",
|
|
11035
|
+
"turn/end",
|
|
11036
|
+
"step/start",
|
|
11037
|
+
"step/end",
|
|
11038
|
+
"tool/call",
|
|
11039
|
+
"request/header",
|
|
11040
|
+
"request/context",
|
|
11041
|
+
"tool/ptc-dispatch-start",
|
|
11042
|
+
"tool/ptc-dispatch",
|
|
11043
|
+
"llm/retry",
|
|
11044
|
+
"llm/retry-started",
|
|
11045
|
+
"session/title",
|
|
11046
|
+
"session/title-llm-request",
|
|
11047
|
+
"command/run",
|
|
11048
|
+
"command/done",
|
|
11049
|
+
"compaction/start",
|
|
11050
|
+
"compaction/summary",
|
|
11051
|
+
"compaction/end",
|
|
11052
|
+
"compaction/prune",
|
|
11053
|
+
"session/end-seed"
|
|
11054
|
+
]);
|
|
11055
|
+
function record(value, subject) {
|
|
11056
|
+
if (!isSessionFormatJsonObject(value)) throw new SessionFormatError(`${subject} requires an object`);
|
|
11057
|
+
return value;
|
|
11058
|
+
}
|
|
11059
|
+
function text(value, subject) {
|
|
11060
|
+
if (typeof value !== "string" || value.length === 0) throw new SessionFormatError(`${subject} requires a nonempty string`);
|
|
11061
|
+
return value;
|
|
11062
|
+
}
|
|
11063
|
+
function array(value, subject) {
|
|
11064
|
+
if (!Array.isArray(value)) throw new SessionFormatError(`${subject} requires an array`);
|
|
11065
|
+
return value;
|
|
11066
|
+
}
|
|
11067
|
+
function earlier(value, seq, subject) {
|
|
11068
|
+
const coordinate = sessionFormatCount(value, subject);
|
|
11069
|
+
if (coordinate >= seq) throw new SessionFormatError(`${subject} must name an earlier event`);
|
|
11070
|
+
return coordinate;
|
|
11071
|
+
}
|
|
11072
|
+
/** One native artifact's lifecycle state; no state survives between reads. */
|
|
11073
|
+
var Relationships = class {
|
|
11074
|
+
artifact;
|
|
11075
|
+
knownEventTypes;
|
|
11076
|
+
turn = null;
|
|
11077
|
+
step = null;
|
|
11078
|
+
nextTurn = 1;
|
|
11079
|
+
nextStep = 1;
|
|
11080
|
+
provider;
|
|
11081
|
+
surface = [];
|
|
11082
|
+
protectedHead;
|
|
11083
|
+
compaction;
|
|
11084
|
+
tools = /* @__PURE__ */ new Map();
|
|
11085
|
+
dispatches = /* @__PURE__ */ new Map();
|
|
11086
|
+
retries = [];
|
|
11087
|
+
startedRetries = /* @__PURE__ */ new Set();
|
|
11088
|
+
commands = /* @__PURE__ */ new Set();
|
|
11089
|
+
orphanCompactions = /* @__PURE__ */ new Set();
|
|
11090
|
+
constructor(artifact, knownEventTypes) {
|
|
11091
|
+
this.artifact = artifact;
|
|
11092
|
+
this.knownEventTypes = knownEventTypes;
|
|
11093
|
+
let start;
|
|
11094
|
+
for (const event of artifact.events) {
|
|
11095
|
+
if (!knownEventTypes.has(event.type)) continue;
|
|
11096
|
+
if (event.type === "compaction/start") start = event.seq;
|
|
11097
|
+
if (event.type === "compaction/end") start = void 0;
|
|
11098
|
+
if (event.type === "session/end-seed") {
|
|
11099
|
+
if (start !== void 0) this.orphanCompactions.add(start);
|
|
11100
|
+
start = void 0;
|
|
11101
|
+
}
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
requireTurn(type) {
|
|
11105
|
+
if (this.turn === null) throw new SessionFormatError(`${type} is outside an open turn`);
|
|
11106
|
+
}
|
|
11107
|
+
requireStep(event, data) {
|
|
11108
|
+
if (this.turn === null || this.step === null || data["turn"] !== this.turn || data["step"] !== this.step) throw new SessionFormatError(`${event.type} does not match an open turn and step`);
|
|
11109
|
+
}
|
|
11110
|
+
closeTools(type) {
|
|
11111
|
+
if (this.tools.size !== 0) throw new SessionFormatError(`${type} leaves unresolved tool call ${String(this.tools.keys().next().value)}`);
|
|
11112
|
+
this.tools.clear();
|
|
11113
|
+
}
|
|
11114
|
+
developer(event, data) {
|
|
11115
|
+
if (data["headerSeq"] === void 0) return;
|
|
11116
|
+
const headerSeq = earlier(data["headerSeq"], event.seq, "developer/message headerSeq");
|
|
11117
|
+
const headerEvent = this.artifact.events[headerSeq];
|
|
11118
|
+
if (headerEvent?.type !== "request/header") throw new SessionFormatError("developer/message headerSeq must reference an earlier request/header");
|
|
11119
|
+
if (!this.knownEventTypes.has(headerEvent.type)) throw new SessionFormatError("developer/message headerSeq must reference a known request/header");
|
|
11120
|
+
const tools = record(record(headerEvent.data, "request/header data")["header"], "request header")["tools"];
|
|
11121
|
+
const content = array(record(data["message"], "developer message")["content"], "developer content");
|
|
11122
|
+
for (const value of content) {
|
|
11123
|
+
if (!isSessionFormatJsonObject(value) || value["type"] !== "tool-addition") continue;
|
|
11124
|
+
const toolName = value["toolName"];
|
|
11125
|
+
const definitions = Array.isArray(tools) ? tools.filter((tool) => isSessionFormatJsonObject(tool) && tool["name"] === toolName) : [];
|
|
11126
|
+
if (definitions.length !== 1) throw new SessionFormatError(`developer/message tool-addition "${toolName}" must name exactly one tool in headerSeq ${headerSeq}`);
|
|
11127
|
+
const definition = definitions[0];
|
|
11128
|
+
if (typeof definition["description"] !== "string" || !isSessionFormatJsonObject(definition["parameters"])) throw new SessionFormatError(`developer/message tool-addition "${toolName}" requires a complete tool definition in headerSeq ${headerSeq}`);
|
|
11129
|
+
}
|
|
11130
|
+
}
|
|
11131
|
+
foldSurface(event) {
|
|
11132
|
+
if (!SURFACE_TYPES.has(event.type)) return;
|
|
11133
|
+
if (event.type === "system/message" && this.surface.length > 0 && this.protectedHead === void 0) throw new SessionFormatError("system/message requires a protected first surface head");
|
|
11134
|
+
if (event["surfaceOp"] === "append") {
|
|
11135
|
+
if (event.type === "system/message" && this.surface.length === 0) this.protectedHead = event.seq;
|
|
11136
|
+
this.surface.push(event.seq);
|
|
11137
|
+
return;
|
|
11138
|
+
}
|
|
11139
|
+
const operation = record(event["surfaceOp"], `${event.type} surfaceOp`);
|
|
11140
|
+
const first = this.surface.indexOf(earlier(operation["startSeq"], event.seq, "replacement start"));
|
|
11141
|
+
const last = this.surface.indexOf(earlier(operation["endSeq"], event.seq, "replacement end"));
|
|
11142
|
+
if (first < 0 || last < first) throw new SessionFormatError(`${event.type} replacement range is not on the current surface`);
|
|
11143
|
+
const removed = this.surface.slice(first, last + 1);
|
|
11144
|
+
const sources = array(event["sourceEventSeqs"], "replacement sourceEventSeqs");
|
|
11145
|
+
if (removed.some((seq) => !sources.includes(seq))) throw new SessionFormatError("replacement sourceEventSeqs omit a shadowed surface node");
|
|
11146
|
+
if (this.protectedHead !== void 0 && removed.includes(this.protectedHead)) {
|
|
11147
|
+
if (event.type !== "system/message" || removed.length !== 1) throw new SessionFormatError("surface replacement cannot shadow the protected system head");
|
|
11148
|
+
this.protectedHead = event.seq;
|
|
11149
|
+
}
|
|
11150
|
+
this.surface.splice(first, removed.length, event.seq);
|
|
11151
|
+
}
|
|
11152
|
+
tool(event, data) {
|
|
11153
|
+
if (event.type === "tool/result" && event["surfaceOp"] !== "append") {
|
|
11154
|
+
this.requireTurn(event.type);
|
|
11155
|
+
return;
|
|
11156
|
+
}
|
|
11157
|
+
this.requireStep(event, data);
|
|
11158
|
+
if (event.type === "assistant/message") {
|
|
11159
|
+
const message = record(data["message"], "assistant message");
|
|
11160
|
+
for (const value of array(message["content"], "assistant content")) {
|
|
11161
|
+
const block = record(value, "assistant content block");
|
|
11162
|
+
if (block["type"] !== "tool-call") continue;
|
|
11163
|
+
const id = text(block["id"], "tool call id");
|
|
11164
|
+
if (this.tools.has(id)) throw new SessionFormatError(`assistant/message repeats advertised tool call ${id}`);
|
|
11165
|
+
this.tools.set(id, {
|
|
11166
|
+
name: block["name"],
|
|
11167
|
+
arguments: block["arguments"],
|
|
11168
|
+
started: false
|
|
11169
|
+
});
|
|
11170
|
+
}
|
|
11171
|
+
return;
|
|
11172
|
+
}
|
|
11173
|
+
const message = event.type === "tool/result" ? record(data["message"], "tool result message") : void 0;
|
|
11174
|
+
const id = text(message === void 0 ? data["callId"] : message["toolCallId"], "tool call id");
|
|
11175
|
+
const pending = this.tools.get(id);
|
|
11176
|
+
if (pending === void 0) throw new SessionFormatError(`${event.type} ${id} has no advertised tool lifecycle`);
|
|
11177
|
+
if (message === void 0) {
|
|
11178
|
+
if (pending.started || pending.name !== data["name"] || pending.arguments !== data["arguments"]) throw new SessionFormatError(`tool/call ${id} does not match one advertised tool call`);
|
|
11179
|
+
pending.started = true;
|
|
11180
|
+
} else {
|
|
11181
|
+
if (!pending.started && !notStartedRepair(event, data, message, id)) throw new SessionFormatError(`tool/result ${id} is not the exact TOOL_NOT_STARTED repair`);
|
|
11182
|
+
this.tools.delete(id);
|
|
11183
|
+
}
|
|
11184
|
+
}
|
|
11185
|
+
dispatch(event, data) {
|
|
11186
|
+
this.requireTurn(event.type);
|
|
11187
|
+
const id = text(data["subCallId"], "PTC subCallId");
|
|
11188
|
+
const root = text(data["rootCallId"], "PTC rootCallId");
|
|
11189
|
+
const parent = text(data["parentCallId"], "PTC parentCallId");
|
|
11190
|
+
const existing = this.dispatches.get(id);
|
|
11191
|
+
if (existing !== void 0 && existing.data["rootCallId"] !== root) throw new SessionFormatError("PTC dispatch changes its rootCallId");
|
|
11192
|
+
if (parent !== root && this.dispatches.get(parent)?.data["rootCallId"] !== root) throw new SessionFormatError("PTC parentCallId does not belong to rootCallId");
|
|
11193
|
+
if (event.type === "tool/ptc-dispatch-start") {
|
|
11194
|
+
if (existing !== void 0) throw new SessionFormatError("PTC dispatch repeats subCallId");
|
|
11195
|
+
this.dispatches.set(id, {
|
|
11196
|
+
data,
|
|
11197
|
+
settled: false
|
|
11198
|
+
});
|
|
11199
|
+
return;
|
|
11200
|
+
}
|
|
11201
|
+
if (existing === void 0 || existing.settled) throw new SessionFormatError("PTC dispatch has no unique start");
|
|
11202
|
+
if ([
|
|
11203
|
+
"rootCallId",
|
|
11204
|
+
"parentCallId",
|
|
11205
|
+
"name",
|
|
11206
|
+
"arguments"
|
|
11207
|
+
].some((key) => !(0, node_util.isDeepStrictEqual)(existing.data[key], data[key]))) throw new SessionFormatError("PTC dispatch does not match its start");
|
|
11208
|
+
existing.settled = true;
|
|
11209
|
+
}
|
|
11210
|
+
retry(event, data) {
|
|
11211
|
+
const id = text(data["retryId"], "retryId");
|
|
11212
|
+
const attempt = sessionFormatCount(data["retry"], "retry");
|
|
11213
|
+
if (event.type === "llm/retry-started") {
|
|
11214
|
+
const scheduled = this.retries.find((item) => item["retryId"] === id && item["retry"] === attempt);
|
|
11215
|
+
if (scheduled === void 0) throw new SessionFormatError("llm/retry-started pairs no prior scheduled attempt");
|
|
11216
|
+
if (scheduled["turn"] !== data["turn"] || scheduled["step"] !== data["step"]) throw new SessionFormatError("llm/retry-started changes scheduled coordinates");
|
|
11217
|
+
const key = JSON.stringify([id, attempt]);
|
|
11218
|
+
if (this.startedRetries.has(key)) throw new SessionFormatError("llm/retry-started repeats one scheduled attempt");
|
|
11219
|
+
this.startedRetries.add(key);
|
|
11220
|
+
return;
|
|
11221
|
+
}
|
|
11222
|
+
if (this.turn === null || data["turn"] !== this.turn || data["step"] !== (this.step ?? this.nextStep - 1)) throw new SessionFormatError("llm/retry does not match the current turn and step");
|
|
11223
|
+
if (data["provider"] !== this.provider) throw new SessionFormatError("llm/retry provider does not match the open request/header");
|
|
11224
|
+
const prior = this.retries.findLast((item) => [
|
|
11225
|
+
"turn",
|
|
11226
|
+
"step",
|
|
11227
|
+
"provider",
|
|
11228
|
+
"policyKey"
|
|
11229
|
+
].every((key) => item[key] === data[key]));
|
|
11230
|
+
if (attempt !== (prior === void 0 ? 1 : sessionFormatCount(prior["retry"], "prior retry") + 1)) throw new SessionFormatError("llm/retry skips its policy attempt sequence");
|
|
11231
|
+
if (prior === void 0 ? this.retries.some((item) => item["retryId"] === id) : prior["retryId"] !== id) throw new SessionFormatError("llm/retry must keep one retryId per policy chain");
|
|
11232
|
+
this.retries.push(data);
|
|
11233
|
+
}
|
|
11234
|
+
compact(event, data) {
|
|
11235
|
+
if (event.type === "compaction/prune" || event.type === "compaction/summary") this.span(event, data);
|
|
11236
|
+
if (event.type === "compaction/prune") return;
|
|
11237
|
+
if (event.type === "compaction/start") {
|
|
11238
|
+
if (this.compaction !== void 0) throw new SessionFormatError("compaction/start overlaps an open compaction");
|
|
11239
|
+
const owner = data["turn"] === null ? null : sessionFormatCount(data["turn"], "compaction turn");
|
|
11240
|
+
if (owner !== this.turn) throw new SessionFormatError("compaction/start does not match the open turn");
|
|
11241
|
+
this.compaction = {
|
|
11242
|
+
id: text(data["compactionId"], "compactionId"),
|
|
11243
|
+
command: data["sourceCommandId"],
|
|
11244
|
+
turn: owner,
|
|
11245
|
+
seq: event.seq,
|
|
11246
|
+
summarized: false
|
|
11247
|
+
};
|
|
11248
|
+
return;
|
|
11249
|
+
}
|
|
11250
|
+
const current = this.compactionOwner(data, event.type);
|
|
11251
|
+
if (current.turn !== this.turn) throw new SessionFormatError(`${event.type} does not match the open turn`);
|
|
11252
|
+
if (event.type === "compaction/summary") {
|
|
11253
|
+
if (current.summarized) throw new SessionFormatError("compaction/summary repeats");
|
|
11254
|
+
current.summarized = true;
|
|
11255
|
+
} else {
|
|
11256
|
+
if (data["turn"] !== current.turn) throw new SessionFormatError("compaction/end changes its owner turn");
|
|
11257
|
+
if (data["error"] === void 0 && !current.summarized) throw new SessionFormatError("successful compaction/end requires one summary");
|
|
11258
|
+
this.compaction = void 0;
|
|
11259
|
+
}
|
|
11260
|
+
}
|
|
11261
|
+
compactionOwner(data, subject) {
|
|
11262
|
+
if (this.compaction === void 0 || this.compaction.id !== data["compactionId"] || this.compaction.command !== data["sourceCommandId"]) throw new SessionFormatError(`${subject} has no matching compaction/start`);
|
|
11263
|
+
return this.compaction;
|
|
11264
|
+
}
|
|
11265
|
+
span(event, data) {
|
|
11266
|
+
const range = record(data["shadowedRange"], "compaction shadowedRange");
|
|
11267
|
+
const start = this.surface.indexOf(earlier(range["start"], event.seq, "compaction range start"));
|
|
11268
|
+
const end = this.surface.indexOf(earlier(range["end"], event.seq, "compaction range end"));
|
|
11269
|
+
const seqs = array(data["shadowedSeqs"], "compaction shadowedSeqs");
|
|
11270
|
+
if (start < 0 || end < start || !(0, node_util.isDeepStrictEqual)(this.surface.slice(start, end + 1), seqs)) throw new SessionFormatError(`${event.type} shadowedSeqs do not name an exact current surface span`);
|
|
11271
|
+
if (this.protectedHead !== void 0 && seqs.includes(this.protectedHead)) throw new SessionFormatError("compaction cannot shadow the protected system head");
|
|
11272
|
+
}
|
|
11273
|
+
accept(event) {
|
|
11274
|
+
if (!this.knownEventTypes.has(event.type) || !RELATIONSHIP_TYPES.has(event.type)) return;
|
|
11275
|
+
const data = record(event.data, event.type);
|
|
11276
|
+
if (STEP_EVENT_TYPES.has(event.type)) this.requireStep(event, data);
|
|
11277
|
+
if (event.type.startsWith("turn/") && this.compaction !== void 0 && !this.orphanCompactions.has(this.compaction.seq)) throw new SessionFormatError(`${event.type} crosses an open compaction`);
|
|
11278
|
+
this.foldSurface(event);
|
|
11279
|
+
switch (event.type) {
|
|
11280
|
+
case "turn/start":
|
|
11281
|
+
if (this.turn !== null || data["turn"] !== this.nextTurn) throw new SessionFormatError("turn/start does not open the expected turn");
|
|
11282
|
+
this.turn = this.nextTurn;
|
|
11283
|
+
this.nextStep = 1;
|
|
11284
|
+
this.tools.clear();
|
|
11285
|
+
break;
|
|
11286
|
+
case "turn/end":
|
|
11287
|
+
if (this.turn === null || data["turn"] !== this.turn || this.step !== null) throw new SessionFormatError("turn/end does not match the open turn with no open step");
|
|
11288
|
+
this.closeTools(event.type);
|
|
11289
|
+
this.turn = null;
|
|
11290
|
+
this.nextTurn += 1;
|
|
11291
|
+
break;
|
|
11292
|
+
case "step/start":
|
|
11293
|
+
if (this.turn === null || data["turn"] !== this.turn || this.step !== null || data["step"] !== this.nextStep) throw new SessionFormatError("step/start does not match the open turn and next step");
|
|
11294
|
+
this.step = this.nextStep;
|
|
11295
|
+
break;
|
|
11296
|
+
case "step/end":
|
|
11297
|
+
this.requireStep(event, data);
|
|
11298
|
+
this.closeTools(event.type);
|
|
11299
|
+
this.step = null;
|
|
11300
|
+
this.nextStep += 1;
|
|
11301
|
+
break;
|
|
11302
|
+
case "assistant/message":
|
|
11303
|
+
case "tool/call":
|
|
11304
|
+
case "tool/result":
|
|
11305
|
+
this.tool(event, data);
|
|
11306
|
+
break;
|
|
11307
|
+
case "developer/message":
|
|
11308
|
+
this.developer(event, data);
|
|
11309
|
+
break;
|
|
11310
|
+
case "request/header":
|
|
11311
|
+
this.requireTurn(event.type);
|
|
11312
|
+
this.provider = record(record(data["header"], "request header")["config"], "request config")["provider"];
|
|
11313
|
+
break;
|
|
11314
|
+
case "request/context":
|
|
11315
|
+
this.requireTurn(event.type);
|
|
11316
|
+
break;
|
|
11317
|
+
case "tool/ptc-dispatch-start":
|
|
11318
|
+
case "tool/ptc-dispatch":
|
|
11319
|
+
this.dispatch(event, data);
|
|
11320
|
+
break;
|
|
11321
|
+
case "llm/retry":
|
|
11322
|
+
case "llm/retry-started":
|
|
11323
|
+
this.retry(event, data);
|
|
11324
|
+
break;
|
|
11325
|
+
case "session/title":
|
|
11326
|
+
case "session/title-llm-request":
|
|
11327
|
+
titleSources(this.artifact.events, event, data, this.knownEventTypes);
|
|
11328
|
+
break;
|
|
11329
|
+
case "command/run": {
|
|
11330
|
+
const id = text(data["commandId"], "commandId");
|
|
11331
|
+
if (this.commands.has(id)) throw new SessionFormatError(`command/run repeats commandId ${id}`);
|
|
11332
|
+
this.commands.add(id);
|
|
11333
|
+
break;
|
|
11334
|
+
}
|
|
11335
|
+
case "command/done":
|
|
11336
|
+
if (!this.commands.has(text(data["commandId"], "commandId"))) throw new SessionFormatError("command/done has no prior command/run");
|
|
11337
|
+
if (data["sourceEventSeq"] !== void 0) {
|
|
11338
|
+
const source = this.artifact.events[earlier(data["sourceEventSeq"], event.seq, "command sourceEventSeq")];
|
|
11339
|
+
if (data["kind"] !== "success" || source?.type === "command/run" || source?.type === "command/done") throw new SessionFormatError("command/done has invalid sourceEventSeq");
|
|
11340
|
+
}
|
|
11341
|
+
break;
|
|
11342
|
+
case "compaction/start":
|
|
11343
|
+
case "compaction/summary":
|
|
11344
|
+
case "compaction/end":
|
|
11345
|
+
case "compaction/prune":
|
|
11346
|
+
this.compact(event, data);
|
|
11347
|
+
break;
|
|
11348
|
+
case "user/message": {
|
|
11349
|
+
const source = record(data["source"], "user message source");
|
|
11350
|
+
if (event["surfaceOp"] !== "append" && source["kind"] === "compact-checkpoint") this.compactionOwner(source, "compaction checkpoint");
|
|
11351
|
+
break;
|
|
11352
|
+
}
|
|
11353
|
+
case "session/end-seed":
|
|
11354
|
+
this.compaction = void 0;
|
|
11355
|
+
break;
|
|
11356
|
+
}
|
|
11357
|
+
}
|
|
11358
|
+
};
|
|
11359
|
+
function notStartedRepair(event, data, message, callId) {
|
|
11360
|
+
const error = record(data["error"], "not-started error");
|
|
11361
|
+
if (error["name"] !== "ToolNotStartedError" || error["code"] !== "TOOL_NOT_STARTED" || message["isError"] !== true || event["sourceEventSeqs"] !== void 0) return false;
|
|
11362
|
+
const id = text(message["id"], "not-started message id");
|
|
11363
|
+
if (id.startsWith(`forked-tool-result-${callId}-`)) return true;
|
|
11364
|
+
const prefix = `interrupted-tool-result-${callId}-`;
|
|
11365
|
+
const suffix = id.slice(prefix.length);
|
|
11366
|
+
const content = array(message["content"], "not-started content");
|
|
11367
|
+
const block = content[0];
|
|
11368
|
+
return id.startsWith(prefix) && /^(0|[1-9]\d*)$/.test(suffix) && Number.isSafeInteger(Number(suffix)) && content.length === 1 && isSessionFormatJsonObject(block) && block["type"] === "text" && block["text"] === "The tool call was interrupted before the Harness recorded it as started. Retry it if it is still needed.";
|
|
11369
|
+
}
|
|
11370
|
+
function titleSources(events, event, data, knownEventTypes) {
|
|
11371
|
+
const references = array(data["messageSeqs"], "title messageSeqs");
|
|
11372
|
+
if (event.type === "session/title" && references.length === 0 !== (record(data["source"], "title source")["kind"] === "user")) throw new SessionFormatError("session/title messageSeqs must be empty exactly for a user title");
|
|
11373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11374
|
+
for (const value of references) {
|
|
11375
|
+
const seq = earlier(value, event.seq, "title messageSeqs");
|
|
11376
|
+
const source = events[seq];
|
|
11377
|
+
if (seen.has(seq) || source?.type !== "user/message" || !knownEventTypes.has(source.type) || record(record(source.data, "title input")["source"], "title input source")["kind"] !== "user") throw new SessionFormatError(`${event.type} messageSeqs must cite distinct earlier human user/message events`);
|
|
11378
|
+
seen.add(seq);
|
|
11379
|
+
}
|
|
11380
|
+
if (event.type !== "session/title-llm-request") return;
|
|
11381
|
+
const messages = array(data["messages"], "title messages");
|
|
11382
|
+
const message = record(messages[0], "title message");
|
|
11383
|
+
const content = array(message["content"], "title content");
|
|
11384
|
+
const block = content[0];
|
|
11385
|
+
if (references.length === 0 || messages.length !== 1 || message["role"] !== "user" || record(message["source"], "title source")["kind"] !== "dsh-session-title-llm" || content.length !== 1 || !isSessionFormatJsonObject(block) || block["type"] !== "text") throw new SessionFormatError("session/title-llm-request messages do not represent messageSeqs");
|
|
11386
|
+
}
|
|
11387
|
+
/**
|
|
11388
|
+
* Validate native V4 lifecycle and ownership facts without rewriting any event.
|
|
11389
|
+
* @param artifact - artifact whose V4 envelopes and messages have been admitted.
|
|
11390
|
+
* @param knownEventTypes - installed event types whose payloads this reader interprets.
|
|
11391
|
+
*/
|
|
11392
|
+
function assertV4LifecycleRelationships(artifact, knownEventTypes) {
|
|
11393
|
+
const state = new Relationships(artifact, knownEventTypes);
|
|
11394
|
+
for (const event of artifact.events) state.accept(event);
|
|
11395
|
+
}
|
|
11396
|
+
//#endregion
|
|
11397
|
+
//#region ../session-format-v3-to-v4/src/facts.ts
|
|
11398
|
+
/** Historical child identity reduced to the fields required by a parent's catalog. */
|
|
11399
|
+
/**
|
|
11400
|
+
* Validate the historical fields used for catalog membership without interpreting extensions.
|
|
11401
|
+
* @param value - catalog payload or prepared historical discovery fact.
|
|
11402
|
+
* @param subject - event type or child identity used in rejection diagnostics.
|
|
11403
|
+
* @returns validated catalog payload.
|
|
11404
|
+
*/
|
|
11405
|
+
function catalogFact(value, subject = "subagent/catalog") {
|
|
11406
|
+
if (!isSessionFormatJsonObject(value) || value["version"] !== 0 && value["version"] !== 1 || typeof value["childId"] !== "string" || value["mode"] !== "continuable" && value["mode"] !== "one-shot" && value["mode"] !== "unknown" || value["version"] === 0 && value["mode"] === "unknown" || value["mode"] === "continuable" && typeof value["label"] !== "string" || value["label"] !== void 0 && typeof value["label"] !== "string") throw new SessionFormatError(`${subject} requires a supported versioned catalog fact`);
|
|
11407
|
+
sessionFormatCount(value["childCreatedAt"], "catalog child creation time");
|
|
11408
|
+
return value;
|
|
11409
|
+
}
|
|
11410
|
+
//#endregion
|
|
11411
|
+
//#region ../session-format-v3-to-v4/src/validation.ts
|
|
11412
|
+
/** Native V4 metadata and generation-owned relationship validation. */
|
|
11413
|
+
/**
|
|
11414
|
+
* Validate the exact native V4 logical header.
|
|
11415
|
+
* @param header - decoded or otherwise untrusted V4 Session header candidate.
|
|
11416
|
+
*/
|
|
11417
|
+
function assertReleasedV4Header(header) {
|
|
11418
|
+
if (!isSessionFormatJsonObject(header) || header["version"] !== 4) throw new SessionFormatError("expected format v4 header");
|
|
11419
|
+
const required = [
|
|
11420
|
+
"version",
|
|
11421
|
+
"id",
|
|
11422
|
+
"createdAt",
|
|
11423
|
+
"isSeeded",
|
|
11424
|
+
"delegationDepth"
|
|
11425
|
+
];
|
|
11426
|
+
const allowed = new Set([
|
|
11427
|
+
...required,
|
|
11428
|
+
"cwd",
|
|
11429
|
+
"parentSession",
|
|
11430
|
+
"origin",
|
|
11431
|
+
"agentPreset"
|
|
11432
|
+
]);
|
|
11433
|
+
const missing = required.find((key) => !Object.hasOwn(header, key));
|
|
11434
|
+
const unexpected = Object.keys(header).find((key) => !allowed.has(key));
|
|
11435
|
+
if (missing !== void 0) throw new SessionFormatError(`format v4 header lacks required field ${missing}`);
|
|
11436
|
+
if (unexpected !== void 0) throw new SessionFormatError(`format v4 header has unexpected field ${unexpected}`);
|
|
11437
|
+
if (typeof header.id !== "string") throw new SessionFormatError("format v4 header id must be a string");
|
|
11438
|
+
sessionFormatCount(header.createdAt, "format v4 header createdAt");
|
|
11439
|
+
sessionFormatCount(header.delegationDepth, "format v4 header delegationDepth");
|
|
11440
|
+
if (typeof header.isSeeded !== "boolean") throw new SessionFormatError("format v4 header isSeeded must be boolean");
|
|
11441
|
+
if (header.cwd !== void 0 && (typeof header.cwd !== "string" || !(0, node_path.isAbsolute)(header.cwd))) throw new SessionFormatError("format v4 header cwd must be absolute");
|
|
11442
|
+
for (const key of ["parentSession", "agentPreset"]) if (header[key] !== void 0 && typeof header[key] !== "string") throw new SessionFormatError(`format v4 header ${key} must be a string`);
|
|
11443
|
+
if (header.origin !== void 0 && header.origin !== "subagent") throw new SessionFormatError("format v4 header origin must be \"subagent\"");
|
|
11444
|
+
}
|
|
11445
|
+
/**
|
|
11446
|
+
* Validate V4 inheritance, vocabulary, native message admission, and
|
|
11447
|
+
* lifecycle, compaction, tool, retry, title, command, catalog, and delivery ownership.
|
|
11448
|
+
* Installed Session restoration owns common event envelopes and message acceptance.
|
|
11449
|
+
* @param artifact - complete detached V4 artifact.
|
|
11450
|
+
* @param knownEventTypes - event types understood by the installed Session package.
|
|
11451
|
+
* @returns the same validated artifact and event objects.
|
|
11452
|
+
*/
|
|
11453
|
+
function restoreReleasedV4Artifact(artifact, knownEventTypes) {
|
|
11454
|
+
assertReleasedV4Header(artifact.header);
|
|
11455
|
+
const cut = sessionFormatCount(artifact.inheritedEventCount, "format v4 inherited event count");
|
|
11456
|
+
if (cut > artifact.events.length) throw new SessionFormatError("format v4 inherited event count exceeds its events");
|
|
11457
|
+
if (!artifact.header.isSeeded && cut !== 0) throw new SessionFormatError("unseeded format v4 Session has inherited events");
|
|
11458
|
+
let lastInheritedMarker;
|
|
11459
|
+
for (const [index, event] of artifact.events.entries()) {
|
|
11460
|
+
if (!knownEventTypes.has(event.type) && event["ignorable"] !== true) throw new SessionFormatUnsupportedMigrationError(`format v4 contains unknown event type ${JSON.stringify(event.type)} at seq ${index}`);
|
|
11461
|
+
if (event.seq !== index) throw new SessionFormatError(`format v4 event ${index} is not dense`);
|
|
11462
|
+
if (!knownEventTypes.has(event.type)) continue;
|
|
11463
|
+
assertV4RetiredSyntax(event);
|
|
11464
|
+
assertV4SystemMessageFields(event);
|
|
11465
|
+
assertV4ToolResultMessage(event);
|
|
11466
|
+
assertV4ForkResult(event);
|
|
11467
|
+
if (event.type === "session/end-seed" && isSessionFormatJsonObject(event.data) && event.data["inherited"] === true) lastInheritedMarker = index;
|
|
11468
|
+
}
|
|
11469
|
+
if (artifact.header.isSeeded && lastInheritedMarker !== cut) throw new SessionFormatError("format v4 seeded header disagrees with its last inherited end-seed marker");
|
|
11470
|
+
if (!artifact.header.isSeeded && lastInheritedMarker !== void 0) throw new SessionFormatError("format v4 unseeded Session contains an inherited end-seed marker");
|
|
11471
|
+
assertReleasedV4Relationships(artifact, knownEventTypes);
|
|
11472
|
+
return artifact;
|
|
11473
|
+
}
|
|
11474
|
+
/**
|
|
11475
|
+
* Validate delivery generation and active-generation coordinates before evaluating ownership.
|
|
11476
|
+
* @param event - decoded event whose delivery payload may be inspected.
|
|
11477
|
+
* @param currentVersion - generation whose watermark coordinates are active.
|
|
11478
|
+
* @returns the active delivery's nonempty Session id, or undefined for other events and generations.
|
|
11479
|
+
*/
|
|
11480
|
+
function validateDeliveryAccepted(event, currentVersion) {
|
|
11481
|
+
if (event.type !== "session-log-deepseek/delivery-accepted") return void 0;
|
|
11482
|
+
const data = event.data;
|
|
11483
|
+
if (!isSessionFormatJsonObject(data)) throw new SessionFormatError("delivery-accepted data must be an object");
|
|
11484
|
+
if (sessionFormatCount(data["sessionFormatVersion"] === void 0 ? 0 : data["sessionFormatVersion"], "delivery sessionFormatVersion") !== currentVersion) return void 0;
|
|
11485
|
+
if (sessionFormatCount(data["throughSeq"], "delivery throughSeq") >= event.seq) throw new SessionFormatError("delivery throughSeq must precede its marker");
|
|
11486
|
+
const id = data["sessionId"];
|
|
11487
|
+
if (typeof id !== "string" || id.length === 0) throw new SessionFormatError("delivery requires a nonempty Session id");
|
|
11488
|
+
return id;
|
|
11489
|
+
}
|
|
11490
|
+
/**
|
|
11491
|
+
* Validate native developer fields, message sources, lifecycle, catalog, and delivery
|
|
11492
|
+
* relationships without changing event vocabulary or tail recovery.
|
|
11493
|
+
* The owning admission stage rejects unknown required events;
|
|
11494
|
+
* unknown ignorable records retain their uninterpreted payloads.
|
|
11495
|
+
* @param artifact - decoded artifact with its final inherited cut.
|
|
11496
|
+
* @param knownEventTypes - installed event types whose payloads this reader interprets.
|
|
11497
|
+
*/
|
|
11498
|
+
function assertReleasedV4Relationships(artifact, knownEventTypes) {
|
|
11499
|
+
const ids = /* @__PURE__ */ new Set();
|
|
11500
|
+
for (const event of artifact.events) {
|
|
11501
|
+
if (!knownEventTypes.has(event.type)) continue;
|
|
11502
|
+
assertV4DeveloperData(event);
|
|
11503
|
+
assertV4MessageSources(event);
|
|
11504
|
+
const deliveryId = validateDeliveryAccepted(event, 4);
|
|
11505
|
+
if (deliveryId !== void 0 && !(artifact.header.parentSession !== void 0 && event.seq < artifact.inheritedEventCount) && deliveryId !== artifact.header.id) throw new SessionFormatError("current-generation delivery marker names the wrong Session");
|
|
11506
|
+
if (event.type === "subagent/catalog" && event.seq >= artifact.inheritedEventCount) {
|
|
11507
|
+
const id = catalogFact(event.data)["childId"];
|
|
11508
|
+
if (ids.has(id)) throw new SessionFormatError(`duplicate catalog child ${id}`);
|
|
11509
|
+
ids.add(id);
|
|
11510
|
+
}
|
|
11511
|
+
}
|
|
11512
|
+
assertV4LifecycleRelationships(artifact, knownEventTypes);
|
|
11513
|
+
}
|
|
11514
|
+
//#endregion
|
|
11515
|
+
//#region ../session-format-v3-to-v4/src/codec.ts
|
|
11516
|
+
/** V4 framing with native tool-role admission and released physical rows. */
|
|
11517
|
+
function physicalV2(value) {
|
|
11518
|
+
if (!isSessionFormatJsonObject(value) || value["version"] !== 4) throw new SessionFormatError("expected format v4 physical header");
|
|
11519
|
+
return {
|
|
11520
|
+
...value,
|
|
11521
|
+
version: 2
|
|
11522
|
+
};
|
|
11523
|
+
}
|
|
11524
|
+
/**
|
|
11525
|
+
* V4 physical encoder and decoder retain the released row framing while
|
|
11526
|
+
* validating the native tool-role message directly.
|
|
11527
|
+
*/
|
|
11528
|
+
const releasedV4SessionFormatCodec = Object.freeze({
|
|
11529
|
+
version: 4,
|
|
11530
|
+
decodeHeader(value) {
|
|
11531
|
+
return {
|
|
11532
|
+
...releasedV2SessionFormatCodec.decodeHeader(physicalV2(value)),
|
|
11533
|
+
version: 4
|
|
11534
|
+
};
|
|
11535
|
+
},
|
|
11536
|
+
createDecoder(value, recovery) {
|
|
11537
|
+
const decoder = releasedV2SessionFormatCodec.createDecoder(physicalV2(value), recovery);
|
|
11538
|
+
return {
|
|
11539
|
+
...decoder,
|
|
11540
|
+
header: {
|
|
11541
|
+
...decoder.header,
|
|
11542
|
+
version: 4
|
|
11543
|
+
},
|
|
11544
|
+
decodeRow(row, context) {
|
|
11545
|
+
assertV4RowAdmission(row);
|
|
11546
|
+
decoder.decodeRow(row, {
|
|
11547
|
+
emitRun: context.emitRun.bind(context),
|
|
11548
|
+
emitEvent: context.emitEvent.bind(context)
|
|
11549
|
+
});
|
|
11550
|
+
}
|
|
11551
|
+
};
|
|
11552
|
+
},
|
|
11553
|
+
encodeHeader(header, inheritedEventCount) {
|
|
11554
|
+
assertReleasedV4Header(header);
|
|
11555
|
+
return {
|
|
11556
|
+
...releasedV2SessionFormatCodec.encodeHeader({
|
|
11557
|
+
...header,
|
|
11558
|
+
version: 2
|
|
11559
|
+
}, inheritedEventCount),
|
|
11560
|
+
version: 4
|
|
11561
|
+
};
|
|
11562
|
+
},
|
|
11563
|
+
encodeEvent(event) {
|
|
11564
|
+
if (event.type === "developer/message" && event["ignorable"] === true) {
|
|
11565
|
+
assertV4DeveloperData(event);
|
|
11566
|
+
assertV4RetiredSyntax(event);
|
|
11567
|
+
}
|
|
11568
|
+
assertV4RowAdmission(event);
|
|
11569
|
+
return releasedV2SessionFormatCodec.encodeEvent(event);
|
|
11570
|
+
}
|
|
11571
|
+
});
|
|
11572
|
+
/**
|
|
11573
|
+
* Apply native V4 admission before a scanner discards a recoverable suffix.
|
|
11574
|
+
* Ignorable developer payloads require reader vocabulary; physical decoding defers them.
|
|
11575
|
+
* @param row - parsed physical row before framing and source-event range decoding.
|
|
11576
|
+
* @param knownEventTypes - installed event types, supplied by native readers before tail recovery.
|
|
11577
|
+
*/
|
|
11578
|
+
function assertV4RowAdmission(row, knownEventTypes) {
|
|
11579
|
+
if (isSessionFormatJsonObject(row)) {
|
|
11580
|
+
if (row["type"] === "developer/message" && row["ignorable"] === true && knownEventTypes?.has("developer/message") !== true) return;
|
|
11581
|
+
assertV4DeveloperData(row);
|
|
11582
|
+
}
|
|
11583
|
+
assertV4SourceRowAdmission(row);
|
|
11584
|
+
assertV4RetiredSyntax(row);
|
|
11585
|
+
assertV4SystemMessageFields(row);
|
|
11586
|
+
if (!isSessionFormatJsonObject(row) || row["type"] !== "tool/result") return;
|
|
11587
|
+
const event = row;
|
|
11588
|
+
assertV4ToolResultMessage(event);
|
|
11589
|
+
assertV4ForkResult(event);
|
|
11590
|
+
}
|
|
11591
|
+
//#endregion
|
|
11592
|
+
//#region ../session-format-v3-to-v4/src/extension-identities.ts
|
|
11593
|
+
/** First-party event names understood by the released V3 reader, independent of the installed writer. */
|
|
11594
|
+
const RELEASED_V3_EVENT_TYPES = new Set([
|
|
11595
|
+
"agent-preset/selected",
|
|
11596
|
+
"agent/inbox/spliced",
|
|
11597
|
+
"approval/asked",
|
|
11598
|
+
"approval/decided",
|
|
11599
|
+
"approval/policy",
|
|
11600
|
+
"assistant/attempt",
|
|
11601
|
+
"assistant/message",
|
|
11602
|
+
"command/done",
|
|
11603
|
+
"command/run",
|
|
11604
|
+
"compaction/end",
|
|
11605
|
+
"compaction/prune",
|
|
11606
|
+
"compaction/start",
|
|
11607
|
+
"compaction/summary",
|
|
11608
|
+
"deliverables/presented",
|
|
11609
|
+
"feedback/message-delete",
|
|
11610
|
+
"feedback/message-put",
|
|
11611
|
+
"feedback/record",
|
|
11612
|
+
"goal/change",
|
|
11613
|
+
"hook/invoked",
|
|
11614
|
+
"hook/result",
|
|
11615
|
+
"image/offload",
|
|
11616
|
+
"llm/retry",
|
|
11617
|
+
"llm/retry-started",
|
|
11618
|
+
"model/selection",
|
|
11619
|
+
"permission/preset",
|
|
11620
|
+
"plan/mode",
|
|
11621
|
+
"request/context",
|
|
11622
|
+
"request/header",
|
|
11623
|
+
"sandbox/mode",
|
|
11624
|
+
"schedule/change",
|
|
11625
|
+
"session-log-deepseek/delivery-accepted",
|
|
11626
|
+
"session/end-seed",
|
|
11627
|
+
"session/title",
|
|
11628
|
+
"session/title-llm-request",
|
|
11629
|
+
"step/end",
|
|
11630
|
+
"step/start",
|
|
11631
|
+
"subagent/catalog",
|
|
11632
|
+
"subagent/descriptor",
|
|
11633
|
+
"subagent/model-selection-policy",
|
|
11634
|
+
"system/message",
|
|
11635
|
+
"team/member",
|
|
11636
|
+
"team/message/delivered",
|
|
11637
|
+
"team/message/queued",
|
|
11638
|
+
"team/task",
|
|
11639
|
+
"todo/write",
|
|
11640
|
+
"tool-workflow/agent-end",
|
|
11641
|
+
"tool-workflow/agent-start",
|
|
11642
|
+
"tool-workflow/run-end",
|
|
11643
|
+
"tool-workflow/run-start",
|
|
11644
|
+
"tool/call",
|
|
11645
|
+
"tool/ptc-dispatch",
|
|
11646
|
+
"tool/ptc-dispatch-start",
|
|
11647
|
+
"tool/result",
|
|
11648
|
+
"turn/end",
|
|
11649
|
+
"turn/start",
|
|
11650
|
+
"user/message",
|
|
11651
|
+
"web/deepseek-search-llm-request",
|
|
11652
|
+
"workspace/changes"
|
|
11653
|
+
]);
|
|
11654
|
+
//#endregion
|
|
11655
|
+
//#region ../session-format-v3-to-v4/src/migration.ts
|
|
11656
|
+
/** Append historical child facts after converting V3 source events to V4. */
|
|
11657
|
+
/** Header-only migration declaration; body restoration requires explicit child evidence. */
|
|
11658
|
+
const sessionFormatV3ToV4 = defineSessionFormatMigration({
|
|
11659
|
+
name: "@deepseek-ai/dsh-session-format-v3-to-v4",
|
|
11660
|
+
fromVersion: 3,
|
|
11661
|
+
toVersion: 4,
|
|
11662
|
+
migrateHeader(header) {
|
|
11663
|
+
assertReleasedV3Header(header);
|
|
11664
|
+
return {
|
|
11665
|
+
...header,
|
|
11666
|
+
version: 4
|
|
11667
|
+
};
|
|
11668
|
+
},
|
|
11669
|
+
createStage() {
|
|
11670
|
+
throw new SessionFormatUnsupportedMigrationError("V3 catalog migration requires explicit historical child facts, including an empty array for a parent without children");
|
|
11671
|
+
},
|
|
11672
|
+
validateTargetHeader: assertReleasedV4Header
|
|
11673
|
+
});
|
|
10557
11674
|
/** Physical codec dispatch and complete adjacent chain, independent of mounted plugins. */
|
|
10558
11675
|
const sessionFormatCatalog = createSessionFormatCatalog({
|
|
10559
|
-
currentVersion:
|
|
11676
|
+
currentVersion: 4,
|
|
10560
11677
|
codecs: [
|
|
10561
11678
|
releasedV0SessionFormatCodec,
|
|
10562
11679
|
releasedV1SessionFormatCodec,
|
|
10563
11680
|
releasedV2SessionFormatCodec,
|
|
10564
|
-
releasedV3SessionFormatCodec
|
|
11681
|
+
releasedV3SessionFormatCodec,
|
|
11682
|
+
releasedV4SessionFormatCodec
|
|
10565
11683
|
],
|
|
10566
|
-
currentEncoder:
|
|
11684
|
+
currentEncoder: releasedV4SessionFormatCodec,
|
|
10567
11685
|
migrations: [
|
|
10568
11686
|
sessionFormatV0ToV1,
|
|
10569
11687
|
sessionFormatV1ToV2,
|
|
10570
|
-
sessionFormatV2ToV3
|
|
11688
|
+
sessionFormatV2ToV3,
|
|
11689
|
+
sessionFormatV3ToV4
|
|
10571
11690
|
],
|
|
10572
11691
|
restoreCurrent(artifact) {
|
|
10573
|
-
const restored =
|
|
11692
|
+
const restored = restoreReleasedV4Artifact(artifact, KNOWN_SESSION_EVENT_TYPES);
|
|
10574
11693
|
validateInstalledCurrentSessionArtifact(restored);
|
|
10575
11694
|
return restored;
|
|
10576
11695
|
},
|
|
10577
11696
|
restoreTransformedCurrent(artifact) {
|
|
10578
|
-
return
|
|
11697
|
+
return restoreReleasedV4Artifact(artifact, KNOWN_SESSION_EVENT_TYPES);
|
|
10579
11698
|
},
|
|
10580
11699
|
restoreCurrentHeader(header) {
|
|
10581
|
-
|
|
11700
|
+
assertReleasedV4Header(header);
|
|
10582
11701
|
validateInstalledCurrentSessionHeader(header);
|
|
10583
11702
|
return header;
|
|
10584
11703
|
}
|
|
10585
11704
|
});
|
|
11705
|
+
createSessionFormatCatalog({
|
|
11706
|
+
currentVersion: 3,
|
|
11707
|
+
codecs: [
|
|
11708
|
+
releasedV0SessionFormatCodec,
|
|
11709
|
+
releasedV1SessionFormatCodec,
|
|
11710
|
+
releasedV2SessionFormatCodec,
|
|
11711
|
+
releasedV3SessionFormatCodec
|
|
11712
|
+
],
|
|
11713
|
+
currentEncoder: releasedV3SessionFormatCodec,
|
|
11714
|
+
migrations: [
|
|
11715
|
+
sessionFormatV0ToV1,
|
|
11716
|
+
sessionFormatV1ToV2,
|
|
11717
|
+
sessionFormatV2ToV3
|
|
11718
|
+
],
|
|
11719
|
+
restoreCurrent: (artifact) => restoreReleasedV3Artifact(artifact, RELEASED_V3_EVENT_TYPES),
|
|
11720
|
+
restoreTransformedCurrent: (artifact) => restoreReleasedV3Artifact(artifact, RELEASED_V3_EVENT_TYPES),
|
|
11721
|
+
restoreCurrentHeader(header) {
|
|
11722
|
+
assertReleasedV3Header(header);
|
|
11723
|
+
return header;
|
|
11724
|
+
}
|
|
11725
|
+
});
|
|
10586
11726
|
//#endregion
|
|
10587
11727
|
//#region lib/types/format.js
|
|
10588
11728
|
/**
|
|
@@ -10648,7 +11788,7 @@ function assertNoRetiredHeaderFields(value) {
|
|
|
10648
11788
|
function fromHeaderLine(line) {
|
|
10649
11789
|
return {
|
|
10650
11790
|
meta: {
|
|
10651
|
-
version:
|
|
11791
|
+
version: 4,
|
|
10652
11792
|
id: line.id,
|
|
10653
11793
|
createdAt: line.createdAt,
|
|
10654
11794
|
...line.cwd !== void 0 ? { cwd: line.cwd } : {},
|
|
@@ -10674,7 +11814,7 @@ function isHeaderLine(value) {
|
|
|
10674
11814
|
*/
|
|
10675
11815
|
function refuseForeignFormatVersion(parsed) {
|
|
10676
11816
|
const { version, id } = parsed;
|
|
10677
|
-
if (typeof version !== "number" || version ===
|
|
11817
|
+
if (typeof version !== "number" || version === 4) return;
|
|
10678
11818
|
throw new SessionFormatUnsupportedError(sessionFormatVersionRefusal(typeof id === "string" ? id : String(id), version));
|
|
10679
11819
|
}
|
|
10680
11820
|
/** Parse one complete header record supplied independently from event rows. */
|
|
@@ -10780,6 +11920,7 @@ var SessionLogScanner = class {
|
|
|
10780
11920
|
finish() {
|
|
10781
11921
|
this.finished = true;
|
|
10782
11922
|
const artifact = this.restore.finish();
|
|
11923
|
+
assertReleasedV4Relationships(artifact, KNOWN_SESSION_EVENT_TYPES);
|
|
10783
11924
|
return {
|
|
10784
11925
|
meta: this.meta,
|
|
10785
11926
|
inheritedEventCount: SessionLogOffset(artifact.inheritedEventCount),
|
|
@@ -10800,7 +11941,7 @@ var SessionLogScanner = class {
|
|
|
10800
11941
|
return;
|
|
10801
11942
|
}
|
|
10802
11943
|
try {
|
|
10803
|
-
|
|
11944
|
+
assertV4RowAdmission(decoded, KNOWN_SESSION_EVENT_TYPES);
|
|
10804
11945
|
} catch (error) {
|
|
10805
11946
|
if (error instanceof SessionFormatUnsupportedMigrationError) throw new SessionFormatUnsupportedError(error.message);
|
|
10806
11947
|
throw error;
|
|
@@ -11705,6 +12846,7 @@ async function publishPreparedMigration(options, suffix, artifact, sourceIdentit
|
|
|
11705
12846
|
const verifiedStage = await verifyCurrentFile(staged.path, compression, artifact.header.id, eventCount);
|
|
11706
12847
|
if (verifiedStage.bytes !== staged.bytes || verifiedStage.digest !== staged.digest) throw new Error("staged session generation changed during verification");
|
|
11707
12848
|
await internals.barrier("before-source-check", 1);
|
|
12849
|
+
await options.validateRelatedSources?.();
|
|
11708
12850
|
if (identity(await internals.fs.stat(sourcePath)) !== identity(sourceIdentity)) throw new JsonlGenerationSourceChangedError(sourcePath);
|
|
11709
12851
|
const published = await publishCurrentExclusive(staged.path, currentPath, internals);
|
|
11710
12852
|
if (published && internals.platform === "win32") staged = {
|
|
@@ -11753,6 +12895,7 @@ async function prepareMigration(options, internals) {
|
|
|
11753
12895
|
throw error;
|
|
11754
12896
|
}
|
|
11755
12897
|
if (artifact.header.version !== format.currentVersion) throw new Error(`format migration returned v${artifact.header.version}, expected v${format.currentVersion}`);
|
|
12898
|
+
await options.validateRelatedSources?.();
|
|
11756
12899
|
const sourceIdentity = source.identity;
|
|
11757
12900
|
let publication;
|
|
11758
12901
|
return {
|