@zwbigi/ink-xy 0.1.2 → 0.1.4

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 (598) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +2 -2
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/server/app/_global-error.html +1 -1
  5. package/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  8. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found.html +1 -1
  13. package/.next/server/app/_not-found.rsc +1 -1
  14. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  20. package/.next/server/app/api/inkos/route.js.nft.json +1 -1
  21. package/.next/server/app/api/skills/install/route.js.nft.json +1 -1
  22. package/.next/server/app/api/skills/search/route.js.nft.json +1 -1
  23. package/.next/server/app/index.html +1 -1
  24. package/.next/server/app/index.rsc +1 -1
  25. package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  26. package/.next/server/app/index.segments/_full.segment.rsc +1 -1
  27. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  28. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  29. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  30. package/.next/server/app-paths-manifest.json +2 -2
  31. package/.next/server/chunks/162.js +1 -1
  32. package/.next/server/middleware-build-manifest.js +1 -1
  33. package/.next/server/pages/404.html +1 -1
  34. package/.next/server/pages/500.html +1 -1
  35. package/.next/trace +4 -4
  36. package/.next/trace-build +1 -1
  37. package/bin/pi-web.js +4 -1
  38. package/inkos/.env.example +20 -0
  39. package/inkos/.node-version +1 -0
  40. package/inkos/.nvmrc +1 -0
  41. package/inkos/CHANGELOG.md +787 -0
  42. package/inkos/CONTRIBUTING.md +89 -0
  43. package/inkos/LICENSE +661 -0
  44. package/inkos/README.en.md +483 -0
  45. package/inkos/README.ja.md +461 -0
  46. package/inkos/README.md +272 -0
  47. package/inkos/assets/15qun.jpg +0 -0
  48. package/inkos/assets/41777702961_.pic.jpg +0 -0
  49. package/inkos/assets/inkos-short-demo-cover.png +0 -0
  50. package/inkos/assets/inkos-text.svg +40 -0
  51. package/inkos/assets/logo.svg +47 -0
  52. package/inkos/assets/screenshot-chapters.png +0 -0
  53. package/inkos/assets/screenshot-pipeline.png +0 -0
  54. package/inkos/assets/screenshot-state.png +0 -0
  55. package/inkos/assets/screenshot-terminal.png +0 -0
  56. package/inkos/assets/wechat-group-v8.jpg +0 -0
  57. package/inkos/package.json +42 -0
  58. package/inkos/packages/cli/package.json +74 -0
  59. package/inkos/packages/cli/src/__tests__/analytics.test.ts +154 -0
  60. package/inkos/packages/cli/src/__tests__/cli-integration.test.ts +1031 -0
  61. package/inkos/packages/cli/src/__tests__/daemon.test.ts +93 -0
  62. package/inkos/packages/cli/src/__tests__/doctor.test.ts +36 -0
  63. package/inkos/packages/cli/src/__tests__/interact-command.test.ts +142 -0
  64. package/inkos/packages/cli/src/__tests__/interaction-tools.test.ts +107 -0
  65. package/inkos/packages/cli/src/__tests__/llm-overrides.test.ts +25 -0
  66. package/inkos/packages/cli/src/__tests__/localization.test.ts +121 -0
  67. package/inkos/packages/cli/src/__tests__/progress-text.test.ts +92 -0
  68. package/inkos/packages/cli/src/__tests__/project-bootstrap.test.ts +71 -0
  69. package/inkos/packages/cli/src/__tests__/publish-package.test.ts +272 -0
  70. package/inkos/packages/cli/src/__tests__/revision-command.test.ts +82 -0
  71. package/inkos/packages/cli/src/__tests__/runtime-requirements.test.ts +89 -0
  72. package/inkos/packages/cli/src/__tests__/short-fiction-command.test.ts +48 -0
  73. package/inkos/packages/cli/src/__tests__/studio-runtime.test.ts +142 -0
  74. package/inkos/packages/cli/src/__tests__/studio.test.ts +87 -0
  75. package/inkos/packages/cli/src/__tests__/tui-activity-state.test.ts +20 -0
  76. package/inkos/packages/cli/src/__tests__/tui-agent-session.test.ts +213 -0
  77. package/inkos/packages/cli/src/__tests__/tui-chat-depth.test.ts +27 -0
  78. package/inkos/packages/cli/src/__tests__/tui-chat-draft.test.ts +44 -0
  79. package/inkos/packages/cli/src/__tests__/tui-command.test.ts +86 -0
  80. package/inkos/packages/cli/src/__tests__/tui-composer-caret.test.ts +46 -0
  81. package/inkos/packages/cli/src/__tests__/tui-composer-display.test.ts +40 -0
  82. package/inkos/packages/cli/src/__tests__/tui-dashboard.test.tsx +219 -0
  83. package/inkos/packages/cli/src/__tests__/tui-effects-i18n.test.ts +29 -0
  84. package/inkos/packages/cli/src/__tests__/tui-i18n.test.ts +22 -0
  85. package/inkos/packages/cli/src/__tests__/tui-input-chrome.test.ts +10 -0
  86. package/inkos/packages/cli/src/__tests__/tui-input-history.test.ts +40 -0
  87. package/inkos/packages/cli/src/__tests__/tui-layout.test.ts +55 -0
  88. package/inkos/packages/cli/src/__tests__/tui-local-commands.test.ts +47 -0
  89. package/inkos/packages/cli/src/__tests__/tui-session-store.test.ts +81 -0
  90. package/inkos/packages/cli/src/__tests__/tui-setup-i18n.test.ts +31 -0
  91. package/inkos/packages/cli/src/__tests__/tui-slash-autocomplete.test.ts +33 -0
  92. package/inkos/packages/cli/src/commands/agent.ts +65 -0
  93. package/inkos/packages/cli/src/commands/analytics.ts +77 -0
  94. package/inkos/packages/cli/src/commands/audit.ts +52 -0
  95. package/inkos/packages/cli/src/commands/book.ts +260 -0
  96. package/inkos/packages/cli/src/commands/compose.ts +50 -0
  97. package/inkos/packages/cli/src/commands/config.ts +328 -0
  98. package/inkos/packages/cli/src/commands/consolidate.ts +50 -0
  99. package/inkos/packages/cli/src/commands/daemon.ts +121 -0
  100. package/inkos/packages/cli/src/commands/detect.ts +125 -0
  101. package/inkos/packages/cli/src/commands/doctor.ts +391 -0
  102. package/inkos/packages/cli/src/commands/draft.ts +43 -0
  103. package/inkos/packages/cli/src/commands/eval.ts +217 -0
  104. package/inkos/packages/cli/src/commands/export.ts +45 -0
  105. package/inkos/packages/cli/src/commands/fanfic.ts +183 -0
  106. package/inkos/packages/cli/src/commands/genre.ts +160 -0
  107. package/inkos/packages/cli/src/commands/import.ts +158 -0
  108. package/inkos/packages/cli/src/commands/init.ts +47 -0
  109. package/inkos/packages/cli/src/commands/interact.ts +109 -0
  110. package/inkos/packages/cli/src/commands/plan.ts +54 -0
  111. package/inkos/packages/cli/src/commands/radar.ts +60 -0
  112. package/inkos/packages/cli/src/commands/review.ts +253 -0
  113. package/inkos/packages/cli/src/commands/revise.ts +58 -0
  114. package/inkos/packages/cli/src/commands/short-fiction.ts +294 -0
  115. package/inkos/packages/cli/src/commands/status.ts +138 -0
  116. package/inkos/packages/cli/src/commands/studio.ts +194 -0
  117. package/inkos/packages/cli/src/commands/style.ts +99 -0
  118. package/inkos/packages/cli/src/commands/tui.ts +18 -0
  119. package/inkos/packages/cli/src/commands/update.ts +45 -0
  120. package/inkos/packages/cli/src/commands/write.ts +324 -0
  121. package/inkos/packages/cli/src/index.ts +5 -0
  122. package/inkos/packages/cli/src/interaction/tools.ts +49 -0
  123. package/inkos/packages/cli/src/localization.ts +215 -0
  124. package/inkos/packages/cli/src/program.ts +106 -0
  125. package/inkos/packages/cli/src/progress-text.ts +85 -0
  126. package/inkos/packages/cli/src/project-bootstrap.ts +175 -0
  127. package/inkos/packages/cli/src/runtime-requirements.ts +135 -0
  128. package/inkos/packages/cli/src/tui/__tests__/markdown.test.ts +64 -0
  129. package/inkos/packages/cli/src/tui/activity-state.ts +41 -0
  130. package/inkos/packages/cli/src/tui/agent-input.ts +264 -0
  131. package/inkos/packages/cli/src/tui/ansi.ts +72 -0
  132. package/inkos/packages/cli/src/tui/app.ts +130 -0
  133. package/inkos/packages/cli/src/tui/chat-depth.ts +22 -0
  134. package/inkos/packages/cli/src/tui/chat-draft.ts +42 -0
  135. package/inkos/packages/cli/src/tui/composer-caret.ts +22 -0
  136. package/inkos/packages/cli/src/tui/composer-display.ts +26 -0
  137. package/inkos/packages/cli/src/tui/dashboard-model.ts +164 -0
  138. package/inkos/packages/cli/src/tui/dashboard.tsx +544 -0
  139. package/inkos/packages/cli/src/tui/effects.ts +542 -0
  140. package/inkos/packages/cli/src/tui/i18n.ts +278 -0
  141. package/inkos/packages/cli/src/tui/input-history.ts +69 -0
  142. package/inkos/packages/cli/src/tui/local-commands.ts +55 -0
  143. package/inkos/packages/cli/src/tui/markdown.ts +64 -0
  144. package/inkos/packages/cli/src/tui/session-store.ts +6 -0
  145. package/inkos/packages/cli/src/tui/setup.ts +397 -0
  146. package/inkos/packages/cli/src/tui/slash-autocomplete.ts +62 -0
  147. package/inkos/packages/cli/src/tui/theme.ts +17 -0
  148. package/inkos/packages/cli/src/utils.ts +222 -0
  149. package/inkos/packages/cli/tsconfig.json +9 -0
  150. package/inkos/packages/core/genres/cozy.md +43 -0
  151. package/inkos/packages/core/genres/cultivation.md +42 -0
  152. package/inkos/packages/core/genres/dungeon-core.md +40 -0
  153. package/inkos/packages/core/genres/horror.md +51 -0
  154. package/inkos/packages/core/genres/isekai.md +43 -0
  155. package/inkos/packages/core/genres/litrpg.md +43 -0
  156. package/inkos/packages/core/genres/other.md +24 -0
  157. package/inkos/packages/core/genres/progression.md +41 -0
  158. package/inkos/packages/core/genres/romantasy.md +45 -0
  159. package/inkos/packages/core/genres/sci-fi.md +42 -0
  160. package/inkos/packages/core/genres/system-apocalypse.md +40 -0
  161. package/inkos/packages/core/genres/tower-climber.md +41 -0
  162. package/inkos/packages/core/genres/urban.md +53 -0
  163. package/inkos/packages/core/genres/xianxia.md +46 -0
  164. package/inkos/packages/core/genres/xuanhuan.md +64 -0
  165. package/inkos/packages/core/package.json +61 -0
  166. package/inkos/packages/core/src/__tests__/agent-max-tokens-policy.test.ts +29 -0
  167. package/inkos/packages/core/src/__tests__/agent-session.test.ts +866 -0
  168. package/inkos/packages/core/src/__tests__/agent-system-prompt.test.ts +167 -0
  169. package/inkos/packages/core/src/__tests__/agent-tools-params.test.ts +197 -0
  170. package/inkos/packages/core/src/__tests__/agent-tools.test.ts +421 -0
  171. package/inkos/packages/core/src/__tests__/ai-tells.test.ts +90 -0
  172. package/inkos/packages/core/src/__tests__/architect-phase5-consolidated.test.ts +445 -0
  173. package/inkos/packages/core/src/__tests__/architect-phase5.test.ts +455 -0
  174. package/inkos/packages/core/src/__tests__/architect-phase7.test.ts +210 -0
  175. package/inkos/packages/core/src/__tests__/architect.test.ts +859 -0
  176. package/inkos/packages/core/src/__tests__/audit-parse.test.ts +78 -0
  177. package/inkos/packages/core/src/__tests__/book-id.test.ts +26 -0
  178. package/inkos/packages/core/src/__tests__/book-session-store.test.ts +447 -0
  179. package/inkos/packages/core/src/__tests__/book-session.test.ts +113 -0
  180. package/inkos/packages/core/src/__tests__/chapter-analyzer.test.ts +574 -0
  181. package/inkos/packages/core/src/__tests__/chapter-memo-parser.test.ts +247 -0
  182. package/inkos/packages/core/src/__tests__/chapter-persistence.test.ts +198 -0
  183. package/inkos/packages/core/src/__tests__/chapter-review-cycle.test.ts +294 -0
  184. package/inkos/packages/core/src/__tests__/chapter-splitter.test.ts +156 -0
  185. package/inkos/packages/core/src/__tests__/chapter-state-recovery.test.ts +235 -0
  186. package/inkos/packages/core/src/__tests__/chapter-truth-validation.test.ts +253 -0
  187. package/inkos/packages/core/src/__tests__/composer.test.ts +627 -0
  188. package/inkos/packages/core/src/__tests__/config-loader.test.ts +325 -0
  189. package/inkos/packages/core/src/__tests__/config-migration.test.ts +102 -0
  190. package/inkos/packages/core/src/__tests__/consolidator.test.ts +32 -0
  191. package/inkos/packages/core/src/__tests__/context-filter.test.ts +60 -0
  192. package/inkos/packages/core/src/__tests__/context-transform.test.ts +108 -0
  193. package/inkos/packages/core/src/__tests__/continuity.test.ts +391 -0
  194. package/inkos/packages/core/src/__tests__/detection-insights.test.ts +59 -0
  195. package/inkos/packages/core/src/__tests__/detector.test.ts +86 -0
  196. package/inkos/packages/core/src/__tests__/draft-directive-parser.test.ts +386 -0
  197. package/inkos/packages/core/src/__tests__/edit-controller.test.ts +190 -0
  198. package/inkos/packages/core/src/__tests__/effective-llm-config.test.ts +486 -0
  199. package/inkos/packages/core/src/__tests__/fanfic-dimensions.test.ts +58 -0
  200. package/inkos/packages/core/src/__tests__/fanfic-models.test.ts +69 -0
  201. package/inkos/packages/core/src/__tests__/governed-working-set.test.ts +155 -0
  202. package/inkos/packages/core/src/__tests__/hook-arbiter.test.ts +124 -0
  203. package/inkos/packages/core/src/__tests__/hook-governance.test.ts +228 -0
  204. package/inkos/packages/core/src/__tests__/hook-health.test.ts +166 -0
  205. package/inkos/packages/core/src/__tests__/hook-ledger-validator.test.ts +236 -0
  206. package/inkos/packages/core/src/__tests__/hook-promotion.test.ts +192 -0
  207. package/inkos/packages/core/src/__tests__/hook-stale-detection.test.ts +136 -0
  208. package/inkos/packages/core/src/__tests__/index-notify-lazy.test.ts +20 -0
  209. package/inkos/packages/core/src/__tests__/interaction-chat-tokens.test.ts +170 -0
  210. package/inkos/packages/core/src/__tests__/interaction-models.test.ts +155 -0
  211. package/inkos/packages/core/src/__tests__/interaction-nl-router.test.ts +223 -0
  212. package/inkos/packages/core/src/__tests__/interaction-runtime.test.ts +633 -0
  213. package/inkos/packages/core/src/__tests__/interaction-tools.test.ts +343 -0
  214. package/inkos/packages/core/src/__tests__/length-metrics.test.ts +82 -0
  215. package/inkos/packages/core/src/__tests__/length-normalizer.test.ts +331 -0
  216. package/inkos/packages/core/src/__tests__/list-models.test.ts +109 -0
  217. package/inkos/packages/core/src/__tests__/llm-env.test.ts +31 -0
  218. package/inkos/packages/core/src/__tests__/logger.test.ts +175 -0
  219. package/inkos/packages/core/src/__tests__/long-span-fatigue.test.ts +160 -0
  220. package/inkos/packages/core/src/__tests__/memory-retrieval.test.ts +1303 -0
  221. package/inkos/packages/core/src/__tests__/models.test.ts +918 -0
  222. package/inkos/packages/core/src/__tests__/outline-paths.test.ts +97 -0
  223. package/inkos/packages/core/src/__tests__/path-safety.test.ts +22 -0
  224. package/inkos/packages/core/src/__tests__/persisted-governed-plan.test.ts +134 -0
  225. package/inkos/packages/core/src/__tests__/phase5-cleanup.test.ts +393 -0
  226. package/inkos/packages/core/src/__tests__/phase5-hotfix.test.ts +288 -0
  227. package/inkos/packages/core/src/__tests__/phase7-hotfix.test.ts +614 -0
  228. package/inkos/packages/core/src/__tests__/pipeline-agent.test.ts +354 -0
  229. package/inkos/packages/core/src/__tests__/pipeline-runner-memory-sync.test.ts +317 -0
  230. package/inkos/packages/core/src/__tests__/pipeline-runner.test.ts +5200 -0
  231. package/inkos/packages/core/src/__tests__/planner-context.test.ts +137 -0
  232. package/inkos/packages/core/src/__tests__/planner-prompts-ratio.test.ts +11 -0
  233. package/inkos/packages/core/src/__tests__/planner-prompts.test.ts +171 -0
  234. package/inkos/packages/core/src/__tests__/planner.test.ts +362 -0
  235. package/inkos/packages/core/src/__tests__/planning-materials.test.ts +90 -0
  236. package/inkos/packages/core/src/__tests__/polisher.test.ts +189 -0
  237. package/inkos/packages/core/src/__tests__/post-write-validator.test.ts +291 -0
  238. package/inkos/packages/core/src/__tests__/probe.test.ts +77 -0
  239. package/inkos/packages/core/src/__tests__/project-interaction.test.ts +241 -0
  240. package/inkos/packages/core/src/__tests__/provider.test.ts +953 -0
  241. package/inkos/packages/core/src/__tests__/providers-group.test.ts +34 -0
  242. package/inkos/packages/core/src/__tests__/providers-lookup.test.ts +81 -0
  243. package/inkos/packages/core/src/__tests__/providers-schema.test.ts +158 -0
  244. package/inkos/packages/core/src/__tests__/proxy-fetch.test.ts +75 -0
  245. package/inkos/packages/core/src/__tests__/revise-foundation.test.ts +514 -0
  246. package/inkos/packages/core/src/__tests__/reviser.test.ts +859 -0
  247. package/inkos/packages/core/src/__tests__/runtime-state-store.test.ts +388 -0
  248. package/inkos/packages/core/src/__tests__/scheduler.test.ts +123 -0
  249. package/inkos/packages/core/src/__tests__/secrets-migration.test.ts +71 -0
  250. package/inkos/packages/core/src/__tests__/secrets.test.ts +95 -0
  251. package/inkos/packages/core/src/__tests__/sensitive-words.test.ts +88 -0
  252. package/inkos/packages/core/src/__tests__/service-presets-regression.test.ts +73 -0
  253. package/inkos/packages/core/src/__tests__/service-resolver-regression.test.ts +75 -0
  254. package/inkos/packages/core/src/__tests__/service-resolver.test.ts +228 -0
  255. package/inkos/packages/core/src/__tests__/session-transcript-restore.test.ts +1311 -0
  256. package/inkos/packages/core/src/__tests__/session-transcript.test.ts +195 -0
  257. package/inkos/packages/core/src/__tests__/settler-delta-parser.test.ts +133 -0
  258. package/inkos/packages/core/src/__tests__/short-fiction-public.test.ts +241 -0
  259. package/inkos/packages/core/src/__tests__/spot-fix-patches.test.ts +104 -0
  260. package/inkos/packages/core/src/__tests__/state-manager.test.ts +1298 -0
  261. package/inkos/packages/core/src/__tests__/state-projections.test.ts +130 -0
  262. package/inkos/packages/core/src/__tests__/state-reducer.test.ts +372 -0
  263. package/inkos/packages/core/src/__tests__/state-validator-agent.test.ts +165 -0
  264. package/inkos/packages/core/src/__tests__/state-validator.test.ts +122 -0
  265. package/inkos/packages/core/src/__tests__/style-analyzer.test.ts +61 -0
  266. package/inkos/packages/core/src/__tests__/temperature-constraints.test.ts +57 -0
  267. package/inkos/packages/core/src/__tests__/v13-hotfix-round4.test.ts +343 -0
  268. package/inkos/packages/core/src/__tests__/verify-service.test.ts +77 -0
  269. package/inkos/packages/core/src/__tests__/webhook.test.ts +91 -0
  270. package/inkos/packages/core/src/__tests__/writer-parser.test.ts +348 -0
  271. package/inkos/packages/core/src/__tests__/writer-prompts.test.ts +269 -0
  272. package/inkos/packages/core/src/__tests__/writer.test.ts +1360 -0
  273. package/inkos/packages/core/src/agent/agent-session.ts +737 -0
  274. package/inkos/packages/core/src/agent/agent-system-prompt.ts +199 -0
  275. package/inkos/packages/core/src/agent/agent-tools.ts +835 -0
  276. package/inkos/packages/core/src/agent/context-transform.ts +85 -0
  277. package/inkos/packages/core/src/agent/index.ts +14 -0
  278. package/inkos/packages/core/src/agents/ai-tells.ts +161 -0
  279. package/inkos/packages/core/src/agents/architect.ts +1291 -0
  280. package/inkos/packages/core/src/agents/base.ts +100 -0
  281. package/inkos/packages/core/src/agents/chapter-analyzer.ts +634 -0
  282. package/inkos/packages/core/src/agents/composer.ts +469 -0
  283. package/inkos/packages/core/src/agents/consolidator.ts +218 -0
  284. package/inkos/packages/core/src/agents/continuity.ts +824 -0
  285. package/inkos/packages/core/src/agents/detection-insights.ts +72 -0
  286. package/inkos/packages/core/src/agents/detector.ts +224 -0
  287. package/inkos/packages/core/src/agents/en-prompt-sections.ts +129 -0
  288. package/inkos/packages/core/src/agents/fanfic-canon-importer.ts +146 -0
  289. package/inkos/packages/core/src/agents/fanfic-dimensions.ts +87 -0
  290. package/inkos/packages/core/src/agents/fanfic-prompt-sections.ts +109 -0
  291. package/inkos/packages/core/src/agents/foundation-reviewer.ts +204 -0
  292. package/inkos/packages/core/src/agents/length-normalizer.ts +218 -0
  293. package/inkos/packages/core/src/agents/observer-prompts.ts +127 -0
  294. package/inkos/packages/core/src/agents/planner-context.ts +297 -0
  295. package/inkos/packages/core/src/agents/planner-prompts.ts +404 -0
  296. package/inkos/packages/core/src/agents/planner.ts +783 -0
  297. package/inkos/packages/core/src/agents/polisher.ts +153 -0
  298. package/inkos/packages/core/src/agents/post-write-validator.ts +873 -0
  299. package/inkos/packages/core/src/agents/radar-source.ts +123 -0
  300. package/inkos/packages/core/src/agents/radar.ts +120 -0
  301. package/inkos/packages/core/src/agents/reviser.ts +701 -0
  302. package/inkos/packages/core/src/agents/rules-reader.ts +155 -0
  303. package/inkos/packages/core/src/agents/sensitive-words.ts +142 -0
  304. package/inkos/packages/core/src/agents/settler-delta-parser.ts +53 -0
  305. package/inkos/packages/core/src/agents/settler-parser.ts +38 -0
  306. package/inkos/packages/core/src/agents/settler-prompts.ts +230 -0
  307. package/inkos/packages/core/src/agents/short-fiction.ts +429 -0
  308. package/inkos/packages/core/src/agents/state-validator.ts +322 -0
  309. package/inkos/packages/core/src/agents/style-analyzer.ts +93 -0
  310. package/inkos/packages/core/src/agents/writer-parser.ts +178 -0
  311. package/inkos/packages/core/src/agents/writer-prompts.ts +899 -0
  312. package/inkos/packages/core/src/agents/writer.ts +1450 -0
  313. package/inkos/packages/core/src/index.ts +392 -0
  314. package/inkos/packages/core/src/interaction/book-session-store.ts +226 -0
  315. package/inkos/packages/core/src/interaction/draft-directive-parser.ts +266 -0
  316. package/inkos/packages/core/src/interaction/edit-controller.ts +270 -0
  317. package/inkos/packages/core/src/interaction/events.ts +41 -0
  318. package/inkos/packages/core/src/interaction/export-artifact.ts +151 -0
  319. package/inkos/packages/core/src/interaction/intents.ts +63 -0
  320. package/inkos/packages/core/src/interaction/modes.ts +13 -0
  321. package/inkos/packages/core/src/interaction/nl-router.ts +258 -0
  322. package/inkos/packages/core/src/interaction/project-control.ts +150 -0
  323. package/inkos/packages/core/src/interaction/project-session-store.ts +81 -0
  324. package/inkos/packages/core/src/interaction/project-tools.ts +704 -0
  325. package/inkos/packages/core/src/interaction/request-router.ts +5 -0
  326. package/inkos/packages/core/src/interaction/runtime.ts +1167 -0
  327. package/inkos/packages/core/src/interaction/session-transcript-legacy.ts +113 -0
  328. package/inkos/packages/core/src/interaction/session-transcript-restore.ts +607 -0
  329. package/inkos/packages/core/src/interaction/session-transcript-schema.ts +76 -0
  330. package/inkos/packages/core/src/interaction/session-transcript.ts +189 -0
  331. package/inkos/packages/core/src/interaction/session.ts +226 -0
  332. package/inkos/packages/core/src/interaction/truth-authority.ts +45 -0
  333. package/inkos/packages/core/src/llm/config-migration.ts +58 -0
  334. package/inkos/packages/core/src/llm/cover-providers.ts +45 -0
  335. package/inkos/packages/core/src/llm/provider.ts +1331 -0
  336. package/inkos/packages/core/src/llm/providers/endpoints/ai360.ts +42 -0
  337. package/inkos/packages/core/src/llm/providers/endpoints/anthropic.ts +82 -0
  338. package/inkos/packages/core/src/llm/providers/endpoints/astronCodingPlan.ts +30 -0
  339. package/inkos/packages/core/src/llm/providers/endpoints/baichuan.ts +28 -0
  340. package/inkos/packages/core/src/llm/providers/endpoints/bailian.ts +65 -0
  341. package/inkos/packages/core/src/llm/providers/endpoints/bailianCodingPlan.ts +30 -0
  342. package/inkos/packages/core/src/llm/providers/endpoints/custom.ts +22 -0
  343. package/inkos/packages/core/src/llm/providers/endpoints/deepseek.ts +35 -0
  344. package/inkos/packages/core/src/llm/providers/endpoints/giteeai.ts +41 -0
  345. package/inkos/packages/core/src/llm/providers/endpoints/githubCopilot.ts +43 -0
  346. package/inkos/packages/core/src/llm/providers/endpoints/glmCodingPlan.ts +28 -0
  347. package/inkos/packages/core/src/llm/providers/endpoints/google.ts +51 -0
  348. package/inkos/packages/core/src/llm/providers/endpoints/hunyuan.ts +42 -0
  349. package/inkos/packages/core/src/llm/providers/endpoints/infiniai.ts +72 -0
  350. package/inkos/packages/core/src/llm/providers/endpoints/internlm.ts +28 -0
  351. package/inkos/packages/core/src/llm/providers/endpoints/kimiCode.ts +23 -0
  352. package/inkos/packages/core/src/llm/providers/endpoints/kimiCodingPlan.ts +24 -0
  353. package/inkos/packages/core/src/llm/providers/endpoints/kkaiapi.ts +56 -0
  354. package/inkos/packages/core/src/llm/providers/endpoints/longcat.ts +25 -0
  355. package/inkos/packages/core/src/llm/providers/endpoints/minimax.ts +39 -0
  356. package/inkos/packages/core/src/llm/providers/endpoints/minimaxCodingPlan.ts +28 -0
  357. package/inkos/packages/core/src/llm/providers/endpoints/mistral.ts +40 -0
  358. package/inkos/packages/core/src/llm/providers/endpoints/modelscope.ts +30 -0
  359. package/inkos/packages/core/src/llm/providers/endpoints/moonshot.ts +39 -0
  360. package/inkos/packages/core/src/llm/providers/endpoints/newapi.ts +21 -0
  361. package/inkos/packages/core/src/llm/providers/endpoints/ollama.ts +73 -0
  362. package/inkos/packages/core/src/llm/providers/endpoints/openai.ts +77 -0
  363. package/inkos/packages/core/src/llm/providers/endpoints/opencodeCodingPlan.ts +30 -0
  364. package/inkos/packages/core/src/llm/providers/endpoints/openrouter.ts +87 -0
  365. package/inkos/packages/core/src/llm/providers/endpoints/ppio.ts +86 -0
  366. package/inkos/packages/core/src/llm/providers/endpoints/qiniu.ts +32 -0
  367. package/inkos/packages/core/src/llm/providers/endpoints/sensenova.ts +45 -0
  368. package/inkos/packages/core/src/llm/providers/endpoints/siliconcloud.ts +126 -0
  369. package/inkos/packages/core/src/llm/providers/endpoints/spark.ts +33 -0
  370. package/inkos/packages/core/src/llm/providers/endpoints/stepfun.ts +35 -0
  371. package/inkos/packages/core/src/llm/providers/endpoints/tencentcloud.ts +25 -0
  372. package/inkos/packages/core/src/llm/providers/endpoints/volcengine.ts +52 -0
  373. package/inkos/packages/core/src/llm/providers/endpoints/volcengineCodingPlan.ts +42 -0
  374. package/inkos/packages/core/src/llm/providers/endpoints/wenxin.ts +106 -0
  375. package/inkos/packages/core/src/llm/providers/endpoints/xai.ts +34 -0
  376. package/inkos/packages/core/src/llm/providers/endpoints/xiaomimimo.ts +26 -0
  377. package/inkos/packages/core/src/llm/providers/endpoints/zeroone.ts +34 -0
  378. package/inkos/packages/core/src/llm/providers/endpoints/zhipu.ts +61 -0
  379. package/inkos/packages/core/src/llm/providers/index.ts +71 -0
  380. package/inkos/packages/core/src/llm/providers/lookup.ts +70 -0
  381. package/inkos/packages/core/src/llm/providers/probe.ts +35 -0
  382. package/inkos/packages/core/src/llm/providers/types.ts +89 -0
  383. package/inkos/packages/core/src/llm/providers/verify.ts +104 -0
  384. package/inkos/packages/core/src/llm/secrets.ts +77 -0
  385. package/inkos/packages/core/src/llm/service-presets.ts +215 -0
  386. package/inkos/packages/core/src/llm/service-resolver.ts +91 -0
  387. package/inkos/packages/core/src/models/book-rules.ts +126 -0
  388. package/inkos/packages/core/src/models/book.ts +70 -0
  389. package/inkos/packages/core/src/models/chapter.ts +42 -0
  390. package/inkos/packages/core/src/models/detection.ts +25 -0
  391. package/inkos/packages/core/src/models/genre-profile.ts +36 -0
  392. package/inkos/packages/core/src/models/input-governance.ts +99 -0
  393. package/inkos/packages/core/src/models/length-governance.ts +46 -0
  394. package/inkos/packages/core/src/models/project.ts +161 -0
  395. package/inkos/packages/core/src/models/runtime-state.ts +144 -0
  396. package/inkos/packages/core/src/models/state.ts +52 -0
  397. package/inkos/packages/core/src/models/style-profile.ts +15 -0
  398. package/inkos/packages/core/src/notify/dispatcher.ts +96 -0
  399. package/inkos/packages/core/src/notify/feishu.ts +34 -0
  400. package/inkos/packages/core/src/notify/telegram.ts +25 -0
  401. package/inkos/packages/core/src/notify/webhook.ts +58 -0
  402. package/inkos/packages/core/src/notify/wechat-work.ts +22 -0
  403. package/inkos/packages/core/src/pipeline/agent.ts +691 -0
  404. package/inkos/packages/core/src/pipeline/chapter-persistence.ts +79 -0
  405. package/inkos/packages/core/src/pipeline/chapter-review-cycle.ts +324 -0
  406. package/inkos/packages/core/src/pipeline/chapter-state-recovery.ts +236 -0
  407. package/inkos/packages/core/src/pipeline/chapter-truth-validation.ts +145 -0
  408. package/inkos/packages/core/src/pipeline/detection-runner.ts +164 -0
  409. package/inkos/packages/core/src/pipeline/persisted-governed-plan.ts +216 -0
  410. package/inkos/packages/core/src/pipeline/runner.ts +3438 -0
  411. package/inkos/packages/core/src/pipeline/scheduler.ts +411 -0
  412. package/inkos/packages/core/src/pipeline/short-fiction-runner.ts +801 -0
  413. package/inkos/packages/core/src/prompts/index.ts +1 -0
  414. package/inkos/packages/core/src/prompts/short-fiction.ts +273 -0
  415. package/inkos/packages/core/src/state/manager.ts +560 -0
  416. package/inkos/packages/core/src/state/memory-db.ts +359 -0
  417. package/inkos/packages/core/src/state/runtime-state-store.ts +164 -0
  418. package/inkos/packages/core/src/state/state-bootstrap.ts +657 -0
  419. package/inkos/packages/core/src/state/state-projections.ts +255 -0
  420. package/inkos/packages/core/src/state/state-reducer.ts +260 -0
  421. package/inkos/packages/core/src/state/state-validator.ts +117 -0
  422. package/inkos/packages/core/src/utils/analytics.ts +92 -0
  423. package/inkos/packages/core/src/utils/book-id.ts +31 -0
  424. package/inkos/packages/core/src/utils/cadence-policy.ts +46 -0
  425. package/inkos/packages/core/src/utils/chapter-cadence.ts +211 -0
  426. package/inkos/packages/core/src/utils/chapter-memo-parser.ts +157 -0
  427. package/inkos/packages/core/src/utils/chapter-splitter.ts +80 -0
  428. package/inkos/packages/core/src/utils/config-loader.ts +29 -0
  429. package/inkos/packages/core/src/utils/context-assembly.ts +98 -0
  430. package/inkos/packages/core/src/utils/context-filter.ts +190 -0
  431. package/inkos/packages/core/src/utils/effective-llm-config.ts +529 -0
  432. package/inkos/packages/core/src/utils/governed-context.ts +101 -0
  433. package/inkos/packages/core/src/utils/governed-working-set.ts +395 -0
  434. package/inkos/packages/core/src/utils/hook-arbiter.ts +332 -0
  435. package/inkos/packages/core/src/utils/hook-governance.ts +199 -0
  436. package/inkos/packages/core/src/utils/hook-health.ts +189 -0
  437. package/inkos/packages/core/src/utils/hook-ledger-validator.ts +277 -0
  438. package/inkos/packages/core/src/utils/hook-lifecycle.ts +224 -0
  439. package/inkos/packages/core/src/utils/hook-policy.ts +115 -0
  440. package/inkos/packages/core/src/utils/hook-promotion.ts +313 -0
  441. package/inkos/packages/core/src/utils/hook-stale-detection.ts +168 -0
  442. package/inkos/packages/core/src/utils/length-metrics.ts +123 -0
  443. package/inkos/packages/core/src/utils/llm-endpoint-auth.ts +40 -0
  444. package/inkos/packages/core/src/utils/llm-env.ts +74 -0
  445. package/inkos/packages/core/src/utils/logger.ts +123 -0
  446. package/inkos/packages/core/src/utils/long-span-fatigue.ts +545 -0
  447. package/inkos/packages/core/src/utils/memory-retrieval.ts +527 -0
  448. package/inkos/packages/core/src/utils/narrative-control.ts +177 -0
  449. package/inkos/packages/core/src/utils/outline-paths.ts +275 -0
  450. package/inkos/packages/core/src/utils/path-safety.ts +11 -0
  451. package/inkos/packages/core/src/utils/planning-materials.ts +185 -0
  452. package/inkos/packages/core/src/utils/pov-filter.ts +149 -0
  453. package/inkos/packages/core/src/utils/proxy-fetch.ts +44 -0
  454. package/inkos/packages/core/src/utils/runtime-writer.ts +41 -0
  455. package/inkos/packages/core/src/utils/spot-fix-patches.ts +189 -0
  456. package/inkos/packages/core/src/utils/story-markdown.ts +346 -0
  457. package/inkos/packages/core/src/utils/web-search.ts +82 -0
  458. package/inkos/packages/core/src/utils/writing-methodology.ts +164 -0
  459. package/inkos/packages/core/tsconfig.json +8 -0
  460. package/inkos/packages/core/vitest.config.ts +7 -0
  461. package/inkos/packages/studio/components.json +25 -0
  462. package/inkos/packages/studio/index.html +13 -0
  463. package/inkos/packages/studio/package.json +72 -0
  464. package/inkos/packages/studio/postcss.config.js +3 -0
  465. package/inkos/packages/studio/src/App.test.ts +25 -0
  466. package/inkos/packages/studio/src/App.tsx +280 -0
  467. package/inkos/packages/studio/src/api/__tests__/normalize-base-url.test.ts +40 -0
  468. package/inkos/packages/studio/src/api/book-create.test.ts +104 -0
  469. package/inkos/packages/studio/src/api/book-create.ts +94 -0
  470. package/inkos/packages/studio/src/api/errors.ts +17 -0
  471. package/inkos/packages/studio/src/api/index.ts +30 -0
  472. package/inkos/packages/studio/src/api/lib/run-store.ts +177 -0
  473. package/inkos/packages/studio/src/api/lib/sse.ts +50 -0
  474. package/inkos/packages/studio/src/api/phase5-hotfix.test.ts +335 -0
  475. package/inkos/packages/studio/src/api/safety.ts +6 -0
  476. package/inkos/packages/studio/src/api/server.test.ts +3162 -0
  477. package/inkos/packages/studio/src/api/server.ts +3666 -0
  478. package/inkos/packages/studio/src/api/v13-hotfix-round4.test.ts +226 -0
  479. package/inkos/packages/studio/src/app-state.test.ts +8 -0
  480. package/inkos/packages/studio/src/app-state.ts +1 -0
  481. package/inkos/packages/studio/src/components/ConfirmDialog.tsx +95 -0
  482. package/inkos/packages/studio/src/components/ServiceConfigSourceCard.tsx +139 -0
  483. package/inkos/packages/studio/src/components/ServiceQuickLinks.tsx +65 -0
  484. package/inkos/packages/studio/src/components/Sidebar.tsx +652 -0
  485. package/inkos/packages/studio/src/components/ai-elements/code-block.tsx +562 -0
  486. package/inkos/packages/studio/src/components/ai-elements/confirmation.tsx +174 -0
  487. package/inkos/packages/studio/src/components/ai-elements/message.tsx +360 -0
  488. package/inkos/packages/studio/src/components/ai-elements/prompt-input.tsx +1457 -0
  489. package/inkos/packages/studio/src/components/ai-elements/reasoning.tsx +226 -0
  490. package/inkos/packages/studio/src/components/ai-elements/shimmer.tsx +77 -0
  491. package/inkos/packages/studio/src/components/ai-elements/tool.tsx +173 -0
  492. package/inkos/packages/studio/src/components/chat/BookSidebar.tsx +291 -0
  493. package/inkos/packages/studio/src/components/chat/ChatMessage.tsx +39 -0
  494. package/inkos/packages/studio/src/components/chat/QuickActions.tsx +73 -0
  495. package/inkos/packages/studio/src/components/chat/ToolExecutionSteps.tsx +320 -0
  496. package/inkos/packages/studio/src/components/chat/__tests__/ToolExecutionSteps.test.ts +114 -0
  497. package/inkos/packages/studio/src/components/chat-utils.ts +56 -0
  498. package/inkos/packages/studio/src/components/chatbar-state.test.ts +69 -0
  499. package/inkos/packages/studio/src/components/sidebar/ChaptersSection.tsx +66 -0
  500. package/inkos/packages/studio/src/components/sidebar/CharacterSection.tsx +129 -0
  501. package/inkos/packages/studio/src/components/sidebar/FoundationSection.tsx +61 -0
  502. package/inkos/packages/studio/src/components/sidebar/ProgressSection.tsx +124 -0
  503. package/inkos/packages/studio/src/components/sidebar/SidebarCard.tsx +30 -0
  504. package/inkos/packages/studio/src/components/sidebar/SummarySection.tsx +89 -0
  505. package/inkos/packages/studio/src/components/ui/alert.tsx +76 -0
  506. package/inkos/packages/studio/src/components/ui/badge.tsx +52 -0
  507. package/inkos/packages/studio/src/components/ui/button-group.tsx +87 -0
  508. package/inkos/packages/studio/src/components/ui/button.tsx +58 -0
  509. package/inkos/packages/studio/src/components/ui/collapsible.tsx +19 -0
  510. package/inkos/packages/studio/src/components/ui/command.tsx +194 -0
  511. package/inkos/packages/studio/src/components/ui/dialog.tsx +158 -0
  512. package/inkos/packages/studio/src/components/ui/dropdown-menu.tsx +266 -0
  513. package/inkos/packages/studio/src/components/ui/hover-card.tsx +51 -0
  514. package/inkos/packages/studio/src/components/ui/input-group.tsx +158 -0
  515. package/inkos/packages/studio/src/components/ui/input.tsx +20 -0
  516. package/inkos/packages/studio/src/components/ui/select.tsx +199 -0
  517. package/inkos/packages/studio/src/components/ui/separator.tsx +23 -0
  518. package/inkos/packages/studio/src/components/ui/spinner.tsx +10 -0
  519. package/inkos/packages/studio/src/components/ui/textarea.tsx +18 -0
  520. package/inkos/packages/studio/src/components/ui/tooltip.tsx +66 -0
  521. package/inkos/packages/studio/src/constants/service-groups.ts +29 -0
  522. package/inkos/packages/studio/src/hooks/use-api.test.ts +93 -0
  523. package/inkos/packages/studio/src/hooks/use-api.ts +189 -0
  524. package/inkos/packages/studio/src/hooks/use-book-activity.test.ts +129 -0
  525. package/inkos/packages/studio/src/hooks/use-book-activity.ts +180 -0
  526. package/inkos/packages/studio/src/hooks/use-colors.ts +27 -0
  527. package/inkos/packages/studio/src/hooks/use-hash-route.test.ts +101 -0
  528. package/inkos/packages/studio/src/hooks/use-hash-route.ts +89 -0
  529. package/inkos/packages/studio/src/hooks/use-i18n.ts +289 -0
  530. package/inkos/packages/studio/src/hooks/use-session-events.ts +64 -0
  531. package/inkos/packages/studio/src/hooks/use-sse.test.ts +52 -0
  532. package/inkos/packages/studio/src/hooks/use-sse.ts +92 -0
  533. package/inkos/packages/studio/src/hooks/use-theme.test.ts +31 -0
  534. package/inkos/packages/studio/src/hooks/use-theme.ts +75 -0
  535. package/inkos/packages/studio/src/index.css +323 -0
  536. package/inkos/packages/studio/src/lib/error-copy.test.ts +34 -0
  537. package/inkos/packages/studio/src/lib/error-copy.ts +37 -0
  538. package/inkos/packages/studio/src/lib/utils.ts +6 -0
  539. package/inkos/packages/studio/src/main.tsx +10 -0
  540. package/inkos/packages/studio/src/pages/Analytics.tsx +80 -0
  541. package/inkos/packages/studio/src/pages/BookCreate.tsx +895 -0
  542. package/inkos/packages/studio/src/pages/BookDetail.tsx +652 -0
  543. package/inkos/packages/studio/src/pages/ChapterReader.tsx +266 -0
  544. package/inkos/packages/studio/src/pages/ChatPage.tsx +521 -0
  545. package/inkos/packages/studio/src/pages/DaemonControl.tsx +116 -0
  546. package/inkos/packages/studio/src/pages/Dashboard.tsx +379 -0
  547. package/inkos/packages/studio/src/pages/DoctorView.tsx +82 -0
  548. package/inkos/packages/studio/src/pages/GenreManager.tsx +464 -0
  549. package/inkos/packages/studio/src/pages/ImportManager.tsx +216 -0
  550. package/inkos/packages/studio/src/pages/LanguageSelector.tsx +74 -0
  551. package/inkos/packages/studio/src/pages/LogViewer.tsx +82 -0
  552. package/inkos/packages/studio/src/pages/RadarView.tsx +157 -0
  553. package/inkos/packages/studio/src/pages/ServiceDetailPage.tsx +393 -0
  554. package/inkos/packages/studio/src/pages/ServiceListPage.tsx +463 -0
  555. package/inkos/packages/studio/src/pages/StyleManager.tsx +225 -0
  556. package/inkos/packages/studio/src/pages/TruthFiles.tsx +194 -0
  557. package/inkos/packages/studio/src/pages/chat-page-state.test.ts +206 -0
  558. package/inkos/packages/studio/src/pages/chat-page-state.ts +112 -0
  559. package/inkos/packages/studio/src/pages/page-state.test.ts +258 -0
  560. package/inkos/packages/studio/src/pages/service-detail-state.test.ts +294 -0
  561. package/inkos/packages/studio/src/pages/service-detail-state.ts +234 -0
  562. package/inkos/packages/studio/src/pages/style-manager-state.test.ts +22 -0
  563. package/inkos/packages/studio/src/pages/truth-files-state.test.ts +61 -0
  564. package/inkos/packages/studio/src/shared/contracts.ts +143 -0
  565. package/inkos/packages/studio/src/store/chat/__tests__/message-parts.test.ts +172 -0
  566. package/inkos/packages/studio/src/store/chat/index.ts +3 -0
  567. package/inkos/packages/studio/src/store/chat/initialState.ts +8 -0
  568. package/inkos/packages/studio/src/store/chat/message-policy.test.ts +16 -0
  569. package/inkos/packages/studio/src/store/chat/message-policy.ts +5 -0
  570. package/inkos/packages/studio/src/store/chat/parts-builder.ts +187 -0
  571. package/inkos/packages/studio/src/store/chat/selectors.ts +13 -0
  572. package/inkos/packages/studio/src/store/chat/slices/create/action.ts +10 -0
  573. package/inkos/packages/studio/src/store/chat/slices/create/initialState.ts +9 -0
  574. package/inkos/packages/studio/src/store/chat/slices/message/action.ts +417 -0
  575. package/inkos/packages/studio/src/store/chat/slices/message/initialState.ts +10 -0
  576. package/inkos/packages/studio/src/store/chat/slices/message/runtime.test.ts +21 -0
  577. package/inkos/packages/studio/src/store/chat/slices/message/runtime.ts +233 -0
  578. package/inkos/packages/studio/src/store/chat/slices/message/stream-events.ts +272 -0
  579. package/inkos/packages/studio/src/store/chat/store.ts +11 -0
  580. package/inkos/packages/studio/src/store/chat/types.ts +169 -0
  581. package/inkos/packages/studio/src/store/service/index.ts +2 -0
  582. package/inkos/packages/studio/src/store/service/store.ts +123 -0
  583. package/inkos/packages/studio/src/store/service/types.ts +50 -0
  584. package/inkos/packages/studio/tsconfig.json +24 -0
  585. package/inkos/packages/studio/tsconfig.server.json +11 -0
  586. package/inkos/packages/studio/vite.config.ts +34 -0
  587. package/inkos/packages/studio/vitest.config.ts +14 -0
  588. package/inkos/pnpm-lock.yaml +9569 -0
  589. package/inkos/pnpm-workspace.yaml +2 -0
  590. package/inkos/scripts/prepare-package-for-publish.mjs +135 -0
  591. package/inkos/scripts/restore-package-json.mjs +31 -0
  592. package/inkos/scripts/set-package-versions.mjs +74 -0
  593. package/inkos/scripts/verify-no-workspace-protocol.mjs +140 -0
  594. package/inkos/skills/SKILL.md +654 -0
  595. package/inkos/tsconfig.json +19 -0
  596. package/package.json +4 -3
  597. /package/.next/static/{F2hMZMf1IyCVAWpkbtRz7 → -3vIrBZXdQ0rp7Wa3Kz40}/_buildManifest.js +0 -0
  598. /package/.next/static/{F2hMZMf1IyCVAWpkbtRz7 → -3vIrBZXdQ0rp7Wa3Kz40}/_ssgManifest.js +0 -0
@@ -0,0 +1,469 @@
1
+ import { readFile, readdir, mkdir } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { BaseAgent } from "./base.js";
4
+ import type { BookConfig } from "../models/book.js";
5
+ import {
6
+ ContextPackageSchema,
7
+ type ChapterTrace,
8
+ type ContextPackage,
9
+ type RuleStack,
10
+ } from "../models/input-governance.js";
11
+ import type { PlanChapterOutput } from "./planner.js";
12
+ import {
13
+ parseChapterSummariesMarkdown,
14
+ retrieveMemorySelection,
15
+ } from "../utils/memory-retrieval.js";
16
+ import {
17
+ buildGovernedRuleStack,
18
+ buildGovernedTrace,
19
+ } from "../utils/context-assembly.js";
20
+ import { writeGovernedRuntimeArtifacts } from "../utils/runtime-writer.js";
21
+
22
+ export interface ComposeChapterInput {
23
+ readonly book: BookConfig;
24
+ readonly bookDir: string;
25
+ readonly chapterNumber: number;
26
+ readonly plan: PlanChapterOutput;
27
+ }
28
+
29
+ export interface ComposeChapterOutput {
30
+ readonly contextPackage: ContextPackage;
31
+ readonly ruleStack: RuleStack;
32
+ readonly trace: ChapterTrace;
33
+ readonly contextPath: string;
34
+ readonly ruleStackPath: string;
35
+ readonly tracePath: string;
36
+ }
37
+
38
+ export async function composeGovernedChapter(input: ComposeChapterInput): Promise<ComposeChapterOutput> {
39
+ const storyDir = join(input.bookDir, "story");
40
+ const runtimeDir = join(storyDir, "runtime");
41
+ await mkdir(runtimeDir, { recursive: true });
42
+
43
+ const selectedContext = await collectSelectedContext(
44
+ storyDir,
45
+ input.plan,
46
+ input.book.language ?? "zh",
47
+ );
48
+ const contextPackage = ContextPackageSchema.parse({
49
+ chapter: input.chapterNumber,
50
+ selectedContext,
51
+ });
52
+
53
+ const ruleStack = buildGovernedRuleStack(input.plan, input.chapterNumber);
54
+ const trace = buildGovernedTrace({
55
+ chapterNumber: input.chapterNumber,
56
+ plan: input.plan,
57
+ contextPackage,
58
+ composerInputs: [input.plan.runtimePath],
59
+ });
60
+ const {
61
+ contextPath,
62
+ ruleStackPath,
63
+ tracePath,
64
+ } = await writeGovernedRuntimeArtifacts({
65
+ runtimeDir,
66
+ chapterNumber: input.chapterNumber,
67
+ contextPackage,
68
+ ruleStack,
69
+ trace,
70
+ });
71
+
72
+ return {
73
+ contextPackage,
74
+ ruleStack,
75
+ trace,
76
+ contextPath,
77
+ ruleStackPath,
78
+ tracePath,
79
+ };
80
+ }
81
+
82
+ export class ComposerAgent extends BaseAgent {
83
+ get name(): string {
84
+ return "composer";
85
+ }
86
+
87
+ async composeChapter(input: ComposeChapterInput): Promise<ComposeChapterOutput> {
88
+ return composeGovernedChapter(input);
89
+ }
90
+ }
91
+
92
+ async function collectSelectedContext(
93
+ storyDir: string,
94
+ plan: PlanChapterOutput,
95
+ language: "zh" | "en",
96
+ ): Promise<ContextPackage["selectedContext"]> {
97
+ const retrievalHints = deriveRetrievalHints(plan);
98
+ const memoBodyExcerpt = plan.memo.body.trim();
99
+ const chapterMemoEntry = memoBodyExcerpt.length > 0
100
+ ? [{
101
+ source: "runtime/chapter_memo",
102
+ reason: "Carry the planner's chapter memo into governed writing.",
103
+ excerpt: [
104
+ `goal=${plan.memo.goal}`,
105
+ plan.memo.isGoldenOpening ? "golden-opening=true" : undefined,
106
+ memoBodyExcerpt,
107
+ ].filter(Boolean).join(" | "),
108
+ }]
109
+ : [{
110
+ source: "runtime/chapter_memo",
111
+ reason: "Carry the planner's chapter memo into governed writing.",
112
+ excerpt: `goal=${plan.memo.goal}`,
113
+ }];
114
+
115
+ const entries = await Promise.all([
116
+ maybeContextSource(storyDir, "current_focus.md", "Current task focus for this chapter."),
117
+ maybeContextSource(
118
+ storyDir,
119
+ "audit_drift.md",
120
+ "Carry forward audit drift guidance from the previous chapter without polluting hard state facts.",
121
+ ),
122
+ maybeContextSource(
123
+ storyDir,
124
+ "current_state.md",
125
+ "Preserve hard state facts referenced by the active chapter brief or hard constraints.",
126
+ retrievalHints,
127
+ ),
128
+ maybeContextSource(
129
+ storyDir,
130
+ "outline/story_frame.md",
131
+ "Preserve canon constraints referenced by the active chapter brief or hard constraints.",
132
+ retrievalHints,
133
+ ),
134
+ maybeContextSource(
135
+ storyDir,
136
+ "outline/volume_map.md",
137
+ "Anchor the default planning node for this chapter.",
138
+ plan.intent.outlineNode ? [plan.intent.outlineNode] : [],
139
+ ),
140
+ maybeContextSource(
141
+ storyDir,
142
+ "parent_canon.md",
143
+ "Preserve parent canon constraints for governed continuation or fanfic writing.",
144
+ ),
145
+ maybeContextSource(
146
+ storyDir,
147
+ "fanfic_canon.md",
148
+ "Preserve extracted fanfic canon constraints for governed writing.",
149
+ ),
150
+ ]);
151
+ const trailEntries = await buildRecentChapterTrailEntries(storyDir, plan.intent.chapter);
152
+
153
+ const memorySelection = await retrieveMemorySelection({
154
+ bookDir: dirname(storyDir),
155
+ chapterNumber: plan.intent.chapter,
156
+ goal: plan.intent.goal,
157
+ outlineNode: plan.intent.outlineNode,
158
+ mustKeep: retrievalHints,
159
+ });
160
+ const hookDebtEntries = await buildHookDebtEntries(
161
+ storyDir,
162
+ plan,
163
+ memorySelection.activeHooks,
164
+ language,
165
+ );
166
+
167
+ const summaryEntries = memorySelection.summaries.map((summary) => ({
168
+ source: `story/chapter_summaries.md#${summary.chapter}`,
169
+ reason: "Relevant episodic memory retrieved for the current chapter goal.",
170
+ excerpt: [summary.title, summary.events, summary.stateChanges, summary.hookActivity]
171
+ .filter(Boolean)
172
+ .join(" | "),
173
+ }));
174
+ const factEntries = memorySelection.facts.map((fact) => ({
175
+ source: `story/current_state.md#${toFactAnchor(fact.predicate)}`,
176
+ reason: "Relevant current-state fact retrieved for the current chapter goal.",
177
+ excerpt: `${fact.predicate} | ${fact.object}`,
178
+ }));
179
+ const hookEntries = memorySelection.hooks.map((hook) => ({
180
+ source: `story/pending_hooks.md#${hook.hookId}`,
181
+ reason: "Carry forward unresolved hooks that match the chapter focus.",
182
+ excerpt: [hook.type, hook.status, hook.expectedPayoff, hook.payoffTiming, hook.notes]
183
+ .filter(Boolean)
184
+ .join(" | "),
185
+ }));
186
+ const volumeSummaryEntries = memorySelection.volumeSummaries.map((summary) => ({
187
+ source: `story/volume_summaries.md#${summary.anchor}`,
188
+ reason: "Carry forward long-span arc memory compressed from earlier volumes.",
189
+ excerpt: `${summary.heading} | ${summary.content}`,
190
+ }));
191
+
192
+ return [
193
+ ...chapterMemoEntry,
194
+ ...entries.filter((entry): entry is NonNullable<typeof entry> => entry !== null),
195
+ ...trailEntries,
196
+ ...hookDebtEntries,
197
+ ...factEntries,
198
+ ...summaryEntries,
199
+ ...volumeSummaryEntries,
200
+ ...hookEntries,
201
+ ];
202
+ }
203
+
204
+ function deriveRetrievalHints(plan: PlanChapterOutput): string[] {
205
+ return [
206
+ plan.intent.goal,
207
+ plan.intent.outlineNode,
208
+ ...plan.memo.threadRefs,
209
+ ].filter((value): value is string => Boolean(value));
210
+ }
211
+
212
+ async function buildRecentChapterTrailEntries(
213
+ storyDir: string,
214
+ chapterNumber: number,
215
+ ): Promise<ContextPackage["selectedContext"]> {
216
+ const content = await readFileOrDefault(join(storyDir, "chapter_summaries.md"));
217
+ if (!content || content === "(文件尚未创建)") {
218
+ return [];
219
+ }
220
+
221
+ const recentSummaries = parseChapterSummariesMarkdown(content)
222
+ .filter((summary) => summary.chapter < chapterNumber)
223
+ .sort((left, right) => right.chapter - left.chapter)
224
+ .slice(0, 5);
225
+ if (recentSummaries.length === 0) {
226
+ return [];
227
+ }
228
+
229
+ const entries: ContextPackage["selectedContext"] = [];
230
+ const recentTitles = recentSummaries
231
+ .map((summary) => [summary.chapter, summary.title].filter(Boolean).join(": "))
232
+ .filter(Boolean)
233
+ .join(" | ");
234
+ if (recentTitles) {
235
+ entries.push({
236
+ source: "story/chapter_summaries.md#recent_titles",
237
+ reason: "Keep recent title history visible to avoid repetitive chapter naming.",
238
+ excerpt: recentTitles,
239
+ });
240
+ }
241
+
242
+ const moodTrail = recentSummaries
243
+ .filter((summary) => summary.mood || summary.chapterType)
244
+ .map((summary) => `${summary.chapter}: ${summary.mood || "(none)"} / ${summary.chapterType || "(none)"}`)
245
+ .join(" | ");
246
+ if (moodTrail) {
247
+ entries.push({
248
+ source: "story/chapter_summaries.md#recent_mood_type_trail",
249
+ reason: "Keep recent mood and chapter-type cadence visible before writing the next chapter.",
250
+ excerpt: moodTrail,
251
+ });
252
+ }
253
+
254
+ const endingTrail = await buildRecentEndingTrail(storyDir, chapterNumber);
255
+ if (endingTrail) {
256
+ entries.push({
257
+ source: "story/chapters#recent_endings",
258
+ reason: "Show how recent chapters ended so the writer avoids structural repetition (e.g. 3 consecutive collapse endings).",
259
+ excerpt: endingTrail,
260
+ });
261
+ }
262
+
263
+ return entries;
264
+ }
265
+
266
+ async function buildRecentEndingTrail(
267
+ storyDir: string,
268
+ chapterNumber: number,
269
+ ): Promise<string | undefined> {
270
+ const chaptersDir = join(dirname(storyDir), "chapters");
271
+ try {
272
+ const files = await readdir(chaptersDir);
273
+ const chapterFiles = files
274
+ .filter((file) => file.endsWith(".md"))
275
+ .map((file) => ({ file, num: parseInt(file.slice(0, 4), 10) }))
276
+ .filter((entry) => Number.isFinite(entry.num) && entry.num < chapterNumber)
277
+ .sort((a, b) => b.num - a.num)
278
+ .slice(0, 3);
279
+
280
+ const endings: string[] = [];
281
+ for (const entry of chapterFiles.reverse()) {
282
+ const content = await readFile(join(chaptersDir, entry.file), "utf-8");
283
+ const lastLine = extractLastMeaningfulSentence(content);
284
+ if (lastLine) {
285
+ endings.push(`ch${entry.num}: ${lastLine}`);
286
+ }
287
+ }
288
+ return endings.length >= 2 ? endings.join(" | ") : undefined;
289
+ } catch {
290
+ return undefined;
291
+ }
292
+ }
293
+
294
+ function extractLastMeaningfulSentence(content: string): string | undefined {
295
+ const lines = content.split("\n").map((line) => line.trim()).filter((line) =>
296
+ line.length > 5 && !line.startsWith("#") && !line.startsWith("|") && !line.startsWith("==="),
297
+ );
298
+ const last = lines.at(-1);
299
+ if (!last) return undefined;
300
+ return last.length > 60 ? last.slice(0, 57) + "..." : last;
301
+ }
302
+
303
+ async function buildHookDebtEntries(
304
+ storyDir: string,
305
+ plan: PlanChapterOutput,
306
+ activeHooks: ReadonlyArray<{
307
+ readonly hookId: string;
308
+ readonly startChapter: number;
309
+ readonly type: string;
310
+ readonly status: string;
311
+ readonly lastAdvancedChapter: number;
312
+ readonly expectedPayoff: string;
313
+ readonly payoffTiming?: string;
314
+ readonly notes: string;
315
+ }>,
316
+ language: "zh" | "en",
317
+ ): Promise<ContextPackage["selectedContext"]> {
318
+ const targetHookIds = [...new Set(plan.memo.threadRefs)];
319
+ if (targetHookIds.length === 0) {
320
+ return [];
321
+ }
322
+
323
+ const summaries = parseChapterSummariesMarkdown(
324
+ await readFileOrDefault(join(storyDir, "chapter_summaries.md")),
325
+ );
326
+
327
+ return targetHookIds.flatMap((hookId) => {
328
+ const hook = activeHooks.find((entry) => entry.hookId === hookId);
329
+ if (!hook) {
330
+ return [];
331
+ }
332
+
333
+ const seedSummary = findHookSummary(summaries, hook.hookId, hook.startChapter, "seed");
334
+ const latestSummary = findHookSummary(summaries, hook.hookId, hook.lastAdvancedChapter, "latest");
335
+ const role = language === "en" ? "memo-referenced debt" : "备忘引用旧债";
336
+ const promise = hook.expectedPayoff || (language === "en" ? "(unspecified)" : "(未写明)");
337
+ const seedBeat = seedSummary
338
+ ? renderHookDebtBeat(seedSummary)
339
+ : (hook.notes || promise);
340
+ const latestBeat = latestSummary && latestSummary !== seedSummary
341
+ ? renderHookDebtBeat(latestSummary)
342
+ : undefined;
343
+ const age = Math.max(0, plan.intent.chapter - Math.max(1, hook.startChapter));
344
+
345
+ return [{
346
+ source: `runtime/hook_debt#${hook.hookId}`,
347
+ reason: language === "en"
348
+ ? "Narrative debt brief with original seed text for this hook agenda target."
349
+ : "含原始种子文本的叙事债务简报。",
350
+ excerpt: language === "en"
351
+ ? [
352
+ `${hook.hookId} (${hook.type}, ${role}, open ${age} chapters)`,
353
+ `reader promise: ${promise}`,
354
+ `original seed (ch${hook.startChapter}): ${seedBeat}`,
355
+ latestBeat ? `latest turn (ch${hook.lastAdvancedChapter}): ${latestBeat}` : undefined,
356
+ ].filter(Boolean).join(" | ")
357
+ : [
358
+ `${hook.hookId}(${hook.type},${role},已开${age}章)`,
359
+ `读者承诺:${promise}`,
360
+ `种于第${hook.startChapter}章:${seedBeat}`,
361
+ latestBeat ? `推进于第${hook.lastAdvancedChapter}章:${latestBeat}` : undefined,
362
+ ].filter(Boolean).join(" | "),
363
+ }];
364
+ });
365
+ }
366
+
367
+ async function maybeContextSource(
368
+ storyDir: string,
369
+ fileName: string,
370
+ reason: string,
371
+ preferredExcerpts: ReadonlyArray<string> = [],
372
+ ): Promise<ContextPackage["selectedContext"][number] | null> {
373
+ const path = join(storyDir, fileName);
374
+ let content = await readFileOrDefault(path);
375
+ let resolvedFileName = fileName;
376
+
377
+ if ((!content || content === "(文件尚未创建)")) {
378
+ // Phase 5 back-compat: the new outline/ files may be absent on legacy
379
+ // books. Fall back to the deprecated paths transparently.
380
+ const legacyFallback = outlineFallback(fileName);
381
+ if (legacyFallback) {
382
+ const legacyPath = join(storyDir, legacyFallback);
383
+ const legacyContent = await readFileOrDefault(legacyPath);
384
+ if (legacyContent && legacyContent !== "(文件尚未创建)") {
385
+ content = legacyContent;
386
+ resolvedFileName = legacyFallback;
387
+ }
388
+ }
389
+ }
390
+
391
+ if (!content || content === "(文件尚未创建)") return null;
392
+
393
+ return {
394
+ source: `story/${resolvedFileName}`,
395
+ reason,
396
+ excerpt: pickExcerpt(content, preferredExcerpts),
397
+ };
398
+ }
399
+
400
+ function outlineFallback(fileName: string): string | null {
401
+ if (fileName === "outline/story_frame.md") return "story_bible.md";
402
+ if (fileName === "outline/volume_map.md") return "volume_outline.md";
403
+ return null;
404
+ }
405
+
406
+ function pickExcerpt(content: string, preferredExcerpts: ReadonlyArray<string>): string | undefined {
407
+ for (const preferred of preferredExcerpts) {
408
+ if (preferred && content.includes(preferred)) return preferred;
409
+ }
410
+
411
+ return content
412
+ .split("\n")
413
+ .map((line) => line.trim())
414
+ .find((line) => line.length > 0 && !line.startsWith("#"));
415
+ }
416
+
417
+ function toFactAnchor(predicate: string): string {
418
+ return predicate
419
+ .trim()
420
+ .toLowerCase()
421
+ .replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-")
422
+ .replace(/^-+|-+$/g, "")
423
+ || "fact";
424
+ }
425
+
426
+ async function readFileOrDefault(path: string): Promise<string> {
427
+ try {
428
+ return await readFile(path, "utf-8");
429
+ } catch {
430
+ return "(文件尚未创建)";
431
+ }
432
+ }
433
+
434
+ function findHookSummary(
435
+ summaries: ReadonlyArray<ReturnType<typeof parseChapterSummariesMarkdown>[number]>,
436
+ hookId: string,
437
+ chapter: number,
438
+ mode: "seed" | "latest",
439
+ ) {
440
+ const directChapterHit = summaries.find((summary) => summary.chapter === chapter);
441
+ const hookMentions = summaries.filter((summary) => summaryMentionsHook(summary, hookId));
442
+ if (mode === "seed") {
443
+ return hookMentions.find((summary) => summary.chapter === chapter)
444
+ ?? hookMentions.at(0)
445
+ ?? directChapterHit;
446
+ }
447
+
448
+ return [...hookMentions].reverse().find((summary) => summary.chapter === chapter)
449
+ ?? hookMentions.at(-1)
450
+ ?? directChapterHit;
451
+ }
452
+
453
+ function summaryMentionsHook(
454
+ summary: ReturnType<typeof parseChapterSummariesMarkdown>[number],
455
+ hookId: string,
456
+ ): boolean {
457
+ return [
458
+ summary.title,
459
+ summary.events,
460
+ summary.stateChanges,
461
+ summary.hookActivity,
462
+ ].some((text) => text.includes(hookId));
463
+ }
464
+
465
+ function renderHookDebtBeat(
466
+ summary: ReturnType<typeof parseChapterSummariesMarkdown>[number],
467
+ ): string {
468
+ return `ch${summary.chapter} ${summary.title} - ${summary.events || summary.hookActivity || summary.stateChanges || "(none)"}`;
469
+ }
@@ -0,0 +1,218 @@
1
+ import { BaseAgent } from "./base.js";
2
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { readVolumeMap } from "../utils/outline-paths.js";
5
+ import {
6
+ parsePendingHooksMarkdown,
7
+ renderHookSnapshot,
8
+ } from "../utils/story-markdown.js";
9
+ import type { StoredHook } from "../state/memory-db.js";
10
+
11
+ export interface ConsolidationResult {
12
+ readonly volumeSummaries: string;
13
+ readonly archivedVolumes: number;
14
+ readonly retainedChapters: number;
15
+ /**
16
+ * Phase 7 hotfix 2: number of ledger hooks whose `promoted` flag flipped
17
+ * from false to true during this consolidation run (advanced_count rule).
18
+ * 0 when pending_hooks.md is absent or no hook crossed the threshold.
19
+ */
20
+ readonly promotedHookCount: number;
21
+ }
22
+
23
+ /**
24
+ * Consolidates chapter summaries into volume-level narrative summaries.
25
+ * Reduces token usage for long books while preserving critical context.
26
+ */
27
+ export class ConsolidatorAgent extends BaseAgent {
28
+ get name(): string {
29
+ return "consolidator";
30
+ }
31
+
32
+ /**
33
+ * Consolidate chapter summaries by volume.
34
+ * - Reads outline/volume_map.md (fallback: legacy volume_outline.md) to
35
+ * determine volume boundaries
36
+ * - For each completed volume, LLM compresses chapter summaries into a narrative paragraph
37
+ * - Archives detailed summaries, keeps only recent volume's per-chapter rows
38
+ */
39
+ async consolidate(bookDir: string): Promise<ConsolidationResult> {
40
+ const storyDir = join(bookDir, "story");
41
+ const summariesPath = join(storyDir, "chapter_summaries.md");
42
+ const volumeSummariesPath = join(storyDir, "volume_summaries.md");
43
+
44
+ const [summariesRaw, outlineRaw] = await Promise.all([
45
+ readFile(summariesPath, "utf-8").catch(() => ""),
46
+ readVolumeMap(bookDir, ""),
47
+ ]);
48
+
49
+ // Phase 7 hotfix 2: pre-archive re-promotion pass. Runs independently of
50
+ // summary consolidation so a new book (no completed volumes yet) still
51
+ // flips the `promoted` flag whenever a seed's advanced_count crosses the
52
+ // threshold.
53
+ const promotedHookCount = await this.rerunAdvancedCountPromotion(storyDir);
54
+
55
+ if (!summariesRaw || !outlineRaw) {
56
+ return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
57
+ }
58
+
59
+ // Parse volume boundaries from outline
60
+ const volumeBoundaries = this.parseVolumeBoundaries(outlineRaw);
61
+ if (volumeBoundaries.length === 0) {
62
+ return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
63
+ }
64
+
65
+ // Parse chapter summaries into rows
66
+ const { header, rows } = this.parseSummaryTable(summariesRaw);
67
+ if (rows.length === 0) {
68
+ return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
69
+ }
70
+
71
+ const maxChapter = Math.max(...rows.map((r) => r.chapter));
72
+
73
+ // Determine which volumes are "completed" (all chapters written)
74
+ const completedVolumes: Array<{ name: string; startCh: number; endCh: number; rows: typeof rows }> = [];
75
+ const currentVolumeRows: typeof rows = [];
76
+
77
+ for (const vol of volumeBoundaries) {
78
+ const volRows = rows.filter((r) => r.chapter >= vol.startCh && r.chapter <= vol.endCh);
79
+ if (vol.endCh <= maxChapter && volRows.length > 0) {
80
+ completedVolumes.push({ ...vol, rows: volRows });
81
+ } else {
82
+ // Current/incomplete volume — keep detailed rows
83
+ currentVolumeRows.push(...volRows);
84
+ }
85
+ }
86
+
87
+ // Also keep any rows not covered by volume boundaries
88
+ const coveredChapters = new Set(volumeBoundaries.flatMap((v) => {
89
+ const chs: number[] = [];
90
+ for (let i = v.startCh; i <= v.endCh; i++) chs.push(i);
91
+ return chs;
92
+ }));
93
+ for (const r of rows) {
94
+ if (!coveredChapters.has(r.chapter)) currentVolumeRows.push(r);
95
+ }
96
+
97
+ if (completedVolumes.length === 0) {
98
+ return {
99
+ volumeSummaries: "",
100
+ archivedVolumes: 0,
101
+ retainedChapters: currentVolumeRows.length,
102
+ promotedHookCount,
103
+ };
104
+ }
105
+
106
+ // LLM consolidation for each completed volume
107
+ const existingVolSummaries = await readFile(volumeSummariesPath, "utf-8").catch(() => "");
108
+ const newSummaries: string[] = existingVolSummaries ? [existingVolSummaries.trim()] : ["# Volume Summaries\n"];
109
+
110
+ for (const vol of completedVolumes) {
111
+ const volSummaryRows = vol.rows.map((r) => r.raw).join("\n");
112
+
113
+ const response = await this.chat([
114
+ {
115
+ role: "system",
116
+ content: `You are a narrative summarizer. Compress chapter-by-chapter summaries into a single coherent paragraph (max 500 words) that captures the key events, character developments, and plot progression of this volume. Preserve specific names, locations, and plot points. Write in the same language as the input.`,
117
+ },
118
+ {
119
+ role: "user",
120
+ content: `Volume: ${vol.name} (Chapters ${vol.startCh}-${vol.endCh})\n\nChapter summaries:\n${header}\n${volSummaryRows}`,
121
+ },
122
+ ], { temperature: 0.3 });
123
+
124
+ newSummaries.push(`\n## ${vol.name} (Ch.${vol.startCh}-${vol.endCh})\n\n${response.content.trim()}`);
125
+ }
126
+
127
+ // Write volume summaries
128
+ await writeFile(volumeSummariesPath, newSummaries.join("\n"), "utf-8");
129
+
130
+ // Archive detailed summaries
131
+ const archiveDir = join(storyDir, "summaries_archive");
132
+ await mkdir(archiveDir, { recursive: true });
133
+ for (const vol of completedVolumes) {
134
+ const archivePath = join(archiveDir, `vol_${vol.startCh}-${vol.endCh}.md`);
135
+ await writeFile(archivePath, `# ${vol.name}\n\n${header}\n${vol.rows.map((r) => r.raw).join("\n")}`, "utf-8");
136
+ }
137
+
138
+ // Rewrite chapter_summaries.md with only current volume rows
139
+ const retainedContent = currentVolumeRows.length > 0
140
+ ? `${header}\n${currentVolumeRows.map((r) => r.raw).join("\n")}\n`
141
+ : `${header}\n`;
142
+ await writeFile(summariesPath, retainedContent, "utf-8");
143
+
144
+ return {
145
+ volumeSummaries: newSummaries.join("\n"),
146
+ archivedVolumes: completedVolumes.length,
147
+ retainedChapters: currentVolumeRows.length,
148
+ promotedHookCount,
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Phase 7 hotfix 2 — re-run promotion for seeds whose advancedCount has
154
+ * crossed the 2-chapter threshold since architect seed time. Delegates to
155
+ * the shared `rerunPromotionPass` in utils/hook-promotion.ts.
156
+ *
157
+ * Returns the number of hooks that flipped from promoted=false (or
158
+ * undefined) to promoted=true this run.
159
+ */
160
+ private async rerunAdvancedCountPromotion(storyDir: string): Promise<number> {
161
+ const ledgerPath = join(storyDir, "pending_hooks.md");
162
+ const raw = await readFile(ledgerPath, "utf-8").catch(() => "");
163
+ if (!raw.trim()) return 0;
164
+
165
+ const hooks = parsePendingHooksMarkdown(raw);
166
+ if (hooks.length === 0) return 0;
167
+
168
+ const language: "zh" | "en" = /[\u4e00-\u9fff]/.test(raw) ? "zh" : "en";
169
+ const summariesRaw = await readFile(join(storyDir, "chapter_summaries.md"), "utf-8").catch(() => "");
170
+
171
+ const { rerunPromotionPass } = await import("../utils/hook-promotion.js");
172
+ const result = rerunPromotionPass(hooks, summariesRaw);
173
+ if (!result.updated) return 0;
174
+
175
+ await writeFile(ledgerPath, renderHookSnapshot([...result.hooks], language), "utf-8");
176
+ return result.flippedCount;
177
+ }
178
+
179
+ private parseVolumeBoundaries(outline: string): Array<{ name: string; startCh: number; endCh: number }> {
180
+ const volumes: Array<{ name: string; startCh: number; endCh: number }> = [];
181
+ const lines = outline.split("\n");
182
+ const volumeHeader = /^(第[一二三四五六七八九十百千万零〇\d]+卷|Volume\s+\d+)/i;
183
+ const rangePattern = /[((]\s*(?:第|[Cc]hapters?\s+)?(\d+)\s*[-–~~—]\s*(\d+)\s*(?:章)?\s*[))]|(?:第|[Cc]hapters?\s+)(\d+)\s*[-–~~—]\s*(\d+)\s*(?:章)?/i;
184
+
185
+ for (const rawLine of lines) {
186
+ const line = rawLine.replace(/^#+\s*/, "").trim();
187
+ if (!volumeHeader.test(line)) continue;
188
+
189
+ const rangeMatch = line.match(rangePattern);
190
+ if (!rangeMatch) continue;
191
+
192
+ const startCh = parseInt(rangeMatch[1] ?? rangeMatch[3] ?? "0", 10);
193
+ const endCh = parseInt(rangeMatch[2] ?? rangeMatch[4] ?? "0", 10);
194
+ if (startCh <= 0 || endCh <= 0) continue;
195
+
196
+ const rangeIndex = rangeMatch.index ?? line.length;
197
+ const name = line.slice(0, rangeIndex).replace(/[((]\s*$/, "").trim();
198
+ if (name.length > 0) {
199
+ volumes.push({ name, startCh, endCh });
200
+ }
201
+ }
202
+ return volumes;
203
+ }
204
+
205
+ private parseSummaryTable(raw: string): { header: string; rows: Array<{ chapter: number; raw: string }> } {
206
+ const lines = raw.split("\n");
207
+ const headerLines = lines.filter((l) => l.startsWith("|") && (l.includes("章节") || l.includes("Chapter") || l.includes("---")));
208
+ const dataLines = lines.filter((l) => l.startsWith("|") && !l.includes("章节") && !l.includes("Chapter") && !l.includes("---"));
209
+
210
+ const header = headerLines.join("\n");
211
+ const rows = dataLines.map((line) => {
212
+ const match = line.match(/\|\s*(\d+)\s*\|/);
213
+ return { chapter: match ? parseInt(match[1]!, 10) : 0, raw: line };
214
+ }).filter((r) => r.chapter > 0);
215
+
216
+ return { header, rows };
217
+ }
218
+ }