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,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Oracle Adapters — gate-honest stubs for the 8 non-text-detectable
|
|
3
|
+
* failure classes. Each registers with NO transport by default (honest
|
|
4
|
+
* 'unavailable'); the host wires real transports per environment.
|
|
5
|
+
*/
|
|
6
|
+
import { registerOracle, extractCodeBlocks, type OraclePayload } from "./oracle-registry";
|
|
7
|
+
import type { ScanContext } from "./flaw-registry";
|
|
8
|
+
|
|
9
|
+
const COMPILED_LANGS = /^(ts|tsx|typescript|js|jsx|javascript|rust|rs|go|cpp|c\+\+|cc|cxx|c|swift|java|kotlin|python|py)$/i;
|
|
10
|
+
|
|
11
|
+
export function registerBuiltinOracles(): void {
|
|
12
|
+
// 1. COMPILER / LINTER HARNESS — type errors, concurrency, version semantics.
|
|
13
|
+
registerOracle({
|
|
14
|
+
id: "oracle.compiler", kind: "compiler",
|
|
15
|
+
description: "Compiles/lints extracted code (tsc, rustc, go vet, swiftc, mypy) to catch type/concurrency/version errors regex cannot.",
|
|
16
|
+
appliesTo: (p) => p.codeBlocks.some(b => COMPILED_LANGS.test(b.lang)),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 2. SAST — taint/dataflow (SQLi, XSS, SSRF, command injection).
|
|
20
|
+
registerOracle({
|
|
21
|
+
id: "oracle.sast", kind: "sast",
|
|
22
|
+
description: "Static analysis (Semgrep/CodeQL) for taint flows — full dataflow that single-function regex cannot trace.",
|
|
23
|
+
appliesTo: (p) => p.codeBlocks.length > 0,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 3. SCA / CVE — known-vulnerable dependencies + slopsquat existence check.
|
|
27
|
+
registerOracle({
|
|
28
|
+
id: "oracle.sca-cve", kind: "sca-cve",
|
|
29
|
+
description: "Software Composition Analysis: queries OSV/NVD for CVEs and verifies package existence (anti-slopsquat) in detected manifests.",
|
|
30
|
+
appliesTo: (p) => (p.manifests?.length ?? 0) > 0 || /\b(npm|pip|cargo|go)\s+(install|add|get)\b/i.test(p.answer),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 4. LICENSE — dependency license policy conflicts.
|
|
34
|
+
registerOracle({
|
|
35
|
+
id: "oracle.license", kind: "license",
|
|
36
|
+
description: "License compliance scan against a policy (e.g. no GPL in proprietary).",
|
|
37
|
+
appliesTo: (p) => (p.manifests?.length ?? 0) > 0,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 5. NLI — cross-paragraph semantic contradiction / entailment.
|
|
41
|
+
registerOracle({
|
|
42
|
+
id: "oracle.nli", kind: "nli",
|
|
43
|
+
description: "Natural Language Inference model for non-adjacent contradiction / multi-hop logical invalidity beyond regex.",
|
|
44
|
+
appliesTo: (_p, ctx: ScanContext) => ctx.answer.length > 400,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 6. SPAN-FABRICATION — quote/citation fabrication vs retrieved sources.
|
|
48
|
+
registerOracle({
|
|
49
|
+
id: "oracle.span-fabrication", kind: "span-fabrication",
|
|
50
|
+
description: "Fuzzy span-match of quoted text against retrieved source documents to detect fabricated quotes.",
|
|
51
|
+
appliesTo: (p) => /"[^"]{20,}"/.test(p.answer) && (p.sources?.length ?? 0) > 0,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 7. SANDBOX — execution effects (FS writes, process spawn, network egress).
|
|
55
|
+
registerOracle({
|
|
56
|
+
id: "oracle.sandbox", kind: "sandbox",
|
|
57
|
+
description: "Sandboxed execution + ASAN/TSAN for memory safety, data races, unauthorized FS/network effects.",
|
|
58
|
+
appliesTo: (p) => p.codeBlocks.some(b => /^(c|cpp|c\+\+|rust|rs|go)$/i.test(b.lang)),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 8. FORMAL — bounded model checking / theorem proving for safety properties.
|
|
62
|
+
registerOracle({
|
|
63
|
+
id: "oracle.formal", kind: "formal",
|
|
64
|
+
description: "Bounded model checker (CBMC/KLEE) or SMT (Z3) to verify stated safety invariants on critical paths.",
|
|
65
|
+
appliesTo: (_p, ctx: ScanContext) => /\b(invariant|safety property|must never|formally verif|prove that|assert)\b/i.test(ctx.prompt),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oracle Registry — Deterministic / External Verification Layer.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors flaw-registry but for ASYNC, environment-gated oracles that cover
|
|
5
|
+
* failure modes the lexical (text-only) layer cannot reach:
|
|
6
|
+
* compiler/linter, SAST taint, SBOM/CVE, license, NLI contradiction,
|
|
7
|
+
* source-span fabrication, sandboxed execution, formal verification.
|
|
8
|
+
*
|
|
9
|
+
* HONESTY KERNEL (from turn-1 §1 EETT / §E2 / §E3 / §153 EGS):
|
|
10
|
+
* - An oracle NEVER fabricates a result. If its transport is not wired,
|
|
11
|
+
* it returns an honest `*_UNAVAILABLE` info issue (gate honesty).
|
|
12
|
+
* - Every real verdict carries provenance (tool, version, hashes via meta).
|
|
13
|
+
* - Oracles are opt-in: host supplies transports; default = SIMULATED/UNAVAILABLE.
|
|
14
|
+
*/
|
|
15
|
+
import type { GuardIssue } from "./universal-rigor-guard";
|
|
16
|
+
import type { ScanContext, ScanSource } from "./flaw-registry";
|
|
17
|
+
|
|
18
|
+
export type OracleKind =
|
|
19
|
+
| "compiler" | "sast" | "sca-cve" | "license"
|
|
20
|
+
| "nli" | "span-fabrication" | "sandbox" | "formal" | "custom";
|
|
21
|
+
|
|
22
|
+
export type OracleStatus = "verified" | "failed" | "unavailable" | "simulated" | "error";
|
|
23
|
+
|
|
24
|
+
export interface OracleResult {
|
|
25
|
+
oracleId: string;
|
|
26
|
+
kind: OracleKind;
|
|
27
|
+
status: OracleStatus;
|
|
28
|
+
/** Issues to merge into the guard's GuardIssue stream (already shaped). */
|
|
29
|
+
issues: GuardIssue[];
|
|
30
|
+
/** Provenance for audit (tool, version, exit code, hashes). */
|
|
31
|
+
meta?: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Transport: host-supplied async callable that actually performs the side-effect
|
|
36
|
+
* (spawning a compiler, calling Semgrep, querying OSV, running an NLI model).
|
|
37
|
+
* If absent, the oracle degrades to `unavailable`.
|
|
38
|
+
*/
|
|
39
|
+
export type OracleTransport = (payload: OraclePayload) => Promise<OracleTransportResult>;
|
|
40
|
+
|
|
41
|
+
export interface OraclePayload {
|
|
42
|
+
prompt: string;
|
|
43
|
+
answer: string;
|
|
44
|
+
/** Extracted code blocks (lang + body) for compiler/SAST/sandbox oracles. */
|
|
45
|
+
codeBlocks: { lang: string; code: string }[];
|
|
46
|
+
/** Retrieved source documents for span-fabrication / NLI grounding. */
|
|
47
|
+
sources?: ScanSource[];
|
|
48
|
+
/** Dependency manifests detected in the answer (package.json, requirements). */
|
|
49
|
+
manifests?: { kind: string; raw: string }[];
|
|
50
|
+
meta?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface OracleTransportResult {
|
|
54
|
+
status: OracleStatus;
|
|
55
|
+
issues: GuardIssue[];
|
|
56
|
+
meta?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Oracle {
|
|
60
|
+
id: string;
|
|
61
|
+
kind: OracleKind;
|
|
62
|
+
description?: string;
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
/** Gate: skip when irrelevant (e.g. no code blocks for a compiler oracle). */
|
|
65
|
+
appliesTo?: (p: OraclePayload, ctx: ScanContext) => boolean;
|
|
66
|
+
/** Host-wired transport. Undefined ⇒ oracle reports `unavailable` honestly. */
|
|
67
|
+
transport?: OracleTransport;
|
|
68
|
+
/** Optional timeout (ms) for the transport call. */
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ORACLES = new Map<string, Oracle>();
|
|
73
|
+
|
|
74
|
+
export function registerOracle(o: Oracle): void {
|
|
75
|
+
ORACLES.set(o.id, { enabled: true, timeoutMs: 30_000, ...o });
|
|
76
|
+
}
|
|
77
|
+
export function registerOracles(os: Oracle[]): void { os.forEach(registerOracle); }
|
|
78
|
+
export function setOracleEnabled(id: string, on: boolean): void {
|
|
79
|
+
const o = ORACLES.get(id); if (o) o.enabled = on;
|
|
80
|
+
}
|
|
81
|
+
export function setOracleTransport(id: string, transport: OracleTransport): void {
|
|
82
|
+
const o = ORACLES.get(id); if (o) o.transport = transport;
|
|
83
|
+
}
|
|
84
|
+
export function listOracles(): { id: string; kind: OracleKind; wired: boolean; enabled: boolean }[] {
|
|
85
|
+
return [...ORACLES.values()].map(o => ({ id: o.id, kind: o.kind, wired: !!o.transport, enabled: o.enabled !== false }));
|
|
86
|
+
}
|
|
87
|
+
export function clearOracles(): void { ORACLES.clear(); }
|
|
88
|
+
|
|
89
|
+
// ── Code-block extraction (shared util) ─────────────────────────────────────
|
|
90
|
+
export function extractCodeBlocks(answer: string): { lang: string; code: string }[] {
|
|
91
|
+
const out: { lang: string; code: string }[] = [];
|
|
92
|
+
const re = /```([a-zA-Z0-9_+-]*)\n([\s\S]*?)```/g;
|
|
93
|
+
let m: RegExpExecArray | null;
|
|
94
|
+
while ((m = re.exec(answer)) !== null) {
|
|
95
|
+
out.push({ lang: (m[1] || "text").toLowerCase(), code: m[2] });
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function detectManifests(answer: string): { kind: string; raw: string }[] {
|
|
101
|
+
const out: { kind: string; raw: string }[] = [];
|
|
102
|
+
for (const b of extractCodeBlocks(answer)) {
|
|
103
|
+
if (/"dependencies"\s*:/.test(b.code) || /"devDependencies"\s*:/.test(b.code)) out.push({ kind: "npm", raw: b.code });
|
|
104
|
+
if (/^\s*[\w.-]+(==|>=|~=)\d/m.test(b.code)) out.push({ kind: "pip", raw: b.code });
|
|
105
|
+
if (/^\s*\[dependencies\]/m.test(b.code)) out.push({ kind: "cargo", raw: b.code });
|
|
106
|
+
if (/^\s*require\s+\(/m.test(b.code) || /^\s*go\s+\d+\.\d+/m.test(b.code)) out.push({ kind: "gomod", raw: b.code });
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const id = setTimeout(() => reject(new Error(`oracle timeout ${ms}ms`)), ms);
|
|
114
|
+
p.then(v => { clearTimeout(id); resolve(v); }).catch(e => { clearTimeout(id); reject(e); });
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Run all enabled, applicable oracles. Builds the payload from the ScanContext.
|
|
120
|
+
* Returns merged results. Unwired oracles report honest `unavailable`.
|
|
121
|
+
*/
|
|
122
|
+
export async function runOracles(ctx: ScanContext): Promise<OracleResult[]> {
|
|
123
|
+
const codeBlocks = extractCodeBlocks(ctx.answer);
|
|
124
|
+
const manifests = detectManifests(ctx.answer);
|
|
125
|
+
const payload: OraclePayload = {
|
|
126
|
+
prompt: ctx.prompt, answer: ctx.answer, codeBlocks,
|
|
127
|
+
sources: ctx.sources, manifests, meta: ctx.meta,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const tasks = [...ORACLES.values()]
|
|
131
|
+
.filter(o => o.enabled !== false)
|
|
132
|
+
.filter(o => !o.appliesTo || o.appliesTo(payload, ctx))
|
|
133
|
+
.map(async (o): Promise<OracleResult> => {
|
|
134
|
+
if (!o.transport) {
|
|
135
|
+
return {
|
|
136
|
+
oracleId: o.id, kind: o.kind, status: "unavailable",
|
|
137
|
+
issues: [{
|
|
138
|
+
severity: "info", code: `ORACLE_UNAVAILABLE_${o.kind.toUpperCase().replace(/-/g, "_")}`,
|
|
139
|
+
message: `Oracle '${o.id}' (${o.kind}) is not wired in this environment — ${o.description ?? "deterministic verification skipped"}. Failure modes in this class are NOT covered by text-only scanning.`,
|
|
140
|
+
remediation: `Wire a transport for '${o.id}' via setOracleTransport() to enable ${o.kind} verification.`,
|
|
141
|
+
}],
|
|
142
|
+
meta: { wired: false },
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const r = await withTimeout(o.transport(payload), o.timeoutMs ?? 30_000);
|
|
147
|
+
return { oracleId: o.id, kind: o.kind, status: r.status, issues: r.issues, meta: { wired: true, ...r.meta } };
|
|
148
|
+
} catch (err: any) {
|
|
149
|
+
return {
|
|
150
|
+
oracleId: o.id, kind: o.kind, status: "error",
|
|
151
|
+
issues: [{ severity: "info", code: "ORACLE_ERROR", message: `Oracle '${o.id}' threw: ${err?.message ?? "unknown"}` }],
|
|
152
|
+
meta: { wired: true, error: true },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return Promise.all(tasks);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Flatten oracle results into the GuardIssue stream for unified scoring. */
|
|
161
|
+
export function oracleResultsToIssues(results: OracleResult[]): GuardIssue[] {
|
|
162
|
+
return results.flatMap(r => r.issues.map(i => ({ ...i, code: i.code })));
|
|
163
|
+
}
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
// High-level research orchestrator.
|
|
2
|
+
// Steps:
|
|
3
|
+
// 1) Use Gemini/Gemma to generate K candidate hypotheses
|
|
4
|
+
// 2) Use SerpAPI and/or Jina Search to gather evidence URLs
|
|
5
|
+
// 3) Optional: Jina Reader to fetch passages
|
|
6
|
+
// 4) Use Gemini/Gemma + Jina rerank to score (support/refute/silent + strength) per hypothesis
|
|
7
|
+
// 5) Feed scored Evidence items into the BeliefSearch engine
|
|
8
|
+
// 6) When SPRT commits (or budget exhausts), synthesize the final answer
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
applyEvidence,
|
|
12
|
+
newState,
|
|
13
|
+
posterior,
|
|
14
|
+
forceCommit,
|
|
15
|
+
setAnchor,
|
|
16
|
+
} from "./gbse/engine";
|
|
17
|
+
import { Verdict, type BeliefState, type Evidence, type EngineConfig } from "./gbse/types";
|
|
18
|
+
import { defaultConfig } from "./gbse/config";
|
|
19
|
+
import { TIERS, pickTier } from "./gbse/tiers";
|
|
20
|
+
import { RelevanceGraph } from "./gbse/graph";
|
|
21
|
+
import {
|
|
22
|
+
geminiGenerate,
|
|
23
|
+
geminiGenerateJSON,
|
|
24
|
+
type GeminiModel,
|
|
25
|
+
} from "./connectors/gemini";
|
|
26
|
+
import { jinaRerank, jinaSearch, type JinaSearchResult } from "./connectors/jina";
|
|
27
|
+
import { serpapiSearch, type SerpResult } from "./connectors/serpapi";
|
|
28
|
+
|
|
29
|
+
export interface ApiKeys {
|
|
30
|
+
gemini?: string;
|
|
31
|
+
jina?: string;
|
|
32
|
+
serpapi?: string;
|
|
33
|
+
serpapiProxy?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type SearchProvider = "serpapi" | "jina";
|
|
37
|
+
|
|
38
|
+
export interface OrchestratorOptions {
|
|
39
|
+
query: string;
|
|
40
|
+
keys: ApiKeys;
|
|
41
|
+
hypothesisModel: GeminiModel;
|
|
42
|
+
scoringModel: GeminiModel;
|
|
43
|
+
searchProvider: SearchProvider;
|
|
44
|
+
useJinaRerank: boolean;
|
|
45
|
+
tierOverride?: number;
|
|
46
|
+
onEvent: (evt: TraceEvt) => void;
|
|
47
|
+
onState?: (state: BeliefState, graph: RelevanceGraph, totalTokens: number) => void;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface TraceEvt {
|
|
52
|
+
ts: number;
|
|
53
|
+
phase: string;
|
|
54
|
+
level: "info" | "warn" | "error" | "ok";
|
|
55
|
+
message: string;
|
|
56
|
+
data?: unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RunResult {
|
|
60
|
+
state: BeliefState;
|
|
61
|
+
graph: RelevanceGraph;
|
|
62
|
+
finalAnswer: string;
|
|
63
|
+
totalTokens: number;
|
|
64
|
+
config: EngineConfig;
|
|
65
|
+
tier: number;
|
|
66
|
+
tierReason: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface HypothesisGen {
|
|
70
|
+
hypotheses: string[];
|
|
71
|
+
priors?: number[];
|
|
72
|
+
key_entities?: string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface EvidenceScore {
|
|
76
|
+
verdict: "support" | "refute" | "silent";
|
|
77
|
+
strength: number;
|
|
78
|
+
reliability: number;
|
|
79
|
+
rationale: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// --------- helpers ---------
|
|
83
|
+
|
|
84
|
+
function emit(opts: OrchestratorOptions, e: Omit<TraceEvt, "ts">): void {
|
|
85
|
+
opts.onEvent({ ts: Date.now(), ...e });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function safeUrlSourceId(url: string): string {
|
|
89
|
+
try {
|
|
90
|
+
const u = new URL(url);
|
|
91
|
+
return u.hostname.replace(/^www\./, "");
|
|
92
|
+
} catch {
|
|
93
|
+
return url.slice(0, 64);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Map a generic search hit shape to a uniform `SearchHit`.
|
|
98
|
+
export interface SearchHit {
|
|
99
|
+
title: string;
|
|
100
|
+
link: string;
|
|
101
|
+
snippet: string;
|
|
102
|
+
source?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function fromSerp(r: SerpResult): SearchHit {
|
|
106
|
+
return { title: r.title, link: r.link, snippet: r.snippet, source: r.source };
|
|
107
|
+
}
|
|
108
|
+
function fromJina(r: JinaSearchResult): SearchHit {
|
|
109
|
+
return { title: r.title, link: r.url, snippet: r.description, source: safeUrlSourceId(r.url) };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --------- the run ---------
|
|
113
|
+
|
|
114
|
+
export async function runResearch(opts: OrchestratorOptions): Promise<RunResult> {
|
|
115
|
+
if (!opts.keys.gemini) throw new Error("Gemini API key is required (used for hypothesis generation and scoring).");
|
|
116
|
+
|
|
117
|
+
const { tier, reason: tierReason } = opts.tierOverride !== undefined
|
|
118
|
+
? { tier: opts.tierOverride, reason: "manual override" }
|
|
119
|
+
: pickTier(opts.query, 0);
|
|
120
|
+
const spec = TIERS[Math.max(0, Math.min(5, tier))];
|
|
121
|
+
emit(opts, { phase: "tier", level: "ok", message: `Selected ${spec.name} — ${tierReason}`, data: spec });
|
|
122
|
+
|
|
123
|
+
const cfg = defaultConfig({
|
|
124
|
+
maxTokens: spec.maxTokens,
|
|
125
|
+
maxSeconds: spec.maxSeconds,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
let totalTokens = 0;
|
|
129
|
+
|
|
130
|
+
// Tier 0 fast-path: simple query + high anchor coverage = skip hypothesis loop
|
|
131
|
+
if (tier === 0 && opts.keys.jina) {
|
|
132
|
+
emit(opts, { phase: "tier0", level: "ok", message: "Tier 0 fast-path: direct synthesis from anchor" });
|
|
133
|
+
const anchorHits = await jinaSearch(opts.query, { apiKey: opts.keys.jina, count: 5, signal: opts.signal });
|
|
134
|
+
const combinedLen = anchorHits.reduce((s, h) => s + (h.content?.length ?? 0), 0);
|
|
135
|
+
if (anchorHits.length >= 2 && combinedLen > 800) {
|
|
136
|
+
const response = await geminiGenerate(
|
|
137
|
+
`Answer the user's question using ONLY the retrieved context below. If insufficient, say so honestly.\n\nQuestion: ${opts.query}\n\nContext:\n${anchorHits.map((h, i) => `[Source ${i+1}] ${h.title}\n${h.content}`).join("\n\n---\n\n")}`,
|
|
138
|
+
{ model: opts.scoringModel, apiKey: opts.keys.gemini, maxOutputTokens: 1024, temperature: 0.2, signal: opts.signal }
|
|
139
|
+
);
|
|
140
|
+
totalTokens += response.totalTokens;
|
|
141
|
+
const graph = new RelevanceGraph(cfg.graph);
|
|
142
|
+
graph.addNode("__query__", opts.query.slice(0, 80), 1.0);
|
|
143
|
+
const state = newState(["Direct answer from anchor"], [1.0], cfg);
|
|
144
|
+
setAnchor(state, Object.keys(state.hyps)[0], Verdict.SUPPORT, 0.6, cfg);
|
|
145
|
+
return { state, graph, finalAnswer: response.text, totalTokens, config: cfg, tier, tierReason };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------- Phase 1: Hypothesis generation ----------
|
|
150
|
+
emit(opts, { phase: "hypotheses", level: "info", message: `Generating ${spec.hypothesisCount || 4} candidate hypotheses with ${opts.hypothesisModel}…` });
|
|
151
|
+
const hypPrompt = `You are a Bayesian researcher. For the user's question, propose ${spec.hypothesisCount || 4} DISTINCT, mutually-competing hypothesis answers. Each must be a single declarative sentence. Then list 3-6 key entities/concepts.
|
|
152
|
+
|
|
153
|
+
Question: """${opts.query}"""
|
|
154
|
+
|
|
155
|
+
JSON schema:
|
|
156
|
+
{
|
|
157
|
+
"hypotheses": ["..."], // ${spec.hypothesisCount || 4} items, competing, non-overlapping
|
|
158
|
+
"priors": [0.5, ...], // same length, in [0.01, 0.99]
|
|
159
|
+
"key_entities": ["..."]
|
|
160
|
+
}`;
|
|
161
|
+
const { value: hypJson, usage: hypUsage } = await geminiGenerateJSON<HypothesisGen>(hypPrompt, {
|
|
162
|
+
model: opts.hypothesisModel,
|
|
163
|
+
apiKey: opts.keys.gemini,
|
|
164
|
+
maxOutputTokens: 1024,
|
|
165
|
+
temperature: 0.4,
|
|
166
|
+
signal: opts.signal,
|
|
167
|
+
});
|
|
168
|
+
totalTokens += hypUsage.totalTokens;
|
|
169
|
+
if (!hypJson || !Array.isArray(hypJson.hypotheses) || hypJson.hypotheses.length === 0) {
|
|
170
|
+
throw new Error("Hypothesis generator returned no valid hypotheses. Raw output: " + hypUsage.text.slice(0, 200));
|
|
171
|
+
}
|
|
172
|
+
const hypotheses = hypJson.hypotheses.slice(0, 12);
|
|
173
|
+
const priors = (hypJson.priors ?? hypotheses.map(() => 0.5)).slice(0, hypotheses.length);
|
|
174
|
+
const entities = (hypJson.key_entities ?? []).slice(0, 8);
|
|
175
|
+
emit(opts, { phase: "hypotheses", level: "ok", message: `${hypotheses.length} hypotheses; ${entities.length} entities`, data: { hypotheses, entities } });
|
|
176
|
+
|
|
177
|
+
// ---------- Phase 2: Construct belief state + graph ----------
|
|
178
|
+
const state = newState(hypotheses, priors, cfg);
|
|
179
|
+
const graph = new RelevanceGraph(cfg.graph);
|
|
180
|
+
graph.addNode("__query__", `Q: ${opts.query.slice(0, 80)}`, 1.0);
|
|
181
|
+
for (const hid of Object.keys(state.hyps)) {
|
|
182
|
+
graph.addNode(hid, state.hyps[hid].text.slice(0, 80), 0.5);
|
|
183
|
+
graph.addEdge("__query__", hid, +1.0);
|
|
184
|
+
}
|
|
185
|
+
for (const e of entities) {
|
|
186
|
+
graph.addNode(`E:${e}`, e, 0.4);
|
|
187
|
+
graph.addEdge("__query__", `E:${e}`, +0.6);
|
|
188
|
+
}
|
|
189
|
+
emit(opts, { phase: "state", level: "ok", message: `Belief state ${state.sid} initialized.` });
|
|
190
|
+
opts.onState?.(state, graph, totalTokens);
|
|
191
|
+
|
|
192
|
+
// ---------- Phase 3: Evidence gathering ----------
|
|
193
|
+
const probesPerHyp = Math.max(2, Math.ceil(spec.evidenceProbes / Math.max(1, hypotheses.length)));
|
|
194
|
+
emit(opts, { phase: "search", level: "info", message: `Gathering evidence via ${opts.searchProvider} (~${probesPerHyp} probes / hypothesis)…` });
|
|
195
|
+
|
|
196
|
+
const hids = Object.keys(state.hyps);
|
|
197
|
+
for (const hid of hids) {
|
|
198
|
+
if (opts.signal?.aborted) break;
|
|
199
|
+
if (state.committed) break;
|
|
200
|
+
const h = state.hyps[hid];
|
|
201
|
+
if (!h.alive) continue;
|
|
202
|
+
|
|
203
|
+
// Build a targeted search query: hypothesis claim + key entities.
|
|
204
|
+
const probeQuery = `${h.text} ${entities.slice(0, 3).join(" ")}`.slice(0, 256);
|
|
205
|
+
let hits: SearchHit[] = [];
|
|
206
|
+
try {
|
|
207
|
+
if (opts.searchProvider === "serpapi") {
|
|
208
|
+
if (!opts.keys.serpapi) throw new Error("SerpAPI key missing");
|
|
209
|
+
const r = await serpapiSearch(probeQuery, {
|
|
210
|
+
apiKey: opts.keys.serpapi,
|
|
211
|
+
proxyBase: opts.keys.serpapiProxy,
|
|
212
|
+
num: probesPerHyp + 2,
|
|
213
|
+
signal: opts.signal,
|
|
214
|
+
});
|
|
215
|
+
hits = r.map(fromSerp);
|
|
216
|
+
} else {
|
|
217
|
+
if (!opts.keys.jina) throw new Error("Jina API key missing");
|
|
218
|
+
const r = await jinaSearch(probeQuery, {
|
|
219
|
+
apiKey: opts.keys.jina,
|
|
220
|
+
count: probesPerHyp + 2,
|
|
221
|
+
signal: opts.signal,
|
|
222
|
+
});
|
|
223
|
+
hits = r.map(fromJina);
|
|
224
|
+
}
|
|
225
|
+
} catch (err) {
|
|
226
|
+
emit(opts, { phase: "search", level: "error", message: `Search failed for hypothesis: ${(err as Error).message}` });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
emit(opts, { phase: "search", level: "ok", message: `${hits.length} hits for "${h.text.slice(0, 50)}…"` });
|
|
230
|
+
|
|
231
|
+
// Optional rerank with Jina
|
|
232
|
+
if (opts.useJinaRerank && opts.keys.jina && hits.length > 1) {
|
|
233
|
+
try {
|
|
234
|
+
const docs = hits.map((h) => `${h.title}\n${h.snippet}`);
|
|
235
|
+
const reranked = await jinaRerank(probeQuery, docs, { apiKey: opts.keys.jina, topN: Math.min(hits.length, probesPerHyp + 1), signal: opts.signal });
|
|
236
|
+
hits = reranked.map((r) => hits[r.index]).filter(Boolean);
|
|
237
|
+
emit(opts, { phase: "rerank", level: "ok", message: `Jina rerank → top ${hits.length}` });
|
|
238
|
+
} catch (err) {
|
|
239
|
+
emit(opts, { phase: "rerank", level: "warn", message: `Rerank failed (continuing): ${(err as Error).message}` });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Score each hit with the scoring model (judge model)
|
|
244
|
+
for (const hit of hits.slice(0, probesPerHyp)) {
|
|
245
|
+
if (opts.signal?.aborted) break;
|
|
246
|
+
if (state.committed) break;
|
|
247
|
+
const scorePrompt = `You are a strict Bayesian judge. Decide whether the SOURCE supports, refutes, or is silent on the HYPOTHESIS. Return ONLY this JSON:
|
|
248
|
+
{
|
|
249
|
+
"verdict": "support" | "refute" | "silent",
|
|
250
|
+
"strength": number in [0,1], // 0=ambiguous, 1=decisive
|
|
251
|
+
"reliability": number in [0,1], // trust in the source quality
|
|
252
|
+
"rationale": "<= 30 words"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
HYPOTHESIS: ${h.text}
|
|
256
|
+
SOURCE TITLE: ${hit.title}
|
|
257
|
+
SOURCE URL: ${hit.link}
|
|
258
|
+
SOURCE EXCERPT: ${hit.snippet}`;
|
|
259
|
+
try {
|
|
260
|
+
const { value, usage } = await geminiGenerateJSON<EvidenceScore>(scorePrompt, {
|
|
261
|
+
model: opts.scoringModel,
|
|
262
|
+
apiKey: opts.keys.gemini,
|
|
263
|
+
maxOutputTokens: 256,
|
|
264
|
+
temperature: 0.0,
|
|
265
|
+
signal: opts.signal,
|
|
266
|
+
});
|
|
267
|
+
totalTokens += usage.totalTokens;
|
|
268
|
+
if (!value) {
|
|
269
|
+
emit(opts, { phase: "score", level: "warn", message: `Skipped: judge returned invalid JSON.` });
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const verdict =
|
|
273
|
+
value.verdict === "support" ? Verdict.SUPPORT :
|
|
274
|
+
value.verdict === "refute" ? Verdict.REFUTE : Verdict.SILENT;
|
|
275
|
+
const ev: Evidence = {
|
|
276
|
+
sourceId: safeUrlSourceId(hit.link),
|
|
277
|
+
text: `${hit.title} — ${value.rationale}`,
|
|
278
|
+
verdict,
|
|
279
|
+
reliability: Math.max(0, Math.min(1, value.reliability)),
|
|
280
|
+
strength: Math.max(0, Math.min(1, value.strength)),
|
|
281
|
+
tokenCost: usage.totalTokens,
|
|
282
|
+
url: hit.link,
|
|
283
|
+
ts: Date.now(),
|
|
284
|
+
};
|
|
285
|
+
applyEvidence(state, hid, ev, cfg);
|
|
286
|
+
|
|
287
|
+
// Update graph: add the source as a node, sign by verdict.
|
|
288
|
+
const nodeId = `S:${ev.sourceId}:${hit.link.slice(-12)}`;
|
|
289
|
+
graph.addNode(nodeId, hit.title.slice(0, 64), ev.reliability);
|
|
290
|
+
const signed = verdict === Verdict.SUPPORT ? +ev.strength :
|
|
291
|
+
verdict === Verdict.REFUTE ? -ev.strength : 0;
|
|
292
|
+
if (signed !== 0) graph.addEdge(hid, nodeId, signed);
|
|
293
|
+
|
|
294
|
+
emit(opts, {
|
|
295
|
+
phase: "evidence",
|
|
296
|
+
level: verdict === Verdict.SUPPORT ? "ok" : verdict === Verdict.REFUTE ? "warn" : "info",
|
|
297
|
+
message: `[${verdict.toUpperCase()}] strength=${value.strength.toFixed(2)} rel=${value.reliability.toFixed(2)} for "${h.text.slice(0, 40)}…" — ${value.rationale}`,
|
|
298
|
+
data: { hid, ev, hit, posterior: posterior(state) },
|
|
299
|
+
});
|
|
300
|
+
opts.onState?.(state, graph, totalTokens);
|
|
301
|
+
|
|
302
|
+
if (state.committed) {
|
|
303
|
+
emit(opts, { phase: "commit", level: "ok", message: `SPRT committed → ${state.committed} (${state.commitReason})` });
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
} catch (err) {
|
|
307
|
+
emit(opts, { phase: "score", level: "error", message: `Judge call failed: ${(err as Error).message}` });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---------- Phase 4: Final synthesis ----------
|
|
313
|
+
if (!state.committed) {
|
|
314
|
+
forceCommit(state);
|
|
315
|
+
emit(opts, { phase: "commit", level: "warn", message: `Budget/probes exhausted — forced commit: ${state.committed}` });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const synthPrompt = `You are a careful synthesizer. Use ONLY the surviving hypothesis and the evidence below. If support is weak, say so.
|
|
319
|
+
|
|
320
|
+
Question: ${opts.query}
|
|
321
|
+
|
|
322
|
+
Leading hypothesis: ${state.committed ? state.hyps[state.committed].text : "(unknown)"}
|
|
323
|
+
Commit reason: ${state.commitReason}
|
|
324
|
+
|
|
325
|
+
Evidence considered:
|
|
326
|
+
${Object.values(state.hyps).map(h => `• [${h.hid}] ${h.text}\n${h.evidence.map(e => ` - [${e.verdict}] (${e.sourceId}) ${e.text}`).join("\n")}`).join("\n\n")}
|
|
327
|
+
|
|
328
|
+
Write:
|
|
329
|
+
1) A direct answer (2-4 sentences).
|
|
330
|
+
2) Key evidence (bulleted).
|
|
331
|
+
3) Uncertainties / caveats.
|
|
332
|
+
4) Next probes (if any).`;
|
|
333
|
+
|
|
334
|
+
const synth = await geminiGenerate(synthPrompt, {
|
|
335
|
+
model: opts.scoringModel,
|
|
336
|
+
apiKey: opts.keys.gemini,
|
|
337
|
+
maxOutputTokens: 1024,
|
|
338
|
+
temperature: 0.2,
|
|
339
|
+
systemInstruction: "Be precise. Cite the hypothesis and source IDs. Never invent facts not in the evidence.",
|
|
340
|
+
signal: opts.signal,
|
|
341
|
+
});
|
|
342
|
+
totalTokens += synth.totalTokens;
|
|
343
|
+
emit(opts, { phase: "synthesis", level: "ok", message: `Final synthesis ready (${synth.totalTokens} tokens)` });
|
|
344
|
+
|
|
345
|
+
// Anchor the committed hypothesis with the synthesis confidence (conservative).
|
|
346
|
+
if (state.committed) setAnchor(state, state.committed, Verdict.SUPPORT, 0.5, cfg);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
state,
|
|
350
|
+
graph,
|
|
351
|
+
finalAnswer: synth.text,
|
|
352
|
+
totalTokens,
|
|
353
|
+
config: cfg,
|
|
354
|
+
tier: spec.tier,
|
|
355
|
+
tierReason,
|
|
356
|
+
};
|
|
357
|
+
}
|