narraeon 0.5.1 → 0.5.3
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/node/runtime/history/HistoryQuery.js +550 -0
- package/dist/node/runtime/history/HistoryRendering.js +86 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +85 -6
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +23 -1
- package/dist/node/runtime/play/OrderedPlayPrompts.js +13 -4
- package/dist/node/runtime/play/PlayCallChain.js +39 -304
- package/dist/node/runtime/play/PlayCallChainError.js +6 -0
- package/dist/node/runtime/play/PlayContinuity.js +13 -8
- package/dist/node/runtime/play/PlayDocumentTools.js +59 -156
- package/dist/node/runtime/play/PlayTraceRetention.js +264 -0
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +302 -112
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +60 -16
- package/dist/node/runtime/prompt/PromptCompilationCodec.js +13 -3
- package/dist/node/runtime/world/StateDirectoryHandles.js +31 -0
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +1 -19
- package/dist/node/shared/history-use-guidance.js +6 -0
- package/dist/node/shared/ordered-play-prompts.js +12 -4
- package/dist/web/assets/index-CVN48xrw.css +1 -0
- package/dist/web/assets/index-EIOVZb8X.js +1211 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-DG85hpWC.css +0 -1
- package/dist/web/assets/index-DfxvCjb6.js +0 -1207
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { hasLegacyHistoryTools } from "../prompt/FileNativeToolRegistry.js";
|
|
2
|
+
import { PlayCallChainError } from "./PlayCallChainError.js";
|
|
3
|
+
import { preparePlayerRevisionTrace, prepareForkTrace, restorePlayDocuments, independentContextCopy, } from "./PlayTraceRetention.js";
|
|
1
4
|
import { currentPlayPrompt, recordedPlayPrompt, playPromptRunsThroughEvents, } from "./PlayPromptRun.js";
|
|
2
5
|
import { comparePromptPrefixes } from "../prompt/WorldPromptDiagnostics.js";
|
|
3
6
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { completedPlayerRounds, continuationNotice,
|
|
7
|
+
import { completedPlayerRounds, continuationNotice, playerInputAppend, toolStepNotice, } from "./PlayContinuity.js";
|
|
5
8
|
import { renderFreshContextCoverage } from "../prompt/WorldMaterialCoverage.js";
|
|
6
9
|
import { renderWriteSizeAdvice, readDeclaredWorldClock, } from "../prompt/WorldMaintenanceReport.js";
|
|
7
10
|
import { WorldDocumentStore } from "../world/WorldDocumentStore.js";
|
|
@@ -13,6 +16,8 @@ import { FileNativePlayDocuments, fingerprintControl, } from "./PlayDocumentTool
|
|
|
13
16
|
const callChainToolNames = new Set([
|
|
14
17
|
"state_list",
|
|
15
18
|
"history_list",
|
|
19
|
+
"history_search",
|
|
20
|
+
"history_read",
|
|
16
21
|
// Kept executable only for contexts whose frozen tool universe contains it.
|
|
17
22
|
"context_list",
|
|
18
23
|
"context_search",
|
|
@@ -23,12 +28,7 @@ const callChainToolNames = new Set([
|
|
|
23
28
|
"world_checkpoint",
|
|
24
29
|
]);
|
|
25
30
|
const projectedEventTail = 40;
|
|
26
|
-
export
|
|
27
|
-
constructor(message) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.name = "PlayCallChainError";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
31
|
+
export { PlayCallChainError } from "./PlayCallChainError.js";
|
|
32
32
|
/**
|
|
33
33
|
* The production play loop: one durable logical context, no Runtime-authored
|
|
34
34
|
* adjudication/narration phases. Responses with tool calls are intermediate
|
|
@@ -200,10 +200,8 @@ export class PlayCallChain {
|
|
|
200
200
|
return;
|
|
201
201
|
const { hostBinding, playPreset } = await input.resolvePrompt();
|
|
202
202
|
const binding = await this.#worlds.bindPlayCallChain(input.worldId);
|
|
203
|
-
const baseline = await this.#worlds.bindPlayCallChainAt(input.worldId, session.baselineHead);
|
|
204
|
-
const documents = new FileNativePlayDocuments(binding.files);
|
|
205
|
-
const documentMaintenance = await this.#bindDocumentMaintenance(documents, input.worldId, binding.parentHead);
|
|
206
203
|
const compilation = this.#compiler.compilePlayCallChain({
|
|
204
|
+
frozenWorldContext: currentPlayPrompt(session).bootstrap,
|
|
207
205
|
extensionControls: await this.#worlds.extensionControls(input.worldId, playPreset, this.#compiler.locale),
|
|
208
206
|
endpoint: {
|
|
209
207
|
id: `${input.worldId}:${binding.parentHead}`,
|
|
@@ -212,21 +210,17 @@ export class PlayCallChain {
|
|
|
212
210
|
hostBinding,
|
|
213
211
|
world: {
|
|
214
212
|
controlFingerprint: fingerprintControl(binding.files),
|
|
215
|
-
documentSnapshot: documents.snapshot,
|
|
213
|
+
documentSnapshot: session.documents.snapshot,
|
|
216
214
|
additionalMaterials: structuredClone(binding.additionalMaterials),
|
|
217
|
-
// Only pre-context history belongs in the prefix. Native conversation stays appended.
|
|
218
215
|
history: structuredClone(binding.history),
|
|
219
|
-
|
|
220
|
-
narrativeCheckpoint: baseline.narrativeCheckpoint,
|
|
221
|
-
documentMaintenance,
|
|
222
|
-
documentMaintenanceUnavailableReason: documents.maintenanceWarning,
|
|
216
|
+
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
223
217
|
},
|
|
224
218
|
playerInputPlacement: "append",
|
|
225
219
|
playerInput: input.playerText,
|
|
226
220
|
modelBinding: input.modelHost.binding(),
|
|
227
221
|
}, playPreset);
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
// The native conversation already contains successful writes and exact reads.
|
|
223
|
+
// Rebinding initial coverage here would incorrectly reauthorize stale scopes.
|
|
230
224
|
session.history = historyEntries(binding.history);
|
|
231
225
|
session.narrativeCheckpoint = binding.narrativeCheckpoint;
|
|
232
226
|
session.nextMaterials = structuredClone(binding.additionalMaterials);
|
|
@@ -248,7 +242,7 @@ export class PlayCallChain {
|
|
|
248
242
|
});
|
|
249
243
|
session.documentAuthorizationCheckpoints.push({
|
|
250
244
|
afterEventId: session.nextEventId,
|
|
251
|
-
authorization: documents.authorizationCheckpoint(),
|
|
245
|
+
authorization: session.documents.authorizationCheckpoint(),
|
|
252
246
|
});
|
|
253
247
|
await this.#persist(session);
|
|
254
248
|
crashAtPlayAdvanceEdge("after_prompt_run_prepared");
|
|
@@ -631,8 +625,7 @@ export class PlayCallChain {
|
|
|
631
625
|
: null;
|
|
632
626
|
const timelineGeneration = playerRevisionTimelineGeneration(input.source.timelineGeneration, request.operationId, request.worldId, request.continuation);
|
|
633
627
|
const requestFingerprint = playerRevisionRequestFingerprint(request, input.selectedEvent.committedHead, restoresHead);
|
|
634
|
-
const
|
|
635
|
-
const transcript = transcriptThroughEvents(sourceContext.transcript, prefixEvents);
|
|
628
|
+
const traceRetention = preparePlayerRevisionTrace(sourceContext, input.selectedEventIndex);
|
|
636
629
|
const outcome = await this.#worlds.reviseTimeline({
|
|
637
630
|
operationId: request.operationId,
|
|
638
631
|
worldId: request.worldId,
|
|
@@ -642,33 +635,17 @@ export class PlayCallChain {
|
|
|
642
635
|
replacementText: request.replacementText,
|
|
643
636
|
requestFingerprint,
|
|
644
637
|
});
|
|
645
|
-
const events = [
|
|
646
|
-
...prefixEvents,
|
|
647
|
-
{
|
|
648
|
-
id: input.selectedEvent.id,
|
|
649
|
-
kind: "player",
|
|
650
|
-
exchangeId: request.replacementExchangeId,
|
|
651
|
-
text: request.replacementText,
|
|
652
|
-
context: input.selectedEvent.context,
|
|
653
|
-
committedHead: outcome.head,
|
|
654
|
-
},
|
|
655
|
-
];
|
|
656
|
-
const completedKeys = completedToolKeys(prefixEvents);
|
|
657
638
|
const [baseline, selected, binding] = await Promise.all([
|
|
658
639
|
this.#worlds.recoverEndpoint(request.worldId, sourceContext.baselineHead),
|
|
659
640
|
this.#worlds.recoverEndpoint(request.worldId, restoresHead),
|
|
660
641
|
this.#worlds.bindPlayCallChain(request.worldId),
|
|
661
642
|
]);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
exactText,
|
|
666
|
-
])),
|
|
667
|
-
checkpoint: selected.narrativeCheckpoint,
|
|
643
|
+
const continuedTrace = traceRetention.withReplacement(selected, {
|
|
644
|
+
head: outcome.head,
|
|
645
|
+
exchangeId: request.replacementExchangeId,
|
|
668
646
|
text: request.replacementText,
|
|
669
647
|
locale: this.#compiler.locale,
|
|
670
|
-
|
|
671
|
-
}));
|
|
648
|
+
});
|
|
672
649
|
if (binding.parentHead !== outcome.head)
|
|
673
650
|
throw new PlayCallChainError("The timeline revision was committed, but current-world materialization has not reached the new endpoint.");
|
|
674
651
|
if (request.continuation === "fresh_context") {
|
|
@@ -679,7 +656,7 @@ export class PlayCallChain {
|
|
|
679
656
|
sourceContext,
|
|
680
657
|
sourceContexts: input.sourceContexts,
|
|
681
658
|
selectedContextIndex: input.selectedContextIndex,
|
|
682
|
-
|
|
659
|
+
traceRetention,
|
|
683
660
|
baseline,
|
|
684
661
|
selected,
|
|
685
662
|
binding,
|
|
@@ -700,9 +677,8 @@ export class PlayCallChain {
|
|
|
700
677
|
playCallChain: projectView(revised),
|
|
701
678
|
};
|
|
702
679
|
}
|
|
703
|
-
const documents = restorePlayDocuments(binding.files, sourceContext, prefixEvents);
|
|
704
680
|
const revised = {
|
|
705
|
-
|
|
681
|
+
...continuedTrace.atEndpoint({ baseline, selected, binding }),
|
|
706
682
|
schemaVersion: 3,
|
|
707
683
|
kind: "play_call_chain",
|
|
708
684
|
chainId: playerRevisionChainId(sourceContext.chainId, request.operationId, request.worldId, request.continuation),
|
|
@@ -712,44 +688,7 @@ export class PlayCallChain {
|
|
|
712
688
|
? null
|
|
713
689
|
: input.sourceContexts[input.selectedContextIndex - 1].chainId,
|
|
714
690
|
timelineGeneration,
|
|
715
|
-
baselineHead: sourceContext.baselineHead,
|
|
716
|
-
baselineHistoryLength: sourceContext.baselineHistoryLength ?? baseline.history.length,
|
|
717
691
|
parentHead: outcome.head,
|
|
718
|
-
promptRuns: playPromptRunsThroughEvents(sourceContext, events),
|
|
719
|
-
...(sourceContext.presetFiles === undefined
|
|
720
|
-
? {}
|
|
721
|
-
: { presetFiles: structuredClone(sourceContext.presetFiles) }),
|
|
722
|
-
playPreset: structuredClone(sourceContext.playPreset),
|
|
723
|
-
...(sourceContext.followups === undefined
|
|
724
|
-
? {}
|
|
725
|
-
: { followups: structuredClone(sourceContext.followups) }),
|
|
726
|
-
...(sourceContext.playPresetScriptsEnabled === undefined
|
|
727
|
-
? {}
|
|
728
|
-
: {
|
|
729
|
-
playPresetScriptsEnabled: sourceContext.playPresetScriptsEnabled,
|
|
730
|
-
}),
|
|
731
|
-
...(sourceContext.modelBinding === undefined
|
|
732
|
-
? {}
|
|
733
|
-
: { modelBinding: structuredClone(sourceContext.modelBinding) }),
|
|
734
|
-
status: "ready",
|
|
735
|
-
canRetry: false,
|
|
736
|
-
bootstrap: structuredClone(sourceContext.bootstrap),
|
|
737
|
-
tools: structuredClone(sourceContext.tools),
|
|
738
|
-
transcript,
|
|
739
|
-
events,
|
|
740
|
-
completedTools: sourceContext.completedTools
|
|
741
|
-
.filter(({ key }) => completedKeys.has(key))
|
|
742
|
-
.map((item) => structuredClone(item)),
|
|
743
|
-
documentAuthorizationCheckpoints: authorizationCheckpointsThroughEvents(sourceContext, events, documents.authorizationCheckpoint()),
|
|
744
|
-
changedDocuments: changedDocumentsAtHead(sourceContext.changedDocuments, baseline.state, selected.state),
|
|
745
|
-
nextMaterials: structuredClone(binding.additionalMaterials),
|
|
746
|
-
nextEventId: Math.max(0, ...events.map(({ id }) => id)) + 1,
|
|
747
|
-
exchange: Math.max(0, ...prefixEvents
|
|
748
|
-
.filter((event) => event.kind === "assistant" && event.status === "completed")
|
|
749
|
-
.map(({ exchange }) => exchange)),
|
|
750
|
-
lastRequest: null,
|
|
751
|
-
lastRequestAttempt: 0,
|
|
752
|
-
lastFailure: null,
|
|
753
692
|
updatedAt: Date.now(),
|
|
754
693
|
};
|
|
755
694
|
await this.#worlds.playTimeline.persist(structuredClone(revised));
|
|
@@ -803,12 +742,14 @@ export class PlayCallChain {
|
|
|
803
742
|
let previousChainId = input.selectedContextIndex === 0
|
|
804
743
|
? null
|
|
805
744
|
: input.sourceContexts[input.selectedContextIndex - 1].chainId;
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
745
|
+
const retainedPrefix = input.traceRetention.pagePrefixAtEndpoint({
|
|
746
|
+
baseline: input.baseline,
|
|
747
|
+
selected: input.selected,
|
|
748
|
+
binding: input.binding,
|
|
749
|
+
});
|
|
750
|
+
if (retainedPrefix !== null) {
|
|
810
751
|
const prefix = {
|
|
811
|
-
|
|
752
|
+
...retainedPrefix,
|
|
812
753
|
schemaVersion: 3,
|
|
813
754
|
kind: "play_call_chain",
|
|
814
755
|
chainId: derivedChainId(sourceContext.chainId, "timeline-revision-prefix:" + request.operationId, request.worldId),
|
|
@@ -816,42 +757,7 @@ export class PlayCallChain {
|
|
|
816
757
|
previousContexts: [],
|
|
817
758
|
previousChainId,
|
|
818
759
|
timelineGeneration: input.timelineGeneration,
|
|
819
|
-
baselineHead: sourceContext.baselineHead,
|
|
820
|
-
baselineHistoryLength: sourceContext.baselineHistoryLength ?? input.baseline.history.length,
|
|
821
760
|
parentHead: input.restoresHead,
|
|
822
|
-
promptRuns: playPromptRunsThroughEvents(sourceContext, input.prefixEvents),
|
|
823
|
-
...(sourceContext.presetFiles === undefined
|
|
824
|
-
? {}
|
|
825
|
-
: { presetFiles: structuredClone(sourceContext.presetFiles) }),
|
|
826
|
-
playPreset: structuredClone(sourceContext.playPreset),
|
|
827
|
-
...(sourceContext.followups === undefined
|
|
828
|
-
? {}
|
|
829
|
-
: { followups: structuredClone(sourceContext.followups) }),
|
|
830
|
-
...(sourceContext.playPresetScriptsEnabled === undefined
|
|
831
|
-
? {}
|
|
832
|
-
: {
|
|
833
|
-
playPresetScriptsEnabled: sourceContext.playPresetScriptsEnabled,
|
|
834
|
-
}),
|
|
835
|
-
...(sourceContext.modelBinding === undefined
|
|
836
|
-
? {}
|
|
837
|
-
: { modelBinding: structuredClone(sourceContext.modelBinding) }),
|
|
838
|
-
status: "ready",
|
|
839
|
-
canRetry: false,
|
|
840
|
-
bootstrap: structuredClone(sourceContext.bootstrap),
|
|
841
|
-
tools: structuredClone(sourceContext.tools),
|
|
842
|
-
transcript: prefixTranscript,
|
|
843
|
-
events: structuredClone(input.prefixEvents),
|
|
844
|
-
completedTools: sourceContext.completedTools
|
|
845
|
-
.filter(({ key }) => completedKeys.has(key))
|
|
846
|
-
.map((item) => structuredClone(item)),
|
|
847
|
-
documentAuthorizationCheckpoints: authorizationCheckpointsThroughEvents(sourceContext, input.prefixEvents, documents.authorizationCheckpoint()),
|
|
848
|
-
changedDocuments: changedDocumentsAtHead(sourceContext.changedDocuments, input.baseline.state, input.selected.state),
|
|
849
|
-
nextMaterials: structuredClone(input.selected.additionalMaterials),
|
|
850
|
-
nextEventId: Math.max(0, ...input.prefixEvents.map(({ id }) => id)) + 1,
|
|
851
|
-
exchange: completedAssistantExchange(input.prefixEvents),
|
|
852
|
-
lastRequest: null,
|
|
853
|
-
lastRequestAttempt: 0,
|
|
854
|
-
lastFailure: null,
|
|
855
761
|
updatedAt: Date.now(),
|
|
856
762
|
};
|
|
857
763
|
await this.#worlds.playTimeline.persistDetached(structuredClone(prefix));
|
|
@@ -934,20 +840,14 @@ export class PlayCallChain {
|
|
|
934
840
|
? input.targetWorldId
|
|
935
841
|
: input.sourceWorldId, input.sourceHead),
|
|
936
842
|
]);
|
|
937
|
-
const
|
|
938
|
-
// A complete context already defines the selected trace closure. Preserve
|
|
939
|
-
// its model transcript verbatim instead of reconstructing it from the
|
|
940
|
-
// independently persisted page-event projection.
|
|
941
|
-
const transcript = selectsCompleteContext
|
|
942
|
-
? structuredClone(sourceContext.transcript)
|
|
943
|
-
: transcriptThroughEvents(sourceContext.transcript, input.sourceEvents);
|
|
944
|
-
const completedKeys = selectsCompleteContext
|
|
945
|
-
? null
|
|
946
|
-
: completedToolKeys(input.sourceEvents);
|
|
947
|
-
const events = structuredClone(input.sourceEvents);
|
|
843
|
+
const traceRetention = prepareForkTrace(sourceContext, input.sourceEvents);
|
|
948
844
|
const derivedBinding = input.targetBinding ??
|
|
949
845
|
(await this.#worlds.bindPlayCallChain(input.targetWorldId));
|
|
950
|
-
const
|
|
846
|
+
const retainedTrace = traceRetention.atEndpoint({
|
|
847
|
+
baseline,
|
|
848
|
+
selected,
|
|
849
|
+
binding: derivedBinding,
|
|
850
|
+
});
|
|
951
851
|
const now = Date.now();
|
|
952
852
|
const timelineGeneration = this.#worlds.playTimeline.newGeneration();
|
|
953
853
|
let previousChainId = null;
|
|
@@ -977,7 +877,7 @@ export class PlayCallChain {
|
|
|
977
877
|
previousChainId = chainId;
|
|
978
878
|
}
|
|
979
879
|
const derived = {
|
|
980
|
-
|
|
880
|
+
...retainedTrace,
|
|
981
881
|
schemaVersion: 3,
|
|
982
882
|
kind: "play_call_chain",
|
|
983
883
|
chainId: derivedChainId(sourceContext.chainId, input.branchIdentity, input.targetWorldId),
|
|
@@ -985,44 +885,7 @@ export class PlayCallChain {
|
|
|
985
885
|
previousContexts: [],
|
|
986
886
|
previousChainId,
|
|
987
887
|
timelineGeneration,
|
|
988
|
-
baselineHead: sourceContext.baselineHead,
|
|
989
|
-
baselineHistoryLength: sourceContext.baselineHistoryLength ?? baseline.history.length,
|
|
990
888
|
parentHead: input.sourceHead,
|
|
991
|
-
promptRuns: playPromptRunsThroughEvents(sourceContext, events),
|
|
992
|
-
...(sourceContext.presetFiles === undefined
|
|
993
|
-
? {}
|
|
994
|
-
: { presetFiles: structuredClone(sourceContext.presetFiles) }),
|
|
995
|
-
playPreset: structuredClone(sourceContext.playPreset),
|
|
996
|
-
...(sourceContext.followups === undefined
|
|
997
|
-
? {}
|
|
998
|
-
: { followups: structuredClone(sourceContext.followups) }),
|
|
999
|
-
...(sourceContext.playPresetScriptsEnabled === undefined
|
|
1000
|
-
? {}
|
|
1001
|
-
: {
|
|
1002
|
-
playPresetScriptsEnabled: sourceContext.playPresetScriptsEnabled,
|
|
1003
|
-
}),
|
|
1004
|
-
...(sourceContext.modelBinding === undefined
|
|
1005
|
-
? {}
|
|
1006
|
-
: { modelBinding: structuredClone(sourceContext.modelBinding) }),
|
|
1007
|
-
status: "ready",
|
|
1008
|
-
canRetry: false,
|
|
1009
|
-
bootstrap: structuredClone(sourceContext.bootstrap),
|
|
1010
|
-
tools: structuredClone(sourceContext.tools),
|
|
1011
|
-
transcript,
|
|
1012
|
-
events,
|
|
1013
|
-
completedTools: sourceContext.completedTools
|
|
1014
|
-
.filter(({ key }) => completedKeys === null || completedKeys.has(key))
|
|
1015
|
-
.map((item) => structuredClone(item)),
|
|
1016
|
-
documentAuthorizationCheckpoints: authorizationCheckpointsThroughEvents(sourceContext, events, derivedDocuments.authorizationCheckpoint()),
|
|
1017
|
-
changedDocuments: changedDocumentsAtHead(sourceContext.changedDocuments, baseline.state, selected.state),
|
|
1018
|
-
nextMaterials: structuredClone(derivedBinding.additionalMaterials),
|
|
1019
|
-
nextEventId: Math.max(0, ...events.map(({ id }) => id)) + 1,
|
|
1020
|
-
exchange: Math.max(0, ...events
|
|
1021
|
-
.filter((event) => event.kind === "assistant" && event.status === "completed")
|
|
1022
|
-
.map(({ exchange }) => exchange)),
|
|
1023
|
-
lastRequest: null,
|
|
1024
|
-
lastRequestAttempt: 0,
|
|
1025
|
-
lastFailure: null,
|
|
1026
889
|
updatedAt: now,
|
|
1027
890
|
};
|
|
1028
891
|
if (input.targetWorldRoot === undefined)
|
|
@@ -2123,7 +1986,11 @@ function prepareTool(session, working, exchange, call, locale) {
|
|
|
2123
1986
|
failureKind: "protocol",
|
|
2124
1987
|
markdown: "world_checkpoint requires an empty argument object.",
|
|
2125
1988
|
}
|
|
2126
|
-
: session.documents.execute(call, session.history
|
|
1989
|
+
: session.documents.execute(call, session.history, {
|
|
1990
|
+
historyScope: session.worldId,
|
|
1991
|
+
locale,
|
|
1992
|
+
legacyHistoryTools: hasLegacyHistoryTools(currentPlayPrompt(session).tools),
|
|
1993
|
+
});
|
|
2127
1994
|
}
|
|
2128
1995
|
catch (error) {
|
|
2129
1996
|
result = {
|
|
@@ -2184,53 +2051,6 @@ function mergeChangedDocuments(session, changes) {
|
|
|
2184
2051
|
session.changedDocuments[index] = projected;
|
|
2185
2052
|
}
|
|
2186
2053
|
}
|
|
2187
|
-
function restorePlayDocuments(files, context, events) {
|
|
2188
|
-
const documents = new FileNativePlayDocuments(files);
|
|
2189
|
-
try {
|
|
2190
|
-
const checkpoint = documentAuthorizationThroughEvents(context, events);
|
|
2191
|
-
const runs = playPromptRunsThroughEvents(context, events);
|
|
2192
|
-
if (checkpoint === undefined || runs.length > 0)
|
|
2193
|
-
documents.bindBootstrap(currentPlayPrompt({ ...context, promptRuns: runs }).bootstrap);
|
|
2194
|
-
if (checkpoint !== undefined)
|
|
2195
|
-
documents.restoreAuthorizationCheckpoint(checkpoint.authorization);
|
|
2196
|
-
}
|
|
2197
|
-
catch (error) {
|
|
2198
|
-
throw new PlayCallChainError(`Call-chain document authorization could not be restored: ${error instanceof Error ? error.message : "invalid checkpoint"}。`);
|
|
2199
|
-
}
|
|
2200
|
-
return documents;
|
|
2201
|
-
}
|
|
2202
|
-
function documentAuthorizationThroughEvents(context, events) {
|
|
2203
|
-
const checkpoints = context.documentAuthorizationCheckpoints;
|
|
2204
|
-
if (checkpoints === undefined || checkpoints.length === 0)
|
|
2205
|
-
return undefined;
|
|
2206
|
-
const selectedEventIds = new Set(events.map(({ id }) => id));
|
|
2207
|
-
const selected = checkpoints.findLast(({ afterEventId }) => afterEventId === 0 || selectedEventIds.has(afterEventId));
|
|
2208
|
-
// Legacy V1 records had no durable dynamic authorization. If the selected
|
|
2209
|
-
// prefix predates their first lazily-written checkpoint, retain the exact
|
|
2210
|
-
// old recovery behavior and rebuild only bootstrap authorization.
|
|
2211
|
-
return selected === undefined ? undefined : structuredClone(selected);
|
|
2212
|
-
}
|
|
2213
|
-
function independentContextCopy(source) {
|
|
2214
|
-
const context = structuredClone(source);
|
|
2215
|
-
context.continuityContextId ??= source.chainId;
|
|
2216
|
-
delete context.derivedFrom;
|
|
2217
|
-
delete context.branchedBeforePlayer;
|
|
2218
|
-
return context;
|
|
2219
|
-
}
|
|
2220
|
-
function authorizationCheckpointsThroughEvents(context, events, fallback) {
|
|
2221
|
-
const eventIds = new Set(events.map(({ id }) => id));
|
|
2222
|
-
const selected = (context.documentAuthorizationCheckpoints ?? [])
|
|
2223
|
-
.filter(({ afterEventId }) => afterEventId === 0 || eventIds.has(afterEventId))
|
|
2224
|
-
.map((checkpoint) => structuredClone(checkpoint));
|
|
2225
|
-
if (selected.length > 0)
|
|
2226
|
-
return selected;
|
|
2227
|
-
return [
|
|
2228
|
-
{
|
|
2229
|
-
afterEventId: Math.max(0, ...events.map(({ id }) => id)),
|
|
2230
|
-
authorization: structuredClone(fallback),
|
|
2231
|
-
},
|
|
2232
|
-
];
|
|
2233
|
-
}
|
|
2234
2054
|
function projectContext(context) {
|
|
2235
2055
|
return {
|
|
2236
2056
|
chainId: context.chainId,
|
|
@@ -2349,86 +2169,6 @@ function eventsThroughHead(events, head) {
|
|
|
2349
2169
|
end += 1;
|
|
2350
2170
|
return structuredClone(events.slice(0, end));
|
|
2351
2171
|
}
|
|
2352
|
-
function transcriptThroughEvents(transcript, events) {
|
|
2353
|
-
const result = [];
|
|
2354
|
-
let cursor = 0;
|
|
2355
|
-
for (const [index, event] of events.entries()) {
|
|
2356
|
-
if (event.kind === "player") {
|
|
2357
|
-
const marker = transcript[cursor];
|
|
2358
|
-
if (isPlayerRoundMarker(marker)) {
|
|
2359
|
-
result.push(structuredClone(marker));
|
|
2360
|
-
cursor += 1;
|
|
2361
|
-
}
|
|
2362
|
-
const item = transcript[cursor];
|
|
2363
|
-
if (item?.kind !== "player" || item.text !== event.text)
|
|
2364
|
-
throw new PlayCallChainError("Source call-chain events do not match the model transcript, so a fork cannot be created safely.");
|
|
2365
|
-
result.push(structuredClone(item));
|
|
2366
|
-
cursor += 1;
|
|
2367
|
-
continue;
|
|
2368
|
-
}
|
|
2369
|
-
if (event.kind === "assistant" && event.status === "completed") {
|
|
2370
|
-
const notice = transcript[cursor];
|
|
2371
|
-
const hasContinuationNotice = notice?.kind === "runtime_notice" && notice.notice === "continuation";
|
|
2372
|
-
const item = transcript[cursor + (hasContinuationNotice ? 1 : 0)];
|
|
2373
|
-
const hasToolCall = events
|
|
2374
|
-
.slice(index + 1)
|
|
2375
|
-
.find((candidate) => candidate.kind === "player" ||
|
|
2376
|
-
candidate.kind === "assistant" ||
|
|
2377
|
-
candidate.kind === "tool_call");
|
|
2378
|
-
const recorded = event.text.trim().length > 0 ||
|
|
2379
|
-
hasToolCall?.kind === "tool_call" ||
|
|
2380
|
-
item?.kind === "assistant";
|
|
2381
|
-
if (!recorded)
|
|
2382
|
-
continue;
|
|
2383
|
-
if (item?.kind !== "assistant" || item.text !== event.text)
|
|
2384
|
-
throw new PlayCallChainError("Source call-chain responses do not match the model transcript, so a fork cannot be created safely.");
|
|
2385
|
-
// A continuation notice belongs to the generation it precedes, never
|
|
2386
|
-
// to a fork ending at the previously completed narrative.
|
|
2387
|
-
if (hasContinuationNotice) {
|
|
2388
|
-
result.push(structuredClone(notice));
|
|
2389
|
-
cursor += 1;
|
|
2390
|
-
}
|
|
2391
|
-
result.push(structuredClone(item));
|
|
2392
|
-
cursor += 1;
|
|
2393
|
-
continue;
|
|
2394
|
-
}
|
|
2395
|
-
if (event.kind === "tool_result") {
|
|
2396
|
-
const item = transcript[cursor];
|
|
2397
|
-
if (item?.kind !== "tool" || item.toolCallId !== event.callId)
|
|
2398
|
-
throw new PlayCallChainError("Source call-chain tool results do not match the model transcript, so a fork cannot be created safely.");
|
|
2399
|
-
result.push(structuredClone(item));
|
|
2400
|
-
cursor += 1;
|
|
2401
|
-
// A tool-step notice follows the entire settled batch and remains part
|
|
2402
|
-
// of its closure even when the selected endpoint stops at that batch.
|
|
2403
|
-
const notice = transcript[cursor];
|
|
2404
|
-
if (notice?.kind === "runtime_notice" && notice.notice === "tool_step") {
|
|
2405
|
-
result.push(structuredClone(notice));
|
|
2406
|
-
cursor += 1;
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
}
|
|
2410
|
-
return result;
|
|
2411
|
-
}
|
|
2412
|
-
function completedToolKeys(events) {
|
|
2413
|
-
const keys = new Set();
|
|
2414
|
-
let exchange = null;
|
|
2415
|
-
for (const event of events) {
|
|
2416
|
-
if (event.kind === "player")
|
|
2417
|
-
exchange = null;
|
|
2418
|
-
else if (event.kind === "assistant")
|
|
2419
|
-
exchange = event.exchange;
|
|
2420
|
-
else if (event.kind === "tool_call" && exchange !== null)
|
|
2421
|
-
keys.add(`${exchange}:${event.callId}`);
|
|
2422
|
-
}
|
|
2423
|
-
return keys;
|
|
2424
|
-
}
|
|
2425
|
-
function changedDocumentsAtHead(changes, baseline, selected) {
|
|
2426
|
-
const before = new Map(baseline.map((file) => [file.path, file.contents]));
|
|
2427
|
-
const after = new Map(selected.map((file) => [file.path, file.contents]));
|
|
2428
|
-
return changes
|
|
2429
|
-
.filter(({ path }) => after.has(path) && before.get(path) !== after.get(path))
|
|
2430
|
-
.map((change) => structuredClone(change));
|
|
2431
|
-
}
|
|
2432
2172
|
function derivedChainId(sourceChainId, branchIdentity, targetWorldId) {
|
|
2433
2173
|
return `play-branch-${createHash("sha256")
|
|
2434
2174
|
.update(`${sourceChainId}\0${branchIdentity}\0${targetWorldId}`)
|
|
@@ -2479,11 +2219,6 @@ function playerRevisionTimelineGeneration(sourceGeneration, operationId, worldId
|
|
|
2479
2219
|
.digest("hex")
|
|
2480
2220
|
.slice(0, 40)}`;
|
|
2481
2221
|
}
|
|
2482
|
-
function completedAssistantExchange(events) {
|
|
2483
|
-
return Math.max(0, ...events
|
|
2484
|
-
.filter((event) => event.kind === "assistant" && event.status === "completed")
|
|
2485
|
-
.map(({ exchange }) => exchange));
|
|
2486
|
-
}
|
|
2487
2222
|
function historyEntries(history) {
|
|
2488
2223
|
return Object.entries(history).map(([path, contents]) => ({
|
|
2489
2224
|
path,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HistoryQuery, historyInputs } from "../history/HistoryQuery.js";
|
|
2
|
+
import { renderHistoryMessage, renderHistoryInjectionNotice, } from "../history/HistoryRendering.js";
|
|
1
3
|
/** Narrative history contains only committed original inputs and final prose. */
|
|
2
4
|
export function completedPlayerRounds(history) {
|
|
3
5
|
let pending = false;
|
|
@@ -68,21 +70,24 @@ export function playerInputAppend(input) {
|
|
|
68
70
|
{ kind: "player", text: input.text },
|
|
69
71
|
];
|
|
70
72
|
}
|
|
71
|
-
export function checkpointReplayBlocks(history, checkpoint, locale) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
73
|
+
export function checkpointReplayBlocks(history, checkpoint, locale, excluded = new Set(), snapshot = new HistoryQuery("checkpoint-replay", historyInputs(Object.entries(history).map(([path, contents]) => ({ path, contents }))))) {
|
|
74
|
+
const eligible = checkpointHistory(history, checkpoint);
|
|
75
|
+
if (eligible.length === 0)
|
|
74
76
|
return [];
|
|
77
|
+
const ids = new Set(eligible.filter(([id]) => !excluded.has(id)).map(([id]) => id));
|
|
78
|
+
const entries = snapshot.messages.filter(({ id }) => ids.has(id));
|
|
75
79
|
const zh = locale === "zh-CN";
|
|
76
80
|
return [
|
|
77
81
|
{
|
|
78
82
|
source: "runtime:checkpoint-history:notice",
|
|
79
|
-
markdown: zh
|
|
83
|
+
markdown: (zh
|
|
80
84
|
? "# 检查点后的已提交原文\n\n以下是当前时间线上最近一次已生效检查点之后的玩家原文与最终主持叙事;尚无检查点时从世界起点选取,开场白除外。部分结果可能已写入当前文档;这些记录用于核对连续性,不代表需要再次执行其中的事件。当前明确的世界修订优先,不能用旧叙事推翻修订。"
|
|
81
|
-
: "# Committed history after the checkpoint\n\nThese are the current timeline’s original player inputs and final narratives after its last effective checkpoint (or the world origin if none, excluding the opening). Some results may already be in current documents. These records support continuity, not repeated execution. Explicit current world corrections take precedence over old narrative."
|
|
85
|
+
: "# Committed history after the checkpoint\n\nThese are the current timeline’s original player inputs and final narratives after its last effective checkpoint (or the world origin if none, excluding the opening). Some results may already be in current documents. These records support continuity, not repeated execution. Explicit current world corrections take precedence over old narrative.") +
|
|
86
|
+
`\n\n${renderHistoryInjectionNotice(snapshot.messages.length, entries, "checkpoint", locale)}`,
|
|
82
87
|
},
|
|
83
|
-
...entries.map((
|
|
84
|
-
source: `runtime:checkpoint-history:${id}`,
|
|
85
|
-
markdown:
|
|
88
|
+
...entries.map((message) => ({
|
|
89
|
+
source: `runtime:checkpoint-history:${message.id}`,
|
|
90
|
+
markdown: renderHistoryMessage(message, locale),
|
|
86
91
|
})),
|
|
87
92
|
];
|
|
88
93
|
}
|