narraeon 0.7.7 → 0.7.10
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/authoring/AuthoringRequestSnapshot.js +14 -1
- package/dist/node/runtime/history/HistoryRendering.js +25 -6
- package/dist/node/runtime/history/HistoryTools.js +31 -0
- package/dist/node/runtime/play/PlayCallChain.js +4 -9
- package/dist/node/runtime/play/PlayDocumentTools.js +35 -37
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +33 -17
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +5 -9
- package/dist/node/runtime/setting/SettingAuthoringTransaction.js +426 -103
- package/dist/node/runtime/setting/SettingImprovementSession.js +40 -3
- package/dist/node/runtime/world/FileNativeWorldStore.js +13 -3
- package/dist/node/runtime/world/WorldDocumentMaintenance.js +9 -2
- package/dist/node/runtime/world/WorldLedgerAuthority.js +18 -2
- package/dist/node/runtime/world-revision/FileNativeWorldRevisionStore.js +61 -17
- package/dist/node/runtime/world-revision/LegacyWorldRevisionEpochUpgrade.js +46 -0
- package/dist/node/runtime/world-revision/WorldRevisionAuthoringTransaction.js +83 -30
- package/dist/node/runtime/world-revision/WorldRevisionSession.js +64 -4
- package/dist/node/runtime/world-revision/WorldRevisionWorkspace.js +32 -11
- package/dist/node/shared/default-setting-improvement-prompt.js +23 -8
- package/dist/node/shared/ordered-author-prompts.js +9 -7
- package/dist/web/assets/index-B2kbaOGe.js +1408 -0
- package/dist/web/assets/{index-BjxBYYQi.css → index-DH2BVsYd.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-Da0tdDij.js +0 -1408
|
@@ -5,9 +5,11 @@ import { appendAuthoringUserMessage, ModelToolConversation, rememberAuthoringReq
|
|
|
5
5
|
import { contentTreeFingerprint } from "../content/ContentTreeFingerprint.js";
|
|
6
6
|
import { equalModelHostBinding } from "../model/ModelHost.js";
|
|
7
7
|
import { settingImprovementPromptForBinding, } from "../play/FileNativePlayPresetStore.js";
|
|
8
|
+
import { authoringPreloadAuthorization, buildAuthoringPreload, worldRevisionAuthoringSurface, } from "../setting/SettingAuthoringTransaction.js";
|
|
8
9
|
import { WorldDocumentStore } from "../world/WorldDocumentStore.js";
|
|
9
10
|
import { WorldRevisionAuthoringTransaction, worldRevisionRuntimeContract, worldRevisionToolDefinitions, } from "./WorldRevisionAuthoringTransaction.js";
|
|
10
11
|
import { WorldRevisionWorkspaceError } from "./WorldRevisionWorkspace.js";
|
|
12
|
+
import { historyEntries } from "../history/HistoryTools.js";
|
|
11
13
|
/** Conversation orchestration over the shared locked revision worktree. */
|
|
12
14
|
export class WorldRevisionSession {
|
|
13
15
|
#store;
|
|
@@ -197,7 +199,7 @@ export class WorldRevisionSession {
|
|
|
197
199
|
const host = await this.#bindExistingModelHost(session.modelBinding);
|
|
198
200
|
if (!equalModelHostBinding(session.modelBinding, host.binding()))
|
|
199
201
|
throw new Error("No saved model connection matches this world-revision conversation");
|
|
200
|
-
const { playPreset, bootstrap } = await this.#compileCurrent(input.worldId, session.modelBinding);
|
|
202
|
+
const { playPreset, bootstrap, preload } = await this.#compileCurrent(input.worldId, session.modelBinding);
|
|
201
203
|
session.schemaVersion = 2;
|
|
202
204
|
session.requests ??= [];
|
|
203
205
|
session.requests.push({
|
|
@@ -205,7 +207,12 @@ export class WorldRevisionSession {
|
|
|
205
207
|
modelItemStart: session.modelItems.length,
|
|
206
208
|
bootstrap,
|
|
207
209
|
playPreset: structuredClone(playPreset),
|
|
210
|
+
...(preload === null
|
|
211
|
+
? {}
|
|
212
|
+
: { preload: structuredClone(preload.record) }),
|
|
208
213
|
});
|
|
214
|
+
if (preload !== null)
|
|
215
|
+
session.authorization = authoringPreloadAuthorization(session.authorization, preload.record);
|
|
209
216
|
appendAuthoringUserMessage(session, message, requestId);
|
|
210
217
|
await this.#store.saveSession(session);
|
|
211
218
|
return {
|
|
@@ -262,6 +269,9 @@ export class WorldRevisionSession {
|
|
|
262
269
|
const playPreset = await this.#bindPlayPreset();
|
|
263
270
|
const locale = this.#locale();
|
|
264
271
|
const gameplay = await this.#worlds.gameplay(worldId);
|
|
272
|
+
const preload = epoch === null
|
|
273
|
+
? null
|
|
274
|
+
: await this.#preload(epoch, modelBinding, playPreset, locale);
|
|
265
275
|
const bootstrap = this.#compiler.compileWorldRevision({
|
|
266
276
|
...(epoch === null
|
|
267
277
|
? {}
|
|
@@ -273,8 +283,54 @@ export class WorldRevisionSession {
|
|
|
273
283
|
playPreset,
|
|
274
284
|
modelBinding: modelBinding,
|
|
275
285
|
tools: worldRevisionToolDefinitions(locale, gameplay),
|
|
286
|
+
...(preload === null ? {} : { preload: preload.blocks }),
|
|
287
|
+
});
|
|
288
|
+
return { bootstrap, playPreset, preload };
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Preloads the control documents and the documents the world frame injects.
|
|
292
|
+
* A preview that cannot be built degrades to the directory and control files.
|
|
293
|
+
*/
|
|
294
|
+
async #preload(epoch, modelBinding, playPreset, locale) {
|
|
295
|
+
const [binding, maintenance] = await Promise.all([
|
|
296
|
+
this.#worlds.bindWorldRevision({
|
|
297
|
+
worldId: epoch.worldId,
|
|
298
|
+
epochId: epoch.epochId,
|
|
299
|
+
}),
|
|
300
|
+
this.#worlds.inspectDocumentMaintenance(epoch.worldId, epoch.baseHead),
|
|
301
|
+
]);
|
|
302
|
+
const snapshot = WorldDocumentStore.open({
|
|
303
|
+
layout: "world_state",
|
|
304
|
+
files: epoch.files,
|
|
305
|
+
});
|
|
306
|
+
let injected = [];
|
|
307
|
+
let injectedUnavailable;
|
|
308
|
+
try {
|
|
309
|
+
const preview = this.#preview({
|
|
310
|
+
snapshot,
|
|
311
|
+
modelBinding,
|
|
312
|
+
playPreset,
|
|
313
|
+
binding,
|
|
314
|
+
epoch,
|
|
315
|
+
maintenance,
|
|
316
|
+
});
|
|
317
|
+
injected = preview.compilation.coverage.flatMap(({ slot, status, readAuthorization }) => status === "resolved" && readAuthorization !== undefined
|
|
318
|
+
? [{ shortRef: readAuthorization.shortRef, slot }]
|
|
319
|
+
: []);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
injectedUnavailable =
|
|
323
|
+
error instanceof Error ? error.message : "unknown preview failure";
|
|
324
|
+
}
|
|
325
|
+
return buildAuthoringPreload({
|
|
326
|
+
snapshot,
|
|
327
|
+
surface: worldRevisionAuthoringSurface,
|
|
328
|
+
locale,
|
|
329
|
+
revision: epoch.revision,
|
|
330
|
+
sourcePrefix: "world-revision:preload",
|
|
331
|
+
injected,
|
|
332
|
+
...(injectedUnavailable === undefined ? {} : { injectedUnavailable }),
|
|
276
333
|
});
|
|
277
|
-
return { bootstrap, playPreset };
|
|
278
334
|
}
|
|
279
335
|
async cancel(sessionId) {
|
|
280
336
|
const running = this.#conversation.cancel(sessionId);
|
|
@@ -408,6 +464,10 @@ export class WorldRevisionSession {
|
|
|
408
464
|
return new WorldRevisionAuthoringTransaction({
|
|
409
465
|
baseFiles: epoch.files,
|
|
410
466
|
immutableBaseFiles: epoch.baseFiles,
|
|
467
|
+
history: {
|
|
468
|
+
scope: epoch.worldId,
|
|
469
|
+
entries: historyEntries(binding.history, binding.historyBackgrounds),
|
|
470
|
+
},
|
|
411
471
|
locale: session.locale,
|
|
412
472
|
revision: epoch.revision,
|
|
413
473
|
authorization: session.authorization,
|
|
@@ -565,8 +625,8 @@ export class WorldRevisionSession {
|
|
|
565
625
|
}
|
|
566
626
|
function retargetSession(session, epoch, locale) {
|
|
567
627
|
const markdown = locale === "zh-CN"
|
|
568
|
-
? `# 新的世界修订 epoch\n\n上一轮修订已经应用或放弃。当前工作树是 ${epoch.epochId},从世界端点 ${epoch.baseHead} 重新建立。此前全部
|
|
569
|
-
: `# New world-revision epoch\n\nThe previous revision was applied or discarded. Worktree ${epoch.epochId} was rebuilt from world endpoint ${epoch.baseHead}. Every prior
|
|
628
|
+
? `# 新的世界修订 epoch\n\n上一轮修订已经应用或放弃。当前工作树是 ${epoch.epochId},从世界端点 ${epoch.baseHead} 重新建立。此前全部 search cursor 和 read 写授权已失效;修改前必须重新读取。`
|
|
629
|
+
: `# New world-revision epoch\n\nThe previous revision was applied or discarded. Worktree ${epoch.epochId} was rebuilt from world endpoint ${epoch.baseHead}. Every prior search cursor and read authorization is invalid; re-read before writing.`;
|
|
570
630
|
session.modelItems.push({
|
|
571
631
|
kind: "prompt_delta",
|
|
572
632
|
logicalMessages: [
|
|
@@ -353,18 +353,21 @@ function assertExpectedRevision(epoch, expected) {
|
|
|
353
353
|
throw new WorldRevisionWorkspaceError("The world-revision worktree changed; refresh it before writing");
|
|
354
354
|
}
|
|
355
355
|
function stateChanges(baseFiles, files) {
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
356
|
+
const baseStateFiles = baseFiles.filter(({ path }) => path.startsWith("state/"));
|
|
357
|
+
const base = new Map(baseStateFiles.map((file) => [file.path, file]));
|
|
358
|
+
const candidateStateFiles = files.filter(({ path }) => path.startsWith("state/"));
|
|
359
359
|
const snapshot = WorldDocumentStore.open({
|
|
360
360
|
layout: "world_state",
|
|
361
|
-
files:
|
|
361
|
+
files: candidateStateFiles,
|
|
362
362
|
});
|
|
363
363
|
if (snapshot.status !== "usable")
|
|
364
364
|
throw new WorldRevisionWorkspaceError("Only a usable world-state snapshot can be applied");
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
365
|
+
const baseSnapshot = WorldDocumentStore.open({
|
|
366
|
+
layout: "world_state",
|
|
367
|
+
files: baseStateFiles,
|
|
368
|
+
});
|
|
369
|
+
const candidatePaths = new Set(candidateStateFiles.map(({ path }) => path));
|
|
370
|
+
const changes = candidateStateFiles.flatMap((file) => {
|
|
368
371
|
const previous = base.get(file.path);
|
|
369
372
|
if (previous?.contents === file.contents)
|
|
370
373
|
return [];
|
|
@@ -375,10 +378,6 @@ function stateChanges(baseFiles, files) {
|
|
|
375
378
|
if (result.kind !== "read_document" || !result.ok)
|
|
376
379
|
throw new WorldRevisionWorkspaceError(`Cannot resolve changed world document: ${file.path}`);
|
|
377
380
|
if (previous !== undefined) {
|
|
378
|
-
const baseSnapshot = WorldDocumentStore.open({
|
|
379
|
-
layout: "world_state",
|
|
380
|
-
files: baseFiles.filter(({ path }) => path.startsWith("state/")),
|
|
381
|
-
});
|
|
382
381
|
const before = baseSnapshot.query({
|
|
383
382
|
kind: "read_document",
|
|
384
383
|
document: { logicalPath: file.path },
|
|
@@ -401,6 +400,28 @@ function stateChanges(baseFiles, files) {
|
|
|
401
400
|
},
|
|
402
401
|
];
|
|
403
402
|
});
|
|
403
|
+
// A base document whose path is gone was deleted or moved away; a move's new path is a create above.
|
|
404
|
+
const removals = baseStateFiles
|
|
405
|
+
.filter(({ path }) => !candidatePaths.has(path))
|
|
406
|
+
.map((file) => {
|
|
407
|
+
const before = baseSnapshot.query({
|
|
408
|
+
kind: "read_document",
|
|
409
|
+
document: { logicalPath: file.path },
|
|
410
|
+
});
|
|
411
|
+
if (before.kind !== "read_document" || !before.ok)
|
|
412
|
+
throw new WorldRevisionWorkspaceError(`Cannot resolve removed world document: ${file.path}`);
|
|
413
|
+
return {
|
|
414
|
+
kind: "delete",
|
|
415
|
+
documentId: before.document.documentId,
|
|
416
|
+
stableShortRef: before.document.shortRef,
|
|
417
|
+
relativePath: file.path.slice("state/".length),
|
|
418
|
+
codec: before.document.codec,
|
|
419
|
+
expectedPreviousHash: sha256(file.contents),
|
|
420
|
+
nextHash: null,
|
|
421
|
+
canonicalNextBytes: null,
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
return [...changes, ...removals];
|
|
404
425
|
}
|
|
405
426
|
function controlFiles(files) {
|
|
406
427
|
return files
|
|
@@ -7,17 +7,22 @@
|
|
|
7
7
|
* mechanical setting-improvement boundary.
|
|
8
8
|
*/
|
|
9
9
|
export const defaultSettingImprovementPromptPath = "prompts/setting-improvement.md";
|
|
10
|
+
/** Content-package-only paragraphs; a running-world revision omits them. */
|
|
11
|
+
const contentPackageWorldStartZhCN = "内容包设定完善定义开场结束处的世界起点,可以包含已有历史、当前状态、持续过程和各人的认知。简单起点足以开玩,普通未定义内容可在游玩中生长;不预写未来必然发生的经历或强制剧情阶段。";
|
|
12
|
+
const contentPackageOpeningZhCN = "仅在内容包分支检查 opening.md:写玩家当时可感知的具体局面,保留尚待回答的请求与必要原话,不替玩家决定行动、台词或内心。独处、安静和等待也可形成开场。开场与文档停在同一时点,将约束首次行动的结果写给自然所有者,无需复制整篇叙事。";
|
|
13
|
+
const contentPackageWorldStartEn = "Content-package setting improvement defines the world where the opening ends, including established history, current state, ongoing processes and individual knowledge. A simple starting point can be playable and ordinary undefined content can grow during play. Do not prewrite inevitable future experiences or mandatory plot stages.";
|
|
14
|
+
const contentPackageOpeningEn = "Inspect opening.md only in the content-package branch. Show a concrete situation the player can perceive, retaining unanswered requests and consequential wording without choosing player action, speech or inner thoughts. Solitude, quiet and waiting can also open a story. Documents and opening stop at the same moment; give outcomes constraining the first action to natural owners without copying the whole narrative.";
|
|
10
15
|
export const defaultSettingImprovementPromptZhCN = `# 系统推荐的创作方法
|
|
11
16
|
|
|
12
17
|
以当前编辑目标、已有内容和用户目标为起点。讨论时说明必要判断,落实时定向修改;用户要求审查或重组时,相关结构与重复表达也在范围内。不为填满结构制造内容,无需修改也是合法结果。
|
|
13
18
|
|
|
14
19
|
## 编辑目标与世界起点
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
${contentPackageWorldStartZhCN}
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
运行中世界修订维护当前已经演变的世界,按用户要求调整并保留未受影响的事实、经历与认知,不退回初始取值。明确修订不是世界内事件,不需虚构行动解释它。
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
涉及结构、材料发现或更新位置时,以本请求 bootstrap 已提供的目录、控制文档与注入文档为起点;未提供而受影响的文档再用工具读取。实际操作遵守本轮工具边界。
|
|
21
26
|
|
|
22
27
|
## 世界信息怎样组织
|
|
23
28
|
|
|
@@ -41,7 +46,7 @@ world/ 或 state/ 承载虚构事实,control/ 安排本世界的取材、规
|
|
|
41
46
|
|
|
42
47
|
## 开场与整理
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
${contentPackageOpeningZhCN}
|
|
45
50
|
|
|
46
51
|
整理结束已完成或失效的进程、约定和限制,保留仍有意义的成果、后遗影响、经历与引用。补录、去重和归档不让事情再发生。长材料按需发现,软体积提示不授权截断;暂时离场不等于退役,退役也不会移除显式全文槽。
|
|
47
52
|
|
|
@@ -53,11 +58,11 @@ Start with the editing target, existing content and the user's goal. Discuss nec
|
|
|
53
58
|
|
|
54
59
|
## Editing target and world starting point
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
${contentPackageWorldStartEn}
|
|
57
62
|
|
|
58
|
-
Running-world revision maintains the world as it has evolved: apply requested changes while preserving unaffected facts, events and knowledge instead of resetting initial values.
|
|
63
|
+
Running-world revision maintains the world as it has evolved: apply requested changes while preserving unaffected facts, events and knowledge instead of resetting initial values. Explicit revision is not an in-world event and needs no invented action to explain it.
|
|
59
64
|
|
|
60
|
-
When structure, discovery or update locations are affected,
|
|
65
|
+
When structure, discovery or update locations are affected, start from the directory, control documents and injected documents this request's bootstrap already provides; read affected documents with tools only when they were not provided. Follow this request's actual tool boundary.
|
|
61
66
|
|
|
62
67
|
## Organizing world information
|
|
63
68
|
|
|
@@ -81,7 +86,7 @@ world/ or state/ carries fictional facts. control/ arranges world-specific mater
|
|
|
81
86
|
|
|
82
87
|
## Opening and consolidation
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
${contentPackageOpeningEn}
|
|
85
90
|
|
|
86
91
|
Consolidation closes completed or expired processes, commitments and restrictions while retaining meaningful results, lasting effects, events and references. Recording, deduplication and archiving do not perform events again. Keep long material discoverable on demand; advisory size limits do not authorize truncation. Temporary absence is not retirement, and retirement does not remove explicit full-document slots.
|
|
87
92
|
|
|
@@ -93,3 +98,13 @@ export function defaultSettingImprovementPromptForLocale(locale) {
|
|
|
93
98
|
? defaultSettingImprovementPromptZhCN
|
|
94
99
|
: defaultSettingImprovementPromptEn;
|
|
95
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* The same guidance for a running-world revision: the content-package-only
|
|
103
|
+
* paragraphs are omitted, and everything else stays byte-identical.
|
|
104
|
+
*/
|
|
105
|
+
export function revisionSettingImprovementPromptForLocale(locale) {
|
|
106
|
+
const paragraphs = locale === "zh-CN"
|
|
107
|
+
? [contentPackageWorldStartZhCN, contentPackageOpeningZhCN]
|
|
108
|
+
: [contentPackageWorldStartEn, contentPackageOpeningEn];
|
|
109
|
+
return paragraphs.reduce((text, paragraph) => text.replace(`${paragraph}\n\n`, ""), defaultSettingImprovementPromptForLocale(locale));
|
|
110
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defaultSettingImprovementPromptForLocale } from "./default-setting-improvement-prompt.js";
|
|
1
|
+
import { defaultSettingImprovementPromptForLocale, revisionSettingImprovementPromptForLocale, } from "./default-setting-improvement-prompt.js";
|
|
2
2
|
import { tabletopAuthorBody } from "./tabletop-guidance.js";
|
|
3
3
|
/** Authoring uses the same permissions as play, with target-specific dynamic references. */
|
|
4
|
-
export function builtinAuthorPrompts(locale) {
|
|
4
|
+
export function builtinAuthorPrompts(locale, target = "setting") {
|
|
5
5
|
const zh = locale === "zh-CN";
|
|
6
6
|
return [
|
|
7
7
|
{
|
|
@@ -18,7 +18,9 @@ export function builtinAuthorPrompts(locale) {
|
|
|
18
18
|
name: zh ? "创作建议" : "Authoring guidance",
|
|
19
19
|
required: false,
|
|
20
20
|
category: "policy",
|
|
21
|
-
body:
|
|
21
|
+
body: target === "world-revision"
|
|
22
|
+
? revisionSettingImprovementPromptForLocale(locale)
|
|
23
|
+
: defaultSettingImprovementPromptForLocale(locale),
|
|
22
24
|
},
|
|
23
25
|
{
|
|
24
26
|
id: "author.tabletop",
|
|
@@ -53,15 +55,15 @@ export function builtinAuthorPrompts(locale) {
|
|
|
53
55
|
export function authoringMechanics(locale, target) {
|
|
54
56
|
const zh = locale === "zh-CN";
|
|
55
57
|
const common = zh
|
|
56
|
-
? `只使用请求随附的工具及其参数契约。含工具调用的响应是中间步;收到全部工具结果后继续,非空且无工具调用的响应结束本次发送。完整工具响应按调用顺序结算,失败调用不撤销成功同级调用。修改既有文件前必须完整读取;cursor 只属于产生它的树 revision;本事务自身成功修改后的完整读取授权由 Runtime 承接。外部树变化或跨 epoch
|
|
57
|
-
: `Use only the attached tools and their parameter contracts. Responses with tool calls are intermediate steps; continue after all results. A nonempty tool-free response ends this send. Complete tool responses settle calls in order; a failed call does not undo successful siblings. Completely read existing files before writing. Cursors belong to their producing tree revision. Runtime carries complete-read authorization through this transaction’s own successful changes. External tree changes or an epoch transition invalidate old authorization and require new reads. Tools expose logical paths, never host files. Use returned @refs for documents; YAML whole-document references are single-key {$ref: "@ref"} maps, and locators use map keys or zero-based array indexes. Actual receipts determine current values; model prose cannot commit changes.`;
|
|
58
|
+
? `只使用请求随附的工具及其参数契约。含工具调用的响应是中间步;收到全部工具结果后继续,非空且无工具调用的响应结束本次发送。完整工具响应按调用顺序结算,失败调用不撤销成功同级调用。修改既有文件前必须完整读取;cursor 只属于产生它的树 revision;本事务自身成功修改后的完整读取授权由 Runtime 承接。外部树变化或跨 epoch 后旧授权失效,需要重新读取。bootstrap 中 Runtime 预载的完整文档视为已在该 revision 完整读取,可直接修改;目录不算读取。工具返回逻辑路径,不能访问宿主文件。文档引用使用返回的 @ref;YAML 整文档引用为单键 {$ref: "@ref"},locator 使用 map key 或从零开始的数组下标。成功回执决定当前值,模型文字不构成提交。`
|
|
59
|
+
: `Use only the attached tools and their parameter contracts. Responses with tool calls are intermediate steps; continue after all results. A nonempty tool-free response ends this send. Complete tool responses settle calls in order; a failed call does not undo successful siblings. Completely read existing files before writing. Cursors belong to their producing tree revision. Runtime carries complete-read authorization through this transaction’s own successful changes. External tree changes or an epoch transition invalidate old authorization and require new reads. Runtime-preloaded complete documents in the bootstrap count as completely read at that revision; the directory does not. Tools expose logical paths, never host files. Use returned @refs for documents; YAML whole-document references are single-key {$ref: "@ref"} maps, and locators use map keys or zero-based array indexes. Actual receipts determine current values; model prose cannot commit changes.`;
|
|
58
60
|
const boundary = target === "setting"
|
|
59
61
|
? zh
|
|
60
62
|
? "内容包工具的成功变化在完整响应结算时原子发布到当前树,无需应用。setting_create 建立新文档身份;删除仅在无绑定与引用阻挡时接受。"
|
|
61
63
|
: "Successful content-package changes publish atomically to the current tree when the complete response settles, with no Apply step. setting_create establishes new document identities; deletion is accepted only without binding or reference blockers."
|
|
62
64
|
: zh
|
|
63
|
-
? "世界修订工具只修改持久独占 epoch 的 state/* 与 control/* 工作树。opening.md
|
|
64
|
-
: "World-revision tools modify only state/* and control/* in the durably locked exclusive epoch. opening.md cannot be read or changed; existing state documents
|
|
65
|
+
? "世界修订工具只修改持久独占 epoch 的 state/* 与 control/* 工作树。opening.md 不可读取或修改;既有状态文档可以删除或移动,删除须无绑定与引用阻挡。只有玩家应用或放弃才结束 epoch;应用提交世界,放弃不改变世界。跨 epoch 全部旧读取授权失效。世界修订可用 history_list、history_search、history_read 只读导航已提交历史,历史不能被改写。"
|
|
66
|
+
: "World-revision tools modify only state/* and control/* in the durably locked exclusive epoch. opening.md cannot be read or changed; existing state documents can be deleted or moved, and deletion requires no binding or reference blockers. Only player Apply or Discard ends the epoch; Apply commits the world, Discard leaves it unchanged. Every old read authorization expires across epochs. World revision may read committed history with history_list, history_search and history_read; history cannot be rewritten.";
|
|
65
67
|
const views = zh
|
|
66
68
|
? 'control/player-views.yaml 的 format 为 narraeon.player-views/v1;views 中每项有 id、title 和 items,items 中每项有 id、label、select。精确选择一个 YAML 字段使用 select: {document: "@ref", locator: {yaml: [字段名]}};Markdown 小节使用 locator: {markdown: [标题]}。document 必须来自实际返回的引用,locator 必须对应已读正文;省略 locator 会展示整份文档,选择容器会递归展示子树。'
|
|
67
69
|
: 'control/player-views.yaml uses format: narraeon.player-views/v1. Each views entry has id, title and items; each item has id, label and select. Select one YAML field with select: {document: "@ref", locator: {yaml: [field]}}; select a Markdown section with locator: {markdown: [heading]}. Use a returned document reference and a locator from the actual read body. Omitting locator displays the whole document; selecting a container recursively displays its subtree.';
|