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,643 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* innovation-genome-v9.ts — Compliance, GenAI Observability & Continuous Quality Plane
|
|
3
|
+
* Additive over V1-V8. Never modifies prior modules.
|
|
4
|
+
*
|
|
5
|
+
* Ported capabilities:
|
|
6
|
+
* 1. EU AI Act Art 11/12/26/72/79 registry (retention, Annex IV, serious incidents)
|
|
7
|
+
* 2. OTel GenAI v1.41 emitter (create_agent, invoke_agent, execute_tool spans)
|
|
8
|
+
* 3. Pre-emit PII/secret redactor (email, phone, IPv4, IBAN, PAN/Luhn, sensitive keys)
|
|
9
|
+
* 4. LLM-as-Judge panel (family separation, golden-set calibration)
|
|
10
|
+
* 5. Golden dataset curator (promote_failure_to_case + reviewer approval)
|
|
11
|
+
* 6. Eval-gated CI harness (deterministic PASS/FAIL)
|
|
12
|
+
* 7. Online drift monitor (CUSUM change-point detector)
|
|
13
|
+
* 8. MCP audit log (gen_ai.tool.* conventions)
|
|
14
|
+
* 9. Deterministic replay engine
|
|
15
|
+
* 10. Evidence-pack exporter (EU AI Act conformity + SOC-2 audit)
|
|
16
|
+
*
|
|
17
|
+
* RUNTIME HONESTY: no Python, no SQLite, no browser executed. Structural port only.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
canonicalJson,
|
|
22
|
+
sha256Text,
|
|
23
|
+
utcNow,
|
|
24
|
+
} from "@/lib/innovation-genome-v3";
|
|
25
|
+
import {
|
|
26
|
+
V8ControlPlaneStore,
|
|
27
|
+
V8_PROTOCOL_VERSION,
|
|
28
|
+
} from "@/lib/innovation-genome-v8";
|
|
29
|
+
|
|
30
|
+
export const V9_SCHEMA_VERSION = 1;
|
|
31
|
+
export const V9_PROTOCOL_VERSION = "9.0-rc1";
|
|
32
|
+
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
34
|
+
// 1. Store
|
|
35
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
36
|
+
|
|
37
|
+
export class V9CompliancePlaneStore extends V8ControlPlaneStore {
|
|
38
|
+
v9Migrations: Array<{ version: number; name: string; checksum: string; appliedUtc: string }> = [];
|
|
39
|
+
_retentionPolicies = new Map<string, Record<string, unknown>>();
|
|
40
|
+
_seriousIncidents = new Map<string, Record<string, unknown>>();
|
|
41
|
+
_technicalDocs = new Map<string, Record<string, unknown>>();
|
|
42
|
+
_judgeScores = new Map<string, Record<string, unknown>>();
|
|
43
|
+
_goldenCases = new Map<string, Record<string, unknown>>();
|
|
44
|
+
_evalGateRuns = new Map<string, Record<string, unknown>>();
|
|
45
|
+
_driftWindows = new Map<string, Record<string, unknown>>();
|
|
46
|
+
_mcpToolCalls = new Map<string, Record<string, unknown>>();
|
|
47
|
+
_replayReports = new Map<string, Record<string, unknown>>();
|
|
48
|
+
_evidencePacks = new Map<string, Record<string, unknown>>();
|
|
49
|
+
|
|
50
|
+
v9SchemaCurrent(): boolean {
|
|
51
|
+
return this.v9Migrations.length >= V9_SCHEMA_VERSION;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Public accessor for V9 replay engine — subclass of EventStore so `eventRowsForRun` is accessible. */
|
|
55
|
+
getEventRows(runId: string) {
|
|
56
|
+
return this.eventRowsForRun(runId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
61
|
+
// 2. PII Redactor
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
63
|
+
|
|
64
|
+
const EMAIL_RE = /\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b/g;
|
|
65
|
+
const PHONE_E164_RE = /\+?[1-9]\d{7,14}\b/g;
|
|
66
|
+
const IPV4_RE = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g;
|
|
67
|
+
const IBAN_RE = /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b/g;
|
|
68
|
+
const PAN_RE = /\b(?:\d[ -]?){13,19}\b/g;
|
|
69
|
+
const SENSITIVE_KEY_RE = /(authorization|api[_-]?key|token|secret|password|passwd|cookie|credential|private[_-]?key|session|bearer)/i;
|
|
70
|
+
|
|
71
|
+
function luhnCheck(digits: string): boolean {
|
|
72
|
+
const stripped = digits.replace(/[^0-9]/g, "");
|
|
73
|
+
if (stripped.length < 13 || stripped.length > 19) return false;
|
|
74
|
+
let total = 0;
|
|
75
|
+
const parity = stripped.length % 2;
|
|
76
|
+
for (let i = 0; i < stripped.length; i++) {
|
|
77
|
+
let n = parseInt(stripped[i], 10);
|
|
78
|
+
if (i % 2 === parity) { n *= 2; if (n > 9) n -= 9; }
|
|
79
|
+
total += n;
|
|
80
|
+
}
|
|
81
|
+
return total % 10 === 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RedactionStats {
|
|
85
|
+
emails: number; phones: number; ipv4: number; iban: number; pan: number; sensitiveKeys: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class PIIRedactor {
|
|
89
|
+
constructor(private readonly pseudonymKey: string) {
|
|
90
|
+
if (pseudonymKey.length < 32) throw new Error("PII pseudonym key must be at least 32 bytes");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private tag(kind: string, value: string): string {
|
|
94
|
+
const digest = sha256Text(this.pseudonymKey + ":" + value).slice(0, 16);
|
|
95
|
+
return `<REDACTED:${kind}:${digest}:len=${value.length}>`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
redactText(text: string): [string, RedactionStats] {
|
|
99
|
+
const stats: RedactionStats = { emails: 0, phones: 0, ipv4: 0, iban: 0, pan: 0, sensitiveKeys: 0 };
|
|
100
|
+
const tag = this.tag.bind(this);
|
|
101
|
+
text = text.replace(EMAIL_RE, (m) => { stats.emails++; return tag("EMAIL", m); });
|
|
102
|
+
text = text.replace(PHONE_E164_RE, (m) => { stats.phones++; return tag("PHONE", m); });
|
|
103
|
+
text = text.replace(IPV4_RE, (m) => { stats.ipv4++; return tag("IPV4", m); });
|
|
104
|
+
text = text.replace(IBAN_RE, (m) => { stats.iban++; return tag("IBAN", m); });
|
|
105
|
+
text = text.replace(PAN_RE, (m) => {
|
|
106
|
+
if (luhnCheck(m)) { stats.pan++; return tag("PAN", m); }
|
|
107
|
+
return m;
|
|
108
|
+
});
|
|
109
|
+
return [text, stats];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
redactAttributes(attributes: Record<string, unknown>): [Record<string, unknown>, RedactionStats] {
|
|
113
|
+
const stats: RedactionStats = { emails: 0, phones: 0, ipv4: 0, iban: 0, pan: 0, sensitiveKeys: 0 };
|
|
114
|
+
const walk = (keyPath: string, value: unknown): unknown => {
|
|
115
|
+
if (SENSITIVE_KEY_RE.test(keyPath)) { stats.sensitiveKeys++; return this.tag("KEY", String(value)); }
|
|
116
|
+
if (typeof value === "string") { const [r, s] = this.redactText(value); Object.keys(s).forEach(k => (stats as any)[k] += (s as any)[k]); return r; }
|
|
117
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
118
|
+
const out: Record<string, unknown> = {};
|
|
119
|
+
for (const k of Object.keys(value as Record<string, unknown>).sort()) out[k] = walk(keyPath ? `${keyPath}.${k}` : k, (value as Record<string, unknown>)[k]);
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(value)) return value.map(v => walk(keyPath, v));
|
|
123
|
+
return value;
|
|
124
|
+
};
|
|
125
|
+
const out: Record<string, unknown> = {};
|
|
126
|
+
for (const k of Object.keys(attributes).sort()) out[k] = walk(k, attributes[k]);
|
|
127
|
+
return [out, stats];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
132
|
+
// 3. OTel GenAI v1.41 Emitter
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
134
|
+
|
|
135
|
+
export interface OtelSpanRecord {
|
|
136
|
+
traceId: string; spanId: string; parentSpanId: string; name: string; kind: string;
|
|
137
|
+
startUnixNano: number; endUnixNano: number; statusCode: string;
|
|
138
|
+
attributes: Record<string, unknown>; events: readonly Record<string, unknown>[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface OtelExporter {
|
|
142
|
+
export(span: OtelSpanRecord): void;
|
|
143
|
+
exportMetric(name: string, value: number, attributes: Record<string, unknown>, unit: string): void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class NullOtelExporter implements OtelExporter {
|
|
147
|
+
export(): void {}
|
|
148
|
+
exportMetric(): void {}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function randomHex16(): string {
|
|
152
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
|
|
153
|
+
return Math.random().toString(16).slice(2, 18);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export class OtelGenAIEmitter {
|
|
157
|
+
constructor(private readonly exporter: OtelExporter, private readonly redactor: PIIRedactor) {}
|
|
158
|
+
|
|
159
|
+
emitInvokeAgent(opts: {
|
|
160
|
+
agentId: string; agentName: string; providerName: string; modelName: string;
|
|
161
|
+
inputTokens?: number; outputTokens?: number; startNs: number; endNs: number; success: boolean;
|
|
162
|
+
extra?: Record<string, unknown>;
|
|
163
|
+
}): OtelSpanRecord {
|
|
164
|
+
const attrs: Record<string, unknown> = {
|
|
165
|
+
"gen_ai.operation.name": "invoke_agent", "gen_ai.agent.id": opts.agentId,
|
|
166
|
+
"gen_ai.agent.name": opts.agentName, "gen_ai.provider.name": opts.providerName,
|
|
167
|
+
"gen_ai.request.model": opts.modelName, "error.type": opts.success ? "" : "invocation_error",
|
|
168
|
+
"innovation_genome.protocol.version": V9_PROTOCOL_VERSION,
|
|
169
|
+
};
|
|
170
|
+
if (opts.inputTokens !== undefined) attrs["gen_ai.usage.input_tokens"] = opts.inputTokens;
|
|
171
|
+
if (opts.outputTokens !== undefined) attrs["gen_ai.usage.output_tokens"] = opts.outputTokens;
|
|
172
|
+
if (opts.extra) Object.assign(attrs, opts.extra);
|
|
173
|
+
|
|
174
|
+
const [redacted] = this.redactor.redactAttributes(attrs);
|
|
175
|
+
const record: OtelSpanRecord = {
|
|
176
|
+
traceId: randomHex16() + randomHex16(), spanId: randomHex16(), parentSpanId: "",
|
|
177
|
+
name: `invoke_agent ${opts.agentName}`.slice(0, 128), kind: "client",
|
|
178
|
+
startUnixNano: opts.startNs, endUnixNano: opts.endNs,
|
|
179
|
+
statusCode: opts.success ? "OK" : "ERROR", attributes: redacted, events: [],
|
|
180
|
+
};
|
|
181
|
+
this.exporter.export(record);
|
|
182
|
+
const durationMs = Math.max(0, (opts.endNs - opts.startNs) / 1_000_000);
|
|
183
|
+
this.exporter.exportMetric("gen_ai.client.operation.duration", durationMs, {
|
|
184
|
+
"gen_ai.operation.name": "invoke_agent", "gen_ai.provider.name": opts.providerName,
|
|
185
|
+
"gen_ai.request.model": opts.modelName,
|
|
186
|
+
}, "ms");
|
|
187
|
+
return record;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
emitExecuteTool(opts: {
|
|
191
|
+
toolName: string; toolCallId: string; startNs: number; endNs: number;
|
|
192
|
+
success: boolean; errorType?: string; extra?: Record<string, unknown>;
|
|
193
|
+
}): OtelSpanRecord {
|
|
194
|
+
const attrs: Record<string, unknown> = {
|
|
195
|
+
"gen_ai.operation.name": "execute_tool", "gen_ai.tool.name": opts.toolName,
|
|
196
|
+
"gen_ai.tool.call.id": opts.toolCallId, "error.type": opts.success ? "" : (opts.errorType || "tool_error"),
|
|
197
|
+
"innovation_genome.protocol.version": V9_PROTOCOL_VERSION,
|
|
198
|
+
};
|
|
199
|
+
if (opts.extra) Object.assign(attrs, opts.extra);
|
|
200
|
+
const [redacted] = this.redactor.redactAttributes(attrs);
|
|
201
|
+
const record: OtelSpanRecord = {
|
|
202
|
+
traceId: randomHex16() + randomHex16(), spanId: randomHex16(), parentSpanId: "",
|
|
203
|
+
name: `execute_tool ${opts.toolName}`.slice(0, 128), kind: "internal",
|
|
204
|
+
startUnixNano: opts.startNs, endUnixNano: opts.endNs,
|
|
205
|
+
statusCode: opts.success ? "OK" : "ERROR", attributes: redacted, events: [],
|
|
206
|
+
};
|
|
207
|
+
this.exporter.export(record);
|
|
208
|
+
return record;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
213
|
+
// 4. EU AI Act Registry
|
|
214
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
215
|
+
|
|
216
|
+
export type SeriousIncidentClass = "death_or_serious_health" | "critical_infrastructure_disruption" | "fundamental_rights_infringement" | "serious_harm_property_environment" | "other_material_incident";
|
|
217
|
+
|
|
218
|
+
export class EUAIActRegistry {
|
|
219
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly runId: string, private readonly tenantId: string) {}
|
|
220
|
+
|
|
221
|
+
setRetentionPolicy(category: string, minimumDays: number, maximumDays: number, lawfulBasis: string): string {
|
|
222
|
+
if (category === "article_26_deployer_log" && minimumDays < 183) throw new Error("Article 26(6) requires ≥6 months (183 days) log retention");
|
|
223
|
+
if (maximumDays < minimumDays) throw new Error("maximum retention cannot be below minimum retention");
|
|
224
|
+
const policyId = "retention-" + sha256Text(canonicalJson({ tenant_id: this.tenantId, category, minimum_days: minimumDays, maximum_days: maximumDays, lawful_basis: lawfulBasis })).slice(0, 32);
|
|
225
|
+
this.store._retentionPolicies.set(policyId, { policyId, tenantId: this.tenantId, category, minimumRetentionDays: minimumDays, maximumRetentionDays: maximumDays, lawfulBasis, createdUtc: utcNow() });
|
|
226
|
+
this.store.appendEvent(this.runId, "retention_policy_set_v9", { policy_id: policyId, category, minimum_days: minimumDays, maximum_days: maximumDays });
|
|
227
|
+
return policyId;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
registerAnnexIVDocumentation(document: Record<string, unknown>): string {
|
|
231
|
+
const docJson = canonicalJson(document);
|
|
232
|
+
const docHash = sha256Text(docJson);
|
|
233
|
+
const docId = "annex-iv-" + docHash.slice(0, 32);
|
|
234
|
+
this.store._technicalDocs.set(docId, { docId, tenantId: this.tenantId, runId: this.runId, schema: "annex_iv_v1", documentJson: docJson, documentHash: docHash, createdUtc: utcNow() });
|
|
235
|
+
this.store.appendEvent(this.runId, "annex_iv_documented_v9", { doc_id: docId, document_hash: docHash });
|
|
236
|
+
return docId;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
classifyAndRegisterIncident(summary: string, classification: SeriousIncidentClass, evidencePackHash: string): [string, boolean] {
|
|
240
|
+
const reportable = ["death_or_serious_health", "critical_infrastructure_disruption", "fundamental_rights_infringement"].includes(classification);
|
|
241
|
+
const sid = "serious-" + sha256Text(canonicalJson({ run_id: this.runId, tenant_id: this.tenantId, summary, classification, evidence_pack_hash: evidencePackHash, nonce: randomHex16() })).slice(0, 32);
|
|
242
|
+
this.store._seriousIncidents.set(sid, { seriousIncidentId: sid, runId: this.runId, tenantId: this.tenantId, classification, summary, firstDetectedUtc: utcNow(), article79Reportable: reportable, evidencePackHash, status: "open", createdUtc: utcNow(), updatedUtc: utcNow() });
|
|
243
|
+
this.store.appendEvent(this.runId, "serious_incident_registered_v9", { serious_incident_id: sid, classification, article_79_reportable: reportable, evidence_pack_hash: evidencePackHash });
|
|
244
|
+
return [sid, reportable];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
postMarketMonitoringPlan(): Record<string, unknown> {
|
|
248
|
+
return { article: "72", tenant_id: this.tenantId, run_id: this.runId, monitoring_objectives: ["Detect quality drift on production judges (V9 drift monitor)", "Detect prompt-injection escapes (V8 firewall + V9 emitter)", "Detect serious incidents (V9 registry Article 79 flow)"], data_sources: ["V4 event chain", "V5 canary hits", "V5 telemetry spans (V7 hardened, V9 redacted)", "V7 doctor reports", "V8 outbox/release/incident tables", "V9 judge scores and drift windows"], review_cadence_days: 30, human_oversight_roles: ["provider", "deployer"] };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function buildAnnexIVDocument(opts: {
|
|
253
|
+
tenantId: string; runId: string; systemName: string; intendedPurpose: string;
|
|
254
|
+
providerName: string; deployerName: string; systemArchitectureSummary: string;
|
|
255
|
+
riskManagementSummary: string; dataGovernanceSummary: string; performanceMetricsSummary: string;
|
|
256
|
+
changeLog: readonly Record<string, unknown>[]; harmonisedStandards: readonly string[];
|
|
257
|
+
protocolVersions?: readonly string[]; references?: readonly string[];
|
|
258
|
+
}): Record<string, unknown> {
|
|
259
|
+
return {
|
|
260
|
+
schema: "annex_iv_v1", system_name: opts.systemName, intended_purpose: opts.intendedPurpose,
|
|
261
|
+
provider: opts.providerName, deployer: opts.deployerName,
|
|
262
|
+
identification: { tenant_id: opts.tenantId, run_id: opts.runId, protocol_versions: opts.protocolVersions ?? [V8_PROTOCOL_VERSION, V9_PROTOCOL_VERSION] },
|
|
263
|
+
system_architecture: opts.systemArchitectureSummary, risk_management_system: opts.riskManagementSummary,
|
|
264
|
+
data_governance: opts.dataGovernanceSummary, performance: opts.performanceMetricsSummary,
|
|
265
|
+
logging_capability: { article_12_supported: true, log_categories: ["risk_situations", "post_market_monitoring", "deployer_oversight"], integrity: "V4 hash chain + V7 semantic chain + V9 deterministic replay", minimum_retention_days_default: 183 },
|
|
266
|
+
changes_through_lifecycle: [...opts.changeLog], harmonised_standards: [...opts.harmonisedStandards],
|
|
267
|
+
references: [...(opts.references ?? [])], generated_utc: utcNow(),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
272
|
+
// 5. LLM-as-Judge Panel
|
|
273
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
274
|
+
|
|
275
|
+
export interface JudgeRequest {
|
|
276
|
+
subjectId: string; subjectType: string; tenantId: string; runId: string;
|
|
277
|
+
inputs: Record<string, unknown>; output: string; reference?: string; context?: string; modelName?: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface JudgeScore {
|
|
281
|
+
judgeName: string; judgeFamily: string; metric: string; value: number; passed: boolean; rationale: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface Judge {
|
|
285
|
+
name: string; family: string;
|
|
286
|
+
score(request: JudgeRequest): readonly JudgeScore[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export class StructuralJudge implements Judge {
|
|
290
|
+
name = "v9-structural-judge";
|
|
291
|
+
family = "structural";
|
|
292
|
+
|
|
293
|
+
score(request: JudgeRequest): JudgeScore[] {
|
|
294
|
+
const out = request.output || "";
|
|
295
|
+
const ref = request.reference || "";
|
|
296
|
+
const ctx = request.context || "";
|
|
297
|
+
|
|
298
|
+
const outTokens = new Set((out.match(/\w{4,}/g) || []).map(w => w.toLowerCase()));
|
|
299
|
+
const ctxTokens = new Set((ctx.match(/\w{4,}/g) || []).map(w => w.toLowerCase()));
|
|
300
|
+
const refTokens = new Set((ref.match(/\w{4,}/g) || []).map(w => w.toLowerCase()));
|
|
301
|
+
|
|
302
|
+
const intersection = (a: Set<string>, b: Set<string>) => { let n = 0; for (const x of a) if (b.has(x)) n++; return n; };
|
|
303
|
+
const union = (a: Set<string>, b: Set<string>) => new Set([...a, ...b]).size;
|
|
304
|
+
|
|
305
|
+
const grounded = outTokens.size > 0 ? intersection(outTokens, ctxTokens) / outTokens.size : 0;
|
|
306
|
+
const faith = (refTokens.size > 0 || outTokens.size > 0) ? intersection(outTokens, refTokens) / Math.max(1, union(outTokens, refTokens)) : 0.5;
|
|
307
|
+
const banned = ["<script", "curl http", "rm -rf", "DROP TABLE"];
|
|
308
|
+
const safetyHits = banned.filter(b => out.toLowerCase().includes(b.toLowerCase())).length;
|
|
309
|
+
const safety = Math.max(0, 1 - 0.25 * safetyHits);
|
|
310
|
+
const completeness = Math.min(1, out.trim().length / 400);
|
|
311
|
+
|
|
312
|
+
return [
|
|
313
|
+
{ judgeName: this.name, judgeFamily: this.family, metric: "groundedness", value: Math.round(grounded * 10000) / 10000, passed: grounded >= 0.4, rationale: `${intersection(outTokens, ctxTokens)}/${outTokens.size} shared` },
|
|
314
|
+
{ judgeName: this.name, judgeFamily: this.family, metric: "faithfulness", value: Math.round(faith * 10000) / 10000, passed: faith >= 0.4, rationale: `jaccard=${faith.toFixed(3)}` },
|
|
315
|
+
{ judgeName: this.name, judgeFamily: this.family, metric: "safety", value: Math.round(safety * 10000) / 10000, passed: safety >= 0.9, rationale: `banned_hits=${safetyHits}` },
|
|
316
|
+
{ judgeName: this.name, judgeFamily: this.family, metric: "completeness", value: Math.round(completeness * 10000) / 10000, passed: completeness >= 0.3, rationale: `len=${out.length}` },
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export class LLMJudgePanel {
|
|
322
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly judges: readonly Judge[], private readonly forbidFamilies: ReadonlySet<string> = new Set()) {
|
|
323
|
+
if (judges.length === 0) throw new Error("At least one judge is required");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
score(request: JudgeRequest): JudgeScore[] {
|
|
327
|
+
const scores: JudgeScore[] = [];
|
|
328
|
+
for (const judge of this.judges) {
|
|
329
|
+
if (this.forbidFamilies.has(judge.family)) continue;
|
|
330
|
+
for (const s of judge.score(request)) {
|
|
331
|
+
scores.push(s);
|
|
332
|
+
const scoreId = "score-" + sha256Text(canonicalJson({ subject: request.subjectId, judge: judge.name, metric: s.metric, value: s.value, nonce: randomHex16() })).slice(0, 32);
|
|
333
|
+
this.store._judgeScores.set(scoreId, { scoreId, runId: request.runId, tenantId: request.tenantId, subjectId: request.subjectId, subjectType: request.subjectType, judgeName: judge.name, judgeFamily: judge.family, modelName: request.modelName ?? "static-judge", metric: s.metric, value: s.value, passed: s.passed, rationaleHash: sha256Text(s.rationale), createdUtc: utcNow() });
|
|
334
|
+
this.store.appendEvent(request.runId, "judge_scored_v9", { subject_id: request.subjectId, judge_name: judge.name, metric: s.metric, value: s.value, passed: s.passed });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return scores;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
342
|
+
// 6. Golden Dataset Curator + Eval Gate
|
|
343
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
344
|
+
|
|
345
|
+
export interface GoldenCase {
|
|
346
|
+
caseId: string; tenantId: string; dataset: string; origin: string; originRef: string;
|
|
347
|
+
inputs: Record<string, unknown>; expected: Record<string, unknown>; labels: Record<string, unknown>;
|
|
348
|
+
status: string; inputsHash: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export class GoldenDatasetCurator {
|
|
352
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly tenantId: string, private readonly runId: string) {}
|
|
353
|
+
|
|
354
|
+
promoteFailureToCase(dataset: string, origin: string, originRef: string, inputs: Record<string, unknown>, expected: Record<string, unknown>, labels?: Record<string, unknown>): string {
|
|
355
|
+
const inputsJson = canonicalJson(inputs);
|
|
356
|
+
const inputsHash = sha256Text(inputsJson);
|
|
357
|
+
const caseId = "goldcase-" + sha256Text(canonicalJson({ tenant_id: this.tenantId, dataset, inputs_hash: inputsHash })).slice(0, 32);
|
|
358
|
+
if (!this.store._goldenCases.has(caseId)) {
|
|
359
|
+
this.store._goldenCases.set(caseId, { caseId, tenantId: this.tenantId, dataset, origin, originRef, inputsJson, expectedJson: canonicalJson(expected), labelsJson: canonicalJson(labels || {}), reviewerIdsJson: "[]", status: "candidate", inputsHash, createdUtc: utcNow(), updatedUtc: utcNow() });
|
|
360
|
+
}
|
|
361
|
+
this.store.appendEvent(this.runId, "golden_case_created_v9", { case_id: caseId, dataset, origin });
|
|
362
|
+
return caseId;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
approve(caseId: string, reviewerId: string): void {
|
|
366
|
+
const row = this.store._goldenCases.get(caseId);
|
|
367
|
+
if (!row) throw new Error(caseId);
|
|
368
|
+
const reviewers = JSON.parse(String(row.reviewerIdsJson || "[]")) as string[];
|
|
369
|
+
if (!reviewers.includes(reviewerId)) reviewers.push(reviewerId);
|
|
370
|
+
row.reviewerIdsJson = canonicalJson(reviewers);
|
|
371
|
+
row.status = "approved";
|
|
372
|
+
row.updatedUtc = utcNow();
|
|
373
|
+
this.store._goldenCases.set(caseId, row);
|
|
374
|
+
this.store.appendEvent(this.runId, "golden_case_approved_v9", { case_id: caseId, reviewer_id: reviewerId });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
approvedCases(dataset: string): GoldenCase[] {
|
|
378
|
+
return [...this.store._goldenCases.values()].filter((r: any) => r.tenantId === this.tenantId && r.dataset === dataset && r.status === "approved").map((r: any) => ({
|
|
379
|
+
caseId: r.caseId, tenantId: r.tenantId, dataset: r.dataset, origin: r.origin, originRef: r.originRef,
|
|
380
|
+
inputs: JSON.parse(r.inputsJson), expected: JSON.parse(r.expectedJson), labels: JSON.parse(r.labelsJson),
|
|
381
|
+
status: r.status, inputsHash: r.inputsHash,
|
|
382
|
+
}));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface EvalGateThresholds {
|
|
387
|
+
minimumPassRate: number;
|
|
388
|
+
minimumPerMetric?: Record<string, number>;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export type CandidateRunner = (goldenCase: GoldenCase) => string;
|
|
392
|
+
|
|
393
|
+
export class EvalGate {
|
|
394
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly tenantId: string, private readonly runId: string, private readonly curator: GoldenDatasetCurator, private readonly judgePanel: LLMJudgePanel) {}
|
|
395
|
+
|
|
396
|
+
run(opts: { dataset: string; candidateRef: string; candidateRunner: CandidateRunner; thresholds: EvalGateThresholds }): Record<string, unknown> {
|
|
397
|
+
const cases = this.curator.approvedCases(opts.dataset);
|
|
398
|
+
if (cases.length === 0) throw new Error(`No approved golden cases in dataset ${JSON.stringify(opts.dataset)}`);
|
|
399
|
+
|
|
400
|
+
const perMetric: Record<string, boolean[]> = {};
|
|
401
|
+
for (const c of cases) {
|
|
402
|
+
const output = opts.candidateRunner(c);
|
|
403
|
+
const scores = this.judgePanel.score({ subjectId: c.caseId, subjectType: "golden_case", tenantId: this.tenantId, runId: this.runId, inputs: c.inputs, output, reference: canonicalJson(c.expected), context: canonicalJson(c.inputs), modelName: opts.candidateRef });
|
|
404
|
+
for (const s of scores) { (perMetric[s.metric] ??= []).push(s.passed); }
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const metricPassRate: Record<string, number> = {};
|
|
408
|
+
for (const [m, bs] of Object.entries(perMetric)) metricPassRate[m] = bs.filter(Boolean).length / Math.max(1, bs.length);
|
|
409
|
+
const values = Object.values(metricPassRate);
|
|
410
|
+
const overallPassRate = values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
|
|
411
|
+
const thresholdMap = opts.thresholds.minimumPerMetric ?? {};
|
|
412
|
+
const passed = overallPassRate >= opts.thresholds.minimumPassRate && Object.entries(thresholdMap).every(([m, t]) => (metricPassRate[m] ?? 0) >= t);
|
|
413
|
+
|
|
414
|
+
const result = { dataset: opts.dataset, candidate_ref: opts.candidateRef, case_count: cases.length, metric_pass_rate: metricPassRate, overall_pass_rate: Math.round(overallPassRate * 10000) / 10000, passed };
|
|
415
|
+
const resultJson = canonicalJson(result);
|
|
416
|
+
const gateRunId = "gate-" + sha256Text(resultJson).slice(0, 32);
|
|
417
|
+
this.store._evalGateRuns.set(gateRunId, { gateRunId, tenantId: this.tenantId, dataset: opts.dataset, candidateRef: opts.candidateRef, resultJson, resultHash: sha256Text(resultJson), passed, createdUtc: utcNow() });
|
|
418
|
+
this.store.appendEvent(this.runId, "eval_gate_run_v9", { gate_run_id: gateRunId, dataset: opts.dataset, candidate_ref: opts.candidateRef, passed, result_hash: sha256Text(resultJson) });
|
|
419
|
+
return { gate_run_id: gateRunId, ...result };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
424
|
+
// 7. Drift Monitor (CUSUM)
|
|
425
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
426
|
+
|
|
427
|
+
export interface DriftSample { metricKey: string; value: number; tsUnix: number; }
|
|
428
|
+
export interface DriftDecision { alarm: boolean; mean: number; stddev: number; cusumHigh: number; cusumLow: number; sampleCount: number; }
|
|
429
|
+
|
|
430
|
+
export class DriftMonitor {
|
|
431
|
+
private buffers = new Map<string, DriftSample[]>();
|
|
432
|
+
constructor(
|
|
433
|
+
private readonly store: V9CompliancePlaneStore, private readonly tenantId: string, private readonly runId: string,
|
|
434
|
+
private readonly windowSize = 50, private readonly cusumThreshold = 5.0, private readonly allowance = 0.5,
|
|
435
|
+
) {}
|
|
436
|
+
|
|
437
|
+
observe(sample: DriftSample): DriftDecision {
|
|
438
|
+
const buf = this.buffers.get(sample.metricKey) ?? [];
|
|
439
|
+
buf.push(sample);
|
|
440
|
+
if (buf.length > this.windowSize) buf.splice(0, buf.length - this.windowSize);
|
|
441
|
+
this.buffers.set(sample.metricKey, buf);
|
|
442
|
+
|
|
443
|
+
const values = buf.map(s => s.value);
|
|
444
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
445
|
+
const stddev = values.length > 1 ? Math.sqrt(values.reduce((a, b) => a + (b - mean) ** 2, 0) / values.length) : 0;
|
|
446
|
+
|
|
447
|
+
const target = mean;
|
|
448
|
+
const k = this.allowance * (stddev > 1e-9 ? stddev : 1e-3);
|
|
449
|
+
let high = 0, low = 0;
|
|
450
|
+
for (const v of values) {
|
|
451
|
+
high = Math.max(0, high + (v - target - k));
|
|
452
|
+
low = Math.min(0, low + (v - target + k));
|
|
453
|
+
}
|
|
454
|
+
const threshold = this.cusumThreshold * Math.max(stddev, 1e-3);
|
|
455
|
+
const alarm = high > threshold || -low > threshold;
|
|
456
|
+
|
|
457
|
+
if (alarm) {
|
|
458
|
+
this.store.appendEvent(this.runId, "drift_alarm_v9", { metric_key: sample.metricKey, cusum_high: high, cusum_low: low, mean, sample_count: values.length });
|
|
459
|
+
}
|
|
460
|
+
return { alarm, mean: Math.round(mean * 1e6) / 1e6, stddev: Math.round(stddev * 1e6) / 1e6, cusumHigh: Math.round(high * 1e6) / 1e6, cusumLow: Math.round(low * 1e6) / 1e6, sampleCount: values.length };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
465
|
+
// 8. MCP Audit Log
|
|
466
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
467
|
+
|
|
468
|
+
export class MCPAuditLog {
|
|
469
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly runId: string, private readonly tenantId: string, private readonly emitter: OtelGenAIEmitter) {}
|
|
470
|
+
|
|
471
|
+
recordCall(opts: { agentId: string; toolName: string; toolCallId: string; arguments: Record<string, unknown>; result: Record<string, unknown>; status: string; errorType?: string; startedUnixNs?: number; endedUnixNs?: number }): string {
|
|
472
|
+
const argsHash = sha256Text(canonicalJson(opts.arguments));
|
|
473
|
+
const resultHash = sha256Text(canonicalJson(opts.result));
|
|
474
|
+
const startNs = opts.startedUnixNs ?? Date.now() * 1_000_000;
|
|
475
|
+
const endNs = opts.endedUnixNs ?? startNs;
|
|
476
|
+
const durationMs = Math.max(0, Math.round((endNs - startNs) / 1_000_000));
|
|
477
|
+
const mcpCallId = "mcp-" + sha256Text(canonicalJson({ run_id: this.runId, tool_name: opts.toolName, tool_call_id: opts.toolCallId, args_hash: argsHash })).slice(0, 32);
|
|
478
|
+
|
|
479
|
+
this.store._mcpToolCalls.set(mcpCallId, { mcpCallId, runId: this.runId, tenantId: this.tenantId, agentId: opts.agentId, toolName: opts.toolName, toolCallId: opts.toolCallId, argumentsHash: argsHash, resultHash, status: opts.status, errorType: opts.errorType ?? "", durationMs, startedUtc: utcNow() });
|
|
480
|
+
|
|
481
|
+
this.emitter.emitExecuteTool({ toolName: opts.toolName, toolCallId: opts.toolCallId, startNs, endNs, success: opts.status === "ok", errorType: opts.errorType, extra: { "mcp.call.id": mcpCallId, "gen_ai.agent.id": opts.agentId, "gen_ai.tool.arguments.hash": argsHash, "gen_ai.tool.result.hash": resultHash } });
|
|
482
|
+
|
|
483
|
+
this.store.appendEvent(this.runId, "mcp_tool_call_v9", { mcp_call_id: mcpCallId, tool_name: opts.toolName, arguments_hash: argsHash, result_hash: resultHash, status: opts.status });
|
|
484
|
+
return mcpCallId;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
489
|
+
// 9. Deterministic Replay Engine
|
|
490
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
491
|
+
|
|
492
|
+
export class DeterministicReplayEngine {
|
|
493
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly runId: string, private readonly tenantId: string) {}
|
|
494
|
+
|
|
495
|
+
replay(startSeq = 1, endSeq?: number): Record<string, unknown> {
|
|
496
|
+
// Re-derive semantic chain from stored events — same algorithm as V7
|
|
497
|
+
const events = this.store.getEventRows(this.runId).filter(e => e.seq >= startSeq);
|
|
498
|
+
const filtered = endSeq !== undefined ? events.filter(e => e.seq <= endSeq) : events;
|
|
499
|
+
|
|
500
|
+
const divergences: Array<Record<string, unknown>> = [];
|
|
501
|
+
let expectedPrev = "0".repeat(64);
|
|
502
|
+
let replayHead = "0".repeat(64);
|
|
503
|
+
let originalHead = "";
|
|
504
|
+
|
|
505
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
506
|
+
const row = filtered[i];
|
|
507
|
+
const material = canonicalJson({ run_id: this.runId, created_utc: row.createdUtc, kind: row.kind, payload_json: row.payloadJson, prev_hash: row.prevHash });
|
|
508
|
+
const recomputed = sha256Text(material);
|
|
509
|
+
if (recomputed !== row.eventHash) {
|
|
510
|
+
divergences.push({ seq: row.seq, recomputed, stored: row.eventHash });
|
|
511
|
+
}
|
|
512
|
+
expectedPrev = row.eventHash;
|
|
513
|
+
replayHead = expectedPrev;
|
|
514
|
+
originalHead = row.eventHash;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const equivalent = divergences.length === 0;
|
|
518
|
+
const reportId = "replay-" + sha256Text(canonicalJson({ run_id: this.runId, start_seq: startSeq, end_seq: endSeq, divergences })).slice(0, 32);
|
|
519
|
+
this.store._replayReports.set(reportId, { reportId, runId: this.runId, tenantId: this.tenantId, eventRangeStart: startSeq, eventRangeEnd: endSeq ?? 0, originalHeadHash: originalHead || "0".repeat(64), replayHeadHash: replayHead, equivalent, divergenceJson: canonicalJson(divergences), createdUtc: utcNow() });
|
|
520
|
+
this.store.appendEvent(this.runId, "deterministic_replay_v9", { report_id: reportId, equivalent, divergence_count: divergences.length });
|
|
521
|
+
return { report_id: reportId, equivalent, divergences, original_head_hash: originalHead, replay_head_hash: replayHead };
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
526
|
+
// 10. Evidence Pack Exporter
|
|
527
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
528
|
+
|
|
529
|
+
export class EvidencePackExporter {
|
|
530
|
+
constructor(private readonly store: V9CompliancePlaneStore, private readonly tenantId: string, private readonly runId: string) {}
|
|
531
|
+
|
|
532
|
+
export(): Record<string, unknown> {
|
|
533
|
+
const retention = [...this.store._retentionPolicies.values()].filter((r: any) => r.tenantId === this.tenantId);
|
|
534
|
+
const serious = [...this.store._seriousIncidents.values()].filter((r: any) => r.tenantId === this.tenantId && r.runId === this.runId);
|
|
535
|
+
const judgeScores = [...this.store._judgeScores.values()].filter((r: any) => r.tenantId === this.tenantId && r.runId === this.runId);
|
|
536
|
+
const replays = [...this.store._replayReports.values()].filter((r: any) => r.tenantId === this.tenantId && r.runId === this.runId);
|
|
537
|
+
|
|
538
|
+
const pack = {
|
|
539
|
+
version: V9_PROTOCOL_VERSION, tenant_id: this.tenantId, run_id: this.runId, generated_utc: utcNow(),
|
|
540
|
+
eu_ai_act: { article_12_retention_policies: retention, article_72_post_market_monitoring_plan: new EUAIActRegistry(this.store, this.runId, this.tenantId).postMarketMonitoringPlan(), article_79_serious_incidents: serious },
|
|
541
|
+
observability: { judge_score_count: judgeScores.length },
|
|
542
|
+
chain_integrity: { deterministic_replay_reports: replays.length },
|
|
543
|
+
};
|
|
544
|
+
const packJson = canonicalJson(pack);
|
|
545
|
+
const packHash = sha256Text(packJson);
|
|
546
|
+
const packId = "pack-" + packHash.slice(0, 32);
|
|
547
|
+
this.store._evidencePacks.set(packId, { packId, tenantId: this.tenantId, runId: this.runId, packJson, packHash, createdUtc: utcNow() });
|
|
548
|
+
this.store.appendEvent(this.runId, "evidence_pack_exported_v9", { pack_id: packId, pack_hash: packHash });
|
|
549
|
+
return { pack_id: packId, pack_hash: packHash, pack };
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
554
|
+
// 11. Compatibility Audit + Diagnostics
|
|
555
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
556
|
+
|
|
557
|
+
export function compatibilityAuditV9(): Record<string, unknown> {
|
|
558
|
+
return {
|
|
559
|
+
version: V9_PROTOCOL_VERSION, modifies_v1_through_v8: false, adds_eu_ai_act_registry: true,
|
|
560
|
+
adds_annex_iv_documentation_helper: true, adds_serious_incident_article_79: true,
|
|
561
|
+
adds_retention_policy_enforcement: true, adds_otel_genai_v141_emitter: true,
|
|
562
|
+
adds_pii_secret_redactor: true, adds_llm_judge_panel: true, adds_golden_dataset_curator: true,
|
|
563
|
+
adds_eval_gate_ci_harness: true, adds_cusum_drift_monitor: true, adds_mcp_audit_log: true,
|
|
564
|
+
adds_deterministic_replay_engine: true, adds_evidence_pack_exporter: true,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export interface DiagnosticCheck { id: string; passed: boolean; detail: string; }
|
|
569
|
+
|
|
570
|
+
export async function runInnovationGenomeV9Diagnostics(): Promise<{ ok: boolean; checks: DiagnosticCheck[] }> {
|
|
571
|
+
const checks: DiagnosticCheck[] = [];
|
|
572
|
+
const add = (id: string, passed: boolean, detail: string) => checks.push({ id, passed, detail });
|
|
573
|
+
|
|
574
|
+
const store = new V9CompliancePlaneStore();
|
|
575
|
+
store.createRun("run-v9", 1, "general", "low", { version: "v9-test" });
|
|
576
|
+
|
|
577
|
+
// PII
|
|
578
|
+
const r = new PIIRedactor("K".repeat(32));
|
|
579
|
+
const [redacted, stats] = r.redactText("email a@b.com phone +12025550123 ip 192.168.1.5 iban GB29NWBK60161331926819 pan 4111 1111 1111 1111");
|
|
580
|
+
add("pii-email-redacted", !redacted.includes("a@b.com") && stats.emails >= 1, `emails=${stats.emails}`);
|
|
581
|
+
add("pii-pan-luhn", stats.pan >= 1, `pan=${stats.pan}`);
|
|
582
|
+
const [attrOut] = r.redactAttributes({ authorization: "Bearer secret-xyz", safe: 42 });
|
|
583
|
+
add("pii-key-redacted", String(attrOut.authorization).startsWith("<REDACTED:KEY:"), "sensitive key");
|
|
584
|
+
add("pii-safe-preserved", attrOut.safe === 42, "non-sensitive preserved");
|
|
585
|
+
|
|
586
|
+
// OTel
|
|
587
|
+
const emitter = new OtelGenAIEmitter(new NullOtelExporter(), r);
|
|
588
|
+
const rec = emitter.emitInvokeAgent({ agentId: "a1", agentName: "test", providerName: "static", modelName: "m1", inputTokens: 10, outputTokens: 20, startNs: 1e9, endNs: 1.05e9, success: true });
|
|
589
|
+
add("otel-span-emitted", rec.statusCode === "OK" && rec.name.includes("invoke_agent"), rec.name);
|
|
590
|
+
|
|
591
|
+
// EU AI Act
|
|
592
|
+
const reg = new EUAIActRegistry(store, "run-v9", "tenant-v9");
|
|
593
|
+
let threw = false;
|
|
594
|
+
try { reg.setRetentionPolicy("article_26_deployer_log", 30, 365, "GDPR"); } catch { threw = true; }
|
|
595
|
+
add("euai-art26-minimum-183", threw, "rejects < 183 days");
|
|
596
|
+
const pid = reg.setRetentionPolicy("article_26_deployer_log", 200, 365, "GDPR Art. 6(1)(c)");
|
|
597
|
+
add("euai-retention-set", pid.startsWith("retention-"), pid.slice(0, 20));
|
|
598
|
+
const docId = reg.registerAnnexIVDocumentation(buildAnnexIVDocument({ tenantId: "tenant-v9", runId: "run-v9", systemName: "IGE", intendedPurpose: "Discovery", providerName: "P", deployerName: "D", systemArchitectureSummary: "s", riskManagementSummary: "r", dataGovernanceSummary: "d", performanceMetricsSummary: "p", changeLog: [], harmonisedStandards: [] }));
|
|
599
|
+
add("euai-annex-iv", docId.startsWith("annex-iv-"), docId.slice(0, 20));
|
|
600
|
+
const [sid, reportable] = reg.classifyAndRegisterIncident("Unsafe egress", "fundamental_rights_infringement", "a".repeat(64));
|
|
601
|
+
add("euai-art79-reportable", reportable, sid.slice(0, 20));
|
|
602
|
+
|
|
603
|
+
// Judge
|
|
604
|
+
const panel = new LLMJudgePanel(store, [new StructuralJudge()]);
|
|
605
|
+
const scores = panel.score({ subjectId: "c1", subjectType: "candidate", tenantId: "tenant-v9", runId: "run-v9", inputs: { q: "capital of France" }, output: "The capital of France is Paris. This grounded answer references France Paris.", reference: "France Paris capital", context: "Question about France Paris capital city" });
|
|
606
|
+
const metrics = new Set(scores.map(s => s.metric));
|
|
607
|
+
add("judge-4-metrics", metrics.size === 4, [...metrics].join(","));
|
|
608
|
+
|
|
609
|
+
// Eval Gate
|
|
610
|
+
const curator = new GoldenDatasetCurator(store, "tenant-v9", "run-v9");
|
|
611
|
+
const cid = curator.promoteFailureToCase("prod-golden", "canary", "case-1", { q: "what is Paris" }, { a: "capital of France" }, { topic: "geography" });
|
|
612
|
+
curator.approve(cid, "reviewer-1");
|
|
613
|
+
const gate = new EvalGate(store, "tenant-v9", "run-v9", curator, panel);
|
|
614
|
+
const gateResult = gate.run({ dataset: "prod-golden", candidateRef: "v1", candidateRunner: () => "Paris is the capital of France and is well grounded in Paris France context capital.", thresholds: { minimumPassRate: 0.5 } });
|
|
615
|
+
add("eval-gate-passes", gateResult.passed === true, `overall=${gateResult.overall_pass_rate}`);
|
|
616
|
+
|
|
617
|
+
// Drift
|
|
618
|
+
const mon = new DriftMonitor(store, "tenant-v9", "run-v9", 30, 3.0, 0.5);
|
|
619
|
+
let alarm = false;
|
|
620
|
+
for (let i = 0; i < 30; i++) mon.observe({ metricKey: "judge:g:model:m1", value: 0.9, tsUnix: Date.now() / 1000 + i });
|
|
621
|
+
for (let i = 0; i < 30; i++) { const d = mon.observe({ metricKey: "judge:g:model:m1", value: 0.4, tsUnix: Date.now() / 1000 + 60 + i }); if (d.alarm) { alarm = true; break; } }
|
|
622
|
+
add("drift-cusum-detects-shift", alarm, "alarm triggered");
|
|
623
|
+
|
|
624
|
+
// MCP + Replay
|
|
625
|
+
const log = new MCPAuditLog(store, "run-v9", "tenant-v9", emitter);
|
|
626
|
+
const callId = log.recordCall({ agentId: "agent-1", toolName: "search", toolCallId: "c1", arguments: { q: "x" }, result: { hits: 1 }, status: "ok" });
|
|
627
|
+
add("mcp-call-recorded", callId.startsWith("mcp-"), callId.slice(0, 20));
|
|
628
|
+
|
|
629
|
+
const engine = new DeterministicReplayEngine(store, "run-v9", "tenant-v9");
|
|
630
|
+
const replay = engine.replay();
|
|
631
|
+
add("replay-equivalent", replay.equivalent === true, "chain intact");
|
|
632
|
+
|
|
633
|
+
// Evidence Pack
|
|
634
|
+
const exporter = new EvidencePackExporter(store, "tenant-v9", "run-v9");
|
|
635
|
+
const pack = exporter.export();
|
|
636
|
+
add("evidence-pack", (pack.pack_id as string).startsWith("pack-"), String(pack.pack_hash).slice(0, 16));
|
|
637
|
+
|
|
638
|
+
// Audit
|
|
639
|
+
const audit = compatibilityAuditV9();
|
|
640
|
+
add("audit-v9", audit.modifies_v1_through_v8 === false && audit.adds_eu_ai_act_registry === true, "additive");
|
|
641
|
+
|
|
642
|
+
return { ok: checks.every(c => c.passed), checks };
|
|
643
|
+
}
|