@actalk/inkos-core 1.5.0 → 1.6.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 (187) hide show
  1. package/dist/agent/agent-session.d.ts +4 -0
  2. package/dist/agent/agent-session.d.ts.map +1 -1
  3. package/dist/agent/agent-session.js +78 -5
  4. package/dist/agent/agent-session.js.map +1 -1
  5. package/dist/agent/agent-system-prompt.d.ts +2 -0
  6. package/dist/agent/agent-system-prompt.d.ts.map +1 -1
  7. package/dist/agent/agent-system-prompt.js +181 -16
  8. package/dist/agent/agent-system-prompt.js.map +1 -1
  9. package/dist/agent/agent-tools.d.ts +97 -1
  10. package/dist/agent/agent-tools.d.ts.map +1 -1
  11. package/dist/agent/agent-tools.js +434 -3
  12. package/dist/agent/agent-tools.js.map +1 -1
  13. package/dist/agent/film-authoring-tools.d.ts +89 -0
  14. package/dist/agent/film-authoring-tools.d.ts.map +1 -0
  15. package/dist/agent/film-authoring-tools.js +336 -0
  16. package/dist/agent/film-authoring-tools.js.map +1 -0
  17. package/dist/agent/index.d.ts +2 -1
  18. package/dist/agent/index.d.ts.map +1 -1
  19. package/dist/agent/index.js +2 -1
  20. package/dist/agent/index.js.map +1 -1
  21. package/dist/agent/llm-stub.d.ts +19 -0
  22. package/dist/agent/llm-stub.d.ts.map +1 -0
  23. package/dist/agent/llm-stub.js +134 -0
  24. package/dist/agent/llm-stub.js.map +1 -0
  25. package/dist/agents/composer.d.ts.map +1 -1
  26. package/dist/agents/composer.js +24 -12
  27. package/dist/agents/composer.js.map +1 -1
  28. package/dist/agents/researcher.d.ts +38 -0
  29. package/dist/agents/researcher.d.ts.map +1 -0
  30. package/dist/agents/researcher.js +164 -0
  31. package/dist/agents/researcher.js.map +1 -0
  32. package/dist/agents/script-storyboard.d.ts +51 -0
  33. package/dist/agents/script-storyboard.d.ts.map +1 -0
  34. package/dist/agents/script-storyboard.js +368 -0
  35. package/dist/agents/script-storyboard.js.map +1 -0
  36. package/dist/index.d.ts +23 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +24 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/interaction/action-envelope.d.ts +669 -1
  41. package/dist/interaction/action-envelope.d.ts.map +1 -1
  42. package/dist/interaction/action-envelope.js +87 -0
  43. package/dist/interaction/action-envelope.js.map +1 -1
  44. package/dist/interaction/edit-controller.d.ts.map +1 -1
  45. package/dist/interaction/edit-controller.js +91 -1
  46. package/dist/interaction/edit-controller.js.map +1 -1
  47. package/dist/interaction/intents.d.ts +2 -2
  48. package/dist/interaction/project-control.d.ts +1 -1
  49. package/dist/interaction/session-transcript-schema.d.ts +22 -22
  50. package/dist/interaction/session.d.ts +14 -14
  51. package/dist/interaction/session.d.ts.map +1 -1
  52. package/dist/interaction/session.js +1 -1
  53. package/dist/interaction/session.js.map +1 -1
  54. package/dist/interactive-film/authoring-generate.d.ts +4 -0
  55. package/dist/interactive-film/authoring-generate.d.ts.map +1 -0
  56. package/dist/interactive-film/authoring-generate.js +16 -0
  57. package/dist/interactive-film/authoring-generate.js.map +1 -0
  58. package/dist/interactive-film/authoring-store.d.ts +35 -0
  59. package/dist/interactive-film/authoring-store.d.ts.map +1 -0
  60. package/dist/interactive-film/authoring-store.js +114 -0
  61. package/dist/interactive-film/authoring-store.js.map +1 -0
  62. package/dist/interactive-film/authoring-tools.d.ts +9 -0
  63. package/dist/interactive-film/authoring-tools.d.ts.map +1 -0
  64. package/dist/interactive-film/authoring-tools.js +19 -0
  65. package/dist/interactive-film/authoring-tools.js.map +1 -0
  66. package/dist/interactive-film/delta.d.ts +591 -0
  67. package/dist/interactive-film/delta.d.ts.map +1 -0
  68. package/dist/interactive-film/delta.js +54 -0
  69. package/dist/interactive-film/delta.js.map +1 -0
  70. package/dist/interactive-film/emotion.d.ts +20 -0
  71. package/dist/interactive-film/emotion.d.ts.map +1 -0
  72. package/dist/interactive-film/emotion.js +57 -0
  73. package/dist/interactive-film/emotion.js.map +1 -0
  74. package/dist/interactive-film/evaluator.d.ts +7 -0
  75. package/dist/interactive-film/evaluator.d.ts.map +1 -0
  76. package/dist/interactive-film/evaluator.js +40 -0
  77. package/dist/interactive-film/evaluator.js.map +1 -0
  78. package/dist/interactive-film/export-html.d.ts +6 -0
  79. package/dist/interactive-film/export-html.d.ts.map +1 -0
  80. package/dist/interactive-film/export-html.js +84 -0
  81. package/dist/interactive-film/export-html.js.map +1 -0
  82. package/dist/interactive-film/export-ink.d.ts +3 -0
  83. package/dist/interactive-film/export-ink.d.ts.map +1 -0
  84. package/dist/interactive-film/export-ink.js +51 -0
  85. package/dist/interactive-film/export-ink.js.map +1 -0
  86. package/dist/interactive-film/film-context.d.ts +4 -0
  87. package/dist/interactive-film/film-context.d.ts.map +1 -0
  88. package/dist/interactive-film/film-context.js +30 -0
  89. package/dist/interactive-film/film-context.js.map +1 -0
  90. package/dist/interactive-film/generate.d.ts +13 -0
  91. package/dist/interactive-film/generate.d.ts.map +1 -0
  92. package/dist/interactive-film/generate.js +32 -0
  93. package/dist/interactive-film/generate.js.map +1 -0
  94. package/dist/interactive-film/graph-schema.d.ts +819 -0
  95. package/dist/interactive-film/graph-schema.d.ts.map +1 -0
  96. package/dist/interactive-film/graph-schema.js +84 -0
  97. package/dist/interactive-film/graph-schema.js.map +1 -0
  98. package/dist/interactive-film/graph-store.d.ts +5 -0
  99. package/dist/interactive-film/graph-store.d.ts.map +1 -0
  100. package/dist/interactive-film/graph-store.js +24 -0
  101. package/dist/interactive-film/graph-store.js.map +1 -0
  102. package/dist/interactive-film/memory-link.d.ts +9 -0
  103. package/dist/interactive-film/memory-link.d.ts.map +1 -0
  104. package/dist/interactive-film/memory-link.js +15 -0
  105. package/dist/interactive-film/memory-link.js.map +1 -0
  106. package/dist/interactive-film/node-image.d.ts +29 -0
  107. package/dist/interactive-film/node-image.d.ts.map +1 -0
  108. package/dist/interactive-film/node-image.js +52 -0
  109. package/dist/interactive-film/node-image.js.map +1 -0
  110. package/dist/interactive-film/paths.d.ts +14 -0
  111. package/dist/interactive-film/paths.d.ts.map +1 -0
  112. package/dist/interactive-film/paths.js +60 -0
  113. package/dist/interactive-film/paths.js.map +1 -0
  114. package/dist/interactive-film/validation.d.ts +14 -0
  115. package/dist/interactive-film/validation.d.ts.map +1 -0
  116. package/dist/interactive-film/validation.js +292 -0
  117. package/dist/interactive-film/validation.js.map +1 -0
  118. package/dist/llm/provider.d.ts.map +1 -1
  119. package/dist/llm/provider.js +53 -16
  120. package/dist/llm/provider.js.map +1 -1
  121. package/dist/models/book.d.ts +13 -0
  122. package/dist/models/book.d.ts.map +1 -1
  123. package/dist/models/book.js +3 -0
  124. package/dist/models/book.js.map +1 -1
  125. package/dist/models/input-governance.d.ts +14 -5
  126. package/dist/models/input-governance.d.ts.map +1 -1
  127. package/dist/models/input-governance.js +3 -0
  128. package/dist/models/input-governance.js.map +1 -1
  129. package/dist/models/project.d.ts +63 -10
  130. package/dist/models/project.d.ts.map +1 -1
  131. package/dist/models/project.js +11 -0
  132. package/dist/models/project.js.map +1 -1
  133. package/dist/pipeline/runner.js +1 -1
  134. package/dist/pipeline/runner.js.map +1 -1
  135. package/dist/pipeline/script-storyboard-runner.d.ts +127 -0
  136. package/dist/pipeline/script-storyboard-runner.d.ts.map +1 -0
  137. package/dist/pipeline/script-storyboard-runner.js +446 -0
  138. package/dist/pipeline/script-storyboard-runner.js.map +1 -0
  139. package/dist/play/play-agents.d.ts.map +1 -1
  140. package/dist/play/play-agents.js +5 -2
  141. package/dist/play/play-agents.js.map +1 -1
  142. package/dist/play/play-store.d.ts +2 -2
  143. package/dist/skills/builtin-prompts.d.ts +10 -0
  144. package/dist/skills/builtin-prompts.d.ts.map +1 -0
  145. package/dist/skills/builtin-prompts.js +152 -0
  146. package/dist/skills/builtin-prompts.js.map +1 -0
  147. package/dist/skills/builtin.d.ts +3 -0
  148. package/dist/skills/builtin.d.ts.map +1 -0
  149. package/dist/skills/builtin.js +252 -0
  150. package/dist/skills/builtin.js.map +1 -0
  151. package/dist/skills/context-planner.d.ts +17 -0
  152. package/dist/skills/context-planner.d.ts.map +1 -0
  153. package/dist/skills/context-planner.js +48 -0
  154. package/dist/skills/context-planner.js.map +1 -0
  155. package/dist/skills/external-loader.d.ts +20 -0
  156. package/dist/skills/external-loader.d.ts.map +1 -0
  157. package/dist/skills/external-loader.js +126 -0
  158. package/dist/skills/external-loader.js.map +1 -0
  159. package/dist/skills/index.d.ts +8 -0
  160. package/dist/skills/index.d.ts.map +1 -0
  161. package/dist/skills/index.js +8 -0
  162. package/dist/skills/index.js.map +1 -0
  163. package/dist/skills/prompt-pack.d.ts +33 -0
  164. package/dist/skills/prompt-pack.d.ts.map +1 -0
  165. package/dist/skills/prompt-pack.js +82 -0
  166. package/dist/skills/prompt-pack.js.map +1 -0
  167. package/dist/skills/registry.d.ts +6 -0
  168. package/dist/skills/registry.d.ts.map +1 -0
  169. package/dist/skills/registry.js +101 -0
  170. package/dist/skills/registry.js.map +1 -0
  171. package/dist/skills/types.d.ts +146 -0
  172. package/dist/skills/types.d.ts.map +1 -0
  173. package/dist/skills/types.js +32 -0
  174. package/dist/skills/types.js.map +1 -0
  175. package/dist/state/manager.d.ts +8 -2
  176. package/dist/state/manager.d.ts.map +1 -1
  177. package/dist/state/manager.js +58 -5
  178. package/dist/state/manager.js.map +1 -1
  179. package/dist/utils/context-assembly.d.ts +3 -0
  180. package/dist/utils/context-assembly.d.ts.map +1 -1
  181. package/dist/utils/context-assembly.js +3 -0
  182. package/dist/utils/context-assembly.js.map +1 -1
  183. package/dist/utils/web-search.d.ts +6 -1
  184. package/dist/utils/web-search.d.ts.map +1 -1
  185. package/dist/utils/web-search.js +8 -4
  186. package/dist/utils/web-search.js.map +1 -1
  187. package/package.json +14 -3
@@ -2,7 +2,7 @@ import { Type } from "@mariozechner/pi-ai";
2
2
  import { ArchitectIncompleteFoundationError } from "../agents/architect.js";
3
3
  import { defaultChapterLength } from "../utils/length-metrics.js";
4
4
  import { inferLanguage } from "../utils/language.js";
5
- import { readFile, writeFile, readdir, stat } from "node:fs/promises";
5
+ import { mkdir, readFile, writeFile, readdir, stat } from "node:fs/promises";
6
6
  import { isAbsolute, join, resolve } from "node:path";
7
7
  import { StateManager } from "../state/manager.js";
8
8
  import { assertSafeTruthFileName, createInteractionToolsFromDeps } from "../interaction/project-tools.js";
@@ -11,10 +11,14 @@ import { assertSafeBookId, deriveBookIdFromTitle } from "../utils/book-id.js";
11
11
  import { safeChildPath } from "../utils/path-safety.js";
12
12
  import { normalizePlatformId, normalizePlatformOrOther } from "../models/book.js";
13
13
  import { generateShortFictionCover, runShortFictionProduction } from "../pipeline/short-fiction-runner.js";
14
+ import { runInteractiveFilmCreation, runScriptCreation, runStoryboardCreation } from "../pipeline/script-storyboard-runner.js";
15
+ import { runResearchReport } from "../agents/researcher.js";
14
16
  import { createPlayDB } from "../play/play-db-factory.js";
15
17
  import { PlayRunner } from "../play/play-runner.js";
16
18
  import { PlayStore } from "../play/play-store.js";
17
19
  import { ActionPayloadSchema, isUsablePlayInitialScene } from "../interaction/action-envelope.js";
20
+ import { ResearchSearchConfigSchema } from "../models/project.js";
21
+ import { searchWeb } from "../utils/web-search.js";
18
22
  function textResult(text, details) {
19
23
  return { content: [{ type: "text", text }], details: details };
20
24
  }
@@ -89,6 +93,12 @@ const ProposeActionParams = Type.Object({
89
93
  Type.Literal("continuation_import"),
90
94
  Type.Literal("spinoff_create"),
91
95
  Type.Literal("style_imitation"),
96
+ Type.Literal("script_create"),
97
+ Type.Literal("storyboard_create"),
98
+ Type.Literal("interactive_film_create"),
99
+ Type.Literal("draft_structure"),
100
+ Type.Literal("connect_choice"),
101
+ Type.Literal("remove_node"),
92
102
  ], {
93
103
  description: "The production or assisted Studio workflow the user appears to want, but which needs explicit confirmation from general chat.",
94
104
  }),
@@ -172,12 +182,65 @@ const ProposeActionParams = Type.Object({
172
182
  coverPrompt: Type.Optional(Type.String({ description: "Confirmed visual direction." })),
173
183
  outputDir: Type.Optional(Type.String({ description: "Confirmed output directory." })),
174
184
  }, { description: "Structured execution args for action=generate_cover." })),
185
+ scriptCreate: Type.Optional(Type.Object({
186
+ title: Type.Optional(Type.String({ description: "Confirmed script project title." })),
187
+ sourceKind: Type.Optional(Type.String({ description: "Source type, e.g. novel excerpt, original idea, outline, existing script." })),
188
+ targetFormat: Type.Optional(Type.Union([
189
+ Type.Literal("vertical_short_drama"),
190
+ Type.Literal("screenplay"),
191
+ Type.Literal("audio_drama"),
192
+ Type.Literal("interactive_script"),
193
+ Type.Literal("general_script"),
194
+ ], { description: "Confirmed script output format." })),
195
+ sourceText: Type.Optional(Type.String({ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing." })),
196
+ sourcePath: Type.Optional(Type.String({ description: "Optional project-relative source file path." })),
197
+ requirements: Type.Optional(Type.String({ description: "Confirmed script format, production constraints, tone, episode structure, or user preferences." })),
198
+ episodeCount: Type.Optional(Type.Number({ description: "Optional target episode/segment count." })),
199
+ episodeDuration: Type.Optional(Type.String({ description: "Optional per-episode/per-segment duration." })),
200
+ projectId: Type.Optional(Type.String({ description: "Optional output id under dramas/." })),
201
+ outDir: Type.Optional(Type.String({ description: "Optional project-relative output directory. Default dramas/." })),
202
+ }, { description: "Structured execution args for action=script_create." })),
203
+ storyboardCreate: Type.Optional(Type.Object({
204
+ title: Type.Optional(Type.String({ description: "Confirmed storyboard project title." })),
205
+ sourceKind: Type.Optional(Type.String({ description: "Source type, e.g. script, novel excerpt, idea, scene list." })),
206
+ sourceText: Type.Optional(Type.String({ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing." })),
207
+ sourcePath: Type.Optional(Type.String({ description: "Optional project-relative source file path." })),
208
+ requirements: Type.Optional(Type.String({ description: "Confirmed shot/storyboard requirements." })),
209
+ visualStyle: Type.Optional(Type.String({ description: "Confirmed visual style, if the user specified one." })),
210
+ aspectRatio: Type.Optional(Type.String({ description: "Confirmed aspect ratio, e.g. 9:16, 16:9, 1:1." })),
211
+ granularity: Type.Optional(Type.String({ description: "Confirmed storyboard granularity." })),
212
+ maxShots: Type.Optional(Type.Number({ description: "Optional max shot count." })),
213
+ projectId: Type.Optional(Type.String({ description: "Optional output id under storyboards/." })),
214
+ outDir: Type.Optional(Type.String({ description: "Optional project-relative output directory. Default storyboards/." })),
215
+ }, { description: "Structured execution args for action=storyboard_create." })),
216
+ interactiveFilmCreate: Type.Optional(Type.Object({
217
+ title: Type.Optional(Type.String({ description: "Confirmed interactive-film project title." })),
218
+ sourceKind: Type.Optional(Type.String({ description: "Source type, e.g. novel excerpt, script, outline, original idea." })),
219
+ sourceText: Type.Optional(Type.String({ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing." })),
220
+ sourcePath: Type.Optional(Type.String({ description: "Optional project-relative source file path." })),
221
+ requirements: Type.Optional(Type.String({ description: "Confirmed branching, variable/flag, ending, production, visual, or market requirements." })),
222
+ targetAudience: Type.Optional(Type.String({ description: "Confirmed target audience or market." })),
223
+ episodeCount: Type.Optional(Type.Number({ description: "Optional target episode/segment count." })),
224
+ episodeDuration: Type.Optional(Type.String({ description: "Optional per-episode/per-segment duration." })),
225
+ budget: Type.Optional(Type.String({ description: "Optional budget or production constraints." })),
226
+ referenceMode: Type.Optional(Type.String({ description: "Optional reference mode, e.g. 盛世天下-style multi-ending interactive drama." })),
227
+ projectId: Type.Optional(Type.String({ description: "Optional output id under interactive-films/." })),
228
+ outDir: Type.Optional(Type.String({ description: "Optional project-relative output directory. Default interactive-films/." })),
229
+ }, { description: "Structured execution args for action=interactive_film_create." })),
175
230
  });
176
231
  function proposedActionSessionKind(action) {
177
232
  if (action === "create_book")
178
233
  return "book-create";
179
234
  if (action === "play_start")
180
235
  return "play";
236
+ if (action === "script_create")
237
+ return "script";
238
+ if (action === "storyboard_create")
239
+ return "storyboard";
240
+ if (action === "interactive_film_create")
241
+ return "interactive-film";
242
+ if (action === "draft_structure" || action === "connect_choice" || action === "remove_node")
243
+ return "interactive-film-authoring";
181
244
  if (action === "fanfic_init" || action === "continuation_import" || action === "spinoff_create" || action === "style_imitation")
182
245
  return "chat";
183
246
  return "short";
@@ -211,6 +274,18 @@ function proposedActionFallbackTitle(action, isZh) {
211
274
  return isZh ? "打开番外创作" : "Open side-story workflow";
212
275
  case "style_imitation":
213
276
  return isZh ? "打开仿写/文风分析" : "Open style imitation";
277
+ case "script_create":
278
+ return isZh ? "创建剧本" : "Create script";
279
+ case "storyboard_create":
280
+ return isZh ? "创建分镜" : "Create storyboard";
281
+ case "interactive_film_create":
282
+ return isZh ? "创建互动影游" : "Create interactive film";
283
+ case "draft_structure":
284
+ return isZh ? "生成故事结构" : "Draft story structure";
285
+ case "connect_choice":
286
+ return isZh ? "连接选项" : "Connect choice";
287
+ case "remove_node":
288
+ return isZh ? "删除节点" : "Remove node";
214
289
  }
215
290
  }
216
291
  function proposedActionFallbackSummary(action, isZh) {
@@ -241,6 +316,11 @@ function compactObject(value) {
241
316
  out[key] = items;
242
317
  continue;
243
318
  }
319
+ if (typeof raw === "number") {
320
+ if (Number.isFinite(raw) && raw > 0)
321
+ out[key] = raw;
322
+ continue;
323
+ }
244
324
  if (raw !== undefined && raw !== null) {
245
325
  out[key] = raw;
246
326
  }
@@ -295,6 +375,21 @@ function proposedActionPayload(params) {
295
375
  if (generateCover)
296
376
  payload.generateCover = generateCover;
297
377
  }
378
+ if (params.action === "script_create") {
379
+ const scriptCreate = compactObject(params.scriptCreate);
380
+ if (scriptCreate)
381
+ payload.scriptCreate = scriptCreate;
382
+ }
383
+ if (params.action === "storyboard_create") {
384
+ const storyboardCreate = compactObject(params.storyboardCreate);
385
+ if (storyboardCreate)
386
+ payload.storyboardCreate = storyboardCreate;
387
+ }
388
+ if (params.action === "interactive_film_create") {
389
+ const interactiveFilmCreate = compactObject(params.interactiveFilmCreate);
390
+ if (interactiveFilmCreate)
391
+ payload.interactiveFilmCreate = interactiveFilmCreate;
392
+ }
298
393
  return Object.keys(payload).length > 0 ? payload : undefined;
299
394
  }
300
395
  function validateProposedActionPayload(payload) {
@@ -323,6 +418,18 @@ function assertExecutableProposedAction(params, payload) {
323
418
  }
324
419
  if (params.action === "generate_cover") {
325
420
  requireProposedText(payload?.generateCover?.title, "generateCover.title");
421
+ return;
422
+ }
423
+ if (params.action === "script_create") {
424
+ requireProposedText(payload?.scriptCreate?.title, "scriptCreate.title");
425
+ return;
426
+ }
427
+ if (params.action === "storyboard_create") {
428
+ requireProposedText(payload?.storyboardCreate?.title, "storyboardCreate.title");
429
+ return;
430
+ }
431
+ if (params.action === "interactive_film_create") {
432
+ requireProposedText(payload?.interactiveFilmCreate?.title, "interactiveFilmCreate.title");
326
433
  }
327
434
  }
328
435
  export function createProposeActionTool(language = "zh", options = {}) {
@@ -620,7 +727,97 @@ export function createSubAgentTool(pipeline, activeBookId, projectRoot, options
620
727
  };
621
728
  }
622
729
  // ---------------------------------------------------------------------------
623
- // 2. Standalone Short Fiction Tool
730
+ // 2. Research Tool (research_web)
731
+ // ---------------------------------------------------------------------------
732
+ const ResearchWebParams = Type.Object({
733
+ topic: Type.String({
734
+ description: "Research question or topic, e.g. 1990s county cold-storage accounting workflow or Tang dynasty courier stations.",
735
+ }),
736
+ purpose: Type.Union([
737
+ Type.Literal("worldbuilding"),
738
+ Type.Literal("era"),
739
+ Type.Literal("profession"),
740
+ Type.Literal("market"),
741
+ Type.Literal("fact-check"),
742
+ Type.Literal("general"),
743
+ ], {
744
+ description: "Why this research is needed. Research reports are references only and must not directly mutate story state.",
745
+ }),
746
+ depth: Type.Optional(Type.Union([
747
+ Type.Literal("quick"),
748
+ Type.Literal("standard"),
749
+ Type.Literal("deep"),
750
+ ], {
751
+ description: "Research depth. Default standard.",
752
+ })),
753
+ });
754
+ export function createResearchWebTool(projectRoot) {
755
+ return {
756
+ name: "research_web",
757
+ description: "Collect traceable web research for worldbuilding, era, profession, market, or fact-check questions. " +
758
+ "Saves a Markdown report under .inkos/research/. It is reference material only; it must not modify books, chapters, or truth files.",
759
+ label: "Research Web",
760
+ parameters: ResearchWebParams,
761
+ async execute(_toolCallId, params, _signal, onUpdate) {
762
+ onUpdate?.(textResult(`Researching: ${params.topic}`));
763
+ const searchConfig = await readResearchSearchConfig(projectRoot);
764
+ const searchOptions = searchConfig.enabled
765
+ ? {
766
+ apiKey: searchConfig.apiKey,
767
+ apiKeyEnv: searchConfig.apiKeyEnv,
768
+ baseUrl: searchConfig.baseUrl,
769
+ }
770
+ : {};
771
+ const report = await runResearchReport({
772
+ topic: params.topic,
773
+ purpose: params.purpose,
774
+ depth: params.depth ?? "standard",
775
+ }, {
776
+ search: (query, maxResults) => searchWeb(query, maxResults, searchOptions),
777
+ });
778
+ const reportDir = join(projectRoot, ".inkos", "research");
779
+ await mkdir(reportDir, { recursive: true });
780
+ const fileName = `${new Date().toISOString().replace(/[:.]/g, "-")}-${slugResearchTopic(params.topic)}.md`;
781
+ const reportPath = join(reportDir, fileName);
782
+ await writeFile(reportPath, report.markdown, "utf-8");
783
+ return textResult([
784
+ `Research report saved: ${reportPath}`,
785
+ `Sources: ${report.sources.length}; confidence: ${report.confidence}.`,
786
+ report.partialFailures.length > 0 ? `Partial failures: ${report.partialFailures.length}.` : "Partial failures: none.",
787
+ ].join("\n"), {
788
+ kind: "research_report",
789
+ reportPath,
790
+ topic: params.topic,
791
+ purpose: params.purpose,
792
+ depth: params.depth ?? "standard",
793
+ sources: report.sources,
794
+ claims: report.claims,
795
+ confidence: report.confidence,
796
+ partialFailures: report.partialFailures,
797
+ });
798
+ },
799
+ };
800
+ }
801
+ function slugResearchTopic(topic) {
802
+ const slug = topic
803
+ .normalize("NFKC")
804
+ .toLowerCase()
805
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-")
806
+ .replace(/^-+|-+$/g, "")
807
+ .slice(0, 60);
808
+ return slug || "research";
809
+ }
810
+ async function readResearchSearchConfig(projectRoot) {
811
+ try {
812
+ const raw = JSON.parse(await readFile(join(projectRoot, "inkos.json"), "utf-8"));
813
+ return ResearchSearchConfigSchema.parse(raw.researchSearch ?? {});
814
+ }
815
+ catch {
816
+ return ResearchSearchConfigSchema.parse({});
817
+ }
818
+ }
819
+ // ---------------------------------------------------------------------------
820
+ // 3. Standalone Short Fiction Tool
624
821
  // ---------------------------------------------------------------------------
625
822
  const ShortFictionRunParams = Type.Object({
626
823
  direction: Type.String({
@@ -721,7 +918,241 @@ function summarizeCoverGenerationError(error) {
721
918
  return text.slice(0, 300);
722
919
  }
723
920
  // ---------------------------------------------------------------------------
724
- // 3. Standalone Cover Tool
921
+ // 3. Script and Storyboard tools
922
+ // ---------------------------------------------------------------------------
923
+ const ScriptCreateParams = Type.Object({
924
+ title: Type.String({
925
+ description: "Required script project title.",
926
+ }),
927
+ instruction: Type.String({
928
+ description: "Confirmed script creation instruction, including format, source, and user preferences.",
929
+ }),
930
+ sourceKind: Type.Optional(Type.String({
931
+ description: "Source type, e.g. novel excerpt, original idea, outline, existing script.",
932
+ })),
933
+ targetFormat: Type.Optional(Type.Union([
934
+ Type.Literal("vertical_short_drama"),
935
+ Type.Literal("screenplay"),
936
+ Type.Literal("audio_drama"),
937
+ Type.Literal("interactive_script"),
938
+ Type.Literal("general_script"),
939
+ ], { description: "Confirmed script output format." })),
940
+ sourceText: Type.Optional(Type.String({
941
+ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing.",
942
+ })),
943
+ sourcePath: Type.Optional(Type.String({
944
+ description: "Optional project-relative source file path.",
945
+ })),
946
+ requirements: Type.Optional(Type.String({
947
+ description: "Confirmed script format, production constraints, tone, episode structure, or user preferences.",
948
+ })),
949
+ episodeCount: Type.Optional(Type.Number({
950
+ description: "Optional target episode/segment count.",
951
+ })),
952
+ episodeDuration: Type.Optional(Type.String({
953
+ description: "Optional per-episode/per-segment duration.",
954
+ })),
955
+ projectId: Type.Optional(Type.String({
956
+ description: "Optional output id under dramas/.",
957
+ })),
958
+ outDir: Type.Optional(Type.String({
959
+ description: "Optional project-relative output directory. Default dramas/.",
960
+ })),
961
+ });
962
+ export function createScriptCreationTool(pipeline, projectRoot, options = {}) {
963
+ return {
964
+ name: "script_create",
965
+ description: "Create a script project from a novel excerpt, idea, outline, or existing script. " +
966
+ "Writes human-readable Markdown spec and script files under dramas/.",
967
+ label: "Script Creation",
968
+ parameters: ScriptCreateParams,
969
+ async execute(_toolCallId, params, _signal, onUpdate) {
970
+ const progress = (message) => onUpdate?.(textResult(message));
971
+ const payload = options.actionPayload?.scriptCreate;
972
+ const result = await runScriptCreation({
973
+ projectRoot,
974
+ runtime: pipeline.createAgentContext("script-creation"),
975
+ title: payload?.title ?? params.title,
976
+ instruction: params.instruction,
977
+ sourceKind: payload?.sourceKind ?? params.sourceKind,
978
+ targetFormat: (payload?.targetFormat ?? params.targetFormat),
979
+ sourceText: payload?.sourceText ?? params.sourceText,
980
+ sourcePath: payload?.sourcePath ?? params.sourcePath,
981
+ requirements: payload?.requirements ?? params.requirements,
982
+ episodeCount: payload?.episodeCount ?? params.episodeCount,
983
+ episodeDuration: payload?.episodeDuration ?? params.episodeDuration,
984
+ projectId: payload?.projectId ?? params.projectId,
985
+ outDir: payload?.outDir ?? params.outDir,
986
+ onProgress: progress,
987
+ });
988
+ return textResult([
989
+ `Script "${result.projectId}" completed.`,
990
+ `Spec: ${result.specPath}`,
991
+ `Script: ${result.scriptPath}`,
992
+ ].join("\n"), { kind: "script_created", ...result });
993
+ },
994
+ };
995
+ }
996
+ const StoryboardCreateParams = Type.Object({
997
+ title: Type.String({
998
+ description: "Required storyboard project title.",
999
+ }),
1000
+ instruction: Type.String({
1001
+ description: "Confirmed storyboard creation instruction, including source, style, aspect ratio, and shot granularity.",
1002
+ }),
1003
+ sourceKind: Type.Optional(Type.String({
1004
+ description: "Source type, e.g. script, novel excerpt, idea, scene list.",
1005
+ })),
1006
+ sourceText: Type.Optional(Type.String({
1007
+ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing.",
1008
+ })),
1009
+ sourcePath: Type.Optional(Type.String({
1010
+ description: "Optional project-relative source file path.",
1011
+ })),
1012
+ requirements: Type.Optional(Type.String({
1013
+ description: "Confirmed shot/storyboard requirements.",
1014
+ })),
1015
+ visualStyle: Type.Optional(Type.String({
1016
+ description: "Confirmed visual style, if the user specified one.",
1017
+ })),
1018
+ aspectRatio: Type.Optional(Type.String({
1019
+ description: "Confirmed aspect ratio, e.g. 9:16, 16:9, 1:1.",
1020
+ })),
1021
+ granularity: Type.Optional(Type.String({
1022
+ description: "Confirmed storyboard granularity.",
1023
+ })),
1024
+ maxShots: Type.Optional(Type.Number({
1025
+ description: "Optional max shot count.",
1026
+ })),
1027
+ projectId: Type.Optional(Type.String({
1028
+ description: "Optional output id under storyboards/.",
1029
+ })),
1030
+ outDir: Type.Optional(Type.String({
1031
+ description: "Optional project-relative output directory. Default storyboards/.",
1032
+ })),
1033
+ });
1034
+ export function createStoryboardCreationTool(pipeline, projectRoot, options = {}) {
1035
+ return {
1036
+ name: "storyboard_create",
1037
+ description: "Create a storyboard project and image prompts from a script, novel excerpt, idea, or scene list. " +
1038
+ "Writes human-readable Markdown spec, storyboard, and image prompt files under storyboards/.",
1039
+ label: "Storyboard Creation",
1040
+ parameters: StoryboardCreateParams,
1041
+ async execute(_toolCallId, params, _signal, onUpdate) {
1042
+ const progress = (message) => onUpdate?.(textResult(message));
1043
+ const payload = options.actionPayload?.storyboardCreate;
1044
+ const result = await runStoryboardCreation({
1045
+ projectRoot,
1046
+ runtime: pipeline.createAgentContext("storyboard-creation"),
1047
+ title: payload?.title ?? params.title,
1048
+ instruction: params.instruction,
1049
+ sourceKind: payload?.sourceKind ?? params.sourceKind,
1050
+ sourceText: payload?.sourceText ?? params.sourceText,
1051
+ sourcePath: payload?.sourcePath ?? params.sourcePath,
1052
+ requirements: payload?.requirements ?? params.requirements,
1053
+ visualStyle: payload?.visualStyle ?? params.visualStyle,
1054
+ aspectRatio: payload?.aspectRatio ?? params.aspectRatio,
1055
+ granularity: payload?.granularity ?? params.granularity,
1056
+ maxShots: payload?.maxShots ?? params.maxShots,
1057
+ projectId: payload?.projectId ?? params.projectId,
1058
+ outDir: payload?.outDir ?? params.outDir,
1059
+ onProgress: progress,
1060
+ });
1061
+ return textResult([
1062
+ `Storyboard "${result.projectId}" completed.`,
1063
+ `Spec: ${result.specPath}`,
1064
+ `Storyboard: ${result.storyboardPath}`,
1065
+ `Image prompts: ${result.imagePromptsPath}`,
1066
+ `Image assets: ${result.assetsManifestPath}`,
1067
+ ].join("\n"), { kind: "storyboard_created", ...result });
1068
+ },
1069
+ };
1070
+ }
1071
+ const InteractiveFilmCreateParams = Type.Object({
1072
+ title: Type.String({
1073
+ description: "Required interactive-film project title.",
1074
+ }),
1075
+ instruction: Type.String({
1076
+ description: "Confirmed interactive-film creation instruction, including branching, variables/flags, endings, source, and user preferences.",
1077
+ }),
1078
+ sourceKind: Type.Optional(Type.String({
1079
+ description: "Source type, e.g. novel excerpt, script, outline, original idea.",
1080
+ })),
1081
+ sourceText: Type.Optional(Type.String({
1082
+ description: "User-provided source text. For long sources, prefer sourcePath instead of summarizing.",
1083
+ })),
1084
+ sourcePath: Type.Optional(Type.String({
1085
+ description: "Optional project-relative source file path.",
1086
+ })),
1087
+ requirements: Type.Optional(Type.String({
1088
+ description: "Confirmed branching, variable/flag, ending, production, visual, or market requirements.",
1089
+ })),
1090
+ targetAudience: Type.Optional(Type.String({
1091
+ description: "Optional confirmed target audience or market.",
1092
+ })),
1093
+ episodeCount: Type.Optional(Type.Number({
1094
+ description: "Optional target episode/segment count.",
1095
+ })),
1096
+ episodeDuration: Type.Optional(Type.String({
1097
+ description: "Optional per-episode/per-segment duration.",
1098
+ })),
1099
+ budget: Type.Optional(Type.String({
1100
+ description: "Optional budget or production constraints.",
1101
+ })),
1102
+ referenceMode: Type.Optional(Type.String({
1103
+ description: "Optional reference mode, e.g. 盛世天下-style multi-ending interactive drama.",
1104
+ })),
1105
+ projectId: Type.Optional(Type.String({
1106
+ description: "Optional output id under interactive-films/.",
1107
+ })),
1108
+ outDir: Type.Optional(Type.String({
1109
+ description: "Optional project-relative output directory. Default interactive-films/.",
1110
+ })),
1111
+ });
1112
+ export function createInteractiveFilmCreationTool(pipeline, projectRoot, options = {}) {
1113
+ return {
1114
+ name: "interactive_film_create",
1115
+ description: "Create an interactive film/game script package with story tree, variables/flags, endings, script, storyboard, and image prompts. " +
1116
+ "Writes human-readable Markdown files under interactive-films/.",
1117
+ label: "Interactive Film Creation",
1118
+ parameters: InteractiveFilmCreateParams,
1119
+ async execute(_toolCallId, params, _signal, onUpdate) {
1120
+ const progress = (message) => onUpdate?.(textResult(message));
1121
+ const payload = options.actionPayload?.interactiveFilmCreate;
1122
+ const result = await runInteractiveFilmCreation({
1123
+ projectRoot,
1124
+ runtime: pipeline.createAgentContext("interactive-film-creation"),
1125
+ title: payload?.title ?? params.title,
1126
+ instruction: params.instruction,
1127
+ sourceKind: payload?.sourceKind ?? params.sourceKind,
1128
+ sourceText: payload?.sourceText ?? params.sourceText,
1129
+ sourcePath: payload?.sourcePath ?? params.sourcePath,
1130
+ requirements: payload?.requirements ?? params.requirements,
1131
+ targetAudience: payload?.targetAudience ?? params.targetAudience,
1132
+ episodeCount: payload?.episodeCount ?? params.episodeCount,
1133
+ episodeDuration: payload?.episodeDuration ?? params.episodeDuration,
1134
+ budget: payload?.budget ?? params.budget,
1135
+ referenceMode: payload?.referenceMode ?? params.referenceMode,
1136
+ projectId: payload?.projectId ?? params.projectId,
1137
+ outDir: payload?.outDir ?? params.outDir,
1138
+ onProgress: progress,
1139
+ });
1140
+ return textResult([
1141
+ `Interactive film "${result.projectId}" completed.`,
1142
+ `Spec: ${result.specPath}`,
1143
+ `Story graph: ${result.storyGraphPath}`,
1144
+ `Story tree: ${result.storyTreePath}`,
1145
+ `Flags: ${result.flagsPath}`,
1146
+ `Script: ${result.scriptPath}`,
1147
+ `Storyboard: ${result.storyboardPath}`,
1148
+ `Image prompts: ${result.imagePromptsPath}`,
1149
+ `Image assets: ${result.assetsManifestPath}`,
1150
+ ].join("\n"), { kind: "interactive_film_created", ...result });
1151
+ },
1152
+ };
1153
+ }
1154
+ // ---------------------------------------------------------------------------
1155
+ // 4. Standalone Cover Tool
725
1156
  // ---------------------------------------------------------------------------
726
1157
  const GenerateCoverParams = Type.Object({
727
1158
  title: Type.String({