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,380 @@
1
+ /**
2
+ * REALITY GROUNDING SYSTEM — abstract ⇄ documentary-real control axis.
3
+ * ---------------------------------------------------------------------------
4
+ * Type C net-new. A single user-selected integer 0..6 that STRICTLY constrains
5
+ * what the fiction engine may put on the page. This is not a tone hint: each
6
+ * level emits hard PERMIT / FORBID clauses, a causality contract, a sensory
7
+ * specificity floor, and a verification register that are injected directly
8
+ * into the per-chapter prompt.
9
+ *
10
+ * RUNTIME HONESTY
11
+ * - Pure, deterministic, offline. No LLM, no network, no storage side effects.
12
+ * - The engine cannot *force* an LLM to obey; it can only (a) state the
13
+ * constraint unambiguously, (b) forbid the specific failure modes by name,
14
+ * and (c) run a real local post-hoc audit that flags violations. Both the
15
+ * directive and the audit are implemented here. The audit is lexical, so it
16
+ * detects surface violations, not every semantic one — declared, not hidden.
17
+ */
18
+
19
+ export type RealityLevelId = 0 | 1 | 2 | 3 | 4 | 5 | 6;
20
+
21
+ export type RealityLevel = {
22
+ id: RealityLevelId;
23
+ key: string;
24
+ label: string;
25
+ short: string;
26
+ /** one-line description shown next to the slider */
27
+ blurb: string;
28
+ /** what the prose MAY contain */
29
+ permit: string[];
30
+ /** what the prose MUST NOT contain — enforced by name */
31
+ forbid: string[];
32
+ /** how cause→effect must behave */
33
+ causality: string;
34
+ /** concrete-detail floor per scene */
35
+ sensoryFloor: string;
36
+ /** how claims inside the fiction are evidenced */
37
+ register: string;
38
+ /** metaphor budget: max simile/metaphor per 100 words (0 = none) */
39
+ figurativePer100w: number;
40
+ /** how much unexplained phenomena is allowed, 0..1 */
41
+ unexplainedTolerance: number;
42
+ /** required proper-noun / measurement density per 1000 words */
43
+ specificityPer1000w: number;
44
+ accent: string;
45
+ };
46
+
47
+ export const REALITY_LEVELS: readonly RealityLevel[] = [
48
+ {
49
+ id: 0,
50
+ key: "MYTHIC_ABSTRACT",
51
+ label: "Mythic / Abstract",
52
+ short: "Mythic",
53
+ blurb: "Archetypal. Physics is symbolic. Figures may be forces rather than people.",
54
+ permit: [
55
+ "impossible geography, symbolic physics, talking elements, unnamed archetypal figures",
56
+ "time that folds, repeats, or runs at emblematic speed",
57
+ "events whose meaning outranks their mechanism",
58
+ ],
59
+ forbid: [
60
+ "modern procedural detail that anchors the tale to one dated bureaucracy",
61
+ "clinical brand names, model numbers, or statistics presented as fact",
62
+ ],
63
+ causality: "Cause may be moral or symbolic rather than physical; consequence must still follow inevitably from the transgression or the vow.",
64
+ sensoryFloor: "At least one concrete physical image per scene so the abstraction has a body.",
65
+ register: "Assertion by narration. No sourcing, no documentation.",
66
+ figurativePer100w: 6,
67
+ unexplainedTolerance: 1.0,
68
+ specificityPer1000w: 2,
69
+ accent: "violet",
70
+ },
71
+ {
72
+ id: 1,
73
+ key: "SURREAL",
74
+ label: "Surreal",
75
+ short: "Surreal",
76
+ blurb: "Dream logic. Transformations need no explanation, but they must recur consistently.",
77
+ permit: [
78
+ "dream logic, uncanny transformation, non-sequitur adjacency",
79
+ "objects and rooms behaving as psychological states",
80
+ ],
81
+ forbid: [
82
+ "explaining the surreal element with a rational cause (a dream frame, a drug, a hoax)",
83
+ "treating the impossible as a puzzle to be solved",
84
+ ],
85
+ causality: "Local causality may break, but any broken rule must break the SAME way each time it appears.",
86
+ sensoryFloor: "Two concrete, mundane sensory details per scene to anchor the strangeness.",
87
+ register: "Assertion by narration. Contradiction is permitted if it is deliberate and recurring.",
88
+ figurativePer100w: 5,
89
+ unexplainedTolerance: 0.9,
90
+ specificityPer1000w: 3,
91
+ accent: "fuchsia",
92
+ },
93
+ {
94
+ id: 2,
95
+ key: "FABULIST",
96
+ label: "Fabulist",
97
+ short: "Fabulist",
98
+ blurb: "One clean impossible premise, worked out with rigorous internal logic.",
99
+ permit: [
100
+ "exactly ONE openly impossible premise, stated plainly and early",
101
+ "institutions and ordinary people reacting to that premise realistically",
102
+ ],
103
+ forbid: [
104
+ "a second unrelated impossibility",
105
+ "the impossible premise solving a problem it was not established to solve",
106
+ ],
107
+ causality: "One rule is suspended. Everything downstream of it obeys ordinary cause and effect, including cost and paperwork.",
108
+ sensoryFloor: "Ordinary-life texture in every scene: labour, money, food, weather, fatigue.",
109
+ register: "Assertion by narration for the premise; ordinary plausibility for everything else.",
110
+ figurativePer100w: 4,
111
+ unexplainedTolerance: 0.55,
112
+ specificityPer1000w: 6,
113
+ accent: "indigo",
114
+ },
115
+ {
116
+ id: 3,
117
+ key: "MAGIC_REALIST",
118
+ label: "Magic Realist",
119
+ short: "Magic-Real",
120
+ blurb: "The impossible is real, unremarked, and socially absorbed. No wonder, no explanation.",
121
+ permit: [
122
+ "the impossible treated as unremarkable by the characters",
123
+ "the marvellous embedded in domestic, civic and economic routine",
124
+ ],
125
+ forbid: [
126
+ "characters reacting with astonishment or investigating the marvellous",
127
+ "systematised 'magic rules' or a magic-system exposition",
128
+ ],
129
+ causality: "The marvellous has social and economic consequences even though nobody explains it. Consequence is mandatory; explanation is forbidden.",
130
+ sensoryFloor: "Every marvellous beat is paired with a mundane, verifiable detail in the same paragraph.",
131
+ register: "Flat declarative. The impossible receives the same sentence temperature as the weather.",
132
+ figurativePer100w: 3.5,
133
+ unexplainedTolerance: 0.45,
134
+ specificityPer1000w: 8,
135
+ accent: "sky",
136
+ },
137
+ {
138
+ id: 4,
139
+ key: "HEIGHTENED_REALIST",
140
+ label: "Heightened Realist",
141
+ short: "Heightened",
142
+ blurb: "Everything is physically possible, but coincidence and intensity run hot.",
143
+ permit: [
144
+ "improbable but physically possible events; charged coincidence; dramatic compression of time",
145
+ "heightened, stylised dialogue",
146
+ ],
147
+ forbid: [
148
+ "any event that violates physics, biology, or established law",
149
+ "supernatural agency of any kind, including ambiguous hints of it",
150
+ ],
151
+ causality: "Strict physical causality. Coincidence is allowed to start a problem but never to solve one.",
152
+ sensoryFloor: "Named places, specific times, working objects, and at least one concrete cost per scene.",
153
+ register: "Realist. Any factual claim a character makes must be something a person in that role could plausibly know.",
154
+ figurativePer100w: 3,
155
+ unexplainedTolerance: 0.15,
156
+ specificityPer1000w: 12,
157
+ accent: "teal",
158
+ },
159
+ {
160
+ id: 5,
161
+ key: "REALIST",
162
+ label: "Realist",
163
+ short: "Realist",
164
+ blurb: "Ordinary probability. Boring things take the time they actually take.",
165
+ permit: [
166
+ "only events with everyday probability; institutional friction, delay, and paperwork",
167
+ "unresolved and undramatic outcomes",
168
+ ],
169
+ forbid: [
170
+ "supernatural or impossible events",
171
+ "convenient coincidence at any turning point",
172
+ "expertise a character has no route to; instant mastery; money or transport appearing without account",
173
+ ],
174
+ causality: "Strict physical, legal, and economic causality. Every capability is paid for in time, money, training, or relationship.",
175
+ sensoryFloor: "Named specifics in every scene: place, hour, tool, quantity, price, or procedure.",
176
+ register: "Realist. Distances, durations, prices, and procedures must be internally consistent and plausible.",
177
+ figurativePer100w: 2,
178
+ unexplainedTolerance: 0.05,
179
+ specificityPer1000w: 18,
180
+ accent: "emerald",
181
+ },
182
+ {
183
+ id: 6,
184
+ key: "DOCUMENTARY",
185
+ label: "Documentary Real",
186
+ short: "Documentary",
187
+ blurb: "Procedural, evidence-forward, unornamented. Reads like reported fact.",
188
+ permit: [
189
+ "verifiable-sounding procedure, timestamps, measurements, forms, transcripts, and chains of custody",
190
+ "flat reportorial narration; direct quotation; source attribution inside the fiction",
191
+ ],
192
+ forbid: [
193
+ "any impossible or supernatural event",
194
+ "coincidence at a turning point",
195
+ "omniscient access to a mind the narrator could not have interviewed",
196
+ "lyrical or ornamental figuration; metaphor used for decoration",
197
+ "round, unsourced statistics presented as authoritative",
198
+ ],
199
+ causality: "Documented causality only. If a fact appears, the text shows how the narrator could know it — a document, a witness, an observation, or an admission.",
200
+ sensoryFloor: "Every scene carries a date or clock time, a named location, and at least one measured quantity with units.",
201
+ register: "Reportorial. Attribute inside the fiction ('the log recorded', 'she said', 'the invoice showed'). Prefer the specific to the general in every sentence.",
202
+ figurativePer100w: 0.5,
203
+ unexplainedTolerance: 0,
204
+ specificityPer1000w: 28,
205
+ accent: "zinc",
206
+ },
207
+ ] as const;
208
+
209
+ export function realityLevel(id: number): RealityLevel {
210
+ const clamped = Math.max(0, Math.min(6, Math.round(id))) as RealityLevelId;
211
+ return REALITY_LEVELS[clamped];
212
+ }
213
+
214
+ /**
215
+ * The hard directive injected into every chapter prompt. Deliberately
216
+ * imperative and specific: named forbidden failure modes beat vague tone words.
217
+ */
218
+ export function buildRealityDirective(id: number, opts?: { chapterWords?: number }): string {
219
+ const L = realityLevel(id);
220
+ const words = Math.max(300, opts?.chapterWords ?? 1200);
221
+ const figBudget = Math.max(0, Math.round((L.figurativePer100w * words) / 100));
222
+ const specBudget = Math.max(1, Math.round((L.specificityPer1000w * words) / 1000));
223
+
224
+ const lines = [
225
+ `REALITY GROUNDING — LEVEL ${L.id}/6: ${L.label.toUpperCase()}. This is a HARD CONSTRAINT and outranks genre, tone, and style. If any other instruction conflicts with it, obey this one.`,
226
+ `Stance: ${L.blurb}`,
227
+ `Causality contract: ${L.causality}`,
228
+ `Concrete-detail floor: ${L.sensoryFloor}`,
229
+ `Evidence register: ${L.register}`,
230
+ "PERMITTED in this chapter:",
231
+ ...L.permit.map((p) => ` - ${p}`),
232
+ "FORBIDDEN in this chapter — do not write these under any circumstance:",
233
+ ...L.forbid.map((p) => ` - ${p}`),
234
+ `Figurative budget: at most ${figBudget} simile(s)/metaphor(s) in this chapter (~${L.figurativePer100w} per 100 words). Prefer literal description.`,
235
+ `Specificity floor: at least ${specBudget} concrete specifics (named place, clock time, date, measured quantity with units, price, procedure, or proper noun) in this chapter.`,
236
+ ];
237
+
238
+ if (L.id >= 4) {
239
+ lines.push(
240
+ "Physical-plausibility check before you write each scene: could this happen to a real person, in a real body, under real law and real economics? If not, rewrite the beat until it could.",
241
+ "No character may acquire a skill, a document, money, or access without the text showing the cost and the route.",
242
+ );
243
+ }
244
+ if (L.id === 6) {
245
+ lines.push(
246
+ "Every scene must open with a locatable anchor: a date or clock time AND a named place.",
247
+ "Attribute every non-observed fact inside the fiction (a document, a witness, a recording, an admission). If it cannot be attributed, cut it.",
248
+ "Write plainly. No lyric flourish. Short declaratives. Numbers carry units.",
249
+ );
250
+ }
251
+ if (L.id <= 1) {
252
+ lines.push(
253
+ "Do not rationalise the impossible. No dream frame, no hallucination reveal, no hoax explanation.",
254
+ );
255
+ }
256
+ return lines.join("\n");
257
+ }
258
+
259
+ // ── Local post-hoc audit (real computation, lexical scope declared) ─────────
260
+
261
+ export type RealityFinding = {
262
+ rule: string;
263
+ severity: "pass" | "warn" | "fail";
264
+ detail: string;
265
+ };
266
+ export type RealityAuditReport = {
267
+ level: RealityLevel;
268
+ findings: RealityFinding[];
269
+ score: number; // 0..1 conformance
270
+ passCount: number;
271
+ warnCount: number;
272
+ failCount: number;
273
+ method: string;
274
+ };
275
+
276
+ const FIGURATIVE_RE = /\b(like a|like the|as if|as though|as a|resembl\w+|reminded \w+ of)\b/gi;
277
+ const IMPOSSIBLE_RE = /\b(magic|spell|wizard|sorcer\w+|dragon|ghost|spectre|specter|demon|angel|telepath\w+|teleport\w+|levitat\w+|resurrect\w+|immortal|prophec\w+|enchant\w+|curse[ds]?)\b/gi;
278
+ const HEDGE_RE = /\b(somehow|inexplicably|for some reason|mysteriously|without explanation)\b/gi;
279
+ const TIME_ANCHOR_RE = /\b(\d{1,2}[:.]\d{2}\s*(?:am|pm)?|\d{1,2}\s*(?:am|pm)\b|monday|tuesday|wednesday|thursday|friday|saturday|sunday|january|february|march|april|may|june|july|august|september|october|november|december|\b(?:19|20)\d{2}\b)/gi;
280
+ const MEASURE_RE = /\b\d+(?:[.,]\d+)?\s?(?:%|kg|km|cm|mm|m\b|g\b|lb|lbs|oz|mi\b|ft\b|in\b|l\b|ml|hours?|minutes?|seconds?|days?|weeks?|months?|years?|dollars?|euros?|pounds?|usd|eur|gbp|£|\$|€)/gi;
281
+ const PROPER_RE = /(?<![.!?]\s)(?<!^)\b[A-Z][a-z]{2,}\b/gm;
282
+
283
+ function countMatches(text: string, re: RegExp): number {
284
+ const m = text.match(re);
285
+ return m ? m.length : 0;
286
+ }
287
+
288
+ /**
289
+ * Runs the level's checkable constraints over finished prose. Lexical only:
290
+ * it reliably detects surface violations (named impossible nouns, figurative
291
+ * density, missing anchors) and cannot detect every semantic violation. That
292
+ * limitation is reported in `method` rather than hidden.
293
+ */
294
+ export function auditRealityGrounding(text: string, id: number): RealityAuditReport {
295
+ const L = realityLevel(id);
296
+ const words = (text.trim().match(/\S+/g) || []).length;
297
+ const findings: RealityFinding[] = [];
298
+ const per100 = (n: number) => (words ? (n * 100) / words : 0);
299
+ const per1000 = (n: number) => (words ? (n * 1000) / words : 0);
300
+
301
+ // 1. figurative density
302
+ const fig = countMatches(text, FIGURATIVE_RE);
303
+ const figRate = per100(fig);
304
+ findings.push({
305
+ rule: "RG-FIG figurative budget",
306
+ severity: figRate <= L.figurativePer100w ? "pass" : figRate <= L.figurativePer100w * 1.6 ? "warn" : "fail",
307
+ detail: `${fig} figurative markers = ${figRate.toFixed(2)}/100w (budget ${L.figurativePer100w}/100w)`,
308
+ });
309
+
310
+ // 2. impossible vocabulary vs tolerance
311
+ const imp = countMatches(text, IMPOSSIBLE_RE);
312
+ const impAllowed = L.unexplainedTolerance > 0.3;
313
+ findings.push({
314
+ rule: "RG-IMP impossible-phenomena vocabulary",
315
+ severity: impAllowed ? "pass" : imp === 0 ? "pass" : imp <= 1 ? "warn" : "fail",
316
+ detail: impAllowed
317
+ ? `${imp} marker(s) — permitted at level ${L.id}`
318
+ : `${imp} marker(s) found; level ${L.id} forbids supernatural phenomena`,
319
+ });
320
+
321
+ // 3. unexplained hedges
322
+ const hedge = countMatches(text, HEDGE_RE);
323
+ findings.push({
324
+ rule: "RG-HEDGE unexplained hedging",
325
+ severity: hedge === 0 ? "pass" : L.unexplainedTolerance >= 0.45 ? "pass" : hedge <= 1 ? "warn" : "fail",
326
+ detail: `${hedge} hedge phrase(s) ("somehow", "inexplicably", …); tolerance ${L.unexplainedTolerance.toFixed(2)}`,
327
+ });
328
+
329
+ // 4. specificity floor
330
+ const specifics = countMatches(text, TIME_ANCHOR_RE) + countMatches(text, MEASURE_RE) + Math.min(countMatches(text, PROPER_RE), 40);
331
+ const specRate = per1000(specifics);
332
+ findings.push({
333
+ rule: "RG-SPEC specificity floor",
334
+ severity: specRate >= L.specificityPer1000w ? "pass" : specRate >= L.specificityPer1000w * 0.6 ? "warn" : "fail",
335
+ detail: `${specifics} specifics = ${specRate.toFixed(1)}/1000w (floor ${L.specificityPer1000w}/1000w)`,
336
+ });
337
+
338
+ // 5. documentary anchors
339
+ if (L.id === 6) {
340
+ const anchors = countMatches(text, TIME_ANCHOR_RE);
341
+ const measures = countMatches(text, MEASURE_RE);
342
+ findings.push({
343
+ rule: "RG-DOC time anchors",
344
+ severity: anchors >= 1 ? "pass" : "fail",
345
+ detail: `${anchors} date/clock anchor(s); documentary level requires at least one per scene`,
346
+ });
347
+ findings.push({
348
+ rule: "RG-DOC measured quantities",
349
+ severity: measures >= 1 ? "pass" : "fail",
350
+ detail: `${measures} measured quantit(ies) with units`,
351
+ });
352
+ }
353
+
354
+ const passCount = findings.filter((f) => f.severity === "pass").length;
355
+ const warnCount = findings.filter((f) => f.severity === "warn").length;
356
+ const failCount = findings.filter((f) => f.severity === "fail").length;
357
+ const score = findings.length ? (passCount + 0.5 * warnCount) / findings.length : 0;
358
+
359
+ return {
360
+ level: L, findings, score, passCount, warnCount, failCount,
361
+ method:
362
+ "Lexical audit over finished prose (regex families: figurative markers, supernatural nouns, hedges, " +
363
+ "time anchors, measured quantities, proper nouns). Detects surface violations; does NOT detect every " +
364
+ "semantic violation (e.g. an implausible causal chain written in plain words). No LLM was used.",
365
+ };
366
+ }
367
+
368
+ export const REALITY_STORAGE_KEY = "veritas.sage.realityLevel.v1";
369
+
370
+ export function loadRealityLevel(fallback = 4): RealityLevelId {
371
+ try {
372
+ const raw = localStorage.getItem(REALITY_STORAGE_KEY);
373
+ if (raw == null) return fallback as RealityLevelId;
374
+ const n = Number(raw);
375
+ return (Number.isFinite(n) ? Math.max(0, Math.min(6, Math.round(n))) : fallback) as RealityLevelId;
376
+ } catch { return fallback as RealityLevelId; }
377
+ }
378
+ export function saveRealityLevel(id: number): void {
379
+ try { localStorage.setItem(REALITY_STORAGE_KEY, String(Math.max(0, Math.min(6, Math.round(id))))); } catch { /* optional */ }
380
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * SAGE repetition / context-poisoning detector.
3
+ * ---------------------------------------------------------------------------
4
+ * Real, deterministic, locally-computed detector (no LLM call) for the
5
+ * degenerate-repetition failure mode reported on weaker frontier models
6
+ * (e.g. gemini-3.1-flash-lite): a single word, sentence, or paragraph
7
+ * repeating throughout later chapters. This is the classic small-model
8
+ * self-reinforcing-loop failure, distinct from ordinary stylistic motif
9
+ * recurrence.
10
+ *
11
+ * Runs after each chapter is generated. If flagged, ChatApp.tsx performs
12
+ * exactly ONE regeneration for that chapter using a fresh, compact prompt
13
+ * (never re-feeding the poisoned draft back into context).
14
+ */
15
+
16
+ export type RepetitionReport = {
17
+ flagged: boolean;
18
+ reasons: string[];
19
+ evidence: string[];
20
+ };
21
+
22
+ function splitSentences(text: string): string[] {
23
+ return text
24
+ .replace(/\s+/g, " ")
25
+ .split(/(?<=[.!?])\s+/)
26
+ .map((s) => s.trim())
27
+ .filter((s) => s.length > 0);
28
+ }
29
+
30
+ function splitParagraphs(text: string): string[] {
31
+ return text
32
+ .split(/\n{2,}/)
33
+ .map((p) => p.trim())
34
+ .filter((p) => p.length > 0);
35
+ }
36
+
37
+ function normalize(s: string): string {
38
+ return s.toLowerCase().replace(/[^a-z0-9' ]/g, "").replace(/\s+/g, " ").trim();
39
+ }
40
+
41
+ /** Detects exact-duplicate sentences (>=3x) or paragraphs (>=2x). */
42
+ function detectExactDuplicates(text: string): { sentenceHit?: string; paragraphHit?: string } {
43
+ const sentences = splitSentences(text);
44
+ const sCounts = new Map<string, number>();
45
+ for (const s of sentences) {
46
+ if (s.length < 15) continue;
47
+ const key = normalize(s);
48
+ sCounts.set(key, (sCounts.get(key) ?? 0) + 1);
49
+ }
50
+ const sentenceHit = Array.from(sCounts.entries()).find(([, n]) => n >= 3)?.[0];
51
+
52
+ const paragraphs = splitParagraphs(text);
53
+ const pCounts = new Map<string, number>();
54
+ for (const p of paragraphs) {
55
+ if (p.length < 40) continue;
56
+ const key = normalize(p);
57
+ pCounts.set(key, (pCounts.get(key) ?? 0) + 1);
58
+ }
59
+ const paragraphHit = Array.from(pCounts.entries()).find(([, n]) => n >= 2)?.[0];
60
+
61
+ return { sentenceHit, paragraphHit };
62
+ }
63
+
64
+ /** Detects a single word dominating the text (word-level loop). */
65
+ function detectWordLoop(text: string): { word: string; ratio: number } | null {
66
+ const words = normalize(text).split(" ").filter((w) => w.length >= 4);
67
+ if (words.length < 60) return null;
68
+ const counts = new Map<string, number>();
69
+ for (const w of words) counts.set(w, (counts.get(w) ?? 0) + 1);
70
+ let topWord = "";
71
+ let topCount = 0;
72
+ for (const [w, c] of counts) {
73
+ if (c > topCount) {
74
+ topCount = c;
75
+ topWord = w;
76
+ }
77
+ }
78
+ const ratio = topCount / words.length;
79
+ return ratio > 0.12 ? { word: topWord, ratio } : null;
80
+ }
81
+
82
+ /** Detects a repeated 4-gram phrase loop (classic degenerate-repetition signature). */
83
+ function detectPhraseLoop(text: string): string | null {
84
+ const words = normalize(text).split(" ").filter(Boolean);
85
+ if (words.length < 20) return null;
86
+ const shingles = new Map<string, number>();
87
+ for (let i = 0; i + 4 <= words.length; i += 1) {
88
+ const gram = words.slice(i, i + 4).join(" ");
89
+ shingles.set(gram, (shingles.get(gram) ?? 0) + 1);
90
+ }
91
+ const hit = Array.from(shingles.entries()).find(([, n]) => n >= 4);
92
+ return hit ? hit[0] : null;
93
+ }
94
+
95
+ export function detectDegenerateRepetition(text: string): RepetitionReport {
96
+ const reasons: string[] = [];
97
+ const evidence: string[] = [];
98
+
99
+ const { sentenceHit, paragraphHit } = detectExactDuplicates(text);
100
+ if (sentenceHit) {
101
+ reasons.push("exact sentence repeated 3+ times");
102
+ evidence.push(sentenceHit.slice(0, 140));
103
+ }
104
+ if (paragraphHit) {
105
+ reasons.push("exact paragraph repeated 2+ times");
106
+ evidence.push(paragraphHit.slice(0, 200));
107
+ }
108
+ const wordLoop = detectWordLoop(text);
109
+ if (wordLoop) {
110
+ reasons.push(`single word "${wordLoop.word}" dominates (${(wordLoop.ratio * 100).toFixed(1)}% of tokens)`);
111
+ evidence.push(wordLoop.word);
112
+ }
113
+ const phraseLoop = detectPhraseLoop(text);
114
+ if (phraseLoop) {
115
+ reasons.push("4-word phrase loop detected 4+ times");
116
+ evidence.push(phraseLoop);
117
+ }
118
+
119
+ return { flagged: reasons.length > 0, reasons, evidence };
120
+ }
121
+
122
+ /**
123
+ * Self-test hook — exposed on window in dev builds so the fix can be
124
+ * hand-traced against a synthetic context-poisoning sample without needing
125
+ * a live model call. Not executed automatically; call
126
+ * `window.__sageRepetitionSelfTest()` in the browser console.
127
+ */
128
+ export function sageRepetitionSelfTest(): { pass: boolean; cases: { name: string; pass: boolean }[] } {
129
+ const cases: { name: string; pass: boolean }[] = [];
130
+
131
+ const poisoned = "The bell tolled again. The bell tolled again. The bell tolled again. Nothing else happened in the square that day.";
132
+ cases.push({ name: "exact sentence loop (3x)", pass: detectDegenerateRepetition(poisoned).flagged });
133
+
134
+ const wordLoopSample = Array(80).fill("shadow").join(" ") + " walked into the room and sat down quietly near the window.";
135
+ cases.push({ name: "single-word loop", pass: detectDegenerateRepetition(wordLoopSample).flagged });
136
+
137
+ const phraseLoopSample = Array(6).fill("in the quiet dark of").join(" the corridor ") + " she waited for morning.";
138
+ cases.push({ name: "phrase loop", pass: detectDegenerateRepetition(phraseLoopSample).flagged });
139
+
140
+ const clean =
141
+ "Mira crossed the bridge before dawn. The river below carried last night's rain toward the mill. " +
142
+ "She had promised her brother she would not look back, and she kept that promise exactly once. " +
143
+ "By the time the sun cleared the ridgeline, the town behind her was only smoke and roofline.";
144
+ cases.push({ name: "clean prose (no false positive)", pass: !detectDegenerateRepetition(clean).flagged });
145
+
146
+ return { pass: cases.every((c) => c.pass), cases };
147
+ }
148
+
149
+ if (typeof window !== "undefined") {
150
+ (window as unknown as { __sageRepetitionSelfTest?: typeof sageRepetitionSelfTest }).__sageRepetitionSelfTest = sageRepetitionSelfTest;
151
+ }