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,2015 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* innovation-genome-v3.ts — Innovation Genome Engine V3.0, verified discovery
|
|
3
|
+
* runtime. TypeScript port of the supplied `innovation_genome_v3.py`.
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* RUNTIME HONESTY (per task instructions — read before trusting any claim
|
|
6
|
+
* of "verbatim 1:1" below):
|
|
7
|
+
*
|
|
8
|
+
* Tools actually available in this session: file read/write/edit, a Vite
|
|
9
|
+
* build tool, static grep. No Python interpreter, no independent test
|
|
10
|
+
* runner, no network fetch, no theorem prover, no SQLite. Nothing below
|
|
11
|
+
* was executed against the original Python file — it could not be, because
|
|
12
|
+
* the Python source pasted into the task was itself already truncated
|
|
13
|
+
* (see §7 note) before it reached this file. Every claim of "1:1" is a
|
|
14
|
+
* claim about the TypeScript SOURCE CODE topology (same class/field names,
|
|
15
|
+
* same algorithms, same validation rules), verified by side-by-side reading
|
|
16
|
+
* of this file against the pasted spec — not by running both and diffing
|
|
17
|
+
* output, which is impossible without a Python runtime in this workspace.
|
|
18
|
+
*
|
|
19
|
+
* Sections 1–6 of the pasted spec (deterministic utilities, the P/A/E/N/V/T/S
|
|
20
|
+
* graph-life scheduler, GoalSpec/EvaluatorSpec/Candidate/Report dataclasses,
|
|
21
|
+
* adapter protocols, mutation policy + calibration, budget accounting) were
|
|
22
|
+
* supplied WITHOUT truncation. Those are ported line-for-line: same field
|
|
23
|
+
* names, same validation predicates, same hashing material, same thresholds.
|
|
24
|
+
*
|
|
25
|
+
* Section 7 onward (EventStore SQL bodies, ToolRegistry, the 7-D descriptor,
|
|
26
|
+
* the versioned archive, the GEPA-feedback mutation engine, the discovery
|
|
27
|
+
* engine, and `generate_v3_report`) arrived in the task prompt with the SQL
|
|
28
|
+
* query strings and several class/method BODIES already blanked out by
|
|
29
|
+
* whatever stripped the code before it reached me (e.g. `self.@7.@KC(,
|
|
30
|
+
* (row,))` — the query text itself is missing, only the call shape survives).
|
|
31
|
+
* I cannot invent the exact missing SQL and call it "verbatim" — that would
|
|
32
|
+
* be a fabrication. Instead: every CONTRACT that IS fully legible in that
|
|
33
|
+
* truncated region (event-hash chain material, `verify_chain` walk logic,
|
|
34
|
+
* checkpoint hashing, memory priority validation, the "Versioning Invariant"
|
|
35
|
+
* docstring, the 105-mechanism report shape actually printed by
|
|
36
|
+
* `generate_v3_report`, and the full CLI `main()` body) is reproduced
|
|
37
|
+
* exactly. Where only a docstring survived and the body did not (the
|
|
38
|
+
* subprocess tool adapter, the archive bucketing, the mutation engine, the
|
|
39
|
+
* discovery engine's internal loop), I implement the DESCRIBED BEHAVIOUR in
|
|
40
|
+
* full, functioning, non-mocked code, and say so explicitly in the comment
|
|
41
|
+
* directly above each such class. SQLite itself does not exist in a browser
|
|
42
|
+
* runtime at all (no filesystem, no native SQLite driver in a Vite SPA), so
|
|
43
|
+
* the EventStore below is disclosed, necessary substitution: an in-memory,
|
|
44
|
+
* hash-chained event log using the EXACT SAME hashing algorithm the spec
|
|
45
|
+
* gives for `append_event`/`verify_chain`, backed by `localStorage` so it
|
|
46
|
+
* survives reloads the same way the Python version's on-disk SQLite file
|
|
47
|
+
* would survive process restarts.
|
|
48
|
+
* ============================================================================ */
|
|
49
|
+
|
|
50
|
+
import {
|
|
51
|
+
DIMENSIONS,
|
|
52
|
+
compileInnovationPrompt,
|
|
53
|
+
seedToGenome,
|
|
54
|
+
type Genome,
|
|
55
|
+
} from '@/lib/innovation-genome-engine';
|
|
56
|
+
import {
|
|
57
|
+
DIMENSION_BLOCKS,
|
|
58
|
+
DOMAIN_PACKS,
|
|
59
|
+
CapabilityGate,
|
|
60
|
+
SafetyGate,
|
|
61
|
+
applyMutation,
|
|
62
|
+
classifyPersonaExtended,
|
|
63
|
+
selectPathExtended,
|
|
64
|
+
type DomainPack,
|
|
65
|
+
type RiskTier,
|
|
66
|
+
} from '@/lib/innovation-genome-engine-v2';
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
69
|
+
// 0. Synchronous SHA-256 (FIPS 180-4) — browsers have no synchronous
|
|
70
|
+
// crypto.subtle.digest, and content_hash is used as a *synchronous*
|
|
71
|
+
// getter throughout the Python spec (a @property), so a sync
|
|
72
|
+
// implementation is required for parity. Verified in diagnostics
|
|
73
|
+
// against the two published NIST test vectors (empty string and "abc").
|
|
74
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
75
|
+
const SHA256_K = new Uint32Array([
|
|
76
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
77
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
78
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
79
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
80
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
81
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
82
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
83
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
function rotr(x: number, n: number): number {
|
|
87
|
+
return (x >>> n) | (x << (32 - n));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function sha256Bytes(bytes: Uint8Array): Uint8Array {
|
|
91
|
+
let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
|
|
92
|
+
let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
|
|
93
|
+
|
|
94
|
+
const bitLen = bytes.length * 8;
|
|
95
|
+
const padded = new Uint8Array((bytes.length + 9 + 63) & ~63);
|
|
96
|
+
padded.set(bytes);
|
|
97
|
+
padded[bytes.length] = 0x80;
|
|
98
|
+
const view = new DataView(padded.buffer);
|
|
99
|
+
view.setUint32(padded.length - 4, bitLen >>> 0, false);
|
|
100
|
+
view.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000), false);
|
|
101
|
+
|
|
102
|
+
const w = new Uint32Array(64);
|
|
103
|
+
for (let chunk = 0; chunk < padded.length; chunk += 64) {
|
|
104
|
+
for (let i = 0; i < 16; i++) w[i] = view.getUint32(chunk + i * 4, false);
|
|
105
|
+
for (let i = 16; i < 64; i++) {
|
|
106
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3);
|
|
107
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
|
|
108
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
112
|
+
for (let i = 0; i < 64; i++) {
|
|
113
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
114
|
+
const ch = (e & f) ^ (~e & g);
|
|
115
|
+
const temp1 = (h + S1 + ch + SHA256_K[i] + w[i]) >>> 0;
|
|
116
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
117
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
118
|
+
const temp2 = (S0 + maj) >>> 0;
|
|
119
|
+
h = g; g = f; f = e; e = (d + temp1) >>> 0;
|
|
120
|
+
d = c; c = b; b = a; a = (temp1 + temp2) >>> 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0; h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0;
|
|
124
|
+
h4 = (h4 + e) >>> 0; h5 = (h5 + f) >>> 0; h6 = (h6 + g) >>> 0; h7 = (h7 + h) >>> 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const out = new Uint8Array(32);
|
|
128
|
+
const outView = new DataView(out.buffer);
|
|
129
|
+
[h0, h1, h2, h3, h4, h5, h6, h7].forEach((h, i) => outView.setUint32(i * 4, h, false));
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function bytesToHex(bytes: Uint8Array): string {
|
|
134
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
138
|
+
// 1. Deterministic utilities — verbatim port of §1
|
|
139
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
140
|
+
export const ZERO_HASH = "0".repeat(64);
|
|
141
|
+
export const STANDARD_METRICS = ["novelty", "utility", "tractability", "robustness", "taste"] as const;
|
|
142
|
+
export type StandardMetric = (typeof STANDARD_METRICS)[number];
|
|
143
|
+
|
|
144
|
+
export function utcNow(): string {
|
|
145
|
+
return new Date().toISOString();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function canonicalJson(value: unknown): string {
|
|
149
|
+
const canon = (v: unknown): string => {
|
|
150
|
+
if (v === null || v === undefined) return "null";
|
|
151
|
+
if (typeof v === "number") return Number.isFinite(v) ? String(v) : "null";
|
|
152
|
+
if (typeof v === "boolean") return v ? "true" : "false";
|
|
153
|
+
if (typeof v === "string") return JSON.stringify(v);
|
|
154
|
+
if (Array.isArray(v)) return `[${v.map(canon).join(",")}]`;
|
|
155
|
+
if (typeof v === "object") {
|
|
156
|
+
const record = v as Record<string, unknown>;
|
|
157
|
+
const keys = Object.keys(record).sort();
|
|
158
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canon(record[k])}`).join(",")}}`;
|
|
159
|
+
}
|
|
160
|
+
return "null";
|
|
161
|
+
};
|
|
162
|
+
return canon(value);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function sha256Text(value: string): string {
|
|
166
|
+
return bytesToHex(sha256Bytes(new TextEncoder().encode(value)));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* derive_seed(*parts) — Python takes the first 8 bytes of sha256 as a
|
|
171
|
+
* big-endian *64-bit* unsigned integer. JS `number` only safely represents
|
|
172
|
+
* 53 bits, and the downstream `seedToGenome(seed: number)` from the V1
|
|
173
|
+
* engine already expects a 32-bit-range seed. Disclosed adaptation: this
|
|
174
|
+
* function truncates to the low 32 bits of that 64-bit value so it is a
|
|
175
|
+
* valid argument everywhere V1/V2 expect a `number` seed. The full 64-bit
|
|
176
|
+
* value remains available via `deriveSeedBigInt` for anyone who needs exact
|
|
177
|
+
* parity with the Python integer.
|
|
178
|
+
*/
|
|
179
|
+
export function deriveSeedBigInt(...parts: unknown[]): bigint {
|
|
180
|
+
const material = parts.map((p) => String(p)).join("|");
|
|
181
|
+
const digest = sha256Bytes(new TextEncoder().encode(material));
|
|
182
|
+
let value = 0n;
|
|
183
|
+
for (let i = 0; i < 8; i++) value = (value << 8n) | BigInt(digest[i]);
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function deriveSeed(...parts: unknown[]): number {
|
|
188
|
+
return Number(deriveSeedBigInt(...parts) % 4294967296n);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function clamp01(value: number): number {
|
|
192
|
+
return Math.round(Math.max(0, Math.min(1, value)) * 10000) / 10000;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function dimensionIds(): readonly string[] {
|
|
196
|
+
return DIMENSIONS.map((d) => d.id);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function validateGenome(genome: Genome): void {
|
|
200
|
+
const expected = new Set(dimensionIds());
|
|
201
|
+
const actual = new Set(Object.keys(genome));
|
|
202
|
+
|
|
203
|
+
const missing = [...expected].filter((k) => !actual.has(k)).sort();
|
|
204
|
+
const extra = [...actual].filter((k) => !expected.has(k)).sort();
|
|
205
|
+
|
|
206
|
+
if (missing.length) throw new Error(`Genome missing dimensions: ${JSON.stringify(missing)}`);
|
|
207
|
+
if (extra.length) throw new Error(`Genome has unknown dimensions: ${JSON.stringify(extra)}`);
|
|
208
|
+
|
|
209
|
+
for (const [name, value] of Object.entries(genome)) {
|
|
210
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
211
|
+
throw new TypeError(`Genome value ${JSON.stringify(name)} is not numeric`);
|
|
212
|
+
}
|
|
213
|
+
if (value < 0 || value > 1) {
|
|
214
|
+
throw new Error(`Genome value ${JSON.stringify(name)}=${value} outside [0,1]`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
220
|
+
// 2. Executable P/A/E/N/V/T/S graph-life scheduler — verbatim port of §2
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
222
|
+
export const NODE_LABELS: Readonly<Record<string, string>> = {
|
|
223
|
+
P: "Problem Choice and Framing",
|
|
224
|
+
A: "Anomaly Valuation",
|
|
225
|
+
E: "Embodiment and World Contact",
|
|
226
|
+
N: "Analogy and Representation Transfer",
|
|
227
|
+
V: "Evaluator Construction and Revision",
|
|
228
|
+
T: "Taste-Weighted Selection",
|
|
229
|
+
S: "Social Stabilization and Independent Review",
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const NODE_ORDER = Object.keys(NODE_LABELS); // insertion order, matches Python dict iteration order
|
|
233
|
+
|
|
234
|
+
export const NODE_GRAPH: Readonly<Record<string, ReadonlySet<string>>> = {
|
|
235
|
+
P: new Set(["A", "N", "V", "T", "S"]),
|
|
236
|
+
A: new Set(["P", "E", "N", "V", "T"]),
|
|
237
|
+
E: new Set(["P", "A", "V", "S"]),
|
|
238
|
+
N: new Set(["P", "A", "V", "T", "S"]),
|
|
239
|
+
V: new Set(["P", "A", "E", "N", "T", "S"]),
|
|
240
|
+
T: new Set(["P", "A", "N", "V", "S"]),
|
|
241
|
+
S: new Set(["P", "E", "N", "V", "T"]),
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/** Finite operational forms of V1/V2 paths — ported verbatim, including deliberate node repeats (ρ, σ, τ, υ, ψ, ω represent loops). */
|
|
245
|
+
export const PATH_NODES: Readonly<Record<string, readonly string[]>> = {
|
|
246
|
+
"α": ["P", "A", "E", "N", "V", "T", "S"],
|
|
247
|
+
"β": ["P", "N", "A", "V", "E", "T", "S"],
|
|
248
|
+
"γ": ["A", "P", "E", "V", "N", "T", "S"],
|
|
249
|
+
"δ": ["E", "A", "P", "N", "V", "T", "S"],
|
|
250
|
+
"ε": ["N", "P", "V", "A", "E", "T", "S"],
|
|
251
|
+
"ζ": ["T", "P", "A", "N", "V", "E", "S"],
|
|
252
|
+
"η": ["P", "V", "A", "E", "N", "T", "S"],
|
|
253
|
+
"θ": ["A", "N", "P", "V", "E", "T", "S"],
|
|
254
|
+
"ι": ["E", "P", "A", "V", "N", "T", "S"],
|
|
255
|
+
"κ": ["P", "A", "N", "V", "T", "E", "S"],
|
|
256
|
+
"λ": ["S", "P", "A", "E", "N", "V", "T"],
|
|
257
|
+
"μ": ["A", "E", "P", "V", "N", "T", "S"],
|
|
258
|
+
"ν": ["T", "A", "P", "N", "V", "E", "S"],
|
|
259
|
+
"ξ": ["N", "A", "V", "P", "E", "T", "S"],
|
|
260
|
+
"ο": ["P", "E", "A", "N", "V", "T", "S"],
|
|
261
|
+
"π": ["V", "P", "A", "E", "N", "T", "S"],
|
|
262
|
+
"ρ": ["P", "A", "V", "A", "V", "N", "T", "S"],
|
|
263
|
+
"σ": ["E", "A", "N", "V", "E", "A", "T", "S"],
|
|
264
|
+
"τ": ["T", "P", "A", "T", "N", "V", "E", "T", "S"],
|
|
265
|
+
"υ": ["P", "A", "E", "N", "V", "T", "S", "P"],
|
|
266
|
+
"φ": ["P", "N", "A", "V", "T", "E", "S"],
|
|
267
|
+
"χ": ["V", "P", "A", "E", "N", "T", "S"],
|
|
268
|
+
"ψ": ["P", "N", "N", "A", "V", "E", "T", "S"],
|
|
269
|
+
"ω": ["P", "A", "E", "N", "V", "T", "S", "S"],
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const DEFAULT_PRIMARY: readonly string[] = ["P", "A", "E", "N", "V", "T", "S"];
|
|
273
|
+
|
|
274
|
+
export interface StageWaveJSON {
|
|
275
|
+
tick: number;
|
|
276
|
+
nodes: string[];
|
|
277
|
+
labels: string[];
|
|
278
|
+
source: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export class StageWave {
|
|
282
|
+
constructor(
|
|
283
|
+
public readonly tick: number,
|
|
284
|
+
public readonly nodes: readonly string[],
|
|
285
|
+
public readonly source: string,
|
|
286
|
+
) {
|
|
287
|
+
Object.freeze(this);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
toDict(): StageWaveJSON {
|
|
291
|
+
return {
|
|
292
|
+
tick: this.tick,
|
|
293
|
+
nodes: [...this.nodes],
|
|
294
|
+
labels: this.nodes.map((n) => NODE_LABELS[n]),
|
|
295
|
+
source: this.source,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export class GraphLifeScheduler {
|
|
301
|
+
readonly seed: number;
|
|
302
|
+
readonly genome: Genome;
|
|
303
|
+
readonly pathId: string;
|
|
304
|
+
readonly primary: readonly string[];
|
|
305
|
+
|
|
306
|
+
constructor(seed: number, genome: Genome, pathId: string) {
|
|
307
|
+
validateGenome(genome);
|
|
308
|
+
this.seed = seed;
|
|
309
|
+
this.genome = { ...genome };
|
|
310
|
+
this.pathId = pathId;
|
|
311
|
+
this.primary = PATH_NODES[pathId] ?? DEFAULT_PRIMARY;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private initialAlive(): Set<string> {
|
|
315
|
+
const digest = sha256Bytes(new TextEncoder().encode(`graph-life:${this.seed}:${this.pathId}`));
|
|
316
|
+
const alive = new Set<string>();
|
|
317
|
+
NODE_ORDER.forEach((node, index) => {
|
|
318
|
+
if (digest[index] & 1) alive.add(node);
|
|
319
|
+
});
|
|
320
|
+
alive.add(this.primary[0]);
|
|
321
|
+
return alive;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
build(maxTicks = 7): StageWave[] {
|
|
325
|
+
if (maxTicks < 1) throw new Error("maxTicks must be >= 1");
|
|
326
|
+
|
|
327
|
+
let alive = this.initialAlive();
|
|
328
|
+
const seenStates = new Set<string>();
|
|
329
|
+
const visited = new Set<string>();
|
|
330
|
+
const waves: StageWave[] = [];
|
|
331
|
+
|
|
332
|
+
const birthThreshold = this.genome.portfolio_breadth >= 0.67 ? 3 : 2;
|
|
333
|
+
const survivalMax = this.genome.mechanism_independence >= 0.67 ? 4 : 3;
|
|
334
|
+
|
|
335
|
+
const primaryRank = new Map<string, number>();
|
|
336
|
+
this.primary.forEach((node, index) => {
|
|
337
|
+
if (!primaryRank.has(node)) primaryRank.set(node, index);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
for (let tick = 0; tick < maxTicks; tick++) {
|
|
341
|
+
const state = [...alive].sort().join(",");
|
|
342
|
+
if (seenStates.has(state)) break;
|
|
343
|
+
seenStates.add(state);
|
|
344
|
+
|
|
345
|
+
const ordered = [...alive].sort((a, b) => {
|
|
346
|
+
const ra = primaryRank.get(a) ?? 10_000;
|
|
347
|
+
const rb = primaryRank.get(b) ?? 10_000;
|
|
348
|
+
if (ra !== rb) return ra - rb;
|
|
349
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
if (ordered.length > 0) {
|
|
353
|
+
waves.push(new StageWave(tick, ordered, "graph-life"));
|
|
354
|
+
ordered.forEach((n) => visited.add(n));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const nextAlive = new Set<string>();
|
|
358
|
+
for (const node of NODE_ORDER) {
|
|
359
|
+
let liveNeighbors = 0;
|
|
360
|
+
for (const neighbor of NODE_GRAPH[node]) if (alive.has(neighbor)) liveNeighbors++;
|
|
361
|
+
|
|
362
|
+
if (alive.has(node)) {
|
|
363
|
+
if (liveNeighbors >= 1 && liveNeighbors <= survivalMax) nextAlive.add(node);
|
|
364
|
+
} else if (liveNeighbors === birthThreshold) {
|
|
365
|
+
nextAlive.add(node);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const nextRequired = this.primary.find((n) => !visited.has(n));
|
|
370
|
+
if (nextRequired !== undefined) nextAlive.add(nextRequired);
|
|
371
|
+
|
|
372
|
+
alive = nextAlive;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
let nextTick = waves.length;
|
|
376
|
+
for (const node of this.primary) {
|
|
377
|
+
if (!visited.has(node)) {
|
|
378
|
+
waves.push(new StageWave(nextTick, [node], "path-coverage"));
|
|
379
|
+
visited.add(node);
|
|
380
|
+
nextTick += 1;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return waves;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
389
|
+
// 3. Versioned goals, evaluator contracts, candidates and reports —
|
|
390
|
+
// verbatim port of §3
|
|
391
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
392
|
+
export interface GoalSpecInit {
|
|
393
|
+
version: number;
|
|
394
|
+
statement: string;
|
|
395
|
+
successCriteria: readonly string[];
|
|
396
|
+
hardConstraints: readonly string[];
|
|
397
|
+
softObjectives?: readonly string[];
|
|
398
|
+
parentHash?: string;
|
|
399
|
+
rationale?: string;
|
|
400
|
+
approvals?: readonly string[];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export class GoalSpec {
|
|
404
|
+
readonly version: number;
|
|
405
|
+
readonly statement: string;
|
|
406
|
+
readonly successCriteria: readonly string[];
|
|
407
|
+
readonly hardConstraints: readonly string[];
|
|
408
|
+
readonly softObjectives: readonly string[];
|
|
409
|
+
readonly parentHash: string;
|
|
410
|
+
readonly rationale: string;
|
|
411
|
+
readonly approvals: readonly string[];
|
|
412
|
+
|
|
413
|
+
constructor(init: GoalSpecInit) {
|
|
414
|
+
this.version = init.version;
|
|
415
|
+
this.statement = init.statement;
|
|
416
|
+
this.successCriteria = init.successCriteria;
|
|
417
|
+
this.hardConstraints = init.hardConstraints;
|
|
418
|
+
this.softObjectives = init.softObjectives ?? [];
|
|
419
|
+
this.parentHash = init.parentHash ?? "";
|
|
420
|
+
this.rationale = init.rationale ?? "initial goal";
|
|
421
|
+
this.approvals = init.approvals ?? [];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
validate(): void {
|
|
425
|
+
if (this.version < 1) throw new Error("Goal version must be >= 1");
|
|
426
|
+
if (!this.statement.trim()) throw new Error("Goal statement is empty");
|
|
427
|
+
if (this.successCriteria.length === 0) throw new Error("Goal must contain success criteria");
|
|
428
|
+
if (this.hardConstraints.length === 0) throw new Error("Goal must contain hard constraints");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
get contentHash(): string {
|
|
432
|
+
return sha256Text(
|
|
433
|
+
canonicalJson({
|
|
434
|
+
version: this.version,
|
|
435
|
+
statement: this.statement,
|
|
436
|
+
success_criteria: this.successCriteria,
|
|
437
|
+
hard_constraints: this.hardConstraints,
|
|
438
|
+
soft_objectives: this.softObjectives,
|
|
439
|
+
parent_hash: this.parentHash,
|
|
440
|
+
rationale: this.rationale,
|
|
441
|
+
approvals: this.approvals,
|
|
442
|
+
}),
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
static fromDict(raw: Record<string, unknown>): GoalSpec {
|
|
447
|
+
return new GoalSpec({
|
|
448
|
+
version: Number(raw.version),
|
|
449
|
+
statement: String(raw.statement),
|
|
450
|
+
successCriteria: [...(raw.success_criteria as string[])],
|
|
451
|
+
hardConstraints: [...(raw.hard_constraints as string[])],
|
|
452
|
+
softObjectives: [...((raw.soft_objectives as string[]) ?? [])],
|
|
453
|
+
parentHash: String(raw.parent_hash ?? ""),
|
|
454
|
+
rationale: String(raw.rationale ?? ""),
|
|
455
|
+
approvals: [...((raw.approvals as string[]) ?? [])],
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export interface EvaluatorSpecInit {
|
|
461
|
+
version: number;
|
|
462
|
+
metrics?: readonly string[];
|
|
463
|
+
hardGates?: readonly string[];
|
|
464
|
+
parentHash?: string;
|
|
465
|
+
rationale?: string;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export class EvaluatorSpec {
|
|
469
|
+
readonly version: number;
|
|
470
|
+
readonly metrics: readonly string[];
|
|
471
|
+
readonly hardGates: readonly string[];
|
|
472
|
+
readonly parentHash: string;
|
|
473
|
+
readonly rationale: string;
|
|
474
|
+
|
|
475
|
+
constructor(init: EvaluatorSpecInit) {
|
|
476
|
+
this.version = init.version;
|
|
477
|
+
this.metrics = init.metrics ?? STANDARD_METRICS;
|
|
478
|
+
this.hardGates = init.hardGates ?? ["correctness", "scope", "evidence", "safety"];
|
|
479
|
+
this.parentHash = init.parentHash ?? "";
|
|
480
|
+
this.rationale = init.rationale ?? "initial evaluator";
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
validate(): void {
|
|
484
|
+
if (this.version < 1) throw new Error("Evaluator version must be >= 1");
|
|
485
|
+
if (this.metrics.length === 0) throw new Error("Evaluator must define metrics");
|
|
486
|
+
if (new Set(this.metrics).size !== this.metrics.length) throw new Error("Evaluator metrics contain duplicates");
|
|
487
|
+
if (this.hardGates.length === 0) throw new Error("Evaluator must define hard gates");
|
|
488
|
+
if (new Set(this.hardGates).size !== this.hardGates.length) throw new Error("Evaluator hard gates contain duplicates");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
get contentHash(): string {
|
|
492
|
+
return sha256Text(
|
|
493
|
+
canonicalJson({
|
|
494
|
+
version: this.version,
|
|
495
|
+
metrics: this.metrics,
|
|
496
|
+
hard_gates: this.hardGates,
|
|
497
|
+
parent_hash: this.parentHash,
|
|
498
|
+
rationale: this.rationale,
|
|
499
|
+
}),
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
static fromDict(raw: Record<string, unknown>): EvaluatorSpec {
|
|
504
|
+
return new EvaluatorSpec({
|
|
505
|
+
version: Number(raw.version),
|
|
506
|
+
metrics: [...(raw.metrics as string[])],
|
|
507
|
+
hardGates: [...(raw.hard_gates as string[])],
|
|
508
|
+
parentHash: String(raw.parent_hash ?? ""),
|
|
509
|
+
rationale: String(raw.rationale ?? ""),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export interface GenerationRequest {
|
|
515
|
+
runId: string;
|
|
516
|
+
epoch: number;
|
|
517
|
+
candidateIndex: number;
|
|
518
|
+
candidateSeed: number;
|
|
519
|
+
goal: GoalSpec;
|
|
520
|
+
evaluatorSpec: EvaluatorSpec;
|
|
521
|
+
genome: Genome;
|
|
522
|
+
persona: { name: string; tagline: string };
|
|
523
|
+
path: { id: string; name: string; seq: string };
|
|
524
|
+
schedule: readonly StageWave[];
|
|
525
|
+
prompt: string;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
export interface CandidateJSON {
|
|
529
|
+
candidate_id: string;
|
|
530
|
+
run_id: string;
|
|
531
|
+
epoch: number;
|
|
532
|
+
candidate_index: number;
|
|
533
|
+
candidate_seed: number;
|
|
534
|
+
goal_hash: string;
|
|
535
|
+
evaluator_hash: string;
|
|
536
|
+
genome: Genome;
|
|
537
|
+
persona: Record<string, string>;
|
|
538
|
+
path: Record<string, string>;
|
|
539
|
+
schedule: StageWaveJSON[];
|
|
540
|
+
artifact: string;
|
|
541
|
+
producer_name: string;
|
|
542
|
+
producer_family: string;
|
|
543
|
+
parent_ids: string[];
|
|
544
|
+
mutation_rationale: string;
|
|
545
|
+
artifact_hash: string;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export class BlindCandidate {
|
|
549
|
+
constructor(
|
|
550
|
+
public readonly candidateId: string,
|
|
551
|
+
public readonly runId: string,
|
|
552
|
+
public readonly epoch: number,
|
|
553
|
+
public readonly goalHash: string,
|
|
554
|
+
public readonly evaluatorHash: string,
|
|
555
|
+
public readonly genome: Genome,
|
|
556
|
+
public readonly artifact: string,
|
|
557
|
+
public readonly artifactHash: string,
|
|
558
|
+
) {}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export class Candidate {
|
|
562
|
+
constructor(
|
|
563
|
+
public readonly candidateId: string,
|
|
564
|
+
public readonly runId: string,
|
|
565
|
+
public readonly epoch: number,
|
|
566
|
+
public readonly candidateIndex: number,
|
|
567
|
+
public readonly candidateSeed: number,
|
|
568
|
+
public readonly goalHash: string,
|
|
569
|
+
public readonly evaluatorHash: string,
|
|
570
|
+
public readonly genome: Genome,
|
|
571
|
+
public readonly persona: { name: string; tagline: string },
|
|
572
|
+
public readonly path: { id: string; name: string; seq: string },
|
|
573
|
+
public readonly schedule: readonly StageWave[],
|
|
574
|
+
public readonly artifact: string,
|
|
575
|
+
public readonly producerName: string,
|
|
576
|
+
public readonly producerFamily: string,
|
|
577
|
+
public readonly parentIds: readonly string[],
|
|
578
|
+
public readonly mutationRationale: string,
|
|
579
|
+
public readonly artifactHash: string,
|
|
580
|
+
) {}
|
|
581
|
+
|
|
582
|
+
/** Evaluator-family separation: strips producer identity, persona, and path from what the evaluator sees, so evaluators cannot exhibit self-preference toward a producer they recognize. */
|
|
583
|
+
blind(): BlindCandidate {
|
|
584
|
+
return new BlindCandidate(
|
|
585
|
+
this.candidateId,
|
|
586
|
+
this.runId,
|
|
587
|
+
this.epoch,
|
|
588
|
+
this.goalHash,
|
|
589
|
+
this.evaluatorHash,
|
|
590
|
+
{ ...this.genome },
|
|
591
|
+
this.artifact,
|
|
592
|
+
this.artifactHash,
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
toDict(): CandidateJSON {
|
|
597
|
+
return {
|
|
598
|
+
candidate_id: this.candidateId,
|
|
599
|
+
run_id: this.runId,
|
|
600
|
+
epoch: this.epoch,
|
|
601
|
+
candidate_index: this.candidateIndex,
|
|
602
|
+
candidate_seed: this.candidateSeed,
|
|
603
|
+
goal_hash: this.goalHash,
|
|
604
|
+
evaluator_hash: this.evaluatorHash,
|
|
605
|
+
genome: { ...this.genome },
|
|
606
|
+
persona: { ...this.persona },
|
|
607
|
+
path: { ...this.path },
|
|
608
|
+
schedule: this.schedule.map((w) => w.toDict()),
|
|
609
|
+
artifact: this.artifact,
|
|
610
|
+
producer_name: this.producerName,
|
|
611
|
+
producer_family: this.producerFamily,
|
|
612
|
+
parent_ids: [...this.parentIds],
|
|
613
|
+
mutation_rationale: this.mutationRationale,
|
|
614
|
+
artifact_hash: this.artifactHash,
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export interface EvaluationReportInit {
|
|
620
|
+
candidateId: string;
|
|
621
|
+
evaluatorId: string;
|
|
622
|
+
evaluatorFamily: string;
|
|
623
|
+
evaluatorSpecHash: string;
|
|
624
|
+
metrics: Record<string, number>;
|
|
625
|
+
hardGates: Record<string, boolean>;
|
|
626
|
+
confidence: number;
|
|
627
|
+
evidence: readonly string[];
|
|
628
|
+
actionableInformation?: readonly string[];
|
|
629
|
+
anomalies?: readonly string[];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export class EvaluationReport {
|
|
633
|
+
readonly candidateId: string;
|
|
634
|
+
readonly evaluatorId: string;
|
|
635
|
+
readonly evaluatorFamily: string;
|
|
636
|
+
readonly evaluatorSpecHash: string;
|
|
637
|
+
readonly metrics: Record<string, number>;
|
|
638
|
+
readonly hardGates: Record<string, boolean>;
|
|
639
|
+
readonly confidence: number;
|
|
640
|
+
readonly evidence: readonly string[];
|
|
641
|
+
readonly actionableInformation: readonly string[];
|
|
642
|
+
readonly anomalies: readonly string[];
|
|
643
|
+
|
|
644
|
+
constructor(init: EvaluationReportInit) {
|
|
645
|
+
this.candidateId = init.candidateId;
|
|
646
|
+
this.evaluatorId = init.evaluatorId;
|
|
647
|
+
this.evaluatorFamily = init.evaluatorFamily;
|
|
648
|
+
this.evaluatorSpecHash = init.evaluatorSpecHash;
|
|
649
|
+
this.metrics = { ...init.metrics };
|
|
650
|
+
this.hardGates = { ...init.hardGates };
|
|
651
|
+
this.confidence = init.confidence;
|
|
652
|
+
this.evidence = init.evidence;
|
|
653
|
+
this.actionableInformation = init.actionableInformation ?? [];
|
|
654
|
+
this.anomalies = init.anomalies ?? [];
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
validate(candidateId: string, spec: EvaluatorSpec): void {
|
|
658
|
+
if (this.candidateId !== candidateId) throw new Error("Evaluator returned wrong candidate_id");
|
|
659
|
+
if (this.evaluatorSpecHash !== spec.contentHash) throw new Error("Evaluator used a different evaluator specification");
|
|
660
|
+
|
|
661
|
+
const missingMetrics = spec.metrics.filter((m) => !(m in this.metrics)).sort();
|
|
662
|
+
if (missingMetrics.length) throw new Error(`Evaluator omitted metrics: ${JSON.stringify(missingMetrics)}`);
|
|
663
|
+
|
|
664
|
+
const missingGates = spec.hardGates.filter((g) => !(g in this.hardGates)).sort();
|
|
665
|
+
if (missingGates.length) throw new Error(`Evaluator omitted hard gates: ${JSON.stringify(missingGates)}`);
|
|
666
|
+
|
|
667
|
+
for (const name of spec.metrics) {
|
|
668
|
+
const value = this.metrics[name];
|
|
669
|
+
if (value < 0 || value > 1) throw new Error(`Metric ${JSON.stringify(name)}=${value} outside [0,1]`);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (this.confidence < 0 || this.confidence > 1) throw new Error("Evaluator confidence outside [0,1]");
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
export interface AggregateReportInit {
|
|
677
|
+
candidateId: string;
|
|
678
|
+
evaluatorSpecHash: string;
|
|
679
|
+
metrics: Record<string, number>;
|
|
680
|
+
metricSpread: Record<string, number>;
|
|
681
|
+
hardGates: Record<string, boolean>;
|
|
682
|
+
confidence: number;
|
|
683
|
+
evidence: readonly string[];
|
|
684
|
+
actionableInformation: readonly string[];
|
|
685
|
+
anomalies: readonly string[];
|
|
686
|
+
evaluatorIds: readonly string[];
|
|
687
|
+
errors: readonly string[];
|
|
688
|
+
verified: boolean;
|
|
689
|
+
reports: readonly EvaluationReport[];
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export class AggregateReport {
|
|
693
|
+
readonly candidateId: string;
|
|
694
|
+
readonly evaluatorSpecHash: string;
|
|
695
|
+
readonly metrics: Record<string, number>;
|
|
696
|
+
readonly metricSpread: Record<string, number>;
|
|
697
|
+
readonly hardGates: Record<string, boolean>;
|
|
698
|
+
readonly confidence: number;
|
|
699
|
+
readonly evidence: readonly string[];
|
|
700
|
+
readonly actionableInformation: readonly string[];
|
|
701
|
+
readonly anomalies: readonly string[];
|
|
702
|
+
readonly evaluatorIds: readonly string[];
|
|
703
|
+
readonly errors: readonly string[];
|
|
704
|
+
readonly verified: boolean;
|
|
705
|
+
readonly reports: readonly EvaluationReport[];
|
|
706
|
+
|
|
707
|
+
constructor(init: AggregateReportInit) {
|
|
708
|
+
Object.assign(this, init);
|
|
709
|
+
this.candidateId = init.candidateId;
|
|
710
|
+
this.evaluatorSpecHash = init.evaluatorSpecHash;
|
|
711
|
+
this.metrics = { ...init.metrics };
|
|
712
|
+
this.metricSpread = { ...init.metricSpread };
|
|
713
|
+
this.hardGates = { ...init.hardGates };
|
|
714
|
+
this.confidence = init.confidence;
|
|
715
|
+
this.evidence = init.evidence;
|
|
716
|
+
this.actionableInformation = init.actionableInformation;
|
|
717
|
+
this.anomalies = init.anomalies;
|
|
718
|
+
this.evaluatorIds = init.evaluatorIds;
|
|
719
|
+
this.errors = init.errors;
|
|
720
|
+
this.verified = init.verified;
|
|
721
|
+
this.reports = init.reports;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
726
|
+
// 4. External adapter contracts — verbatim port of §4
|
|
727
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
728
|
+
export interface ToolRegistryLike {
|
|
729
|
+
callTool(name: string, args: readonly string[]): Promise<{ stdout: string; stderr: string; exitCode: number }>;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export interface CandidateProducer {
|
|
733
|
+
name: string;
|
|
734
|
+
family: string;
|
|
735
|
+
produce(request: GenerationRequest, tools: ToolRegistryLike): Promise<string> | string;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export interface CandidateEvaluator {
|
|
739
|
+
name: string;
|
|
740
|
+
family: string;
|
|
741
|
+
spec: EvaluatorSpec;
|
|
742
|
+
evaluate(candidate: BlindCandidate, goal: GoalSpec, tools: ToolRegistryLike): Promise<EvaluationReport> | EvaluationReport;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export class FunctionProducer implements CandidateProducer {
|
|
746
|
+
constructor(
|
|
747
|
+
public readonly name: string,
|
|
748
|
+
public readonly family: string,
|
|
749
|
+
private readonly fn: (request: GenerationRequest, tools: ToolRegistryLike) => Promise<string> | string,
|
|
750
|
+
) {}
|
|
751
|
+
produce(request: GenerationRequest, tools: ToolRegistryLike) {
|
|
752
|
+
return this.fn(request, tools);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
export class FunctionEvaluator implements CandidateEvaluator {
|
|
757
|
+
constructor(
|
|
758
|
+
public readonly name: string,
|
|
759
|
+
public readonly family: string,
|
|
760
|
+
public readonly spec: EvaluatorSpec,
|
|
761
|
+
private readonly fn: (candidate: BlindCandidate, goal: GoalSpec, tools: ToolRegistryLike) => Promise<EvaluationReport> | EvaluationReport,
|
|
762
|
+
) {}
|
|
763
|
+
evaluate(candidate: BlindCandidate, goal: GoalSpec, tools: ToolRegistryLike) {
|
|
764
|
+
return this.fn(candidate, goal, tools);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
export interface ReviewDecision {
|
|
769
|
+
approved: boolean;
|
|
770
|
+
reference: string;
|
|
771
|
+
rationale: string;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export interface GoalProposer {
|
|
775
|
+
name: string;
|
|
776
|
+
propose(currentGoal: GoalSpec, epochTrace: readonly Record<string, unknown>[]): GoalSpec | null;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
export interface GoalReviewer {
|
|
780
|
+
name: string;
|
|
781
|
+
review(currentGoal: GoalSpec, proposedGoal: GoalSpec): ReviewDecision;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export interface EvaluatorProposer {
|
|
785
|
+
name: string;
|
|
786
|
+
propose(
|
|
787
|
+
currentSpec: EvaluatorSpec,
|
|
788
|
+
epochTrace: readonly Record<string, unknown>[],
|
|
789
|
+
): [EvaluatorSpec, readonly CandidateEvaluator[]] | null;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
793
|
+
// 5. Goal and evaluator mutation policies — verbatim port of §5
|
|
794
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
795
|
+
export interface GoalMutationPolicyOptions {
|
|
796
|
+
minimumApprovals?: number;
|
|
797
|
+
requireHumanApproval?: boolean;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export class GoalMutationPolicy {
|
|
801
|
+
readonly minimumApprovals: number;
|
|
802
|
+
readonly requireHumanApproval: boolean;
|
|
803
|
+
|
|
804
|
+
constructor(options: GoalMutationPolicyOptions = {}) {
|
|
805
|
+
this.minimumApprovals = options.minimumApprovals ?? 1;
|
|
806
|
+
this.requireHumanApproval = options.requireHumanApproval ?? false;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
validate(parent: GoalSpec, proposed: GoalSpec, atEpochBoundary: boolean): string[] {
|
|
810
|
+
const errors: string[] = [];
|
|
811
|
+
|
|
812
|
+
if (!atEpochBoundary) errors.push("Goal mutation attempted inside an epoch");
|
|
813
|
+
if (proposed.version !== parent.version + 1) errors.push("Goal version must increment exactly once");
|
|
814
|
+
if (proposed.parentHash !== parent.contentHash) errors.push("Goal parent_hash does not match current goal");
|
|
815
|
+
if (!proposed.rationale.trim()) errors.push("Goal mutation lacks rationale");
|
|
816
|
+
|
|
817
|
+
const parentConstraints = new Set(parent.hardConstraints);
|
|
818
|
+
const proposedConstraints = new Set(proposed.hardConstraints);
|
|
819
|
+
for (const c of parentConstraints) {
|
|
820
|
+
if (!proposedConstraints.has(c)) {
|
|
821
|
+
errors.push("Goal mutation removed a hard constraint");
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (proposed.approvals.length < this.minimumApprovals) errors.push("Goal mutation lacks sufficient approvals");
|
|
827
|
+
|
|
828
|
+
if (this.requireHumanApproval && !proposed.approvals.some((a) => a.startsWith("human:"))) {
|
|
829
|
+
errors.push("High-stakes goal mutation lacks human approval");
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
try {
|
|
833
|
+
proposed.validate();
|
|
834
|
+
} catch (exc) {
|
|
835
|
+
errors.push(exc instanceof Error ? exc.message : String(exc));
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return errors;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
export interface CalibrationCase {
|
|
843
|
+
artifact: string;
|
|
844
|
+
expectedHardGates: Record<string, boolean>;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
export async function calibrateEvaluator(
|
|
848
|
+
evaluator: CandidateEvaluator,
|
|
849
|
+
cases: readonly CalibrationCase[],
|
|
850
|
+
goal: GoalSpec,
|
|
851
|
+
tools: ToolRegistryLike,
|
|
852
|
+
): Promise<number> {
|
|
853
|
+
if (cases.length === 0) throw new Error("Evaluator calibration requires at least one case");
|
|
854
|
+
|
|
855
|
+
let correct = 0;
|
|
856
|
+
let total = 0;
|
|
857
|
+
|
|
858
|
+
for (let index = 0; index < cases.length; index++) {
|
|
859
|
+
const testCase = cases[index];
|
|
860
|
+
const artifactHash = sha256Text(testCase.artifact);
|
|
861
|
+
const candidate = new BlindCandidate(
|
|
862
|
+
`calibration-${index}-${artifactHash.slice(0, 12)}`,
|
|
863
|
+
"calibration",
|
|
864
|
+
-1,
|
|
865
|
+
goal.contentHash,
|
|
866
|
+
evaluator.spec.contentHash,
|
|
867
|
+
seedToGenome(index),
|
|
868
|
+
testCase.artifact,
|
|
869
|
+
artifactHash,
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
const report = await evaluator.evaluate(candidate, goal, tools);
|
|
873
|
+
report.validate(candidate.candidateId, evaluator.spec);
|
|
874
|
+
|
|
875
|
+
for (const [gate, expected] of Object.entries(testCase.expectedHardGates)) {
|
|
876
|
+
if (!(gate in report.hardGates)) {
|
|
877
|
+
total += 1;
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
correct += report.hardGates[gate] === expected ? 1 : 0;
|
|
881
|
+
total += 1;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return correct / Math.max(1, total);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
889
|
+
// 6. Budget accounting — verbatim port of §6
|
|
890
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
891
|
+
export class BudgetExceeded extends Error {}
|
|
892
|
+
|
|
893
|
+
export interface BudgetLimitsInit {
|
|
894
|
+
candidates?: number;
|
|
895
|
+
evaluatorCalls?: number;
|
|
896
|
+
toolCalls?: number;
|
|
897
|
+
worldActions?: number;
|
|
898
|
+
reportedTokens?: number;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
export class BudgetLimits {
|
|
902
|
+
readonly candidates: number;
|
|
903
|
+
readonly evaluatorCalls: number;
|
|
904
|
+
readonly toolCalls: number;
|
|
905
|
+
readonly worldActions: number;
|
|
906
|
+
readonly reportedTokens: number;
|
|
907
|
+
|
|
908
|
+
constructor(init: BudgetLimitsInit = {}) {
|
|
909
|
+
this.candidates = init.candidates ?? 32;
|
|
910
|
+
this.evaluatorCalls = init.evaluatorCalls ?? 128;
|
|
911
|
+
this.toolCalls = init.toolCalls ?? 128;
|
|
912
|
+
this.worldActions = init.worldActions ?? 32;
|
|
913
|
+
this.reportedTokens = init.reportedTokens ?? 2_000_000;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
type BudgetResource = "candidates" | "evaluatorCalls" | "toolCalls" | "worldActions" | "reportedTokens";
|
|
918
|
+
|
|
919
|
+
export class BudgetState {
|
|
920
|
+
candidates = 0;
|
|
921
|
+
evaluatorCalls = 0;
|
|
922
|
+
toolCalls = 0;
|
|
923
|
+
worldActions = 0;
|
|
924
|
+
reportedTokens = 0;
|
|
925
|
+
|
|
926
|
+
constructor(public readonly limits: BudgetLimits) {}
|
|
927
|
+
|
|
928
|
+
consume(resource: BudgetResource, amount = 1): void {
|
|
929
|
+
if (amount < 0) throw new Error("Budget consumption cannot be negative");
|
|
930
|
+
const validResources: BudgetResource[] = ["candidates", "evaluatorCalls", "toolCalls", "worldActions", "reportedTokens"];
|
|
931
|
+
if (!validResources.includes(resource)) throw new Error(`Unknown budget resource ${JSON.stringify(resource)}`);
|
|
932
|
+
|
|
933
|
+
const current = this[resource];
|
|
934
|
+
const limit = this.limits[resource];
|
|
935
|
+
|
|
936
|
+
if (current + amount > limit) {
|
|
937
|
+
throw new BudgetExceeded(`${resource} budget exceeded: ${current}+${amount}>${limit}`);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
this[resource] = current + amount;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
toDict(): Record<string, unknown> {
|
|
944
|
+
return {
|
|
945
|
+
limits: {
|
|
946
|
+
candidates: this.limits.candidates,
|
|
947
|
+
evaluator_calls: this.limits.evaluatorCalls,
|
|
948
|
+
tool_calls: this.limits.toolCalls,
|
|
949
|
+
world_actions: this.limits.worldActions,
|
|
950
|
+
reported_tokens: this.limits.reportedTokens,
|
|
951
|
+
},
|
|
952
|
+
candidates: this.candidates,
|
|
953
|
+
evaluator_calls: this.evaluatorCalls,
|
|
954
|
+
tool_calls: this.toolCalls,
|
|
955
|
+
world_actions: this.worldActions,
|
|
956
|
+
reported_tokens: this.reportedTokens,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
962
|
+
// 7. Tamper-evident, hash-chained event/memory/checkpoint store
|
|
963
|
+
// ---------------------------------------------------------------------
|
|
964
|
+
// DISCLOSED ADAPTATION (see file header): the Python spec used SQLite,
|
|
965
|
+
// which does not exist in a browser runtime. The hash-chain ALGORITHM given
|
|
966
|
+
// in the legible parts of §7 (event_hash = sha256(canonical_json({run_id,
|
|
967
|
+
// created_utc, kind, payload_json, prev_hash})); verify_chain walks rows
|
|
968
|
+
// checking prev_hash continuity and re-deriving each hash) is reproduced
|
|
969
|
+
// exactly, over an in-memory array persisted to localStorage instead of a
|
|
970
|
+
// .sqlite file, so the tamper-evidence property is real and testable.
|
|
971
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
972
|
+
interface EventRow {
|
|
973
|
+
runId: string;
|
|
974
|
+
seq: number;
|
|
975
|
+
createdUtc: string;
|
|
976
|
+
kind: string;
|
|
977
|
+
payloadJson: string;
|
|
978
|
+
prevHash: string;
|
|
979
|
+
eventHash: string;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
interface CheckpointRow {
|
|
983
|
+
runId: string;
|
|
984
|
+
name: string;
|
|
985
|
+
createdUtc: string;
|
|
986
|
+
eventSeq: number;
|
|
987
|
+
stateJson: string;
|
|
988
|
+
stateHash: string;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
interface MemoryRow {
|
|
992
|
+
memoryId: string;
|
|
993
|
+
runId: string;
|
|
994
|
+
kind: string;
|
|
995
|
+
status: "open" | "resolved";
|
|
996
|
+
priority: number;
|
|
997
|
+
payloadJson: string;
|
|
998
|
+
createdSeq: number;
|
|
999
|
+
updatedSeq: number;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
interface RunRow {
|
|
1003
|
+
runId: string;
|
|
1004
|
+
createdUtc: string;
|
|
1005
|
+
seed: number;
|
|
1006
|
+
domain: string;
|
|
1007
|
+
risk: RiskTier;
|
|
1008
|
+
status: string;
|
|
1009
|
+
configJson: string;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
export class EventStore {
|
|
1013
|
+
private runs = new Map<string, RunRow>();
|
|
1014
|
+
private events: EventRow[] = [];
|
|
1015
|
+
private checkpoints: CheckpointRow[] = [];
|
|
1016
|
+
private memories: MemoryRow[] = [];
|
|
1017
|
+
private nextSeq = 1;
|
|
1018
|
+
|
|
1019
|
+
constructor(private readonly persistenceKey: string | null = null) {
|
|
1020
|
+
if (this.persistenceKey) this.loadFromStorage();
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
private loadFromStorage(): void {
|
|
1024
|
+
try {
|
|
1025
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(this.persistenceKey!) : null;
|
|
1026
|
+
if (!raw) return;
|
|
1027
|
+
const parsed = JSON.parse(raw);
|
|
1028
|
+
this.runs = new Map(parsed.runs ?? []);
|
|
1029
|
+
this.events = parsed.events ?? [];
|
|
1030
|
+
this.checkpoints = parsed.checkpoints ?? [];
|
|
1031
|
+
this.memories = parsed.memories ?? [];
|
|
1032
|
+
this.nextSeq = parsed.nextSeq ?? 1;
|
|
1033
|
+
} catch {
|
|
1034
|
+
/* corrupted or absent storage — start fresh, never crash */
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
private persist(): void {
|
|
1039
|
+
if (!this.persistenceKey || typeof localStorage === "undefined") return;
|
|
1040
|
+
try {
|
|
1041
|
+
localStorage.setItem(
|
|
1042
|
+
this.persistenceKey,
|
|
1043
|
+
JSON.stringify({
|
|
1044
|
+
runs: [...this.runs.entries()],
|
|
1045
|
+
events: this.events,
|
|
1046
|
+
checkpoints: this.checkpoints,
|
|
1047
|
+
memories: this.memories,
|
|
1048
|
+
nextSeq: this.nextSeq,
|
|
1049
|
+
}),
|
|
1050
|
+
);
|
|
1051
|
+
} catch {
|
|
1052
|
+
/* storage full/unavailable — the in-memory chain remains authoritative for this session */
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
createRun(runId: string, seed: number, domain: string, risk: RiskTier, config: Record<string, unknown>): void {
|
|
1057
|
+
this.runs.set(runId, {
|
|
1058
|
+
runId,
|
|
1059
|
+
createdUtc: utcNow(),
|
|
1060
|
+
seed,
|
|
1061
|
+
domain,
|
|
1062
|
+
risk,
|
|
1063
|
+
status: "running",
|
|
1064
|
+
configJson: canonicalJson(config),
|
|
1065
|
+
});
|
|
1066
|
+
this.appendEvent(runId, "run_created", { seed, domain, risk });
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
setRunStatus(runId: string, status: string): void {
|
|
1070
|
+
const run = this.runs.get(runId);
|
|
1071
|
+
if (run) run.status = status;
|
|
1072
|
+
this.persist();
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** Exact hash-chain material and algorithm from the legible part of §7. */
|
|
1076
|
+
appendEvent(runId: string, kind: string, payload: Record<string, unknown>): number {
|
|
1077
|
+
const payloadJson = canonicalJson(payload);
|
|
1078
|
+
const created = utcNow();
|
|
1079
|
+
|
|
1080
|
+
const priorForRun = this.events.filter((e) => e.runId === runId);
|
|
1081
|
+
const prevHash = priorForRun.length > 0 ? priorForRun[priorForRun.length - 1].eventHash : ZERO_HASH;
|
|
1082
|
+
|
|
1083
|
+
const material = canonicalJson({
|
|
1084
|
+
run_id: runId,
|
|
1085
|
+
created_utc: created,
|
|
1086
|
+
kind,
|
|
1087
|
+
payload_json: payloadJson,
|
|
1088
|
+
prev_hash: prevHash,
|
|
1089
|
+
});
|
|
1090
|
+
const eventHash = sha256Text(material);
|
|
1091
|
+
|
|
1092
|
+
const seq = this.nextSeq++;
|
|
1093
|
+
this.events.push({ runId, seq, createdUtc: created, kind, payloadJson, prevHash, eventHash });
|
|
1094
|
+
this.persist();
|
|
1095
|
+
return seq;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Protected accessor so V4+ subclasses can re-derive their own chains from
|
|
1100
|
+
* the REAL stored payloads (not just link continuity). Additive: nothing
|
|
1101
|
+
* that already existed is changed.
|
|
1102
|
+
*/
|
|
1103
|
+
protected eventRowsForRun(runId: string): ReadonlyArray<{
|
|
1104
|
+
runId: string;
|
|
1105
|
+
seq: number;
|
|
1106
|
+
createdUtc: string;
|
|
1107
|
+
kind: string;
|
|
1108
|
+
payloadJson: string;
|
|
1109
|
+
prevHash: string;
|
|
1110
|
+
eventHash: string;
|
|
1111
|
+
}> {
|
|
1112
|
+
return this.events.filter((e) => e.runId === runId).sort((a, b) => a.seq - b.seq);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/** Test-only tamper hook: mutates a stored payload so chain verifiers can be proven to detect it. */
|
|
1116
|
+
_tamperPayloadForTest(runId: string, kind: string, newPayloadJson: string): boolean {
|
|
1117
|
+
const row = this.events.find((e) => e.runId === runId && e.kind === kind);
|
|
1118
|
+
if (!row) return false;
|
|
1119
|
+
row.payloadJson = newPayloadJson;
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
verifyChain(runId: string): boolean {
|
|
1124
|
+
const rows = this.events.filter((e) => e.runId === runId).sort((a, b) => a.seq - b.seq);
|
|
1125
|
+
let expectedPrev = ZERO_HASH;
|
|
1126
|
+
|
|
1127
|
+
for (const row of rows) {
|
|
1128
|
+
if (row.prevHash !== expectedPrev) return false;
|
|
1129
|
+
|
|
1130
|
+
const material = canonicalJson({
|
|
1131
|
+
run_id: row.runId,
|
|
1132
|
+
created_utc: row.createdUtc,
|
|
1133
|
+
kind: row.kind,
|
|
1134
|
+
payload_json: row.payloadJson,
|
|
1135
|
+
prev_hash: row.prevHash,
|
|
1136
|
+
});
|
|
1137
|
+
if (sha256Text(material) !== row.eventHash) return false;
|
|
1138
|
+
|
|
1139
|
+
expectedPrev = row.eventHash;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
saveCheckpoint(runId: string, name: string, state: Record<string, unknown>): string {
|
|
1146
|
+
const eventsForRun = this.events.filter((e) => e.runId === runId);
|
|
1147
|
+
const eventSeq = eventsForRun.length > 0 ? eventsForRun[eventsForRun.length - 1].seq : 0;
|
|
1148
|
+
const stateJson = canonicalJson(state);
|
|
1149
|
+
const stateHash = sha256Text(stateJson);
|
|
1150
|
+
|
|
1151
|
+
this.checkpoints = this.checkpoints.filter((c) => !(c.runId === runId && c.name === name));
|
|
1152
|
+
this.checkpoints.push({ runId, name, createdUtc: utcNow(), eventSeq, stateJson, stateHash });
|
|
1153
|
+
|
|
1154
|
+
this.appendEvent(runId, "checkpoint_saved", { name, event_seq: eventSeq, state_hash: stateHash });
|
|
1155
|
+
return stateHash;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
loadCheckpoint(runId: string, name: string): Record<string, unknown> {
|
|
1159
|
+
const row = this.checkpoints.find((c) => c.runId === runId && c.name === name);
|
|
1160
|
+
if (!row) throw new Error(`Checkpoint ${JSON.stringify(name)} does not exist`);
|
|
1161
|
+
if (sha256Text(row.stateJson) !== row.stateHash) throw new Error("Checkpoint hash mismatch");
|
|
1162
|
+
return JSON.parse(row.stateJson);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/** Internal accessor used by MemoryLedger — kept private-by-convention (not exported). */
|
|
1166
|
+
_memories(): MemoryRow[] {
|
|
1167
|
+
return this.memories;
|
|
1168
|
+
}
|
|
1169
|
+
_addMemoryRow(row: MemoryRow): void {
|
|
1170
|
+
this.memories.push(row);
|
|
1171
|
+
this.persist();
|
|
1172
|
+
}
|
|
1173
|
+
_updateMemoryRow(memoryId: string, runId: string, payloadJson: string, updatedSeq: number): void {
|
|
1174
|
+
const row = this.memories.find((m) => m.memoryId === memoryId && m.runId === runId);
|
|
1175
|
+
if (row) {
|
|
1176
|
+
row.payloadJson = payloadJson;
|
|
1177
|
+
row.status = "resolved";
|
|
1178
|
+
row.updatedSeq = updatedSeq;
|
|
1179
|
+
}
|
|
1180
|
+
this.persist();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
export class MemoryLedger {
|
|
1185
|
+
constructor(
|
|
1186
|
+
private readonly store: EventStore,
|
|
1187
|
+
private readonly runId: string,
|
|
1188
|
+
) {}
|
|
1189
|
+
|
|
1190
|
+
add(kind: string, payload: Record<string, unknown>, priority: number): string {
|
|
1191
|
+
if (priority < 0 || priority > 1) throw new Error("Memory priority outside [0,1]");
|
|
1192
|
+
|
|
1193
|
+
const createdSeq = this.store.appendEvent(this.runId, "memory_added", { kind, priority, payload: { ...payload } });
|
|
1194
|
+
|
|
1195
|
+
const memoryId = sha256Text(
|
|
1196
|
+
canonicalJson({ run_id: this.runId, kind, payload, created_seq: createdSeq }),
|
|
1197
|
+
);
|
|
1198
|
+
|
|
1199
|
+
this.store._addMemoryRow({
|
|
1200
|
+
memoryId,
|
|
1201
|
+
runId: this.runId,
|
|
1202
|
+
kind,
|
|
1203
|
+
status: "open",
|
|
1204
|
+
priority,
|
|
1205
|
+
payloadJson: canonicalJson(payload),
|
|
1206
|
+
createdSeq,
|
|
1207
|
+
updatedSeq: createdSeq,
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
return memoryId;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
resolve(memoryId: string, resolution: Record<string, unknown>): void {
|
|
1214
|
+
const seq = this.store.appendEvent(this.runId, "memory_resolved", { memory_id: memoryId, resolution: { ...resolution } });
|
|
1215
|
+
|
|
1216
|
+
const row = this.store._memories().find((m) => m.memoryId === memoryId && m.runId === this.runId);
|
|
1217
|
+
if (!row) throw new Error(memoryId);
|
|
1218
|
+
|
|
1219
|
+
const payload = JSON.parse(row.payloadJson);
|
|
1220
|
+
payload.resolution = { ...resolution };
|
|
1221
|
+
|
|
1222
|
+
this.store._updateMemoryRow(memoryId, this.runId, canonicalJson(payload), seq);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
listOpen(options: { kinds?: readonly string[]; limit?: number } = {}): Array<Record<string, unknown>> {
|
|
1226
|
+
const limit = options.limit ?? 10;
|
|
1227
|
+
if (limit < 1) return [];
|
|
1228
|
+
|
|
1229
|
+
let rows = this.store._memories().filter((m) => m.runId === this.runId && m.status === "open");
|
|
1230
|
+
if (options.kinds && options.kinds.length > 0) {
|
|
1231
|
+
const kindSet = new Set(options.kinds);
|
|
1232
|
+
rows = rows.filter((r) => kindSet.has(r.kind));
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
rows = rows.sort((a, b) => b.priority - a.priority || a.createdSeq - b.createdSeq);
|
|
1236
|
+
|
|
1237
|
+
return rows.slice(0, limit).map((r) => ({
|
|
1238
|
+
memory_id: r.memoryId,
|
|
1239
|
+
kind: r.kind,
|
|
1240
|
+
priority: r.priority,
|
|
1241
|
+
payload: JSON.parse(r.payloadJson),
|
|
1242
|
+
}));
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1247
|
+
// 8. Browser-safe tool registry
|
|
1248
|
+
// ---------------------------------------------------------------------
|
|
1249
|
+
// DISCLOSED ADAPTATION: the pasted spec's docstring says "Local subprocess
|
|
1250
|
+
// adapter" with security properties "shell=False, exact allowlist, fixed
|
|
1251
|
+
// working directory, timeout, bounded captured output" — but arbitrary
|
|
1252
|
+
// subprocess execution is categorically unavailable and unsafe inside a
|
|
1253
|
+
// Vite single-page browser bundle (there is no child_process, no shell, no
|
|
1254
|
+
// filesystem). Faithfully translating the STATED SECURITY PROPERTIES rather
|
|
1255
|
+
// than the impossible mechanism: this registry (a) never invokes a shell,
|
|
1256
|
+
// (b) only runs functions from a fixed, explicit allowlist supplied at
|
|
1257
|
+
// construction, (c) enforces a timeout via Promise.race, (d) truncates
|
|
1258
|
+
// captured output to a bounded byte length, and (e) is fully budget-gated
|
|
1259
|
+
// through BudgetState, exactly like the Python tool-call accounting.
|
|
1260
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1261
|
+
export type ToolFunction = (args: readonly string[]) => Promise<string> | string;
|
|
1262
|
+
|
|
1263
|
+
export interface ToolRegistryOptions {
|
|
1264
|
+
timeoutMs?: number;
|
|
1265
|
+
maxOutputBytes?: number;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export class ToolRegistry implements ToolRegistryLike {
|
|
1269
|
+
private readonly allowlist = new Map<string, ToolFunction>();
|
|
1270
|
+
private readonly timeoutMs: number;
|
|
1271
|
+
private readonly maxOutputBytes: number;
|
|
1272
|
+
|
|
1273
|
+
constructor(
|
|
1274
|
+
public readonly capabilityGate: CapabilityGate,
|
|
1275
|
+
private readonly budget: BudgetState,
|
|
1276
|
+
options: ToolRegistryOptions = {},
|
|
1277
|
+
) {
|
|
1278
|
+
this.timeoutMs = options.timeoutMs ?? 5_000;
|
|
1279
|
+
this.maxOutputBytes = options.maxOutputBytes ?? 65_536;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
register(name: string, fn: ToolFunction): void {
|
|
1283
|
+
this.allowlist.set(name, fn);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
async callTool(name: string, args: readonly string[]): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
1287
|
+
this.budget.consume("toolCalls", 1);
|
|
1288
|
+
|
|
1289
|
+
const fn = this.allowlist.get(name);
|
|
1290
|
+
if (!fn) {
|
|
1291
|
+
return { stdout: "", stderr: `tool not in allowlist: ${JSON.stringify(name)}`, exitCode: 127 };
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
try {
|
|
1295
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
1296
|
+
setTimeout(() => reject(new Error(`tool ${name} timed out after ${this.timeoutMs}ms`)), this.timeoutMs);
|
|
1297
|
+
});
|
|
1298
|
+
const stdout = await Promise.race([Promise.resolve(fn(args)), timeout]);
|
|
1299
|
+
return { stdout: stdout.slice(0, this.maxOutputBytes), stderr: "", exitCode: 0 };
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
return { stdout: "", stderr: error instanceof Error ? error.message : String(error), exitCode: 1 };
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1307
|
+
// 9. Seven-dimensional descriptor + versioned MAP-Elites archive
|
|
1308
|
+
// ---------------------------------------------------------------------
|
|
1309
|
+
// DISCLOSED: the docstrings survived ("Seven-dimensional descriptor based
|
|
1310
|
+
// on V1 discovery blocks", "MAP-Elites style archive with versioning
|
|
1311
|
+
// logic... Scores are never compared across materially different Goal or
|
|
1312
|
+
// Evaluator specifications (The Versioning Invariant)") but the method
|
|
1313
|
+
// bodies did not. Implemented in full below, functioning and non-mocked:
|
|
1314
|
+
// the descriptor buckets each of V1's 7 discovery blocks (Problem Choice,
|
|
1315
|
+
// Anomaly Valuation, Embodiment, Analogy, Evaluator Revision, Taste, Social
|
|
1316
|
+
// Stabilization) into a coarse decile via the mean of that block's genome
|
|
1317
|
+
// dimensions, and the archive enforces the Versioning Invariant by keying
|
|
1318
|
+
// every bucket on `${goalHash}:${evaluatorSpecHash}` so scores under a
|
|
1319
|
+
// mutated Goal or Evaluator can never silently overwrite or compare against
|
|
1320
|
+
// scores from a materially different specification.
|
|
1321
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1322
|
+
export function sevenDimensionalDescriptor(genome: Genome): readonly number[] {
|
|
1323
|
+
return Object.keys(DIMENSION_BLOCKS).map((block) => {
|
|
1324
|
+
const ids = DIMENSION_BLOCKS[block];
|
|
1325
|
+
const mean = ids.reduce((sum, id) => sum + (genome[id] ?? 0), 0) / Math.max(1, ids.length);
|
|
1326
|
+
return Math.floor(clamp01(mean) * 10); // decile bucket: 0-10
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
export interface ArchiveEntry {
|
|
1331
|
+
candidate: Candidate;
|
|
1332
|
+
report: AggregateReport;
|
|
1333
|
+
descriptor: readonly number[];
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function scoreOf(report: AggregateReport): number {
|
|
1337
|
+
const values = Object.values(report.metrics);
|
|
1338
|
+
return values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
export class VersionedArchive {
|
|
1342
|
+
readonly buckets = new Map<string, Map<string, ArchiveEntry>>();
|
|
1343
|
+
|
|
1344
|
+
private contextKey(goalHash: string, evaluatorSpecHash: string): string {
|
|
1345
|
+
return `${goalHash}:${evaluatorSpecHash}`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
private cellKey(descriptor: readonly number[]): string {
|
|
1349
|
+
return descriptor.join(".");
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/** The Versioning Invariant: a candidate can only ever be compared to prior entries under the SAME (goalHash, evaluatorSpecHash) context. */
|
|
1353
|
+
add(candidate: Candidate, report: AggregateReport): boolean {
|
|
1354
|
+
const ctxKey = this.contextKey(candidate.goalHash, report.evaluatorSpecHash);
|
|
1355
|
+
if (!this.buckets.has(ctxKey)) this.buckets.set(ctxKey, new Map());
|
|
1356
|
+
const bucket = this.buckets.get(ctxKey)!;
|
|
1357
|
+
|
|
1358
|
+
const descriptor = sevenDimensionalDescriptor(candidate.genome);
|
|
1359
|
+
const cellKey = this.cellKey(descriptor);
|
|
1360
|
+
const existing = bucket.get(cellKey);
|
|
1361
|
+
|
|
1362
|
+
if (!existing || scoreOf(report) > scoreOf(existing.report)) {
|
|
1363
|
+
bucket.set(cellKey, { candidate, report, descriptor });
|
|
1364
|
+
return true;
|
|
1365
|
+
}
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
getBest(ctxKey: string): ArchiveEntry | null {
|
|
1370
|
+
const bucket = this.buckets.get(ctxKey);
|
|
1371
|
+
if (!bucket || bucket.size === 0) return null;
|
|
1372
|
+
let best: ArchiveEntry | null = null;
|
|
1373
|
+
for (const entry of bucket.values()) {
|
|
1374
|
+
if (!best || scoreOf(entry.report) > scoreOf(best.report)) best = entry;
|
|
1375
|
+
}
|
|
1376
|
+
return best;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
size(ctxKey: string): number {
|
|
1380
|
+
return this.buckets.get(ctxKey)?.size ?? 0;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/** Retains temporarily inferior stepping stones (DGM open-ended branching): returns ALL entries in the context, not only the elite. */
|
|
1384
|
+
allEntries(ctxKey: string): ArchiveEntry[] {
|
|
1385
|
+
return [...(this.buckets.get(ctxKey)?.values() ?? [])];
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1390
|
+
// 10. Structural-diversity monitor
|
|
1391
|
+
// ---------------------------------------------------------------------
|
|
1392
|
+
// Motivated by the file's own design rationale: "Structural-diversity
|
|
1393
|
+
// monitoring, because repeated LLM mutation can converge into narrow
|
|
1394
|
+
// structural attractors." No body was supplied for this in the truncated
|
|
1395
|
+
// spec; implemented here as a real, testable metric: mean pairwise
|
|
1396
|
+
// Euclidean distance across the 21-dimension genome vectors of an epoch's
|
|
1397
|
+
// candidates, flagged narrow if below a fixed threshold.
|
|
1398
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1399
|
+
export interface DiversityReport {
|
|
1400
|
+
meanPairwiseDistance: number;
|
|
1401
|
+
narrowAttractor: boolean;
|
|
1402
|
+
populationSize: number;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
const NARROW_ATTRACTOR_THRESHOLD = 0.35;
|
|
1406
|
+
|
|
1407
|
+
export function assessStructuralDiversity(genomes: readonly Genome[]): DiversityReport {
|
|
1408
|
+
if (genomes.length < 2) {
|
|
1409
|
+
return { meanPairwiseDistance: 1, narrowAttractor: false, populationSize: genomes.length };
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
const ids = dimensionIds();
|
|
1413
|
+
const vectors = genomes.map((g) => ids.map((id) => g[id] ?? 0));
|
|
1414
|
+
|
|
1415
|
+
let sum = 0;
|
|
1416
|
+
let count = 0;
|
|
1417
|
+
for (let i = 0; i < vectors.length; i++) {
|
|
1418
|
+
for (let j = i + 1; j < vectors.length; j++) {
|
|
1419
|
+
let squared = 0;
|
|
1420
|
+
for (let k = 0; k < ids.length; k++) squared += (vectors[i][k] - vectors[j][k]) ** 2;
|
|
1421
|
+
sum += Math.sqrt(squared);
|
|
1422
|
+
count += 1;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
const meanDistance = count > 0 ? sum / count : 0;
|
|
1427
|
+
return {
|
|
1428
|
+
meanPairwiseDistance: Math.round(meanDistance * 10000) / 10000,
|
|
1429
|
+
narrowAttractor: meanDistance < NARROW_ATTRACTOR_THRESHOLD,
|
|
1430
|
+
populationSize: genomes.length,
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1435
|
+
// 11. GEPA-style textual-feedback mutation engine
|
|
1436
|
+
// ---------------------------------------------------------------------
|
|
1437
|
+
// Design rationale from the file header: "Trajectory feedback and textual
|
|
1438
|
+
// diagnostic information, rather than scalar-only optimization, following
|
|
1439
|
+
// GEPA." Implemented here as real logic: scans an AggregateReport's
|
|
1440
|
+
// `actionableInformation` and `anomalies` strings for keywords naming a V1
|
|
1441
|
+
// discovery block (e.g. "evaluator", "anomaly", "taste"), and nudges every
|
|
1442
|
+
// dimension in that block toward its high pole using V2's real
|
|
1443
|
+
// `applyMutation("nudge", ...)` operator — never a scalar-only step.
|
|
1444
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1445
|
+
const BLOCK_KEYWORDS: Readonly<Record<string, string>> = {
|
|
1446
|
+
"Problem Choice": "problem",
|
|
1447
|
+
"Anomaly Valuation": "anomaly",
|
|
1448
|
+
Embodiment: "world",
|
|
1449
|
+
Analogy: "analog",
|
|
1450
|
+
"Evaluator Revision": "evaluator",
|
|
1451
|
+
Taste: "taste",
|
|
1452
|
+
"Social Stabilization": "social",
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
export function nudgeGenomeFromFeedback(
|
|
1456
|
+
genome: Genome,
|
|
1457
|
+
report: AggregateReport,
|
|
1458
|
+
parentSeed: number,
|
|
1459
|
+
operationIndex = 0,
|
|
1460
|
+
): Genome {
|
|
1461
|
+
const feedbackText = [...report.actionableInformation, ...report.anomalies].join(" ").toLowerCase();
|
|
1462
|
+
|
|
1463
|
+
const targetedBlocks = Object.entries(BLOCK_KEYWORDS)
|
|
1464
|
+
.filter(([, keyword]) => feedbackText.includes(keyword))
|
|
1465
|
+
.map(([block]) => block);
|
|
1466
|
+
|
|
1467
|
+
if (targetedBlocks.length === 0) {
|
|
1468
|
+
return applyMutation(genome, parentSeed, "nudge", operationIndex);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
let mutated = { ...genome };
|
|
1472
|
+
for (const block of targetedBlocks) {
|
|
1473
|
+
const ids = DIMENSION_BLOCKS[block] ?? [];
|
|
1474
|
+
for (const id of ids) {
|
|
1475
|
+
mutated[id] = clamp01((mutated[id] ?? 0.5) + 0.1);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return mutated;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1482
|
+
// 12. Evaluation committee — committee aggregation, quorum, co-evolution
|
|
1483
|
+
// only at epoch boundaries
|
|
1484
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1485
|
+
export interface CommitteePolicyOptions {
|
|
1486
|
+
quorum?: number;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
export class CommitteePolicy {
|
|
1490
|
+
readonly quorum: number;
|
|
1491
|
+
constructor(options: CommitteePolicyOptions = {}) {
|
|
1492
|
+
this.quorum = options.quorum ?? 1;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
export class EvaluationCommittee {
|
|
1497
|
+
constructor(
|
|
1498
|
+
public readonly evaluators: readonly CandidateEvaluator[],
|
|
1499
|
+
public readonly policy: CommitteePolicy,
|
|
1500
|
+
) {}
|
|
1501
|
+
|
|
1502
|
+
get spec(): EvaluatorSpec {
|
|
1503
|
+
return this.evaluators[0].spec;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
async evaluate(candidate: BlindCandidate, goal: GoalSpec, tools: ToolRegistryLike, budget: BudgetState): Promise<AggregateReport> {
|
|
1507
|
+
const reports: EvaluationReport[] = [];
|
|
1508
|
+
const errors: string[] = [];
|
|
1509
|
+
|
|
1510
|
+
for (const evaluator of this.evaluators) {
|
|
1511
|
+
try {
|
|
1512
|
+
budget.consume("evaluatorCalls", 1);
|
|
1513
|
+
const report = await evaluator.evaluate(candidate, goal, tools);
|
|
1514
|
+
report.validate(candidate.candidateId, evaluator.spec);
|
|
1515
|
+
reports.push(report);
|
|
1516
|
+
} catch (error) {
|
|
1517
|
+
errors.push(`${evaluator.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
const spec = this.spec;
|
|
1522
|
+
const metrics: Record<string, number> = {};
|
|
1523
|
+
const metricSpread: Record<string, number> = {};
|
|
1524
|
+
|
|
1525
|
+
for (const metric of spec.metrics) {
|
|
1526
|
+
const values = reports.map((r) => r.metrics[metric]).filter((v) => typeof v === "number");
|
|
1527
|
+
metrics[metric] = values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
|
|
1528
|
+
metricSpread[metric] = values.length ? Math.max(...values) - Math.min(...values) : 0;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// Hard-gate aggregation rule (this module's own, disclosed): a gate
|
|
1532
|
+
// passes for the committee only if EVERY reporting evaluator marks it
|
|
1533
|
+
// true — evaluator-family separation must not let one lenient evaluator
|
|
1534
|
+
// paper over another's rejection.
|
|
1535
|
+
const hardGates: Record<string, boolean> = {};
|
|
1536
|
+
for (const gate of spec.hardGates) {
|
|
1537
|
+
hardGates[gate] = reports.length > 0 && reports.every((r) => r.hardGates[gate] === true);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const confidence = reports.length ? reports.reduce((a, r) => a + r.confidence, 0) / reports.length : 0;
|
|
1541
|
+
const verified = reports.length >= this.policy.quorum && errors.length === 0 && Object.values(hardGates).every(Boolean);
|
|
1542
|
+
|
|
1543
|
+
return new AggregateReport({
|
|
1544
|
+
candidateId: candidate.candidateId,
|
|
1545
|
+
evaluatorSpecHash: spec.contentHash,
|
|
1546
|
+
metrics,
|
|
1547
|
+
metricSpread,
|
|
1548
|
+
hardGates,
|
|
1549
|
+
confidence,
|
|
1550
|
+
evidence: reports.flatMap((r) => r.evidence),
|
|
1551
|
+
actionableInformation: reports.flatMap((r) => r.actionableInformation),
|
|
1552
|
+
anomalies: reports.flatMap((r) => r.anomalies),
|
|
1553
|
+
evaluatorIds: reports.map((r) => r.evaluatorId),
|
|
1554
|
+
errors,
|
|
1555
|
+
verified,
|
|
1556
|
+
reports,
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1562
|
+
// 13. Discovery engine — "High-horizon orchestrator enforcing the
|
|
1563
|
+
// Hashimoto-Fowler Harness Thesis" (docstring survived; body did not).
|
|
1564
|
+
// Implemented in full below as the described P/A/E/N/V/T/S-scheduled,
|
|
1565
|
+
// goal-versioned, budget-accounted, tamper-evident-logged epoch runner.
|
|
1566
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1567
|
+
export interface DiscoveryEngineInit {
|
|
1568
|
+
runId: string;
|
|
1569
|
+
seed: number;
|
|
1570
|
+
domain: string;
|
|
1571
|
+
goal: GoalSpec;
|
|
1572
|
+
producers: readonly CandidateProducer[];
|
|
1573
|
+
committee: EvaluationCommittee;
|
|
1574
|
+
tools: ToolRegistryLike;
|
|
1575
|
+
store: EventStore;
|
|
1576
|
+
budget: BudgetState;
|
|
1577
|
+
risk?: RiskTier;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
export class DiscoveryEngine {
|
|
1581
|
+
readonly runId: string;
|
|
1582
|
+
readonly seed: number;
|
|
1583
|
+
readonly domain: string;
|
|
1584
|
+
goal: GoalSpec;
|
|
1585
|
+
readonly producers: readonly CandidateProducer[];
|
|
1586
|
+
readonly committee: EvaluationCommittee;
|
|
1587
|
+
readonly tools: ToolRegistryLike;
|
|
1588
|
+
readonly store: EventStore;
|
|
1589
|
+
readonly budget: BudgetState;
|
|
1590
|
+
readonly risk: RiskTier;
|
|
1591
|
+
readonly archive = new VersionedArchive();
|
|
1592
|
+
readonly memory: MemoryLedger;
|
|
1593
|
+
|
|
1594
|
+
private epochTrace: Array<Record<string, unknown>> = [];
|
|
1595
|
+
|
|
1596
|
+
constructor(init: DiscoveryEngineInit) {
|
|
1597
|
+
this.runId = init.runId;
|
|
1598
|
+
this.seed = init.seed;
|
|
1599
|
+
this.domain = init.domain;
|
|
1600
|
+
this.goal = init.goal;
|
|
1601
|
+
this.producers = init.producers;
|
|
1602
|
+
this.committee = init.committee;
|
|
1603
|
+
this.tools = init.tools;
|
|
1604
|
+
this.store = init.store;
|
|
1605
|
+
this.budget = init.budget;
|
|
1606
|
+
this.risk = init.risk ?? "medium";
|
|
1607
|
+
this.memory = new MemoryLedger(this.store, this.runId);
|
|
1608
|
+
|
|
1609
|
+
this.goal.validate();
|
|
1610
|
+
this.store.createRun(this.runId, this.seed, this.domain, this.risk, {
|
|
1611
|
+
goal_hash: this.goal.contentHash,
|
|
1612
|
+
evaluator_spec_hash: this.committee.spec.contentHash,
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/** Runs one epoch: generates n candidates, evaluates each through the committee, updates the versioned archive, and enforces budget + safety gates throughout. Mirrors the Python CLI's `engine.run_epoch(epoch=1, n_candidates=2)`. */
|
|
1617
|
+
async runEpoch(epoch: number, nCandidates: number): Promise<Array<{ candidate: Candidate; report: AggregateReport }>> {
|
|
1618
|
+
if (nCandidates < 1) throw new Error("nCandidates must be >= 1");
|
|
1619
|
+
|
|
1620
|
+
const domainPack: DomainPack = DOMAIN_PACKS[this.domain] ?? DOMAIN_PACKS.general;
|
|
1621
|
+
const safetyGate = new SafetyGate(this.risk, domainPack);
|
|
1622
|
+
|
|
1623
|
+
const results: Array<{ candidate: Candidate; report: AggregateReport }> = [];
|
|
1624
|
+
const epochGenomes: Genome[] = [];
|
|
1625
|
+
|
|
1626
|
+
for (let index = 0; index < nCandidates; index++) {
|
|
1627
|
+
this.budget.consume("candidates", 1);
|
|
1628
|
+
|
|
1629
|
+
const candidateSeed = deriveSeed(this.runId, epoch, index, this.seed);
|
|
1630
|
+
let genome = seedToGenome(candidateSeed);
|
|
1631
|
+
genome = safetyGate.transformGenome(genome);
|
|
1632
|
+
epochGenomes.push(genome);
|
|
1633
|
+
|
|
1634
|
+
const persona = classifyPersonaExtended(genome);
|
|
1635
|
+
const path = selectPathExtended(genome);
|
|
1636
|
+
const schedule = new GraphLifeScheduler(candidateSeed, genome, path.id).build();
|
|
1637
|
+
|
|
1638
|
+
const promptBase = compileInnovationPrompt(genome, persona, path, this.goal.statement, this.domain);
|
|
1639
|
+
const prompt = promptBase + safetyGate.promptAddendum();
|
|
1640
|
+
|
|
1641
|
+
const request: GenerationRequest = {
|
|
1642
|
+
runId: this.runId,
|
|
1643
|
+
epoch,
|
|
1644
|
+
candidateIndex: index,
|
|
1645
|
+
candidateSeed,
|
|
1646
|
+
goal: this.goal,
|
|
1647
|
+
evaluatorSpec: this.committee.spec,
|
|
1648
|
+
genome,
|
|
1649
|
+
persona,
|
|
1650
|
+
path,
|
|
1651
|
+
schedule,
|
|
1652
|
+
prompt,
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
this.store.appendEvent(this.runId, "candidate_requested", {
|
|
1656
|
+
epoch,
|
|
1657
|
+
candidate_index: index,
|
|
1658
|
+
candidate_seed: candidateSeed,
|
|
1659
|
+
persona: persona.name,
|
|
1660
|
+
path: path.id,
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
const producer = this.producers[index % this.producers.length];
|
|
1664
|
+
const artifact = await producer.produce(request, this.tools);
|
|
1665
|
+
const artifactHash = sha256Text(artifact);
|
|
1666
|
+
const candidateId = sha256Text(canonicalJson({ runId: this.runId, epoch, index, artifactHash })).slice(0, 24);
|
|
1667
|
+
|
|
1668
|
+
const candidate = new Candidate(
|
|
1669
|
+
candidateId,
|
|
1670
|
+
this.runId,
|
|
1671
|
+
epoch,
|
|
1672
|
+
index,
|
|
1673
|
+
candidateSeed,
|
|
1674
|
+
this.goal.contentHash,
|
|
1675
|
+
this.committee.spec.contentHash,
|
|
1676
|
+
genome,
|
|
1677
|
+
persona,
|
|
1678
|
+
path,
|
|
1679
|
+
schedule,
|
|
1680
|
+
artifact,
|
|
1681
|
+
producer.name,
|
|
1682
|
+
producer.family,
|
|
1683
|
+
[],
|
|
1684
|
+
"initial generation",
|
|
1685
|
+
artifactHash,
|
|
1686
|
+
);
|
|
1687
|
+
|
|
1688
|
+
this.store.appendEvent(this.runId, "candidate_generated", { candidate_id: candidateId, artifact_hash: artifactHash });
|
|
1689
|
+
|
|
1690
|
+
const report = await this.committee.evaluate(candidate.blind(), this.goal, this.tools, this.budget);
|
|
1691
|
+
|
|
1692
|
+
this.store.appendEvent(this.runId, "candidate_evaluated", {
|
|
1693
|
+
candidate_id: candidateId,
|
|
1694
|
+
verified: report.verified,
|
|
1695
|
+
metrics: report.metrics,
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
this.archive.add(candidate, report);
|
|
1699
|
+
results.push({ candidate, report });
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
const diversity = assessStructuralDiversity(epochGenomes);
|
|
1703
|
+
this.store.appendEvent(this.runId, "epoch_completed", {
|
|
1704
|
+
epoch,
|
|
1705
|
+
n_candidates: nCandidates,
|
|
1706
|
+
mean_pairwise_distance: diversity.meanPairwiseDistance,
|
|
1707
|
+
narrow_attractor: diversity.narrowAttractor,
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
this.epochTrace.push({
|
|
1711
|
+
epoch,
|
|
1712
|
+
n_candidates: nCandidates,
|
|
1713
|
+
results: results.map((r) => ({ candidate_id: r.candidate.candidateId, verified: r.report.verified })),
|
|
1714
|
+
diversity,
|
|
1715
|
+
});
|
|
1716
|
+
|
|
1717
|
+
return results;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
getEpochTrace(): readonly Record<string, unknown>[] {
|
|
1721
|
+
return this.epochTrace;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1726
|
+
// 14. Final verifiable receipt — port of `generate_v3_report`
|
|
1727
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1728
|
+
export interface V3Report {
|
|
1729
|
+
sys: { version: string; status: string };
|
|
1730
|
+
run_id: string;
|
|
1731
|
+
goal: Record<string, unknown>;
|
|
1732
|
+
evaluator_spec: Record<string, unknown>;
|
|
1733
|
+
budget: Record<string, unknown>;
|
|
1734
|
+
archive_size: number;
|
|
1735
|
+
tamper_evident_seal: boolean;
|
|
1736
|
+
best_candidate?: {
|
|
1737
|
+
id: string;
|
|
1738
|
+
metrics: Record<string, number>;
|
|
1739
|
+
verified: boolean;
|
|
1740
|
+
artifact_preview: string;
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
export function generateV3Report(engine: DiscoveryEngine): V3Report {
|
|
1745
|
+
const ctxKey = `${engine.goal.contentHash}:${engine.committee.spec.contentHash}`;
|
|
1746
|
+
const bestEntry = engine.archive.getBest(ctxKey);
|
|
1747
|
+
|
|
1748
|
+
const report: V3Report = {
|
|
1749
|
+
sys: { version: "v3.1-OMEGA", status: "TERMINAL_SATURATION" },
|
|
1750
|
+
run_id: engine.runId,
|
|
1751
|
+
goal: {
|
|
1752
|
+
version: engine.goal.version,
|
|
1753
|
+
statement: engine.goal.statement,
|
|
1754
|
+
success_criteria: engine.goal.successCriteria,
|
|
1755
|
+
hard_constraints: engine.goal.hardConstraints,
|
|
1756
|
+
soft_objectives: engine.goal.softObjectives,
|
|
1757
|
+
parent_hash: engine.goal.parentHash,
|
|
1758
|
+
rationale: engine.goal.rationale,
|
|
1759
|
+
approvals: engine.goal.approvals,
|
|
1760
|
+
},
|
|
1761
|
+
evaluator_spec: {
|
|
1762
|
+
version: engine.committee.spec.version,
|
|
1763
|
+
metrics: engine.committee.spec.metrics,
|
|
1764
|
+
hard_gates: engine.committee.spec.hardGates,
|
|
1765
|
+
parent_hash: engine.committee.spec.parentHash,
|
|
1766
|
+
rationale: engine.committee.spec.rationale,
|
|
1767
|
+
},
|
|
1768
|
+
budget: engine.budget.toDict(),
|
|
1769
|
+
archive_size: engine.archive.size(ctxKey),
|
|
1770
|
+
tamper_evident_seal: engine.store.verifyChain(engine.runId),
|
|
1771
|
+
};
|
|
1772
|
+
|
|
1773
|
+
if (bestEntry) {
|
|
1774
|
+
report.best_candidate = {
|
|
1775
|
+
id: bestEntry.candidate.candidateId,
|
|
1776
|
+
metrics: bestEntry.report.metrics,
|
|
1777
|
+
verified: bestEntry.report.verified,
|
|
1778
|
+
artifact_preview: `${bestEntry.candidate.artifact.slice(0, 200)}...`,
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
return report;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1786
|
+
// 15. CLI equivalent — port of `main()`. There is no argparse/stdout in a
|
|
1787
|
+
// browser; this exported async function performs EXACTLY the same
|
|
1788
|
+
// construction sequence and returns the same report `main()` prints.
|
|
1789
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1790
|
+
export interface V3DemoOptions {
|
|
1791
|
+
problem: string;
|
|
1792
|
+
domain?: string;
|
|
1793
|
+
seed?: number;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
export async function runInnovationGenomeV3Demo(options: V3DemoOptions): Promise<V3Report> {
|
|
1797
|
+
const domain = options.domain ?? "mathematics";
|
|
1798
|
+
const seed = options.seed ?? Math.floor(Date.now() / 1000);
|
|
1799
|
+
|
|
1800
|
+
const goal = new GoalSpec({
|
|
1801
|
+
version: 1,
|
|
1802
|
+
statement: options.problem,
|
|
1803
|
+
successCriteria: ["Proof survives committee audit"],
|
|
1804
|
+
hardConstraints: ["Exact constants must be re-derived"],
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
const spec = new EvaluatorSpec({ version: 1 });
|
|
1808
|
+
|
|
1809
|
+
const producer = new FunctionProducer("BaseLLM", "GPT", (request) => `Solved ${request.goal.statement} via ${request.path.id}`);
|
|
1810
|
+
|
|
1811
|
+
const evaluator = new FunctionEvaluator("StaticAudit", "Audit", spec, (candidate) =>
|
|
1812
|
+
new EvaluationReport({
|
|
1813
|
+
candidateId: candidate.candidateId,
|
|
1814
|
+
evaluatorId: "StaticAudit",
|
|
1815
|
+
evaluatorFamily: "Audit",
|
|
1816
|
+
evaluatorSpecHash: spec.contentHash,
|
|
1817
|
+
metrics: Object.fromEntries(spec.metrics.map((m) => [m, 0.8])),
|
|
1818
|
+
hardGates: Object.fromEntries(spec.hardGates.map((g) => [g, true])),
|
|
1819
|
+
confidence: 0.9,
|
|
1820
|
+
evidence: ["Evidence A"],
|
|
1821
|
+
}),
|
|
1822
|
+
);
|
|
1823
|
+
|
|
1824
|
+
const store = new EventStore();
|
|
1825
|
+
const budget = new BudgetState(new BudgetLimits());
|
|
1826
|
+
const registry = new ToolRegistry(new CapabilityGate(), budget);
|
|
1827
|
+
const committee = new EvaluationCommittee([evaluator], new CommitteePolicy({ quorum: 1 }));
|
|
1828
|
+
|
|
1829
|
+
const runId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : sha256Text(String(Math.random()));
|
|
1830
|
+
|
|
1831
|
+
const engine = new DiscoveryEngine({
|
|
1832
|
+
runId,
|
|
1833
|
+
seed,
|
|
1834
|
+
domain,
|
|
1835
|
+
goal,
|
|
1836
|
+
producers: [producer],
|
|
1837
|
+
committee,
|
|
1838
|
+
tools: registry,
|
|
1839
|
+
store,
|
|
1840
|
+
budget,
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1843
|
+
await engine.runEpoch(1, 2);
|
|
1844
|
+
|
|
1845
|
+
return generateV3Report(engine);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1849
|
+
// 16. Diagnostics — the original test_innovation_genome_v3.py content was
|
|
1850
|
+
// never supplied in the task prompt (only its filename appeared in the
|
|
1851
|
+
// file tree), so it could not be ported. This suite independently
|
|
1852
|
+
// verifies every contract that IS legible in the spec, plus the SHA-256
|
|
1853
|
+
// implementation against the two published NIST test vectors.
|
|
1854
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1855
|
+
export interface DiagnosticCheck {
|
|
1856
|
+
id: string;
|
|
1857
|
+
passed: boolean;
|
|
1858
|
+
detail: string;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
export async function runInnovationGenomeV3Diagnostics(): Promise<{ ok: boolean; checks: DiagnosticCheck[] }> {
|
|
1862
|
+
const checks: DiagnosticCheck[] = [];
|
|
1863
|
+
const add = (id: string, passed: boolean, detail: string) => checks.push({ id, passed, detail });
|
|
1864
|
+
|
|
1865
|
+
// SHA-256 against NIST test vectors (real cryptographic verification, no network needed).
|
|
1866
|
+
add("sha256-empty-string", sha256Text("") === "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "FIPS 180-4 vector");
|
|
1867
|
+
add("sha256-abc", sha256Text("abc") === "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "FIPS 180-4 vector");
|
|
1868
|
+
|
|
1869
|
+
// Genome validation.
|
|
1870
|
+
const genome = seedToGenome(42);
|
|
1871
|
+
try { validateGenome(genome); add("validate-genome-ok", true, "no throw on valid genome"); }
|
|
1872
|
+
catch { add("validate-genome-ok", false, "unexpected throw"); }
|
|
1873
|
+
|
|
1874
|
+
try {
|
|
1875
|
+
validateGenome({ ...genome, extra_dim: 0.5 });
|
|
1876
|
+
add("validate-genome-rejects-extra", false, "did not throw");
|
|
1877
|
+
} catch (e) {
|
|
1878
|
+
add("validate-genome-rejects-extra", e instanceof Error && e.message.includes("unknown dimensions"), String(e));
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
// GoalSpec / EvaluatorSpec hashing determinism + validation.
|
|
1882
|
+
const goal = new GoalSpec({ version: 1, statement: "Prove X", successCriteria: ["c1"], hardConstraints: ["h1"] });
|
|
1883
|
+
const goal2 = new GoalSpec({ version: 1, statement: "Prove X", successCriteria: ["c1"], hardConstraints: ["h1"] });
|
|
1884
|
+
add("goal-hash-deterministic", goal.contentHash === goal2.contentHash, goal.contentHash);
|
|
1885
|
+
try { new GoalSpec({ version: 0, statement: "x", successCriteria: ["c"], hardConstraints: ["h"] }).validate(); add("goal-rejects-version-0", false, "no throw"); }
|
|
1886
|
+
catch { add("goal-rejects-version-0", true, "threw as expected"); }
|
|
1887
|
+
|
|
1888
|
+
const spec = new EvaluatorSpec({ version: 1 });
|
|
1889
|
+
try { spec.validate(); add("evaluator-spec-valid", true, "no throw"); }
|
|
1890
|
+
catch { add("evaluator-spec-valid", false, "unexpected throw"); }
|
|
1891
|
+
|
|
1892
|
+
// GraphLifeScheduler determinism + primary-path coverage guarantee.
|
|
1893
|
+
const scheduler1 = new GraphLifeScheduler(42, genome, "α");
|
|
1894
|
+
const scheduler2 = new GraphLifeScheduler(42, genome, "α");
|
|
1895
|
+
const waves1 = scheduler1.build();
|
|
1896
|
+
const waves2 = scheduler2.build();
|
|
1897
|
+
add("graph-life-deterministic", JSON.stringify(waves1.map((w) => w.toDict())) === JSON.stringify(waves2.map((w) => w.toDict())), `waves=${waves1.length}`);
|
|
1898
|
+
|
|
1899
|
+
const visitedNodes = new Set(waves1.flatMap((w) => w.nodes));
|
|
1900
|
+
const coversPrimary = PATH_NODES["α"].every((n) => visitedNodes.has(n));
|
|
1901
|
+
add("graph-life-covers-primary-path", coversPrimary, `visited=${[...visitedNodes].join(",")}`);
|
|
1902
|
+
|
|
1903
|
+
// GoalMutationPolicy.
|
|
1904
|
+
const policy = new GoalMutationPolicy();
|
|
1905
|
+
const proposedGoal = new GoalSpec({
|
|
1906
|
+
version: 2,
|
|
1907
|
+
statement: "Prove X, extended",
|
|
1908
|
+
successCriteria: ["c1"],
|
|
1909
|
+
hardConstraints: ["h1"],
|
|
1910
|
+
parentHash: goal.contentHash,
|
|
1911
|
+
rationale: "extend scope",
|
|
1912
|
+
approvals: ["reviewer:alice"],
|
|
1913
|
+
});
|
|
1914
|
+
const errors = policy.validate(goal, proposedGoal, true);
|
|
1915
|
+
add("goal-mutation-policy-accepts-valid", errors.length === 0, JSON.stringify(errors));
|
|
1916
|
+
|
|
1917
|
+
const badProposedGoal = new GoalSpec({
|
|
1918
|
+
version: 2,
|
|
1919
|
+
statement: "Prove X, extended",
|
|
1920
|
+
successCriteria: ["c1"],
|
|
1921
|
+
hardConstraints: [], // removed the hard constraint
|
|
1922
|
+
parentHash: goal.contentHash,
|
|
1923
|
+
rationale: "extend scope",
|
|
1924
|
+
approvals: ["reviewer:alice"],
|
|
1925
|
+
});
|
|
1926
|
+
const errors2 = policy.validate(goal, badProposedGoal, true);
|
|
1927
|
+
add("goal-mutation-policy-rejects-constraint-removal", errors2.some((e) => e.includes("hard constraint") || e.includes("must contain")), JSON.stringify(errors2));
|
|
1928
|
+
|
|
1929
|
+
// Budget accounting.
|
|
1930
|
+
const budget = new BudgetState(new BudgetLimits({ candidates: 2 }));
|
|
1931
|
+
budget.consume("candidates", 2);
|
|
1932
|
+
let budgetThrew = false;
|
|
1933
|
+
try { budget.consume("candidates", 1); } catch (e) { budgetThrew = e instanceof BudgetExceeded; }
|
|
1934
|
+
add("budget-exceeded-throws", budgetThrew, "BudgetExceeded raised at limit");
|
|
1935
|
+
|
|
1936
|
+
// EventStore hash chain.
|
|
1937
|
+
const store = new EventStore();
|
|
1938
|
+
store.createRun("test-run", 1, "general", "low", {});
|
|
1939
|
+
store.appendEvent("test-run", "custom_event", { a: 1 });
|
|
1940
|
+
store.appendEvent("test-run", "custom_event", { a: 2 });
|
|
1941
|
+
add("event-store-chain-verifies", store.verifyChain("test-run"), "3 events (run_created + 2 custom)");
|
|
1942
|
+
|
|
1943
|
+
// Checkpoint round-trip.
|
|
1944
|
+
const stateHash = store.saveCheckpoint("test-run", "cp1", { value: 42 });
|
|
1945
|
+
const loaded = store.loadCheckpoint("test-run", "cp1");
|
|
1946
|
+
add("checkpoint-roundtrip", (loaded as { value: number }).value === 42 && typeof stateHash === "string", stateHash);
|
|
1947
|
+
|
|
1948
|
+
// MemoryLedger.
|
|
1949
|
+
const memory = new MemoryLedger(store, "test-run");
|
|
1950
|
+
const memId = memory.add("insight", { text: "found a pattern" }, 0.8);
|
|
1951
|
+
add("memory-open-lists", memory.listOpen().some((m) => m.memory_id === memId), memId);
|
|
1952
|
+
memory.resolve(memId, { outcome: "confirmed" });
|
|
1953
|
+
add("memory-resolved-removed-from-open", !memory.listOpen().some((m) => m.memory_id === memId), "resolved");
|
|
1954
|
+
|
|
1955
|
+
// Calibration.
|
|
1956
|
+
const calibrationEvaluator = new FunctionEvaluator("Cal", "CalFam", spec, (candidate) =>
|
|
1957
|
+
new EvaluationReport({
|
|
1958
|
+
candidateId: candidate.candidateId,
|
|
1959
|
+
evaluatorId: "Cal",
|
|
1960
|
+
evaluatorFamily: "CalFam",
|
|
1961
|
+
evaluatorSpecHash: spec.contentHash,
|
|
1962
|
+
metrics: Object.fromEntries(spec.metrics.map((m) => [m, 0.5])),
|
|
1963
|
+
hardGates: Object.fromEntries(spec.hardGates.map((g) => [g, true])),
|
|
1964
|
+
confidence: 0.7,
|
|
1965
|
+
evidence: [],
|
|
1966
|
+
}),
|
|
1967
|
+
);
|
|
1968
|
+
const dummyTools: ToolRegistryLike = { callTool: async () => ({ stdout: "", stderr: "", exitCode: 0 }) };
|
|
1969
|
+
const calibrationScore = await calibrateEvaluator(
|
|
1970
|
+
calibrationEvaluator,
|
|
1971
|
+
[{ artifact: "artifact-1", expectedHardGates: { correctness: true } }],
|
|
1972
|
+
goal,
|
|
1973
|
+
dummyTools,
|
|
1974
|
+
);
|
|
1975
|
+
add("calibration-score-in-range", calibrationScore >= 0 && calibrationScore <= 1, String(calibrationScore));
|
|
1976
|
+
|
|
1977
|
+
// Versioned archive invariant: different goal hash must not share a bucket.
|
|
1978
|
+
const archive = new VersionedArchive();
|
|
1979
|
+
const dummyReport = new AggregateReport({
|
|
1980
|
+
candidateId: "c1",
|
|
1981
|
+
evaluatorSpecHash: spec.contentHash,
|
|
1982
|
+
metrics: { novelty: 0.5, utility: 0.5, tractability: 0.5, robustness: 0.5, taste: 0.5 },
|
|
1983
|
+
metricSpread: {},
|
|
1984
|
+
hardGates: { correctness: true, scope: true, evidence: true, safety: true },
|
|
1985
|
+
confidence: 0.8,
|
|
1986
|
+
evidence: [],
|
|
1987
|
+
actionableInformation: [],
|
|
1988
|
+
anomalies: [],
|
|
1989
|
+
evaluatorIds: ["e1"],
|
|
1990
|
+
errors: [],
|
|
1991
|
+
verified: true,
|
|
1992
|
+
reports: [],
|
|
1993
|
+
});
|
|
1994
|
+
const dummyCandidate = new Candidate(
|
|
1995
|
+
"c1", "run1", 1, 0, 1, goal.contentHash, spec.contentHash, genome,
|
|
1996
|
+
{ name: "P", tagline: "t" }, { id: "α", name: "n", seq: "s" }, [],
|
|
1997
|
+
"artifact text", "prod", "fam", [], "initial", sha256Text("artifact text"),
|
|
1998
|
+
);
|
|
1999
|
+
archive.add(dummyCandidate, dummyReport);
|
|
2000
|
+
const ctxA = `${goal.contentHash}:${spec.contentHash}`;
|
|
2001
|
+
const ctxB = `${proposedGoal.contentHash}:${spec.contentHash}`;
|
|
2002
|
+
add("archive-versioning-invariant", archive.size(ctxA) === 1 && archive.size(ctxB) === 0, `sizeA=${archive.size(ctxA)} sizeB=${archive.size(ctxB)}`);
|
|
2003
|
+
|
|
2004
|
+
// Structural diversity monitor.
|
|
2005
|
+
const identicalGenomes = [genome, genome, genome];
|
|
2006
|
+
const diverse = assessStructuralDiversity(identicalGenomes);
|
|
2007
|
+
add("diversity-detects-narrow-attractor", diverse.narrowAttractor === true, JSON.stringify(diverse));
|
|
2008
|
+
|
|
2009
|
+
// Full end-to-end demo run (mirrors the CLI main()).
|
|
2010
|
+
const demoReport = await runInnovationGenomeV3Demo({ problem: "Prove X.", domain: "mathematics", seed: 42 });
|
|
2011
|
+
add("demo-report-sealed", demoReport.tamper_evident_seal === true, "hash chain verified end-to-end");
|
|
2012
|
+
add("demo-report-has-best-candidate", demoReport.best_candidate !== undefined, JSON.stringify(demoReport.best_candidate ?? null));
|
|
2013
|
+
|
|
2014
|
+
return { ok: checks.every((c) => c.passed), checks };
|
|
2015
|
+
}
|