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
|
@@ -28,16 +28,7 @@ export function checkpointHistory(history, checkpoint) {
|
|
|
28
28
|
.slice(cutoff + 1)
|
|
29
29
|
.filter(([id]) => !id.includes("message.genesis"));
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
const markdown = locale === "zh-CN"
|
|
33
|
-
? `距上次检查点已完成 ${rounds} 回合。${hasCheckpoint ? "" : "当前尚无检查点,以世界起点计数。"}`
|
|
34
|
-
: `Completed player rounds since the last checkpoint: ${rounds}.${hasCheckpoint ? "" : " No checkpoint yet; counting from the world origin."}`;
|
|
35
|
-
return {
|
|
36
|
-
kind: "runtime_notice",
|
|
37
|
-
notice: "checkpoint_rounds",
|
|
38
|
-
text: `${locale === "zh-CN" ? "[Runtime 回合提示]" : "[Runtime round marker]"}\n${markdown}`,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
31
|
+
/** Retain released markers when reconstructing frozen transcript prefixes. */
|
|
41
32
|
export function isPlayerRoundMarker(item) {
|
|
42
33
|
return item?.kind === "runtime_notice" && item.notice === "checkpoint_rounds";
|
|
43
34
|
}
|
|
@@ -59,16 +50,8 @@ export function toolStepNotice(locale) {
|
|
|
59
50
|
: "[Runtime settlement]\nThe previous response called tools, so its text has not yet been displayed or committed as final story. The tools have settled as their receipts state. Use those receipts to present this turn’s still-unshown content in nonempty final prose with no tool calls, consistent with committed state. Tool settlement does not require advancing the story beyond that content.",
|
|
60
51
|
};
|
|
61
52
|
}
|
|
62
|
-
export function playerInputAppend(
|
|
63
|
-
return [
|
|
64
|
-
...(input.text.trim().length > 0 && input.checkpointAvailable
|
|
65
|
-
? [
|
|
66
|
-
playerRoundMarker(completedPlayerRounds(input.history) -
|
|
67
|
-
(input.checkpoint?.completedPlayerRounds ?? 0), input.checkpoint !== undefined, input.locale),
|
|
68
|
-
]
|
|
69
|
-
: []),
|
|
70
|
-
{ kind: "player", text: input.text },
|
|
71
|
-
];
|
|
53
|
+
export function playerInputAppend(text) {
|
|
54
|
+
return [{ kind: "player", text }];
|
|
72
55
|
}
|
|
73
56
|
export function checkpointReplayBlocks(history, checkpoint, locale, excluded = new Set(), snapshot = new HistoryQuery("checkpoint-replay", historyInputs(Object.entries(history).map(([path, contents]) => ({ path, contents }))))) {
|
|
74
57
|
const eligible = checkpointHistory(history, checkpoint);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ModelHostBindingMismatchError, ModelHostContinuationError, ModelHostFailureError, ModelHostOutcomeUnknownError, modelHostFailureRequiresFreshContext, } from "../model/ModelHost.js";
|
|
2
|
+
/** Classification does not grant permission to replay a request. */
|
|
3
|
+
export function classifyPlayFailure(error, canRetry) {
|
|
4
|
+
if (error instanceof ModelHostOutcomeUnknownError)
|
|
5
|
+
return "outcome_unknown";
|
|
6
|
+
if (error instanceof ModelHostContinuationError ||
|
|
7
|
+
error instanceof ModelHostBindingMismatchError)
|
|
8
|
+
return "context_unavailable";
|
|
9
|
+
if (error instanceof ModelHostFailureError) {
|
|
10
|
+
if (typeof error.details === "object" &&
|
|
11
|
+
error.details !== null &&
|
|
12
|
+
"code" in error.details &&
|
|
13
|
+
error.details.code === "model_output_limit")
|
|
14
|
+
return "output_limit";
|
|
15
|
+
if (modelHostFailureRequiresFreshContext(error))
|
|
16
|
+
return "context_limit";
|
|
17
|
+
return canRetry ? "retryable" : "provider_error";
|
|
18
|
+
}
|
|
19
|
+
return canRetry ? "retryable" : "other";
|
|
20
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { captureNarrativeOrigin } from "../history/HistoryBackground.js";
|
|
1
2
|
import { FileNativePlayDocuments, } from "./PlayDocumentTools.js";
|
|
2
3
|
import { currentPlayPrompt, playPromptRunsThroughEvents, } from "./PlayPromptRun.js";
|
|
3
4
|
import { isPlayerRoundMarker, playerInputAppend } from "./PlayContinuity.js";
|
|
@@ -12,7 +13,7 @@ export function preparePlayerRevisionTrace(source, eventIndex) {
|
|
|
12
13
|
const player = source.events[eventIndex];
|
|
13
14
|
return {
|
|
14
15
|
// This append precedes the operation's materialization check, as before.
|
|
15
|
-
withReplacement(
|
|
16
|
+
withReplacement(replacement) {
|
|
16
17
|
const events = [
|
|
17
18
|
...prefix,
|
|
18
19
|
{
|
|
@@ -26,16 +27,7 @@ export function preparePlayerRevisionTrace(source, eventIndex) {
|
|
|
26
27
|
];
|
|
27
28
|
const continuedTranscript = [
|
|
28
29
|
...transcript,
|
|
29
|
-
...playerInputAppend(
|
|
30
|
-
history: Object.fromEntries(selected.history.map(({ messageId, exactText }) => [
|
|
31
|
-
messageId,
|
|
32
|
-
exactText,
|
|
33
|
-
])),
|
|
34
|
-
checkpoint: selected.narrativeCheckpoint,
|
|
35
|
-
text: replacement.text,
|
|
36
|
-
locale: replacement.locale,
|
|
37
|
-
checkpointAvailable: source.tools.some(({ name }) => name === "world_checkpoint"),
|
|
38
|
-
}),
|
|
30
|
+
...playerInputAppend(replacement.text),
|
|
39
31
|
];
|
|
40
32
|
return {
|
|
41
33
|
atEndpoint(endpoint) {
|
|
@@ -93,7 +85,9 @@ function assembleRetainedTrace(source, endpoint, selection) {
|
|
|
93
85
|
? null
|
|
94
86
|
: completedToolKeys(selection.toolEvents);
|
|
95
87
|
const documents = restorePlayDocuments(endpoint.binding.files, source, selection.authorizationEvents);
|
|
88
|
+
const lastEvent = selection.events.at(-1);
|
|
96
89
|
return {
|
|
90
|
+
...(source.importedFromArchive ? { importedFromArchive: true } : {}),
|
|
97
91
|
continuityContextId: source.continuityContextId ?? source.chainId,
|
|
98
92
|
baselineHead: source.baselineHead,
|
|
99
93
|
baselineHistoryLength: source.baselineHistoryLength ?? endpoint.baseline.history.length,
|
|
@@ -111,6 +105,11 @@ function assembleRetainedTrace(source, endpoint, selection) {
|
|
|
111
105
|
...(source.modelBinding === undefined
|
|
112
106
|
? {}
|
|
113
107
|
: { modelBinding: structuredClone(source.modelBinding) }),
|
|
108
|
+
...(lastEvent?.kind === "player" || lastEvent?.kind === "tool_result"
|
|
109
|
+
? {
|
|
110
|
+
narrativeOrigin: captureNarrativeOrigin(documents.snapshot, lastEvent.kind === "player" ? lastEvent.id : lastEvent.id + 1),
|
|
111
|
+
}
|
|
112
|
+
: {}),
|
|
114
113
|
status: "ready",
|
|
115
114
|
canRetry: false,
|
|
116
115
|
bootstrap: structuredClone(source.bootstrap),
|
|
@@ -8,10 +8,12 @@ import { builtinAuthorPrompts, authoringMechanics, } from "../../shared/ordered-
|
|
|
8
8
|
import { builtinPlayPrompts, } from "../../shared/ordered-play-prompts.js";
|
|
9
9
|
import { parseOrderedPlayPrompts } from "../play/OrderedPlayPrompts.js";
|
|
10
10
|
import { renderDocumentWritePosition } from "./WorldMaintenanceReport.js";
|
|
11
|
-
import { parseDocument
|
|
11
|
+
import { parseDocument } from "yaml";
|
|
12
|
+
import { renderWorldYamlSource } from "../world/WorldYamlRendering.js";
|
|
13
|
+
export { renderWorldYamlSource } from "../world/WorldYamlRendering.js";
|
|
12
14
|
import { measureWorldMaterials, refreshMaintenanceTotals, } from "./WorldMaintenanceReport.js";
|
|
13
15
|
import { renderPlayerViewBindings } from "../world/PlayerViewBindings.js";
|
|
14
|
-
import { checkpointHistory, checkpointReplayBlocks,
|
|
16
|
+
import { checkpointHistory, checkpointReplayBlocks, } from "../play/PlayContinuity.js";
|
|
15
17
|
import { defaultAppLocale, } from "../../protocol/appPreferences.js";
|
|
16
18
|
import { WorldDocumentStore, } from "../world/WorldDocumentStore.js";
|
|
17
19
|
import { defaultRuntimeToolDefinitionStrategy, registeredRuntimeToolNames, runtimeToolsForNames, } from "./FileNativeToolRegistry.js";
|
|
@@ -52,7 +54,7 @@ ${historyUseGuidance("en")}`,
|
|
|
52
54
|
|
|
53
55
|
A nonempty tool-free response ends the model/tool loop started by the current player submission. A response that calls any tool is an intermediate step; its text has not yet been displayed or committed as final story. After all tool results, use the receipts to present this turn's still-unshown content in a later tool-free response consistent with committed state.
|
|
54
56
|
|
|
55
|
-
The player's next submission may choose a fresh context; the old model transcript will not enter that request. Fresh contexts include all committed original player inputs and final narratives after the last effective world_checkpoint, excluding tools, reasoning, and the opening. A checkpoint declaration takes effect only after the subsequent nonempty tool-free final prose successfully commits, including that prose in its boundary; further tool calls may follow registration.
|
|
57
|
+
The player's next submission may choose a fresh context; the old model transcript will not enter that request. Fresh contexts include all committed original player inputs and final narratives after the last effective world_checkpoint, excluding tools, reasoning, and the opening. A checkpoint declaration takes effect only after the subsequent nonempty tool-free final prose successfully commits, including that prose in its boundary; further tool calls may follow registration. Registration neither ends an in-world event nor switches context. Author instructions decide save timing.
|
|
56
58
|
|
|
57
59
|
Runtime executes only real tool definitions, file validation, and authority commits. This block does not define story content, point of view, style, player agency, or state semantics.
|
|
58
60
|
|
|
@@ -83,7 +85,7 @@ ${historyUseGuidance("zh-CN")}`,
|
|
|
83
85
|
|
|
84
86
|
非空且不调用工具的响应会结束本次玩家提交触发的模型/工具循环。只要响应调用了任何工具,它就是工具中间步,其中的文字尚未展示或提交为最终故事。收到全部工具结果后,根据回执在后续无工具响应中呈现本轮尚未展示的内容,并与已提交状态一致。
|
|
85
87
|
|
|
86
|
-
下一次玩家提交可以选择“全新上下文”;旧模型 transcript 不会进入那个请求。新上下文会补入最近一次已生效 world_checkpoint
|
|
88
|
+
下一次玩家提交可以选择“全新上下文”;旧模型 transcript 不会进入那个请求。新上下文会补入最近一次已生效 world_checkpoint 之后的全部已提交玩家原文与最终叙事,不含工具、推理和开场白。检查点登记仅在随后非空且无工具调用的最终正文成功提交后生效,边界包含该正文;登记后仍可继续工具调用。登记不结束世界内事件,也不自动切换上下文。具体保存时机由作者提示规定。
|
|
87
89
|
|
|
88
90
|
Runtime 只执行真实工具定义、文件校验和权威提交;本段不规定故事、人称、文风、玩家代理权或状态语义。
|
|
89
91
|
|
|
@@ -371,11 +373,11 @@ export class FileNativePromptCompiler {
|
|
|
371
373
|
const playCompilation = playPreset === undefined
|
|
372
374
|
? undefined
|
|
373
375
|
: this.compilePlayCallChain(input, playPreset);
|
|
374
|
-
return promptPreview(input, playPreset, playCompilation?.bootstrap ?? this.compileBootstrap(input), playCompilation
|
|
376
|
+
return promptPreview(input, playPreset, playCompilation?.bootstrap ?? this.compileBootstrap(input), playCompilation);
|
|
375
377
|
}
|
|
376
378
|
previewPlayPreset(input, playPreset) {
|
|
377
379
|
const playCompilation = this.compilePlayPreset(input, playPreset);
|
|
378
|
-
return promptPreview(input, playPreset, playCompilation.bootstrap, playCompilation
|
|
380
|
+
return promptPreview(input, playPreset, playCompilation.bootstrap, playCompilation);
|
|
379
381
|
}
|
|
380
382
|
/**
|
|
381
383
|
* Compile the production play surface. The selected preset contributes its
|
|
@@ -907,14 +909,7 @@ function withoutAppendedContextGenesis(input) {
|
|
|
907
909
|
},
|
|
908
910
|
};
|
|
909
911
|
}
|
|
910
|
-
function promptPreview(input, playPreset, compilation, playCompilation
|
|
911
|
-
const marker = playerInputAppend({
|
|
912
|
-
history: input.world.history ?? {},
|
|
913
|
-
checkpoint: input.world.narrativeCheckpoint,
|
|
914
|
-
text: input.playerInput,
|
|
915
|
-
locale,
|
|
916
|
-
checkpointAvailable: playCompilation?.toolUniverse.some(({ name }) => name === "world_checkpoint") ?? false,
|
|
917
|
-
})[0];
|
|
912
|
+
function promptPreview(input, playPreset, compilation, playCompilation) {
|
|
918
913
|
const result = {
|
|
919
914
|
diagnosticBinding: {
|
|
920
915
|
endpoint: input.endpoint.id,
|
|
@@ -935,17 +930,6 @@ function promptPreview(input, playPreset, compilation, playCompilation, locale =
|
|
|
935
930
|
initialAppend: {
|
|
936
931
|
logical: { kind: "player", text: input.playerInput },
|
|
937
932
|
provider: { role: "user", content: input.playerInput },
|
|
938
|
-
...(isPlayerRoundMarker(marker)
|
|
939
|
-
? {
|
|
940
|
-
beforePlayer: {
|
|
941
|
-
logical: marker,
|
|
942
|
-
provider: {
|
|
943
|
-
role: "user",
|
|
944
|
-
content: marker.text,
|
|
945
|
-
},
|
|
946
|
-
},
|
|
947
|
-
}
|
|
948
|
-
: {}),
|
|
949
933
|
},
|
|
950
934
|
}
|
|
951
935
|
: {}),
|
|
@@ -1250,45 +1234,11 @@ function renderSnapshotDocument(snapshot, selector) {
|
|
|
1250
1234
|
markdown: renderYamlDocument(read.descriptor, selected.node.value),
|
|
1251
1235
|
};
|
|
1252
1236
|
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Serialize a YAML node exactly the way context_read does, so what bootstrap
|
|
1255
|
-
* injects is what a later read would hand back. Internal document ids become
|
|
1256
|
-
* the @shortRef handles the model can actually pass to a tool.
|
|
1257
|
-
*/
|
|
1258
|
-
export function renderWorldYamlSource(value) {
|
|
1259
|
-
return stringify(promptYamlValue(value), {
|
|
1260
|
-
indent: 2,
|
|
1261
|
-
lineWidth: 0,
|
|
1262
|
-
}).trimEnd();
|
|
1263
|
-
}
|
|
1264
1237
|
function renderYamlDocument(descriptor, value) {
|
|
1265
1238
|
if (!isRecord(value))
|
|
1266
1239
|
throw new PromptCompilationError("world_document_query_failed", "YAML world-document root node must be a map");
|
|
1267
1240
|
return `## ${descriptor.title} [ref: @${descriptor.shortRef} · YAML]\n\n> ${descriptor.summary}\n\n${renderWorldYamlSource(value)}`;
|
|
1268
1241
|
}
|
|
1269
|
-
function projectedReference(value) {
|
|
1270
|
-
if (!isRecord(value) ||
|
|
1271
|
-
Object.keys(value).length !== 2 ||
|
|
1272
|
-
typeof value.$ref !== "string" ||
|
|
1273
|
-
!isRecord(value.target))
|
|
1274
|
-
return null;
|
|
1275
|
-
return typeof value.target.shortRef === "string"
|
|
1276
|
-
? { shortRef: value.target.shortRef }
|
|
1277
|
-
: null;
|
|
1278
|
-
}
|
|
1279
|
-
function promptYamlValue(value) {
|
|
1280
|
-
const reference = projectedReference(value);
|
|
1281
|
-
if (reference !== null)
|
|
1282
|
-
return { $ref: `@${reference.shortRef}` };
|
|
1283
|
-
if (Array.isArray(value))
|
|
1284
|
-
return value.map(promptYamlValue);
|
|
1285
|
-
if (isRecord(value))
|
|
1286
|
-
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
1287
|
-
key,
|
|
1288
|
-
promptYamlValue(child),
|
|
1289
|
-
]));
|
|
1290
|
-
return value;
|
|
1291
|
-
}
|
|
1292
1242
|
function throwQueryFailure(failure, operation) {
|
|
1293
1243
|
throw new PromptCompilationError("world_document_query_failed", `World-document snapshot could not complete the ${operation} query`, {
|
|
1294
1244
|
requestKind: failure.requestKind,
|
|
@@ -1310,6 +1260,9 @@ function resolveContext(input, worldFrame, snapshot, coverage, locale) {
|
|
|
1310
1260
|
const history = new HistoryQuery("prompt-compilation", historyInputs(Object.entries(input.world.history ?? {}).map(([path, contents]) => ({
|
|
1311
1261
|
path,
|
|
1312
1262
|
contents,
|
|
1263
|
+
...(input.world.historyBackgrounds?.[path] === undefined
|
|
1264
|
+
? {}
|
|
1265
|
+
: { background: input.world.historyBackgrounds[path] }),
|
|
1313
1266
|
}))));
|
|
1314
1267
|
const excluded = new Set(input.world.historyAlreadyAppended ?? []);
|
|
1315
1268
|
const checkpointEntries = input.world.replayHistory === true
|
|
@@ -239,7 +239,7 @@ function createToolDefinitions(locale) {
|
|
|
239
239
|
}
|
|
240
240
|
const toolDescriptions = {
|
|
241
241
|
en: {
|
|
242
|
-
world_checkpoint: "Register a
|
|
242
|
+
world_checkpoint: "Register a gap between events: the previous event has naturally settled at this turn's stopping point, or the scene has actually changed, suggesting that the player start a fresh context here. Arguments must be an empty object. Registration takes effect only after the subsequent nonempty tool-free final prose successfully commits, and includes that prose. A fresh context replays only committed player inputs and final narratives after this boundary, so save still-needed outcomes before registration. Consolidation and maintenance do not require this tool. Do not advance the story to register a checkpoint; registration neither switches context nor ends an in-world event. Further tool calls may follow registration; Runtime does not verify semantic completeness.",
|
|
243
243
|
state_list: "List document and state directory handles known to Runtime. parent must be @dir-/ for the state root or an @dir-* handle from the initial directory index or an earlier state_list result; no preliminary listing is needed for an already supplied handle. Catalog directories declared by the frozen world frame remain listed and usable when empty. A cursor is valid only for the same state snapshot, parent, and limit. Pass returned document handles to context_read. This tool never lists committed history.",
|
|
244
244
|
history_list: "Browse committed history on the current timeline. Omit anchors for the latest page; use only before or after with a Runtime ref (including refs in injected history) for the immediately adjacent page, excluding the anchor. Every page is oldest to newest. limit defaults to 20 (1–100 messages). Previews are only the first 120 Unicode code points. Use independent earlierCursor/laterCursor with {cursor} alone; no first-query parameters on continuation. Cursors bind world, tool, query and immutable ordered history. After history_changed rerun the query. Opening messages are queryable. Read full originals with history_read; positions are snapshot locations, not identities or turns.",
|
|
245
245
|
history_search: "Search committed originals on the current timeline with queries: 1–5 nonempty literal strings (max 256 code points each); no query alias or semantic filters. match defaults to any, or all within one message. NFKC normalization always applies; caseSensitive defaults to false (locale-und case folding), normalized duplicate queries collapse. order defaults to newest_first, or oldest_first. limit defaults to 10 (1–50 matching messages). Optional after/before refs exclude timeline boundaries independently of order; after must precede before. Each result has at most 3 partial original fragments, query occurrence counts and complete-original length. Read a hit and nearby context with history_read. Continue with {cursor} alone; history_changed requires a new query.",
|
|
@@ -253,7 +253,7 @@ const toolDescriptions = {
|
|
|
253
253
|
artifact_clear: "Clear the projection of an artifact that this follow-up declared in advance and explicitly allows to be cleared. The call cannot choose a channel, key, or any other output contract.",
|
|
254
254
|
},
|
|
255
255
|
"zh-CN": {
|
|
256
|
-
world_checkpoint: "
|
|
256
|
+
world_checkpoint: "登记事件间隙:上一事件已在本轮停笔位置自然告一段落,或场景已实际转换,建议玩家从这里开始全新上下文。参数为空对象。仅在随后非空且无工具调用的最终正文成功提交后生效,边界包含该正文;全新上下文只补入边界之后的已提交玩家原文与最终叙事,因此登记前应已保存后续仍需要的结果。整理与归并不需要调用本工具。不要为了登记而推进剧情;登记不切换上下文,也不结束世界内事件。登记后仍可继续工具调用;Runtime 不认证语义完整。",
|
|
257
257
|
state_list: "列出 Runtime 已知的文档与状态目录句柄。状态根目录的 parent 使用 @dir-/;继续下级时使用初始目录索引或 state_list 返回的 @dir-*;已有句柄无需再次列目录确认。冻结世界 frame 声明的 catalog 目录即使为空,也会继续列出并可用。cursor 只对同一状态快照、parent 和 limit 有效。返回的文档句柄交给 context_read;本工具不列出已提交历史。",
|
|
258
258
|
history_list: "浏览当前时间线已提交历史。无锚点时取最新一页;before 或 after 只能选一个,取锚点紧邻前/后的一页且不含锚点。页内始终由旧到新。limit 默认20,整数1–100。预览仅含前120个Unicode码点;完整原文用history_read。earlierCursor/laterCursor独立翻页,续页只传{cursor},不可混用首次参数。游标绑定世界、工具、查询和有序历史快照;history_changed须重新查询。开场白可查询。提示词注入中的ref可直接使用,无需再次列举;位置不是身份或回合数。",
|
|
259
259
|
history_search: "搜索当前时间线已提交原文。queries必填:1–5个非空字面词,每词最多256个Unicode码点,不接受query别名或语义筛选。match默认any,all限于同一消息。始终NFKC规范化,caseSensitive默认false(locale-und大小写折叠),规范化重复词合并。order默认newest_first,可选oldest_first;limit默认10,整数1–50条命中消息。after/before始终按时间线先后排除边界,after必须早于before,与结果排序无关。每项最多3个部分原文片段、逐词命中次数及完整原文长度;可用history_read读取前后文。续页只传{cursor};history_changed须重新查询。",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isHistoryBackground, } from "../history/HistoryBackground.js";
|
|
1
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
3
|
import { link, mkdir, open, readFile, readdir, rename, rm, writeFile, } from "node:fs/promises";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
@@ -799,7 +800,10 @@ function assertHead(value) {
|
|
|
799
800
|
}
|
|
800
801
|
function isHistoryMessage(value) {
|
|
801
802
|
if (!isRecord(value) ||
|
|
802
|
-
!hasExactKeys(value, ["messageId", "role", "exactText"]) ||
|
|
803
|
+
!hasExactKeys(value, ["messageId", "role", "exactText"], ["background"]) ||
|
|
804
|
+
(value.background !== undefined &&
|
|
805
|
+
(!isHistoryBackground(value.background) ||
|
|
806
|
+
(value.role === "player" && value.background.kind !== "snapshot"))) ||
|
|
803
807
|
typeof value.messageId !== "string" ||
|
|
804
808
|
(value.role !== "player" && value.role !== "narrator") ||
|
|
805
809
|
typeof value.exactText !== "string")
|
|
@@ -912,9 +916,8 @@ function validRelativePath(path) {
|
|
|
912
916
|
.split("/")
|
|
913
917
|
.some((part) => part === "" || part === "." || part === ".."));
|
|
914
918
|
}
|
|
915
|
-
function hasExactKeys(value, keys) {
|
|
916
|
-
return (Object.keys(value).
|
|
917
|
-
keys.every((key) => Object.hasOwn(value, key)));
|
|
919
|
+
function hasExactKeys(value, keys, optional = []) {
|
|
920
|
+
return (Object.keys(value).every((key) => keys.includes(key) || optional.includes(key)) && keys.every((key) => Object.hasOwn(value, key)));
|
|
918
921
|
}
|
|
919
922
|
function isNarrativeCheckpoint(value, effective) {
|
|
920
923
|
if (!isRecord(value) ||
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
export function historyBackgroundRecord(messages) {
|
|
3
|
+
return Object.fromEntries(messages.flatMap((message) => message.background === undefined
|
|
4
|
+
? []
|
|
5
|
+
: [[message.messageId, structuredClone(message.background)]]));
|
|
6
|
+
}
|
|
7
|
+
export function projectHistoryBackgrounds(messages) {
|
|
8
|
+
return Object.entries(historyBackgroundRecord(messages)).map(([id, background]) => ({
|
|
9
|
+
path: `${id}.json`,
|
|
10
|
+
contents: `${JSON.stringify(background)}\n`,
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
2
13
|
export function fileNativeHistoryMessageIdFromProjectionPath(path) {
|
|
3
14
|
const match = /^(\d{8})-(\d+)-(player|narrator)-[a-f0-9]{12}\.md$/u.exec(path);
|
|
4
15
|
if (match === null)
|