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.
- package/diagnostics.mjs +141 -0
- package/dist/compute.worker-j5ahIpF-.js +1 -0
- package/dist/graph.worker-DG6iGCB9.js +1 -0
- package/dist/index.html +2314 -0
- package/flatten-guide.md +248 -0
- package/index.html +12 -0
- package/package.json +38 -0
- package/src/App.orig.tsx +204 -0
- package/src/App.orig1.tsx +27 -0
- package/src/App.orig2.tsx +25 -0
- package/src/App.tsx +3 -0
- package/src/BaseApp.tsx +18 -0
- package/src/INTEGRATION_LEDGER.md +22 -0
- package/src/PERSIST_CANARY.orig.txt +1 -0
- package/src/PERSIST_CANARY.txt +1 -0
- package/src/REGRESSION_LEDGER.md +651 -0
- package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
- package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
- package/src/chat/VeritasChat.tsx +69 -0
- package/src/chat/synthesis.ts +31 -0
- package/src/chat/tier.ts +45 -0
- package/src/components/AdaptersPage.orig.tsx +99 -0
- package/src/components/AdaptersPage.tsx +3 -0
- package/src/components/AdversarialPanel.tsx +136 -0
- package/src/components/AnchorBaselinePanel.tsx +75 -0
- package/src/components/AnswerPanel.tsx +60 -0
- package/src/components/ArtifactPanel.tsx +43 -0
- package/src/components/ChatApp.orig.tsx +1582 -0
- package/src/components/ChatApp.orig1.tsx +722 -0
- package/src/components/ChatApp.tsx +95 -0
- package/src/components/ChatAugmentPanels.tsx +73 -0
- package/src/components/CitationLedgerPanel.tsx +313 -0
- package/src/components/ComputeSandboxPanel.tsx +130 -0
- package/src/components/ConfigPanel.tsx +110 -0
- package/src/components/ControlPlanePage.tsx +195 -0
- package/src/components/CreativeTreeLifePage.tsx +234 -0
- package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
- package/src/components/DebugTracePanel.tsx +137 -0
- package/src/components/DeepReasoningTrace.tsx +48 -0
- package/src/components/EntitySheetPanel.tsx +51 -0
- package/src/components/FailureModesPanel.tsx +104 -0
- package/src/components/GBSDashboard.tsx +239 -0
- package/src/components/GraphView.tsx +116 -0
- package/src/components/HUD.tsx +125 -0
- package/src/components/HypothesisPanel.tsx +67 -0
- package/src/components/InnovationGenomeEngine.tsx +792 -0
- package/src/components/InnovationPersonaGuide.tsx +348 -0
- package/src/components/InnovationPersonaPanel.tsx +289 -0
- package/src/components/LiveResourceHUD.tsx +109 -0
- package/src/components/LongWriterPanel.tsx +112 -0
- package/src/components/MainPipelineV10Bridge.tsx +72 -0
- package/src/components/MarkdownLite.tsx +63 -0
- package/src/components/MemoryInspector.tsx +38 -0
- package/src/components/MemoryMonitor.tsx +52 -0
- package/src/components/MemoryStressPanel.tsx +176 -0
- package/src/components/ModulesPage.tsx +171 -0
- package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
- package/src/components/PipelineDebugConsole.tsx +3 -0
- package/src/components/PreFlightHUD.tsx +67 -0
- package/src/components/PrismaFetchTracePanel.tsx +87 -0
- package/src/components/ReportOSPanel.tsx +165 -0
- package/src/components/ResourceEstimatorPage.tsx +327 -0
- package/src/components/RichText.tsx +181 -0
- package/src/components/SagaOsPanel.tsx +605 -0
- package/src/components/SagaOsReference.tsx +421 -0
- package/src/components/SagaOsV2Panel.tsx +594 -0
- package/src/components/SagaStyleTab.tsx +382 -0
- package/src/components/SageBlueprintPanel.tsx +416 -0
- package/src/components/SharedChatInput.tsx +77 -0
- package/src/components/StatFinancePanel.tsx +190 -0
- package/src/components/StylePersonaPanel.tsx +128 -0
- package/src/components/TemplatesPage.tsx +300 -0
- package/src/components/TestPanel.tsx +63 -0
- package/src/components/TraceLog.tsx +89 -0
- package/src/components/V15CalibrationAugment.tsx +1134 -0
- package/src/components/V15CalibrationDialog.tsx +1871 -0
- package/src/components/V15Overlay.orig.tsx +120 -0
- package/src/components/V15Overlay.tsx +4 -0
- package/src/components/V15OverlayWrapper.tsx +362 -0
- package/src/components/V15Toggle.tsx +34 -0
- package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
- package/src/console/EngineeringConsole.tsx +103 -0
- package/src/console/FictionSystemPanels.tsx +882 -0
- package/src/console/InspectorPanels.tsx +254 -0
- package/src/console/ManuscriptGraderPanel.tsx +182 -0
- package/src/console/RecursiveReviserPanel.tsx +245 -0
- package/src/console/SeedControlPanel.tsx +135 -0
- package/src/console/StoryControllerPanel.tsx +443 -0
- package/src/console/engineBridge.ts +258 -0
- package/src/console/gemini.ts +106 -0
- package/src/console/grading.ts +250 -0
- package/src/console/util.ts +45 -0
- package/src/fiction/continuity.ts +372 -0
- package/src/fiction/crash-protection.ts +111 -0
- package/src/fiction/diagnostics.ts +208 -0
- package/src/fiction/diff.ts +74 -0
- package/src/fiction/entity-state-machine.ts +214 -0
- package/src/fiction/nive-engine.ts +404 -0
- package/src/fiction/pipeline-selftest.ts +132 -0
- package/src/fiction/seed-control.ts +193 -0
- package/src/fiction/story-engine.ts +326 -0
- package/src/fiction/types.ts +68 -0
- package/src/flatten-guide.md +379 -0
- package/src/index.css +14 -0
- package/src/index.orig.css +13 -0
- package/src/lib/academic-sources.base.ts +188 -0
- package/src/lib/academic-sources.orig.ts +28 -0
- package/src/lib/academic-sources.ts +10 -0
- package/src/lib/advanced-math.ts +119 -0
- package/src/lib/adversarial-engine.base.ts +409 -0
- package/src/lib/adversarial-engine.ts +82 -0
- package/src/lib/ai-revision.ts +623 -0
- package/src/lib/app-state.tsx +426 -0
- package/src/lib/artifact-registry.ts +97 -0
- package/src/lib/artifacts.ts +180 -0
- package/src/lib/atlas-dr.ts +167 -0
- package/src/lib/browser-mtls.ts +127 -0
- package/src/lib/browser-queue.ts +221 -0
- package/src/lib/browser-search-scraper.base.ts +382 -0
- package/src/lib/browser-search-scraper.orig.ts +90 -0
- package/src/lib/browser-search-scraper.ts +10 -0
- package/src/lib/calc-interceptor.ts +82 -0
- package/src/lib/citation-lane-tap.ts +78 -0
- package/src/lib/citation-ledger-store.ts +369 -0
- package/src/lib/citation-ledger.ts +203 -0
- package/src/lib/compute-sandbox.ts +215 -0
- package/src/lib/compute.worker.ts +153 -0
- package/src/lib/connectors/gemini.ts +120 -0
- package/src/lib/connectors/jina.ts +233 -0
- package/src/lib/connectors/marketdata.ts +51 -0
- package/src/lib/connectors/prismafetch.base.ts +175 -0
- package/src/lib/connectors/prismafetch.ts +1 -0
- package/src/lib/connectors/serpapi.ts +65 -0
- package/src/lib/connectors/wikidata.ts +44 -0
- package/src/lib/constraints.ts +331 -0
- package/src/lib/continuation-detector.base.ts +153 -0
- package/src/lib/continuation-detector.ts +48 -0
- package/src/lib/contradraft.ts +168 -0
- package/src/lib/cors-proxy.ts +120 -0
- package/src/lib/coverage.ts +81 -0
- package/src/lib/debug/architecture-prescription.ts +556 -0
- package/src/lib/debug/covea-repair.ts +543 -0
- package/src/lib/debug/helios-ground.ts +319 -0
- package/src/lib/debug/intent-decomposer.ts +281 -0
- package/src/lib/debug/intent-lattice.ts +349 -0
- package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
- package/src/lib/debug/pipeline-trace-bus.ts +424 -0
- package/src/lib/debug/prompt-forge.ts +678 -0
- package/src/lib/debug/repair-sites.ts +513 -0
- package/src/lib/debug/research-phase.ts +266 -0
- package/src/lib/debug/retrieval-context.ts +76 -0
- package/src/lib/debug/retrieval-hardener.ts +142 -0
- package/src/lib/debug/scraper-debug-runner.ts +576 -0
- package/src/lib/debug/scraper-forensics.ts +418 -0
- package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
- package/src/lib/debug/self-test.ts +415 -0
- package/src/lib/debug/step-attribution.ts +211 -0
- package/src/lib/debug/template-rubric.ts +301 -0
- package/src/lib/debug/unified-innovation.ts +126 -0
- package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
- package/src/lib/defense-registry.ts +104 -0
- package/src/lib/defenses.ts +162 -0
- package/src/lib/deterministic-citation-ledger.ts +174 -0
- package/src/lib/elo-registry.base.ts +117 -0
- package/src/lib/elo-registry.ts +1 -0
- package/src/lib/entity-resolver.ts +291 -0
- package/src/lib/failure-modes.ts +267 -0
- package/src/lib/feature-registry.ts +69 -0
- package/src/lib/flaw-registry.ts +143 -0
- package/src/lib/flaws/_template.ts +52 -0
- package/src/lib/flaws/builtins.ts +63 -0
- package/src/lib/flaws/finance.ts +169 -0
- package/src/lib/flaws/fixers.ts +10 -0
- package/src/lib/flaws/index.ts +32 -0
- package/src/lib/flaws/legal.ts +157 -0
- package/src/lib/flaws/medical.ts +188 -0
- package/src/lib/flaws/original-defenses-pack.ts +81 -0
- package/src/lib/flaws/sample-declarative-pack.json +38 -0
- package/src/lib/flaws/selftest.ts +201 -0
- package/src/lib/flaws/software-extended.ts +125 -0
- package/src/lib/flaws/software-rn-webgl.ts +182 -0
- package/src/lib/flaws/statistics-advanced.ts +46 -0
- package/src/lib/flaws/statistics.ts +61 -0
- package/src/lib/gbse/config.ts +20 -0
- package/src/lib/gbse/engine.ts +226 -0
- package/src/lib/gbse/graph.ts +118 -0
- package/src/lib/gbse/graph.worker.ts +21 -0
- package/src/lib/gbse/tests.ts +117 -0
- package/src/lib/gbse/tiers.ts +65 -0
- package/src/lib/gbse/types.ts +94 -0
- package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
- package/src/lib/innovation-genome-engine-v2.ts +3 -0
- package/src/lib/innovation-genome-engine.orig.ts +330 -0
- package/src/lib/innovation-genome-engine.ts +3 -0
- package/src/lib/innovation-genome-v10.orig.ts +314 -0
- package/src/lib/innovation-genome-v10.ts +2 -0
- package/src/lib/innovation-genome-v3.orig.ts +2015 -0
- package/src/lib/innovation-genome-v3.ts +2 -0
- package/src/lib/innovation-genome-v4.orig.ts +1959 -0
- package/src/lib/innovation-genome-v4.ts +2 -0
- package/src/lib/innovation-genome-v5.orig.ts +1210 -0
- package/src/lib/innovation-genome-v5.ts +2 -0
- package/src/lib/innovation-genome-v7.orig.ts +2549 -0
- package/src/lib/innovation-genome-v7.ts +2 -0
- package/src/lib/innovation-genome-v8.orig.ts +1485 -0
- package/src/lib/innovation-genome-v8.ts +2 -0
- package/src/lib/innovation-genome-v9.orig.ts +643 -0
- package/src/lib/innovation-genome-v9.ts +2 -0
- package/src/lib/jina.base.ts +293 -0
- package/src/lib/jina.ts +1 -0
- package/src/lib/live-telemetry.ts +61 -0
- package/src/lib/longwriter.ts +84 -0
- package/src/lib/manuscript-grade-v2.ts +298 -0
- package/src/lib/memory-governor.ts +173 -0
- package/src/lib/memory-stress-tests.ts +248 -0
- package/src/lib/model-intelligence.ts +48 -0
- package/src/lib/model-rotator.ts +75 -0
- package/src/lib/models.orig.ts +513 -0
- package/src/lib/models.ts +151 -0
- package/src/lib/n-deep.base.ts +592 -0
- package/src/lib/n-deep.ts +51 -0
- package/src/lib/nih-simulator.ts +56 -0
- package/src/lib/nih-vulnerability-fixes.ts +460 -0
- package/src/lib/omega-templates.base.ts +294 -0
- package/src/lib/omega-templates.ts +225 -0
- package/src/lib/omni-nexus.ts +64 -0
- package/src/lib/oracle-adapters.ts +67 -0
- package/src/lib/oracle-registry.ts +163 -0
- package/src/lib/orchestrator.ts +357 -0
- package/src/lib/output-boundary.ts +235 -0
- package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
- package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
- package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
- package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
- package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
- package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
- package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
- package/src/lib/overrides/vite-native-scraper.ts +848 -0
- package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
- package/src/lib/overrides/vite-native-selftest.ts +183 -0
- package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
- package/src/lib/philosophy-toolkit.ts +75 -0
- package/src/lib/pipeline.orig.ts +659 -0
- package/src/lib/pipeline.ts +42 -0
- package/src/lib/precache.ts +128 -0
- package/src/lib/py-sandbox.ts +205 -0
- package/src/lib/quality-score.ts +96 -0
- package/src/lib/quant-engine.ts +231 -0
- package/src/lib/quant-lib.ts +80 -0
- package/src/lib/reportos.ts +175 -0
- package/src/lib/research-os.ts +306 -0
- package/src/lib/resource-estimator.ts +114 -0
- package/src/lib/rpm-governor.ts +93 -0
- package/src/lib/runtime-estimator.ts +135 -0
- package/src/lib/sage/empty-space-v6.ts +560 -0
- package/src/lib/sage/empty-space-v7.ts +222 -0
- package/src/lib/sage/empty-space.ts +733 -0
- package/src/lib/sage/engine.ts +786 -0
- package/src/lib/sage/index.ts +17 -0
- package/src/lib/sage/judge.ts +246 -0
- package/src/lib/sage/prng.ts +53 -0
- package/src/lib/sage/reality-grounding.ts +380 -0
- package/src/lib/sage/repetition.ts +151 -0
- package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
- package/src/lib/sage/saga-os-v2.ts +274 -0
- package/src/lib/sage/saga-os-v3-core.ts +1491 -0
- package/src/lib/sage/saga-os-v3-release.ts +739 -0
- package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
- package/src/lib/sage/saga-os-v4.ts +212 -0
- package/src/lib/sage/saga-os-v5.ts +205 -0
- package/src/lib/sage/saga-os-v6.ts +346 -0
- package/src/lib/sage/saga-os-v7.ts +314 -0
- package/src/lib/sage/saga-os-v8.ts +505 -0
- package/src/lib/sage/saga-os.ts +329 -0
- package/src/lib/sage/taxonomy.ts +403 -0
- package/src/lib/sage/unified-roll.ts +524 -0
- package/src/lib/sage/worked-sketches.ts +44 -0
- package/src/lib/sage.ts +166 -0
- package/src/lib/scraper-debug-bus.ts +53 -0
- package/src/lib/scraper-enhanced.orig.ts +153 -0
- package/src/lib/scraper-enhanced.ts +10 -0
- package/src/lib/scraper-hardener.base.orig.ts +155 -0
- package/src/lib/scraper-hardener.base.ts +317 -0
- package/src/lib/scraper-hardener.ts +1 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
- package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
- package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
- package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
- package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
- package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
- package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
- package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
- package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
- package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
- package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
- package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
- package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
- package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
- package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
- package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
- package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
- package/src/lib/scraper-vnext/query-strategist.ts +423 -0
- package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
- package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
- package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
- package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
- package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
- package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
- package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
- package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
- package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
- package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
- package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
- package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
- package/src/lib/scraper-vnext/strata-engine.ts +13 -0
- package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
- package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
- package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
- package/src/lib/scraper-vnext/terminal-final.ts +277 -0
- package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
- package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
- package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
- package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
- package/src/lib/search-cache.ts +138 -0
- package/src/lib/sloop-runner.base.ts +223 -0
- package/src/lib/sloop-runner.ts +104 -0
- package/src/lib/sloop.ts +114 -0
- package/src/lib/small-model-adapters.ts +82 -0
- package/src/lib/sscp.ts +86 -0
- package/src/lib/system-map.ts +54 -0
- package/src/lib/uni-node.ts +86 -0
- package/src/lib/universal-rigor-guard.ts +433 -0
- package/src/lib/v15-gate-testbed.ts +274 -0
- package/src/lib/v15-gemini.base.ts +89 -0
- package/src/lib/v15-gemini.ts +1 -0
- package/src/lib/v15-grounding.orig.ts +241 -0
- package/src/lib/v15-grounding.ts +79 -0
- package/src/lib/v15-pipeline.base.ts +769 -0
- package/src/lib/v15-pipeline.orig.ts +1154 -0
- package/src/lib/v15-pipeline.ts +674 -0
- package/src/lib/v15-questions.ts +64 -0
- package/src/lib/v15-rate-limiter.orig.ts +91 -0
- package/src/lib/v15-rate-limiter.ts +48 -0
- package/src/lib/v15-state.base.ts +87 -0
- package/src/lib/v15-state.ts +28 -0
- package/src/lib/visual-table-generator.tsx +207 -0
- package/src/lib/wasm-runtime.ts +133 -0
- package/src/lib/williams-persona-guide.ts +265 -0
- package/src/lib/williams-style.base.ts +464 -0
- package/src/lib/williams-style.ts +59 -0
- package/src/lib/worker-pool.ts +96 -0
- package/src/lib/writing-tiers.ts +125 -0
- package/src/main.orig.tsx +10 -0
- package/src/main.tsx +10 -0
- package/src/utils/cn.orig.ts +6 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/unify.mjs +537 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* research-phase.ts — prewriting innovation research, separated from report writing.
|
|
3
|
+
*
|
|
4
|
+
* Sequence:
|
|
5
|
+
* user intent -> unified v1+v2 genome -> Williams-seeded query planner
|
|
6
|
+
* -> HELIOS retrieval -> immutable research dossier -> report writer
|
|
7
|
+
*
|
|
8
|
+
* Query invariant: no query may copy more than three consecutive words from
|
|
9
|
+
* the user's original prompt. This forces genuine intent decomposition rather
|
|
10
|
+
* than feeding the prose request back into a search engine.
|
|
11
|
+
*/
|
|
12
|
+
import { heliosGround } from "@/lib/debug/helios-ground";
|
|
13
|
+
import {
|
|
14
|
+
buildUnifiedInnovationPlan,
|
|
15
|
+
PATH_NODE_NAMES,
|
|
16
|
+
type UnifiedInnovationPlan,
|
|
17
|
+
} from "@/lib/debug/unified-innovation";
|
|
18
|
+
|
|
19
|
+
export type ResearchQueryKind =
|
|
20
|
+
| "pain-point"
|
|
21
|
+
| "complaint"
|
|
22
|
+
| "failed-workaround"
|
|
23
|
+
| "adjacent-solution"
|
|
24
|
+
| "technical-building-block"
|
|
25
|
+
| "regulatory-constraint"
|
|
26
|
+
| "market-evidence"
|
|
27
|
+
| "falsification";
|
|
28
|
+
|
|
29
|
+
export interface ResearchQuery {
|
|
30
|
+
id: string;
|
|
31
|
+
kind: ResearchQueryKind;
|
|
32
|
+
query: string;
|
|
33
|
+
rationale: string;
|
|
34
|
+
pathNode: string;
|
|
35
|
+
source: "deterministic" | "llm";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ResearchPhaseResult {
|
|
39
|
+
version: "prewriting-research/1";
|
|
40
|
+
startedAt: number;
|
|
41
|
+
endedAt: number;
|
|
42
|
+
question: string;
|
|
43
|
+
innovation: UnifiedInnovationPlan;
|
|
44
|
+
queries: ResearchQuery[];
|
|
45
|
+
rejectedQueries: Array<{ query: string; reason: string }>;
|
|
46
|
+
sources: Array<{ title: string; url: string; content: string }>;
|
|
47
|
+
provider: string;
|
|
48
|
+
evidenceBlock: string;
|
|
49
|
+
dossier: string;
|
|
50
|
+
debug: string[];
|
|
51
|
+
status: "grounded" | "evidence-starved";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ResearchPhaseOptions {
|
|
55
|
+
question: string;
|
|
56
|
+
personaSeed?: number;
|
|
57
|
+
risk?: "low" | "medium" | "high" | "critical";
|
|
58
|
+
/** Optional independent LLM planner. Deterministic queries always remain. */
|
|
59
|
+
generateQueries?: (prompt: string) => Promise<{ ok: boolean; text: string; error?: string }>;
|
|
60
|
+
onProgress?: (message: string) => void;
|
|
61
|
+
maxQueries?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function words(text: string): string[] {
|
|
65
|
+
return text.toLowerCase().match(/[a-z0-9][a-z0-9-]*/g) ?? [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Find the longest exact consecutive word run shared with the prompt. */
|
|
69
|
+
export function longestPromptQuote(prompt: string, query: string): number {
|
|
70
|
+
const p = words(prompt), q = words(query);
|
|
71
|
+
let best = 0;
|
|
72
|
+
for (let i = 0; i < q.length; i++) {
|
|
73
|
+
for (let j = 0; j < p.length; j++) {
|
|
74
|
+
let n = 0;
|
|
75
|
+
while (i + n < q.length && j + n < p.length && q[i + n] === p[j + n]) n++;
|
|
76
|
+
if (n > best) best = n;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return best;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function queryIsDecomposed(prompt: string, query: string): boolean {
|
|
83
|
+
const q = query.trim();
|
|
84
|
+
return q.length >= 8 && q.length <= 180 && longestPromptQuote(prompt, q) <= 3;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function keyTerms(question: string, plan: UnifiedInnovationPlan): { domain: string; objects: string[] } {
|
|
88
|
+
const stop = new Set(["find", "product", "doesn't", "exist", "large", "unmet", "demand", "made", "using", "existing", "technology", "space"]);
|
|
89
|
+
const tokens = words(question).filter((w) => w.length > 3 && !stop.has(w));
|
|
90
|
+
return { domain: plan.domain === "general" ? (tokens[0] ?? "industry") : plan.domain, objects: [...new Set(tokens)].slice(0, 4) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Persona-seeded deterministic floor. These are investigation tasks, not paraphrases. */
|
|
94
|
+
export function buildDeterministicResearchQueries(
|
|
95
|
+
question: string,
|
|
96
|
+
plan: UnifiedInnovationPlan,
|
|
97
|
+
): ResearchQuery[] {
|
|
98
|
+
const { domain, objects } = keyTerms(question, plan);
|
|
99
|
+
const object = objects[0] ?? domain;
|
|
100
|
+
const voice = plan.williams.archetype.name;
|
|
101
|
+
const nodes = plan.expansion.path.seq.split(/\s*→\s*/);
|
|
102
|
+
const node = (i: number) => PATH_NODE_NAMES[nodes[i % Math.max(1, nodes.length)]] ?? "Problem Choice";
|
|
103
|
+
const rows: Array<[ResearchQueryKind, string, string]> = [
|
|
104
|
+
["pain-point", `${domain} user pain points recurring complaints unmet needs`, "Discover repeated user harm before proposing a product."],
|
|
105
|
+
["complaint", `${object} negative reviews failure frustration workaround forum`, "Find first-person evidence and current product failures."],
|
|
106
|
+
["failed-workaround", `${domain} failed solutions abandoned products limitations case study`, "Map near-misses and why prior approaches failed."],
|
|
107
|
+
["adjacent-solution", `${domain} adjacent industries analogous devices transferable mechanism`, "Search for proven mechanisms outside the obvious category."],
|
|
108
|
+
["technical-building-block", `${object} available components manufacturing feasibility sensor delivery platform`, "Verify that the required building blocks exist now."],
|
|
109
|
+
["regulatory-constraint", `${domain} regulation safety approval compliance adverse event`, "Expose constraints before ideation collapses onto an illegal or unsafe design."],
|
|
110
|
+
["market-evidence", `${domain} market segments adoption willingness pay underserved users evidence`, "Test whether the pain is sufficiently broad and economically meaningful."],
|
|
111
|
+
["falsification", `${object} contraindications failure modes false assumptions validation experiment`, "Construct the cheapest test that can kill a weak concept."],
|
|
112
|
+
];
|
|
113
|
+
return rows.map(([kind, query, rationale], i) => ({
|
|
114
|
+
id: `D${i + 1}`,
|
|
115
|
+
kind,
|
|
116
|
+
query,
|
|
117
|
+
rationale: `${rationale} Persona lens: ${voice}.`,
|
|
118
|
+
pathNode: node(i),
|
|
119
|
+
source: "deterministic" as const,
|
|
120
|
+
})).filter((q) => queryIsDecomposed(question, q.query));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function plannerPrompt(question: string, plan: UnifiedInnovationPlan, deterministic: ResearchQuery[]): string {
|
|
124
|
+
return [
|
|
125
|
+
"You are the independent SEARCH STRATEGIST. You do not write the report and do not propose the final product.",
|
|
126
|
+
"Deconstruct intent into exact web-search strings that collect evidence before ideation.",
|
|
127
|
+
"Return JSON only: {\"queries\":[{\"kind\":\"pain-point|complaint|failed-workaround|adjacent-solution|technical-building-block|regulatory-constraint|market-evidence|falsification\",\"query\":\"...\",\"rationale\":\"...\",\"pathNode\":\"full node name\"}]}",
|
|
128
|
+
"HARD RULES:",
|
|
129
|
+
"- 8 to 12 queries; 3 to 14 search terms each; no sentence-length queries.",
|
|
130
|
+
"- No query may copy four consecutive words from the user's prompt.",
|
|
131
|
+
"- Begin with pain points, complaints, failed workarounds and adjacent products; only then search mechanisms, feasibility, regulation and market evidence.",
|
|
132
|
+
"- Never invent a company, statistic, URL, product or user quote.",
|
|
133
|
+
"- Path nodes MUST use full names, never P/A/E/N/V/T/S abbreviations.",
|
|
134
|
+
`WILLIAMS PERSONA: ${plan.williams.archetype.name}\n${plan.williams.systemPromptFragment}`,
|
|
135
|
+
`UNIFIED INNOVATION PATH:\n${plan.expandedPath}`,
|
|
136
|
+
`USER INPUT (for intent only; do not quote it in queries):\n${question}`,
|
|
137
|
+
"DETERMINISTIC FLOOR ALREADY PRESENT (add orthogonal searches; do not paraphrase these):",
|
|
138
|
+
...deterministic.map((q) => `- ${q.kind}: ${q.query}`),
|
|
139
|
+
].join("\n");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parseLlmQueries(raw: string, question: string): { accepted: ResearchQuery[]; rejected: Array<{ query: string; reason: string }> } {
|
|
143
|
+
const accepted: ResearchQuery[] = [];
|
|
144
|
+
const rejected: Array<{ query: string; reason: string }> = [];
|
|
145
|
+
try {
|
|
146
|
+
const clean = raw.replace(/```json/gi, "").replace(/```/g, "");
|
|
147
|
+
const match = clean.match(/\{[\s\S]*\}/);
|
|
148
|
+
const json = JSON.parse(match?.[0] ?? clean);
|
|
149
|
+
const rows = Array.isArray(json?.queries) ? json.queries : [];
|
|
150
|
+
const validKinds = new Set<ResearchQueryKind>([
|
|
151
|
+
"pain-point", "complaint", "failed-workaround", "adjacent-solution",
|
|
152
|
+
"technical-building-block", "regulatory-constraint", "market-evidence", "falsification",
|
|
153
|
+
]);
|
|
154
|
+
for (const row of rows.slice(0, 16)) {
|
|
155
|
+
const query = String(row?.query ?? "").trim();
|
|
156
|
+
const kind = String(row?.kind ?? "") as ResearchQueryKind;
|
|
157
|
+
if (!validKinds.has(kind)) { rejected.push({ query, reason: "invalid-kind" }); continue; }
|
|
158
|
+
if (!queryIsDecomposed(question, query)) {
|
|
159
|
+
rejected.push({ query, reason: `quote-or-length-rule (longest exact run ${longestPromptQuote(question, query)})` });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
accepted.push({
|
|
163
|
+
id: `L${accepted.length + 1}`,
|
|
164
|
+
kind,
|
|
165
|
+
query,
|
|
166
|
+
rationale: String(row?.rationale ?? "LLM-added orthogonal search"),
|
|
167
|
+
pathNode: String(row?.pathNode ?? "Problem Choice"),
|
|
168
|
+
source: "llm",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
rejected.push({ query: raw.slice(0, 200), reason: `parse-error: ${e instanceof Error ? e.message : String(e)}` });
|
|
173
|
+
}
|
|
174
|
+
return { accepted, rejected };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function dedupeQueries(rows: ResearchQuery[]): ResearchQuery[] {
|
|
178
|
+
const seen = new Set<string>();
|
|
179
|
+
return rows.filter((row) => {
|
|
180
|
+
const key = row.query.toLowerCase().replace(/\s+/g, " ");
|
|
181
|
+
if (seen.has(key)) return false;
|
|
182
|
+
seen.add(key);
|
|
183
|
+
return true;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function runPrewritingResearch(opts: ResearchPhaseOptions): Promise<ResearchPhaseResult> {
|
|
188
|
+
const startedAt = Date.now();
|
|
189
|
+
const debug: string[] = [];
|
|
190
|
+
const say = (m: string) => { debug.push(m); opts.onProgress?.(m); };
|
|
191
|
+
const innovation = buildUnifiedInnovationPlan(opts.question, {
|
|
192
|
+
personaSeed: opts.personaSeed,
|
|
193
|
+
risk: opts.risk,
|
|
194
|
+
});
|
|
195
|
+
const deterministic = buildDeterministicResearchQueries(opts.question, innovation);
|
|
196
|
+
let llm: ResearchQuery[] = [];
|
|
197
|
+
const rejectedQueries: Array<{ query: string; reason: string }> = [];
|
|
198
|
+
|
|
199
|
+
if (opts.generateQueries) {
|
|
200
|
+
try {
|
|
201
|
+
say("research-query-planner: independent LLM decomposition started");
|
|
202
|
+
const result = await opts.generateQueries(plannerPrompt(opts.question, innovation, deterministic));
|
|
203
|
+
if (result.ok) {
|
|
204
|
+
const parsed = parseLlmQueries(result.text, opts.question);
|
|
205
|
+
llm = parsed.accepted;
|
|
206
|
+
rejectedQueries.push(...parsed.rejected);
|
|
207
|
+
say(`research-query-planner: accepted ${llm.length}, rejected ${parsed.rejected.length}`);
|
|
208
|
+
} else {
|
|
209
|
+
rejectedQueries.push({ query: "", reason: `planner-unavailable: ${result.error ?? "unknown"}` });
|
|
210
|
+
}
|
|
211
|
+
} catch (e) {
|
|
212
|
+
rejectedQueries.push({ query: "", reason: `planner-threw: ${e instanceof Error ? e.message : String(e)}` });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const maxQueries = Math.max(4, Math.min(12, opts.maxQueries ?? 8));
|
|
217
|
+
const queries = dedupeQueries([...deterministic, ...llm]).slice(0, maxQueries);
|
|
218
|
+
say(`prewriting-research: ${queries.length} independent queries; path=${innovation.expansion.path.name}`);
|
|
219
|
+
const ground = await heliosGround(opts.question, {
|
|
220
|
+
queries: queries.map((q) => q.query),
|
|
221
|
+
maxQueries,
|
|
222
|
+
onDebug: say,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const dossier = [
|
|
226
|
+
"## PREWRITING RESEARCH DOSSIER — immutable evidence handoff",
|
|
227
|
+
"This phase is separate from report writing. It records searches and evidence; it does not claim the proposed idea is validated.",
|
|
228
|
+
innovation.directive,
|
|
229
|
+
"### RESEARCH QUERY REGISTER",
|
|
230
|
+
...queries.map((q) => `- ${q.id} [${q.kind}] [${q.pathNode}] ${q.query}\n Rationale: ${q.rationale}`),
|
|
231
|
+
"### RETRIEVED SOURCES",
|
|
232
|
+
...(ground.sources.length
|
|
233
|
+
? ground.sources.map((s, i) => `- [R${i + 1}] ${s.title}\n URL: ${s.url}`)
|
|
234
|
+
: ["- [EVIDENCE_STARVED] No URL-backed source survived retrieval and relevance gates."]),
|
|
235
|
+
"### HANDOFF RULES",
|
|
236
|
+
"- Report writer may use only URL-backed sources listed above.",
|
|
237
|
+
"- Search hypotheses, persona angles and innovation branches are not facts.",
|
|
238
|
+
"- Unsupported concepts remain proposals; missing evidence remains a data gap.",
|
|
239
|
+
].join("\n");
|
|
240
|
+
|
|
241
|
+
const researchEvidenceBlock = ground.sources.length
|
|
242
|
+
? [
|
|
243
|
+
`PREWRITING RESEARCH EVIDENCE (${ground.provider}, ${ground.sources.length} URL-backed sources).`,
|
|
244
|
+
"R# identifiers are research-phase provenance and MUST NOT be rewritten as S# citations unless admitted by the report citation ledger.",
|
|
245
|
+
"BEGIN PREWRITING RESEARCH CONTENT",
|
|
246
|
+
...ground.sources.map((s, i) => [`BEGIN RESEARCH SOURCE R${i + 1}`, `[R${i + 1}] ${s.title}`, `URL: ${s.url}`, s.content, `END RESEARCH SOURCE R${i + 1}`].join("\n")),
|
|
247
|
+
"END PREWRITING RESEARCH CONTENT",
|
|
248
|
+
].join("\n\n")
|
|
249
|
+
: "";
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
version: "prewriting-research/1",
|
|
253
|
+
startedAt,
|
|
254
|
+
endedAt: Date.now(),
|
|
255
|
+
question: opts.question,
|
|
256
|
+
innovation,
|
|
257
|
+
queries,
|
|
258
|
+
rejectedQueries,
|
|
259
|
+
sources: ground.sources,
|
|
260
|
+
provider: ground.provider,
|
|
261
|
+
evidenceBlock: researchEvidenceBlock,
|
|
262
|
+
dossier,
|
|
263
|
+
debug,
|
|
264
|
+
status: ground.ok ? "grounded" : "evidence-starved",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* retrieval-context.ts — browser-local run context for scraper lane wrappers.
|
|
3
|
+
*
|
|
4
|
+
* Package template queries are truncated before they reach the lanes. The
|
|
5
|
+
* wrapper owns the full original prompt and full IFL; this registry lets each
|
|
6
|
+
* alias-reachable lane recover that plan at its actual call boundary.
|
|
7
|
+
*
|
|
8
|
+
* Concurrency boundary: browser JS may run concurrent V15 requests. Contexts
|
|
9
|
+
* are retained by run id and selection prefers lexical overlap with the raw
|
|
10
|
+
* lane query, then newest context. Callers MUST clear their run on completion.
|
|
11
|
+
*/
|
|
12
|
+
import type { IntentLattice, LatticeQuery } from "@/lib/debug/intent-lattice";
|
|
13
|
+
|
|
14
|
+
interface RetrievalContext {
|
|
15
|
+
runId: string;
|
|
16
|
+
original: string;
|
|
17
|
+
lattice: IntentLattice;
|
|
18
|
+
createdAt: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const contexts = new Map<string, RetrievalContext>();
|
|
22
|
+
|
|
23
|
+
function words(s: string): Set<string> {
|
|
24
|
+
return new Set((s.toLowerCase().match(/[a-z0-9]{4,}/g) ?? []).filter((w) =>
|
|
25
|
+
!/^(this|that|with|from|have|will|into|using|find|please|report|analysis)$/.test(w)
|
|
26
|
+
));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function overlap(a: string, b: string): number {
|
|
30
|
+
const aw = words(a), bw = words(b);
|
|
31
|
+
if (!aw.size || !bw.size) return 0;
|
|
32
|
+
let n = 0;
|
|
33
|
+
for (const w of aw) if (bw.has(w)) n++;
|
|
34
|
+
return n / Math.max(aw.size, bw.size);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function registerRetrievalContext(runId: string, original: string, lattice: IntentLattice): void {
|
|
38
|
+
contexts.set(runId, { runId, original, lattice, createdAt: Date.now() });
|
|
39
|
+
// Defensive cap: interrupted runs must not grow memory forever.
|
|
40
|
+
if (contexts.size > 8) {
|
|
41
|
+
const oldest = [...contexts.values()].sort((a, b) => a.createdAt - b.createdAt)[0];
|
|
42
|
+
if (oldest) contexts.delete(oldest.runId);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function clearRetrievalContext(runId: string): void {
|
|
47
|
+
contexts.delete(runId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Select the best full-prompt context for a package-supplied lane query. */
|
|
51
|
+
export function resolveRetrievalQueries(rawQuery: string, section = "general"): {
|
|
52
|
+
contextRunId: string | null;
|
|
53
|
+
original: string;
|
|
54
|
+
queries: LatticeQuery[];
|
|
55
|
+
} | null {
|
|
56
|
+
if (contexts.size === 0) return null;
|
|
57
|
+
const ranked = [...contexts.values()].sort((a, b) => {
|
|
58
|
+
const ao = overlap(rawQuery, a.original), bo = overlap(rawQuery, b.original);
|
|
59
|
+
return bo - ao || b.createdAt - a.createdAt;
|
|
60
|
+
});
|
|
61
|
+
const ctx = ranked[0];
|
|
62
|
+
const exact = ctx.lattice.queries.filter((q) => q.section === section);
|
|
63
|
+
if (exact.length) return { contextRunId: ctx.runId, original: ctx.original, queries: exact };
|
|
64
|
+
|
|
65
|
+
// Choose lattice queries closest to the package query's section suffix.
|
|
66
|
+
const scored = ctx.lattice.queries
|
|
67
|
+
.map((q) => ({ q, score: overlap(rawQuery, `${q.section} ${q.q}`) }))
|
|
68
|
+
.sort((a, b) => b.score - a.score);
|
|
69
|
+
const best = scored[0]?.score ?? 0;
|
|
70
|
+
const selected = scored.filter((x) => x.score === best).slice(0, 3).map((x) => x.q);
|
|
71
|
+
return {
|
|
72
|
+
contextRunId: ctx.runId,
|
|
73
|
+
original: ctx.original,
|
|
74
|
+
queries: selected.length ? selected : ctx.lattice.queries.slice(0, 3),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* retrieval-hardener.ts — shared workspace guard for every alias-reachable lane.
|
|
3
|
+
*
|
|
4
|
+
* The package template query builder slices the first 120 chars of the raw
|
|
5
|
+
* question before the IFL directive. Therefore prompt-envelope injection alone
|
|
6
|
+
* cannot guarantee the lattice reaches a scraper. Lane wrappers call these
|
|
7
|
+
* pure functions at the actual retrieval boundary.
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
buildLatticeQueries,
|
|
11
|
+
facetCoherence,
|
|
12
|
+
isLikelyDriftResult,
|
|
13
|
+
type LatticeQuery,
|
|
14
|
+
} from "@/lib/debug/intent-lattice";
|
|
15
|
+
import { resolveRetrievalQueries } from "@/lib/debug/retrieval-context";
|
|
16
|
+
|
|
17
|
+
export interface SourceLike {
|
|
18
|
+
url?: string;
|
|
19
|
+
canonicalUrl?: string;
|
|
20
|
+
externalUrl?: string;
|
|
21
|
+
doi?: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
content?: string;
|
|
24
|
+
snippet?: string;
|
|
25
|
+
pageContent?: string;
|
|
26
|
+
abstract?: string;
|
|
27
|
+
articleText?: string;
|
|
28
|
+
contentSample?: string;
|
|
29
|
+
quarantined?: boolean;
|
|
30
|
+
hardQuarantined?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function absoluteUrl(source: SourceLike): string {
|
|
34
|
+
const raw = source.canonicalUrl || source.externalUrl || source.url || source.doi || "";
|
|
35
|
+
if (/^10\.\d{4,9}\//.test(raw)) return `https://doi.org/${raw}`;
|
|
36
|
+
return /^https?:\/\//i.test(raw) ? raw : "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function sourceText(source: SourceLike): string {
|
|
40
|
+
return [
|
|
41
|
+
source.title,
|
|
42
|
+
source.content,
|
|
43
|
+
source.snippet,
|
|
44
|
+
source.pageContent,
|
|
45
|
+
source.abstract,
|
|
46
|
+
source.articleText,
|
|
47
|
+
source.contentSample,
|
|
48
|
+
].filter(Boolean).join(" ");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Whole-token, compact query; never character-slices the user input. */
|
|
52
|
+
export function hardenRetrievalQuery(query: string, section = "general"): {
|
|
53
|
+
query: string;
|
|
54
|
+
latticeQuery: LatticeQuery;
|
|
55
|
+
alternatives: LatticeQuery[];
|
|
56
|
+
} {
|
|
57
|
+
const active = resolveRetrievalQueries(query, section);
|
|
58
|
+
const lattice = active ? null : buildLatticeQueries(query, [section], 3);
|
|
59
|
+
const alternatives = active?.queries.length
|
|
60
|
+
? active.queries
|
|
61
|
+
: lattice!.queries.length
|
|
62
|
+
? lattice!.queries
|
|
63
|
+
: [{ q: query.trim(), section, facets: lattice!.facets, axes: [] }];
|
|
64
|
+
return { query: alternatives[0].q, latticeQuery: alternatives[0], alternatives };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasAxisSignal(query: LatticeQuery, text: string): boolean {
|
|
68
|
+
const hay = text.toLowerCase();
|
|
69
|
+
const axes = new Map<string, string[]>();
|
|
70
|
+
for (const f of query.facets) {
|
|
71
|
+
const toks = f.phrase.toLowerCase().match(/[a-z0-9]{3,}/g) ?? [];
|
|
72
|
+
axes.set(f.axis, [...(axes.get(f.axis) ?? []), ...toks]);
|
|
73
|
+
}
|
|
74
|
+
const matched = [...axes.entries()].filter(([, toks]) => toks.some((t) => hay.includes(t))).map(([axis]) => axis);
|
|
75
|
+
const domainRequired = axes.has("domain") ? matched.includes("domain") : true;
|
|
76
|
+
return domainRequired && matched.some((axis) => axis !== "domain");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function looksLikeMarkupBoilerplate(text: string): boolean {
|
|
80
|
+
const s = text.toLowerCase();
|
|
81
|
+
return /table,\s*caption,\s*tbody/.test(s)
|
|
82
|
+
|| /--font-inter|font-family:\s*var\(--font/.test(s)
|
|
83
|
+
|| /margin:\s*0;padding:\s*0;border:\s*0/.test(s);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* URL + content + facet coherence gate. `minCoherence` is deliberately low:
|
|
88
|
+
* domain + one object match is enough, but CHAGEE/genome/CSS drift is not.
|
|
89
|
+
*/
|
|
90
|
+
export function filterRelevantSources<T extends SourceLike>(
|
|
91
|
+
sources: T[],
|
|
92
|
+
latticeQuery: LatticeQuery,
|
|
93
|
+
minCoherence = 0.2,
|
|
94
|
+
): { accepted: T[]; rejected: Array<{ source: T; reason: string; coherence: number }> } {
|
|
95
|
+
const accepted: T[] = [];
|
|
96
|
+
const rejected: Array<{ source: T; reason: string; coherence: number }> = [];
|
|
97
|
+
for (const source of sources) {
|
|
98
|
+
const text = sourceText(source);
|
|
99
|
+
const coherence = facetCoherence(latticeQuery, text);
|
|
100
|
+
let reason = "";
|
|
101
|
+
if (source.quarantined || source.hardQuarantined) reason = "upstream-quarantined";
|
|
102
|
+
else if (!absoluteUrl(source)) reason = "non-resolvable-url";
|
|
103
|
+
else if (looksLikeMarkupBoilerplate(text)) reason = "boilerplate";
|
|
104
|
+
else if (text.trim().length < 80) reason = "thin-content";
|
|
105
|
+
else if (!hasAxisSignal(latticeQuery, text) || isLikelyDriftResult(text, latticeQuery) || coherence < minCoherence) reason = "facet-drift";
|
|
106
|
+
if (reason) rejected.push({ source, reason, coherence });
|
|
107
|
+
else accepted.push(source);
|
|
108
|
+
}
|
|
109
|
+
return { accepted, rejected };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Accept a source if it coheres with ANY dispatched lattice query. */
|
|
113
|
+
export function filterRelevantSourcesAny<T extends SourceLike>(
|
|
114
|
+
sources: T[],
|
|
115
|
+
queries: LatticeQuery[],
|
|
116
|
+
minCoherence = 0.2,
|
|
117
|
+
): { accepted: T[]; rejected: Array<{ source: T; reason: string; coherence: number }> } {
|
|
118
|
+
const accepted: T[] = [];
|
|
119
|
+
const rejected: Array<{ source: T; reason: string; coherence: number }> = [];
|
|
120
|
+
for (const source of sources) {
|
|
121
|
+
const attempts = queries.map((q) => filterRelevantSources([source], q, minCoherence));
|
|
122
|
+
if (attempts.some((a) => a.accepted.length === 1)) accepted.push(source);
|
|
123
|
+
else {
|
|
124
|
+
const reasons = attempts.flatMap((a) => a.rejected);
|
|
125
|
+
const best = reasons.sort((a, b) => b.coherence - a.coherence)[0];
|
|
126
|
+
rejected.push(best ?? { source, reason: "facet-drift", coherence: 0 });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { accepted, rejected };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function dedupeSources<T extends SourceLike>(sources: T[]): T[] {
|
|
133
|
+
const seen = new Set<string>();
|
|
134
|
+
const out: T[] = [];
|
|
135
|
+
for (const source of sources) {
|
|
136
|
+
const key = absoluteUrl(source).toLowerCase();
|
|
137
|
+
if (!key || seen.has(key)) continue;
|
|
138
|
+
seen.add(key);
|
|
139
|
+
out.push(source);
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|