2sagaco48 1.0.0

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 (365) hide show
  1. package/diagnostics.mjs +141 -0
  2. package/dist/compute.worker-j5ahIpF-.js +1 -0
  3. package/dist/graph.worker-DG6iGCB9.js +1 -0
  4. package/dist/index.html +2314 -0
  5. package/flatten-guide.md +248 -0
  6. package/index.html +12 -0
  7. package/package.json +38 -0
  8. package/src/App.orig.tsx +204 -0
  9. package/src/App.orig1.tsx +27 -0
  10. package/src/App.orig2.tsx +25 -0
  11. package/src/App.tsx +3 -0
  12. package/src/BaseApp.tsx +18 -0
  13. package/src/INTEGRATION_LEDGER.md +22 -0
  14. package/src/PERSIST_CANARY.orig.txt +1 -0
  15. package/src/PERSIST_CANARY.txt +1 -0
  16. package/src/REGRESSION_LEDGER.md +651 -0
  17. package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
  18. package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
  19. package/src/chat/VeritasChat.tsx +69 -0
  20. package/src/chat/synthesis.ts +31 -0
  21. package/src/chat/tier.ts +45 -0
  22. package/src/components/AdaptersPage.orig.tsx +99 -0
  23. package/src/components/AdaptersPage.tsx +3 -0
  24. package/src/components/AdversarialPanel.tsx +136 -0
  25. package/src/components/AnchorBaselinePanel.tsx +75 -0
  26. package/src/components/AnswerPanel.tsx +60 -0
  27. package/src/components/ArtifactPanel.tsx +43 -0
  28. package/src/components/ChatApp.orig.tsx +1582 -0
  29. package/src/components/ChatApp.orig1.tsx +722 -0
  30. package/src/components/ChatApp.tsx +95 -0
  31. package/src/components/ChatAugmentPanels.tsx +73 -0
  32. package/src/components/CitationLedgerPanel.tsx +313 -0
  33. package/src/components/ComputeSandboxPanel.tsx +130 -0
  34. package/src/components/ConfigPanel.tsx +110 -0
  35. package/src/components/ControlPlanePage.tsx +195 -0
  36. package/src/components/CreativeTreeLifePage.tsx +234 -0
  37. package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
  38. package/src/components/DebugTracePanel.tsx +137 -0
  39. package/src/components/DeepReasoningTrace.tsx +48 -0
  40. package/src/components/EntitySheetPanel.tsx +51 -0
  41. package/src/components/FailureModesPanel.tsx +104 -0
  42. package/src/components/GBSDashboard.tsx +239 -0
  43. package/src/components/GraphView.tsx +116 -0
  44. package/src/components/HUD.tsx +125 -0
  45. package/src/components/HypothesisPanel.tsx +67 -0
  46. package/src/components/InnovationGenomeEngine.tsx +792 -0
  47. package/src/components/InnovationPersonaGuide.tsx +348 -0
  48. package/src/components/InnovationPersonaPanel.tsx +289 -0
  49. package/src/components/LiveResourceHUD.tsx +109 -0
  50. package/src/components/LongWriterPanel.tsx +112 -0
  51. package/src/components/MainPipelineV10Bridge.tsx +72 -0
  52. package/src/components/MarkdownLite.tsx +63 -0
  53. package/src/components/MemoryInspector.tsx +38 -0
  54. package/src/components/MemoryMonitor.tsx +52 -0
  55. package/src/components/MemoryStressPanel.tsx +176 -0
  56. package/src/components/ModulesPage.tsx +171 -0
  57. package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
  58. package/src/components/PipelineDebugConsole.tsx +3 -0
  59. package/src/components/PreFlightHUD.tsx +67 -0
  60. package/src/components/PrismaFetchTracePanel.tsx +87 -0
  61. package/src/components/ReportOSPanel.tsx +165 -0
  62. package/src/components/ResourceEstimatorPage.tsx +327 -0
  63. package/src/components/RichText.tsx +181 -0
  64. package/src/components/SagaOsPanel.tsx +605 -0
  65. package/src/components/SagaOsReference.tsx +421 -0
  66. package/src/components/SagaOsV2Panel.tsx +594 -0
  67. package/src/components/SagaStyleTab.tsx +382 -0
  68. package/src/components/SageBlueprintPanel.tsx +416 -0
  69. package/src/components/SharedChatInput.tsx +77 -0
  70. package/src/components/StatFinancePanel.tsx +190 -0
  71. package/src/components/StylePersonaPanel.tsx +128 -0
  72. package/src/components/TemplatesPage.tsx +300 -0
  73. package/src/components/TestPanel.tsx +63 -0
  74. package/src/components/TraceLog.tsx +89 -0
  75. package/src/components/V15CalibrationAugment.tsx +1134 -0
  76. package/src/components/V15CalibrationDialog.tsx +1871 -0
  77. package/src/components/V15Overlay.orig.tsx +120 -0
  78. package/src/components/V15Overlay.tsx +4 -0
  79. package/src/components/V15OverlayWrapper.tsx +362 -0
  80. package/src/components/V15Toggle.tsx +34 -0
  81. package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
  82. package/src/console/EngineeringConsole.tsx +103 -0
  83. package/src/console/FictionSystemPanels.tsx +882 -0
  84. package/src/console/InspectorPanels.tsx +254 -0
  85. package/src/console/ManuscriptGraderPanel.tsx +182 -0
  86. package/src/console/RecursiveReviserPanel.tsx +245 -0
  87. package/src/console/SeedControlPanel.tsx +135 -0
  88. package/src/console/StoryControllerPanel.tsx +443 -0
  89. package/src/console/engineBridge.ts +258 -0
  90. package/src/console/gemini.ts +106 -0
  91. package/src/console/grading.ts +250 -0
  92. package/src/console/util.ts +45 -0
  93. package/src/fiction/continuity.ts +372 -0
  94. package/src/fiction/crash-protection.ts +111 -0
  95. package/src/fiction/diagnostics.ts +208 -0
  96. package/src/fiction/diff.ts +74 -0
  97. package/src/fiction/entity-state-machine.ts +214 -0
  98. package/src/fiction/nive-engine.ts +404 -0
  99. package/src/fiction/pipeline-selftest.ts +132 -0
  100. package/src/fiction/seed-control.ts +193 -0
  101. package/src/fiction/story-engine.ts +326 -0
  102. package/src/fiction/types.ts +68 -0
  103. package/src/flatten-guide.md +379 -0
  104. package/src/index.css +14 -0
  105. package/src/index.orig.css +13 -0
  106. package/src/lib/academic-sources.base.ts +188 -0
  107. package/src/lib/academic-sources.orig.ts +28 -0
  108. package/src/lib/academic-sources.ts +10 -0
  109. package/src/lib/advanced-math.ts +119 -0
  110. package/src/lib/adversarial-engine.base.ts +409 -0
  111. package/src/lib/adversarial-engine.ts +82 -0
  112. package/src/lib/ai-revision.ts +623 -0
  113. package/src/lib/app-state.tsx +426 -0
  114. package/src/lib/artifact-registry.ts +97 -0
  115. package/src/lib/artifacts.ts +180 -0
  116. package/src/lib/atlas-dr.ts +167 -0
  117. package/src/lib/browser-mtls.ts +127 -0
  118. package/src/lib/browser-queue.ts +221 -0
  119. package/src/lib/browser-search-scraper.base.ts +382 -0
  120. package/src/lib/browser-search-scraper.orig.ts +90 -0
  121. package/src/lib/browser-search-scraper.ts +10 -0
  122. package/src/lib/calc-interceptor.ts +82 -0
  123. package/src/lib/citation-lane-tap.ts +78 -0
  124. package/src/lib/citation-ledger-store.ts +369 -0
  125. package/src/lib/citation-ledger.ts +203 -0
  126. package/src/lib/compute-sandbox.ts +215 -0
  127. package/src/lib/compute.worker.ts +153 -0
  128. package/src/lib/connectors/gemini.ts +120 -0
  129. package/src/lib/connectors/jina.ts +233 -0
  130. package/src/lib/connectors/marketdata.ts +51 -0
  131. package/src/lib/connectors/prismafetch.base.ts +175 -0
  132. package/src/lib/connectors/prismafetch.ts +1 -0
  133. package/src/lib/connectors/serpapi.ts +65 -0
  134. package/src/lib/connectors/wikidata.ts +44 -0
  135. package/src/lib/constraints.ts +331 -0
  136. package/src/lib/continuation-detector.base.ts +153 -0
  137. package/src/lib/continuation-detector.ts +48 -0
  138. package/src/lib/contradraft.ts +168 -0
  139. package/src/lib/cors-proxy.ts +120 -0
  140. package/src/lib/coverage.ts +81 -0
  141. package/src/lib/debug/architecture-prescription.ts +556 -0
  142. package/src/lib/debug/covea-repair.ts +543 -0
  143. package/src/lib/debug/helios-ground.ts +319 -0
  144. package/src/lib/debug/intent-decomposer.ts +281 -0
  145. package/src/lib/debug/intent-lattice.ts +349 -0
  146. package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
  147. package/src/lib/debug/pipeline-trace-bus.ts +424 -0
  148. package/src/lib/debug/prompt-forge.ts +678 -0
  149. package/src/lib/debug/repair-sites.ts +513 -0
  150. package/src/lib/debug/research-phase.ts +266 -0
  151. package/src/lib/debug/retrieval-context.ts +76 -0
  152. package/src/lib/debug/retrieval-hardener.ts +142 -0
  153. package/src/lib/debug/scraper-debug-runner.ts +576 -0
  154. package/src/lib/debug/scraper-forensics.ts +418 -0
  155. package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
  156. package/src/lib/debug/self-test.ts +415 -0
  157. package/src/lib/debug/step-attribution.ts +211 -0
  158. package/src/lib/debug/template-rubric.ts +301 -0
  159. package/src/lib/debug/unified-innovation.ts +126 -0
  160. package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
  161. package/src/lib/defense-registry.ts +104 -0
  162. package/src/lib/defenses.ts +162 -0
  163. package/src/lib/deterministic-citation-ledger.ts +174 -0
  164. package/src/lib/elo-registry.base.ts +117 -0
  165. package/src/lib/elo-registry.ts +1 -0
  166. package/src/lib/entity-resolver.ts +291 -0
  167. package/src/lib/failure-modes.ts +267 -0
  168. package/src/lib/feature-registry.ts +69 -0
  169. package/src/lib/flaw-registry.ts +143 -0
  170. package/src/lib/flaws/_template.ts +52 -0
  171. package/src/lib/flaws/builtins.ts +63 -0
  172. package/src/lib/flaws/finance.ts +169 -0
  173. package/src/lib/flaws/fixers.ts +10 -0
  174. package/src/lib/flaws/index.ts +32 -0
  175. package/src/lib/flaws/legal.ts +157 -0
  176. package/src/lib/flaws/medical.ts +188 -0
  177. package/src/lib/flaws/original-defenses-pack.ts +81 -0
  178. package/src/lib/flaws/sample-declarative-pack.json +38 -0
  179. package/src/lib/flaws/selftest.ts +201 -0
  180. package/src/lib/flaws/software-extended.ts +125 -0
  181. package/src/lib/flaws/software-rn-webgl.ts +182 -0
  182. package/src/lib/flaws/statistics-advanced.ts +46 -0
  183. package/src/lib/flaws/statistics.ts +61 -0
  184. package/src/lib/gbse/config.ts +20 -0
  185. package/src/lib/gbse/engine.ts +226 -0
  186. package/src/lib/gbse/graph.ts +118 -0
  187. package/src/lib/gbse/graph.worker.ts +21 -0
  188. package/src/lib/gbse/tests.ts +117 -0
  189. package/src/lib/gbse/tiers.ts +65 -0
  190. package/src/lib/gbse/types.ts +94 -0
  191. package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
  192. package/src/lib/innovation-genome-engine-v2.ts +3 -0
  193. package/src/lib/innovation-genome-engine.orig.ts +330 -0
  194. package/src/lib/innovation-genome-engine.ts +3 -0
  195. package/src/lib/innovation-genome-v10.orig.ts +314 -0
  196. package/src/lib/innovation-genome-v10.ts +2 -0
  197. package/src/lib/innovation-genome-v3.orig.ts +2015 -0
  198. package/src/lib/innovation-genome-v3.ts +2 -0
  199. package/src/lib/innovation-genome-v4.orig.ts +1959 -0
  200. package/src/lib/innovation-genome-v4.ts +2 -0
  201. package/src/lib/innovation-genome-v5.orig.ts +1210 -0
  202. package/src/lib/innovation-genome-v5.ts +2 -0
  203. package/src/lib/innovation-genome-v7.orig.ts +2549 -0
  204. package/src/lib/innovation-genome-v7.ts +2 -0
  205. package/src/lib/innovation-genome-v8.orig.ts +1485 -0
  206. package/src/lib/innovation-genome-v8.ts +2 -0
  207. package/src/lib/innovation-genome-v9.orig.ts +643 -0
  208. package/src/lib/innovation-genome-v9.ts +2 -0
  209. package/src/lib/jina.base.ts +293 -0
  210. package/src/lib/jina.ts +1 -0
  211. package/src/lib/live-telemetry.ts +61 -0
  212. package/src/lib/longwriter.ts +84 -0
  213. package/src/lib/manuscript-grade-v2.ts +298 -0
  214. package/src/lib/memory-governor.ts +173 -0
  215. package/src/lib/memory-stress-tests.ts +248 -0
  216. package/src/lib/model-intelligence.ts +48 -0
  217. package/src/lib/model-rotator.ts +75 -0
  218. package/src/lib/models.orig.ts +513 -0
  219. package/src/lib/models.ts +151 -0
  220. package/src/lib/n-deep.base.ts +592 -0
  221. package/src/lib/n-deep.ts +51 -0
  222. package/src/lib/nih-simulator.ts +56 -0
  223. package/src/lib/nih-vulnerability-fixes.ts +460 -0
  224. package/src/lib/omega-templates.base.ts +294 -0
  225. package/src/lib/omega-templates.ts +225 -0
  226. package/src/lib/omni-nexus.ts +64 -0
  227. package/src/lib/oracle-adapters.ts +67 -0
  228. package/src/lib/oracle-registry.ts +163 -0
  229. package/src/lib/orchestrator.ts +357 -0
  230. package/src/lib/output-boundary.ts +235 -0
  231. package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
  232. package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
  233. package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
  234. package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
  235. package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
  236. package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
  237. package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
  238. package/src/lib/overrides/vite-native-scraper.ts +848 -0
  239. package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
  240. package/src/lib/overrides/vite-native-selftest.ts +183 -0
  241. package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
  242. package/src/lib/philosophy-toolkit.ts +75 -0
  243. package/src/lib/pipeline.orig.ts +659 -0
  244. package/src/lib/pipeline.ts +42 -0
  245. package/src/lib/precache.ts +128 -0
  246. package/src/lib/py-sandbox.ts +205 -0
  247. package/src/lib/quality-score.ts +96 -0
  248. package/src/lib/quant-engine.ts +231 -0
  249. package/src/lib/quant-lib.ts +80 -0
  250. package/src/lib/reportos.ts +175 -0
  251. package/src/lib/research-os.ts +306 -0
  252. package/src/lib/resource-estimator.ts +114 -0
  253. package/src/lib/rpm-governor.ts +93 -0
  254. package/src/lib/runtime-estimator.ts +135 -0
  255. package/src/lib/sage/empty-space-v6.ts +560 -0
  256. package/src/lib/sage/empty-space-v7.ts +222 -0
  257. package/src/lib/sage/empty-space.ts +733 -0
  258. package/src/lib/sage/engine.ts +786 -0
  259. package/src/lib/sage/index.ts +17 -0
  260. package/src/lib/sage/judge.ts +246 -0
  261. package/src/lib/sage/prng.ts +53 -0
  262. package/src/lib/sage/reality-grounding.ts +380 -0
  263. package/src/lib/sage/repetition.ts +151 -0
  264. package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
  265. package/src/lib/sage/saga-os-v2.ts +274 -0
  266. package/src/lib/sage/saga-os-v3-core.ts +1491 -0
  267. package/src/lib/sage/saga-os-v3-release.ts +739 -0
  268. package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
  269. package/src/lib/sage/saga-os-v4.ts +212 -0
  270. package/src/lib/sage/saga-os-v5.ts +205 -0
  271. package/src/lib/sage/saga-os-v6.ts +346 -0
  272. package/src/lib/sage/saga-os-v7.ts +314 -0
  273. package/src/lib/sage/saga-os-v8.ts +505 -0
  274. package/src/lib/sage/saga-os.ts +329 -0
  275. package/src/lib/sage/taxonomy.ts +403 -0
  276. package/src/lib/sage/unified-roll.ts +524 -0
  277. package/src/lib/sage/worked-sketches.ts +44 -0
  278. package/src/lib/sage.ts +166 -0
  279. package/src/lib/scraper-debug-bus.ts +53 -0
  280. package/src/lib/scraper-enhanced.orig.ts +153 -0
  281. package/src/lib/scraper-enhanced.ts +10 -0
  282. package/src/lib/scraper-hardener.base.orig.ts +155 -0
  283. package/src/lib/scraper-hardener.base.ts +317 -0
  284. package/src/lib/scraper-hardener.ts +1 -0
  285. package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
  286. package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
  287. package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
  288. package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
  289. package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
  290. package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
  291. package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
  292. package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
  293. package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
  294. package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
  295. package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
  296. package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
  297. package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
  298. package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
  299. package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
  300. package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
  301. package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
  302. package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
  303. package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
  304. package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
  305. package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
  306. package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
  307. package/src/lib/scraper-vnext/query-strategist.ts +423 -0
  308. package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
  309. package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
  310. package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
  311. package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
  312. package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
  313. package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
  314. package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
  315. package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
  316. package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
  317. package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
  318. package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
  319. package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
  320. package/src/lib/scraper-vnext/strata-engine.ts +13 -0
  321. package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
  322. package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
  323. package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
  324. package/src/lib/scraper-vnext/terminal-final.ts +277 -0
  325. package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
  326. package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
  327. package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
  328. package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
  329. package/src/lib/search-cache.ts +138 -0
  330. package/src/lib/sloop-runner.base.ts +223 -0
  331. package/src/lib/sloop-runner.ts +104 -0
  332. package/src/lib/sloop.ts +114 -0
  333. package/src/lib/small-model-adapters.ts +82 -0
  334. package/src/lib/sscp.ts +86 -0
  335. package/src/lib/system-map.ts +54 -0
  336. package/src/lib/uni-node.ts +86 -0
  337. package/src/lib/universal-rigor-guard.ts +433 -0
  338. package/src/lib/v15-gate-testbed.ts +274 -0
  339. package/src/lib/v15-gemini.base.ts +89 -0
  340. package/src/lib/v15-gemini.ts +1 -0
  341. package/src/lib/v15-grounding.orig.ts +241 -0
  342. package/src/lib/v15-grounding.ts +79 -0
  343. package/src/lib/v15-pipeline.base.ts +769 -0
  344. package/src/lib/v15-pipeline.orig.ts +1154 -0
  345. package/src/lib/v15-pipeline.ts +674 -0
  346. package/src/lib/v15-questions.ts +64 -0
  347. package/src/lib/v15-rate-limiter.orig.ts +91 -0
  348. package/src/lib/v15-rate-limiter.ts +48 -0
  349. package/src/lib/v15-state.base.ts +87 -0
  350. package/src/lib/v15-state.ts +28 -0
  351. package/src/lib/visual-table-generator.tsx +207 -0
  352. package/src/lib/wasm-runtime.ts +133 -0
  353. package/src/lib/williams-persona-guide.ts +265 -0
  354. package/src/lib/williams-style.base.ts +464 -0
  355. package/src/lib/williams-style.ts +59 -0
  356. package/src/lib/worker-pool.ts +96 -0
  357. package/src/lib/writing-tiers.ts +125 -0
  358. package/src/main.orig.tsx +10 -0
  359. package/src/main.tsx +10 -0
  360. package/src/utils/cn.orig.ts +6 -0
  361. package/src/utils/cn.ts +6 -0
  362. package/src/vite-env.d.ts +1 -0
  363. package/tsconfig.json +31 -0
  364. package/unify.mjs +537 -0
  365. package/vite.config.ts +18 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * SEED CONTROL — deterministic idea-mix governor + author locks.
3
+ * ---------------------------------------------------------------------------
4
+ * Purely local, no model call. Controls the seed-determined concept flow that
5
+ * feeds the live chapter prompt:
6
+ *
7
+ * 1. EMPTY-SPACE vs HUMAN-SEEN MIX — per-chapter ratio (0-100%) of
8
+ * system-scale directives (infrastructure, institutions, ecologies,
9
+ * supply chains — the "empty spaces" between human scenes) to
10
+ * human-scale directives (relationships, craft, interiority, dialogue).
11
+ * 2. MOTIFS PER CHAPTER — how many recurring seed motifs are active (0-6).
12
+ * 3. CONCEPTS PER CHAPTER — target count of new named worldbuilding
13
+ * concepts to introduce (0-4); complements (never overrides) the
14
+ * continuity governor's hard cap.
15
+ * 4. AUTHOR LOCKS — setting / core-concept / theme / motif strings that are
16
+ * BOTH applied to the snapshot blueprint (so the base SAGE compiler emits
17
+ * them) AND injected as hard constraints in the additive seed block.
18
+ *
19
+ * Determinism: all per-chapter selections use mulberry32(seed ^ chapterIndex)
20
+ * so a seed + control state always reproduces the same directives. Logged, so
21
+ * any prompt can be re-derived by hand.
22
+ */
23
+ import type { StorySnapshot } from "./types";
24
+
25
+ export interface AuthorLocks {
26
+ setting: string; // overrides primary setting name when non-empty
27
+ coreConcept: string; // hard constraint: the book's central device/premise
28
+ theme: string; // overrides blueprint theme when non-empty
29
+ motif: string; // one author motif seed phrase (added to motif pool)
30
+ }
31
+
32
+ export interface SeedControl {
33
+ emptyVsHumanMix: number; // 0-100: % of directives that are empty-space (system-scale)
34
+ motifsPerChapter: number; // 0-6
35
+ conceptsPerChapter: number;// 0-4
36
+ locks: AuthorLocks;
37
+ enabled: boolean; // master switch; when false the seed block is omitted
38
+ }
39
+
40
+ export const DEFAULT_SEED_CONTROL: SeedControl = {
41
+ emptyVsHumanMix: 40,
42
+ motifsPerChapter: 3,
43
+ conceptsPerChapter: 2,
44
+ locks: { setting: "", coreConcept: "", theme: "", motif: "" },
45
+ enabled: true,
46
+ };
47
+
48
+ const LS_KEY = "saga.fiction.seedControl.v1";
49
+
50
+ export function loadSeedControl(): SeedControl {
51
+ try {
52
+ const raw = localStorage.getItem(LS_KEY);
53
+ if (!raw) return { ...DEFAULT_SEED_CONTROL, locks: { ...DEFAULT_SEED_CONTROL.locks } };
54
+ const p = JSON.parse(raw) as Partial<SeedControl>;
55
+ return {
56
+ emptyVsHumanMix: clampInt(p.emptyVsHumanMix, 0, 100, 40),
57
+ motifsPerChapter: clampInt(p.motifsPerChapter, 0, 6, 3),
58
+ conceptsPerChapter: clampInt(p.conceptsPerChapter, 0, 4, 2),
59
+ locks: {
60
+ setting: String(p.locks?.setting ?? ""),
61
+ coreConcept: String(p.locks?.coreConcept ?? ""),
62
+ theme: String(p.locks?.theme ?? ""),
63
+ motif: String(p.locks?.motif ?? ""),
64
+ },
65
+ enabled: p.enabled !== false,
66
+ };
67
+ } catch {
68
+ return { ...DEFAULT_SEED_CONTROL, locks: { ...DEFAULT_SEED_CONTROL.locks } };
69
+ }
70
+ }
71
+
72
+ export function saveSeedControl(c: SeedControl): void {
73
+ try {
74
+ localStorage.setItem(LS_KEY, JSON.stringify(c));
75
+ } catch { /* fail-open */ }
76
+ }
77
+
78
+ function clampInt(v: unknown, lo: number, hi: number, fb: number): number {
79
+ const n = typeof v === "number" ? v : Number(v);
80
+ if (!Number.isFinite(n)) return fb;
81
+ return Math.min(hi, Math.max(lo, Math.round(n)));
82
+ }
83
+
84
+ /** Local mulberry32 — no package dependency, deterministic. */
85
+ export function mulberry32(seed: number): () => number {
86
+ let a = seed >>> 0;
87
+ return () => {
88
+ a |= 0; a = (a + 0x6d2b79f5) | 0;
89
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
90
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
91
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
92
+ };
93
+ }
94
+
95
+ // ── deterministic directive pools (workspace-side, seed-indexed) ────────────
96
+ const EMPTY_SPACE_DIRECTIVES = [
97
+ "Surface one piece of working infrastructure (transit, water, power, post, archive) as a lived constraint, not scenery.",
98
+ "Show an institution acting through a minor functionary whose incentives differ from the protagonist's.",
99
+ "Let an ecological or weather system impose a cost or delay that cannot be argued away.",
100
+ "Reveal part of a supply chain: where one ordinary object came from and who was paid too little for it.",
101
+ "Stage one maintenance ritual (repair, inventory, calibration) that exposes who holds real power here.",
102
+ "Let a built environment remember: one structure carries visible evidence of a past use that contradicts its present one.",
103
+ "Introduce one regulatory or customary rule that binds everyone, including the powerful — then enforce it once.",
104
+ "Show distance as cost: one journey consumes time, money, or health that is not refunded.",
105
+ ];
106
+ const HUMAN_SEEN_DIRECTIVES = [
107
+ "Give two characters a private history that surfaces in subtext, not exposition.",
108
+ "Let one character perform competence in their craft through specific procedural detail.",
109
+ "Stage one interior decision where the character chooses against their own stated interest — and dramatize why.",
110
+ "Let dialogue do double duty: the literal topic differs from what is actually being negotiated.",
111
+ "Show one relationship's power balance shifting through a small, concrete exchange of objects or favors.",
112
+ "Let one character misread another's intent, with consequences that persist past the scene.",
113
+ "Ground one emotional beat in a bodily reflex or habit rather than a named feeling.",
114
+ "Give one minor character a want that collides obliquely with the protagonist's plan.",
115
+ ];
116
+ const MOTIF_POOL = [
117
+ "keys and thresholds", "ledger-ink and handwriting", "salt and preservation",
118
+ "bells and signal-hours", "mended cloth", "river crossings", "lamplight after curfew",
119
+ "sealed letters", "iron tallies", "frost on glass", "harbor foghorns", "copper wire",
120
+ ];
121
+
122
+ export interface SeedDirectives {
123
+ chapterIndex: number;
124
+ emptySpace: string[];
125
+ humanSeen: string[];
126
+ motifs: string[];
127
+ conceptsTarget: number;
128
+ }
129
+
130
+ /** Deterministically select this chapter's seed directives. Pure. */
131
+ export function selectSeedDirectives(seed: number, chapterIndex: number, control: SeedControl): SeedDirectives {
132
+ const rng = mulberry32(((seed >>> 0) ^ Math.imul(chapterIndex + 1, 0x9e3779b9)) >>> 0);
133
+ const pick = <T,>(pool: T[], n: number): T[] => {
134
+ const copy = pool.slice();
135
+ const out: T[] = [];
136
+ while (copy.length && out.length < n) out.push(copy.splice(Math.floor(rng() * copy.length), 1)[0]);
137
+ return out;
138
+ };
139
+ const totalSlots = 2;
140
+ const emptyCount = Math.round((totalSlots * control.emptyVsHumanMix) / 100);
141
+ const humanCount = totalSlots - emptyCount;
142
+ const motifs = pick(MOTIF_POOL, Math.max(0, Math.min(6, control.motifsPerChapter)));
143
+ if (control.locks.motif.trim()) motifs.unshift(control.locks.motif.trim().slice(0, 80));
144
+ return {
145
+ chapterIndex,
146
+ emptySpace: pick(EMPTY_SPACE_DIRECTIVES, emptyCount),
147
+ humanSeen: pick(HUMAN_SEEN_DIRECTIVES, humanCount),
148
+ motifs: motifs.slice(0, 7),
149
+ conceptsTarget: Math.max(0, Math.min(4, control.conceptsPerChapter)),
150
+ };
151
+ }
152
+
153
+ /** Additive seed block appended to the live chapter prompt (advisory + locks). */
154
+ export function buildSeedDirectiveBlock(d: SeedDirectives, control: SeedControl): string {
155
+ if (!control.enabled) return "";
156
+ const lines: string[] = [];
157
+ lines.push("SEED DIRECTIVES — deterministic selections for this chapter (seed-derived; locks are binding).");
158
+ const L = control.locks;
159
+ if (L.setting.trim() || L.coreConcept.trim() || L.theme.trim()) {
160
+ lines.push("AUTHOR LOCKS (hard constraints — the chapter MUST honor these):");
161
+ if (L.setting.trim()) lines.push(` · LOCKED SETTING: ${L.setting.trim().slice(0, 200)}`);
162
+ if (L.coreConcept.trim()) lines.push(` · LOCKED CORE CONCEPT: ${L.coreConcept.trim().slice(0, 300)}`);
163
+ if (L.theme.trim()) lines.push(` · LOCKED THEME: ${L.theme.trim().slice(0, 200)}`);
164
+ }
165
+ if (d.emptySpace.length) {
166
+ lines.push("Empty-space beats (system-scale; dramatize, do not lecture):");
167
+ for (const e of d.emptySpace) lines.push(` · ${e}`);
168
+ }
169
+ if (d.humanSeen.length) {
170
+ lines.push("Human-scale beats (relationship/craft/interiority):");
171
+ for (const h of d.humanSeen) lines.push(` · ${h}`);
172
+ }
173
+ if (d.motifs.length) lines.push(`Active motifs this chapter (${d.motifs.length}): ${d.motifs.join("; ")}. Weave at most once each; never list them.`);
174
+ lines.push(`New named worldbuilding concepts this chapter: introduce EXACTLY ${d.conceptsTarget} (0 means use only established concepts).`);
175
+ return lines.join("\n");
176
+ }
177
+
178
+ /**
179
+ * Apply author locks to a FRESH snapshot (mutates only the passed clone-safe
180
+ * object). Because the base SAGE compiler reads blueprint fields, a setting /
181
+ * theme lock changes the base prompt itself — the strongest honest wiring.
182
+ */
183
+ export function applySeedLocks(snapshot: StorySnapshot, control: SeedControl): void {
184
+ const L = control.locks;
185
+ try {
186
+ if (L.setting.trim()) {
187
+ (snapshot.blueprint as { primarySetting: { name: string } }).primarySetting.name = L.setting.trim().slice(0, 200);
188
+ }
189
+ if (L.theme.trim()) {
190
+ (snapshot.blueprint as { theme: string }).theme = L.theme.trim().slice(0, 200);
191
+ }
192
+ } catch { /* fail-open: locks still apply via the seed block */ }
193
+ }
@@ -0,0 +1,326 @@
1
+ /**
2
+ * STORY ENGINE CONTROLLER — live multi-turn fiction pipeline runner.
3
+ * ---------------------------------------------------------------------------
4
+ * Provides real start / stop, mid-story model switching, crash recovery,
5
+ * bounded truncation auto-continuations, and continuity governor injection.
6
+ * Commits each chapter directly to the engine's message store and dispatches
7
+ * state updates to the main chat window.
8
+ */
9
+ import { compileCompactChapterPrompt, rollSage, rollProseCadence, scrubLeakedDirective } from '@/lib/sage/engine';
10
+ import { getPersonaDirective } from '@/lib/williams-style';
11
+ import { detectDegenerateRepetition } from '@/lib/sage/repetition';
12
+ import { generateSynthesizedResponse } from "../lib/models";
13
+ import {
14
+ buildContinuityBlock,
15
+ buildContinuationPrompt,
16
+ extractChapterFacts,
17
+ foldFacts,
18
+ normalizeChapterTitle,
19
+ stripHarnessResidue,
20
+ truncationVerdict,
21
+ type ContinuityLedger,
22
+ } from "./continuity";
23
+ import type { StorySnapshot, ContinuityToggles } from "./types";
24
+ import { DEFAULT_CONTINUITY_TOGGLES } from "./types";
25
+ import { buildSeedDirectiveBlock, selectSeedDirectives, type SeedControl } from "./seed-control";
26
+
27
+ /** Strip a leading "## Title" line the model may echo; keeps body prose intact. */
28
+ function stripEchoedHeading(text: string, title: string): string {
29
+ const t = (title || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
30
+ return text
31
+ .replace(new RegExp(`^#{1,6}\\s*(?:\\d+\\.?\\s*)?${t}\\s*\\n+`, "i"), "")
32
+ .replace(/^#{1,6}\s+.+\n+/, "")
33
+ .trim();
34
+ }
35
+
36
+ /** Full post-generation sanitize: SAGA leak scrub → residue strip → heading echo strip. Never blocks output. */
37
+ function sanitizeChapterBody(raw: string, title: string, toggles: ContinuityToggles): { text: string; notes: string[] } {
38
+ const notes: string[] = [];
39
+ let text = scrubLeakedDirective(raw || "");
40
+ if (toggles.enableFM07ResidueStripping) {
41
+ const { text: cleaned, removed } = stripHarnessResidue(text);
42
+ text = cleaned;
43
+ if (removed.length) notes.push(`residue:${removed.join(",")}`);
44
+ }
45
+ const before = text;
46
+ text = stripEchoedHeading(text, title);
47
+ if (text !== before) notes.push("heading-echo-stripped");
48
+ // Never return empty — preserve a minimal non-blocking fallback so a bad scrub cannot stall the novel.
49
+ if (!text.trim()) {
50
+ text = (raw || "").trim() || "[DRAFT GAP] Chapter generation returned no substantive prose.";
51
+ notes.push("fallback-raw-or-gap");
52
+ }
53
+ return { text, notes };
54
+ }
55
+
56
+ /** Hard ceiling on a single chapter-request prompt (chars). Prevents API 400s at max scale. */
57
+ export const MAX_CHAPTER_PROMPT_CHARS = 60000;
58
+
59
+ /**
60
+ * Realistic per-model output-word ceiling. Mirrors the package generateGemini
61
+ * maxOutputTokens ladder (gemini-2.5-pro 32k, gemini-3.x/2.5-flash 16k, gemma
62
+ * 8k) converted at ~0.72 words/token with headroom. A chapter commissioned
63
+ * above this ceiling can NEVER complete in one call, so truncationVerdict would
64
+ * loop the 2 continuations every chapter and still land short. We clamp the
65
+ * per-call target to the ceiling so the truncation guard only fires on genuine
66
+ * cutoffs, not on impossible commissions.
67
+ */
68
+ export function modelWordCeiling(model: string): number {
69
+ const m = (model || "").toLowerCase();
70
+ if (/gemini-2\.5-pro/.test(m)) return 20000;
71
+ if (/gemini-3|gemini-2\.5-flash/.test(m)) return 10000;
72
+ if (/gemma-4/.test(m)) return 5200;
73
+ if (/gemma-3/.test(m)) return 5200;
74
+ return 5200;
75
+ }
76
+
77
+ async function generateWithOneRetry(
78
+ params: Parameters<typeof generateSynthesizedResponse>[0],
79
+ onLog?: (msg: string) => void,
80
+ ): Promise<string> {
81
+ try {
82
+ return await generateSynthesizedResponse(params);
83
+ } catch (err) {
84
+ const msg = err instanceof Error ? err.message : String(err);
85
+ // One honest retry after a short backoff: a single network blip must not kill an 80-chapter run.
86
+ onLog?.(`Chapter call failed (${msg.slice(0, 120)}). Retrying once in 2s…`);
87
+ await new Promise((r) => setTimeout(r, 2000));
88
+ return generateSynthesizedResponse(params);
89
+ }
90
+ }
91
+
92
+ export interface StoryEngineState {
93
+ status: "idle" | "running" | "paused" | "error" | "complete";
94
+ currentChapter: number;
95
+ totalChapters: number;
96
+ currentTitle: string;
97
+ wordCount: number;
98
+ model: string;
99
+ error?: string;
100
+ log: string[];
101
+ }
102
+
103
+ export interface ChapterOutput {
104
+ index: number;
105
+ title: string;
106
+ text: string;
107
+ words: number;
108
+ truncated: boolean;
109
+ continuationsApplied: number;
110
+ }
111
+
112
+ export function parseChaptersFromText(text: string): { index: number; title: string; text: string }[] {
113
+ if (!text.trim()) return [];
114
+ // Prefer splitting on explicit ## chapter headings (our engine's commit format).
115
+ // This avoids false positives from bare "Chapter N" dialogue/prose and keeps
116
+ // resume indices honest at novel scale.
117
+ const parts = text.split(/(?=^##\s+)/m).map((p) => p.trim()).filter(Boolean);
118
+ if (parts.length >= 2 || (parts.length === 1 && /^##\s+/.test(parts[0]))) {
119
+ const out: { index: number; title: string; text: string }[] = [];
120
+ let idx = 1;
121
+ for (const part of parts) {
122
+ const lines = part.split("\n");
123
+ const head = lines[0] ?? "";
124
+ const m = head.match(/^##\s+(?:(\d+)\.?\s*)?(.*)$/);
125
+ if (!m) continue;
126
+ const body = lines.slice(1).join("\n").trim();
127
+ // Skip empty/preamble-only blocks so a title page doesn't inflate chapter count.
128
+ if (!body || body.length < 40) continue;
129
+ const title = (m[2] || `Chapter ${idx}`).trim() || `Chapter ${idx}`;
130
+ out.push({ index: idx++, title, text: body });
131
+ }
132
+ if (out.length > 0) return out;
133
+ }
134
+ // Fallback: single-block manuscript without ## headings.
135
+ return [{ index: 1, title: "Chapter 1", text: text.trim() }];
136
+ }
137
+
138
+ export async function generateSingleChapterWithContinuity(args: {
139
+ snapshot: StorySnapshot;
140
+ chapterIndex: number;
141
+ model: string;
142
+ apiKey: string;
143
+ provider?: string;
144
+ ledger: ContinuityLedger;
145
+ toggles?: ContinuityToggles;
146
+ priorSummary: string | null;
147
+ signal?: AbortSignal;
148
+ onLog?: (msg: string) => void;
149
+ seedControl?: SeedControl | null;
150
+ }): Promise<{ chapterText: string; updatedLedger: ContinuityLedger; facts: any }> {
151
+ const {
152
+ snapshot,
153
+ chapterIndex,
154
+ model,
155
+ apiKey,
156
+ provider = "gemini",
157
+ ledger,
158
+ toggles = DEFAULT_CONTINUITY_TOGGLES,
159
+ priorSummary,
160
+ signal,
161
+ onLog,
162
+ seedControl = null,
163
+ } = args;
164
+
165
+ const bp = snapshot.blueprint;
166
+ const ch = bp.chapters[chapterIndex];
167
+ if (!ch) throw new Error(`Chapter ${chapterIndex + 1} is out of bounds (total ${bp.chapters.length})`);
168
+
169
+ let title = ch.title;
170
+ if (toggles.enableFM06TitleNormalization) {
171
+ title = normalizeChapterTitle(title);
172
+ }
173
+
174
+ const cadence = bp.cadence || rollProseCadence(bp.seed, bp.tone || "mythic", bp.macro?.name || "The Hero's Journey");
175
+ const basePrompt = compileCompactChapterPrompt(bp, chapterIndex, priorSummary, cadence);
176
+ const styleBlock = getPersonaDirective("The Architect");
177
+ const continuityBlock = buildContinuityBlock({
178
+ snapshot,
179
+ chapterIndex,
180
+ ledger,
181
+ toggles,
182
+ });
183
+
184
+ const seedBlock = seedControl?.enabled
185
+ ? buildSeedDirectiveBlock(selectSeedDirectives(snapshot.seed, chapterIndex, seedControl), seedControl)
186
+ : "";
187
+ if (seedBlock) onLog?.(`Seed directives injected (mix ${seedControl!.emptyVsHumanMix}% empty-space, ${seedControl!.motifsPerChapter} motifs, ${seedControl!.conceptsPerChapter} concepts).`);
188
+
189
+ // Order: style → base SAGE prompt → seed directives → continuity ledger.
190
+ // Continuity keeps the recency slot (it ends with "Write ONLY the chapter prose now.").
191
+ let fullPrompt = [
192
+ styleBlock ? `STYLE PERSONA:\n${styleBlock}` : "",
193
+ basePrompt,
194
+ "",
195
+ seedBlock,
196
+ continuityBlock,
197
+ ]
198
+ .filter(Boolean)
199
+ .join("\n\n");
200
+
201
+ // Prompt-size guard: trim the additive continuity block (never the base prompt) if oversized.
202
+ if (fullPrompt.length > MAX_CHAPTER_PROMPT_CHARS) {
203
+ const over = fullPrompt.length - MAX_CHAPTER_PROMPT_CHARS;
204
+ const trimmedContinuity = continuityBlock.slice(0, Math.max(2000, continuityBlock.length - over));
205
+ fullPrompt = [styleBlock ? `STYLE PERSONA:\n${styleBlock}` : "", basePrompt, "", seedBlock, trimmedContinuity]
206
+ .filter(Boolean)
207
+ .join("\n\n");
208
+ onLog?.(`Prompt capped to ${fullPrompt.length} chars (continuity block trimmed, base prompt intact).`);
209
+ }
210
+
211
+ onLog?.(`Sending chapter ${chapterIndex + 1} prompt to ${model} (${fullPrompt.length} chars)...`);
212
+
213
+ const callOnce = async (): Promise<string> => {
214
+ const raw = await generateWithOneRetry({
215
+ provider: provider as any,
216
+ model: model as any,
217
+ apiKey,
218
+ userMessage: fullPrompt,
219
+ retrievedWebData: [],
220
+ conversationHistory: [],
221
+ }, onLog);
222
+ const { text, notes } = sanitizeChapterBody(raw, title, toggles);
223
+ if (notes.length) onLog?.(`Sanitize: ${notes.join("; ")}`);
224
+ return text;
225
+ };
226
+
227
+ let cleanText = await callOnce();
228
+ if (signal?.aborted) throw new Error("Generation aborted by user.");
229
+
230
+ // SAGA repetition guard (package detectDegenerateRepetition): one fresh regen, never re-feed poisoned draft.
231
+ // Advisory only — a still-flagged second attempt is kept so the novel never stalls.
232
+ try {
233
+ const rep = detectDegenerateRepetition(cleanText);
234
+ if (rep.flagged) {
235
+ onLog?.(`Repetition guard flagged: ${rep.reasons.join("; ")}. Regenerating fresh (not re-feeding draft)…`);
236
+ if (!signal?.aborted) {
237
+ const second = await callOnce();
238
+ const rep2 = detectDegenerateRepetition(second);
239
+ cleanText = second;
240
+ if (rep2.flagged) onLog?.(`Repetition still flagged after regen (${rep2.reasons.join("; ")}); keeping second attempt (best of two).`);
241
+ else onLog?.("Repetition cleared on second attempt.");
242
+ }
243
+ }
244
+ } catch {
245
+ /* fail-open: guard must never block generation */
246
+ }
247
+
248
+ // Check truncation & bounded continuation (FM-04) — never blocks; at most 2 continuations.
249
+ // The truncation TARGET is clamped to what the selected model can actually emit
250
+ // in one call, so we never chase an impossible commission (see modelWordCeiling).
251
+ const effectiveTarget = Math.min(ch.targetWords || 1000, modelWordCeiling(model));
252
+ if ((ch.targetWords || 0) > effectiveTarget) {
253
+ onLog?.(`Chapter target ${ch.targetWords}w exceeds ${model} single-call ceiling; using ${effectiveTarget}w for the truncation check.`);
254
+ }
255
+ let continuations = 0;
256
+ if (toggles.enableFM04TruncationRecovery) {
257
+ let verdict = truncationVerdict(cleanText, effectiveTarget);
258
+ while (verdict.truncated && continuations < 2 && !signal?.aborted) {
259
+ onLog?.(`Chapter truncated (${verdict.reason}). Executing continuation ${continuations + 1}/2...`);
260
+ const contPrompt = buildContinuationPrompt({
261
+ snapshot,
262
+ chapterIndex,
263
+ title,
264
+ sofar: cleanText,
265
+ targetWords: effectiveTarget,
266
+ });
267
+
268
+ const contRaw = await generateWithOneRetry({
269
+ provider: provider as any,
270
+ model: model as any,
271
+ apiKey,
272
+ userMessage: contPrompt,
273
+ retrievedWebData: [],
274
+ conversationHistory: [],
275
+ }, onLog);
276
+
277
+ const contClean = sanitizeChapterBody(contRaw, title, toggles).text;
278
+ // Append only the novel tail — drop any re-echoed prefix the model may have repeated.
279
+ cleanText = `${cleanText.trim()}\n\n${contClean.trim()}`;
280
+ continuations++;
281
+ verdict = truncationVerdict(cleanText, effectiveTarget);
282
+ }
283
+ if (verdict.truncated) onLog?.(`Still short after ${continuations} continuation(s); committing best available (no stall).`);
284
+ }
285
+
286
+ const facts = extractChapterFacts({
287
+ chapter: chapterIndex + 1,
288
+ title,
289
+ text: cleanText,
290
+ snapshot,
291
+ });
292
+
293
+ const updatedLedger = foldFacts(ledger, facts);
294
+
295
+ return {
296
+ chapterText: cleanText,
297
+ updatedLedger,
298
+ facts,
299
+ };
300
+ }
301
+
302
+ /** Creates a safe standalone snapshot from current seed and settings. */
303
+ export function createStorySnapshot(seed: number, chaptersCount = 16): StorySnapshot {
304
+ const bp = rollSage({
305
+ seed,
306
+ chapters: chaptersCount,
307
+ });
308
+
309
+ return {
310
+ blueprint: bp,
311
+ seed,
312
+ roll: {
313
+ world: [
314
+ { label: bp.primarySetting.name, desc: bp.primarySetting.name },
315
+ ...bp.secondarySettings.map((s) => ({ label: s.name, desc: s.name })),
316
+ ],
317
+ cast: {
318
+ cast: [
319
+ { name: bp.protagonist.archetype.name, role: bp.protagonist.role },
320
+ { name: bp.deuteragonist.archetype.name, role: bp.deuteragonist.role },
321
+ { name: bp.antagonist.archetype.name, role: bp.antagonist.role },
322
+ ],
323
+ },
324
+ },
325
+ };
326
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * FICTION TYPES — story snapshot, continuity, and generation contracts.
3
+ */
4
+ import type { SageBlueprint } from '@/lib/sage/engine';
5
+
6
+ export interface CastMember {
7
+ name: string;
8
+ kind?: string;
9
+ role?: string;
10
+ archetype?: string;
11
+ [key: string]: unknown;
12
+ }
13
+
14
+ export interface WorldPlace {
15
+ label: string;
16
+ kind?: string;
17
+ desc?: string;
18
+ [key: string]: unknown;
19
+ }
20
+
21
+ export interface StorySnapshot {
22
+ blueprint: SageBlueprint;
23
+ roll: {
24
+ world: WorldPlace[];
25
+ cast: { cast: CastMember[] };
26
+ [key: string]: unknown;
27
+ };
28
+ seed: number;
29
+ cadenceSeed?: number;
30
+ realityLevel?: number;
31
+ [key: string]: unknown;
32
+ }
33
+
34
+ export interface ContinuityToggles {
35
+ enableFM02ContinueFrom: boolean;
36
+ enableFM03AntiStasis: boolean;
37
+ enableFM04TruncationRecovery: boolean;
38
+ enableFM05MotifCooldown: boolean;
39
+ enableFM06TitleNormalization: boolean;
40
+ enableFM07ResidueStripping: boolean;
41
+ enableFM08PovLock: boolean;
42
+ enableFM09ActLadder: boolean;
43
+ enableFM10TransitRules: boolean;
44
+ enableFM11ConceptBudget: boolean;
45
+ enableFM12BeatLedger: boolean;
46
+ enableFM13ClockMonotonicity: boolean;
47
+ enableFM14MeasurementBudget: boolean;
48
+ enableFM15CurrencyBudget: boolean;
49
+ enableFM16IrreversibleLedger: boolean;
50
+ }
51
+
52
+ export const DEFAULT_CONTINUITY_TOGGLES: ContinuityToggles = {
53
+ enableFM02ContinueFrom: true,
54
+ enableFM03AntiStasis: true,
55
+ enableFM04TruncationRecovery: true,
56
+ enableFM05MotifCooldown: true,
57
+ enableFM06TitleNormalization: true,
58
+ enableFM07ResidueStripping: true,
59
+ enableFM08PovLock: true,
60
+ enableFM09ActLadder: true,
61
+ enableFM10TransitRules: true,
62
+ enableFM11ConceptBudget: true,
63
+ enableFM12BeatLedger: true,
64
+ enableFM13ClockMonotonicity: true,
65
+ enableFM14MeasurementBudget: true,
66
+ enableFM15CurrencyBudget: true,
67
+ enableFM16IrreversibleLedger: true,
68
+ };