narraeon 0.6.3 → 0.7.1
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/V1Runtime.js +3 -0
- package/dist/node/runtime/content/FileNativeContentTree.js +15 -7
- package/dist/node/runtime/play/FileNativePlayPresetStore.js +38 -4
- package/dist/node/runtime/play/OrderedPlayPrompts.js +204 -79
- package/dist/node/runtime/play/PlayCallChain.js +20 -3
- package/dist/node/runtime/play/PlayContinuity.js +10 -2
- package/dist/node/runtime/prompt/FileNativePromptCompiler.js +124 -13
- package/dist/node/runtime/prompt/FileNativeToolRegistry.js +5 -6
- package/dist/node/runtime/prompt/PromptCompilationCodec.js +13 -1
- package/dist/node/runtime/setting/FileNativeSettingImprovementStore.js +15 -10
- package/dist/node/runtime/setting/SettingAuthoringTransaction.js +2 -2
- package/dist/node/shared/default-play-prompts.js +21 -65
- package/dist/node/shared/default-preset-host.js +317 -224
- package/dist/node/shared/ordered-author-prompts.js +5 -0
- package/dist/node/shared/ordered-play-prompts.js +195 -66
- package/dist/node/shared/turn-guide.js +26 -0
- package/dist/web/assets/index-BUCslLEK.css +1 -0
- package/dist/web/assets/index-DkamBH6c.js +1408 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/index-BZwaOsvk.js +0 -1393
- package/dist/web/assets/index-eUMdHbq2.css +0 -1
|
@@ -10,18 +10,21 @@ export function builtinAuthorPrompts(locale) {
|
|
|
10
10
|
? "创作工具与结算(必选)"
|
|
11
11
|
: "Authoring tools and settlement (required)",
|
|
12
12
|
required: true,
|
|
13
|
+
category: "mechanics",
|
|
13
14
|
body: authoringMechanics(locale, "setting"),
|
|
14
15
|
},
|
|
15
16
|
{
|
|
16
17
|
id: "author.guidance",
|
|
17
18
|
name: zh ? "创作建议" : "Authoring guidance",
|
|
18
19
|
required: false,
|
|
20
|
+
category: "policy",
|
|
19
21
|
body: defaultSettingImprovementPromptForLocale(locale),
|
|
20
22
|
},
|
|
21
23
|
{
|
|
22
24
|
id: "author.tabletop",
|
|
23
25
|
name: zh ? "跑团创作建议" : "Tabletop authoring guidance",
|
|
24
26
|
required: false,
|
|
27
|
+
category: "policy",
|
|
25
28
|
body: tabletopAuthorBody(locale),
|
|
26
29
|
gameplay: "tabletop",
|
|
27
30
|
},
|
|
@@ -31,6 +34,7 @@ export function builtinAuthorPrompts(locale) {
|
|
|
31
34
|
? "创作目标与读取授权(动态必选)"
|
|
32
35
|
: "Target and read authorization (required, dynamic)",
|
|
33
36
|
required: true,
|
|
37
|
+
category: "mechanics",
|
|
34
38
|
body: zh
|
|
35
39
|
? "实际请求在此显示内容包或修订世界身份及当前树边界。世界修订保留独占 epoch;跨 epoch 继续时在用户消息前显式通知并清空读取授权。文件正文只通过工具按需读取,不预注入整棵树。"
|
|
36
40
|
: "The actual request shows the content package or revision world identity and current-tree boundary here. World revision retains its exclusive epoch; continuing across epochs announces the transition before the user message and clears read authorization. Files are read on demand through tools, never injected as a whole tree.",
|
|
@@ -39,6 +43,7 @@ export function builtinAuthorPrompts(locale) {
|
|
|
39
43
|
id: "author.play-reference",
|
|
40
44
|
name: zh ? "未来游玩参考(动态)" : "Future play reference (dynamic)",
|
|
41
45
|
required: false,
|
|
46
|
+
category: "policy",
|
|
42
47
|
body: zh
|
|
43
48
|
? "在此展开当前预设已启用的游玩编排,明确标为未来游玩参考,不是本轮叙事要求。内容包及世界正文仍通过创作工具读取。"
|
|
44
49
|
: "Expands the current preset's enabled play arrangement as future-play reference, not narrative instructions for this conversation. Actual package and world content remains behind authoring tools.",
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { historyUseGuidance } from "./history-use-guidance.js";
|
|
2
|
-
import { defaultNarrationPromptForLocale } from "./default-play-prompts.js";
|
|
3
2
|
import { defaultPresetHostFilesForLocale } from "./default-preset-host.js";
|
|
4
3
|
import { tabletopPlayBody } from "./tabletop-guidance.js";
|
|
5
4
|
export function isPromptMessageRole(value) {
|
|
6
5
|
return value === "system" || value === "user" || value === "assistant";
|
|
7
6
|
}
|
|
7
|
+
export const promptGroups = [
|
|
8
|
+
"person",
|
|
9
|
+
"continue",
|
|
10
|
+
"agency",
|
|
11
|
+
"stop",
|
|
12
|
+
];
|
|
13
|
+
export function isPromptGroup(value) {
|
|
14
|
+
return promptGroups.includes(value);
|
|
15
|
+
}
|
|
16
|
+
/** Soft limits for the turn guide; exceeding them warns without rejecting. */
|
|
17
|
+
export const turnGuideLineSoftLimit = 120;
|
|
18
|
+
export const turnGuideLinesSoftLimit = 10;
|
|
8
19
|
/**
|
|
9
20
|
* Unscoped prompts always apply. A scoped prompt applies only when the compiled
|
|
10
21
|
* world or package is tabletop, so fiction requests never see it.
|
|
@@ -20,10 +31,57 @@ export function orderedPromptScope(entry, catalog) {
|
|
|
20
31
|
return catalog.find((item) => item.id === entry.builtin)?.gameplay;
|
|
21
32
|
return undefined;
|
|
22
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* The turn-guide line an entry contributes: the entry's own line, or the
|
|
36
|
+
* catalog default for a builtin without an override. Blank lines contribute
|
|
37
|
+
* nothing; the world placeholder's lines come from the world frame.
|
|
38
|
+
*/
|
|
39
|
+
export function orderedPromptTurnGuide(entry, catalog) {
|
|
40
|
+
const line = entry.kind === "tail"
|
|
41
|
+
? entry.body
|
|
42
|
+
: entry.kind === "builtin"
|
|
43
|
+
? (entry.turnGuide ??
|
|
44
|
+
catalog.find((item) => item.id === entry.builtin)?.turnGuide)
|
|
45
|
+
: entry.kind === "user"
|
|
46
|
+
? entry.turnGuide
|
|
47
|
+
: undefined;
|
|
48
|
+
const trimmed = line?.trim();
|
|
49
|
+
return trimmed === undefined || trimmed === "" ? undefined : trimmed;
|
|
50
|
+
}
|
|
51
|
+
function heading(body, fallback) {
|
|
52
|
+
return /^#\s+(.+)$/mu.exec(body)?.[1] ?? fallback;
|
|
53
|
+
}
|
|
23
54
|
/** Application-owned references. Persist references; freeze resolved text in requests. */
|
|
24
55
|
export function builtinPlayPrompts(locale) {
|
|
25
56
|
const zh = locale === "zh-CN";
|
|
26
57
|
const host = defaultPresetHostFilesForLocale(locale);
|
|
58
|
+
const block = (file, rest) => {
|
|
59
|
+
const body = host[`blocks/${file}.md`];
|
|
60
|
+
return {
|
|
61
|
+
id: `play.${file}`,
|
|
62
|
+
name: heading(body, `blocks/${file}.md`),
|
|
63
|
+
body,
|
|
64
|
+
required: false,
|
|
65
|
+
...rest,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const styleGuides = zh
|
|
69
|
+
? {
|
|
70
|
+
intimate: "把这一刻拆开写,触觉先于视觉;对方是否接受由他的性格和处境决定。",
|
|
71
|
+
noir: "短句,只写看得见的动作和物件,不替角色下判断。",
|
|
72
|
+
wuxia: "用语偏文,不用现代词汇;交手写清出手与应对。",
|
|
73
|
+
horror: "恐惧来自不确定;不替玩家判断危险程度,也不给安全保证。",
|
|
74
|
+
action: "写清谁在哪、朝哪动、打到没打到、代价是什么;伤势和体力累积。",
|
|
75
|
+
literary: "说七分留三分,情绪不直接命名,但结果事实写清楚。",
|
|
76
|
+
}
|
|
77
|
+
: {
|
|
78
|
+
intimate: "Unfold the moment; touch before sight; whether they accept follows their character and situation.",
|
|
79
|
+
noir: "Short sentences; only observable actions and objects; no judging for characters.",
|
|
80
|
+
wuxia: "Period diction, no modern vocabulary; make each exchange of blows legible.",
|
|
81
|
+
horror: "Fear comes from uncertainty; do not rate the danger for the player or promise safety.",
|
|
82
|
+
action: "Make clear who is where, which way they move, what lands and what it costs; wounds and fatigue accumulate.",
|
|
83
|
+
literary: "Say seven parts and leave three; do not name the emotion, but state outcomes clearly.",
|
|
84
|
+
};
|
|
27
85
|
return [
|
|
28
86
|
{
|
|
29
87
|
id: "play.mechanics",
|
|
@@ -31,98 +89,169 @@ export function builtinPlayPrompts(locale) {
|
|
|
31
89
|
? "工具与响应结算(必选)"
|
|
32
90
|
: "Tools and response settlement (required)",
|
|
33
91
|
required: true,
|
|
92
|
+
category: "mechanics",
|
|
34
93
|
body: zh
|
|
35
94
|
? `# 工具与响应结算
|
|
36
95
|
|
|
37
|
-
只可调用请求随附的工具。目录使用预加载索引或工具返回的 @dir-*,无需重复列目录确认;文档和历史使用返回的 @句柄。工具结果标明读取范围、完整性及 cursor
|
|
96
|
+
只可调用请求随附的工具。目录使用预加载索引或工具返回的 @dir-*,无需重复列目录确认;文档和历史使用返回的 @句柄。工具结果标明读取范围、完整性及 cursor;目录摘要不等于正文。材料覆盖中完整正文与精确读取取得对应范围的写入资格,界面绑定标注不扩大写入资格。初始世界材料只在全新上下文加载一次;后续成功工具调用与回执更新当前值和读取资格,不必重读自己刚成功写入的内容。全新上下文不继承旧对话的读取与写入资格。
|
|
38
97
|
|
|
39
98
|
world_patch 的 YAML locator 可以包含 map key 和从零开始的数组下标;remove 删除一个精确位置。未提供的元数据保持原值。world_retire 从目录注入中退役文档,文档仍可读取、引用和恢复。工具响应的成功状态变化与后续最终叙事分别提交,后续失败不回滚已提交变化。成功写入回执表示当前值已改变;以真实回执的结算状态为准,模型文字不能提交世界。
|
|
40
99
|
|
|
41
|
-
包含工具调用的响应属于中间步,其中的文字尚未展示或提交为最终叙事。收到全部工具结果后,根据回执在最终正文中呈现本轮尚未展示的内容,并与已提交状态一致;没有工具调用的非空正文结束本次循环并由 Runtime 提交。world_checkpoint
|
|
100
|
+
包含工具调用的响应属于中间步,其中的文字尚未展示或提交为最终叙事。收到全部工具结果后,根据回执在最终正文中呈现本轮尚未展示的内容,并与已提交状态一致;没有工具调用的非空正文结束本次循环并由 Runtime 提交。world_checkpoint 登记历史重放边界并向玩家建议从此开始全新上下文;在随后非空且无工具调用的最终正文成功提交后生效,边界包含该正文;登记后仍可继续工具调用。登记不结束世界内事件,也不自动切换上下文,玩家自行选择。全新上下文补入边界后的已提交玩家原文与最终叙事,排除开场白、工具和推理。何时登记由检查点政策块规定。
|
|
42
101
|
|
|
43
102
|
${historyUseGuidance(locale)}`
|
|
44
103
|
: `# Tools and response settlement
|
|
45
104
|
|
|
46
|
-
Use only attached tools. Directories use @dir-* handles from the preload or tool results; do not list them again just to confirm a handle; documents and history use returned @handles. Results identify read scope, completeness and cursor; directory summaries are not document bodies. Complete injected bodies and exact reads
|
|
105
|
+
Use only attached tools. Directories use @dir-* handles from the preload or tool results; do not list them again just to confirm a handle; documents and history use returned @handles. Results identify read scope, completeness and cursor; directory summaries are not document bodies. Complete injected bodies and exact reads obtain write authorization within their corresponding scopes; player-view annotations do not widen it. Initial world materials load once per fresh context. Later successful tool calls and receipts update current values and read scopes; do not re-read merely to confirm your own successful writes. A fresh context does not inherit an old conversation's read or write authorization.
|
|
47
106
|
|
|
48
107
|
YAML world_patch locators accept map keys and zero-based array indexes; remove deletes one exact location. Omitted metadata remains unchanged. world_retire removes a document from catalog injection while keeping it readable, referenceable and restorable. Successful tool-response state changes and later final narrative commit separately; later failure does not roll back committed changes. Successful write receipts change current values; the actual receipt determines settlement, not model prose.
|
|
49
108
|
|
|
50
|
-
Responses containing tool calls are intermediate steps; their text has not yet been displayed or committed as final narrative. After all tool results, use the receipts to present this turn's still-unshown content in final prose consistent with committed state. A nonempty tool-free response ends this loop and Runtime commits it. world_checkpoint registers
|
|
109
|
+
Responses containing tool calls are intermediate steps; their text has not yet been displayed or committed as final narrative. After all tool results, use the receipts to present this turn's still-unshown content in final prose consistent with committed state. A nonempty tool-free response ends this loop and Runtime commits it. world_checkpoint registers the history-replay boundary and suggests a fresh context from that point to the player; it takes effect after the subsequent nonempty tool-free final prose successfully commits, including that prose, and further tool calls may follow registration. Registration neither ends an in-world event nor switches context, which the player chooses. Fresh-context replay contains committed player originals and final narratives after the boundary, excluding the opening, tools and reasoning. The checkpoint policy block decides when to register.
|
|
51
110
|
|
|
52
111
|
${historyUseGuidance(locale)}`,
|
|
53
112
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
// Exclusive choice groups: the first member of each ships enabled.
|
|
114
|
+
block("person-second", {
|
|
115
|
+
category: "choice",
|
|
116
|
+
group: "person",
|
|
117
|
+
turnGuide: zh
|
|
118
|
+
? "以第二人称“你”称呼玩家角色;视角只跟随玩家角色。"
|
|
119
|
+
: "Address the player character as “you”; the viewpoint follows only the player character.",
|
|
120
|
+
}),
|
|
121
|
+
block("person-first", {
|
|
122
|
+
category: "choice",
|
|
123
|
+
group: "person",
|
|
124
|
+
turnGuide: zh
|
|
125
|
+
? "以第一人称“我”写玩家角色;只写“我”能感知到的。"
|
|
126
|
+
: "Write the player character as “I”; write only what “I” can perceive.",
|
|
127
|
+
}),
|
|
128
|
+
block("person-third", {
|
|
129
|
+
category: "choice",
|
|
130
|
+
group: "person",
|
|
131
|
+
turnGuide: zh
|
|
132
|
+
? "以第三人称写玩家角色,已知名字时用名字;玩家原文里的“我”“你”转为第三人称。"
|
|
133
|
+
: "Third person for the player character, by name when known; “I” and “you” in the player's text become third person.",
|
|
134
|
+
}),
|
|
135
|
+
block("continue-stage", {
|
|
136
|
+
category: "choice",
|
|
137
|
+
group: "continue",
|
|
138
|
+
turnGuide: zh
|
|
139
|
+
? "先把玩家这次的行动和台词演成叙事,再写世界的反应;上一轮已经演过的不重述。"
|
|
140
|
+
: "Stage the player's action and dialogue as narrative first, then the world's reaction; never restate earlier turns.",
|
|
141
|
+
}),
|
|
142
|
+
block("continue-advance", {
|
|
143
|
+
category: "choice",
|
|
144
|
+
group: "continue",
|
|
145
|
+
turnGuide: zh
|
|
146
|
+
? "玩家原文已经是正文,从它之后的第一件事写起,不复述玩家的话。"
|
|
147
|
+
: "The player's text is already prose: start from the first thing after it and do not repeat the player's words.",
|
|
148
|
+
}),
|
|
149
|
+
block("agency-default", {
|
|
150
|
+
category: "choice",
|
|
151
|
+
group: "agency",
|
|
152
|
+
turnGuide: zh
|
|
153
|
+
? "可以补连带动作和寒暄;目标、立场、承诺、给予或接受、关系与风险的选择只能来自玩家原文。"
|
|
154
|
+
: "Fill in supporting actions and small talk; goals, stance, promises, giving, accepting and risk choices are the player's.",
|
|
155
|
+
}),
|
|
156
|
+
block("agency-strict", {
|
|
157
|
+
category: "choice",
|
|
158
|
+
group: "agency",
|
|
159
|
+
turnGuide: zh
|
|
160
|
+
? "只演玩家原文写出的动作和台词,连带动作也停在原文说到的地方;不替玩家补台词或做选择。"
|
|
161
|
+
: "Perform only what the player's text states; supporting actions stop where the text stops; add no lines or choices.",
|
|
162
|
+
}),
|
|
163
|
+
block("agency-open", {
|
|
164
|
+
category: "choice",
|
|
165
|
+
group: "agency",
|
|
166
|
+
turnGuide: zh
|
|
167
|
+
? "可以替玩家接普通对话、推进日常互动;承诺、给予、关系转折和风险选择仍交回玩家。"
|
|
168
|
+
: "Carry small talk and routine interaction for the player; promises, giving, relationship turns and risks return to them.",
|
|
169
|
+
}),
|
|
170
|
+
block("stop-beat", {
|
|
171
|
+
category: "choice",
|
|
172
|
+
group: "stop",
|
|
173
|
+
turnGuide: zh
|
|
174
|
+
? "对方给出会影响玩家接话的新反馈就停下,即使不是重大决定。"
|
|
175
|
+
: "Stop as soon as the other person's feedback could change how the player replies, even without a consequential decision.",
|
|
176
|
+
}),
|
|
177
|
+
block("stop-scene", {
|
|
178
|
+
category: "choice",
|
|
179
|
+
group: "stop",
|
|
180
|
+
turnGuide: zh
|
|
181
|
+
? "推进到当前场景的自然段落再停;只在归玩家的决定或换场景时停。"
|
|
182
|
+
: "Carry the scene to its natural pause; stop only at a decision that belongs to the player or at a scene change.",
|
|
183
|
+
}),
|
|
184
|
+
// Independent policies: each rule lives in exactly one block.
|
|
185
|
+
block("prose", { category: "policy" }),
|
|
186
|
+
block("scene", { category: "policy" }),
|
|
187
|
+
block("ending", {
|
|
188
|
+
category: "policy",
|
|
189
|
+
turnGuide: zh
|
|
190
|
+
? "结尾停在最后一句话、一个动作或一个环境变化上;不写“你打算怎么做”类交接,不列选项,不总结。"
|
|
191
|
+
: "End on the last line, action or change in the surroundings; no “What do you do?” handoffs, no option lists, no summary.",
|
|
192
|
+
}),
|
|
193
|
+
block("output", {
|
|
194
|
+
category: "policy",
|
|
195
|
+
turnGuide: zh
|
|
196
|
+
? "只写小说正文,无标题、清单、幕后说明;“资料/搜索/句柄/Runtime”等内部词不进正文。"
|
|
197
|
+
: "Only the novel's prose: no headings, lists or backstage notes; words like records, search, handle or Runtime stay out.",
|
|
198
|
+
}),
|
|
199
|
+
block("facts", {
|
|
200
|
+
category: "policy",
|
|
201
|
+
turnGuide: zh
|
|
202
|
+
? "尝试不自动成功,声称不自动成真;正文不改变已裁决的结果。"
|
|
203
|
+
: "Attempts do not automatically succeed and claims are not automatically true; prose never changes an adjudicated result.",
|
|
204
|
+
}),
|
|
205
|
+
block("npc", { category: "policy" }),
|
|
206
|
+
block("save-timing", {
|
|
207
|
+
category: "policy",
|
|
208
|
+
turnGuide: zh
|
|
209
|
+
? "玩家视图里的值若因本轮变化,先用工具写回再写最终正文。"
|
|
210
|
+
: "If a player-view value changed this turn, write it back with a tool before the final prose.",
|
|
211
|
+
}),
|
|
212
|
+
block("ownership", { category: "policy" }),
|
|
213
|
+
block("situation", { category: "policy" }),
|
|
214
|
+
block("checkpoint-policy", { category: "policy" }),
|
|
215
|
+
// Stackable styles ship disabled.
|
|
216
|
+
...Object.keys(host)
|
|
217
|
+
.filter((path) => path.startsWith("blocks/style-"))
|
|
218
|
+
.map((path) => {
|
|
219
|
+
const style = path.slice("blocks/style-".length, -".md".length);
|
|
220
|
+
const turnGuide = styleGuides[style];
|
|
221
|
+
return block(`style-${style}`, {
|
|
222
|
+
category: "style",
|
|
223
|
+
...(turnGuide === undefined ? {} : { turnGuide }),
|
|
224
|
+
});
|
|
225
|
+
}),
|
|
105
226
|
{
|
|
106
227
|
id: "play.tabletop",
|
|
107
228
|
name: zh ? "跑团主持" : "Tabletop GM",
|
|
108
229
|
body: tabletopPlayBody(locale),
|
|
109
230
|
required: false,
|
|
231
|
+
category: "policy",
|
|
110
232
|
gameplay: "tabletop",
|
|
233
|
+
turnGuide: zh
|
|
234
|
+
? "有成败不确定的行动先调用判定工具并完整兑现回执,再叙述。"
|
|
235
|
+
: "For an action whose outcome is uncertain, call the resolution tool first and honor its receipt fully before narrating.",
|
|
111
236
|
},
|
|
112
237
|
];
|
|
113
238
|
}
|
|
239
|
+
/** Ids of the builtins that ship enabled: each group's first member and every policy. */
|
|
240
|
+
export function defaultEnabledBuiltin(item, catalog) {
|
|
241
|
+
if (item.category === "style")
|
|
242
|
+
return false;
|
|
243
|
+
if (item.group === undefined)
|
|
244
|
+
return true;
|
|
245
|
+
return catalog.find((other) => other.group === item.group)?.id === item.id;
|
|
246
|
+
}
|
|
114
247
|
export function defaultOrderedPlayPrompts() {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
248
|
+
const catalog = builtinPlayPrompts("en");
|
|
249
|
+
return catalog
|
|
250
|
+
.map((item) => ({
|
|
251
|
+
id: item.id,
|
|
118
252
|
kind: "builtin",
|
|
119
|
-
builtin: id,
|
|
120
|
-
enabled:
|
|
121
|
-
![
|
|
122
|
-
"play.continue-advance",
|
|
123
|
-
"play.person-first",
|
|
124
|
-
"play.person-third",
|
|
125
|
-
].includes(id),
|
|
253
|
+
builtin: item.id,
|
|
254
|
+
enabled: defaultEnabledBuiltin(item, catalog),
|
|
126
255
|
}))
|
|
127
256
|
.concat([{ id: "world", kind: "world" }]);
|
|
128
257
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { turnGuideLineSoftLimit, turnGuideLinesSoftLimit, } from "./ordered-play-prompts.js";
|
|
2
|
+
/**
|
|
3
|
+
* The fixed Runtime header. It is mechanical: it tells the model what the
|
|
4
|
+
* suffix is and that the prose, not a reply to the suffix, is wanted.
|
|
5
|
+
*/
|
|
6
|
+
export function turnGuideHeader(locale) {
|
|
7
|
+
return locale === "zh-CN"
|
|
8
|
+
? "[Runtime 本轮提示]\n以下是本轮写正文时最容易丢的要求,不要回应本提示,直接写正文。"
|
|
9
|
+
: "[Runtime turn guide]\nThese are the requirements most easily lost while writing this turn's prose. Do not reply to this notice; write the prose.";
|
|
10
|
+
}
|
|
11
|
+
/** The complete suffix text: header, then one line per enabled entry in list order. */
|
|
12
|
+
export function turnGuideText(locale, lines) {
|
|
13
|
+
return [
|
|
14
|
+
turnGuideHeader(locale),
|
|
15
|
+
...lines.map(({ text }) => `- ${text}`),
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
/** Soft-limit warnings; the compiler never rejects on them. */
|
|
19
|
+
export function turnGuideWarnings(lines) {
|
|
20
|
+
const warnings = lines.flatMap(({ text }, index) => [...text].length > turnGuideLineSoftLimit
|
|
21
|
+
? [{ kind: "line_length", index }]
|
|
22
|
+
: []);
|
|
23
|
+
if (lines.length > turnGuideLinesSoftLimit)
|
|
24
|
+
warnings.push({ kind: "line_count" });
|
|
25
|
+
return warnings;
|
|
26
|
+
}
|