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,233 @@
1
+ // Jina AI connectors with PrismaFetch as PRIMARY, OG browser scraper as SECONDARY, and Jina as BACKUP.
2
+ // Token-conservation policy: Jina is only invoked when both local layers are unavailable or empty.
3
+
4
+ import {
5
+ prismaFetchRead,
6
+ prismaFetchSearch,
7
+ resolvePrismaFetchAvailability,
8
+ } from "./prismafetch";
9
+ import { browserScraperRead, browserScraperSearch } from "../browser-search-scraper";
10
+
11
+ export interface JinaEmbedOptions {
12
+ apiKey: string;
13
+ model?: string;
14
+ task?: "retrieval.query" | "retrieval.passage" | "classification" | "separation" | "text-matching";
15
+ signal?: AbortSignal;
16
+ }
17
+
18
+ function tokenize(text: string): string[] {
19
+ return text.toLowerCase().split(/[^a-z0-9]+/i).filter(Boolean);
20
+ }
21
+
22
+ function localHashEmbedding(text: string, dims = 256): number[] {
23
+ const vec = new Array<number>(dims).fill(0);
24
+ for (const token of tokenize(text)) {
25
+ let h = 2166136261;
26
+ for (let i = 0; i < token.length; i++) {
27
+ h ^= token.charCodeAt(i);
28
+ h = Math.imul(h, 16777619);
29
+ }
30
+ vec[Math.abs(h) % dims] += 1;
31
+ }
32
+ const norm = Math.sqrt(vec.reduce((sum, x) => sum + x * x, 0)) || 1;
33
+ return vec.map((x) => x / norm);
34
+ }
35
+
36
+ export async function jinaEmbed(
37
+ texts: string[],
38
+ opts: JinaEmbedOptions,
39
+ ): Promise<{ vectors: number[][]; usage: { totalTokens: number } }> {
40
+ if (texts.length === 0) return { vectors: [], usage: { totalTokens: 0 } };
41
+
42
+ // PRIMARY: deterministic local hash embedding (zero tokens).
43
+ // We only consult Jina if explicitly forced via a non-empty apiKey AND task signals
44
+ // a model-quality requirement, which we approximate by `task === "classification"`
45
+ // or `task === "text-matching"`. Otherwise local is sufficient for retrieval.
46
+ const usesRemote = !!opts.apiKey?.trim() && (opts.task === "classification" || opts.task === "text-matching");
47
+ if (!usesRemote) {
48
+ const vectors = texts.map((text) => localHashEmbedding(text));
49
+ const totalTokens = texts.reduce((sum, text) => sum + Math.max(1, Math.round(text.length / 4)), 0);
50
+ return { vectors, usage: { totalTokens } };
51
+ }
52
+
53
+ const res = await fetch("https://api.jina.ai/v1/embeddings", {
54
+ method: "POST",
55
+ headers: {
56
+ Authorization: `Bearer ${opts.apiKey}`,
57
+ "Content-Type": "application/json",
58
+ },
59
+ body: JSON.stringify({
60
+ model: opts.model ?? "jina-embeddings-v3",
61
+ task: opts.task ?? "retrieval.passage",
62
+ input: texts,
63
+ }),
64
+ signal: opts.signal,
65
+ });
66
+ if (!res.ok) throw new Error(`Jina embeddings ${res.status}: ${(await res.text()).slice(0, 400)}`);
67
+ const data = (await res.json()) as {
68
+ data: { embedding: number[] }[];
69
+ usage?: { total_tokens?: number };
70
+ };
71
+ return {
72
+ vectors: data.data.map((d) => d.embedding),
73
+ usage: { totalTokens: data.usage?.total_tokens ?? 0 },
74
+ };
75
+ }
76
+
77
+ export interface JinaRerankOptions {
78
+ apiKey: string;
79
+ model?: string;
80
+ topN?: number;
81
+ signal?: AbortSignal;
82
+ }
83
+
84
+ export interface JinaRerankResult {
85
+ index: number;
86
+ relevanceScore: number;
87
+ document: string;
88
+ }
89
+
90
+ function lexicalScore(query: string, document: string): number {
91
+ const qTokens = tokenize(query);
92
+ const dTokens = tokenize(document);
93
+ if (qTokens.length === 0 || dTokens.length === 0) return 0;
94
+ const dSet = new Set(dTokens);
95
+ const overlap = qTokens.filter((t) => dSet.has(t)).length;
96
+ const density = overlap / qTokens.length;
97
+ const positionBoost = qTokens.slice(0, 4).filter((t) => dTokens.slice(0, 60).includes(t)).length * 0.05;
98
+ return density + positionBoost;
99
+ }
100
+
101
+ export async function jinaRerank(
102
+ query: string,
103
+ documents: string[],
104
+ opts: JinaRerankOptions,
105
+ ): Promise<JinaRerankResult[]> {
106
+ // PRIMARY: local lexical rerank (zero Jina tokens). Only call remote if `opts.model`
107
+ // explicitly requests a remote reranker AND a key is present.
108
+ const wantsRemote = !!opts.apiKey?.trim() && !!opts.model && opts.model.startsWith("jina-reranker-");
109
+ if (!wantsRemote) {
110
+ return documents
111
+ .map((document, index) => ({ index, relevanceScore: lexicalScore(query, document), document }))
112
+ .sort((a, b) => b.relevanceScore - a.relevanceScore)
113
+ .slice(0, opts.topN ?? documents.length);
114
+ }
115
+
116
+ const res = await fetch("https://api.jina.ai/v1/rerank", {
117
+ method: "POST",
118
+ headers: {
119
+ Authorization: `Bearer ${opts.apiKey}`,
120
+ "Content-Type": "application/json",
121
+ },
122
+ body: JSON.stringify({
123
+ model: opts.model,
124
+ query,
125
+ documents,
126
+ top_n: opts.topN ?? documents.length,
127
+ return_documents: true,
128
+ }),
129
+ signal: opts.signal,
130
+ });
131
+ if (!res.ok) throw new Error(`Jina rerank ${res.status}: ${(await res.text()).slice(0, 400)}`);
132
+ const data = (await res.json()) as {
133
+ results: { index: number; relevance_score: number; document?: { text: string } | string }[];
134
+ };
135
+ return data.results.map((r) => ({
136
+ index: r.index,
137
+ relevanceScore: r.relevance_score,
138
+ document: typeof r.document === "string" ? r.document : r.document?.text ?? documents[r.index],
139
+ }));
140
+ }
141
+
142
+ export interface JinaSearchOptions {
143
+ apiKey: string;
144
+ signal?: AbortSignal;
145
+ count?: number;
146
+ prismafetchUrl?: string;
147
+ }
148
+
149
+ export interface JinaSearchResult {
150
+ url: string;
151
+ title: string;
152
+ description: string;
153
+ content?: string;
154
+ }
155
+
156
+ /** PRIMARY: PrismaFetch. SECONDARY: OG browser scraper. BACKUP: s.jina.ai. */
157
+ export async function jinaSearch(query: string, opts: JinaSearchOptions): Promise<JinaSearchResult[]> {
158
+ const availability = await resolvePrismaFetchAvailability(opts.prismafetchUrl);
159
+ if (availability.ok) {
160
+ try {
161
+ const local = await prismaFetchSearch(query, {
162
+ baseUrl: availability.baseUrl,
163
+ count: opts.count,
164
+ signal: opts.signal,
165
+ });
166
+ if (local.results.length > 0) return local.results;
167
+ } catch {
168
+ // fall through to OG browser scraper
169
+ }
170
+ }
171
+
172
+ try {
173
+ const scraped = await browserScraperSearch(query, { count: opts.count, signal: opts.signal });
174
+ if (scraped.length > 0) return scraped;
175
+ } catch {
176
+ // fall through to Jina backup
177
+ }
178
+
179
+ if (!opts.apiKey?.trim()) {
180
+ return [];
181
+ }
182
+
183
+ const url = `https://s.jina.ai/${encodeURIComponent(query)}?num=${opts.count ?? 8}`;
184
+ const res = await fetch(url, {
185
+ headers: {
186
+ Authorization: `Bearer ${opts.apiKey}`,
187
+ Accept: "application/json",
188
+ "X-Retain-Images": "none",
189
+ },
190
+ signal: opts.signal,
191
+ });
192
+ if (!res.ok) throw new Error(`Jina search ${res.status}: ${(await res.text()).slice(0, 400)}`);
193
+ const data = (await res.json()) as { data?: { url: string; title: string; description: string; content?: string }[] };
194
+ const results = (data.data ?? []).slice(0, opts.count ?? 8);
195
+ return results.map((r) => ({
196
+ url: r.url,
197
+ title: r.title,
198
+ description: r.description,
199
+ content: r.content,
200
+ }));
201
+ }
202
+
203
+ /** PRIMARY: PrismaFetch read. SECONDARY: OG browser scraper read. BACKUP: r.jina.ai. */
204
+ export async function jinaReadUrl(url: string, opts: { apiKey: string; signal?: AbortSignal; prismafetchUrl?: string }): Promise<string> {
205
+ const availability = await resolvePrismaFetchAvailability(opts.prismafetchUrl);
206
+ if (availability.ok) {
207
+ try {
208
+ const local = await prismaFetchRead(url, { baseUrl: availability.baseUrl, signal: opts.signal });
209
+ if (local.markdown && local.markdown.trim().length > 0) return local.markdown;
210
+ } catch {
211
+ // fall through to OG browser scraper
212
+ }
213
+ }
214
+
215
+ try {
216
+ const scraped = await browserScraperRead(url, { signal: opts.signal });
217
+ if (scraped.trim().length > 0) return scraped;
218
+ } catch {
219
+ // fall through to Jina Reader
220
+ }
221
+
222
+ if (!opts.apiKey?.trim()) {
223
+ return "";
224
+ }
225
+
226
+ const fetchUrl = `https://r.jina.ai/${url}`;
227
+ const res = await fetch(fetchUrl, {
228
+ headers: { Authorization: `Bearer ${opts.apiKey}`, Accept: "text/plain" },
229
+ signal: opts.signal,
230
+ });
231
+ if (!res.ok) throw new Error(`Jina reader ${res.status}`);
232
+ return await res.text();
233
+ }
@@ -0,0 +1,51 @@
1
+ import type { StockArtifact } from "../artifacts";
2
+
3
+ /**
4
+ * Browser-side Alpha Vantage resolver.
5
+ * Returns null on any failure; callers must treat missing numbers as unknown.
6
+ */
7
+ export async function alphaVantageStockResolver(ticker: string, apiKey: string): Promise<Partial<StockArtifact> | null> {
8
+ if (!apiKey) return null;
9
+ const base = "https://www.alphavantage.co/query";
10
+ const quoteUrl = `${base}?function=GLOBAL_QUOTE&symbol=${encodeURIComponent(ticker)}&apikey=${encodeURIComponent(apiKey)}`;
11
+ const overviewUrl = `${base}?function=OVERVIEW&symbol=${encodeURIComponent(ticker)}&apikey=${encodeURIComponent(apiKey)}`;
12
+ try {
13
+ const [quoteRes, overviewRes] = await Promise.all([fetch(quoteUrl), fetch(overviewUrl)]);
14
+ if (!quoteRes.ok || !overviewRes.ok) return null;
15
+ const quote = await quoteRes.json() as Record<string, any>;
16
+ const overview = await overviewRes.json() as Record<string, any>;
17
+ const q = quote["Global Quote"] ?? {};
18
+ const priceRaw = q["05. price"];
19
+ const price = priceRaw ? Number(priceRaw) : null;
20
+ const marketCapRaw = overview["MarketCapitalization"];
21
+ const marketCap = marketCapRaw ? compactMarketCap(Number(marketCapRaw)) : "";
22
+ const rating = ratingFromAnalystTarget(price, Number(overview["AnalystTargetPrice"] || 0));
23
+ return {
24
+ price: Number.isFinite(price) ? price : null,
25
+ mktCap: marketCap,
26
+ analystRating: rating,
27
+ asOf: new Date().toISOString(),
28
+ source: "alphavantage",
29
+ };
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ function compactMarketCap(n: number): string {
36
+ if (!Number.isFinite(n) || n <= 0) return "";
37
+ if (n >= 1e12) return `${(n / 1e12).toFixed(2)}T`;
38
+ if (n >= 1e9) return `${(n / 1e9).toFixed(2)}B`;
39
+ if (n >= 1e6) return `${(n / 1e6).toFixed(2)}M`;
40
+ return String(Math.round(n));
41
+ }
42
+
43
+ function ratingFromAnalystTarget(price: number | null, target: number): StockArtifact["analystRating"] {
44
+ if (!price || !target || !Number.isFinite(price) || !Number.isFinite(target)) return "N/A";
45
+ const upside = (target - price) / price;
46
+ if (upside >= 0.25) return "Strong Buy";
47
+ if (upside >= 0.10) return "Buy";
48
+ if (upside > -0.10) return "Hold";
49
+ if (upside > -0.25) return "Sell";
50
+ return "Strong Sell";
51
+ }
@@ -0,0 +1,175 @@
1
+ export type GroundingBackend = "jina" | "prismafetch-local" | "browser-scraper";
2
+
3
+ export interface PrismaFetchRuntimeSettings {
4
+ prismafetchEnabled: boolean;
5
+ prismafetchAutoFallback: boolean;
6
+ prismafetchUrl: string;
7
+ }
8
+
9
+ export interface PrismaFetchSearchResult {
10
+ title: string;
11
+ url: string;
12
+ description: string;
13
+ content: string;
14
+ }
15
+
16
+ export interface PrismaFetchReadResult {
17
+ url: string;
18
+ title: string;
19
+ markdown: string;
20
+ tier?: string;
21
+ links?: string[];
22
+ totalTokens?: number;
23
+ }
24
+
25
+ export interface PrismaFetchSearchResponse {
26
+ query: string;
27
+ backend: string;
28
+ results: PrismaFetchSearchResult[];
29
+ }
30
+
31
+ // ─── NEW: PRISMAFETCH READ MODES + OCR ───────────────────────────────
32
+ export type PrismaFetchReadMode =
33
+ | "auto"
34
+ | "fast_static"
35
+ | "render_headless"
36
+ | "visual_only";
37
+
38
+ export const PRISMAFETCH_READ_MODES: PrismaFetchReadMode[] = [
39
+ "auto",
40
+ "fast_static",
41
+ "render_headless",
42
+ "visual_only",
43
+ ];
44
+
45
+ export interface PrismaFetchReadOptions {
46
+ mode?: PrismaFetchReadMode;
47
+ ocr?: boolean;
48
+ }
49
+
50
+ const SETTINGS_KEY = "veritas.settings.v2";
51
+ const DEFAULT_URL = "http://127.0.0.1:8080";
52
+
53
+ export function normalizePrismaFetchUrl(url?: string): string {
54
+ return (url || DEFAULT_URL).trim().replace(/\/+$/, "");
55
+ }
56
+
57
+ export function getPrismaFetchSettings(): PrismaFetchRuntimeSettings {
58
+ try {
59
+ const raw = localStorage.getItem(SETTINGS_KEY);
60
+ const parsed = raw ? JSON.parse(raw) as Partial<PrismaFetchRuntimeSettings> : {};
61
+ return {
62
+ prismafetchEnabled: parsed.prismafetchEnabled ?? true,
63
+ prismafetchAutoFallback: parsed.prismafetchAutoFallback ?? true,
64
+ prismafetchUrl: normalizePrismaFetchUrl(parsed.prismafetchUrl ?? DEFAULT_URL),
65
+ };
66
+ } catch {
67
+ return {
68
+ prismafetchEnabled: true,
69
+ prismafetchAutoFallback: true,
70
+ prismafetchUrl: DEFAULT_URL,
71
+ };
72
+ }
73
+ }
74
+
75
+ export async function prismaFetchHealth(baseUrl?: string, signal?: AbortSignal): Promise<boolean> {
76
+ const normalized = normalizePrismaFetchUrl(baseUrl ?? getPrismaFetchSettings().prismafetchUrl);
77
+ try {
78
+ const res = await fetch(`${normalized}/healthz`, {
79
+ method: "GET",
80
+ signal,
81
+ headers: { Accept: "application/json" },
82
+ });
83
+ if (!res.ok) return false;
84
+ const data = await res.json() as { status?: string };
85
+ return data.status === "ok";
86
+ } catch {
87
+ return false;
88
+ }
89
+ }
90
+
91
+ export async function resolvePrismaFetchAvailability(explicitBaseUrl?: string): Promise<{ ok: boolean; baseUrl: string; reason?: string }> {
92
+ const settings = getPrismaFetchSettings();
93
+ const baseUrl = normalizePrismaFetchUrl(explicitBaseUrl ?? settings.prismafetchUrl);
94
+ if (!settings.prismafetchEnabled) {
95
+ return { ok: false, baseUrl, reason: "PrismaFetch fallback is disabled in settings." };
96
+ }
97
+ const ok = await prismaFetchHealth(baseUrl);
98
+ if (!ok) {
99
+ return { ok: false, baseUrl, reason: `PrismaFetch server unreachable at ${baseUrl}.` };
100
+ }
101
+ return { ok: true, baseUrl };
102
+ }
103
+
104
+ export async function prismaFetchSearch(
105
+ query: string,
106
+ opts?: { baseUrl?: string; count?: number; signal?: AbortSignal },
107
+ ): Promise<{ results: PrismaFetchSearchResult[]; backend: string }> {
108
+ const baseUrl = normalizePrismaFetchUrl(opts?.baseUrl ?? getPrismaFetchSettings().prismafetchUrl);
109
+ const url = new URL(`${baseUrl}/api/search`);
110
+ url.searchParams.set("query", query);
111
+ url.searchParams.set("count", String(opts?.count ?? 8));
112
+
113
+ const res = await fetch(url.toString(), {
114
+ method: "GET",
115
+ signal: opts?.signal,
116
+ headers: { Accept: "application/json" },
117
+ });
118
+
119
+ if (!res.ok) {
120
+ throw new Error(`PrismaFetch search ${res.status}: ${(await res.text()).slice(0, 400)}`);
121
+ }
122
+
123
+ const data = await res.json() as Partial<PrismaFetchSearchResponse>;
124
+ return {
125
+ backend: data.backend ?? "unknown",
126
+ results: Array.isArray(data.results) ? data.results.map((r) => ({
127
+ title: String(r.title ?? "Untitled"),
128
+ url: String(r.url ?? ""),
129
+ description: String(r.description ?? ""),
130
+ content: String(r.content ?? r.description ?? ""),
131
+ })).filter((r) => !!r.url) : [],
132
+ };
133
+ }
134
+
135
+ export async function prismaFetchRead(
136
+ targetUrl: string,
137
+ opts?: { baseUrl?: string; mode?: PrismaFetchReadMode; ocr?: boolean; signal?: AbortSignal },
138
+ ): Promise<PrismaFetchReadResult> {
139
+ const baseUrl = normalizePrismaFetchUrl(opts?.baseUrl ?? getPrismaFetchSettings().prismafetchUrl);
140
+ const url = new URL(`${baseUrl}/api/read`);
141
+ url.searchParams.set("url", targetUrl);
142
+ if (opts?.mode) url.searchParams.set("mode", opts.mode);
143
+ if (opts?.ocr) url.searchParams.set("ocr", "1");
144
+
145
+ const res = await fetch(url.toString(), {
146
+ method: "GET",
147
+ signal: opts?.signal,
148
+ headers: { Accept: "application/json" },
149
+ });
150
+
151
+ if (!res.ok) {
152
+ throw new Error(`PrismaFetch read ${res.status}: ${(await res.text()).slice(0, 400)}`);
153
+ }
154
+
155
+ const data = await res.json() as Partial<PrismaFetchReadResult>;
156
+ return {
157
+ url: String(data.url ?? targetUrl),
158
+ title: String(data.title ?? targetUrl),
159
+ markdown: String(data.markdown ?? ""),
160
+ tier: typeof data.tier === "string" ? data.tier : undefined,
161
+ links: Array.isArray(data.links) ? data.links.map(String) : [],
162
+ totalTokens: typeof data.totalTokens === "number" ? data.totalTokens : undefined,
163
+ };
164
+ }
165
+
166
+ // Convenience: readMode + ocr from app settings (used by ControlPlane / Modules)
167
+ export function getPrismaFetchReadOptionsFromSettings(settings?: {
168
+ prismafetchReadMode?: PrismaFetchReadMode;
169
+ prismafetchOcr?: boolean;
170
+ }): PrismaFetchReadOptions {
171
+ return {
172
+ mode: settings?.prismafetchReadMode || "auto",
173
+ ocr: !!settings?.prismafetchOcr,
174
+ };
175
+ }
@@ -0,0 +1 @@
1
+ export * from "./prismafetch.base";
@@ -0,0 +1,65 @@
1
+ // SerpAPI connector. NOTE: SerpAPI does not always permit direct browser
2
+ // fetches due to CORS. Users may need to set a CORS proxy URL (e.g. their own
3
+ // Cloudflare Worker) — pass `proxyBase` to route through it.
4
+ // Docs: https://serpapi.com/search-api
5
+
6
+ export interface SerpApiOptions {
7
+ apiKey: string;
8
+ engine?: "google" | "bing" | "duckduckgo" | "google_news" | "google_scholar";
9
+ num?: number;
10
+ hl?: string;
11
+ gl?: string;
12
+ proxyBase?: string; // e.g. "https://my-proxy.workers.dev/?url="
13
+ signal?: AbortSignal;
14
+ }
15
+
16
+ export interface SerpResult {
17
+ position?: number;
18
+ title: string;
19
+ link: string;
20
+ snippet: string;
21
+ source?: string;
22
+ date?: string;
23
+ }
24
+
25
+ export async function serpapiSearch(query: string, opts: SerpApiOptions): Promise<SerpResult[]> {
26
+ const params = new URLSearchParams({
27
+ engine: opts.engine ?? "google",
28
+ q: query,
29
+ api_key: opts.apiKey,
30
+ num: String(opts.num ?? 10),
31
+ hl: opts.hl ?? "en",
32
+ gl: opts.gl ?? "us",
33
+ output: "json",
34
+ });
35
+ let url = `https://serpapi.com/search.json?${params.toString()}`;
36
+ if (opts.proxyBase) url = `${opts.proxyBase}${encodeURIComponent(url)}`;
37
+
38
+ const res = await fetch(url, { signal: opts.signal });
39
+ if (!res.ok) {
40
+ const t = await res.text();
41
+ throw new Error(`SerpAPI ${res.status}: ${t.slice(0, 400)}`);
42
+ }
43
+ const data = (await res.json()) as {
44
+ organic_results?: Array<{
45
+ position?: number;
46
+ title: string;
47
+ link: string;
48
+ snippet?: string;
49
+ source?: string;
50
+ date?: string;
51
+ }>;
52
+ news_results?: Array<{ position?: number; title: string; link: string; snippet?: string; source?: string; date?: string }>;
53
+ error?: string;
54
+ };
55
+ if (data.error) throw new Error(`SerpAPI error: ${data.error}`);
56
+ const raw = data.organic_results ?? data.news_results ?? [];
57
+ return raw.map((r, i) => ({
58
+ position: r.position ?? i + 1,
59
+ title: r.title,
60
+ link: r.link,
61
+ snippet: r.snippet ?? "",
62
+ source: r.source,
63
+ date: r.date,
64
+ }));
65
+ }
@@ -0,0 +1,44 @@
1
+ export interface WikidataEntity {
2
+ id: string;
3
+ label: string;
4
+ description: string;
5
+ concepturi: string;
6
+ }
7
+
8
+ export interface AnchorProbeResult {
9
+ entities: WikidataEntity[];
10
+ coverage: number;
11
+ gapNotes: string[];
12
+ }
13
+
14
+ export async function wikidataSearchEntities(query: string, limit = 6, signal?: AbortSignal): Promise<WikidataEntity[]> {
15
+ const url = new URL("https://www.wikidata.org/w/api.php");
16
+ url.searchParams.set("action", "wbsearchentities");
17
+ url.searchParams.set("language", "en");
18
+ url.searchParams.set("format", "json");
19
+ url.searchParams.set("origin", "*");
20
+ url.searchParams.set("search", query);
21
+ url.searchParams.set("limit", String(limit));
22
+ const res = await fetch(url.toString(), { signal });
23
+ if (!res.ok) throw new Error(`Wikidata search ${res.status}`);
24
+ const data = (await res.json()) as { search?: Array<{ id: string; label?: string; description?: string; concepturi?: string }> };
25
+ return (data.search ?? []).map((e) => ({
26
+ id: e.id,
27
+ label: e.label ?? e.id,
28
+ description: e.description ?? "",
29
+ concepturi: e.concepturi ?? `https://www.wikidata.org/wiki/${e.id}`,
30
+ }));
31
+ }
32
+
33
+ export async function anchorProbe(query: string, signal?: AbortSignal): Promise<AnchorProbeResult> {
34
+ const entities = await wikidataSearchEntities(query, 6, signal);
35
+ const words = query.trim().split(/\s+/).filter((w) => w.length > 3);
36
+ const labelText = entities.map((e) => `${e.label} ${e.description}`).join(" ").toLowerCase();
37
+ const overlap = words.filter((w) => labelText.includes(w.toLowerCase())).length;
38
+ const coverage = Math.min(0.9, Math.max(0, entities.length / 8 + overlap / Math.max(8, words.length * 2)));
39
+ const gapNotes: string[] = [];
40
+ if (coverage < 0.35) gapNotes.push("Anchor coverage is low; live web retrieval should stay enabled.");
41
+ if (/latest|recent|today|202\d|current/i.test(query)) gapNotes.push("Temporal or current-events signal detected; anchor needs live freshness check.");
42
+ if (entities.length === 0) gapNotes.push("No Wikidata entities found for the query phrase.");
43
+ return { entities, coverage, gapNotes };
44
+ }