@morlay/ui-conversation-message-actions 0.0.14-alpha.0 → 0.0.14-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +26 -24
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{plan-Bw4zoI4N.d.mts → plan-C-_enFsA.d.mts} +1 -1
- package/dist/plan.d.mts +1 -1
- package/dist/{src-Da2ZVKlq.mjs → src-DqUEYw-N.mjs} +2 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1254 -80
- package/package.json +19 -19
- package/src/client/chat-node/MessageIconActions.tsx +4 -2
- package/src/index.ts +3 -2
package/dist/testing.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { n as SessionEditor } from "./src-
|
|
1
|
+
import { n as SessionEditor } from "./src-DqUEYw-N.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
4
|
+
import { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, Session, Session as Session$1, SessionId, SessionId as SessionIdBrand, SessionLogOffset, SessionSeq, SessionSeq as SessionSeq$1, SessionStore } from "@deepseek-ai/dsh-session";
|
|
4
5
|
import { SessionBranch, SessionBranchError, balanceRewindPrefix, buildTimeline } from "@morlay/session-branch";
|
|
5
|
-
import crypto, { randomUUID } from "node:crypto";
|
|
6
|
+
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
6
7
|
import { TokenMeter } from "@deepseek-ai/dsh-token-meter";
|
|
7
|
-
import { KNOWN_SESSION_EVENT_TYPES, SESSION_FORMAT_VERSION, Session, Session as Session$1, SessionId, SessionId as SessionIdBrand, SessionLogOffset, SessionSeq, SessionSeq as SessionSeq$1, SessionStore } from "@deepseek-ai/dsh-session";
|
|
8
8
|
import SessionProjectionRegistry from "@deepseek-ai/dsh-session-projection";
|
|
9
9
|
import { SessionAlreadyExistsError, SessionAlreadyOwnedError, SessionHandleClosedError, SessionPersistence, SessionPersistenceNotFoundError, SessionPersistenceRevision, SessionReadOnlyError, assertContiguous, assertVersion, materializeAppendBatch, materializeCreateHeader, validateStoredEvents } from "@deepseek-ai/dsh-session-persistence";
|
|
10
10
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
@@ -14130,7 +14130,7 @@ var CompiledSessionFormatChain = class {
|
|
|
14130
14130
|
}
|
|
14131
14131
|
createStream(sourceHeader, sourceCut, output) {
|
|
14132
14132
|
let header = sourceHeader;
|
|
14133
|
-
const validatedSourceCut = sessionFormatCount(sourceCut, "Session inherited event count");
|
|
14133
|
+
const validatedSourceCut = sourceCut === void 0 ? void 0 : sessionFormatCount(sourceCut, "Session inherited event count");
|
|
14134
14134
|
let inheritedEventCount = validatedSourceCut;
|
|
14135
14135
|
const stages = [];
|
|
14136
14136
|
const plan = this.plan(header.version);
|
|
@@ -14152,11 +14152,7 @@ var CompiledSessionFormatChain = class {
|
|
|
14152
14152
|
migration,
|
|
14153
14153
|
stage
|
|
14154
14154
|
});
|
|
14155
|
-
|
|
14156
|
-
const targetCut = stage.headerInheritedEventCount;
|
|
14157
|
-
if (targetCut === void 0) throw new SessionFormatError(`${migration.name} must expose its inherited cut before the next migration`);
|
|
14158
|
-
inheritedEventCount = targetCut;
|
|
14159
|
-
}
|
|
14155
|
+
inheritedEventCount = stage.headerInheritedEventCount;
|
|
14160
14156
|
}
|
|
14161
14157
|
return new CompiledSessionFormatMigrationStream(header, validatedSourceCut, stages, output);
|
|
14162
14158
|
}
|
|
@@ -14243,7 +14239,7 @@ var CompiledSessionFormatMigrationStream = class {
|
|
|
14243
14239
|
finish() {
|
|
14244
14240
|
let inheritedEventCount = this.sourceInheritedEventCount;
|
|
14245
14241
|
for (const stage of this.stages) inheritedEventCount = stage.finish();
|
|
14246
|
-
return inheritedEventCount;
|
|
14242
|
+
return sessionFormatCount(inheritedEventCount, "finished Session inherited event count");
|
|
14247
14243
|
}
|
|
14248
14244
|
};
|
|
14249
14245
|
function throwUnsupportedRefusal(migration, error, subject = "Session") {
|
|
@@ -14348,7 +14344,7 @@ function createSessionFormatCatalog(options) {
|
|
|
14348
14344
|
const sourceCut = decoder.headerInheritedEventCount;
|
|
14349
14345
|
if (storedVersion === chain.currentVersion) return new CurrentSessionFormatRestore(decoder, sourceCut, restoreOptions.validation === "current" ? options.restoreCurrent : identityArtifact, chain.currentVersion);
|
|
14350
14346
|
const collector = new SessionFormatEventCollector();
|
|
14351
|
-
return new MigratingSessionFormatRestore(decoder, sourceCut, chain.createStream(decoder.header,
|
|
14347
|
+
return new MigratingSessionFormatRestore(decoder, sourceCut, chain.createStream(decoder.header, sourceCut, collector), collector, restoreOptions.validation === "current" ? options.restoreCurrent : options.restoreTransformedCurrent, restoreOptions.validation, storedVersion, chain.currentVersion);
|
|
14352
14348
|
}
|
|
14353
14349
|
return Object.freeze({
|
|
14354
14350
|
currentVersion: chain.currentVersion,
|
|
@@ -14444,10 +14440,6 @@ function restoreCurrentVersion(artifact, currentVersion) {
|
|
|
14444
14440
|
function identityArtifact(artifact) {
|
|
14445
14441
|
return artifact;
|
|
14446
14442
|
}
|
|
14447
|
-
function requiredHistoricalCut(version, cut) {
|
|
14448
|
-
if (cut === void 0) throw new SessionFormatError(`format v${version} decoder must expose its inherited cut before migration`);
|
|
14449
|
-
return cut;
|
|
14450
|
-
}
|
|
14451
14443
|
function malformed(targetVersion, error, storedVersion) {
|
|
14452
14444
|
return Object.freeze({
|
|
14453
14445
|
status: "malformed",
|
|
@@ -15918,6 +15910,39 @@ function assertReleasedV1Header(header) {
|
|
|
15918
15910
|
assertReleasedSessionFormatHeader(header, 1);
|
|
15919
15911
|
}
|
|
15920
15912
|
/**
|
|
15913
|
+
* Validate shared-layout surface references for one released generation.
|
|
15914
|
+
* @param record - exact event envelope.
|
|
15915
|
+
* @param seq - event position used for earlier-reference checks.
|
|
15916
|
+
* @param type - surface event type used in diagnostics.
|
|
15917
|
+
* @param assistantSources - whether this generation admits empty Assistant chunk provenance.
|
|
15918
|
+
*/
|
|
15919
|
+
function assertReleasedSurfaceMetadata(record, seq, type, assistantSources) {
|
|
15920
|
+
const sources = record["sourceEventSeqs"];
|
|
15921
|
+
if (type === "assistant/message" && sources !== void 0 && assistantSources === "forbid-assistant") throw new SessionFormatError(`assistant/message ${seq} retains obsolete chunk provenance`);
|
|
15922
|
+
if (sources !== void 0) {
|
|
15923
|
+
if (!Array.isArray(sources)) throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be an array`);
|
|
15924
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15925
|
+
for (const source of sources) {
|
|
15926
|
+
const current = sessionFormatCount(source, `${type} ${seq} sourceEventSeqs member`);
|
|
15927
|
+
if (current >= seq || seen.has(current)) throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be unique earlier seqs`);
|
|
15928
|
+
seen.add(current);
|
|
15929
|
+
}
|
|
15930
|
+
if (sources.length === 0 && type !== "assistant/message") throw new SessionFormatError(`${type} ${seq} sourceEventSeqs must be non-empty`);
|
|
15931
|
+
}
|
|
15932
|
+
const operation = record["surfaceOp"];
|
|
15933
|
+
if (operation === void 0 || operation === "append") return;
|
|
15934
|
+
const replacement = releasedV0Record(operation, `${type} ${seq} surfaceOp`);
|
|
15935
|
+
assertReleasedV0Keys(replacement, [
|
|
15936
|
+
"op",
|
|
15937
|
+
"start",
|
|
15938
|
+
"end"
|
|
15939
|
+
], [], `${type} ${seq} surfaceOp`);
|
|
15940
|
+
if (replacement["op"] !== "replace") throw new SessionFormatError(`${type} ${seq} surfaceOp must replace`);
|
|
15941
|
+
const start = sessionFormatCount(replacement["start"], `${type} ${seq} surface start`);
|
|
15942
|
+
const end = sessionFormatCount(replacement["end"], `${type} ${seq} surface end`);
|
|
15943
|
+
if (start >= seq || end >= seq) throw new SessionFormatError(`${type} ${seq} has an invalid surface replacement`);
|
|
15944
|
+
}
|
|
15945
|
+
/**
|
|
15921
15946
|
* Validate one exact known payload after legacy normalization.
|
|
15922
15947
|
* @param event - known event to validate.
|
|
15923
15948
|
* @param version - payload generation controlling versioned members.
|
|
@@ -16232,7 +16257,7 @@ var ReleasedV0ToV1Stage = class {
|
|
|
16232
16257
|
constructor(input) {
|
|
16233
16258
|
this.input = input;
|
|
16234
16259
|
assertHeaderVersion(input.sourceHeader, 0);
|
|
16235
|
-
this.headerInheritedEventCount = input.sourceInheritedEventCount;
|
|
16260
|
+
this.headerInheritedEventCount = sessionFormatCount(input.sourceInheritedEventCount, "format v0 inherited event count");
|
|
16236
16261
|
}
|
|
16237
16262
|
transformEvent(event, context) {
|
|
16238
16263
|
const normalized = normalizeReleasedV0Event(event, this.input.sourceHeader.id, this.state);
|
|
@@ -16247,7 +16272,7 @@ var ReleasedV0ToV1Stage = class {
|
|
|
16247
16272
|
for (const event of run.expand()) this.transformEvent(event, context);
|
|
16248
16273
|
}
|
|
16249
16274
|
finish(_context) {
|
|
16250
|
-
return this.
|
|
16275
|
+
return this.headerInheritedEventCount;
|
|
16251
16276
|
}
|
|
16252
16277
|
};
|
|
16253
16278
|
function assertHeaderVersion(header, version) {
|
|
@@ -16287,7 +16312,7 @@ function assertSourceDeliveryMarker$1(event, input) {
|
|
|
16287
16312
|
if (event.type !== "session-log-deepseek/delivery-accepted") return;
|
|
16288
16313
|
const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
|
|
16289
16314
|
const acceptedVersion = data["sessionFormatVersion"] ?? 0;
|
|
16290
|
-
const inherited = input.sourceHeader.parentSession !== void 0 && event.seq < input.sourceInheritedEventCount;
|
|
16315
|
+
const inherited = input.sourceHeader.parentSession !== void 0 && event.seq < sessionFormatCount(input.sourceInheritedEventCount, "format v0 inherited event count");
|
|
16291
16316
|
if (acceptedVersion === 0 && !inherited && data["sessionId"] !== input.sourceHeader.id) throw new SessionFormatError("current-generation delivery marker names the wrong Session");
|
|
16292
16317
|
}
|
|
16293
16318
|
function normalizeLegacyRetry(event, sessionId, retryIds) {
|
|
@@ -16597,7 +16622,7 @@ function legacyMessageId(sessionId, seq) {
|
|
|
16597
16622
|
function malformedLegacy(sessionId, type, seq) {
|
|
16598
16623
|
return new SessionFormatError(`session ${JSON.stringify(sessionId)} contains malformed pre-react-loop ${type} at seq ${seq}`);
|
|
16599
16624
|
}
|
|
16600
|
-
const SURFACE_TYPES$
|
|
16625
|
+
const SURFACE_TYPES$2 = /* @__PURE__ */ new Set([
|
|
16601
16626
|
"user/message",
|
|
16602
16627
|
"assistant/message",
|
|
16603
16628
|
"tool/result"
|
|
@@ -16626,7 +16651,7 @@ function assertReleasedArtifactRelationships(artifact, extensions = {}) {
|
|
|
16626
16651
|
const extensionStepEvent = extensions.stepEvents?.has(event.type) === true;
|
|
16627
16652
|
if (RELEASED_V0_EVENT_DISPOSITIONS[event.type] === void 0 && !extensionStepEvent) continue;
|
|
16628
16653
|
const data = releasedV0Record(event.data, `${event.type} ${event.seq} data`);
|
|
16629
|
-
if (SURFACE_TYPES$
|
|
16654
|
+
if (SURFACE_TYPES$2.has(event.type)) surface = applySurface(surface, event);
|
|
16630
16655
|
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`);
|
|
16631
16656
|
if (extensionStepEvent) {
|
|
16632
16657
|
requireOpenStep(event, data, openTurn, openStep);
|
|
@@ -16959,7 +16984,7 @@ const EVENT_REQUIRED$1 = [
|
|
|
16959
16984
|
"time",
|
|
16960
16985
|
"data"
|
|
16961
16986
|
];
|
|
16962
|
-
const SURFACE_TYPES = /* @__PURE__ */ new Set([
|
|
16987
|
+
const SURFACE_TYPES$1 = /* @__PURE__ */ new Set([
|
|
16963
16988
|
"user/message",
|
|
16964
16989
|
"assistant/message",
|
|
16965
16990
|
"tool/result"
|
|
@@ -16991,7 +17016,7 @@ function assertReleasedV2Header(header) {
|
|
|
16991
17016
|
for (const key of ["parentSession", "agentPreset"]) if (record[key] !== void 0 && typeof record[key] !== "string") throw new SessionFormatError(`format v2 header ${key} must be a string`);
|
|
16992
17017
|
if (record["origin"] !== void 0 && record["origin"] !== "subagent") throw new SessionFormatError("format v2 header origin must be \"subagent\"");
|
|
16993
17018
|
}
|
|
16994
|
-
function validateReleasedV2Artifact(artifact, mode, knownEventTypes) {
|
|
17019
|
+
function validateReleasedV2Artifact(artifact, mode, knownEventTypes, relationshipHeaderVersion = artifact.header.version) {
|
|
16995
17020
|
assertReleasedV2Header(artifact.header);
|
|
16996
17021
|
const cut = sessionFormatCount(artifact.inheritedEventCount, "format v2 inherited event count");
|
|
16997
17022
|
if (cut > artifact.events.length) throw new SessionFormatError("format v2 inherited event count exceeds its events");
|
|
@@ -17005,7 +17030,7 @@ function validateReleasedV2Artifact(artifact, mode, knownEventTypes) {
|
|
|
17005
17030
|
const installed = knownEventTypes?.has(type) === true;
|
|
17006
17031
|
const ignorableUnknown = disposition === void 0 && record["ignorable"] === true;
|
|
17007
17032
|
if (mode === "current" && disposition === void 0 && !installed && !ignorableUnknown) throw new SessionFormatUnsupportedMigrationError(`format v2 contains unknown event type ${JSON.stringify(type)} at seq ${index}`);
|
|
17008
|
-
const surface = disposition !== void 0 && SURFACE_TYPES.has(type);
|
|
17033
|
+
const surface = disposition !== void 0 && SURFACE_TYPES$1.has(type);
|
|
17009
17034
|
assertReleasedV2Keys(record, EVENT_REQUIRED$1, mode === "physical" || disposition === void 0 ? SURFACE_OPTIONAL : surface ? SURFACE_OPTIONAL : LOG_OPTIONAL, `format v2 event ${index}`);
|
|
17010
17035
|
if (record["seq"] !== index) throw new SessionFormatError(`format v2 event ${index} is not dense`);
|
|
17011
17036
|
sessionFormatSafeInteger(record["time"], `format v2 event ${index} time`);
|
|
@@ -17016,7 +17041,13 @@ function validateReleasedV2Artifact(artifact, mode, knownEventTypes) {
|
|
|
17016
17041
|
}
|
|
17017
17042
|
if (artifact.header.isSeeded && lastInheritedMarker !== cut) throw new SessionFormatError("format v2 seeded header disagrees with its last inherited end-seed marker");
|
|
17018
17043
|
if (!artifact.header.isSeeded && lastInheritedMarker !== void 0) throw new SessionFormatError("format v2 unseeded Session contains an inherited end-seed marker");
|
|
17019
|
-
if (mode === "current") assertReleasedArtifactRelationships(
|
|
17044
|
+
if (mode === "current") assertReleasedArtifactRelationships({
|
|
17045
|
+
...artifact,
|
|
17046
|
+
header: {
|
|
17047
|
+
...artifact.header,
|
|
17048
|
+
version: relationshipHeaderVersion
|
|
17049
|
+
}
|
|
17050
|
+
}, RELEASED_V2_RELATIONSHIP_EXTENSIONS);
|
|
17020
17051
|
}
|
|
17021
17052
|
/**
|
|
17022
17053
|
* Require one released-v2 value to be a JSON object.
|
|
@@ -17046,10 +17077,11 @@ function assertReleasedV2Keys(value, required, optional, label) {
|
|
|
17046
17077
|
* Restore and validate one decoded released-v2 artifact.
|
|
17047
17078
|
* @param artifact - detached vocabulary-restored artifact.
|
|
17048
17079
|
* @param knownEventTypes - event types understood by the installed current Session package.
|
|
17080
|
+
* @param relationshipHeaderVersion - logical generation whose version-sensitive relationships are checked.
|
|
17049
17081
|
* @returns the same validated artifact.
|
|
17050
17082
|
*/
|
|
17051
|
-
function restoreReleasedV2Artifact(artifact, knownEventTypes) {
|
|
17052
|
-
validateReleasedV2Artifact(artifact, "current", knownEventTypes);
|
|
17083
|
+
function restoreReleasedV2Artifact(artifact, knownEventTypes, relationshipHeaderVersion = artifact.header.version) {
|
|
17084
|
+
validateReleasedV2Artifact(artifact, "current", knownEventTypes, relationshipHeaderVersion);
|
|
17053
17085
|
return artifact;
|
|
17054
17086
|
}
|
|
17055
17087
|
const HEADER_REQUIRED = [
|
|
@@ -17292,7 +17324,7 @@ var TransformedReleasedV1ToV2Stage = class {
|
|
|
17292
17324
|
assertReleasedV1Header(input.sourceHeader);
|
|
17293
17325
|
this.state = {
|
|
17294
17326
|
sourceHeader: input.sourceHeader,
|
|
17295
|
-
sourceCut: input.sourceInheritedEventCount,
|
|
17327
|
+
sourceCut: sessionFormatCount(input.sourceInheritedEventCount, "format v1 inherited event count"),
|
|
17296
17328
|
mapping: /* @__PURE__ */ new Map(),
|
|
17297
17329
|
legacyTurns: legacyTurnState(),
|
|
17298
17330
|
pending: void 0,
|
|
@@ -17321,7 +17353,7 @@ function transformReleasedEvent(state, event, context) {
|
|
|
17321
17353
|
if (event.type === "assistant/chunk") assertChunkEnvelope(event);
|
|
17322
17354
|
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}`);
|
|
17323
17355
|
const interrupted = legacyInterruptedTurn(state.legacyTurns, event);
|
|
17324
|
-
if (event.type === "turn/start" && state.legacyTurns.openTurn !== null && interrupted === void 0) throw refusal(`turn/start ${JSON.stringify(record(event.data)["turn"])} does not close the prior turn`);
|
|
17356
|
+
if (event.type === "turn/start" && state.legacyTurns.openTurn !== null && interrupted === void 0) throw refusal(`turn/start ${JSON.stringify(record$1(event.data)["turn"])} does not close the prior turn`);
|
|
17325
17357
|
assertSourceDeliveryMarker(state, event);
|
|
17326
17358
|
observeLegacyTurn(state.legacyTurns, event);
|
|
17327
17359
|
state.lastTime = event.time;
|
|
@@ -17363,7 +17395,7 @@ function assertChunkEnvelope(event) {
|
|
|
17363
17395
|
}
|
|
17364
17396
|
function assertSourceDeliveryMarker(state, event) {
|
|
17365
17397
|
if (event.type !== "session-log-deepseek/delivery-accepted") return;
|
|
17366
|
-
const data = record(event.data);
|
|
17398
|
+
const data = record$1(event.data);
|
|
17367
17399
|
const inherited = state.sourceHeader.parentSession !== void 0 && event.seq < state.sourceCut;
|
|
17368
17400
|
if (data["sessionFormatVersion"] === 1 && !inherited && data["sessionId"] !== state.sourceHeader.id) throw refusal("current-generation delivery marker names the wrong Session");
|
|
17369
17401
|
}
|
|
@@ -17400,10 +17432,10 @@ function finishMigration(state, context) {
|
|
|
17400
17432
|
return state.targetCut;
|
|
17401
17433
|
}
|
|
17402
17434
|
function transformChunk(state, event, context) {
|
|
17403
|
-
const data = record(event.data);
|
|
17435
|
+
const data = record$1(event.data);
|
|
17404
17436
|
const turn = coordinate(data["turn"]);
|
|
17405
17437
|
const step = coordinate(data["step"]);
|
|
17406
|
-
const chunk = record(data["chunk"]);
|
|
17438
|
+
const chunk = record$1(data["chunk"]);
|
|
17407
17439
|
if (state.pending !== void 0 && (state.pending.group.terminal || state.pending.group.turn !== turn || state.pending.group.step !== step)) finishAttempt(state, context);
|
|
17408
17440
|
else if (state.pending !== void 0) flushBuffered(state, state.pending, context);
|
|
17409
17441
|
state.pending ??= {
|
|
@@ -17420,7 +17452,7 @@ function transformChunk(state, event, context) {
|
|
|
17420
17452
|
if (chunk["type"] === "finish") state.pending.group.terminal = true;
|
|
17421
17453
|
}
|
|
17422
17454
|
function transformMessage(state, event, context) {
|
|
17423
|
-
const data = record(event.data);
|
|
17455
|
+
const data = record$1(event.data);
|
|
17424
17456
|
const turn = coordinate(data["turn"]);
|
|
17425
17457
|
const step = coordinate(data["step"]);
|
|
17426
17458
|
const sources = event.sourceEventSeqs;
|
|
@@ -17500,8 +17532,8 @@ function legacyTurnState() {
|
|
|
17500
17532
|
};
|
|
17501
17533
|
}
|
|
17502
17534
|
function legacyInterruptedTurn(state, event) {
|
|
17503
|
-
if (event.type !== "turn/start" || state.openTurn === null || state.openStep !== null || coordinate(record(event.data)["turn"]) !== state.openTurn + 1 || state.previous?.type !== "agent/inbox/spliced") return void 0;
|
|
17504
|
-
const splice = record(state.previous.data);
|
|
17535
|
+
if (event.type !== "turn/start" || state.openTurn === null || state.openStep !== null || coordinate(record$1(event.data)["turn"]) !== state.openTurn + 1 || state.previous?.type !== "agent/inbox/spliced") return void 0;
|
|
17536
|
+
const splice = record$1(state.previous.data);
|
|
17505
17537
|
if (splice["target"] !== "next-turn" || !Array.isArray(splice["inserted"]) || splice["inserted"].length === 0) return;
|
|
17506
17538
|
return {
|
|
17507
17539
|
type: "turn/end",
|
|
@@ -17514,7 +17546,7 @@ function legacyInterruptedTurn(state, event) {
|
|
|
17514
17546
|
};
|
|
17515
17547
|
}
|
|
17516
17548
|
function observeLegacyTurn(state, event) {
|
|
17517
|
-
const data = record(event.data);
|
|
17549
|
+
const data = record$1(event.data);
|
|
17518
17550
|
if (event.type === "turn/start") {
|
|
17519
17551
|
state.openTurn = coordinate(data["turn"]);
|
|
17520
17552
|
state.openStep = null;
|
|
@@ -17527,8 +17559,8 @@ function observeLegacyTurn(state, event) {
|
|
|
17527
17559
|
}
|
|
17528
17560
|
function splitLegacyGoalChange(event) {
|
|
17529
17561
|
if (event.type !== "user/message") return void 0;
|
|
17530
|
-
const data = record(event.data);
|
|
17531
|
-
const source = record(data["source"]);
|
|
17562
|
+
const data = record$1(event.data);
|
|
17563
|
+
const source = record$1(data["source"]);
|
|
17532
17564
|
if (source["kind"] !== "goal" || source["change"] === void 0) return void 0;
|
|
17533
17565
|
return {
|
|
17534
17566
|
change: {
|
|
@@ -17647,7 +17679,7 @@ function streamOf(group) {
|
|
|
17647
17679
|
return group.stream.map(({ record }) => record);
|
|
17648
17680
|
}
|
|
17649
17681
|
function messageEvent(source, group) {
|
|
17650
|
-
const data = record(source.data);
|
|
17682
|
+
const data = record$1(source.data);
|
|
17651
17683
|
const { sourceEventSeqs: _sourceEventSeqs, ...event } = source;
|
|
17652
17684
|
return {
|
|
17653
17685
|
...event,
|
|
@@ -17674,7 +17706,7 @@ function remapReferences(source, targetSeq, mapping) {
|
|
|
17674
17706
|
const sources = sourceEventSeqs === void 0 ? {} : { sourceEventSeqs: mapList(numberArray(sourceEventSeqs), mapping, `${source.type} ${source.seq} sources`) };
|
|
17675
17707
|
let operation = surfaceOp;
|
|
17676
17708
|
if (surfaceOp !== void 0 && surfaceOp !== "append") {
|
|
17677
|
-
const replacement = record(surfaceOp);
|
|
17709
|
+
const replacement = record$1(surfaceOp);
|
|
17678
17710
|
operation = {
|
|
17679
17711
|
op: "replace",
|
|
17680
17712
|
start: mapOne(coordinate(replacement["start"]), mapping, `${source.type} ${source.seq} surface start`),
|
|
@@ -17690,7 +17722,7 @@ function remapReferences(source, targetSeq, mapping) {
|
|
|
17690
17722
|
};
|
|
17691
17723
|
}
|
|
17692
17724
|
function remapPayloadReferences(event, mapping) {
|
|
17693
|
-
const data = record(event.data);
|
|
17725
|
+
const data = record$1(event.data);
|
|
17694
17726
|
switch (event.type) {
|
|
17695
17727
|
case "command/done": return data["sourceEventSeq"] === void 0 ? data : {
|
|
17696
17728
|
...data,
|
|
@@ -17698,7 +17730,7 @@ function remapPayloadReferences(event, mapping) {
|
|
|
17698
17730
|
};
|
|
17699
17731
|
case "compaction/prune":
|
|
17700
17732
|
case "compaction/summary": {
|
|
17701
|
-
const range = record(data["shadowedRange"]);
|
|
17733
|
+
const range = record$1(data["shadowedRange"]);
|
|
17702
17734
|
return {
|
|
17703
17735
|
...data,
|
|
17704
17736
|
shadowedRange: {
|
|
@@ -17724,7 +17756,7 @@ function mapOne(value, mapping, label) {
|
|
|
17724
17756
|
if (mapped === void 0) throw refusal(`${label} targets consumed assistant/chunk ${value}`);
|
|
17725
17757
|
return mapped;
|
|
17726
17758
|
}
|
|
17727
|
-
function record(value) {
|
|
17759
|
+
function record$1(value) {
|
|
17728
17760
|
return value;
|
|
17729
17761
|
}
|
|
17730
17762
|
function numberArray(value) {
|
|
@@ -17737,6 +17769,860 @@ function refusal(message) {
|
|
|
17737
17769
|
return new SessionFormatUnsupportedMigrationError(message);
|
|
17738
17770
|
}
|
|
17739
17771
|
//#endregion
|
|
17772
|
+
//#region ../../vendor/deepseek-harness/packages/session/session-format-v2-to-v3/lib/index.js
|
|
17773
|
+
/** Audited V2 migration admission and V3 payload validation, independent of installed core Session types. */
|
|
17774
|
+
/** Audited surface event names; all other admitted events are log-only. */
|
|
17775
|
+
const SURFACE_TYPES = /* @__PURE__ */ new Set([
|
|
17776
|
+
"system/message",
|
|
17777
|
+
"user/message",
|
|
17778
|
+
"assistant/message",
|
|
17779
|
+
"tool/result"
|
|
17780
|
+
]);
|
|
17781
|
+
const SOURCE_KINDS = /* @__PURE__ */ new Set([
|
|
17782
|
+
"user",
|
|
17783
|
+
"plugin",
|
|
17784
|
+
"model",
|
|
17785
|
+
"tool",
|
|
17786
|
+
"agent-instructions",
|
|
17787
|
+
"session-reference",
|
|
17788
|
+
"team-message",
|
|
17789
|
+
"goal",
|
|
17790
|
+
"skill-invocation",
|
|
17791
|
+
"skill-catalog",
|
|
17792
|
+
"coordinator",
|
|
17793
|
+
"subagent-report",
|
|
17794
|
+
"subagent-settled",
|
|
17795
|
+
"webhook",
|
|
17796
|
+
"agent-message"
|
|
17797
|
+
]);
|
|
17798
|
+
/**
|
|
17799
|
+
* Require a JSON object at the durable input boundary.
|
|
17800
|
+
* @param value - decoded value.
|
|
17801
|
+
* @param label - diagnostic subject.
|
|
17802
|
+
* @returns the narrowed object.
|
|
17803
|
+
*/
|
|
17804
|
+
function record(value, label) {
|
|
17805
|
+
if (!isSessionFormatJsonObject(value)) throw new SessionFormatError(label + " must be an object");
|
|
17806
|
+
return value;
|
|
17807
|
+
}
|
|
17808
|
+
/**
|
|
17809
|
+
* Reject missing and unaudited members rather than guessing whether they contain coordinates.
|
|
17810
|
+
* @param value - decoded record.
|
|
17811
|
+
* @param required - required member names.
|
|
17812
|
+
* @param optional - additional admitted names.
|
|
17813
|
+
* @param label - diagnostic subject.
|
|
17814
|
+
*/
|
|
17815
|
+
function keys(value, required, optional, label) {
|
|
17816
|
+
const missing = required.find((key) => !Object.hasOwn(value, key));
|
|
17817
|
+
const unexpected = Object.keys(value).find((key) => !required.includes(key) && !optional.includes(key));
|
|
17818
|
+
if (missing !== void 0) throw new SessionFormatError(label + " lacks required field " + missing);
|
|
17819
|
+
if (unexpected !== void 0) throw new SessionFormatError(label + " has unexpected field " + unexpected);
|
|
17820
|
+
}
|
|
17821
|
+
/**
|
|
17822
|
+
* Validate classified payloads before migration, or native V3 system/header payloads.
|
|
17823
|
+
* @param event - decoded logical event.
|
|
17824
|
+
* @param version - source or target generation.
|
|
17825
|
+
*/
|
|
17826
|
+
function assertEvent(event, version) {
|
|
17827
|
+
if (version === 3) {
|
|
17828
|
+
assertV3Event(event);
|
|
17829
|
+
return;
|
|
17830
|
+
}
|
|
17831
|
+
const disposition = RELEASED_V2_EVENT_DISPOSITIONS[event.type];
|
|
17832
|
+
const feedback = event.type === "feedback/message-put" || event.type === "feedback/message-delete";
|
|
17833
|
+
if (disposition === void 0 && !feedback) throw new SessionFormatUnsupportedMigrationError("format v2 to v3 cannot safely transform unclassified event " + event.type);
|
|
17834
|
+
const surface = SURFACE_TYPES.has(event.type);
|
|
17835
|
+
keys(event, [
|
|
17836
|
+
"type",
|
|
17837
|
+
"seq",
|
|
17838
|
+
"time",
|
|
17839
|
+
"data"
|
|
17840
|
+
], surface ? [
|
|
17841
|
+
"ignorable",
|
|
17842
|
+
"sourceEventSeqs",
|
|
17843
|
+
"surfaceOp"
|
|
17844
|
+
] : ["ignorable"], event.type);
|
|
17845
|
+
sessionFormatCount(event.seq, "event seq");
|
|
17846
|
+
sessionFormatSafeInteger(event.time, "event time");
|
|
17847
|
+
if (event["ignorable"] !== void 0 && event["ignorable"] !== true) throw new SessionFormatError("ignorable must be true");
|
|
17848
|
+
if (surface) {
|
|
17849
|
+
assertReleasedSurfaceMetadata(event, event.seq, event.type, "forbid-assistant");
|
|
17850
|
+
if (event["surfaceOp"] === void 0) throw new SessionFormatError(event.type + " requires surfaceOp");
|
|
17851
|
+
}
|
|
17852
|
+
const data = record(event.data, event.type + " data");
|
|
17853
|
+
if (feedback) {
|
|
17854
|
+
assertFeedback(event.type, data);
|
|
17855
|
+
return;
|
|
17856
|
+
}
|
|
17857
|
+
const admitted = disposition;
|
|
17858
|
+
keys(data, admitted.required, admitted.optional, event.type + " data");
|
|
17859
|
+
assertOwnedContent(event, data);
|
|
17860
|
+
if (event.type !== "assistant/attempt") assertReleasedPayloadSemantics(event, version);
|
|
17861
|
+
if (event.type === "assistant/message" || event.type === "assistant/attempt") {
|
|
17862
|
+
for (const coordinate of ["turn", "step"]) if (sessionFormatCount(data[coordinate], coordinate) === 0) throw new SessionFormatError(coordinate + " must be positive");
|
|
17863
|
+
}
|
|
17864
|
+
if (event.type === "session/end-seed" && data["inherited"] !== void 0 && data["inherited"] !== true) throw new SessionFormatError("session/end-seed inherited must be true");
|
|
17865
|
+
if (event.type === "user/message") assertSource(data);
|
|
17866
|
+
if (event.type === "assistant/message" || event.type === "tool/result") assertSource(record(data["message"], "message"));
|
|
17867
|
+
if (event.type === "tool/result" && isSessionFormatJsonObject(data["error"]) && data["error"]["code"] === "TOOL_NOT_STARTED") {
|
|
17868
|
+
const message = record(data["message"], "tool result message");
|
|
17869
|
+
const source = record(message["source"], "tool result source");
|
|
17870
|
+
if (!isRepairIdentity(message["id"], source["callId"])) throw new SessionFormatError("TOOL_NOT_STARTED repair requires its canonical historical message id");
|
|
17871
|
+
}
|
|
17872
|
+
if (event.type === "agent/inbox/spliced" || event.type === "session/title-llm-request") {
|
|
17873
|
+
const messages = data[event.type === "agent/inbox/spliced" ? "inserted" : "messages"];
|
|
17874
|
+
for (const message of messages) assertSource(message);
|
|
17875
|
+
}
|
|
17876
|
+
}
|
|
17877
|
+
/**
|
|
17878
|
+
* Recognize stable generated repair IDs without interpreting their historical suffix as a current coordinate.
|
|
17879
|
+
* @param id - durable message identity.
|
|
17880
|
+
* @param callId - advertised tool identity.
|
|
17881
|
+
* @returns whether the identity has the canonical historical repair form.
|
|
17882
|
+
*/
|
|
17883
|
+
function isRepairIdentity(id, callId) {
|
|
17884
|
+
if (typeof callId !== "string") return false;
|
|
17885
|
+
const prefix = "interrupted-tool-result-" + callId + "-";
|
|
17886
|
+
if (typeof id !== "string" || !id.startsWith(prefix)) return false;
|
|
17887
|
+
const suffix = id.slice(prefix.length);
|
|
17888
|
+
return /^(0|[1-9]\d*)$/.test(suffix) && Number.isSafeInteger(Number(suffix));
|
|
17889
|
+
}
|
|
17890
|
+
function assertSource(message) {
|
|
17891
|
+
const source = record(message["source"], "message source");
|
|
17892
|
+
if (typeof source["kind"] !== "string" || !SOURCE_KINDS.has(source["kind"])) throw new SessionFormatUnsupportedMigrationError("cannot safely transform unclassified message source");
|
|
17893
|
+
if (source["kind"] === "agent-message") {
|
|
17894
|
+
keys(source, [
|
|
17895
|
+
"kind",
|
|
17896
|
+
"form",
|
|
17897
|
+
"senderSessionId"
|
|
17898
|
+
], [], "agent-message source");
|
|
17899
|
+
if (source["form"] !== "relay" || typeof source["senderSessionId"] !== "string" || source["senderSessionId"].length === 0) throw new SessionFormatError("agent-message source requires relay form and senderSessionId");
|
|
17900
|
+
}
|
|
17901
|
+
}
|
|
17902
|
+
const CONTENT_KINDS = /* @__PURE__ */ new Set([
|
|
17903
|
+
"text",
|
|
17904
|
+
"reasoning",
|
|
17905
|
+
"image",
|
|
17906
|
+
"file",
|
|
17907
|
+
"tool-call",
|
|
17908
|
+
"tool-result"
|
|
17909
|
+
]);
|
|
17910
|
+
function contentArray(value, label) {
|
|
17911
|
+
if (!Array.isArray(value)) throw new SessionFormatError(label + ": content must be an array");
|
|
17912
|
+
return value;
|
|
17913
|
+
}
|
|
17914
|
+
function assertOwnedContent(event, data) {
|
|
17915
|
+
const label = "format v2 " + event.type + " at seq " + String(event.seq) + " data";
|
|
17916
|
+
switch (event.type) {
|
|
17917
|
+
case "user/message":
|
|
17918
|
+
case "tool/code-dispatch":
|
|
17919
|
+
assertContentKinds(data["content"], label + ".content");
|
|
17920
|
+
break;
|
|
17921
|
+
case "assistant/message":
|
|
17922
|
+
case "tool/result":
|
|
17923
|
+
case "team/message/queued":
|
|
17924
|
+
assertContentKinds(record(data["message"], label + ".message")["content"], label + ".message.content");
|
|
17925
|
+
break;
|
|
17926
|
+
case "agent/inbox/spliced":
|
|
17927
|
+
case "session/title-llm-request": {
|
|
17928
|
+
const field = event.type === "agent/inbox/spliced" ? "inserted" : "messages";
|
|
17929
|
+
for (const [index, value] of contentArray(data[field], label + "." + field).entries()) {
|
|
17930
|
+
const path = label + "." + field + "[" + String(index) + "]";
|
|
17931
|
+
assertContentKinds(record(value, path)["content"], path + ".content");
|
|
17932
|
+
}
|
|
17933
|
+
break;
|
|
17934
|
+
}
|
|
17935
|
+
case "compaction/summary":
|
|
17936
|
+
assertContentKinds(data["summary"], label + ".summary");
|
|
17937
|
+
if (data["rawOutput"] !== void 0) assertContentKinds(data["rawOutput"], label + ".rawOutput");
|
|
17938
|
+
}
|
|
17939
|
+
if (event.type === "assistant/message" || event.type === "assistant/attempt") for (const [index, value] of contentArray(data["stream"], label + ".stream").entries()) {
|
|
17940
|
+
const path = label + ".stream[" + String(index) + "]";
|
|
17941
|
+
const entry = record(value, path);
|
|
17942
|
+
if (entry["type"] !== "chunk") continue;
|
|
17943
|
+
const chunk = record(entry["chunk"], path + ".chunk");
|
|
17944
|
+
if (chunk["type"] === "block-end") assertContentBlock(chunk["block"], path + ".chunk.block");
|
|
17945
|
+
if (chunk["type"] === "block-start") assertContentKind(chunk["blockType"], path + ".chunk.blockType");
|
|
17946
|
+
}
|
|
17947
|
+
}
|
|
17948
|
+
function assertContentKind(kind, label) {
|
|
17949
|
+
if (typeof kind !== "string" || !CONTENT_KINDS.has(kind)) throw new SessionFormatUnsupportedMigrationError(label + ": cannot safely transform unclassified message content kind " + JSON.stringify(kind));
|
|
17950
|
+
}
|
|
17951
|
+
function assertContentKinds(content, label) {
|
|
17952
|
+
for (const [index, value] of contentArray(content, label).entries()) assertContentBlock(value, label + "[" + String(index) + "]");
|
|
17953
|
+
}
|
|
17954
|
+
function assertContentBlock(value, label) {
|
|
17955
|
+
const block = record(value, label);
|
|
17956
|
+
assertContentKind(block["type"], label);
|
|
17957
|
+
if (block["type"] === "tool-result") {
|
|
17958
|
+
if (!Array.isArray(block["content"])) throw new SessionFormatError(label + ".content: invalid message content kind \"tool-result\": content must be an array");
|
|
17959
|
+
assertContentKinds(block["content"], label + ".content");
|
|
17960
|
+
}
|
|
17961
|
+
if (block["type"] === "file") {
|
|
17962
|
+
keys(block, ["type", "attachment"], [], label + " kind \"file\"");
|
|
17963
|
+
const attachment = record(block["attachment"], label + " kind \"file\" attachment");
|
|
17964
|
+
keys(attachment, [
|
|
17965
|
+
"attachmentId",
|
|
17966
|
+
"name",
|
|
17967
|
+
"bytes"
|
|
17968
|
+
], [], label + " kind \"file\" attachment");
|
|
17969
|
+
if (typeof attachment["attachmentId"] !== "string" || attachment["attachmentId"].length === 0 || typeof attachment["name"] !== "string") throw new SessionFormatError(label + " kind \"file\": file attachment requires attachmentId and name");
|
|
17970
|
+
sessionFormatCount(attachment["bytes"], label + " kind \"file\" attachment bytes");
|
|
17971
|
+
return;
|
|
17972
|
+
}
|
|
17973
|
+
const probe = {
|
|
17974
|
+
type: "user/message",
|
|
17975
|
+
seq: 0,
|
|
17976
|
+
time: 0,
|
|
17977
|
+
data: {
|
|
17978
|
+
id: "content-admission",
|
|
17979
|
+
role: "user",
|
|
17980
|
+
source: { kind: "user" },
|
|
17981
|
+
content: [block["type"] === "tool-result" ? {
|
|
17982
|
+
...block,
|
|
17983
|
+
content: []
|
|
17984
|
+
} : block]
|
|
17985
|
+
}
|
|
17986
|
+
};
|
|
17987
|
+
try {
|
|
17988
|
+
assertReleasedPayloadSemantics(probe, 2);
|
|
17989
|
+
} catch (error) {
|
|
17990
|
+
throw new SessionFormatError(label + ": invalid message content kind " + JSON.stringify(block["type"]) + ": " + String(error));
|
|
17991
|
+
}
|
|
17992
|
+
}
|
|
17993
|
+
/**
|
|
17994
|
+
* Reject V3 structural payload violations even beyond a recoverable physical-row failure.
|
|
17995
|
+
* @param value - raw physical row; ordinary rows retain the frozen decoder's recovery policy.
|
|
17996
|
+
*/
|
|
17997
|
+
function assertV3StructuralRow(value) {
|
|
17998
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
17999
|
+
const row = value;
|
|
18000
|
+
if (row["type"] === "request/header") {
|
|
18001
|
+
const data = record(row["data"], "request/header data");
|
|
18002
|
+
if (Object.hasOwn(record(data["header"], "request header"), "system")) throw new SessionFormatUnsupportedMigrationError("format v3 request/header rejects retired header.system");
|
|
18003
|
+
} else if (row["type"] === "system/message") {
|
|
18004
|
+
const data = record(row["data"], "system/message data");
|
|
18005
|
+
assertSystem({
|
|
18006
|
+
type: "system/message",
|
|
18007
|
+
seq: 0,
|
|
18008
|
+
time: 0,
|
|
18009
|
+
data
|
|
18010
|
+
}, data);
|
|
18011
|
+
}
|
|
18012
|
+
}
|
|
18013
|
+
function assertSystem(event, data) {
|
|
18014
|
+
keys(data, [
|
|
18015
|
+
"turn",
|
|
18016
|
+
"step",
|
|
18017
|
+
"message"
|
|
18018
|
+
], [], "system/message data");
|
|
18019
|
+
for (const coordinate of ["turn", "step"]) if (sessionFormatCount(data[coordinate], coordinate) === 0) throw new SessionFormatError(coordinate + " must be positive");
|
|
18020
|
+
const message = record(data["message"], "system message");
|
|
18021
|
+
keys(message, [
|
|
18022
|
+
"id",
|
|
18023
|
+
"role",
|
|
18024
|
+
"source",
|
|
18025
|
+
"content"
|
|
18026
|
+
], [], "system message");
|
|
18027
|
+
if (typeof message["id"] !== "string" || message["id"].length === 0 || message["role"] !== "system") throw new SessionFormatError("system message requires an id and system role");
|
|
18028
|
+
const source = record(message["source"], "system source");
|
|
18029
|
+
if (source["kind"] !== "plugin" || typeof source["plugin"] !== "string" || source["plugin"].length === 0) throw new SessionFormatError("system message requires plugin source");
|
|
18030
|
+
assertReleasedPayloadSemantics({
|
|
18031
|
+
...event,
|
|
18032
|
+
type: "user/message",
|
|
18033
|
+
data: {
|
|
18034
|
+
...message,
|
|
18035
|
+
role: "user"
|
|
18036
|
+
}
|
|
18037
|
+
}, 3);
|
|
18038
|
+
}
|
|
18039
|
+
function assertFeedback(type, data) {
|
|
18040
|
+
keys(data, type === "feedback/message-put" ? ["sessionId", "item"] : ["sessionId", "messageId"], [], type);
|
|
18041
|
+
if (typeof data["sessionId"] !== "string") throw new SessionFormatError("feedback sessionId must be a string");
|
|
18042
|
+
if (type === "feedback/message-delete") {
|
|
18043
|
+
if (typeof data["messageId"] !== "string") throw new SessionFormatError("feedback messageId must be a string");
|
|
18044
|
+
return;
|
|
18045
|
+
}
|
|
18046
|
+
const item = record(data["item"], "feedback item");
|
|
18047
|
+
keys(item, [
|
|
18048
|
+
"messageId",
|
|
18049
|
+
"rating",
|
|
18050
|
+
"version",
|
|
18051
|
+
"createdAt",
|
|
18052
|
+
"updatedAt"
|
|
18053
|
+
], ["note"], "feedback item");
|
|
18054
|
+
for (const key of ["messageId", "version"]) if (typeof item[key] !== "string") throw new SessionFormatError("feedback " + key + " must be a string");
|
|
18055
|
+
if (item["rating"] !== "positive" && item["rating"] !== "negative") throw new SessionFormatError("invalid feedback rating");
|
|
18056
|
+
if (item["note"] !== void 0 && typeof item["note"] !== "string") throw new SessionFormatError("feedback note must be a string");
|
|
18057
|
+
sessionFormatCount(item["createdAt"], "feedback createdAt");
|
|
18058
|
+
sessionFormatCount(item["updatedAt"], "feedback updatedAt");
|
|
18059
|
+
}
|
|
18060
|
+
/**
|
|
18061
|
+
* Validate one canonical V3 event without interpreting plugin-owned payloads or log relationships.
|
|
18062
|
+
* Unclassified metadata is deferred to vocabulary-aware restoration; unknown required types must not become recoverable corruption.
|
|
18063
|
+
* @param event - decoded logical event.
|
|
18064
|
+
* @param knownEventTypes - additional installed event types whose envelopes are interpreted.
|
|
18065
|
+
*/
|
|
18066
|
+
function assertV3Event(event, knownEventTypes) {
|
|
18067
|
+
const value = record(event, "format v3 event");
|
|
18068
|
+
const subject = `format v3 ${event.type} at seq ${event.seq}`;
|
|
18069
|
+
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));
|
|
18070
|
+
keys(value, [
|
|
18071
|
+
"type",
|
|
18072
|
+
"seq",
|
|
18073
|
+
"time",
|
|
18074
|
+
"data"
|
|
18075
|
+
], SURFACE_TYPES.has(event.type) || opaque ? [
|
|
18076
|
+
"ignorable",
|
|
18077
|
+
"surfaceOp",
|
|
18078
|
+
"sourceEventSeqs"
|
|
18079
|
+
] : ["ignorable"], subject);
|
|
18080
|
+
if (typeof event.type !== "string") throw new SessionFormatError(`${subject} type must be a string`);
|
|
18081
|
+
sessionFormatCount(event.seq, `${subject} seq`);
|
|
18082
|
+
sessionFormatSafeInteger(event.time, `${subject} time`);
|
|
18083
|
+
if (Object.hasOwn(value, "ignorable") && value["ignorable"] !== true) throw new SessionFormatError(`${subject} ignorable must be true when present`);
|
|
18084
|
+
if (SURFACE_TYPES.has(event.type)) {
|
|
18085
|
+
const operation = value["surfaceOp"];
|
|
18086
|
+
if (operation === void 0) throw new SessionFormatError(`${subject} requires a surfaceOp marker`);
|
|
18087
|
+
if (operation !== "append") {
|
|
18088
|
+
const replace = record(operation, `${subject} surfaceOp`);
|
|
18089
|
+
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`);
|
|
18090
|
+
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`);
|
|
18091
|
+
}
|
|
18092
|
+
const sources = value["sourceEventSeqs"];
|
|
18093
|
+
if (event.type === "assistant/message" && sources !== void 0) throw new SessionFormatError(`${subject} embeds its stream and cannot carry sourceEventSeqs`);
|
|
18094
|
+
if (sources !== void 0) {
|
|
18095
|
+
if (!Array.isArray(sources) || sources.length === 0) throw new SessionFormatError(`${subject} sourceEventSeqs must be a non-empty array`);
|
|
18096
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18097
|
+
for (const source of sources) {
|
|
18098
|
+
const seq = sessionFormatCount(source, `${subject} sourceEventSeqs member`);
|
|
18099
|
+
if (seq >= event.seq || seen.has(seq)) throw new SessionFormatError(`${subject} sourceEventSeqs must be unique earlier seqs`);
|
|
18100
|
+
seen.add(seq);
|
|
18101
|
+
}
|
|
18102
|
+
}
|
|
18103
|
+
}
|
|
18104
|
+
assertV3StructuralRow(event);
|
|
18105
|
+
assertCanonicalPayload(event);
|
|
18106
|
+
}
|
|
18107
|
+
function assertCanonicalPayload(event) {
|
|
18108
|
+
const subject = `format v3 ${event.type} at seq ${event.seq}`;
|
|
18109
|
+
if (event.type === "request/header") {
|
|
18110
|
+
const header = record(record(event.data, `${subject} data`)["header"], `${subject} header`);
|
|
18111
|
+
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`);
|
|
18112
|
+
}
|
|
18113
|
+
if (event.type !== "tool/result") return;
|
|
18114
|
+
const data = record(event.data, `${subject} data`);
|
|
18115
|
+
if (data["error"] === void 0) return;
|
|
18116
|
+
const content = record(data["message"], `${subject} message`)["content"];
|
|
18117
|
+
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`);
|
|
18118
|
+
}
|
|
18119
|
+
/**
|
|
18120
|
+
* Canonicalize structurally transformed events without changing their target coordinates.
|
|
18121
|
+
* @param event - transformed event using released replacement names and target coordinates.
|
|
18122
|
+
* @returns a V3 event sharing all unchanged payloads and reference values.
|
|
18123
|
+
*/
|
|
18124
|
+
function canonicalizeTransformedEvent(event) {
|
|
18125
|
+
let target = event;
|
|
18126
|
+
const operation = event["surfaceOp"];
|
|
18127
|
+
if (operation !== void 0 && operation !== "append") {
|
|
18128
|
+
const replace = record(operation, `format v2 ${event.type} at seq ${event.seq} surfaceOp`);
|
|
18129
|
+
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`);
|
|
18130
|
+
target = {
|
|
18131
|
+
...event,
|
|
18132
|
+
surfaceOp: {
|
|
18133
|
+
op: "replace",
|
|
18134
|
+
startSeq: sessionFormatCount(replace["start"], `format v2 ${event.type} at seq ${event.seq} replace start`),
|
|
18135
|
+
endSeq: sessionFormatCount(replace["end"], `format v2 ${event.type} at seq ${event.seq} replace end`)
|
|
18136
|
+
}
|
|
18137
|
+
};
|
|
18138
|
+
}
|
|
18139
|
+
if (event.type === "request/header") {
|
|
18140
|
+
const data = record(event.data, `format v2 request/header at seq ${event.seq} data`);
|
|
18141
|
+
const header = record(data["header"], `format v2 request/header at seq ${event.seq} header`);
|
|
18142
|
+
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);
|
|
18143
|
+
if (empty.length > 0) {
|
|
18144
|
+
const canonical = Object.fromEntries(Object.entries(header).filter(([key]) => !empty.includes(key)));
|
|
18145
|
+
target = {
|
|
18146
|
+
...target,
|
|
18147
|
+
data: {
|
|
18148
|
+
...data,
|
|
18149
|
+
header: canonical
|
|
18150
|
+
}
|
|
18151
|
+
};
|
|
18152
|
+
}
|
|
18153
|
+
}
|
|
18154
|
+
assertV3Event(target);
|
|
18155
|
+
return target;
|
|
18156
|
+
}
|
|
18157
|
+
/** Native V3 system-head validation with a private view for frozen non-system relationships. */
|
|
18158
|
+
/**
|
|
18159
|
+
* Validate v3 logical metadata with the released-v2 fields.
|
|
18160
|
+
* @param header - decoded v3 Session header.
|
|
18161
|
+
*/
|
|
18162
|
+
function assertReleasedV3Header(header) {
|
|
18163
|
+
if (header.version !== 3) throw new SessionFormatError("expected format v3 header");
|
|
18164
|
+
assertReleasedV2Header({
|
|
18165
|
+
...header,
|
|
18166
|
+
version: 2
|
|
18167
|
+
});
|
|
18168
|
+
}
|
|
18169
|
+
/**
|
|
18170
|
+
* Validate system ownership, protected-head operations, ordinary relationships, and inherited cut.
|
|
18171
|
+
* The private relationship view never escapes; the returned artifact and its messages are unchanged.
|
|
18172
|
+
* @param artifact - detached v3 artifact.
|
|
18173
|
+
* @param knownEventTypes - event types understood by the installed Session package.
|
|
18174
|
+
* @returns the same validated artifact.
|
|
18175
|
+
*/
|
|
18176
|
+
function restoreReleasedV3Artifact(artifact, knownEventTypes) {
|
|
18177
|
+
assertReleasedV3Header(artifact.header);
|
|
18178
|
+
let step;
|
|
18179
|
+
let head;
|
|
18180
|
+
let hasSurface = false;
|
|
18181
|
+
const events = artifact.events.map((event) => {
|
|
18182
|
+
assertV3EventAdmission(event);
|
|
18183
|
+
assertV3Event(event, knownEventTypes);
|
|
18184
|
+
const system = event.type === "system/message";
|
|
18185
|
+
if (event.type === "step/start") {
|
|
18186
|
+
const data = record(event.data, event.type);
|
|
18187
|
+
step = {
|
|
18188
|
+
turn: data["turn"],
|
|
18189
|
+
step: data["step"]
|
|
18190
|
+
};
|
|
18191
|
+
} else if (event.type === "step/end" || event.type === "turn/end") step = void 0;
|
|
18192
|
+
if (system) {
|
|
18193
|
+
const data = record(event.data, "system/message");
|
|
18194
|
+
if (step === void 0 || step.turn !== data["turn"] || step.step !== data["step"]) throw new SessionFormatError("system/message does not match an open step");
|
|
18195
|
+
const operation = event["surfaceOp"];
|
|
18196
|
+
if (hasSurface && head === void 0) throw new SessionFormatError("system/message requires a protected first surface head");
|
|
18197
|
+
if (operation === "append") {
|
|
18198
|
+
if (!hasSurface) head = event.seq;
|
|
18199
|
+
} else {
|
|
18200
|
+
const replace = record(operation, "system replacement");
|
|
18201
|
+
if (replace["startSeq"] === head || replace["endSeq"] === head) {
|
|
18202
|
+
if (replace["startSeq"] !== head || replace["endSeq"] !== head) throw new SessionFormatError("system/message must replace exactly the current system head");
|
|
18203
|
+
head = event.seq;
|
|
18204
|
+
}
|
|
18205
|
+
}
|
|
18206
|
+
} else if (SURFACE_TYPES.has(event.type) && event["surfaceOp"] !== "append") {
|
|
18207
|
+
const replace = record(event["surfaceOp"], "surface replacement");
|
|
18208
|
+
if (replace["startSeq"] === head || replace["endSeq"] === head) throw new SessionFormatError("surface replacement cannot shadow the protected system head");
|
|
18209
|
+
}
|
|
18210
|
+
if (event.type === "compaction/prune" || event.type === "compaction/summary") {
|
|
18211
|
+
const seqs = record(event.data, event.type)["shadowedSeqs"];
|
|
18212
|
+
if (Array.isArray(seqs) && seqs.some((seq) => seq === head)) throw new SessionFormatError("compaction cannot shadow the protected system head");
|
|
18213
|
+
}
|
|
18214
|
+
if (SURFACE_TYPES.has(event.type)) hasSurface = true;
|
|
18215
|
+
const projected = relationshipEvent(event);
|
|
18216
|
+
if (!SURFACE_TYPES.has(event.type) || event["surfaceOp"] === "append") return projected;
|
|
18217
|
+
const replacement = event["surfaceOp"];
|
|
18218
|
+
return {
|
|
18219
|
+
...projected,
|
|
18220
|
+
surfaceOp: {
|
|
18221
|
+
op: "replace",
|
|
18222
|
+
start: replacement.startSeq,
|
|
18223
|
+
end: replacement.endSeq
|
|
18224
|
+
}
|
|
18225
|
+
};
|
|
18226
|
+
});
|
|
18227
|
+
restoreReleasedV2Artifact({
|
|
18228
|
+
...artifact,
|
|
18229
|
+
header: {
|
|
18230
|
+
...artifact.header,
|
|
18231
|
+
version: 2
|
|
18232
|
+
},
|
|
18233
|
+
events
|
|
18234
|
+
}, knownEventTypes, 3);
|
|
18235
|
+
return artifact;
|
|
18236
|
+
}
|
|
18237
|
+
/**
|
|
18238
|
+
* Refuse required predecessor PTC tags without interpreting native extension payloads.
|
|
18239
|
+
* @param event - event envelope whose type and ignorable admission markers are available.
|
|
18240
|
+
*/
|
|
18241
|
+
function assertV3EventAdmission(event) {
|
|
18242
|
+
if ((event.type === "tool/code-dispatch-start" || event.type === "tool/code-dispatch") && event["ignorable"] !== true) throw new SessionFormatUnsupportedMigrationError("format v3 contains unknown event type " + JSON.stringify(event.type) + " at seq " + String(event.seq));
|
|
18243
|
+
}
|
|
18244
|
+
function relationshipEvent(event) {
|
|
18245
|
+
switch (event.type) {
|
|
18246
|
+
case "tool/ptc-dispatch-start": return {
|
|
18247
|
+
...event,
|
|
18248
|
+
type: "tool/code-dispatch-start"
|
|
18249
|
+
};
|
|
18250
|
+
case "tool/ptc-dispatch": return {
|
|
18251
|
+
...event,
|
|
18252
|
+
type: "tool/code-dispatch"
|
|
18253
|
+
};
|
|
18254
|
+
case "tool/code-dispatch-start":
|
|
18255
|
+
case "tool/code-dispatch":
|
|
18256
|
+
assertV3EventAdmission(event);
|
|
18257
|
+
return {
|
|
18258
|
+
...event,
|
|
18259
|
+
type: "v3/opaque-released-event"
|
|
18260
|
+
};
|
|
18261
|
+
}
|
|
18262
|
+
if (event.type === "system/message") {
|
|
18263
|
+
const message = record(record(event.data, "system data")["message"], "system message");
|
|
18264
|
+
return {
|
|
18265
|
+
...event,
|
|
18266
|
+
type: "user/message",
|
|
18267
|
+
data: {
|
|
18268
|
+
...message,
|
|
18269
|
+
role: "user"
|
|
18270
|
+
}
|
|
18271
|
+
};
|
|
18272
|
+
}
|
|
18273
|
+
if (event.type !== "tool/result") return event;
|
|
18274
|
+
const data = record(event.data, "tool result");
|
|
18275
|
+
if (data["error"] === void 0) return event;
|
|
18276
|
+
if (record(data["error"], "tool error")["code"] !== "TOOL_NOT_STARTED") return event;
|
|
18277
|
+
const message = record(data["message"], "tool message");
|
|
18278
|
+
const callId = record(message["source"], "tool source")["callId"];
|
|
18279
|
+
const id = message["id"];
|
|
18280
|
+
if (!isRepairIdentity(id, callId)) return event;
|
|
18281
|
+
const prefix = "interrupted-tool-result-" + callId + "-";
|
|
18282
|
+
return {
|
|
18283
|
+
...event,
|
|
18284
|
+
data: {
|
|
18285
|
+
...data,
|
|
18286
|
+
message: {
|
|
18287
|
+
...message,
|
|
18288
|
+
id: `${prefix}${event.seq}`
|
|
18289
|
+
}
|
|
18290
|
+
}
|
|
18291
|
+
};
|
|
18292
|
+
}
|
|
18293
|
+
/** V3 framing with hard structural admission and recoverable canonical event validation. */
|
|
18294
|
+
/** V3 codec validates structural rows before recovery and logical envelopes after provenance decoding. */
|
|
18295
|
+
const releasedV3SessionFormatCodec = Object.freeze({
|
|
18296
|
+
version: 3,
|
|
18297
|
+
decodeHeader(value) {
|
|
18298
|
+
return {
|
|
18299
|
+
...releasedV2SessionFormatCodec.decodeHeader(v2PhysicalHeader(value)),
|
|
18300
|
+
version: 3
|
|
18301
|
+
};
|
|
18302
|
+
},
|
|
18303
|
+
createDecoder(value, recovery) {
|
|
18304
|
+
const decoder = releasedV2SessionFormatCodec.createDecoder(v2PhysicalHeader(value), recovery);
|
|
18305
|
+
let issue;
|
|
18306
|
+
let acceptedInheritedCut;
|
|
18307
|
+
return {
|
|
18308
|
+
header: {
|
|
18309
|
+
...decoder.header,
|
|
18310
|
+
version: 3
|
|
18311
|
+
},
|
|
18312
|
+
decodeRow(row, context) {
|
|
18313
|
+
assertV3RowAdmission(row);
|
|
18314
|
+
decoder.decodeRow(row, {
|
|
18315
|
+
emitRun: context.emitRun.bind(context),
|
|
18316
|
+
emitEvent(event) {
|
|
18317
|
+
assertV3EventAdmission(event);
|
|
18318
|
+
if (issue === void 0) try {
|
|
18319
|
+
assertV3Event(event);
|
|
18320
|
+
} catch (error) {
|
|
18321
|
+
const invalid = error;
|
|
18322
|
+
if (recovery === "strict") throw invalid;
|
|
18323
|
+
issue = invalid;
|
|
18324
|
+
}
|
|
18325
|
+
if (issue !== void 0) {
|
|
18326
|
+
if (event.type === "turn/end") throw issue;
|
|
18327
|
+
return;
|
|
18328
|
+
}
|
|
18329
|
+
if (event.type === "session/end-seed" && isSessionFormatJsonObject(event.data) && event.data["inherited"] === true) acceptedInheritedCut = event.seq;
|
|
18330
|
+
context.emitEvent(event);
|
|
18331
|
+
}
|
|
18332
|
+
});
|
|
18333
|
+
},
|
|
18334
|
+
finish(context) {
|
|
18335
|
+
if (issue === void 0) return decoder.finish(context);
|
|
18336
|
+
if (decoder.header.isSeeded && acceptedInheritedCut === void 0) throw new SessionFormatError("format v3 seeded Session lacks an accepted inherited end-seed marker");
|
|
18337
|
+
if (!decoder.header.isSeeded && acceptedInheritedCut !== void 0) throw new SessionFormatError("format v3 unseeded Session contains an inherited end-seed marker");
|
|
18338
|
+
return acceptedInheritedCut ?? 0;
|
|
18339
|
+
}
|
|
18340
|
+
};
|
|
18341
|
+
},
|
|
18342
|
+
encodeHeader(header, inheritedEventCount) {
|
|
18343
|
+
assertReleasedV3Header(header);
|
|
18344
|
+
return {
|
|
18345
|
+
...releasedV2SessionFormatCodec.encodeHeader({
|
|
18346
|
+
...header,
|
|
18347
|
+
version: 2
|
|
18348
|
+
}, inheritedEventCount),
|
|
18349
|
+
version: 3
|
|
18350
|
+
};
|
|
18351
|
+
},
|
|
18352
|
+
encodeEvent(event) {
|
|
18353
|
+
assertV3EventAdmission(event);
|
|
18354
|
+
assertV3Event(event);
|
|
18355
|
+
return releasedV2SessionFormatCodec.encodeEvent(event);
|
|
18356
|
+
}
|
|
18357
|
+
});
|
|
18358
|
+
/**
|
|
18359
|
+
* Validate owned V3 admission rules before a scanner or codec can discard a recoverable tail.
|
|
18360
|
+
* This checks only identified structural payloads; physical provenance still belongs to decoding.
|
|
18361
|
+
* @param row - parsed physical row, before envelope or compressed-range decoding.
|
|
18362
|
+
*/
|
|
18363
|
+
function assertV3RowAdmission(row) {
|
|
18364
|
+
assertV3StructuralRow(row);
|
|
18365
|
+
if (typeof row === "object" && row !== null && !Array.isArray(row)) assertV3EventAdmission(row);
|
|
18366
|
+
}
|
|
18367
|
+
function v2PhysicalHeader(value) {
|
|
18368
|
+
const header = snapshotSessionFormatJson(value, "format v3 physical header");
|
|
18369
|
+
if (!isSessionFormatJsonObject(header) || header["version"] !== 3) throw new SessionFormatError("expected format v3 physical Session header");
|
|
18370
|
+
return {
|
|
18371
|
+
...header,
|
|
18372
|
+
version: 2
|
|
18373
|
+
};
|
|
18374
|
+
}
|
|
18375
|
+
/** Explicit local-coordinate remapping; captured generations and owner-local counters remain opaque. */
|
|
18376
|
+
/**
|
|
18377
|
+
* Remap only audited same-artifact references, preserving IDs and embedded model input.
|
|
18378
|
+
* @param event - validated source event.
|
|
18379
|
+
* @param seq - output event position.
|
|
18380
|
+
* @param mapping - earlier source positions mapped to output positions.
|
|
18381
|
+
* @returns the event in target coordinates.
|
|
18382
|
+
*/
|
|
18383
|
+
function remapEvent(event, seq, mapping) {
|
|
18384
|
+
const one = (value) => {
|
|
18385
|
+
const source = sessionFormatCount(value, "source event reference");
|
|
18386
|
+
const target = mapping[source];
|
|
18387
|
+
if (source >= event.seq || target === void 0) throw new SessionFormatError("reference must name an earlier source event");
|
|
18388
|
+
return target;
|
|
18389
|
+
};
|
|
18390
|
+
const list = (value) => {
|
|
18391
|
+
if (!Array.isArray(value)) throw new SessionFormatError("sequence references must be an array");
|
|
18392
|
+
return value.map(one);
|
|
18393
|
+
};
|
|
18394
|
+
const range = (value) => {
|
|
18395
|
+
const source = record(value, "sequence range");
|
|
18396
|
+
return {
|
|
18397
|
+
...source,
|
|
18398
|
+
start: one(source["start"]),
|
|
18399
|
+
end: one(source["end"])
|
|
18400
|
+
};
|
|
18401
|
+
};
|
|
18402
|
+
let data = record(event.data, event.type);
|
|
18403
|
+
switch (event.type) {
|
|
18404
|
+
case "command/done":
|
|
18405
|
+
if (data["sourceEventSeq"] !== void 0) data = {
|
|
18406
|
+
...data,
|
|
18407
|
+
sourceEventSeq: one(data["sourceEventSeq"])
|
|
18408
|
+
};
|
|
18409
|
+
break;
|
|
18410
|
+
case "compaction/summary":
|
|
18411
|
+
case "compaction/prune":
|
|
18412
|
+
data = {
|
|
18413
|
+
...data,
|
|
18414
|
+
shadowedRange: range(data["shadowedRange"]),
|
|
18415
|
+
shadowedSeqs: list(data["shadowedSeqs"])
|
|
18416
|
+
};
|
|
18417
|
+
break;
|
|
18418
|
+
case "session/title":
|
|
18419
|
+
case "session/title-llm-request": data = {
|
|
18420
|
+
...data,
|
|
18421
|
+
messageSeqs: list(data["messageSeqs"])
|
|
18422
|
+
};
|
|
18423
|
+
}
|
|
18424
|
+
return {
|
|
18425
|
+
...event,
|
|
18426
|
+
seq,
|
|
18427
|
+
data,
|
|
18428
|
+
...event["sourceEventSeqs"] === void 0 ? {} : { sourceEventSeqs: list(event["sourceEventSeqs"]) },
|
|
18429
|
+
...event["surfaceOp"] === void 0 || event["surfaceOp"] === "append" ? {} : { surfaceOp: range(event["surfaceOp"]) }
|
|
18430
|
+
};
|
|
18431
|
+
}
|
|
18432
|
+
/** Streaming system-prompt promotion followed by canonical V3 envelope conversion. */
|
|
18433
|
+
/** Promote system prompts, remap audited references, and canonicalize envelopes and PTC vocabulary. */
|
|
18434
|
+
const sessionFormatV2ToV3 = defineSessionFormatMigration({
|
|
18435
|
+
name: "@deepseek-ai/dsh-session-format-v2-to-v3",
|
|
18436
|
+
fromVersion: 2,
|
|
18437
|
+
toVersion: 3,
|
|
18438
|
+
migrateHeader(header) {
|
|
18439
|
+
assertReleasedV2Header(header);
|
|
18440
|
+
return {
|
|
18441
|
+
...header,
|
|
18442
|
+
version: 3,
|
|
18443
|
+
...header.agentPreset === "code" ? { agentPreset: "ptc" } : {}
|
|
18444
|
+
};
|
|
18445
|
+
},
|
|
18446
|
+
createStage(input) {
|
|
18447
|
+
return new ReleasedV2ToV3Stage(input);
|
|
18448
|
+
},
|
|
18449
|
+
validateTargetHeader: assertReleasedV3Header
|
|
18450
|
+
});
|
|
18451
|
+
var ReleasedV2ToV3Stage = class {
|
|
18452
|
+
input;
|
|
18453
|
+
headerInheritedEventCount;
|
|
18454
|
+
mapping = [];
|
|
18455
|
+
originalIds = /* @__PURE__ */ new Set();
|
|
18456
|
+
generatedIds = /* @__PURE__ */ new Set();
|
|
18457
|
+
targetSeq = 0;
|
|
18458
|
+
sourceCut;
|
|
18459
|
+
targetCut;
|
|
18460
|
+
lastForeignDeliverySeq;
|
|
18461
|
+
step;
|
|
18462
|
+
head;
|
|
18463
|
+
prompt = "";
|
|
18464
|
+
constructor(input) {
|
|
18465
|
+
this.input = input;
|
|
18466
|
+
assertReleasedV2Header(input.sourceHeader);
|
|
18467
|
+
this.sourceCut = input.sourceHeader.isSeeded ? void 0 : 0;
|
|
18468
|
+
this.targetCut = input.sourceHeader.isSeeded ? void 0 : 0;
|
|
18469
|
+
if (!input.sourceHeader.isSeeded) this.headerInheritedEventCount = 0;
|
|
18470
|
+
}
|
|
18471
|
+
transformEvent(event, context) {
|
|
18472
|
+
if (event.seq !== this.mapping.length) throw new SessionFormatError("format v2 source events must be dense");
|
|
18473
|
+
assertEvent(event, 2);
|
|
18474
|
+
this.observeMessageIds(event);
|
|
18475
|
+
let source = event;
|
|
18476
|
+
const data = record(event.data, event.type);
|
|
18477
|
+
if (event.type === "request/header") {
|
|
18478
|
+
const { system, ...header } = record(data["header"], "request header");
|
|
18479
|
+
const prompt = typeof system === "string" ? system : "";
|
|
18480
|
+
if (prompt !== this.prompt) this.emitSystem(prompt, event, context);
|
|
18481
|
+
source = {
|
|
18482
|
+
...event,
|
|
18483
|
+
data: {
|
|
18484
|
+
...data,
|
|
18485
|
+
header
|
|
18486
|
+
}
|
|
18487
|
+
};
|
|
18488
|
+
}
|
|
18489
|
+
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");
|
|
18490
|
+
if (event.type === "session/end-seed" && data["inherited"] === true) {
|
|
18491
|
+
if (!this.input.sourceHeader.isSeeded) throw new SessionFormatError("format v2 unseeded Session contains an inherited end-seed marker");
|
|
18492
|
+
this.sourceCut = event.seq;
|
|
18493
|
+
this.targetCut = this.targetSeq;
|
|
18494
|
+
}
|
|
18495
|
+
if (event.type === "session-log-deepseek/delivery-accepted") {
|
|
18496
|
+
if (data["sessionFormatVersion"] === 3) throw new SessionFormatError("format v2 delivery marker claims target format v3");
|
|
18497
|
+
if (data["sessionFormatVersion"] === 2 && data["sessionId"] !== this.input.sourceHeader.id) this.lastForeignDeliverySeq = event.seq;
|
|
18498
|
+
}
|
|
18499
|
+
const target = remapEvent(source, this.targetSeq, this.mapping);
|
|
18500
|
+
this.mapping.push(this.targetSeq++);
|
|
18501
|
+
context.emitEvent(canonicalizeTransformedEvent(renamePtcEvent(target)));
|
|
18502
|
+
if (event.type === "step/start") {
|
|
18503
|
+
this.step = {
|
|
18504
|
+
turn: data["turn"],
|
|
18505
|
+
step: data["step"]
|
|
18506
|
+
};
|
|
18507
|
+
if (this.head === void 0) this.emitSystem("", event, context);
|
|
18508
|
+
} else if (event.type === "step/end" || event.type === "turn/end") this.step = void 0;
|
|
18509
|
+
}
|
|
18510
|
+
transformRun(run, context) {
|
|
18511
|
+
for (const event of run.expand()) this.transformEvent(event, context);
|
|
18512
|
+
}
|
|
18513
|
+
finish(_context) {
|
|
18514
|
+
const cut = sessionFormatCount(this.sourceCut, "format v2 inherited end-seed marker");
|
|
18515
|
+
if (this.input.sourceInheritedEventCount !== void 0 && this.input.sourceInheritedEventCount !== cut) throw new SessionFormatError("format v2 inherited end-seed marker disagrees with its source cut");
|
|
18516
|
+
if (this.lastForeignDeliverySeq !== void 0 && (this.input.sourceHeader.parentSession === void 0 || this.lastForeignDeliverySeq >= cut)) throw new SessionFormatError("current-generation delivery marker names the wrong Session");
|
|
18517
|
+
return sessionFormatCount(this.targetCut, "format v3 inherited event count");
|
|
18518
|
+
}
|
|
18519
|
+
observeMessageIds(event) {
|
|
18520
|
+
const data = record(event.data, event.type);
|
|
18521
|
+
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"] : [];
|
|
18522
|
+
for (const message of messages) {
|
|
18523
|
+
const id = message["id"];
|
|
18524
|
+
if (this.generatedIds.has(id)) throw new SessionFormatUnsupportedMigrationError("source message id collides with a generated system message id");
|
|
18525
|
+
this.originalIds.add(id);
|
|
18526
|
+
}
|
|
18527
|
+
}
|
|
18528
|
+
emitSystem(prompt, anchor, context) {
|
|
18529
|
+
if (this.step === void 0) throw new SessionFormatUnsupportedMigrationError("format v2 changed request prompt outside an open step cannot retain source chronology");
|
|
18530
|
+
const identity = JSON.stringify([
|
|
18531
|
+
"session-format-v2-to-v3",
|
|
18532
|
+
this.input.sourceHeader.id,
|
|
18533
|
+
anchor.seq,
|
|
18534
|
+
anchor.type
|
|
18535
|
+
]);
|
|
18536
|
+
const id = "v2-to-v3-system-" + createHash("sha256").update(identity).digest("hex");
|
|
18537
|
+
if (this.originalIds.has(id) || this.generatedIds.has(id)) throw new SessionFormatUnsupportedMigrationError("generated system message id collides with an existing message id");
|
|
18538
|
+
this.generatedIds.add(id);
|
|
18539
|
+
const seq = this.targetSeq++;
|
|
18540
|
+
context.emitEvent(canonicalizeTransformedEvent({
|
|
18541
|
+
type: "system/message",
|
|
18542
|
+
seq,
|
|
18543
|
+
time: anchor.time,
|
|
18544
|
+
data: {
|
|
18545
|
+
...this.step,
|
|
18546
|
+
message: {
|
|
18547
|
+
id,
|
|
18548
|
+
role: "system",
|
|
18549
|
+
source: {
|
|
18550
|
+
kind: "plugin",
|
|
18551
|
+
plugin: "@deepseek-ai/dsh-system-prompt"
|
|
18552
|
+
},
|
|
18553
|
+
content: prompt === "" ? [] : [{
|
|
18554
|
+
type: "text",
|
|
18555
|
+
text: prompt
|
|
18556
|
+
}]
|
|
18557
|
+
}
|
|
18558
|
+
},
|
|
18559
|
+
...this.head === void 0 ? { surfaceOp: "append" } : {
|
|
18560
|
+
surfaceOp: {
|
|
18561
|
+
op: "replace",
|
|
18562
|
+
start: this.head,
|
|
18563
|
+
end: this.head
|
|
18564
|
+
},
|
|
18565
|
+
sourceEventSeqs: [this.head]
|
|
18566
|
+
}
|
|
18567
|
+
}));
|
|
18568
|
+
this.head = seq;
|
|
18569
|
+
this.prompt = prompt;
|
|
18570
|
+
}
|
|
18571
|
+
};
|
|
18572
|
+
/** Source admission precedes renaming, so these payloads have exact audited fields. */
|
|
18573
|
+
function renamePtcEvent(event) {
|
|
18574
|
+
switch (event.type) {
|
|
18575
|
+
case "agent-preset/selected": return event.data["agentPreset"] === "code" ? {
|
|
18576
|
+
...event,
|
|
18577
|
+
data: {
|
|
18578
|
+
...event.data,
|
|
18579
|
+
agentPreset: "ptc"
|
|
18580
|
+
}
|
|
18581
|
+
} : event;
|
|
18582
|
+
case "tool/code-dispatch-start": return {
|
|
18583
|
+
...event,
|
|
18584
|
+
type: "tool/ptc-dispatch-start"
|
|
18585
|
+
};
|
|
18586
|
+
case "tool/code-dispatch": return {
|
|
18587
|
+
...event,
|
|
18588
|
+
type: "tool/ptc-dispatch"
|
|
18589
|
+
};
|
|
18590
|
+
case "user/message": {
|
|
18591
|
+
const data = renameMessageSource(event.data);
|
|
18592
|
+
return data === event.data ? event : {
|
|
18593
|
+
...event,
|
|
18594
|
+
data
|
|
18595
|
+
};
|
|
18596
|
+
}
|
|
18597
|
+
case "agent/inbox/spliced":
|
|
18598
|
+
case "session/title-llm-request": {
|
|
18599
|
+
const data = event.data;
|
|
18600
|
+
const key = event.type === "agent/inbox/spliced" ? "inserted" : "messages";
|
|
18601
|
+
const messages = data[key];
|
|
18602
|
+
const renamed = messages.map(renameMessageSource);
|
|
18603
|
+
return renamed.every((message, index) => message === messages[index]) ? event : {
|
|
18604
|
+
...event,
|
|
18605
|
+
data: {
|
|
18606
|
+
...data,
|
|
18607
|
+
[key]: renamed
|
|
18608
|
+
}
|
|
18609
|
+
};
|
|
18610
|
+
}
|
|
18611
|
+
default: return event;
|
|
18612
|
+
}
|
|
18613
|
+
}
|
|
18614
|
+
function renameMessageSource(message) {
|
|
18615
|
+
const source = message["source"];
|
|
18616
|
+
if (source["kind"] !== "plugin" || source["plugin"] !== "tools-code-mode") return message;
|
|
18617
|
+
return {
|
|
18618
|
+
...message,
|
|
18619
|
+
source: {
|
|
18620
|
+
...source,
|
|
18621
|
+
plugin: "tools-ptc"
|
|
18622
|
+
}
|
|
18623
|
+
};
|
|
18624
|
+
}
|
|
18625
|
+
//#endregion
|
|
17740
18626
|
//#region ../../vendor/deepseek-harness/packages/session/session-format-catalog/lib/index.js
|
|
17741
18627
|
/** Current installed Session validation used after vocabulary-aware format restoration. */
|
|
17742
18628
|
/**
|
|
@@ -17763,30 +18649,56 @@ function validateInstalledCurrentSessionArtifact(artifact) {
|
|
|
17763
18649
|
*/
|
|
17764
18650
|
/** Physical codec dispatch and complete adjacent chain, independent of mounted plugins. */
|
|
17765
18651
|
const sessionFormatCatalog = createSessionFormatCatalog({
|
|
17766
|
-
currentVersion:
|
|
18652
|
+
currentVersion: 3,
|
|
17767
18653
|
codecs: [
|
|
17768
18654
|
releasedV0SessionFormatCodec,
|
|
17769
18655
|
releasedV1SessionFormatCodec,
|
|
17770
|
-
releasedV2SessionFormatCodec
|
|
18656
|
+
releasedV2SessionFormatCodec,
|
|
18657
|
+
releasedV3SessionFormatCodec
|
|
18658
|
+
],
|
|
18659
|
+
currentEncoder: releasedV3SessionFormatCodec,
|
|
18660
|
+
migrations: [
|
|
18661
|
+
sessionFormatV0ToV1,
|
|
18662
|
+
sessionFormatV1ToV2,
|
|
18663
|
+
sessionFormatV2ToV3
|
|
17771
18664
|
],
|
|
17772
|
-
currentEncoder: releasedV2SessionFormatCodec,
|
|
17773
|
-
migrations: [sessionFormatV0ToV1, sessionFormatV1ToV2],
|
|
17774
18665
|
restoreCurrent(artifact) {
|
|
17775
|
-
const restored =
|
|
18666
|
+
const restored = restoreReleasedV3Artifact(artifact, KNOWN_SESSION_EVENT_TYPES);
|
|
17776
18667
|
validateInstalledCurrentSessionArtifact(restored);
|
|
17777
18668
|
return restored;
|
|
17778
18669
|
},
|
|
17779
18670
|
restoreTransformedCurrent(artifact) {
|
|
17780
|
-
return
|
|
18671
|
+
return restoreReleasedV3Artifact(artifact, KNOWN_SESSION_EVENT_TYPES);
|
|
17781
18672
|
},
|
|
17782
18673
|
restoreCurrentHeader(header) {
|
|
17783
|
-
|
|
18674
|
+
assertReleasedV3Header(header);
|
|
17784
18675
|
validateInstalledCurrentSessionHeader(header);
|
|
17785
18676
|
return header;
|
|
17786
18677
|
}
|
|
17787
18678
|
});
|
|
17788
18679
|
//#endregion
|
|
17789
18680
|
//#region ../session-rdb/src/log.ts
|
|
18681
|
+
/**
|
|
18682
|
+
* 把桥接行列里的 replace surfaceOp 归一到当前字段名。
|
|
18683
|
+
*
|
|
18684
|
+
* v2 时代落库的 JSON 用 `start`/`end`,当前格式用 `startSeq`/`endSeq`;
|
|
18685
|
+
* 混合世代回退路径直接采用存储行,必须在此归一,否则上游 v3 surface 校验
|
|
18686
|
+
* 以「invalid replace surfaceOp」拒绝整个会话。非 replace 形状原样返回,
|
|
18687
|
+
* 由读取视图修复决定降级。
|
|
18688
|
+
*/
|
|
18689
|
+
function normalizeSurfaceOp(value) {
|
|
18690
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
18691
|
+
const record = value;
|
|
18692
|
+
if (record["op"] !== "replace") return value;
|
|
18693
|
+
const startSeq = record["startSeq"] ?? record["start"];
|
|
18694
|
+
const endSeq = record["endSeq"] ?? record["end"];
|
|
18695
|
+
if (typeof startSeq !== "number" || typeof endSeq !== "number") return value;
|
|
18696
|
+
return {
|
|
18697
|
+
op: "replace",
|
|
18698
|
+
startSeq: SessionSeq$1(startSeq),
|
|
18699
|
+
endSeq: SessionSeq$1(endSeq)
|
|
18700
|
+
};
|
|
18701
|
+
}
|
|
17790
18702
|
function rowToMeta(row) {
|
|
17791
18703
|
if (!Number.isSafeInteger(row.fCreatedAt) || row.fCreatedAt < 0) throw new Error("stored session createdAt must be a non-negative safe integer");
|
|
17792
18704
|
return {
|
|
@@ -17830,7 +18742,7 @@ function sessionConflictRow(storage) {
|
|
|
17830
18742
|
};
|
|
17831
18743
|
}
|
|
17832
18744
|
function rowToEvent(row) {
|
|
17833
|
-
const surfaceOp = row.fSurfaceOp !== null ? JSON.parse(row.fSurfaceOp) : void 0;
|
|
18745
|
+
const surfaceOp = row.fSurfaceOp !== null ? normalizeSurfaceOp(JSON.parse(row.fSurfaceOp)) : void 0;
|
|
17834
18746
|
const record = JSON.parse(row.fData);
|
|
17835
18747
|
if (typeof record === "object" && record !== null && !Array.isArray(record) && typeof record["type"] === "string" && typeof record["seq"] === "number" && typeof record["time"] === "number" && "data" in record) return {
|
|
17836
18748
|
...record,
|
|
@@ -17847,6 +18759,7 @@ function rowToEvent(row) {
|
|
|
17847
18759
|
};
|
|
17848
18760
|
}
|
|
17849
18761
|
const SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
18762
|
+
"system/message",
|
|
17850
18763
|
"user/message",
|
|
17851
18764
|
"assistant/message",
|
|
17852
18765
|
"tool/result"
|
|
@@ -17869,7 +18782,7 @@ function recomputeReplaceProvenance(events) {
|
|
|
17869
18782
|
const raw = events[i];
|
|
17870
18783
|
const op = raw.surfaceOp;
|
|
17871
18784
|
if (typeof op !== "object" || op === null || op.op !== "replace") continue;
|
|
17872
|
-
const {
|
|
18785
|
+
const { startSeq, endSeq } = op;
|
|
17873
18786
|
const metering = i > 0 ? events[i - 1] : void 0;
|
|
17874
18787
|
const meteringData = metering !== void 0 && METERING_EVENT_TYPES.has(metering.type) ? metering.data : void 0;
|
|
17875
18788
|
if (meteringData?.shadowedSeqs !== void 0) {
|
|
@@ -17877,7 +18790,7 @@ function recomputeReplaceProvenance(events) {
|
|
|
17877
18790
|
continue;
|
|
17878
18791
|
}
|
|
17879
18792
|
const refs = [];
|
|
17880
|
-
for (const candidate of events) if (candidate.seq >=
|
|
18793
|
+
for (const candidate of events) if (candidate.seq >= startSeq && candidate.seq <= endSeq && SURFACE_EVENT_TYPES.has(candidate.type)) refs.push(candidate.seq);
|
|
17881
18794
|
raw.sourceEventSeqs = refs;
|
|
17882
18795
|
}
|
|
17883
18796
|
}
|
|
@@ -17929,7 +18842,9 @@ function findSurfaceRepairs(events) {
|
|
|
17929
18842
|
const degradeToAppend = /* @__PURE__ */ new Set();
|
|
17930
18843
|
const addAppendMarker = /* @__PURE__ */ new Set();
|
|
17931
18844
|
const clearSurfaceOp = /* @__PURE__ */ new Set();
|
|
17932
|
-
|
|
18845
|
+
const clampEnd = /* @__PURE__ */ new Map();
|
|
18846
|
+
for (let index = 0; index < events.length; index++) {
|
|
18847
|
+
const event = events[index];
|
|
17933
18848
|
const op = event.surfaceOp;
|
|
17934
18849
|
if (op === void 0) {
|
|
17935
18850
|
if (SURFACE_EVENT_TYPES.has(event.type)) {
|
|
@@ -17948,12 +18863,18 @@ function findSurfaceRepairs(events) {
|
|
|
17948
18863
|
continue;
|
|
17949
18864
|
}
|
|
17950
18865
|
const replace = typeof op === "object" && op !== null && !Array.isArray(op) ? op : void 0;
|
|
17951
|
-
const start = replace?.["
|
|
17952
|
-
const end = replace?.["
|
|
18866
|
+
const start = replace?.["startSeq"];
|
|
18867
|
+
const end = replace?.["endSeq"];
|
|
17953
18868
|
const shapeOk = replace !== void 0 && replace["op"] === "replace" && isEventSeqLike(start) && isEventSeqLike(end);
|
|
17954
18869
|
const startIdx = shapeOk ? nodes.indexOf(start) : -1;
|
|
17955
|
-
|
|
18870
|
+
let endIdx = shapeOk ? nodes.indexOf(end) : -1;
|
|
18871
|
+
let clamped;
|
|
18872
|
+
if (shapeOk && startIdx !== -1 && endIdx === -1) {
|
|
18873
|
+
clamped = clampReplaceEnd(events, index, nodes, startIdx);
|
|
18874
|
+
if (clamped !== void 0) endIdx = nodes.indexOf(clamped);
|
|
18875
|
+
}
|
|
17956
18876
|
const rangeOk = shapeOk && startIdx !== -1 && endIdx !== -1 && startIdx <= endIdx;
|
|
18877
|
+
const provenanceOk = event.type !== "assistant/message";
|
|
17957
18878
|
let rewriteOk = true;
|
|
17958
18879
|
if (rangeOk && event.type === "tool/result") {
|
|
17959
18880
|
const shadowed = nodes.slice(startIdx, endIdx + 1);
|
|
@@ -17963,30 +18884,202 @@ function findSurfaceRepairs(events) {
|
|
|
17963
18884
|
rewriteOk = original?.type === "tool/result" && toolResultRewriteContentOnly(original, event);
|
|
17964
18885
|
}
|
|
17965
18886
|
}
|
|
17966
|
-
if (!rangeOk || !rewriteOk) {
|
|
18887
|
+
if (!rangeOk || !rewriteOk || !provenanceOk) {
|
|
17967
18888
|
degradeToAppend.add(event.seq);
|
|
17968
18889
|
nodes.push(event.seq);
|
|
17969
18890
|
continue;
|
|
17970
18891
|
}
|
|
18892
|
+
if (clamped !== void 0) clampEnd.set(event.seq, clamped);
|
|
17971
18893
|
nodes.splice(startIdx, endIdx - startIdx + 1, event.seq);
|
|
17972
18894
|
}
|
|
17973
18895
|
return {
|
|
17974
18896
|
degradeToAppend,
|
|
17975
18897
|
addAppendMarker,
|
|
17976
|
-
clearSurfaceOp
|
|
18898
|
+
clearSurfaceOp,
|
|
18899
|
+
clampEnd
|
|
17977
18900
|
};
|
|
17978
18901
|
}
|
|
18902
|
+
/**
|
|
18903
|
+
* 夹取一个 end 落在旧坐标空间的 replace 的结尾。
|
|
18904
|
+
*
|
|
18905
|
+
* 仅当 replace 紧邻一个 metering 事件且其 `shadowedSeqs` 是权威数量时成立;
|
|
18906
|
+
* 夹取点是从 `start` 起的第 `shadowedSeqs.length` 个当前 surface 节点(不足
|
|
18907
|
+
* 则取当前 surface 末尾)。无法夹取(start 也不在当前 surface)时返回
|
|
18908
|
+
* `undefined`,由调用方降级为 append。
|
|
18909
|
+
*/
|
|
18910
|
+
function clampReplaceEnd(events, index, nodes, startIdx) {
|
|
18911
|
+
const metering = index > 0 ? events[index - 1] : void 0;
|
|
18912
|
+
if (metering === void 0 || !METERING_EVENT_TYPES.has(metering.type)) return void 0;
|
|
18913
|
+
const shadowedSeqs = metering.data.shadowedSeqs;
|
|
18914
|
+
if (!Array.isArray(shadowedSeqs) || shadowedSeqs.length === 0) return void 0;
|
|
18915
|
+
return nodes[Math.min(startIdx + shadowedSeqs.length - 1, nodes.length - 1)];
|
|
18916
|
+
}
|
|
17979
18917
|
function repairSurfaceOps(events) {
|
|
17980
18918
|
const repairs = findSurfaceRepairs(events);
|
|
17981
|
-
if (repairs.degradeToAppend.size === 0 && repairs.addAppendMarker.size === 0 && repairs.clearSurfaceOp.size === 0) return;
|
|
18919
|
+
if (repairs.degradeToAppend.size === 0 && repairs.addAppendMarker.size === 0 && repairs.clearSurfaceOp.size === 0 && repairs.clampEnd.size === 0) return;
|
|
17982
18920
|
for (const event of events) {
|
|
17983
18921
|
const raw = event;
|
|
18922
|
+
const clamped = repairs.clampEnd.get(event.seq);
|
|
17984
18923
|
if (repairs.degradeToAppend.has(event.seq)) raw.surfaceOp = "append";
|
|
17985
|
-
else if (
|
|
18924
|
+
else if (clamped !== void 0) {
|
|
18925
|
+
const op = raw.surfaceOp;
|
|
18926
|
+
raw.surfaceOp = {
|
|
18927
|
+
op: "replace",
|
|
18928
|
+
startSeq: SessionSeq$1(op.startSeq),
|
|
18929
|
+
endSeq: SessionSeq$1(clamped)
|
|
18930
|
+
};
|
|
18931
|
+
} else if (repairs.addAppendMarker.has(event.seq)) raw.surfaceOp = "append";
|
|
17986
18932
|
else if (repairs.clearSurfaceOp.has(event.seq)) delete raw.surfaceOp;
|
|
17987
18933
|
}
|
|
17988
18934
|
}
|
|
17989
18935
|
/**
|
|
18936
|
+
* 读取时补全 assistant 结算字段:旧写入器不落库 `stream`(v2 才把流式记录
|
|
18937
|
+
* 嵌入消息),读回时缺失即补空数组——上游 seed 校验要求 turn/step/stream
|
|
18938
|
+
* 三者齐备,缺失会让整个会话加载失败。
|
|
18939
|
+
*/
|
|
18940
|
+
function repairAssistantSettlement(events) {
|
|
18941
|
+
for (const event of events) {
|
|
18942
|
+
if (event.type !== "assistant/message" && event.type !== "assistant/attempt") continue;
|
|
18943
|
+
const data = event.data;
|
|
18944
|
+
if (!Array.isArray(data["stream"])) data["stream"] = [];
|
|
18945
|
+
}
|
|
18946
|
+
}
|
|
18947
|
+
/**
|
|
18948
|
+
* 读取时把旧格式 `request/header` 归一为当前格式:v3 起 system prompt 由
|
|
18949
|
+
* surface 上的 `system/message` 承载,header 必须省略 `system`,空 `tools` /
|
|
18950
|
+
* `adapterDefaults` 也必须省略(上游 v3 事件校验 fail loud)。混合世代回退
|
|
18951
|
+
* 视图直接采用存储行,不归一会让整个会话加载失败。system prompt 因此不再
|
|
18952
|
+
* 进入模型请求;会话下次运行由 v3 的 system/message 机制重建。
|
|
18953
|
+
*/
|
|
18954
|
+
function repairRequestHeaders(events) {
|
|
18955
|
+
for (const event of events) {
|
|
18956
|
+
if (event.type !== "request/header") continue;
|
|
18957
|
+
const header = event.data["header"];
|
|
18958
|
+
if (typeof header !== "object" || header === null || Array.isArray(header)) continue;
|
|
18959
|
+
const record = header;
|
|
18960
|
+
delete record["system"];
|
|
18961
|
+
if (Array.isArray(record["tools"]) && record["tools"].length === 0) delete record["tools"];
|
|
18962
|
+
const defaults = record["adapterDefaults"];
|
|
18963
|
+
if (typeof defaults === "object" && defaults !== null && !Array.isArray(defaults) && Object.keys(defaults).length === 0) delete record["adapterDefaults"];
|
|
18964
|
+
}
|
|
18965
|
+
}
|
|
18966
|
+
/**
|
|
18967
|
+
* 把紧邻 replace 的 metering 事件的 `shadowedRange` / `shadowedSeqs` 对齐到
|
|
18968
|
+
* replace 最终的稠密 range。
|
|
18969
|
+
*
|
|
18970
|
+
* 旧写入器重编号事件后,metering 与 replace 一起落在旧坐标空间:二者数值
|
|
18971
|
+
* 相等但与当前 surface 无关。夹取(或 range 本身已落在稠密空间而 metering
|
|
18972
|
+
* 仍是旧值)后二者不再相等,而上游 token-meter 契约要求紧邻的 metering
|
|
18973
|
+
* range 与 replace range 完全一致(否则报 no adjacent shadow price)。range
|
|
18974
|
+
* 已一致时不改写——当前写入器保证一致,shadowedSeqs 的额外并发节点因此
|
|
18975
|
+
* 原样保留。
|
|
18976
|
+
*/
|
|
18977
|
+
function syncMeteringRanges(events) {
|
|
18978
|
+
for (let i = 1; i < events.length; i++) {
|
|
18979
|
+
const metering = events[i - 1];
|
|
18980
|
+
if (!METERING_EVENT_TYPES.has(metering.type)) continue;
|
|
18981
|
+
const op = events[i].surfaceOp;
|
|
18982
|
+
if (typeof op !== "object" || op === null || op.op !== "replace") continue;
|
|
18983
|
+
const { startSeq, endSeq } = op;
|
|
18984
|
+
const data = metering.data;
|
|
18985
|
+
if (data.shadowedRange?.start === startSeq && data.shadowedRange.end === endSeq) continue;
|
|
18986
|
+
data.shadowedRange = {
|
|
18987
|
+
start: startSeq,
|
|
18988
|
+
end: endSeq
|
|
18989
|
+
};
|
|
18990
|
+
data.shadowedSeqs = events.filter((candidate) => candidate.seq >= startSeq && candidate.seq <= endSeq && SURFACE_EVENT_TYPES.has(candidate.type)).map((candidate) => candidate.seq);
|
|
18991
|
+
}
|
|
18992
|
+
}
|
|
18993
|
+
const PTC_EVENT_RENAMES = {
|
|
18994
|
+
"tool/code-dispatch-start": "tool/ptc-dispatch-start",
|
|
18995
|
+
"tool/code-dispatch": "tool/ptc-dispatch"
|
|
18996
|
+
};
|
|
18997
|
+
/** 把消息的 `tools-code-mode` 插件来源改写为 `tools-ptc`(非该来源原样返回)。 */
|
|
18998
|
+
function renamePtcMessageSource(message) {
|
|
18999
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) return message;
|
|
19000
|
+
const record = message;
|
|
19001
|
+
const source = record["source"];
|
|
19002
|
+
if (typeof source !== "object" || source === null || Array.isArray(source)) return message;
|
|
19003
|
+
const sourceRecord = source;
|
|
19004
|
+
if (sourceRecord["kind"] !== "plugin" || sourceRecord["plugin"] !== "tools-code-mode") return message;
|
|
19005
|
+
return {
|
|
19006
|
+
...record,
|
|
19007
|
+
source: {
|
|
19008
|
+
...sourceRecord,
|
|
19009
|
+
plugin: "tools-ptc"
|
|
19010
|
+
}
|
|
19011
|
+
};
|
|
19012
|
+
}
|
|
19013
|
+
/**
|
|
19014
|
+
* 读取时把 v2 时代的 PTC 词汇归一为当前词汇。
|
|
19015
|
+
*
|
|
19016
|
+
* 上游 v2→v3 迁移把 `tool/code-dispatch(-start)` 改名为
|
|
19017
|
+
* `tool/ptc-dispatch(-start)`、把 `tools-code-mode` 插件来源改名为 `tools-ptc`、
|
|
19018
|
+
* 把 agent preset 值 `code` 改名为 `ptc`。混合世代回退视图直接采用存储行,
|
|
19019
|
+
* 不重命名会被上游 v3 校验以「unknown event type」拒绝整个会话。词汇改写
|
|
19020
|
+
* 与上游迁移链的 renamePtcEvent 对齐。
|
|
19021
|
+
*/
|
|
19022
|
+
function renameLegacyPtcEvents(events) {
|
|
19023
|
+
for (let index = 0; index < events.length; index++) {
|
|
19024
|
+
const event = events[index];
|
|
19025
|
+
const renamedType = PTC_EVENT_RENAMES[event.type];
|
|
19026
|
+
if (renamedType !== void 0) {
|
|
19027
|
+
events[index] = {
|
|
19028
|
+
...event,
|
|
19029
|
+
type: renamedType
|
|
19030
|
+
};
|
|
19031
|
+
continue;
|
|
19032
|
+
}
|
|
19033
|
+
if (event.type === "agent-preset/selected") {
|
|
19034
|
+
const data = event.data;
|
|
19035
|
+
if (data["agentPreset"] === "code") events[index] = {
|
|
19036
|
+
...event,
|
|
19037
|
+
data: {
|
|
19038
|
+
...data,
|
|
19039
|
+
agentPreset: "ptc"
|
|
19040
|
+
}
|
|
19041
|
+
};
|
|
19042
|
+
continue;
|
|
19043
|
+
}
|
|
19044
|
+
if (event.type === "user/message") {
|
|
19045
|
+
const renamed = renamePtcMessageSource(event.data);
|
|
19046
|
+
if (renamed !== event.data) events[index] = {
|
|
19047
|
+
...event,
|
|
19048
|
+
data: renamed
|
|
19049
|
+
};
|
|
19050
|
+
continue;
|
|
19051
|
+
}
|
|
19052
|
+
if (event.type === "agent/inbox/spliced" || event.type === "session/title-llm-request") {
|
|
19053
|
+
const data = event.data;
|
|
19054
|
+
const key = event.type === "agent/inbox/spliced" ? "inserted" : "messages";
|
|
19055
|
+
const list = data[key];
|
|
19056
|
+
if (!Array.isArray(list)) continue;
|
|
19057
|
+
const renamed = list.map(renamePtcMessageSource);
|
|
19058
|
+
if (renamed.some((message, position) => message !== list[position])) events[index] = {
|
|
19059
|
+
...event,
|
|
19060
|
+
data: {
|
|
19061
|
+
...data,
|
|
19062
|
+
[key]: renamed
|
|
19063
|
+
}
|
|
19064
|
+
};
|
|
19065
|
+
}
|
|
19066
|
+
}
|
|
19067
|
+
}
|
|
19068
|
+
/**
|
|
19069
|
+
* 读取视图修复总入口:形状补全(结算字段 / request header)→ PTC 词汇归一
|
|
19070
|
+
* → surface 语义修复 → metering 对齐 → provenance 重算 → 孤儿 inbox splice
|
|
19071
|
+
* 改写。全部只作用于内存视图,不落库。
|
|
19072
|
+
*/
|
|
19073
|
+
function repairReadView(events) {
|
|
19074
|
+
repairAssistantSettlement(events);
|
|
19075
|
+
repairRequestHeaders(events);
|
|
19076
|
+
renameLegacyPtcEvents(events);
|
|
19077
|
+
repairSurfaceOps(events);
|
|
19078
|
+
syncMeteringRanges(events);
|
|
19079
|
+
recomputeReplaceProvenance(events);
|
|
19080
|
+
repairOrphanInboxSplices(events);
|
|
19081
|
+
}
|
|
19082
|
+
/**
|
|
17990
19083
|
* 定位无法从空状态增量重放的 agent/inbox/spliced 事件(孤儿操作)。
|
|
17991
19084
|
*
|
|
17992
19085
|
* 上游 Inbox 每次构造都从会话起点重放全部 inbox splice,失败即拒绝整个
|
|
@@ -23424,35 +24517,43 @@ function registerSessionImport(ctx, persistence) {
|
|
|
23424
24517
|
}
|
|
23425
24518
|
//#endregion
|
|
23426
24519
|
//#region ../session-rdb/src/legacy.ts
|
|
23427
|
-
/** 重建 v0/v1 物理 header 记录(RDB 行 → 物理 JSON 对象)。 */
|
|
24520
|
+
/** 重建 v0/v1/v2 物理 header 记录(RDB 行 → 物理 JSON 对象)。 */
|
|
23428
24521
|
function physicalHeader(row) {
|
|
23429
|
-
|
|
24522
|
+
const common = {
|
|
23430
24523
|
type: "session",
|
|
23431
24524
|
version: row.fVersion,
|
|
23432
24525
|
id: row.fSessionId,
|
|
23433
24526
|
createdAt: row.fCreatedAt,
|
|
23434
24527
|
...row.fCwd !== null ? { cwd: row.fCwd } : {},
|
|
23435
24528
|
...row.fParentSession !== null ? { parentSession: row.fParentSession } : {},
|
|
23436
|
-
...row.fSeedLength !== null ? { seedLength: row.fSeedLength } : {},
|
|
23437
24529
|
...row.fOrigin !== null ? { origin: row.fOrigin } : {},
|
|
23438
24530
|
delegationDepth: row.fDelegationDepth ?? 0
|
|
23439
24531
|
};
|
|
24532
|
+
return row.fVersion < 2 ? {
|
|
24533
|
+
...common,
|
|
24534
|
+
...row.fSeedLength !== null ? { seedLength: row.fSeedLength } : {}
|
|
24535
|
+
} : {
|
|
24536
|
+
...common,
|
|
24537
|
+
isSeeded: row.fSeedLength !== null
|
|
24538
|
+
};
|
|
23440
24539
|
}
|
|
23441
|
-
/** 重建一条 v0/v1 物理事件记录(桥接行 + 事件行 → 物理 JSON 对象)。 */
|
|
24540
|
+
/** 重建一条 v0/v1/v2 物理事件记录(桥接行 + 事件行 → 物理 JSON 对象)。 */
|
|
23442
24541
|
function physicalEvent(row) {
|
|
24542
|
+
const stored = JSON.parse(row.fData);
|
|
24543
|
+
const full = typeof stored === "object" && stored !== null && !Array.isArray(stored) && typeof stored["type"] === "string" && "data" in stored;
|
|
23443
24544
|
return {
|
|
23444
24545
|
type: row.fType,
|
|
23445
24546
|
seq: row.fSequence,
|
|
23446
24547
|
time: row.fCreatedAt,
|
|
23447
|
-
data:
|
|
24548
|
+
data: full ? stored["data"] : stored,
|
|
23448
24549
|
...row.fSurfaceOp !== null ? { surfaceOp: JSON.parse(row.fSurfaceOp) } : {}
|
|
23449
24550
|
};
|
|
23450
24551
|
}
|
|
23451
24552
|
/**
|
|
23452
|
-
*
|
|
23453
|
-
* @param row - 会话行(f_version <
|
|
24553
|
+
* 把非当前格式(v0/v1/v2)行转换为当前逻辑事件与 header。
|
|
24554
|
+
* @param row - 会话行(f_version < SESSION_FORMAT_VERSION)。
|
|
23454
24555
|
* @param eventRows - 已扫描保留的桥接行(稠密 seq,torn tail 已剔除)。
|
|
23455
|
-
* @returns
|
|
24556
|
+
* @returns 当前逻辑 header、继承前缀长度与事件。
|
|
23456
24557
|
* @throws 迁移链拒绝(格式损坏 / 无法迁移)时原样抛出。
|
|
23457
24558
|
*/
|
|
23458
24559
|
function convertLegacyRows(row, eventRows) {
|
|
@@ -23468,9 +24569,40 @@ function convertLegacyRows(row, eventRows) {
|
|
|
23468
24569
|
events: artifact.events
|
|
23469
24570
|
};
|
|
23470
24571
|
}
|
|
23471
|
-
/**
|
|
24572
|
+
/** 会话行是否携带非当前格式(f_version < SESSION_FORMAT_VERSION)数据。 */
|
|
23472
24573
|
function isLegacyVersion(version) {
|
|
23473
|
-
return version <
|
|
24574
|
+
return version < SESSION_FORMAT_VERSION;
|
|
24575
|
+
}
|
|
24576
|
+
/**
|
|
24577
|
+
* 迁移链拒绝后,把旧格式行直接当**当前格式**事件视图采用。
|
|
24578
|
+
*
|
|
24579
|
+
* 旧写入器只在建会话时落一次 header version,之后跟随上游演进继续追加
|
|
24580
|
+
* 事件——同一个 log 因此可能是混合世代(v0 时代的行 + 新版本字段),不是
|
|
24581
|
+
* 任何单一已发布格式,严格迁移链必然拒绝。这些行的数据形状(消息包装、
|
|
24582
|
+
* 类型名)已经是当前格式,只需 header 版本归一到当前、按稠密 seq 读取;
|
|
24583
|
+
* 事件数据的补全与 surface 修复由读取视图修复(repairReadView)负责。
|
|
24584
|
+
*
|
|
24585
|
+
* 调用方须在此之后执行 `validateStoredEvents`:真正属于旧格式的类型
|
|
24586
|
+
* (`assistant/chunk` / `compact/*` 等)仍会被 fail-loud 拒绝。
|
|
24587
|
+
*
|
|
24588
|
+
* @param row - 会话行(f_version < SESSION_FORMAT_VERSION)。
|
|
24589
|
+
* @param eventRows - 该会话全部桥接行(按 f_sequence 升序)。
|
|
24590
|
+
* @returns 当前格式 header(version 归一到 SESSION_FORMAT_VERSION)、收缩后的
|
|
24591
|
+
* 继承前缀长度、稠密事件与可选 torn tail 起点。
|
|
24592
|
+
* @throws scanRows 的已提交区损坏错误(seq gap / 不可解析行)。
|
|
24593
|
+
*/
|
|
24594
|
+
function adoptLegacyRows(row, eventRows) {
|
|
24595
|
+
const { preserved, tornFrom } = scanRows(eventRows, 0);
|
|
24596
|
+
repairRequestHeaders(preserved);
|
|
24597
|
+
return {
|
|
24598
|
+
meta: {
|
|
24599
|
+
...rowToMeta(row),
|
|
24600
|
+
version: SESSION_FORMAT_VERSION
|
|
24601
|
+
},
|
|
24602
|
+
inheritedEventCount: Math.min(row.fSeedLength ?? 0, preserved.length),
|
|
24603
|
+
events: preserved,
|
|
24604
|
+
...tornFrom !== void 0 ? { tornFrom } : {}
|
|
24605
|
+
};
|
|
23474
24606
|
}
|
|
23475
24607
|
//#endregion
|
|
23476
24608
|
//#region ../session-rdb/src/index.ts
|
|
@@ -23615,9 +24747,7 @@ var RdbSessionHandle = class RdbSessionHandle {
|
|
|
23615
24747
|
};
|
|
23616
24748
|
throw new SessionPersistenceNotFoundError(this.id);
|
|
23617
24749
|
}
|
|
23618
|
-
|
|
23619
|
-
recomputeReplaceProvenance(log.events);
|
|
23620
|
-
repairOrphanInboxSplices(log.events);
|
|
24750
|
+
repairReadView(log.events);
|
|
23621
24751
|
return {
|
|
23622
24752
|
eventState: "detached",
|
|
23623
24753
|
events: log.events.slice(offset, offset + length)
|
|
@@ -23813,6 +24943,7 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23813
24943
|
}));
|
|
23814
24944
|
const log = await this.readLog(id, {}, options?.signal);
|
|
23815
24945
|
if (log === void 0) throw new SessionPersistenceNotFoundError(id);
|
|
24946
|
+
repairReadView(log.events);
|
|
23816
24947
|
validateStoredEvents(log.meta, log.events);
|
|
23817
24948
|
return this.tracker.adopt(new RdbSessionHandle(this, id, log.meta, "read", {
|
|
23818
24949
|
cursor: log.events.length,
|
|
@@ -23829,7 +24960,9 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23829
24960
|
}));
|
|
23830
24961
|
const log = await this.readLog(id, {}, options?.signal);
|
|
23831
24962
|
if (log === void 0) throw new SessionPersistenceNotFoundError(id);
|
|
24963
|
+
repairReadView(log.events);
|
|
23832
24964
|
validateStoredEvents(log.meta, log.events);
|
|
24965
|
+
if (log.migrated && log.events.length !== log.storedCount) await this.rewriteMigratedLog(id, log);
|
|
23833
24966
|
this.writeGuard.confirmHead(id, log.events.at(-1)?.seq ?? -1);
|
|
23834
24967
|
return this.tracker.adopt(new RdbSessionHandle(this, id, log.meta, "write", {
|
|
23835
24968
|
cursor: log.events.length,
|
|
@@ -23893,8 +25026,7 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23893
25026
|
signal?.throwIfAborted();
|
|
23894
25027
|
const log = await this.readLog(id, {}, signal);
|
|
23895
25028
|
if (log === void 0) return void 0;
|
|
23896
|
-
|
|
23897
|
-
recomputeReplaceProvenance(log.events);
|
|
25029
|
+
repairReadView(log.events);
|
|
23898
25030
|
const inheritedEventCount = Math.min(log.inheritedEventCount, log.events.length);
|
|
23899
25031
|
return {
|
|
23900
25032
|
meta: log.meta,
|
|
@@ -23917,9 +25049,9 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23917
25049
|
this.writeGuard.confirmHead(meta.id, -1);
|
|
23918
25050
|
}
|
|
23919
25051
|
/** 原样 append 落库(handle 已校验 contiguity;torn tail 先截断)。
|
|
23920
|
-
* 与上游 JSONL 一致:ignorable
|
|
23921
|
-
*
|
|
23922
|
-
*
|
|
25052
|
+
* 与上游 JSONL 一致:ignorable 事件原样存储,不做过滤。写路径校验当前
|
|
25053
|
+
* 格式形状(fail-closed):未知类型(非 ignorable)与非法消息形状拒绝入库;
|
|
25054
|
+
* 非当前格式(v0/v1/v2)数据只在读取时经 legacy 转换链动态转换,不落新库。 */
|
|
23923
25055
|
async appendBatch(meta, inheritedEventCount, events, tornTruncateTo) {
|
|
23924
25056
|
await this.ready;
|
|
23925
25057
|
if (events.length === 0) return false;
|
|
@@ -23962,14 +25094,29 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23962
25094
|
const meta = rowToMeta(row);
|
|
23963
25095
|
const eventRows = options.fromSeq === void 0 ? await this.backend.getEventRows(id) : await this.backend.getEventRows(id, options.fromSeq);
|
|
23964
25096
|
signal?.throwIfAborted();
|
|
23965
|
-
if (isLegacyVersion(row.fVersion)) {
|
|
25097
|
+
if (isLegacyVersion(row.fVersion)) try {
|
|
23966
25098
|
const converted = convertLegacyRows(row, eventRows);
|
|
23967
25099
|
return {
|
|
23968
25100
|
meta: converted.meta,
|
|
23969
25101
|
inheritedEventCount: converted.inheritedEventCount,
|
|
23970
25102
|
events: converted.events,
|
|
23971
25103
|
incarnation: row.fIncarnation,
|
|
23972
|
-
revision: row.fRevision
|
|
25104
|
+
revision: row.fRevision,
|
|
25105
|
+
storedCount: eventRows.length,
|
|
25106
|
+
migrated: true
|
|
25107
|
+
};
|
|
25108
|
+
} catch (error) {
|
|
25109
|
+
this.ctx.logger.warn(`session-rdb: session "${id}" is not a single released format; adopting its stored rows as current-format data (${error instanceof Error ? error.message : String(error)})`);
|
|
25110
|
+
const adopted = adoptLegacyRows(row, eventRows);
|
|
25111
|
+
return {
|
|
25112
|
+
meta: adopted.meta,
|
|
25113
|
+
inheritedEventCount: adopted.inheritedEventCount,
|
|
25114
|
+
events: adopted.events,
|
|
25115
|
+
incarnation: row.fIncarnation,
|
|
25116
|
+
revision: row.fRevision,
|
|
25117
|
+
storedCount: eventRows.length,
|
|
25118
|
+
migrated: false,
|
|
25119
|
+
...adopted.tornFrom !== void 0 ? { tornFrom: adopted.tornFrom } : {}
|
|
23973
25120
|
};
|
|
23974
25121
|
}
|
|
23975
25122
|
const { preserved, tornFrom } = scanRows(eventRows, options.fromSeq ?? 0);
|
|
@@ -23979,9 +25126,35 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
23979
25126
|
events: preserved,
|
|
23980
25127
|
incarnation: row.fIncarnation,
|
|
23981
25128
|
revision: row.fRevision,
|
|
25129
|
+
storedCount: eventRows.length,
|
|
25130
|
+
migrated: false,
|
|
23982
25131
|
...tornFrom !== void 0 ? { tornFrom } : {}
|
|
23983
25132
|
};
|
|
23984
25133
|
}
|
|
25134
|
+
/**
|
|
25135
|
+
* 把迁移链读出的当前格式视图整体落库(非当前格式会话写打开时的一次性迁移)。
|
|
25136
|
+
*
|
|
25137
|
+
* 迁移链会生成/合并事件(end-seed / attempt / chunk 合并),事件 seq 空间
|
|
25138
|
+
* 与存储桥接行数不再相等;写路径以存储 head 为锚点重编号,二者不一致会让
|
|
25139
|
+
* append 撞上已有行。这里在同一事务内删光本会话桥接行、按迁移视图重建
|
|
25140
|
+
* (新事件行,完整信封),并更新 head 与 revision;旧事件行保留(可能被
|
|
25141
|
+
* fork 子会话引用,孤儿由惰性 GC 处理)。
|
|
25142
|
+
*/
|
|
25143
|
+
async rewriteMigratedLog(id, log) {
|
|
25144
|
+
await this.backend.transaction(async (tx) => {
|
|
25145
|
+
await tx.deleteBridgeTail(id, 0);
|
|
25146
|
+
await tx.upsertSession({
|
|
25147
|
+
meta: log.meta,
|
|
25148
|
+
inheritedEventCount: SessionLogOffset(log.inheritedEventCount)
|
|
25149
|
+
}, randomUUID());
|
|
25150
|
+
const { headEventId, headSequence } = await appendEventTail(tx, log.meta, log.events, {
|
|
25151
|
+
parentId: "",
|
|
25152
|
+
nextSeq: 0
|
|
25153
|
+
});
|
|
25154
|
+
await tx.updateHead(id, headEventId, headSequence);
|
|
25155
|
+
await tx.bumpRevision(id);
|
|
25156
|
+
});
|
|
25157
|
+
}
|
|
23985
25158
|
async listSnapshots(signal) {
|
|
23986
25159
|
signal?.throwIfAborted();
|
|
23987
25160
|
await this.ready;
|
|
@@ -24167,6 +25340,7 @@ var SessionPersistenceRdb = class SessionPersistenceRdb extends SessionPersisten
|
|
|
24167
25340
|
if (stored.meta.cwd !== session.header.cwd) throw new Error(`session "${id}" is already persisted at a different cwd (persisted: ${String(stored.meta.cwd)}, live: ${String(session.header.cwd)}) (id collision)`);
|
|
24168
25341
|
if (stored.inheritedEventCount !== session.inheritedEventCount) throw new Error(`session "${id}" is already persisted with a different inherited event count (id collision)`);
|
|
24169
25342
|
assertVersion(stored.meta);
|
|
25343
|
+
repairReadView(stored.events);
|
|
24170
25344
|
const seed = session.snapshotEvents();
|
|
24171
25345
|
if (!seedCoversPrefix(seed, stored.events)) throw new Error(`session "${id}" already has a persisted log on disk that does not match this live session (id collision)`);
|
|
24172
25346
|
handle = await this.open(id, "write");
|