@actalk/inkos-core 1.4.1 → 1.5.0-canary.47.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.
Files changed (262) hide show
  1. package/dist/agent/agent-session.d.ts +15 -0
  2. package/dist/agent/agent-session.d.ts.map +1 -1
  3. package/dist/agent/agent-session.js +256 -26
  4. package/dist/agent/agent-session.js.map +1 -1
  5. package/dist/agent/agent-system-prompt.d.ts +8 -1
  6. package/dist/agent/agent-system-prompt.d.ts.map +1 -1
  7. package/dist/agent/agent-system-prompt.js +382 -176
  8. package/dist/agent/agent-system-prompt.js.map +1 -1
  9. package/dist/agent/agent-tools.d.ts +156 -19
  10. package/dist/agent/agent-tools.d.ts.map +1 -1
  11. package/dist/agent/agent-tools.js +980 -46
  12. package/dist/agent/agent-tools.js.map +1 -1
  13. package/dist/agent/context-transform.d.ts +4 -1
  14. package/dist/agent/context-transform.d.ts.map +1 -1
  15. package/dist/agent/context-transform.js +104 -9
  16. package/dist/agent/context-transform.js.map +1 -1
  17. package/dist/agent/index.d.ts +1 -1
  18. package/dist/agent/index.d.ts.map +1 -1
  19. package/dist/agent/index.js +1 -1
  20. package/dist/agent/index.js.map +1 -1
  21. package/dist/agents/architect.d.ts +11 -1
  22. package/dist/agents/architect.d.ts.map +1 -1
  23. package/dist/agents/architect.js +242 -114
  24. package/dist/agents/architect.js.map +1 -1
  25. package/dist/agents/chapter-analyzer.js +1 -1
  26. package/dist/agents/chapter-analyzer.js.map +1 -1
  27. package/dist/agents/composer.d.ts +36 -0
  28. package/dist/agents/composer.d.ts.map +1 -1
  29. package/dist/agents/composer.js +503 -20
  30. package/dist/agents/composer.js.map +1 -1
  31. package/dist/agents/continuity.d.ts +3 -0
  32. package/dist/agents/continuity.d.ts.map +1 -1
  33. package/dist/agents/continuity.js +28 -14
  34. package/dist/agents/continuity.js.map +1 -1
  35. package/dist/agents/en-prompt-sections.d.ts.map +1 -1
  36. package/dist/agents/en-prompt-sections.js +15 -1
  37. package/dist/agents/en-prompt-sections.js.map +1 -1
  38. package/dist/agents/fanfic-canon-importer.d.ts +1 -0
  39. package/dist/agents/fanfic-canon-importer.d.ts.map +1 -1
  40. package/dist/agents/fanfic-canon-importer.js +53 -6
  41. package/dist/agents/fanfic-canon-importer.js.map +1 -1
  42. package/dist/agents/foundation-reviewer.d.ts +1 -0
  43. package/dist/agents/foundation-reviewer.d.ts.map +1 -1
  44. package/dist/agents/foundation-reviewer.js +17 -12
  45. package/dist/agents/foundation-reviewer.js.map +1 -1
  46. package/dist/agents/length-normalizer.d.ts +1 -0
  47. package/dist/agents/length-normalizer.d.ts.map +1 -1
  48. package/dist/agents/length-normalizer.js +16 -3
  49. package/dist/agents/length-normalizer.js.map +1 -1
  50. package/dist/agents/planner-prompts.d.ts +7 -7
  51. package/dist/agents/planner-prompts.d.ts.map +1 -1
  52. package/dist/agents/planner-prompts.js +29 -29
  53. package/dist/agents/planner-prompts.js.map +1 -1
  54. package/dist/agents/planner.d.ts +6 -5
  55. package/dist/agents/planner.d.ts.map +1 -1
  56. package/dist/agents/planner.js +90 -6
  57. package/dist/agents/planner.js.map +1 -1
  58. package/dist/agents/post-write-validator.d.ts.map +1 -1
  59. package/dist/agents/post-write-validator.js +49 -0
  60. package/dist/agents/post-write-validator.js.map +1 -1
  61. package/dist/agents/reviser.js +10 -0
  62. package/dist/agents/reviser.js.map +1 -1
  63. package/dist/agents/rules-reader.d.ts +6 -14
  64. package/dist/agents/rules-reader.d.ts.map +1 -1
  65. package/dist/agents/rules-reader.js +15 -28
  66. package/dist/agents/rules-reader.js.map +1 -1
  67. package/dist/agents/short-fiction.d.ts +4 -0
  68. package/dist/agents/short-fiction.d.ts.map +1 -1
  69. package/dist/agents/short-fiction.js +51 -8
  70. package/dist/agents/short-fiction.js.map +1 -1
  71. package/dist/agents/state-validator.d.ts +0 -2
  72. package/dist/agents/state-validator.d.ts.map +1 -1
  73. package/dist/agents/state-validator.js +4 -16
  74. package/dist/agents/state-validator.js.map +1 -1
  75. package/dist/agents/style-analyzer.d.ts +1 -1
  76. package/dist/agents/style-analyzer.d.ts.map +1 -1
  77. package/dist/agents/style-analyzer.js +34 -17
  78. package/dist/agents/style-analyzer.js.map +1 -1
  79. package/dist/agents/writer-prompts.d.ts.map +1 -1
  80. package/dist/agents/writer-prompts.js +160 -12
  81. package/dist/agents/writer-prompts.js.map +1 -1
  82. package/dist/agents/writer.d.ts.map +1 -1
  83. package/dist/agents/writer.js +31 -9
  84. package/dist/agents/writer.js.map +1 -1
  85. package/dist/index.d.ts +18 -7
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +17 -7
  88. package/dist/index.js.map +1 -1
  89. package/dist/interaction/action-envelope.d.ts +261 -0
  90. package/dist/interaction/action-envelope.d.ts.map +1 -0
  91. package/dist/interaction/action-envelope.js +102 -0
  92. package/dist/interaction/action-envelope.js.map +1 -0
  93. package/dist/interaction/book-session-store.d.ts +6 -2
  94. package/dist/interaction/book-session-store.d.ts.map +1 -1
  95. package/dist/interaction/book-session-store.js +21 -3
  96. package/dist/interaction/book-session-store.js.map +1 -1
  97. package/dist/interaction/edit-controller.d.ts +5 -0
  98. package/dist/interaction/edit-controller.d.ts.map +1 -1
  99. package/dist/interaction/edit-controller.js +123 -26
  100. package/dist/interaction/edit-controller.js.map +1 -1
  101. package/dist/interaction/events.d.ts +4 -4
  102. package/dist/interaction/intents.d.ts +9 -6
  103. package/dist/interaction/intents.d.ts.map +1 -1
  104. package/dist/interaction/intents.js +2 -1
  105. package/dist/interaction/intents.js.map +1 -1
  106. package/dist/interaction/project-control.d.ts +3 -43
  107. package/dist/interaction/project-control.d.ts.map +1 -1
  108. package/dist/interaction/project-control.js +1 -53
  109. package/dist/interaction/project-control.js.map +1 -1
  110. package/dist/interaction/project-tools.d.ts +1 -1
  111. package/dist/interaction/project-tools.d.ts.map +1 -1
  112. package/dist/interaction/project-tools.js +41 -185
  113. package/dist/interaction/project-tools.js.map +1 -1
  114. package/dist/interaction/runtime.d.ts +1 -1
  115. package/dist/interaction/runtime.d.ts.map +1 -1
  116. package/dist/interaction/runtime.js +49 -75
  117. package/dist/interaction/runtime.js.map +1 -1
  118. package/dist/interaction/session-transcript-legacy.d.ts.map +1 -1
  119. package/dist/interaction/session-transcript-legacy.js +2 -0
  120. package/dist/interaction/session-transcript-legacy.js.map +1 -1
  121. package/dist/interaction/session-transcript-restore.d.ts +4 -3
  122. package/dist/interaction/session-transcript-restore.d.ts.map +1 -1
  123. package/dist/interaction/session-transcript-restore.js +234 -34
  124. package/dist/interaction/session-transcript-restore.js.map +1 -1
  125. package/dist/interaction/session-transcript-schema.d.ts +45 -12
  126. package/dist/interaction/session-transcript-schema.d.ts.map +1 -1
  127. package/dist/interaction/session-transcript-schema.js +6 -0
  128. package/dist/interaction/session-transcript-schema.js.map +1 -1
  129. package/dist/interaction/session-transcript.d.ts +8 -1
  130. package/dist/interaction/session-transcript.d.ts.map +1 -1
  131. package/dist/interaction/session-transcript.js +13 -1
  132. package/dist/interaction/session-transcript.js.map +1 -1
  133. package/dist/interaction/session.d.ts +78 -66
  134. package/dist/interaction/session.d.ts.map +1 -1
  135. package/dist/interaction/session.js +10 -2
  136. package/dist/interaction/session.js.map +1 -1
  137. package/dist/llm/provider.d.ts +32 -34
  138. package/dist/llm/provider.d.ts.map +1 -1
  139. package/dist/llm/provider.js +144 -127
  140. package/dist/llm/provider.js.map +1 -1
  141. package/dist/models/book-rules.d.ts +6 -4
  142. package/dist/models/book-rules.d.ts.map +1 -1
  143. package/dist/models/book-rules.js +187 -8
  144. package/dist/models/book-rules.js.map +1 -1
  145. package/dist/models/context-compression.d.ts +13 -0
  146. package/dist/models/context-compression.d.ts.map +1 -0
  147. package/dist/models/context-compression.js +2 -0
  148. package/dist/models/context-compression.js.map +1 -0
  149. package/dist/models/input-governance.d.ts +53 -12
  150. package/dist/models/input-governance.d.ts.map +1 -1
  151. package/dist/models/input-governance.js +16 -0
  152. package/dist/models/input-governance.js.map +1 -1
  153. package/dist/models/play.d.ts +530 -0
  154. package/dist/models/play.d.ts.map +1 -0
  155. package/dist/models/play.js +318 -0
  156. package/dist/models/play.js.map +1 -0
  157. package/dist/models/project.d.ts +8 -0
  158. package/dist/models/project.d.ts.map +1 -1
  159. package/dist/models/project.js +1 -0
  160. package/dist/models/project.js.map +1 -1
  161. package/dist/pipeline/chapter-review-cycle.d.ts.map +1 -1
  162. package/dist/pipeline/chapter-review-cycle.js +29 -3
  163. package/dist/pipeline/chapter-review-cycle.js.map +1 -1
  164. package/dist/pipeline/persisted-governed-plan.d.ts.map +1 -1
  165. package/dist/pipeline/persisted-governed-plan.js +98 -49
  166. package/dist/pipeline/persisted-governed-plan.js.map +1 -1
  167. package/dist/pipeline/runner.d.ts +31 -0
  168. package/dist/pipeline/runner.d.ts.map +1 -1
  169. package/dist/pipeline/runner.js +212 -68
  170. package/dist/pipeline/runner.js.map +1 -1
  171. package/dist/pipeline/short-fiction-runner.d.ts +14 -0
  172. package/dist/pipeline/short-fiction-runner.d.ts.map +1 -1
  173. package/dist/pipeline/short-fiction-runner.js +242 -94
  174. package/dist/pipeline/short-fiction-runner.js.map +1 -1
  175. package/dist/play/play-agents.d.ts +71 -0
  176. package/dist/play/play-agents.d.ts.map +1 -0
  177. package/dist/play/play-agents.js +511 -0
  178. package/dist/play/play-agents.js.map +1 -0
  179. package/dist/play/play-db-factory.d.ts +9 -0
  180. package/dist/play/play-db-factory.d.ts.map +1 -0
  181. package/dist/play/play-db-factory.js +18 -0
  182. package/dist/play/play-db-factory.js.map +1 -0
  183. package/dist/play/play-db.d.ts +22 -0
  184. package/dist/play/play-db.d.ts.map +1 -0
  185. package/dist/play/play-db.js +248 -0
  186. package/dist/play/play-db.js.map +1 -0
  187. package/dist/play/play-file-db.d.ts +32 -0
  188. package/dist/play/play-file-db.d.ts.map +1 -0
  189. package/dist/play/play-file-db.js +156 -0
  190. package/dist/play/play-file-db.js.map +1 -0
  191. package/dist/play/play-image.d.ts +58 -0
  192. package/dist/play/play-image.d.ts.map +1 -0
  193. package/dist/play/play-image.js +142 -0
  194. package/dist/play/play-image.js.map +1 -0
  195. package/dist/play/play-reducer.d.ts +31 -0
  196. package/dist/play/play-reducer.d.ts.map +1 -0
  197. package/dist/play/play-reducer.js +261 -0
  198. package/dist/play/play-reducer.js.map +1 -0
  199. package/dist/play/play-runner.d.ts +102 -0
  200. package/dist/play/play-runner.d.ts.map +1 -0
  201. package/dist/play/play-runner.js +465 -0
  202. package/dist/play/play-runner.js.map +1 -0
  203. package/dist/play/play-store.d.ts +112 -0
  204. package/dist/play/play-store.d.ts.map +1 -0
  205. package/dist/play/play-store.js +311 -0
  206. package/dist/play/play-store.js.map +1 -0
  207. package/dist/prompts/short-fiction.d.ts +5 -0
  208. package/dist/prompts/short-fiction.d.ts.map +1 -1
  209. package/dist/prompts/short-fiction.js +46 -22
  210. package/dist/prompts/short-fiction.js.map +1 -1
  211. package/dist/state/state-bootstrap.d.ts.map +1 -1
  212. package/dist/state/state-bootstrap.js +12 -25
  213. package/dist/state/state-bootstrap.js.map +1 -1
  214. package/dist/state/state-reducer.js +31 -22
  215. package/dist/state/state-reducer.js.map +1 -1
  216. package/dist/utils/book-eval.d.ts +35 -0
  217. package/dist/utils/book-eval.d.ts.map +1 -0
  218. package/dist/utils/book-eval.js +116 -0
  219. package/dist/utils/book-eval.js.map +1 -0
  220. package/dist/utils/chapter-memo-parser.d.ts +10 -7
  221. package/dist/utils/chapter-memo-parser.d.ts.map +1 -1
  222. package/dist/utils/chapter-memo-parser.js +86 -43
  223. package/dist/utils/chapter-memo-parser.js.map +1 -1
  224. package/dist/utils/context-assembly.d.ts +2 -0
  225. package/dist/utils/context-assembly.d.ts.map +1 -1
  226. package/dist/utils/context-assembly.js +38 -1
  227. package/dist/utils/context-assembly.js.map +1 -1
  228. package/dist/utils/hook-health.d.ts.map +1 -1
  229. package/dist/utils/hook-health.js +5 -2
  230. package/dist/utils/hook-health.js.map +1 -1
  231. package/dist/utils/hook-ledger-validator.d.ts +1 -1
  232. package/dist/utils/hook-ledger-validator.d.ts.map +1 -1
  233. package/dist/utils/hook-ledger-validator.js +5 -5
  234. package/dist/utils/hook-ledger-validator.js.map +1 -1
  235. package/dist/utils/hook-lifecycle.d.ts +1 -0
  236. package/dist/utils/hook-lifecycle.d.ts.map +1 -1
  237. package/dist/utils/hook-lifecycle.js +10 -3
  238. package/dist/utils/hook-lifecycle.js.map +1 -1
  239. package/dist/utils/language.d.ts +10 -0
  240. package/dist/utils/language.d.ts.map +1 -0
  241. package/dist/utils/language.js +18 -0
  242. package/dist/utils/language.js.map +1 -0
  243. package/dist/utils/length-metrics.d.ts +3 -0
  244. package/dist/utils/length-metrics.d.ts.map +1 -1
  245. package/dist/utils/length-metrics.js +8 -0
  246. package/dist/utils/length-metrics.js.map +1 -1
  247. package/dist/utils/memory-retrieval.d.ts.map +1 -1
  248. package/dist/utils/memory-retrieval.js +19 -15
  249. package/dist/utils/memory-retrieval.js.map +1 -1
  250. package/dist/utils/outline-paths.d.ts +12 -0
  251. package/dist/utils/outline-paths.d.ts.map +1 -1
  252. package/dist/utils/outline-paths.js +68 -0
  253. package/dist/utils/outline-paths.js.map +1 -1
  254. package/package.json +1 -1
  255. package/dist/interaction/nl-router.d.ts +0 -8
  256. package/dist/interaction/nl-router.d.ts.map +0 -1
  257. package/dist/interaction/nl-router.js +0 -218
  258. package/dist/interaction/nl-router.js.map +0 -1
  259. package/dist/pipeline/agent.d.ts +0 -15
  260. package/dist/pipeline/agent.d.ts.map +0 -1
  261. package/dist/pipeline/agent.js +0 -597
  262. package/dist/pipeline/agent.js.map +0 -1
@@ -0,0 +1,511 @@
1
+ import { z } from "zod";
2
+ import { BaseAgent } from "../agents/base.js";
3
+ import { PlayActionIntentSchema, PlayMutationSchema, } from "../models/play.js";
4
+ const PlaySceneRenderSchema = z.object({
5
+ sceneText: z.string().min(1),
6
+ suggestedActions: z.array(z.string().min(1)).min(0).max(4).default([]),
7
+ });
8
+ // A play turn runs three internal LLM calls (interpret → mutate → render). The
9
+ // transport-level retry in the provider does NOT cover HTTP 502/503/429 or
10
+ // "temporarily unavailable", so a single flaky upstream response would break the
11
+ // whole turn. Retry those here, then let each agent fail open.
12
+ function isRetryableLlmError(err) {
13
+ const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
14
+ return /50[0-9]|429|temporarily unavailable|timeout|timed out|socket|terminated|econn|network|fetch failed|bad gateway|service unavailable|rate limit/.test(msg);
15
+ }
16
+ async function chatWithRetry(call, retries = 2) {
17
+ let lastErr;
18
+ for (let attempt = 0; attempt <= retries; attempt++) {
19
+ try {
20
+ return await call();
21
+ }
22
+ catch (err) {
23
+ lastErr = err;
24
+ if (attempt >= retries || !isRetryableLlmError(err))
25
+ throw err;
26
+ await new Promise((resolve) => setTimeout(resolve, 400 * (attempt + 1)));
27
+ }
28
+ }
29
+ throw lastErr;
30
+ }
31
+ function trySceneParse(content) {
32
+ try {
33
+ return PlaySceneRenderSchema.parse(parseJson(content));
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ export class PlayActionInterpreterAgent extends BaseAgent {
40
+ constructor(ctx) {
41
+ super(ctx);
42
+ }
43
+ get name() {
44
+ return "play-action-interpreter";
45
+ }
46
+ async interpret(input) {
47
+ // Never throw: a transient upstream error (after retries) or unparseable output
48
+ // degrades to a generic action (the player's raw text as a "do"), not a crash.
49
+ let raw = {};
50
+ try {
51
+ const response = await chatWithRetry(() => this.chat([
52
+ { role: "system", content: buildActionInterpreterSystemPrompt(input.language ?? "zh") },
53
+ { role: "user", content: buildActionInterpreterUserPrompt(input, input.language ?? "zh") },
54
+ ], { temperature: 0.15, maxTokens: 1024 }));
55
+ raw = parseJson(response.content);
56
+ }
57
+ catch { /* transient/malformed → degrade below */ }
58
+ const parsed = PlayActionIntentSchema.safeParse(raw);
59
+ return parsed.success
60
+ ? parsed.data
61
+ : PlayActionIntentSchema.parse({ actionKind: "do", intent: input.input });
62
+ }
63
+ }
64
+ export class PlayWorldMutatorAgent extends BaseAgent {
65
+ constructor(ctx) {
66
+ super(ctx);
67
+ }
68
+ get name() {
69
+ return "play-world-mutator";
70
+ }
71
+ async proposeMutation(input) {
72
+ // Never throw: a transient upstream error (after retries) or an unparseable
73
+ // mutation degrades to a blocked, no-op turn (with a reason), not a crash.
74
+ let raw = {};
75
+ try {
76
+ const response = await chatWithRetry(() => this.chat([
77
+ { role: "system", content: buildWorldMutatorSystemPrompt(input.language ?? "zh") },
78
+ { role: "user", content: buildWorldMutatorUserPrompt(input, input.language ?? "zh") },
79
+ ], { temperature: 0.25, maxTokens: 4096 }));
80
+ raw = parseJson(response.content);
81
+ }
82
+ catch { /* transient/malformed → degrade below */ }
83
+ const parsed = PlayMutationSchema.safeParse(raw);
84
+ const mutation = parsed.success
85
+ ? parsed.data
86
+ : PlayMutationSchema.parse({
87
+ turn: input.turn,
88
+ actionKind: input.action.actionKind,
89
+ blocked: true,
90
+ blockedReason: "模型输出无法解析为有效的状态变更,本回合未推进世界状态。",
91
+ });
92
+ // Observability (#2): a dropped world item must not vanish silently. Log when
93
+ // the model proposed entities/edges/slots that parsing discarded — that is the
94
+ // difference between "the model wrote nothing" and "we threw its work away".
95
+ logDroppedMutationItems(raw, mutation, input.turn);
96
+ return { ...mutation, eventId: mutation.eventId || `evt-${input.turn}` };
97
+ }
98
+ }
99
+ function rawUpsertCount(field) {
100
+ if (Array.isArray(field))
101
+ return field.length;
102
+ if (field && typeof field === "object" && Array.isArray(field.upsert)) {
103
+ return field.upsert.length;
104
+ }
105
+ return 0;
106
+ }
107
+ function logDroppedMutationItems(raw, mutation, turn) {
108
+ if (!raw || typeof raw !== "object")
109
+ return;
110
+ const r = raw;
111
+ const rawE = rawUpsertCount(r.entities);
112
+ const rawEd = rawUpsertCount(r.edges);
113
+ const rawS = rawUpsertCount(r.stateSlots);
114
+ const keptE = mutation.entities.upsert.length;
115
+ const keptEd = mutation.edges.upsert.length;
116
+ const keptS = mutation.stateSlots.upsert.length;
117
+ if (rawE > keptE || rawEd > keptEd || rawS > keptS) {
118
+ // eslint-disable-next-line no-console -- intentional degradation observability
119
+ console.warn(`[play-mutator] turn ${turn}: dropped malformed items — entities ${rawE}->${keptE}, edges ${rawEd}->${keptEd}, slots ${rawS}->${keptS}`);
120
+ }
121
+ }
122
+ export class PlaySceneRendererAgent extends BaseAgent {
123
+ constructor(ctx) {
124
+ super(ctx);
125
+ }
126
+ get name() {
127
+ return "play-scene-renderer";
128
+ }
129
+ async render(input) {
130
+ const language = input.language ?? "zh";
131
+ const messages = [
132
+ { role: "system", content: buildSceneRendererSystemPrompt(input.mode ?? "open", language) },
133
+ { role: "user", content: buildSceneRendererUserPrompt(input, language) },
134
+ ];
135
+ // The renderer must NEVER throw — a hiccup here used to break the turn AND leave
136
+ // a half-committed world (event/state written before render). Retry transient
137
+ // upstream errors, ask once for strict JSON if the output wasn't, then fail open
138
+ // to the raw prose as the scene. (Bigger token budget so long literary scenes
139
+ // don't get truncated mid-JSON, which is itself a common parse failure.)
140
+ let lastContent = "";
141
+ for (let attempt = 0; attempt < 3; attempt++) {
142
+ let content = "";
143
+ try {
144
+ const response = await chatWithRetry(() => this.chat(messages, { temperature: 0.45, maxTokens: 4096 }));
145
+ content = response.content;
146
+ }
147
+ catch {
148
+ break; // transient retries exhausted → fail open below
149
+ }
150
+ lastContent = content || lastContent;
151
+ const parsed = trySceneParse(content);
152
+ if (parsed)
153
+ return parsed;
154
+ messages.push({ role: "assistant", content }, {
155
+ role: "user",
156
+ content: language === "en"
157
+ ? 'That was not strict JSON. Output ONLY one JSON object {"sceneText": "...", "suggestedActions": ["..."]} and nothing else.'
158
+ : '上面不是严格 JSON。只输出一个 JSON 对象 {"sceneText": "...", "suggestedActions": ["..."]},不要任何其他文字。',
159
+ });
160
+ }
161
+ const proseFallback = lastContent
162
+ .trim()
163
+ .replace(/^```(?:json)?\s*/i, "")
164
+ .replace(/```\s*$/i, "")
165
+ .trim();
166
+ return {
167
+ sceneText: proseFallback || (language === "en" ? "(The moment holds, unresolved.)" : "(这一拍悬着,没有落定。)"),
168
+ suggestedActions: [],
169
+ };
170
+ }
171
+ }
172
+ export class PlaySceneReconcilerAgent extends BaseAgent {
173
+ constructor(ctx) {
174
+ super(ctx);
175
+ }
176
+ get name() {
177
+ return "play-scene-reconciler";
178
+ }
179
+ async reconcile(input) {
180
+ const language = input.language ?? "zh";
181
+ const eventId = `evt-${input.turn}`;
182
+ const empty = emptyReconciliation(input.turn, PlayActionIntentSchema.parse(input.action).actionKind);
183
+ const messages = [
184
+ { role: "system", content: buildSceneReconcilerSystemPrompt(language) },
185
+ { role: "user", content: buildSceneReconcilerUserPrompt(input, language) },
186
+ ];
187
+ try {
188
+ const response = await chatWithRetry(() => this.chat(messages, { temperature: 0.1, maxTokens: 2048 }));
189
+ const parsed = PlayMutationSchema.parse(parseJson(response.content));
190
+ return {
191
+ ...parsed,
192
+ eventId: parsed.eventId || eventId,
193
+ turn: parsed.turn || input.turn,
194
+ actionKind: parsed.actionKind || PlayActionIntentSchema.parse(input.action).actionKind,
195
+ };
196
+ }
197
+ catch {
198
+ return empty;
199
+ }
200
+ }
201
+ }
202
+ function emptyReconciliation(turn, actionKind) {
203
+ return {
204
+ eventId: `evt-${turn}`,
205
+ turn,
206
+ actionKind,
207
+ summary: "",
208
+ entities: { upsert: [] },
209
+ edges: { upsert: [], expire: [] },
210
+ stateSlots: { upsert: [] },
211
+ evidence: { transitions: [] },
212
+ blocked: false,
213
+ blockedReason: "",
214
+ notes: [],
215
+ };
216
+ }
217
+ function buildSceneReconcilerSystemPrompt(language) {
218
+ if (language === "en") {
219
+ return [
220
+ "You reconcile an interactive-fiction scene with the world graph.",
221
+ "Compare the rendered prose against the already applied changes and current state summary.",
222
+ "If the prose introduced a concrete named object, clue, evidence, location, organization, or person that is not represented in the applied changes/current state, output ONLY supplemental PlayMutation entries for those missing graph facts.",
223
+ "Do not rewrite prose. Do not invent facts that are not in the rendered scene. If nothing is missing, output an empty PlayMutation with empty arrays.",
224
+ "Use the same eventId/turn/actionKind. For tangible things the player now physically holds, add a holding edge from actor_player with value.role=\"holding\"; if the target is evidence/clue/claim/proof_chain rather than an item, also set value.physical=true. Observed phenomena or learned facts are not holdings.",
225
+ "Output strict JSON matching PlayMutation.",
226
+ ].join("\n");
227
+ }
228
+ return [
229
+ "你负责把互动小说正文和世界图谱对齐。",
230
+ "对照已经应用的本回合变化、当前状态摘要和最终正文。",
231
+ "如果正文里出现了具体且具名的新物件、线索、证据、地点、组织或人物,但它还没有体现在已应用变化/当前状态里,只输出这些缺失图谱事实的补充 PlayMutation。",
232
+ "不要改正文,不要发明正文没有的事实。没有缺失就输出空的 PlayMutation,各数组留空。",
233
+ "沿用同一个 eventId/turn/actionKind。玩家获得或拿在手里的实物,需要补一条 actor_player 指向该实体、value.role=\"holding\" 的 edge;如果目标是 evidence/clue/claim/proof_chain 而不是 item,还要设置 value.physical=true。观察到的现象或知道的信息不是持有物。",
234
+ "输出严格 JSON,必须符合 PlayMutation。",
235
+ ].join("\n");
236
+ }
237
+ function buildSceneReconcilerUserPrompt(input, language) {
238
+ const actionKind = PlayActionIntentSchema.parse(input.action).actionKind;
239
+ const eventId = `evt-${input.turn}`;
240
+ if (language === "en") {
241
+ return [
242
+ `eventId: ${eventId}`,
243
+ `turn: ${input.turn}`,
244
+ `actionKind: ${actionKind}`,
245
+ "",
246
+ ...(input.worldPremise ? ["World setting:", input.worldPremise, ""] : []),
247
+ "Player input:",
248
+ input.input,
249
+ "",
250
+ "Current context before this turn:",
251
+ input.context,
252
+ "",
253
+ "Applied mutation:",
254
+ JSON.stringify(PlayMutationSchema.parse(input.mutation), null, 2),
255
+ "",
256
+ "Current state summary:",
257
+ input.stateBrief,
258
+ "",
259
+ "Rendered scene:",
260
+ input.sceneText,
261
+ ].join("\n");
262
+ }
263
+ return [
264
+ `eventId: ${eventId}`,
265
+ `turn: ${input.turn}`,
266
+ `actionKind: ${actionKind}`,
267
+ "",
268
+ ...(input.worldPremise ? ["世界设定:", input.worldPremise, ""] : []),
269
+ "玩家输入:",
270
+ input.input,
271
+ "",
272
+ "本回合前的当前上下文:",
273
+ input.context,
274
+ "",
275
+ "已应用 mutation:",
276
+ JSON.stringify(PlayMutationSchema.parse(input.mutation), null, 2),
277
+ "",
278
+ "当前状态摘要:",
279
+ input.stateBrief,
280
+ "",
281
+ "最终正文:",
282
+ input.sceneText,
283
+ ].join("\n");
284
+ }
285
+ function buildActionInterpreterSystemPrompt(language) {
286
+ if (language === "en") {
287
+ return [
288
+ "You are an interactive-fiction action interpreter.",
289
+ "Your job is to normalize one line of the player's natural language into one of five action kinds: look / say / move / do / wait.",
290
+ "Do not add drama for the player, do not advance the plot, do not write scene prose.",
291
+ "look = observe/examine/recall a clue; say = speak/probe/confront; move = move to a location; do = perform an action/use an item/investigate; wait = wait/stall/watch.",
292
+ "Output strict JSON, no explanation.",
293
+ ].join("\n");
294
+ }
295
+ return [
296
+ "你是互动小说动作理解器。",
297
+ "你的任务是把玩家一句自然语言,归一成五类动作之一:look / say / move / do / wait。",
298
+ "不要替玩家加戏,不要直接推进剧情,不要写场景正文。",
299
+ "look=观察/检查/回忆线索;say=说话/试探/质问;move=移动到地点;do=执行动作/使用物品/调查;wait=等待/拖延/旁观。",
300
+ "输出严格 JSON,不要解释。",
301
+ ].join("\n");
302
+ }
303
+ function buildActionInterpreterUserPrompt(input, language) {
304
+ if (language === "en") {
305
+ return [
306
+ "Current scene:",
307
+ input.sceneBrief,
308
+ "",
309
+ "Player input:",
310
+ input.input,
311
+ "",
312
+ "Output fields: actionKind, targetEntityLabel?, targetLocationLabel?, intent, manner, risk, ambiguity, secondaryActions.",
313
+ ].join("\n");
314
+ }
315
+ return [
316
+ "当前场景:",
317
+ input.sceneBrief,
318
+ "",
319
+ "玩家输入:",
320
+ input.input,
321
+ "",
322
+ "输出字段:actionKind, targetEntityLabel?, targetLocationLabel?, intent, manner, risk, ambiguity, secondaryActions。",
323
+ ].join("\n");
324
+ }
325
+ function buildWorldMutatorSystemPrompt(language) {
326
+ if (language === "en") {
327
+ return [
328
+ "You are an interactive-fiction world-state drafter.",
329
+ "Based only on the player's action and the current context, propose this turn's possible state changes as a draft.",
330
+ "Do not write final prose; do not commit to the store on the reducer's behalf; do not let key states jump to completion out of nowhere.",
331
+ "One player input advances one adjacent beat. If the player gives a chain of actions, apply only the literal chain up to the nearest new pressure point; do not skip through off-screen aftermath, rewards, or resolution beyond what the input directly attempts.",
332
+ "Do not leap over the process. If the player runs toward, reaches for, uses, opens, or confronts something, account for the movement, resistance, interruption, or immediate pressure inside this same beat instead of jumping straight to an after-the-fact state.",
333
+ "This engine is genre-neutral: romance, adventure, wuxia, mystery, slice-of-life all use the same structure. Entity types: actor/location/item/evidence/clue/claim/proof_chain/organization/rule/scene/event — use as needed.",
334
+ "Give every new or important entity a one-line summary (who/what it is and why it matters), not just a status word — the player expands this summary in the side panel.",
335
+ "Tangible things the player discovers or holds (a clue, a document, a weapon, a token, key evidence) MUST be their own entity (item/evidence/clue), never folded into a person's status — only then can they enter the player's holdings and be tracked. Observed phenomena, knowledge, impressions, or environmental signs are NOT holdings.",
336
+ "Use entity.status to record state progress for any genre, with status words suited to this world's genre, advancing step by step without skipping (e.g. relationship: stranger -> curious -> attracted -> lover; injury: healthy -> bleeding -> critical; clue: found -> collected -> confirmed).",
337
+ "The player entity id is fixed: always use id actor_player for the player character. Never rename this id; only replace its label, summary, and status with this world's player identity.",
338
+ "Whenever a meaningful relationship forms or shifts between entities (ally / rival / kin / suspicion / debt / master-servant …), record it in edges.upsert as {\"fromId\":\"<entity>\",\"type\":\"<relation>\",\"toId\":\"<entity>\",\"value\":{\"role\":\"relation\"}} — this is the ONLY source for the relationship panel, so over-record rather than skip; add a fresh edge when a relationship changes.",
339
+ "When the player physically holds/carries/keeps/takes a tangible thing, record an edge from actor_player to that entity and set value.role=\"holding\". If the held target is evidence/clue/claim/proof_chain rather than an item, also set value.physical=true. If the player only observes or learns something, use value.role=\"observed\" or a normal relation, never holding.",
340
+ "The current context may include an entity roster. Reuse those exact ids in entities, edges, evidence, and stateSlots. If you only know a name, use the exact roster label; never invent a new id for the same person/thing (or the panel shows duplicates).",
341
+ "State tracking is optional and governed by the user's world contract. If the world contract rejects stats, numeric panels, levels, RPG framing, or quantified meters, do NOT output stateSlots; express progress as natural-language entity.status / summary / evidence transitions instead.",
342
+ "When stateSlots are appropriate, prefer natural-language values unless the user explicitly asked for quantitative tracking or the fiction contains a concrete count/clock/deadline. Do not create numbers just because the schema supports them.",
343
+ "Early on (the first few turns), seed only the state the premise already establishes: a concrete deadline may become a timer slot if the world permits quantified tracking; the central mystery/objective -> its first clue/evidence entity; already-named key characters -> actor entities with a one-line summary. Don't leave the opening world nearly empty.",
344
+ "Restraint: only create entities and meters the story actually makes real — never invent gratuitous stats or items just to fill the panel.",
345
+ "Only use evidence.transitions for the evidence lifecycle when this world is genuinely an investigation/mystery; otherwise leave it empty.",
346
+ "If the player's action is invalid or information is insufficient, set blocked=true and write blockedReason.",
347
+ "Time is a synchronization axis, not a fixed tick. For every non-opening turn, set timeAdvance with: elapsed = the natural-language duration spent by this action; anchor = the world time/phase after the action if the world has a clock, season, phase, day/night, retreat period, deadline, or other temporal anchor; rationale = why this duration is right; synchronized = what relevant NPCs/places/pressures changed during the same elapsed time. A glance may pass seconds, a trip half a day, cultivation three years — obey the user's world contract; never invent a universal turn length.",
348
+ "Output strict JSON matching PlayMutation: eventId, turn, actionKind, summary, timeAdvance, entities, edges, stateSlots, evidence, blocked, blockedReason, notes.",
349
+ "The following is only a JSON-shape example. Do not reuse its labels, names, or story facts in the actual world; the reserved player id actor_player is the only example id you must keep for the player entity:",
350
+ `{"eventId":"evt-1","turn":1,"actionKind":"look","summary":"The player-character finds a sample clue and a sample key.","timeAdvance":{"elapsed":"a few breaths","anchor":"still in the same rain-soaked minute","rationale":"The player only examined the immediate scene.","synchronized":["The counterpart notices the pause but does not act openly yet."]},"entities":{"upsert":[{"id":"actor_player","type":"actor","label":"player-character","summary":"Reserved player entity id; replace label, summary, and status with the current world's player identity.","status":"alert","updatedEventId":"evt-1"},{"id":"actor_counterpart","type":"actor","label":"counterpart","summary":"Placeholder for a relevant person in the current world; replace with the real roster id/label.","status":"guarded","updatedEventId":"evt-1"},{"id":"evidence_sample_clue","type":"evidence","label":"sample clue","summary":"A tangible clue discovered this turn; replace with a real object from the scene.","status":"seen","updatedEventId":"evt-1"},{"id":"item_sample_key","type":"item","label":"sample key","summary":"A tangible item collected this turn; replace with a real object from the scene.","status":"collected","updatedEventId":"evt-1"}]},"edges":{"upsert":[{"fromId":"actor_player","type":"suspicious_of","toId":"actor_counterpart","value":{"role":"relation"}},{"fromId":"actor_player","type":"holds","toId":"item_sample_key","value":{"role":"holding"}},{"fromId":"actor_player","type":"holds","toId":"evidence_sample_clue","value":{"role":"holding","physical":true}}]},"stateSlots":{"upsert":[{"id":"slot_sample_timer","kind":"timer","label":"sample timer","value":3,"updatedEventId":"evt-1"}]}}`,
351
+ ].join("\n");
352
+ }
353
+ return [
354
+ "你是互动小说世界状态草案员。",
355
+ "你只根据玩家动作和当前上下文,提出本回合可能发生的状态变化草案。",
356
+ "不要写最终正文;不要越权替 reducer 落库;不要凭空让关键状态一步到位。",
357
+ "一个玩家输入只推进相邻一拍。玩家把多个动作写在一句里时,只处理原话直接包含的动作链,到最近的新压力点就停;不要跳过过程去写场外后果、完整回报或问题解决。",
358
+ "不要替玩家越过过程。玩家奔向、伸手、使用、打开或对峙某物时,必须把移动、阻力、干扰、敌人压近或即时压力算进这一拍,不能直接跳到事后状态。",
359
+ "这套引擎是品类中立的:恋情、冒险、武侠、悬疑、日常等都用同一套结构表达。实体类型用 actor/location/item/evidence/clue/claim/proof_chain/organization/rule/scene/event,按需选用。",
360
+ "给每个新出现或重要的实体写一句 summary(他是谁/这是什么、为什么重要),不要只靠 status 一句话——玩家会在侧栏里展开看这条 summary。",
361
+ "玩家发现或获得的「实物」(线索、文件、凶器、信物、关键证据等)必须建成独立实体(item/evidence/clue),不要塞进某个人物的 status——这样它们才能进入玩家的「持有物」并被追踪。观察到的现象、知识、印象、环境征兆不是持有物。",
362
+ "用 entity.status 记录任意品类的状态推进,状态词按这个世界的题材自定,循序渐进、不要跳级(例如关系:陌生→好奇→心动→恋人;伤势:健康→流血→重伤;线索:发现→收集→坐实)。",
363
+ "玩家本人实体 id 是固定保留字:必须始终用 actor_player。绝不要把它改成本局名字或别的 id;只替换 label、summary、status 为本局玩家身份。",
364
+ "人物之间(或人物与组织/地点之间)一旦形成或改变有意义的关系(盟友/敌对/亲属/怀疑/欠债/上下级/师徒等),就在 edges.upsert 里记一条 {\"fromId\":\"<实体>\",\"type\":\"<关系词>\",\"toId\":\"<实体>\",\"value\":{\"role\":\"relation\"}}——这是侧栏「关系网」的唯一来源,宁可多记勿漏;关系一旦变化(如怀疑→敌对)也补一条新边。",
365
+ "玩家实际持有/携带/收进包里/拿走某个实物时,必须记一条 actor_player 指向该实体的 edge,并设置 value.role=\"holding\"。如果目标是 evidence/clue/claim/proof_chain 而不是 item,还必须设置 value.physical=true。玩家只是观察或知道某件事时,用 value.role=\"observed\" 或普通关系,绝不能写成 holding。",
366
+ "当前上下文可能包含实体名册。entities、edges、evidence、stateSlots 都优先复用名册里的精确 id;如果只知道名字,就用名册里的精确 label。绝不要把同一个人/物换个新 id 重建(否则侧栏会出现重复节点)。",
367
+ "状态追踪是可选的,必须服从用户的世界契约。世界契约禁止数值、面板、等级、RPG 化或量化进度时,不要输出 stateSlots;改用 entity.status / summary / evidence transitions 写自然语言状态。",
368
+ "确实需要 stateSlots 时,也优先用自然语言 value;只有用户明确要求量化追踪,或文本里存在具体倒计时/钟点/数量,才写数字。不要因为 schema 支持就硬造数值。",
369
+ "开局阶段(前几回合),只播种前提里已经确立的状态:明确期限在世界允许量化时才可成为 timer;核心谜题/目标物→第一条 clue/evidence 实体;已点名的关键人物→actor 实体并配一句 summary。不要让开场世界几乎空着。",
370
+ "克制:只建剧情真正落地的实体和数值,不要为了填满侧栏而硬造属性或物品。",
371
+ "只有当这个世界确实是调查/推理题材时,才用 evidence.transitions 走证据生命周期;其他题材留空即可。",
372
+ "如果玩家动作无效或信息不足,blocked=true 并写 blockedReason。",
373
+ "时间是世界同步轴,不是固定 tick。每个非开场回合都要写 timeAdvance:elapsed=本动作按语义经过了多久;anchor=动作结束后世界处在什么时间/阶段(若本局有钟点、昼夜、季节、闭关期、期限、潮汐、巡逻节奏等时间锚点);rationale=为什么是这段时间;synchronized=同一段时间里相关人物/地点/压力发生了什么同步变化。看一眼可能几息,赶路可能半天,闭关可能三年——遵守用户的世界契约,绝不要发明统一回合长度。",
374
+ "输出严格 JSON,必须符合 PlayMutation:eventId, turn, actionKind, summary, timeAdvance, entities, edges, stateSlots, evidence, blocked, blockedReason, notes。",
375
+ "下面的范例只示结构,不得复用范例里的名称、人名或剧情事实;唯一必须保留的示例 id 是玩家本人 actor_player:",
376
+ `{"eventId":"evt-1","turn":1,"actionKind":"look","summary":"玩家角色发现了一个示例线索和一个示例道具。","timeAdvance":{"elapsed":"几息","anchor":"仍在同一个雨夜片刻里","rationale":"玩家只是贴近观察眼前物件,没有离开现场。","synchronized":["相关人物注意到玩家停顿,但还没有公开阻拦。"]},"entities":{"upsert":[{"id":"actor_player","type":"actor","label":"玩家角色","summary":"玩家本人固定实体 id;实际输出只替换 label、summary、status 为本局玩家身份。","status":"警觉","updatedEventId":"evt-1"},{"id":"actor_counterpart","type":"actor","label":"相关人物","summary":"当前世界中相关人物的占位示例;实际输出必须替换为本局真实实体。","status":"戒备","updatedEventId":"evt-1"},{"id":"evidence_sample_clue","type":"evidence","label":"示例线索","summary":"本回合发现的实物线索示例;实际输出必须替换为场景里的真实物件。","status":"已发现","updatedEventId":"evt-1"},{"id":"item_sample_key","type":"item","label":"示例钥匙","summary":"本回合获得的实物道具示例;实际输出必须替换为场景里的真实物件。","status":"已收集","updatedEventId":"evt-1"}]},"edges":{"upsert":[{"fromId":"actor_player","type":"怀疑","toId":"actor_counterpart","value":{"role":"relation"}},{"fromId":"actor_player","type":"持有","toId":"item_sample_key","value":{"role":"holding"}},{"fromId":"actor_player","type":"持有","toId":"evidence_sample_clue","value":{"role":"holding","physical":true}}]},"stateSlots":{"upsert":[{"id":"slot_sample_timer","kind":"timer","label":"示例倒计时","value":3,"updatedEventId":"evt-1"}]}}`,
377
+ ].join("\n");
378
+ }
379
+ function buildWorldMutatorUserPrompt(input, language) {
380
+ if (language === "en") {
381
+ return [
382
+ `turn: ${input.turn}`,
383
+ "Player's words:",
384
+ input.input,
385
+ "",
386
+ "Action interpretation:",
387
+ JSON.stringify(PlayActionIntentSchema.parse(input.action), null, 2),
388
+ "",
389
+ "Current context:",
390
+ input.context,
391
+ "",
392
+ "Requirement: use eventId evt-" + input.turn + "; every new or referenced entity id must be stable, readable, and short.",
393
+ ].join("\n");
394
+ }
395
+ return [
396
+ `turn: ${input.turn}`,
397
+ "玩家原话:",
398
+ input.input,
399
+ "",
400
+ "动作理解:",
401
+ JSON.stringify(PlayActionIntentSchema.parse(input.action), null, 2),
402
+ "",
403
+ "当前上下文:",
404
+ input.context,
405
+ "",
406
+ "要求:eventId 使用 evt-" + input.turn + ";所有新增或引用的实体 id 要稳定、可读、短小。",
407
+ ].join("\n");
408
+ }
409
+ export function buildSceneRendererSystemPrompt(mode = "open", language = "zh") {
410
+ if (language === "en") {
411
+ const base = [
412
+ "You are an interactive-fiction scene-response author.",
413
+ "Write the response only from the already-applied state; do not overturn the reducer's results.",
414
+ "Concrete new objects, clues, evidence, locations, organizations, or named people can only appear if they are already present in Applied changes or Current state summary. If the prose needs a new concrete thing, it must have been created by the mutator first; otherwise describe mood, pressure, or an unnamed detail instead.",
415
+ "It should read like a playable novel — action, senses, pressure, breathing room — never a system log and never a menu-narration that herds the player into picking something.",
416
+ "Bridge from the player's action first. Even though the state is already applied, do not start as if everything is already over; write the follow-through, contact, resistance, interruption, and immediate consequence so the action connects to the new state.",
417
+ "Do not jump straight to the after-action result, and do not write epilogue-style summaries, morals, or closing-theme lines. End on an immediate sensory pressure, changed position, exposed detail, or nearby consequence.",
418
+ "Stay strictly inside the world the premise established — era, place, tech level, genre tone must stay consistent. Never introduce elements that don't belong: a modern-city story must not grow night-watchmen / oil lamps; a historical/wuxia story must not sprout phones / cars / computers. Every detail lands inside the given world.",
419
+ // Presence is a valid turn.
420
+ "The player is not always 'acting'. When they merely observe, linger, feel, idle-chat, or do nothing, give an immersive beat — one living detail, a smell, a bystander's small movement, a thought crossing their mind. NEVER say 'there's nothing more to see' / 'you already looked' / 'stop stalling', and never nag them to hurry up and act. Let the beat breathe.",
421
+ // The world runs on its own clock.
422
+ "The world is not inert. Time moves, the deadline closes in, side characters act on their own, something stirs in the distance, off-screen events happen. Even on a turn where the player did nothing, nudge the world forward a little — so the pull to move forward comes from the STORY (the trail goes cold / the deadline nears / someone moved first), not from the narration pestering them to choose.",
423
+ "If Current state summary includes a Time section, treat elapsed and anchor as canonical. Render the scene after exactly that elapsed interval, at that resulting world time/phase, and include the synchronized pressure/character movement naturally in prose. Do not invent another clock reading, another elapsed amount, or a fixed tick label.",
424
+ "Respect negative player intent as fact. If the player's words say they did NOT touch, open, take, leave, attack, or speak, do not narrate them doing it by implication; write the restraint itself and the world's response to that restraint.",
425
+ // Don't herd — and don't smuggle the herding into a character's mouth.
426
+ "Do not end with herding questions like 'What do you do?' / 'Which way?'. And do NOT route the same pressure through a companion who keeps listing options ('go to A, or B?') — a sidekick is not an options dispenser. Most beats should NOT end on a pending question at all: land on an image, a sound, a smell, or a hanging tension, and stop. Only when the player is genuinely at a fork that demands a decision may a question surface — sparingly.",
427
+ "sceneText is PURE narrative prose. Never put a choice list in the body — no 'Options:' / 'What do you do?' followed by A/B/C, no '- ' bulleted options — no matter how urgent or fork-like the moment is (a tense escape is NOT an excuse for a menu). Weave the available routes into the scene itself (the bamboo by the wall, the half-open skylight, the alley toward the river) and let the player decide by free input. Any springboard goes ONLY in the suggestedActions field, kept sparse — never a menu in the prose.",
428
+ "Example (applies even at a life-or-death beat) — [WRONG, never write this] 'The zombie lunges, the axe is stuck. React now:\\n- yank the axe and swing\\n- squeeze sideways through\\n- roll back'; [RIGHT] 'Its claws are already spread, the sour reek of rot in your nose. Your axe is wedged in the twenty-centimeter gap of the door, and it won't come free. Its weight bears down—'. Take the danger to its peak, then stop, and hand the 'what now' entirely to the player's free input — never list options for them.",
429
+ ];
430
+ const actionsRule = mode === "guided"
431
+ ? "suggestedActions: give 0-3 as optional springboards ('you could…'), ONLY at a genuine decision point — not every turn. They are hints, not the only way forward; the player can type freely or just stay put at any time."
432
+ : "suggestedActions: 0-3 short hints, optional, never restricting the player's input; omit them when there is no real decision point.";
433
+ return [...base, actionsRule, "Output strict JSON: sceneText, suggestedActions."].join("\n");
434
+ }
435
+ const base = [
436
+ "你是互动小说场景回应作者。",
437
+ "你只能根据已经应用后的状态写回应,不要推翻 reducer 结果。",
438
+ "具体的新物件、线索、证据、地点、组织、具名人物,只能来自「已应用的本回合变化」或「当前状态摘要」。如果正文需要一个新的具体东西,它必须先由 mutator 建成实体;否则只写氛围、压力或不具名的细节。",
439
+ "回应要像可玩的小说:有动作、感官、压迫、留白;绝不是系统日志,也绝不是把玩家往'快做个选择'上赶的菜单旁白。",
440
+ "先承接玩家动作。即使状态已经应用,也不要直接跳到动作完成后;要写出动作的跟进、接触、阻力、打断和即时后果,让玩家原话自然接到新状态。",
441
+ "不要写总结性尾声、主题升华或收束感很强的结语。每回合停在眼前的感官压力、位置变化、暴露出的细节或近处后果上。",
442
+ "严格守住前提确立的那个世界——年代、地点、技术水平、题材基调都要一致。绝不要引入不属于它的元素:现代都市故事别冒出更夫/油灯/二更天,古代武侠故事别冒出手机/汽车/电脑。每一拍的细节都落在前提给定的那个世界里。",
443
+ // 在场即合法
444
+ "玩家不一定每回合都在'行动'。当他只是观察、停留、感受、闲聊、发呆,给一段有沉浸感的回应——一个活的细节、一缕气味、旁人的一个小动作、心里掠过的一个念头。绝不要说'这里没什么可看的了''你已经看过了''别磨蹭',也绝不要催他快点行动。让这一拍能呼吸。",
445
+ // 世界自走
446
+ "世界不是死的:时间在走、期限在逼近、配角会自己做事、远处会有动静、场外会发生事。哪怕玩家这一拍什么都没做,也让世界往前动一点点——让'前进的压力'来自故事本身(再不动线索就凉了/期限就到了/有人先动了),而不是来自旁白催他选。",
447
+ "如果当前状态摘要里有 Time/时间段,elapsed 和 anchor 是权威时间:正文必须按这段经过时长、这个动作后的世界时间/阶段来写,并把同步发生的压力、人物移动、远处变化自然溶进正文。不得另写一个钟点、另写一段经过时长,也不要写成固定 tick、回合标签或 UI 提示。",
448
+ "玩家原话里的否定动作就是事实。玩家说没有触碰、没有打开、没有拿走、没有离开、没有攻击、没有开口时,正文不能暗示他做了这些事;要写克制本身,以及世界对这份克制的反应。",
449
+ // 不催不逼,也别借角色之口变相逼选
450
+ "不要用'你想怎么做?''你打算往哪走?'这类逼问句收尾;也不要把同样的催促塞进身边同伴的嘴里('要去 A 还是 B?''去不去问他?')——同伴不是'选项播报员',别让他每段都给你列下一步。**多数 beat 根本不该以一个待决问题结束**:落在一个画面、一处声响、一缕气味或悬着的张力上,然后停住。只有当玩家真的走到了非选不可的岔口,才偶尔点出选择。",
451
+ "sceneText 必须是纯叙事散文。**正文里绝不允许出现'选项:''你想怎么做?'后跟 A/B/C 清单,也不允许用'- '列出可选动作**——无论局势多紧急、多像一个岔路口都不行(被围杀的逃命戏也不是甩菜单的借口)。可走的路要自然融进场景描写里(墙下的竹丛、半开的天窗、通向河边的巷尾),让玩家用自由输入自己决定。要给跳板只放进 suggestedActions 字段、少而精;正文里一个选项清单都不要。",
452
+ "对比一例(生死关头也照此办)——【错,绝不要这样写】「丧尸扑来,斧头卡住。你必须立刻做出反应:\\n- 拔斧劈砍\\n- 侧身挤过\\n- 后翻闪避」;【对】「它的爪子已经张开,腐臭的酸味灌进你的鼻腔。你的斧头死死卡在那道二十厘米宽的门缝里,一时拔不出来。它的重心压下来了——」。把险境写到极致,然后停住,把'怎么办'整个交给玩家的自由输入;一个选项都不要替他列。",
453
+ ];
454
+ const actionsRule = mode === "guided"
455
+ ? "suggestedActions:给 0-3 个,作为'你也许可以这样做'的跳板——只在真正出现抉择点时给,不必每回合都给;它们是参考、不是唯一前进方式,玩家随时可以自由输入、也可以只是待着。"
456
+ : "suggestedActions:0-3 个短句,可选,只是参考、不限制玩家输入;没有明显抉择点时就不给。";
457
+ return [...base, actionsRule, "输出严格 JSON:sceneText, suggestedActions。"].join("\n");
458
+ }
459
+ function buildSceneRendererUserPrompt(input, language) {
460
+ const premise = input.worldPremise?.trim();
461
+ if (language === "en") {
462
+ return [
463
+ ...(premise ? ["World setting (always obey):", premise, ""] : []),
464
+ "Player's words:",
465
+ input.input,
466
+ "",
467
+ "Action:",
468
+ JSON.stringify(PlayActionIntentSchema.parse(input.action), null, 2),
469
+ "",
470
+ "Applied changes this turn:",
471
+ input.mutationSummary,
472
+ "",
473
+ "Current state summary:",
474
+ input.stateBrief,
475
+ input.replayContext ? ["", "Replay constraints:", input.replayContext].join("\n") : "",
476
+ ].join("\n");
477
+ }
478
+ return [
479
+ ...(premise ? ["世界设定(始终遵守):", premise, ""] : []),
480
+ "玩家原话:",
481
+ input.input,
482
+ "",
483
+ "动作:",
484
+ JSON.stringify(PlayActionIntentSchema.parse(input.action), null, 2),
485
+ "",
486
+ "已应用的本回合变化:",
487
+ input.mutationSummary,
488
+ "",
489
+ "当前状态摘要:",
490
+ input.stateBrief,
491
+ input.replayContext ? ["", "重写约束:", input.replayContext].join("\n") : "",
492
+ ].join("\n");
493
+ }
494
+ function parseJson(raw) {
495
+ const trimmed = raw.trim()
496
+ .replace(/^```(?:json)?\s*/i, "")
497
+ .replace(/```\s*$/i, "")
498
+ .trim();
499
+ try {
500
+ return JSON.parse(trimmed);
501
+ }
502
+ catch {
503
+ const start = trimmed.indexOf("{");
504
+ const end = trimmed.lastIndexOf("}");
505
+ if (start >= 0 && end > start) {
506
+ return JSON.parse(trimmed.slice(start, end + 1));
507
+ }
508
+ throw new Error("Play agent did not return JSON.");
509
+ }
510
+ }
511
+ //# sourceMappingURL=play-agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-agents.js","sourceRoot":"","sources":["../../src/play/play-agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type PlayGraphSnapshot } from "./play-file-db.js";
2
+ import type { PlayReducerDB } from "./play-reducer.js";
3
+ export interface PlayGraphDB extends PlayReducerDB {
4
+ readonly snapshot: () => PlayGraphSnapshot;
5
+ readonly replaceWithSnapshot: (snapshot: PlayGraphSnapshot) => void;
6
+ readonly close?: () => void;
7
+ }
8
+ export declare function createPlayDB(runDir: string): PlayGraphDB;
9
+ //# sourceMappingURL=play-db-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-db-factory.d.ts","sourceRoot":"","sources":["../../src/play/play-db-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,iBAAiB,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CASxD"}
@@ -0,0 +1,18 @@
1
+ import { PlayDB } from "./play-db.js";
2
+ import { PlayFileDB } from "./play-file-db.js";
3
+ export function createPlayDB(runDir) {
4
+ try {
5
+ return new PlayDB(runDir);
6
+ }
7
+ catch (error) {
8
+ if (isMissingNodeSqliteError(error)) {
9
+ return new PlayFileDB(runDir);
10
+ }
11
+ throw error;
12
+ }
13
+ }
14
+ function isMissingNodeSqliteError(error) {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ return message.includes("node:sqlite") || message.includes("No such built-in module");
17
+ }
18
+ //# sourceMappingURL=play-db-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-db-factory.js","sourceRoot":"","sources":["../../src/play/play-db-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,UAAU,EAA0B,MAAM,mBAAmB,CAAC;AASvE,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAc;IAC9C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC;AACxF,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type PlayEdge, type PlayEdgeInput, type PlayEntity, type PlayEntityInput, type PlayEvent, type PlayEventInput, type PlayStateSlot, type PlayStateSlotInput } from "../models/play.js";
2
+ import type { PlayGraphSnapshot } from "./play-file-db.js";
3
+ export declare class PlayDB {
4
+ private readonly db;
5
+ constructor(runDir: string);
6
+ private migrate;
7
+ upsertEntity(entity: PlayEntityInput): void;
8
+ getEntity(id: string): PlayEntity | null;
9
+ upsertEdge(edge: PlayEdgeInput): void;
10
+ expireEdge(edgeId: string, validUntilEventId: string): void;
11
+ getCurrentEdgesForEntity(entityId: string): PlayEdge[];
12
+ getEvidenceForClaim(claimId: string): PlayEntity[];
13
+ upsertStateSlot(slot: PlayStateSlotInput): void;
14
+ getStateSlotsForEntity(entityId: string): PlayStateSlot[];
15
+ recordEvent(event: PlayEventInput): void;
16
+ getEvent(id: string): PlayEvent | null;
17
+ snapshot(): PlayGraphSnapshot;
18
+ replaceWithSnapshot(snapshot: PlayGraphSnapshot): void;
19
+ transaction<T>(fn: () => T): T;
20
+ close(): void;
21
+ }
22
+ //# sourceMappingURL=play-db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"play-db.d.ts","sourceRoot":"","sources":["../../src/play/play-db.ts"],"names":[],"mappings":"AAGA,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA8C3D,qBAAa,MAAM;IAEjB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAM;gBAEb,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,OAAO;IAmDf,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAgB3C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAOxC,UAAU,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI;IAsBrC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAM3D,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;IAUtD,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;IAclD,eAAe,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI;IAe/C,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE;IAUzD,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAexC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOtC,QAAQ,IAAI,iBAAiB;IAqB7B,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAatD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAY9B,KAAK,IAAI,IAAI;CAGd"}