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,722 @@
1
+ /**
2
+ * Type B — ChatApp pipeline switch wrapper (P5).
3
+ * ---------------------------------------------------------------------------
4
+ * Normal mode renders the packaged ChatApp unchanged. Fiction mode intercepts
5
+ * the same form submit before the package handler can run, preserving the
6
+ * input/outbox while bypassing retrieval / 4-stage / N-Deep / SLOOP entirely,
7
+ * and honestly disables the controls that do not apply in fiction mode.
8
+ *
9
+ * P5 fixes vs P2:
10
+ * - Single consolidated MutationObserver/interval (was 3 independent ones —
11
+ * redundant DOM churn during long SAGE runs was a real jank/soft-freeze risk).
12
+ * - Live-only controls (4-Stage, N-Deep, Cluster, SLOOP) are truly disabled
13
+ * (native `disabled` + dimmed) in fiction mode, and fully restored in live
14
+ * mode — not just visually implied.
15
+ * - Preview blueprint (used for the fractal preview pane) is now the EXACT
16
+ * blueprint used to generate — no more silent reseed at submit time.
17
+ * - Per-chapter prompt is compact (story bible + this-chapter contract only),
18
+ * fixing the context-poisoning / degenerate-repetition failure mode from
19
+ * resending the full sprawling directive unchanged on every call.
20
+ * - Local repetition detector runs after every chapter; exactly one clean
21
+ * regeneration is attempted if triggered.
22
+ * - Output is scrubbed of any leaked instruction/directive fragments.
23
+ * - Manuscript judge (computed metrics + LLM rubric) available after a run.
24
+ */
25
+ import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent, type KeyboardEvent } from "react";
26
+ import { createPortal } from "react-dom";
27
+ import { ChatApp as PackageChatApp } from "./ChatApp.orig.tsx";
28
+ import { useAppState, type ChatMessage } from "@/lib/app-state";
29
+ import { generateSynthesizedResponse, MODELS } from "@/lib/models";
30
+ import { STYLE_OVERRIDES } from "@/lib/omega-templates";
31
+ import { getPersonaDirective } from "@/lib/williams-style";
32
+ import {
33
+ compileCompactChapterPrompt,
34
+ rollSage,
35
+ buildFractalPreview,
36
+ detectDegenerateRepetition,
37
+ scrubLeakedDirective,
38
+ gradeManuscript,
39
+ computeProseMetrics,
40
+ LENGTH_PROFILES,
41
+ MACRO_STRUCTURES,
42
+ newSageSeed,
43
+ type LengthId,
44
+ type SageBlueprint,
45
+ type ManuscriptGradeReport,
46
+ } from "@/lib/sage";
47
+
48
+ const LS_MODE = "veritas.sage.pipeline.mode.v2";
49
+ const LS_SCALE = "veritas.sage.scale.v2";
50
+ const LS_CHAPTERS = "veritas.sage.chapters.v2";
51
+ const LS_WORDS = "veritas.sage.wordsPerChapter.v2";
52
+ const LS_MACRO = "veritas.sage.macro.v2";
53
+ const LS_PREVIEW_SEED = "veritas.sage.previewSeed.v1";
54
+ const LS_CADENCE_SEED = "veritas.sage.cadenceSeed.v1";
55
+
56
+ function ls(key: string, fallback: string): string {
57
+ try { return localStorage.getItem(key) || fallback; } catch { return fallback; }
58
+ }
59
+ function lsSet(key: string, value: string): void {
60
+ try { localStorage.setItem(key, value); } catch { /* storage optional */ }
61
+ }
62
+ function estTokens(text: string): number {
63
+ return Math.ceil((text || "").length / 4);
64
+ }
65
+ function wordCount(text: string): number {
66
+ return (text.trim().match(/\S+/g) || []).length;
67
+ }
68
+ function stripHeading(text: string, heading: string): string {
69
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
70
+ return text.trim().replace(new RegExp(`^#{1,6}\\s*${escaped}\\s*`, "i"), "").trim();
71
+ }
72
+
73
+ // ── DOM anchor finders (query the origin package's rendered DOM) ───────────
74
+ function findModelSelect(): HTMLElement | null {
75
+ const selects = Array.from(document.querySelectorAll("header select"));
76
+ return (selects.find((s) => s.querySelector('optgroup[label="Claude"]')) as HTMLElement) ?? null;
77
+ }
78
+ function findTemplateRow(): HTMLElement | null {
79
+ const candidates = Array.from(document.querySelectorAll("div.flex.items-center.gap-3"));
80
+ return (candidates.find((el) =>
81
+ /Template/i.test(el.textContent || "") &&
82
+ /Apply template/i.test(el.textContent || "") &&
83
+ el.querySelector("select"),
84
+ ) as HTMLElement) ?? null;
85
+ }
86
+ function findStylePersonaSection(): HTMLElement | null {
87
+ return (Array.from(document.querySelectorAll("section")).find((s) =>
88
+ /Style Persona:/i.test(s.textContent || ""),
89
+ ) as HTMLElement) ?? null;
90
+ }
91
+ /** Finds the header labelled checkbox controls that only apply to live research. */
92
+ function findLiveOnlyLabels(): HTMLLabelElement[] {
93
+ const labels = Array.from(document.querySelectorAll("header label"));
94
+ return labels.filter((l) => /4-Stage|N-Deep|Cluster|SLOOP/i.test(l.textContent || "")) as HTMLLabelElement[];
95
+ }
96
+
97
+ type DomHosts = { toggleHost: HTMLElement | null; styleHost: HTMLElement | null; sageControlsHost: HTMLElement | null };
98
+
99
+ /**
100
+ * Single shared DOM bridge: one MutationObserver + one interval drive all
101
+ * portal-anchor placement AND the honest live-only control gating. Consolidated
102
+ * from three independent observers (P2) to reduce DOM-churn overhead during
103
+ * long SAGE runs, which is the more plausible source of UI jank/soft-freezes
104
+ * than an actual memory leak.
105
+ */
106
+ function useSageDomBridge(fictionMode: boolean): DomHosts {
107
+ const [hosts, setHosts] = useState<DomHosts>({ toggleHost: null, styleHost: null, sageControlsHost: null });
108
+ const fictionModeRef = useRef(fictionMode);
109
+ fictionModeRef.current = fictionMode;
110
+ const disabledElsRef = useRef<Set<HTMLInputElement>>(new Set());
111
+
112
+ useEffect(() => {
113
+ if (typeof document === "undefined") return;
114
+ let mounted = true;
115
+
116
+ const ensureAnchor = (id: string, locate: () => HTMLElement | null, position: InsertPosition): HTMLElement | null => {
117
+ const target = locate();
118
+ if (!target) return null;
119
+ let el = document.getElementById(id) as HTMLElement | null;
120
+ if (!el) {
121
+ el = document.createElement("span");
122
+ el.id = id;
123
+ el.setAttribute("data-workspace-portal", "1");
124
+ try {
125
+ target.insertAdjacentElement(position, el);
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ return el;
131
+ };
132
+
133
+ const applyLiveOnlyGating = () => {
134
+ try {
135
+ const labels = findLiveOnlyLabels();
136
+ const active = new Set<HTMLInputElement>();
137
+ for (const label of labels) {
138
+ const input = label.querySelector("input") as HTMLInputElement | null;
139
+ if (input) active.add(input);
140
+ const shouldDisable = fictionModeRef.current;
141
+ if (input instanceof HTMLInputElement) {
142
+ input.disabled = shouldDisable;
143
+ }
144
+ label.style.opacity = shouldDisable ? "0.4" : "";
145
+ label.style.pointerEvents = shouldDisable ? "none" : "";
146
+ label.title = shouldDisable ? "Not available in Fiction Engine mode" : "";
147
+ // Sibling range-input rows (N-Deep/Cluster/SLOOP sliders) sit as the
148
+ // next element when their checkbox is checked.
149
+ const nextEl = label.nextElementSibling as HTMLElement | null;
150
+ if (nextEl && nextEl.querySelector("input[type=range]")) {
151
+ nextEl.style.opacity = shouldDisable ? "0.4" : "";
152
+ nextEl.style.pointerEvents = shouldDisable ? "none" : "";
153
+ const range = nextEl.querySelector("input") as HTMLInputElement | null;
154
+ if (range instanceof HTMLInputElement) range.disabled = shouldDisable;
155
+ }
156
+ }
157
+ disabledElsRef.current = active;
158
+ } catch {
159
+ /* defensive: never let DOM gating crash the app */
160
+ }
161
+ };
162
+
163
+ const sync = () => {
164
+ const toggleHost = ensureAnchor("veritas-sage-mode-toggle", findModelSelect, "beforebegin");
165
+ const styleHost = ensureAnchor("veritas-main-style-override", findTemplateRow, "beforeend");
166
+ const sageControlsHost = ensureAnchor("veritas-sage-main-controls", findStylePersonaSection, "beforebegin");
167
+ applyLiveOnlyGating();
168
+ if (mounted) {
169
+ setHosts((prev) =>
170
+ prev.toggleHost === toggleHost && prev.styleHost === styleHost && prev.sageControlsHost === sageControlsHost
171
+ ? prev
172
+ : { toggleHost, styleHost, sageControlsHost },
173
+ );
174
+ }
175
+ };
176
+
177
+ sync();
178
+ const observer = new MutationObserver(sync);
179
+ observer.observe(document.body, { childList: true, subtree: true });
180
+ const interval = window.setInterval(sync, 600);
181
+
182
+ return () => {
183
+ mounted = false;
184
+ observer.disconnect();
185
+ window.clearInterval(interval);
186
+ // Force a full restore pass (not just the last-tracked set) so an
187
+ // unmount while fiction mode was active never leaves live controls
188
+ // permanently disabled/dimmed.
189
+ fictionModeRef.current = false;
190
+ applyLiveOnlyGating();
191
+ for (const id of ["veritas-sage-mode-toggle", "veritas-main-style-override", "veritas-sage-main-controls"]) {
192
+ document.getElementById(id)?.remove();
193
+ }
194
+ };
195
+ // Re-run gating immediately when fictionMode flips; anchors themselves are stable.
196
+ }, [fictionMode]);
197
+
198
+ return hosts;
199
+ }
200
+
201
+ function ModeToggle({ fictionMode, setFictionMode }: { fictionMode: boolean; setFictionMode: (v: boolean) => void }) {
202
+ return (
203
+ <button
204
+ type="button"
205
+ onClick={() => setFictionMode(!fictionMode)}
206
+ className={`mr-1 flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-xs font-bold transition-colors ${
207
+ fictionMode
208
+ ? "border-rose-300 bg-rose-600 text-white shadow-sm"
209
+ : "border-emerald-300 bg-emerald-600 text-white shadow-sm"
210
+ }`}
211
+ title={fictionMode ? "Fiction Engine is ACTIVE. Click to switch to Live Research." : "Live Research is ACTIVE. Click to switch to Fiction Engine."}
212
+ >
213
+ <span>{fictionMode ? "📖 Fiction Engine" : "🔎 Live Research"}</span>
214
+ <span className="rounded bg-black/20 px-1 text-[9px] font-normal uppercase tracking-wide">click to swap</span>
215
+ </button>
216
+ );
217
+ }
218
+
219
+ function MainStyleOverrideControl() {
220
+ const { settings, setSetting } = useAppState();
221
+ return (
222
+ <label className="ml-2 inline-flex items-center gap-1.5 text-xs text-zinc-700">
223
+ <span className="font-semibold text-zinc-700">Style override</span>
224
+ <select
225
+ value={settings.styleMode}
226
+ onChange={(e) => setSetting("styleMode", e.target.value)}
227
+ className="rounded-md border border-zinc-300 bg-white px-2 py-1 text-xs text-zinc-900 focus:border-indigo-500 focus:outline-none"
228
+ title="Template style override; wired to settings.styleMode. Active in BOTH live and fiction modes."
229
+ >
230
+ <option value="OMEGA-DEFAULT">OMEGA-DEFAULT</option>
231
+ {STYLE_OVERRIDES.map((o) => (
232
+ <option key={o.token} value={o.token}>{o.token}</option>
233
+ ))}
234
+ </select>
235
+ </label>
236
+ );
237
+ }
238
+
239
+ function FractalPreviewPanel({ bp }: { bp: SageBlueprint }) {
240
+ const [open, setOpen] = useState(false);
241
+ const fractal = useMemo(() => buildFractalPreview(bp), [bp]);
242
+ return (
243
+ <div className="mt-2 rounded-lg border border-rose-100 bg-white">
244
+ <button
245
+ type="button"
246
+ onClick={() => setOpen((o) => !o)}
247
+ className="flex w-full items-center justify-between px-3 py-2 text-left text-[11px] font-bold text-rose-700"
248
+ >
249
+ <span>🔬 Fractal preview — chapter → scene → paragraph ideas (deterministic, no model call)</span>
250
+ <span>{open ? "▲" : "▼"}</span>
251
+ </button>
252
+ {open && (
253
+ <div className="max-h-72 space-y-3 overflow-y-auto border-t border-rose-100 px-3 py-2 text-[11px] text-zinc-700">
254
+ {fractal.map((ch) => (
255
+ <div key={ch.index}>
256
+ <div className="font-bold text-zinc-900">{ch.title}</div>
257
+ <div className="ml-2 mt-1 space-y-1.5">
258
+ {ch.scenes.map((sc, si) => (
259
+ <div key={si} className="border-l-2 border-rose-200 pl-2">
260
+ <div className="font-semibold text-rose-700">{sc.title} — {sc.beat}</div>
261
+ <ul className="ml-3 list-disc text-zinc-600">
262
+ {sc.paragraphIdeas.map((p, pi) => (
263
+ <li key={pi}>{p}</li>
264
+ ))}
265
+ </ul>
266
+ </div>
267
+ ))}
268
+ </div>
269
+ </div>
270
+ ))}
271
+ </div>
272
+ )}
273
+ </div>
274
+ );
275
+ }
276
+
277
+ function GradeReportPanel({ report }: { report: ManuscriptGradeReport }) {
278
+ const [open, setOpen] = useState(true);
279
+ const m = report.metrics;
280
+ return (
281
+ <div className="mt-3 rounded-lg border border-violet-200 bg-violet-50/60">
282
+ <button
283
+ type="button"
284
+ onClick={() => setOpen((o) => !o)}
285
+ className="flex w-full items-center justify-between px-3 py-2 text-left text-[11px] font-bold text-violet-800"
286
+ >
287
+ <span>📚 Manuscript grade report {report.rubric ? `— overall ${report.rubric.overall}/10` : "(parse error)"}</span>
288
+ <span>{open ? "▲" : "▼"}</span>
289
+ </button>
290
+ {open && (
291
+ <div className="border-t border-violet-100 px-3 py-2 text-[11px] text-zinc-700">
292
+ <div className="mb-2 grid grid-cols-3 gap-2 sm:grid-cols-4">
293
+ {[
294
+ ["Words", m.wordCount],
295
+ ["Avg sentence", `${m.avgSentenceLenWords}w (±${m.sentenceLenStdev})`],
296
+ ["Avg paragraph", `${m.avgParagraphLenSentences}s (±${m.paragraphLenStdev})`],
297
+ ["Dialogue", `${(m.dialogueRatio * 100).toFixed(0)}%`],
298
+ ["Distinct words", `${(m.distinctWordRatio * 100).toFixed(0)}%`],
299
+ ["Flesch ease", `${m.fleschReadingEase} (${m.fleschLabel})`],
300
+ ["Repeated 5-grams", `${(m.repeatedPhraseDensity * 100).toFixed(1)}%`],
301
+ ].map(([k, v]) => (
302
+ <div key={String(k)} className="rounded border border-violet-100 bg-white p-1.5 text-center">
303
+ <div className="text-[9px] uppercase text-zinc-400">{k}</div>
304
+ <div className="font-mono text-[11px] font-bold text-zinc-800">{String(v)}</div>
305
+ </div>
306
+ ))}
307
+ </div>
308
+ {report.rubric ? (
309
+ <div className="mb-2 grid grid-cols-2 gap-1.5 sm:grid-cols-3">
310
+ {Object.entries(report.rubric).map(([k, v]) => (
311
+ <div key={k} className="rounded border border-violet-100 bg-white p-1.5">
312
+ <div className="flex justify-between text-[10px] font-semibold text-zinc-700">
313
+ <span>{k}</span>
314
+ <span>{v}/10</span>
315
+ </div>
316
+ {report.justifications?.[k as keyof typeof report.justifications] && (
317
+ <div className="mt-0.5 text-[10px] text-zinc-500">{report.justifications[k as keyof typeof report.justifications]}</div>
318
+ )}
319
+ </div>
320
+ ))}
321
+ </div>
322
+ ) : (
323
+ <div className="mb-2 rounded border border-amber-200 bg-amber-50 p-2 text-[10px] text-amber-800">
324
+ Judge response could not be parsed as structured JSON ({report.parseError}). Raw judge text is preserved below.
325
+ </div>
326
+ )}
327
+ {report.rewriteTargets.length > 0 && (
328
+ <div>
329
+ <div className="mb-1 text-[10px] font-bold uppercase text-violet-700">Rewrite targets</div>
330
+ <div className="space-y-1">
331
+ {report.rewriteTargets.map((t, i) => (
332
+ <div key={i} className="rounded border border-violet-100 bg-white p-1.5">
333
+ <div className="flex gap-1.5 text-[10px]">
334
+ <span className="rounded bg-violet-100 px-1 font-bold text-violet-800">{t.scope}</span>
335
+ <span className="font-semibold text-zinc-700">{t.location}</span>
336
+ </div>
337
+ <div className="mt-0.5 text-[10px] text-zinc-600"><b>Issue:</b> {t.issue}</div>
338
+ <div className="text-[10px] text-zinc-600"><b>Suggestion:</b> {t.suggestion}</div>
339
+ </div>
340
+ ))}
341
+ </div>
342
+ </div>
343
+ )}
344
+ {!report.rubric && (
345
+ <details className="mt-2">
346
+ <summary className="cursor-pointer text-[10px] font-semibold text-amber-700">Raw judge text</summary>
347
+ <pre className="mt-1 max-h-40 overflow-auto whitespace-pre-wrap text-[10px] text-zinc-600">{report.rawJudgeText}</pre>
348
+ </details>
349
+ )}
350
+ </div>
351
+ )}
352
+ </div>
353
+ );
354
+ }
355
+
356
+ function SageMainControls(props: {
357
+ bp: SageBlueprint;
358
+ scaleId: LengthId;
359
+ setScaleId: (v: LengthId) => void;
360
+ chapters: number;
361
+ setChapters: (v: number) => void;
362
+ wordsPerChapter: number;
363
+ setWordsPerChapter: (v: number) => void;
364
+ macroId: string;
365
+ setMacroId: (v: string) => void;
366
+ onRerollBlueprint: () => void;
367
+ onRerollCadence: () => void;
368
+ busy: boolean;
369
+ gradeReport: ManuscriptGradeReport | null;
370
+ grading: boolean;
371
+ onGrade: () => void;
372
+ canGrade: boolean;
373
+ }) {
374
+ const { bp } = props;
375
+ const totalWords = bp.chapters.reduce((a, c) => a + c.targetWords, 0);
376
+ return (
377
+ <div className="rounded-xl border border-rose-200 bg-rose-50 px-4 py-2.5 text-xs text-rose-950">
378
+ <div className="mb-2 flex flex-wrap items-center justify-between gap-2">
379
+ <div className="font-bold uppercase tracking-wide text-rose-700">📖 Fiction Engine active — Live Research disabled</div>
380
+ <div className="font-mono text-[10px] text-rose-700">4-Stage / N-Deep / Cluster / SLOOP / grounding: bypassed &amp; grayed out above</div>
381
+ </div>
382
+ <div className="grid gap-2 md:grid-cols-[auto_auto_auto_1fr] md:items-center">
383
+ <label className="flex items-center gap-1.5">
384
+ Scale
385
+ <select
386
+ value={props.scaleId}
387
+ onChange={(e) => props.setScaleId(e.target.value as LengthId)}
388
+ className="rounded border border-rose-200 bg-white px-2 py-1"
389
+ >
390
+ {LENGTH_PROFILES.map((p) => <option key={p.id} value={p.id}>{p.label}</option>)}
391
+ </select>
392
+ </label>
393
+ <label className="flex items-center gap-1.5">
394
+ Chapters
395
+ <input
396
+ type="number"
397
+ min={1}
398
+ max={80}
399
+ value={props.chapters}
400
+ onChange={(e) => props.setChapters(Math.max(1, Math.min(80, Number(e.target.value) || 1)))}
401
+ className="w-16 rounded border border-rose-200 bg-white px-2 py-1 font-mono"
402
+ />
403
+ </label>
404
+ <label className="flex items-center gap-1.5">
405
+ Words/ch
406
+ <input
407
+ type="number"
408
+ min={300}
409
+ max={12000}
410
+ step={100}
411
+ value={props.wordsPerChapter}
412
+ onChange={(e) => props.setWordsPerChapter(Math.max(300, Math.min(12000, Number(e.target.value) || 300)))}
413
+ className="w-24 rounded border border-rose-200 bg-white px-2 py-1 font-mono"
414
+ />
415
+ </label>
416
+ <label className="flex min-w-0 items-center gap-1.5">
417
+ Macro
418
+ <select
419
+ value={props.macroId}
420
+ onChange={(e) => props.setMacroId(e.target.value)}
421
+ className="min-w-0 flex-1 rounded border border-rose-200 bg-white px-2 py-1"
422
+ >
423
+ <option value="">seed-pick</option>
424
+ {MACRO_STRUCTURES.map((m) => <option key={m.id} value={m.id}>{m.id} {m.name}</option>)}
425
+ </select>
426
+ </label>
427
+ </div>
428
+
429
+ <div className="mt-2 flex flex-wrap items-center gap-2">
430
+ <button type="button" disabled={props.busy} onClick={props.onRerollBlueprint} className="rounded border border-rose-300 bg-white px-2 py-1 font-semibold text-rose-700 hover:bg-rose-100 disabled:opacity-40">
431
+ 🎲 Reroll blueprint (seed {bp.seed})
432
+ </button>
433
+ <button type="button" disabled={props.busy} onClick={props.onRerollCadence} className="rounded border border-rose-300 bg-white px-2 py-1 font-semibold text-rose-700 hover:bg-rose-100 disabled:opacity-40">
434
+ 🎚 Reroll prose cadence ({bp.cadence.label})
435
+ </button>
436
+ <span className="font-mono text-[10px] text-rose-700">
437
+ sent {bp.cadence.sentenceMinWords}-{bp.cadence.sentenceMaxWords}w · para {bp.cadence.paragraphMinSentences}-{bp.cadence.paragraphMaxSentences}s · ch variance {Math.round(bp.cadence.chapterVarianceMinPct * 100)}-{Math.round(bp.cadence.chapterVarianceMaxPct * 100)}%
438
+ </span>
439
+ {props.canGrade && (
440
+ <button type="button" disabled={props.grading} onClick={props.onGrade} className="ml-auto rounded bg-violet-700 px-2.5 py-1 font-bold text-white hover:bg-violet-800 disabled:opacity-40">
441
+ {props.grading ? "Grading…" : "📚 Grade manuscript"}
442
+ </button>
443
+ )}
444
+ </div>
445
+
446
+ <div className="mt-1 text-[10px] text-rose-700">
447
+ Target: ~{totalWords.toLocaleString()} words. World mechanism: {bp.worldMechanism?.name ?? "—"}. Relationship texture: {bp.relationshipMechanism?.name ?? "—"}.
448
+ </div>
449
+
450
+ <FractalPreviewPanel bp={bp} />
451
+ {props.gradeReport && <GradeReportPanel report={props.gradeReport} />}
452
+ </div>
453
+ );
454
+ }
455
+
456
+ export function ChatApp({ onSubmitFromShared }: { onSubmitFromShared?: number } = {}) {
457
+ const app = useAppState();
458
+ const {
459
+ input, setInput, model, keys, persona, busyState, setBusyState,
460
+ pushMessage, updateLastMessage, setLastRun, clearDebugEvents, pushDebugEvent,
461
+ resetTelemetry, patchTelemetry, settings,
462
+ } = app;
463
+
464
+ const [fictionMode, _setFictionMode] = useState(() => ls(LS_MODE, "live") === "fiction");
465
+ const [scaleId, _setScaleId] = useState<LengthId>(() => (ls(LS_SCALE, "novel") as LengthId));
466
+ const defaultScale = useMemo(() => LENGTH_PROFILES.find((p) => p.id === scaleId) ?? LENGTH_PROFILES[3], [scaleId]);
467
+ const [chapters, _setChapters] = useState(() => Number(ls(LS_CHAPTERS, String(defaultScale.chapters))) || defaultScale.chapters);
468
+ const [wordsPerChapter, _setWordsPerChapter] = useState(() => Number(ls(LS_WORDS, String(defaultScale.wordsPerChapter))) || defaultScale.wordsPerChapter);
469
+ const [macroId, _setMacroId] = useState(() => ls(LS_MACRO, ""));
470
+ const [previewSeed, _setPreviewSeed] = useState(() => Number(ls(LS_PREVIEW_SEED, "")) || newSageSeed());
471
+ const [cadenceSeed, _setCadenceSeed] = useState(() => Number(ls(LS_CADENCE_SEED, "")) || newSageSeed());
472
+ const [gradeReport, setGradeReport] = useState<ManuscriptGradeReport | null>(null);
473
+ const [grading, setGrading] = useState(false);
474
+ const [lastFictionText, setLastFictionText] = useState<string | null>(null);
475
+
476
+ const setFictionMode = useCallback((v: boolean) => { _setFictionMode(v); lsSet(LS_MODE, v ? "fiction" : "live"); }, []);
477
+ const setScaleId = useCallback((v: LengthId) => {
478
+ _setScaleId(v); lsSet(LS_SCALE, v);
479
+ const next = LENGTH_PROFILES.find((p) => p.id === v);
480
+ if (next) { _setChapters(next.chapters); _setWordsPerChapter(next.wordsPerChapter); lsSet(LS_CHAPTERS, String(next.chapters)); lsSet(LS_WORDS, String(next.wordsPerChapter)); }
481
+ }, []);
482
+ const setChapters = useCallback((v: number) => { _setChapters(v); lsSet(LS_CHAPTERS, String(v)); }, []);
483
+ const setWordsPerChapter = useCallback((v: number) => { _setWordsPerChapter(v); lsSet(LS_WORDS, String(v)); }, []);
484
+ const setMacroId = useCallback((v: string) => { _setMacroId(v); lsSet(LS_MACRO, v); }, []);
485
+ const rerollBlueprint = useCallback(() => {
486
+ const s = newSageSeed();
487
+ _setPreviewSeed(s);
488
+ lsSet(LS_PREVIEW_SEED, String(s));
489
+ setGradeReport(null);
490
+ }, []);
491
+ const rerollCadence = useCallback(() => {
492
+ const s = newSageSeed();
493
+ _setCadenceSeed(s);
494
+ lsSet(LS_CADENCE_SEED, String(s));
495
+ }, []);
496
+
497
+ // The EXACT blueprint shown in the fractal preview is the one used to
498
+ // generate — no silent reseed at submit time (P2 bug: submit used
499
+ // Date.now() as a fresh seed, so preview and output could differ).
500
+ const blueprint: SageBlueprint = useMemo(
501
+ () =>
502
+ rollSage({
503
+ seed: previewSeed,
504
+ cadenceSeed,
505
+ premise: input.trim() || undefined,
506
+ lengthId: scaleId,
507
+ chapters,
508
+ wordsPerChapter,
509
+ forceMacroId: macroId || undefined,
510
+ }),
511
+ [previewSeed, cadenceSeed, input, scaleId, chapters, wordsPerChapter, macroId],
512
+ );
513
+
514
+ const { toggleHost, styleHost, sageControlsHost } = useSageDomBridge(fictionMode);
515
+
516
+ const runFiction = useCallback(async () => {
517
+ const prompt = input.trim();
518
+ if (!prompt || busyState !== null) return;
519
+ const startedAt = Date.now();
520
+ clearDebugEvents();
521
+ resetTelemetry();
522
+ setGradeReport(null);
523
+ patchTelemetry({ running: true, startedAt, phase: "SAGE: story blueprint", pipelineStage: 1, entropy: 0.8 });
524
+
525
+ const current = MODELS.find((m) => m.id === model);
526
+ if (!current) return;
527
+ const apiKey = keys[current.provider as keyof typeof keys] as string;
528
+ if (!apiKey) {
529
+ pushMessage({ id: `a-${Date.now()}`, role: "assistant", content: `Please configure your ${current.provider.toUpperCase()} API key in Settings.`, ts: Date.now(), model });
530
+ patchTelemetry({ running: false, phase: "missing key" });
531
+ return;
532
+ }
533
+
534
+ const bp = blueprint; // freeze the previewed blueprint for this run
535
+ const userMsg: ChatMessage = { id: `u-${Date.now()}`, role: "user", content: prompt, ts: Date.now(), model };
536
+ pushMessage(userMsg);
537
+ setInput("");
538
+ setBusyState("SAGE: compiling story blueprint");
539
+
540
+ const styleBlock = getPersonaDirective(persona.archetype.name);
541
+ const trace: NonNullable<ChatMessage["pipelineTrace"]> = [
542
+ { stage: 1, label: `SAGE blueprint compiled: ${bp.macro.name}`, ts: Date.now(), ok: true, data: { seed: bp.seed, chapters: bp.chapters.length } },
543
+ ];
544
+ const assistantId = `a-${Date.now()}`;
545
+ pushMessage({
546
+ id: assistantId,
547
+ role: "assistant",
548
+ content: `# ${bp.titleHint}\n\n_SAGE fiction run started: ${bp.chapters.length} chapters, target ~${bp.chapters.reduce((a, c) => a + c.targetWords, 0).toLocaleString()} words. Each chapter uses a freshly-composed, context-isolated prompt — no accumulated chat history is sent._`,
549
+ ts: Date.now(),
550
+ model,
551
+ pipelineTrace: trace,
552
+ usedMultiPass: true,
553
+ });
554
+
555
+ let priorSummary: string | null = null;
556
+ const blocks: string[] = [`# ${bp.titleHint}`];
557
+ let calls = 0;
558
+ try {
559
+ for (let i = 0; i < bp.chapters.length; i += 1) {
560
+ const ch = bp.chapters[i];
561
+ setBusyState(`SAGE: chapter ${i + 1}/${bp.chapters.length}`);
562
+ pushDebugEvent(`[SAGE] chapter ${i + 1}/${bp.chapters.length}: ${ch.title}`);
563
+
564
+ const generateChapter = async (): Promise<string> => {
565
+ // CONTEXT REFRESH: every call gets a brand-new, independent request —
566
+ // conversationHistory is always [] (no prior turns re-sent) and the
567
+ // compact per-chapter prompt (not the full sprawling directive) is
568
+ // the only thing that changes call-to-call. This is the structural
569
+ // fix for weak-model degenerate repetition across later chapters.
570
+ const chapterPrompt = [
571
+ styleBlock ? `STYLE PERSONA (apply silently; never name this in output):\n${styleBlock}` : "",
572
+ settings.templateEnabled ? `TEMPLATE STYLE OVERRIDE ACTIVE: ${settings.styleMode}. Apply compatible surface style only; do not import nonfiction report sections.` : "",
573
+ compileCompactChapterPrompt(bp, i, priorSummary, bp.cadence),
574
+ ].filter(Boolean).join("\n\n");
575
+ const raw = await generateSynthesizedResponse({
576
+ provider: current.provider,
577
+ model,
578
+ apiKey,
579
+ userMessage: chapterPrompt,
580
+ retrievedWebData: [],
581
+ conversationHistory: [],
582
+ });
583
+ calls += 1;
584
+ return scrubLeakedDirective(stripHeading(raw, ch.title));
585
+ };
586
+
587
+ let body = await generateChapter();
588
+ const rep = detectDegenerateRepetition(body);
589
+ if (rep.flagged) {
590
+ pushDebugEvent(`[SAGE] repetition guard triggered on "${ch.title}": ${rep.reasons.join("; ")} — regenerating fresh (not re-feeding poisoned draft)`);
591
+ body = await generateChapter();
592
+ const rep2 = detectDegenerateRepetition(body);
593
+ if (rep2.flagged) {
594
+ pushDebugEvent(`[SAGE] repetition guard still flagged after regeneration: ${rep2.reasons.join("; ")} — keeping second attempt (best of two)`);
595
+ }
596
+ }
597
+ if (!body) body = "[DRAFT GAP] Chapter generation returned no substantive prose.";
598
+
599
+ blocks.push(`## ${ch.title}\n\n${body}`);
600
+ priorSummary = `${ch.title}: ${body.slice(0, 420).replace(/\s+/g, " ")}`;
601
+ trace.push({ stage: 2 + i / 100, label: `SAGE chapter complete: ${ch.title}`, ts: Date.now(), ok: wordCount(body) >= 80, data: { words: wordCount(body), repetitionFlagged: rep.flagged } });
602
+ const full = blocks.join("\n\n");
603
+ updateLastMessage((m) => m.id === assistantId ? { ...m, content: full, pipelineTrace: trace.slice() } : m);
604
+ patchTelemetry({ pipelineStage: 2, tokensOut: estTokens(full), modelCalls: calls, phase: `SAGE chapter ${i + 1}/${bp.chapters.length}` });
605
+ }
606
+ const finalText = blocks.join("\n\n");
607
+ trace.push({ stage: 3, label: "SAGE run complete", ts: Date.now(), ok: true, data: { calls, words: wordCount(finalText) } });
608
+ updateLastMessage((m) => m.id === assistantId ? { ...m, content: finalText, pipelineTrace: trace.slice() } : m);
609
+ setLastRun({
610
+ query: prompt,
611
+ model,
612
+ finalAnswer: finalText,
613
+ totalTokens: estTokens(prompt) + estTokens(finalText),
614
+ elapsedMs: Date.now() - startedAt,
615
+ sources: 0,
616
+ verifiedClaims: 0,
617
+ totalClaims: 0,
618
+ startedAt,
619
+ finishedAt: Date.now(),
620
+ pipelineTrace: trace,
621
+ tier: null,
622
+ });
623
+ patchTelemetry({ running: false, phase: "SAGE complete", elapsedMs: Date.now() - startedAt, modelCalls: calls, tokensOut: estTokens(finalText) });
624
+ pushDebugEvent(`[SAGE] complete: ${calls} chapter call(s), ${wordCount(finalText)} words`);
625
+ setLastFictionText(finalText);
626
+ } catch (err) {
627
+ const message = err instanceof Error ? err.message : String(err);
628
+ updateLastMessage((m) => m.id === assistantId ? { ...m, content: `${m.content}\n\nGeneration Failure: ${message}` } : m);
629
+ patchTelemetry({ running: false, phase: "SAGE error" });
630
+ pushDebugEvent(`[SAGE] ERROR: ${message}`);
631
+ } finally {
632
+ setBusyState(null);
633
+ }
634
+ }, [
635
+ input, busyState, clearDebugEvents, resetTelemetry, patchTelemetry, model, keys,
636
+ pushMessage, setInput, setBusyState, blueprint,
637
+ persona, settings.templateEnabled, settings.styleMode, updateLastMessage, setLastRun,
638
+ pushDebugEvent,
639
+ ]);
640
+
641
+ const onGrade = useCallback(async () => {
642
+ if (!lastFictionText || grading) return;
643
+ const current = MODELS.find((m) => m.id === model);
644
+ if (!current) return;
645
+ const apiKey = keys[current.provider as keyof typeof keys] as string;
646
+ if (!apiKey) return;
647
+ setGrading(true);
648
+ try {
649
+ const report = await gradeManuscript(lastFictionText, blueprint, (userMessage) =>
650
+ generateSynthesizedResponse({
651
+ provider: current.provider,
652
+ model,
653
+ apiKey,
654
+ userMessage,
655
+ retrievedWebData: [],
656
+ conversationHistory: [],
657
+ }),
658
+ );
659
+ setGradeReport(report);
660
+ } catch (err) {
661
+ // Downgrade rather than fabricate: show computed metrics only, mark judge failed.
662
+ setGradeReport({
663
+ metrics: computeProseMetrics(lastFictionText),
664
+ rubric: null,
665
+ justifications: null,
666
+ rewriteTargets: [],
667
+ rawJudgeText: "",
668
+ parseError: err instanceof Error ? err.message : String(err),
669
+ });
670
+ } finally {
671
+ setGrading(false);
672
+ }
673
+ }, [lastFictionText, grading, model, keys, blueprint]);
674
+
675
+ const onSubmitCapture = useCallback((e: FormEvent) => {
676
+ if (!fictionMode) return;
677
+ e.preventDefault();
678
+ e.stopPropagation();
679
+ void runFiction();
680
+ }, [fictionMode, runFiction]);
681
+
682
+ const onKeyDownCapture = useCallback((e: KeyboardEvent) => {
683
+ if (!fictionMode || e.key !== "Enter" || (!e.ctrlKey && !e.metaKey)) return;
684
+ if ((e.target as HTMLElement).tagName !== "TEXTAREA") return;
685
+ e.preventDefault();
686
+ e.stopPropagation();
687
+ void runFiction();
688
+ }, [fictionMode, runFiction]);
689
+
690
+ useEffect(() => {
691
+ if (onSubmitFromShared && onSubmitFromShared > 0 && fictionMode) void runFiction();
692
+ }, [onSubmitFromShared, fictionMode, runFiction]);
693
+
694
+ return (
695
+ <div onSubmitCapture={onSubmitCapture} onKeyDownCapture={onKeyDownCapture}>
696
+ <PackageChatApp onSubmitFromShared={fictionMode ? 0 : onSubmitFromShared} />
697
+ {toggleHost && createPortal(<ModeToggle fictionMode={fictionMode} setFictionMode={setFictionMode} />, toggleHost)}
698
+ {styleHost && createPortal(<MainStyleOverrideControl />, styleHost)}
699
+ {fictionMode && sageControlsHost && createPortal(
700
+ <SageMainControls
701
+ bp={blueprint}
702
+ scaleId={scaleId}
703
+ setScaleId={setScaleId}
704
+ chapters={chapters}
705
+ setChapters={setChapters}
706
+ wordsPerChapter={wordsPerChapter}
707
+ setWordsPerChapter={setWordsPerChapter}
708
+ macroId={macroId}
709
+ setMacroId={setMacroId}
710
+ onRerollBlueprint={rerollBlueprint}
711
+ onRerollCadence={rerollCadence}
712
+ busy={busyState !== null}
713
+ gradeReport={gradeReport}
714
+ grading={grading}
715
+ onGrade={onGrade}
716
+ canGrade={!!lastFictionText}
717
+ />,
718
+ sageControlsHost,
719
+ )}
720
+ </div>
721
+ );
722
+ }