narraeon 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/node/protocol/appPreferences.js +4 -2
- package/dist/node/protocol/playFailure.js +13 -0
- package/dist/node/protocol/protagonistTemplates.js +13 -0
- package/dist/node/protocol/v1.js +25 -19
- package/dist/node/protocol/worldArchives.js +7 -0
- package/dist/node/runtime/V1Runtime.js +48 -6
- package/dist/node/runtime/archive/WorldArchiveZip.js +202 -0
- package/dist/node/runtime/archive/WorldArchives.js +389 -0
- package/dist/node/runtime/config/AppPreferencesStore.js +23 -10
- package/dist/node/runtime/config/ProtagonistTemplateStore.js +134 -0
- package/dist/node/runtime/content/ContentWorkspace.js +5 -0
- package/dist/node/runtime/history/HistoryBackground.js +84 -0
- package/dist/node/runtime/history/HistoryQuery.js +7 -2
- package/dist/node/runtime/history/HistoryRendering.js +31 -4
- package/dist/node/runtime/history/StoryExport.js +33 -0
- package/dist/node/runtime/model/FileNativeModelAdapters.js +34 -2
- package/dist/node/runtime/model/ModelHost.js +12 -1
- package/dist/node/runtime/model/ModelHostStream.js +11 -3
- package/dist/node/runtime/play/FileNativeContinuityCorrection.js +2 -0
- package/dist/node/runtime/play/FileNativePlayAdvanceStore.js +5 -0
- package/dist/node/runtime/play/FileNativePlayTimelineStore.js +91 -4
- package/dist/node/runtime/play/LegacyModelBindingFailure.js +10 -0
- package/dist/node/runtime/play/PlayCallChain.js +212 -61
- package/dist/node/runtime/play/PlayCallChainError.js +3 -1
- package/dist/node/runtime/play/PlayContinuity.js +3 -20
- package/dist/node/runtime/play/PlayFailure.js +20 -0
- package/dist/node/runtime/play/PlayTraceRetention.js +10 -11
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +12 -59
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +2 -2
- package/dist/node/runtime/world/FileNativeAuthorityV3.js +7 -4
- package/dist/node/runtime/world/FileNativeHistoryProjection.js +11 -0
- package/dist/node/runtime/world/FileNativeWorldStore.js +250 -55
- package/dist/node/runtime/world/WorldActivity.js +143 -0
- package/dist/node/runtime/world/WorldYamlRendering.js +28 -0
- package/dist/node/server/createServer.js +48 -0
- package/dist/node/shared/default-preset-host.js +18 -8
- package/dist/node/shared/default-setting-improvement-prompt.js +4 -0
- package/dist/node/shared/history-use-guidance.js +2 -2
- package/dist/node/shared/ordered-play-prompts.js +2 -2
- package/dist/node/shared/sample-packages/index.js +10 -0
- package/dist/node/shared/sample-packages/rainy-inn-en.js +194 -0
- package/dist/node/shared/sample-packages/rainy-inn-zh.js +194 -0
- package/dist/web/assets/index-DxNtxzLR.js +1255 -0
- package/dist/web/assets/{index-CVN48xrw.css → index-DzOnu9uv.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- package/dist/web/assets/index-DTFZQan4.js +0 -1223
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { projectLegacyModelBindingFailure } from "./LegacyModelBindingFailure.js";
|
|
2
|
+
import { classifyPlayFailure } from "./PlayFailure.js";
|
|
3
|
+
import { captureNarrativeOrigin, snapshotBackground, narrativeBackground, } from "../history/HistoryBackground.js";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1
5
|
import { hasLegacyHistoryTools } from "../prompt/FileNativeToolRegistry.js";
|
|
2
6
|
import { PlayCallChainError } from "./PlayCallChainError.js";
|
|
3
7
|
import { preparePlayerRevisionTrace, prepareForkTrace, restorePlayDocuments, independentContextCopy, } from "./PlayTraceRetention.js";
|
|
@@ -8,7 +12,7 @@ import { completedPlayerRounds, continuationNotice, playerInputAppend, toolStepN
|
|
|
8
12
|
import { renderFreshContextCoverage } from "../prompt/WorldMaterialCoverage.js";
|
|
9
13
|
import { renderWriteSizeAdvice, readDeclaredWorldClock, } from "../prompt/WorldMaintenanceReport.js";
|
|
10
14
|
import { WorldDocumentStore } from "../world/WorldDocumentStore.js";
|
|
11
|
-
import { equalModelHostBinding, ModelHostCancelledError, ModelHostContinuationError, ModelHostFailureError, ModelHostOutcomeUnknownError, modelHostFailureRequiresFreshContext, } from "../model/ModelHost.js";
|
|
15
|
+
import { equalModelHostBinding, ModelHostBindingMismatchError, ModelHostCancelledError, ModelHostContinuationError, ModelHostFailureError, ModelHostOutcomeUnknownError, modelHostFailureRequiresFreshContext, } from "../model/ModelHost.js";
|
|
12
16
|
import { FileNativePromptCompiler, } from "../prompt/FileNativePromptCompiler.js";
|
|
13
17
|
import { parsePlayPresetFiles, } from "./FileNativePlayPresetStore.js";
|
|
14
18
|
import { runPlayFollowupRequests, } from "./PlayFollowupRequests.js";
|
|
@@ -88,6 +92,7 @@ export class PlayCallChain {
|
|
|
88
92
|
documentSnapshot: documents.snapshot,
|
|
89
93
|
additionalMaterials: structuredClone(binding.additionalMaterials),
|
|
90
94
|
history: structuredClone(binding.history),
|
|
95
|
+
historyBackgrounds: structuredClone(binding.historyBackgrounds ?? {}),
|
|
91
96
|
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
92
97
|
documentMaintenance,
|
|
93
98
|
documentMaintenanceUnavailableReason: documents.maintenanceWarning,
|
|
@@ -144,7 +149,7 @@ export class PlayCallChain {
|
|
|
144
149
|
lastFailure: null,
|
|
145
150
|
updatedAt: now,
|
|
146
151
|
documents,
|
|
147
|
-
history: historyEntries(binding.history),
|
|
152
|
+
history: historyEntries(binding.history, binding.historyBackgrounds),
|
|
148
153
|
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
149
154
|
completedToolMap: new Map(),
|
|
150
155
|
persistenceCursor: {
|
|
@@ -159,6 +164,7 @@ export class PlayCallChain {
|
|
|
159
164
|
this.#active.set(session.chainId, session);
|
|
160
165
|
this.#worldChains.set(session.worldId, session.chainId);
|
|
161
166
|
await this.#persist(session);
|
|
167
|
+
await this.#worlds.completeArchiveFreshContext(session.worldId);
|
|
162
168
|
return this.#submitPlayer(session, input.exchangeId, input.playerText, "fresh", input.modelHost, signal, input.observer);
|
|
163
169
|
}
|
|
164
170
|
async append(input) {
|
|
@@ -166,6 +172,8 @@ export class PlayCallChain {
|
|
|
166
172
|
}
|
|
167
173
|
async #append(input, signal) {
|
|
168
174
|
validateIdentity(input.exchangeId, "exchange ID");
|
|
175
|
+
if (await this.#worlds.archiveNeedsFreshContext(input.worldId))
|
|
176
|
+
throw new PlayCallChainError("This world was imported from a backup; start a fresh context before continuing.");
|
|
169
177
|
const session = await this.#requireChain(input.worldId, input.chainId);
|
|
170
178
|
await this.#assertCurrentHead(session);
|
|
171
179
|
assertSessionModelHostBinding(session, input.modelHost);
|
|
@@ -175,6 +183,11 @@ export class PlayCallChain {
|
|
|
175
183
|
if (session.status === "interrupted") {
|
|
176
184
|
if (!session.canRetry || session.lastRequest === null)
|
|
177
185
|
throw new PlayCallChainError("The current model context cannot continue; use a fresh context.");
|
|
186
|
+
// Pre-snapshot requests have only a binding fingerprint. Do not invent
|
|
187
|
+
// their original encoding settings from a newly selected connection.
|
|
188
|
+
if (session.lastRequest.modelConfig === undefined &&
|
|
189
|
+
!equalModelHostBinding(session.modelBinding, input.modelHost.binding()))
|
|
190
|
+
throw new PlayCallChainError("This older saved request has no model configuration snapshot. Restore its original settings to retry it, or start a fresh context.");
|
|
178
191
|
return this.#dispatch(session, input.modelHost, structuredClone(session.lastRequest), session.lastRequestAttempt + 1, signal, input.observer);
|
|
179
192
|
}
|
|
180
193
|
await this.#refreshPrompt(session, input);
|
|
@@ -182,8 +195,10 @@ export class PlayCallChain {
|
|
|
182
195
|
const last = session.transcript.at(-1);
|
|
183
196
|
if (last?.kind === "assistant" &&
|
|
184
197
|
last.toolCalls.length === 0 &&
|
|
185
|
-
last.text.trim().length > 0)
|
|
198
|
+
last.text.trim().length > 0) {
|
|
199
|
+
session.narrativeOrigin = await this.#captureOrigin(session);
|
|
186
200
|
session.transcript.push(continuationNotice(this.#compiler.locale));
|
|
201
|
+
}
|
|
187
202
|
return this.#dispatch(session, input.modelHost, createRequest(session, input.modelHost, session.exchange), 1, signal, input.observer);
|
|
188
203
|
}
|
|
189
204
|
if (duplicatePlayerExchange(session, input.exchangeId, input.playerText, "append"))
|
|
@@ -213,6 +228,7 @@ export class PlayCallChain {
|
|
|
213
228
|
documentSnapshot: session.documents.snapshot,
|
|
214
229
|
additionalMaterials: structuredClone(binding.additionalMaterials),
|
|
215
230
|
history: structuredClone(binding.history),
|
|
231
|
+
historyBackgrounds: structuredClone(binding.historyBackgrounds ?? {}),
|
|
216
232
|
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
217
233
|
},
|
|
218
234
|
playerInputPlacement: "append",
|
|
@@ -221,7 +237,7 @@ export class PlayCallChain {
|
|
|
221
237
|
}, playPreset);
|
|
222
238
|
// The native conversation already contains successful writes and exact reads.
|
|
223
239
|
// Rebinding initial coverage here would incorrectly reauthorize stale scopes.
|
|
224
|
-
session.history = historyEntries(binding.history);
|
|
240
|
+
session.history = historyEntries(binding.history, binding.historyBackgrounds);
|
|
225
241
|
session.narrativeCheckpoint = binding.narrativeCheckpoint;
|
|
226
242
|
session.nextMaterials = structuredClone(binding.additionalMaterials);
|
|
227
243
|
(session.promptRuns ??= []).push({
|
|
@@ -458,7 +474,8 @@ export class PlayCallChain {
|
|
|
458
474
|
chainId: session.chainId,
|
|
459
475
|
baselineHead: session.baselineHead,
|
|
460
476
|
parentHead: session.parentHead,
|
|
461
|
-
stale: session.parentHead !== worldHead
|
|
477
|
+
stale: session.parentHead !== worldHead ||
|
|
478
|
+
(await this.#worlds.archiveNeedsFreshContext(worldId)),
|
|
462
479
|
playPreset: structuredClone(recordedPlayPrompt(session).playPreset),
|
|
463
480
|
updatedAt: session.updatedAt,
|
|
464
481
|
bootstrap: {
|
|
@@ -471,17 +488,25 @@ export class PlayCallChain {
|
|
|
471
488
|
async deriveWorld(input) {
|
|
472
489
|
const result = await this.#worlds.deriveWorld({
|
|
473
490
|
...input,
|
|
474
|
-
stageTarget: async ({ targetWorldRoot, targetWorldId, binding }) => {
|
|
491
|
+
stageTarget: async ({ targetWorldRoot, targetWorldId, binding, sourceHead, }) => {
|
|
475
492
|
await this.stageForkToDerivedWorld({
|
|
476
493
|
sourceWorldId: input.sourceWorldId,
|
|
477
|
-
sourceHead
|
|
494
|
+
sourceHead,
|
|
478
495
|
targetWorldId,
|
|
479
496
|
targetWorldRoot,
|
|
480
497
|
targetBinding: binding,
|
|
498
|
+
...(input.retainCompleteTimeline
|
|
499
|
+
? { retainCompleteTimeline: true }
|
|
500
|
+
: {}),
|
|
501
|
+
...(input.identityPrefix === "import"
|
|
502
|
+
? { markImportedContexts: true }
|
|
503
|
+
: {}),
|
|
481
504
|
});
|
|
482
505
|
},
|
|
483
506
|
});
|
|
484
|
-
if (
|
|
507
|
+
if (input.targetDataRoot === undefined &&
|
|
508
|
+
input.publish !== false &&
|
|
509
|
+
(await this.inspectWorld(result.world.worldId)) === null)
|
|
485
510
|
await this.forkToDerivedWorld({
|
|
486
511
|
sourceWorldId: input.sourceWorldId,
|
|
487
512
|
sourceHead: input.sourceHead,
|
|
@@ -517,10 +542,14 @@ export class PlayCallChain {
|
|
|
517
542
|
if (source === null)
|
|
518
543
|
return null;
|
|
519
544
|
const sourceContexts = await this.#worlds.playTimeline.readAllContexts(input.sourceWorldId);
|
|
520
|
-
const selectedContextIndex =
|
|
545
|
+
const selectedContextIndex = input.retainCompleteTimeline
|
|
546
|
+
? sourceContexts.length - 1
|
|
547
|
+
: sourceContexts.findIndex(({ events }) => eventsThroughHead(events, input.sourceHead) !== null);
|
|
521
548
|
if (selectedContextIndex < 0)
|
|
522
549
|
return null;
|
|
523
|
-
const sourceEvents =
|
|
550
|
+
const sourceEvents = input.retainCompleteTimeline
|
|
551
|
+
? sourceContexts[selectedContextIndex].events
|
|
552
|
+
: eventsThroughHead(sourceContexts[selectedContextIndex].events, input.sourceHead);
|
|
524
553
|
return this.#forkSelectionToDerivedWorld({
|
|
525
554
|
sourceWorldId: input.sourceWorldId,
|
|
526
555
|
sourceContexts,
|
|
@@ -531,6 +560,8 @@ export class PlayCallChain {
|
|
|
531
560
|
branchIdentity: input.sourceHead,
|
|
532
561
|
targetWorldRoot: input.targetWorldRoot,
|
|
533
562
|
targetBinding: input.targetBinding,
|
|
563
|
+
...(input.retainCompleteTimeline ? { retainCompleteTimeline: true } : {}),
|
|
564
|
+
...(input.markImportedContexts ? { markImportedContexts: true } : {}),
|
|
534
565
|
});
|
|
535
566
|
}
|
|
536
567
|
/**
|
|
@@ -598,6 +629,9 @@ export class PlayCallChain {
|
|
|
598
629
|
if (selectedContextIndex < 0)
|
|
599
630
|
throw new PlayCallChainError("The model context to revise does not exist.");
|
|
600
631
|
const sourceContext = sourceContexts[selectedContextIndex];
|
|
632
|
+
if (input.continuation === "continue_context" &&
|
|
633
|
+
sourceContext.importedFromArchive)
|
|
634
|
+
throw new PlayCallChainError("This model context was imported from a backup; start a fresh context before continuing.");
|
|
601
635
|
const selectedEventIndex = sourceContext.events.findIndex((event) => event.id === input.eventId);
|
|
602
636
|
const selectedEvent = sourceContext.events[selectedEventIndex];
|
|
603
637
|
if (selectedEventIndex < 0 ||
|
|
@@ -640,11 +674,10 @@ export class PlayCallChain {
|
|
|
640
674
|
this.#worlds.recoverEndpoint(request.worldId, restoresHead),
|
|
641
675
|
this.#worlds.bindPlayCallChain(request.worldId),
|
|
642
676
|
]);
|
|
643
|
-
const continuedTrace = traceRetention.withReplacement(
|
|
677
|
+
const continuedTrace = traceRetention.withReplacement({
|
|
644
678
|
head: outcome.head,
|
|
645
679
|
exchangeId: request.replacementExchangeId,
|
|
646
680
|
text: request.replacementText,
|
|
647
|
-
locale: this.#compiler.locale,
|
|
648
681
|
});
|
|
649
682
|
if (binding.parentHead !== outcome.head)
|
|
650
683
|
throw new PlayCallChainError("The timeline revision was committed, but current-world materialization has not reached the new endpoint.");
|
|
@@ -666,6 +699,7 @@ export class PlayCallChain {
|
|
|
666
699
|
freshPreparation,
|
|
667
700
|
});
|
|
668
701
|
await this.#worlds.playTimeline.persist(structuredClone(revised));
|
|
702
|
+
await this.#worlds.completeArchiveFreshContext(request.worldId);
|
|
669
703
|
const activeId = this.#worldChains.get(request.worldId);
|
|
670
704
|
if (activeId !== undefined)
|
|
671
705
|
this.#active.delete(activeId);
|
|
@@ -719,6 +753,7 @@ export class PlayCallChain {
|
|
|
719
753
|
documentSnapshot: documents.snapshot,
|
|
720
754
|
additionalMaterials: structuredClone(binding.additionalMaterials),
|
|
721
755
|
history: structuredClone(binding.history),
|
|
756
|
+
historyBackgrounds: structuredClone(binding.historyBackgrounds ?? {}),
|
|
722
757
|
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
723
758
|
documentMaintenance,
|
|
724
759
|
documentMaintenanceUnavailableReason: documents.maintenanceWarning,
|
|
@@ -788,13 +823,11 @@ export class PlayCallChain {
|
|
|
788
823
|
canRetry: false,
|
|
789
824
|
bootstrap: structuredClone(fresh.bootstrap),
|
|
790
825
|
tools: structuredClone(fresh.tools),
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
checkpointAvailable: fresh.tools.some(({ name }) => name === "world_checkpoint"),
|
|
797
|
-
}),
|
|
826
|
+
narrativeOrigin: captureNarrativeOrigin(WorldDocumentStore.open({
|
|
827
|
+
layout: "world_state",
|
|
828
|
+
files: Object.entries(input.binding.files).map(([path, contents]) => ({ path, contents })),
|
|
829
|
+
}), 1),
|
|
830
|
+
transcript: playerInputAppend(request.replacementText),
|
|
798
831
|
events: [
|
|
799
832
|
{
|
|
800
833
|
id: 1,
|
|
@@ -832,22 +865,30 @@ export class PlayCallChain {
|
|
|
832
865
|
if (input.targetWorldRoot === undefined &&
|
|
833
866
|
(await this.#worlds.currentHead(input.targetWorldId)) !== input.sourceHead)
|
|
834
867
|
throw new PlayCallChainError("The forked world's Authority does not match the selected history endpoint, so the call chain cannot be written.");
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
868
|
+
let retainedTrace;
|
|
869
|
+
if (input.retainCompleteTimeline) {
|
|
870
|
+
// A backup retains the current page closure, even when its last context
|
|
871
|
+
// precedes a correction or contains an uncommitted response diagnostic.
|
|
872
|
+
retainedTrace = independentContextCopy(sourceContext);
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
const [baseline, selected] = await Promise.all([
|
|
876
|
+
this.#worlds.recoverEndpoint(input.targetWorldRoot === undefined
|
|
877
|
+
? input.targetWorldId
|
|
878
|
+
: input.sourceWorldId, sourceContext.baselineHead),
|
|
879
|
+
this.#worlds.recoverEndpoint(input.targetWorldRoot === undefined
|
|
880
|
+
? input.targetWorldId
|
|
881
|
+
: input.sourceWorldId, input.sourceHead),
|
|
882
|
+
]);
|
|
883
|
+
const traceRetention = prepareForkTrace(sourceContext, input.sourceEvents);
|
|
884
|
+
const derivedBinding = input.targetBinding ??
|
|
885
|
+
(await this.#worlds.bindPlayCallChain(input.targetWorldId));
|
|
886
|
+
retainedTrace = traceRetention.atEndpoint({
|
|
887
|
+
baseline,
|
|
888
|
+
selected,
|
|
889
|
+
binding: derivedBinding,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
851
892
|
const now = Date.now();
|
|
852
893
|
const timelineGeneration = this.#worlds.playTimeline.newGeneration();
|
|
853
894
|
let previousChainId = null;
|
|
@@ -857,6 +898,7 @@ export class PlayCallChain {
|
|
|
857
898
|
const chainId = derivedChainId(context.chainId, `prefix:${input.branchIdentity}:${index}`, input.targetWorldId);
|
|
858
899
|
const previousContext = {
|
|
859
900
|
...independentContextCopy(context),
|
|
901
|
+
...(input.markImportedContexts ? { importedFromArchive: true } : {}),
|
|
860
902
|
schemaVersion: 3,
|
|
861
903
|
kind: "play_call_chain",
|
|
862
904
|
worldId: input.targetWorldId,
|
|
@@ -873,11 +915,15 @@ export class PlayCallChain {
|
|
|
873
915
|
targetWorldRoot: input.targetWorldRoot,
|
|
874
916
|
source: context,
|
|
875
917
|
target: previousContext,
|
|
918
|
+
...(input.retainCompleteTimeline
|
|
919
|
+
? { retainCompleteContext: true }
|
|
920
|
+
: {}),
|
|
876
921
|
});
|
|
877
922
|
previousChainId = chainId;
|
|
878
923
|
}
|
|
879
924
|
const derived = {
|
|
880
925
|
...retainedTrace,
|
|
926
|
+
...(input.markImportedContexts ? { importedFromArchive: true } : {}),
|
|
881
927
|
schemaVersion: 3,
|
|
882
928
|
kind: "play_call_chain",
|
|
883
929
|
chainId: derivedChainId(sourceContext.chainId, input.branchIdentity, input.targetWorldId),
|
|
@@ -885,7 +931,9 @@ export class PlayCallChain {
|
|
|
885
931
|
previousContexts: [],
|
|
886
932
|
previousChainId,
|
|
887
933
|
timelineGeneration,
|
|
888
|
-
parentHead: input.
|
|
934
|
+
parentHead: input.retainCompleteTimeline
|
|
935
|
+
? sourceContext.parentHead
|
|
936
|
+
: input.sourceHead,
|
|
889
937
|
updatedAt: now,
|
|
890
938
|
};
|
|
891
939
|
if (input.targetWorldRoot === undefined)
|
|
@@ -896,6 +944,9 @@ export class PlayCallChain {
|
|
|
896
944
|
targetWorldRoot: input.targetWorldRoot,
|
|
897
945
|
source: sourceContext,
|
|
898
946
|
target: structuredClone(derived),
|
|
947
|
+
...(input.retainCompleteTimeline
|
|
948
|
+
? { retainCompleteContext: true }
|
|
949
|
+
: {}),
|
|
899
950
|
});
|
|
900
951
|
if (input.targetWorldRoot !== undefined) {
|
|
901
952
|
const retainedContexts = [
|
|
@@ -928,21 +979,28 @@ export class PlayCallChain {
|
|
|
928
979
|
this.#active.delete(activeId);
|
|
929
980
|
this.#worldChains.delete(worldId);
|
|
930
981
|
}
|
|
982
|
+
async #captureOrigin(session) {
|
|
983
|
+
const binding = await this.#worlds.bindPlayCallChain(session.worldId);
|
|
984
|
+
if (binding.parentHead !== session.parentHead)
|
|
985
|
+
throw new PlayCallChainError("World advanced before background capture; use a fresh context.");
|
|
986
|
+
return captureNarrativeOrigin(WorldDocumentStore.open({
|
|
987
|
+
layout: "world_state",
|
|
988
|
+
files: Object.entries(binding.files).map(([path, contents]) => ({
|
|
989
|
+
path,
|
|
990
|
+
contents,
|
|
991
|
+
})),
|
|
992
|
+
}), session.nextEventId);
|
|
993
|
+
}
|
|
931
994
|
async #submitPlayer(session, exchangeId, playerText, context, modelHost, signal, observer) {
|
|
932
995
|
if (duplicatePlayerExchange(session, exchangeId, playerText, context))
|
|
933
996
|
return projectView(session);
|
|
934
997
|
if (session.status === "running")
|
|
935
998
|
throw new PlayCallChainError("The call chain is waiting for the model to return.");
|
|
999
|
+
session.narrativeOrigin = await this.#captureOrigin(session);
|
|
936
1000
|
const exchange = session.exchange + 1;
|
|
937
1001
|
const nextRequest = createRequest(session, modelHost, exchange, [
|
|
938
1002
|
...session.transcript,
|
|
939
|
-
...playerInputAppend(
|
|
940
|
-
history: Object.fromEntries(session.history.map(({ path, contents }) => [path, contents])),
|
|
941
|
-
checkpoint: session.narrativeCheckpoint,
|
|
942
|
-
text: playerText,
|
|
943
|
-
locale: this.#compiler.locale,
|
|
944
|
-
checkpointAvailable: currentPlayPrompt(session).tools.some(({ name }) => name === "world_checkpoint"),
|
|
945
|
-
}),
|
|
1003
|
+
...playerInputAppend(playerText),
|
|
946
1004
|
]);
|
|
947
1005
|
const operationKey = `player:${exchangeId}`;
|
|
948
1006
|
const playerOperationId = operationId(session.chainId, operationKey);
|
|
@@ -950,6 +1008,7 @@ export class PlayCallChain {
|
|
|
950
1008
|
schemaVersion: 1,
|
|
951
1009
|
kind: "play_advance",
|
|
952
1010
|
advanceKind: "player",
|
|
1011
|
+
narrativeOrigin: structuredClone(session.narrativeOrigin),
|
|
953
1012
|
playContext: session.continuityContextId ?? session.chainId,
|
|
954
1013
|
worldId: session.worldId,
|
|
955
1014
|
chainId: session.chainId,
|
|
@@ -981,14 +1040,17 @@ export class PlayCallChain {
|
|
|
981
1040
|
const message = error instanceof Error
|
|
982
1041
|
? error.message
|
|
983
1042
|
: "Failed to write the original player text.";
|
|
1043
|
+
const failureKind = classifyPlayFailure(error, false);
|
|
984
1044
|
session.events.push({
|
|
985
1045
|
id: session.nextEventId++,
|
|
986
1046
|
kind: "failure",
|
|
987
1047
|
message,
|
|
1048
|
+
failureKind,
|
|
988
1049
|
});
|
|
989
1050
|
session.status = "interrupted";
|
|
990
1051
|
session.canRetry = false;
|
|
991
1052
|
session.lastFailure = message;
|
|
1053
|
+
session.lastFailureKind = failureKind;
|
|
992
1054
|
await this.#persist(session);
|
|
993
1055
|
await this.#worlds.playAdvances.markSettled(advance, session.parentHead);
|
|
994
1056
|
throw new PlayCallChainError(message);
|
|
@@ -1003,7 +1065,17 @@ export class PlayCallChain {
|
|
|
1003
1065
|
operationId: advance.operationId,
|
|
1004
1066
|
worldId: session.worldId,
|
|
1005
1067
|
parentHead: advance.parentHead,
|
|
1006
|
-
historyAppend: [
|
|
1068
|
+
historyAppend: [
|
|
1069
|
+
{
|
|
1070
|
+
role: "player",
|
|
1071
|
+
exactText: advance.playerText,
|
|
1072
|
+
...(advance.narrativeOrigin?.value == null
|
|
1073
|
+
? {}
|
|
1074
|
+
: {
|
|
1075
|
+
background: snapshotBackground(advance.narrativeOrigin.value),
|
|
1076
|
+
}),
|
|
1077
|
+
},
|
|
1078
|
+
],
|
|
1007
1079
|
nextMaterials: structuredClone(session.nextMaterials),
|
|
1008
1080
|
stateChanges: [],
|
|
1009
1081
|
});
|
|
@@ -1031,6 +1103,10 @@ export class PlayCallChain {
|
|
|
1031
1103
|
session.nextEventId = advance.eventId + 1;
|
|
1032
1104
|
session.parentHead = outcome.head;
|
|
1033
1105
|
appendCommittedHistory(session, outcome.head, outcome.historyAppend);
|
|
1106
|
+
if (advance.narrativeOrigin === undefined)
|
|
1107
|
+
delete session.narrativeOrigin;
|
|
1108
|
+
else
|
|
1109
|
+
session.narrativeOrigin = structuredClone(advance.narrativeOrigin);
|
|
1034
1110
|
session.exchange = advance.exchange;
|
|
1035
1111
|
session.lastRequest = structuredClone(advance.nextRequest);
|
|
1036
1112
|
session.lastRequestAttempt = 0;
|
|
@@ -1038,6 +1114,7 @@ export class PlayCallChain {
|
|
|
1038
1114
|
session.canRetry = true;
|
|
1039
1115
|
session.lastFailure =
|
|
1040
1116
|
"The player message is committed; the frozen model request is ready to continue.";
|
|
1117
|
+
session.lastFailureKind = "retryable";
|
|
1041
1118
|
await this.#persist(session);
|
|
1042
1119
|
crashAtPlayAdvanceEdge("after_timeline_settled");
|
|
1043
1120
|
await this.#worlds.playAdvances.markSettled(advance, outcome.head);
|
|
@@ -1167,7 +1244,11 @@ export class PlayCallChain {
|
|
|
1167
1244
|
: { providerState: response.providerState }),
|
|
1168
1245
|
toolCalls: structuredClone(calls),
|
|
1169
1246
|
};
|
|
1247
|
+
const background = visibleText
|
|
1248
|
+
? narrativeBackground(advance.narrativeOrigin, session.documents.snapshot)
|
|
1249
|
+
: undefined;
|
|
1170
1250
|
return {
|
|
1251
|
+
...(background === undefined ? {} : { background }),
|
|
1171
1252
|
assistantEvent,
|
|
1172
1253
|
playContext: session.continuityContextId ?? session.chainId,
|
|
1173
1254
|
...(worldClock === undefined || stateChanges.length === 0
|
|
@@ -1226,6 +1307,9 @@ export class PlayCallChain {
|
|
|
1226
1307
|
{
|
|
1227
1308
|
role: "narrator",
|
|
1228
1309
|
exactText: settlement.assistantEvent.text,
|
|
1310
|
+
...(settlement.background === undefined
|
|
1311
|
+
? {}
|
|
1312
|
+
: { background: settlement.background }),
|
|
1229
1313
|
},
|
|
1230
1314
|
]
|
|
1231
1315
|
: [],
|
|
@@ -1287,7 +1371,7 @@ export class PlayCallChain {
|
|
|
1287
1371
|
const binding = await this.#worlds.bindPlayCallChain(session.worldId);
|
|
1288
1372
|
session.documents = new FileNativePlayDocuments(binding.files);
|
|
1289
1373
|
session.documents.restoreAuthorizationCheckpoint(settlement.authorizationCheckpoint);
|
|
1290
|
-
session.history = historyEntries(binding.history);
|
|
1374
|
+
session.history = historyEntries(binding.history, binding.historyBackgrounds);
|
|
1291
1375
|
session.nextMaterials = structuredClone(binding.additionalMaterials);
|
|
1292
1376
|
}
|
|
1293
1377
|
mergeChangedDocuments(session, settlement.stateChanges);
|
|
@@ -1297,6 +1381,7 @@ export class PlayCallChain {
|
|
|
1297
1381
|
session.exchange = Math.max(session.exchange, advance.exchange);
|
|
1298
1382
|
session.canRetry = false;
|
|
1299
1383
|
session.lastFailure = null;
|
|
1384
|
+
delete session.lastFailureKind;
|
|
1300
1385
|
let result;
|
|
1301
1386
|
if (settlement.assistantEvent.continuation !== "available") {
|
|
1302
1387
|
const message = "The complete model response has no provider-native continuation payload; use a fresh context.";
|
|
@@ -1304,20 +1389,25 @@ export class PlayCallChain {
|
|
|
1304
1389
|
id: session.nextEventId++,
|
|
1305
1390
|
kind: "failure",
|
|
1306
1391
|
message,
|
|
1392
|
+
failureKind: "context_unavailable",
|
|
1307
1393
|
});
|
|
1308
1394
|
session.status = "interrupted";
|
|
1309
1395
|
session.lastFailure = message;
|
|
1396
|
+
session.lastFailureKind = "context_unavailable";
|
|
1310
1397
|
result = { kind: "terminal", status: "interrupted" };
|
|
1311
1398
|
}
|
|
1312
1399
|
else if (hasToolCalls) {
|
|
1313
1400
|
session.exchange += 1;
|
|
1314
1401
|
const nextRequest = createRequestWithMaxOutputTokens(session, session.lastRequest?.maxOutputTokens ?? 1, session.exchange);
|
|
1402
|
+
if (session.lastRequest?.modelConfig !== undefined)
|
|
1403
|
+
nextRequest.modelConfig = structuredClone(session.lastRequest.modelConfig);
|
|
1315
1404
|
session.lastRequest = structuredClone(nextRequest);
|
|
1316
1405
|
session.lastRequestAttempt = 0;
|
|
1317
1406
|
session.status = "interrupted";
|
|
1318
1407
|
session.canRetry = true;
|
|
1319
1408
|
session.lastFailure =
|
|
1320
1409
|
"The completed tool response is settled; its frozen continuation request is ready.";
|
|
1410
|
+
session.lastFailureKind = "retryable";
|
|
1321
1411
|
result = { kind: "continue", nextRequest };
|
|
1322
1412
|
}
|
|
1323
1413
|
else {
|
|
@@ -1328,8 +1418,10 @@ export class PlayCallChain {
|
|
|
1328
1418
|
id: session.nextEventId++,
|
|
1329
1419
|
kind: "failure",
|
|
1330
1420
|
message,
|
|
1421
|
+
failureKind: "other",
|
|
1331
1422
|
});
|
|
1332
1423
|
session.lastFailure = message;
|
|
1424
|
+
session.lastFailureKind = "other";
|
|
1333
1425
|
}
|
|
1334
1426
|
result = { kind: "terminal", status: session.status };
|
|
1335
1427
|
}
|
|
@@ -1353,7 +1445,7 @@ export class PlayCallChain {
|
|
|
1353
1445
|
return this.#settleResponseAdvance(session, advance, current.settlementPrepared.settlement);
|
|
1354
1446
|
}
|
|
1355
1447
|
async #dispatch(session, modelHost, firstRequest, firstAttempt, signal, observer) {
|
|
1356
|
-
let request = structuredClone(firstRequest);
|
|
1448
|
+
let request = modelHost.freezeRequest?.(firstRequest) ?? structuredClone(firstRequest);
|
|
1357
1449
|
let attempt = firstAttempt;
|
|
1358
1450
|
for (;;) {
|
|
1359
1451
|
this.#setInvocationAbortable(session.chainId, true);
|
|
@@ -1363,6 +1455,7 @@ export class PlayCallChain {
|
|
|
1363
1455
|
session.status = "running";
|
|
1364
1456
|
session.canRetry = false;
|
|
1365
1457
|
session.lastFailure = null;
|
|
1458
|
+
delete session.lastFailureKind;
|
|
1366
1459
|
session.lastRequest = structuredClone(request);
|
|
1367
1460
|
session.lastRequestAttempt = attempt;
|
|
1368
1461
|
const responseExchange = request.exchange ?? session.exchange;
|
|
@@ -1382,6 +1475,9 @@ export class PlayCallChain {
|
|
|
1382
1475
|
schemaVersion: 1,
|
|
1383
1476
|
kind: "play_advance",
|
|
1384
1477
|
advanceKind: "response",
|
|
1478
|
+
...(session.narrativeOrigin === undefined
|
|
1479
|
+
? {}
|
|
1480
|
+
: { narrativeOrigin: structuredClone(session.narrativeOrigin) }),
|
|
1385
1481
|
worldId: session.worldId,
|
|
1386
1482
|
chainId: session.chainId,
|
|
1387
1483
|
advanceId: responseOperationId,
|
|
@@ -1451,20 +1547,31 @@ export class PlayCallChain {
|
|
|
1451
1547
|
const message = error instanceof Error
|
|
1452
1548
|
? error.message
|
|
1453
1549
|
: "The model request was interrupted.";
|
|
1454
|
-
session.events.push({
|
|
1455
|
-
id: session.nextEventId++,
|
|
1456
|
-
kind: error instanceof ModelHostCancelledError
|
|
1457
|
-
? "cancellation"
|
|
1458
|
-
: "failure",
|
|
1459
|
-
message,
|
|
1460
|
-
});
|
|
1461
1550
|
session.status = "interrupted";
|
|
1462
|
-
const requiresFreshContext = error instanceof
|
|
1551
|
+
const requiresFreshContext = error instanceof ModelHostBindingMismatchError ||
|
|
1552
|
+
error instanceof ModelHostContinuationError ||
|
|
1463
1553
|
error instanceof ModelHostOutcomeUnknownError ||
|
|
1464
1554
|
modelHostFailureRequiresFreshContext(error);
|
|
1465
1555
|
session.canRetry =
|
|
1466
1556
|
!requiresFreshContext && error instanceof ModelHostFailureError;
|
|
1467
1557
|
session.lastFailure = message;
|
|
1558
|
+
if (error instanceof ModelHostCancelledError) {
|
|
1559
|
+
delete session.lastFailureKind;
|
|
1560
|
+
session.events.push({
|
|
1561
|
+
id: session.nextEventId++,
|
|
1562
|
+
kind: "cancellation",
|
|
1563
|
+
message,
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
session.lastFailureKind = classifyPlayFailure(error, session.canRetry);
|
|
1568
|
+
session.events.push({
|
|
1569
|
+
id: session.nextEventId++,
|
|
1570
|
+
kind: "failure",
|
|
1571
|
+
message,
|
|
1572
|
+
failureKind: session.lastFailureKind,
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1468
1575
|
await this.#persist(session);
|
|
1469
1576
|
await this.#worlds.playAdvances.markSettled(advance, session.parentHead);
|
|
1470
1577
|
this.#notifySnapshot(observer, session);
|
|
@@ -1582,14 +1689,17 @@ export class PlayCallChain {
|
|
|
1582
1689
|
},
|
|
1583
1690
|
],
|
|
1584
1691
|
});
|
|
1692
|
+
const failureKind = classifyPlayFailure(error, false);
|
|
1585
1693
|
session.events.push({
|
|
1586
1694
|
id: session.nextEventId++,
|
|
1587
1695
|
kind: "failure",
|
|
1588
1696
|
message,
|
|
1697
|
+
failureKind,
|
|
1589
1698
|
});
|
|
1590
1699
|
session.status = "interrupted";
|
|
1591
1700
|
session.canRetry = false;
|
|
1592
1701
|
session.lastFailure = message;
|
|
1702
|
+
session.lastFailureKind = failureKind;
|
|
1593
1703
|
await this.#persist(session);
|
|
1594
1704
|
this.#notifySnapshot(observer, session);
|
|
1595
1705
|
return projectView(session);
|
|
@@ -1613,6 +1723,7 @@ export class PlayCallChain {
|
|
|
1613
1723
|
session.lastRequest = structuredClone(request);
|
|
1614
1724
|
session.lastRequestAttempt = Math.max(0, attempt - 1);
|
|
1615
1725
|
session.lastFailure = message;
|
|
1726
|
+
delete session.lastFailureKind;
|
|
1616
1727
|
await this.#persist(session);
|
|
1617
1728
|
if (advance !== undefined)
|
|
1618
1729
|
await this.#worlds.playAdvances.markSettled(advance, session.parentHead);
|
|
@@ -1784,7 +1895,7 @@ export class PlayCallChain {
|
|
|
1784
1895
|
documentAuthorizationCheckpoints: persisted.documentAuthorizationCheckpoints?.map((checkpoint) => structuredClone(checkpoint)) ?? [],
|
|
1785
1896
|
nextMaterials: structuredClone(binding.additionalMaterials),
|
|
1786
1897
|
documents,
|
|
1787
|
-
history: historyEntries(binding.history),
|
|
1898
|
+
history: historyEntries(binding.history, binding.historyBackgrounds),
|
|
1788
1899
|
narrativeCheckpoint: binding.narrativeCheckpoint,
|
|
1789
1900
|
completedToolMap: new Map(persisted.completedTools.map((item) => [
|
|
1790
1901
|
item.key,
|
|
@@ -1832,14 +1943,21 @@ export class PlayCallChain {
|
|
|
1832
1943
|
: canRetry
|
|
1833
1944
|
? "The service stopped before model dispatch; the frozen request can be retried."
|
|
1834
1945
|
: "The service stopped before preparing a model request; use a fresh context.";
|
|
1946
|
+
const failureKind = dispatchMayHaveStarted
|
|
1947
|
+
? "outcome_unknown"
|
|
1948
|
+
: canRetry
|
|
1949
|
+
? "retryable"
|
|
1950
|
+
: "other";
|
|
1835
1951
|
session.events.push({
|
|
1836
1952
|
id: session.nextEventId++,
|
|
1837
1953
|
kind: "failure",
|
|
1838
1954
|
message,
|
|
1955
|
+
failureKind,
|
|
1839
1956
|
});
|
|
1840
1957
|
session.status = "interrupted";
|
|
1841
1958
|
session.canRetry = canRetry;
|
|
1842
1959
|
session.lastFailure = message;
|
|
1960
|
+
session.lastFailureKind = failureKind;
|
|
1843
1961
|
await this.#persist(session);
|
|
1844
1962
|
}
|
|
1845
1963
|
async #assertCurrentHead(session) {
|
|
@@ -1874,7 +1992,8 @@ function followupOperationId(session) {
|
|
|
1874
1992
|
return `${session.chainId}:followup:${session.parentHead}`;
|
|
1875
1993
|
}
|
|
1876
1994
|
function createRequest(session, modelHost, exchange, appended = session.transcript) {
|
|
1877
|
-
|
|
1995
|
+
const request = createRequestWithMaxOutputTokens(session, modelHost.binding().maxOutputTokens, exchange, appended);
|
|
1996
|
+
return modelHost.freezeRequest?.(request) ?? request;
|
|
1878
1997
|
}
|
|
1879
1998
|
function createRequestWithMaxOutputTokens(session, maxOutputTokens, exchange, appended = session.transcript) {
|
|
1880
1999
|
return {
|
|
@@ -1935,11 +2054,18 @@ function appendCommittedHistory(session, head, messages) {
|
|
|
1935
2054
|
const path = `message.${sequence}.${index + 1}.${message.role}`;
|
|
1936
2055
|
const existing = session.history.find((item) => item.path === path);
|
|
1937
2056
|
if (existing !== undefined) {
|
|
1938
|
-
if (existing.contents !== message.exactText
|
|
2057
|
+
if (existing.contents !== message.exactText ||
|
|
2058
|
+
!isDeepStrictEqual(existing.background, message.background))
|
|
1939
2059
|
throw new PlayCallChainError("Recovered Authority history conflicts with the call-chain history.");
|
|
1940
2060
|
continue;
|
|
1941
2061
|
}
|
|
1942
|
-
session.history.push({
|
|
2062
|
+
session.history.push({
|
|
2063
|
+
path,
|
|
2064
|
+
contents: message.exactText,
|
|
2065
|
+
...(message.background === undefined
|
|
2066
|
+
? {}
|
|
2067
|
+
: { background: structuredClone(message.background) }),
|
|
2068
|
+
});
|
|
1943
2069
|
}
|
|
1944
2070
|
}
|
|
1945
2071
|
function prepareTool(session, working, exchange, call, locale) {
|
|
@@ -2052,7 +2178,9 @@ function mergeChangedDocuments(session, changes) {
|
|
|
2052
2178
|
}
|
|
2053
2179
|
}
|
|
2054
2180
|
function projectContext(context) {
|
|
2181
|
+
const response = context.events.findLast((event) => event.kind === "assistant" && event.status === "completed");
|
|
2055
2182
|
return {
|
|
2183
|
+
...(context.importedFromArchive ? { requiresFreshContext: true } : {}),
|
|
2056
2184
|
chainId: context.chainId,
|
|
2057
2185
|
baselineHead: context.baselineHead,
|
|
2058
2186
|
...(context.baselineHistoryLength === undefined
|
|
@@ -2060,6 +2188,22 @@ function projectContext(context) {
|
|
|
2060
2188
|
: { baselineHistoryLength: context.baselineHistoryLength }),
|
|
2061
2189
|
parentHead: context.parentHead,
|
|
2062
2190
|
playPreset: structuredClone(recordedPlayPrompt(context).playPreset),
|
|
2191
|
+
...(context.modelBinding === undefined
|
|
2192
|
+
? {}
|
|
2193
|
+
: {
|
|
2194
|
+
contextWindowTokens: context.lastRequest?.modelConfig?.contextWindowTokens ??
|
|
2195
|
+
context.modelBinding.contextWindowTokens,
|
|
2196
|
+
}),
|
|
2197
|
+
...(response?.kind !== "assistant"
|
|
2198
|
+
? {}
|
|
2199
|
+
: {
|
|
2200
|
+
latestCompletedResponse: {
|
|
2201
|
+
id: response.id,
|
|
2202
|
+
...(response.usage === undefined
|
|
2203
|
+
? {}
|
|
2204
|
+
: { usage: structuredClone(response.usage) }),
|
|
2205
|
+
},
|
|
2206
|
+
}),
|
|
2063
2207
|
status: context.status,
|
|
2064
2208
|
canRetry: context.canRetry,
|
|
2065
2209
|
events: context.events
|
|
@@ -2067,6 +2211,9 @@ function projectContext(context) {
|
|
|
2067
2211
|
.map((event) => projectActivityEvent(event)),
|
|
2068
2212
|
changedDocuments: structuredClone(context.changedDocuments),
|
|
2069
2213
|
lastFailure: context.lastFailure,
|
|
2214
|
+
...(context.lastFailureKind === undefined
|
|
2215
|
+
? {}
|
|
2216
|
+
: { lastFailureKind: context.lastFailureKind }),
|
|
2070
2217
|
updatedAt: context.updatedAt,
|
|
2071
2218
|
};
|
|
2072
2219
|
}
|
|
@@ -2074,7 +2221,7 @@ function projectActivityEvent(event) {
|
|
|
2074
2221
|
if (event.kind === "player" ||
|
|
2075
2222
|
event.kind === "failure" ||
|
|
2076
2223
|
event.kind === "cancellation")
|
|
2077
|
-
return structuredClone(event);
|
|
2224
|
+
return structuredClone(projectLegacyModelBindingFailure(event));
|
|
2078
2225
|
if (event.kind === "assistant") {
|
|
2079
2226
|
const { reasoning, toolFragment, usage, ...summary } = event;
|
|
2080
2227
|
void reasoning;
|
|
@@ -2219,10 +2366,13 @@ function playerRevisionTimelineGeneration(sourceGeneration, operationId, worldId
|
|
|
2219
2366
|
.digest("hex")
|
|
2220
2367
|
.slice(0, 40)}`;
|
|
2221
2368
|
}
|
|
2222
|
-
function historyEntries(history) {
|
|
2369
|
+
function historyEntries(history, backgrounds = {}) {
|
|
2223
2370
|
return Object.entries(history).map(([path, contents]) => ({
|
|
2224
2371
|
path,
|
|
2225
2372
|
contents,
|
|
2373
|
+
...(backgrounds[path] === undefined
|
|
2374
|
+
? {}
|
|
2375
|
+
: { background: structuredClone(backgrounds[path]) }),
|
|
2226
2376
|
}));
|
|
2227
2377
|
}
|
|
2228
2378
|
function operationId(chainId, key) {
|
|
@@ -2237,7 +2387,8 @@ function assertModelHostBinding(frozen, modelHost) {
|
|
|
2237
2387
|
function assertSessionModelHostBinding(session, modelHost) {
|
|
2238
2388
|
if (session.modelBinding === undefined)
|
|
2239
2389
|
throw new PlayCallChainError("This model context predates durable provider continuation bindings; start a fresh context.");
|
|
2240
|
-
|
|
2390
|
+
if (session.modelBinding.provider !== modelHost.binding().provider)
|
|
2391
|
+
throw new PlayCallChainError("The selected model protocol differs from this context. Switch back to its original protocol to continue, or start a fresh context.", "model_protocol_mismatch");
|
|
2241
2392
|
}
|
|
2242
2393
|
function validateIdentity(value, label) {
|
|
2243
2394
|
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/u.test(value))
|