pi-mega-compact 0.16.3 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/config/vector-cortex.js +91 -0
  2. package/dist/dedup/digest.js +30 -0
  3. package/dist/dedup/l1-lsh-v2.js +60 -0
  4. package/dist/dedup/l1-lsh.js +52 -0
  5. package/dist/dedup/l1-minhash-v2.js +193 -0
  6. package/dist/dedup/l1-minhash.js +91 -0
  7. package/dist/dedup/l1-verify.js +54 -0
  8. package/dist/dedup/mmr.js +45 -0
  9. package/dist/dedup/normalize.js +39 -0
  10. package/dist/dedup/raptor/buildHistory.js +164 -0
  11. package/dist/dedup/raptor/guardrails.js +83 -0
  12. package/dist/dedup/raptor/incremental.js +304 -0
  13. package/dist/dedup/raptor/index.js +190 -0
  14. package/dist/dedup/raptor/kmeans.js +152 -0
  15. package/dist/dedup/raptor/multilevel.js +184 -0
  16. package/dist/dedup/raptor/retrieval.js +93 -0
  17. package/dist/dedup/raptor/summarizer.js +86 -0
  18. package/dist/dedup/raptor/tree.js +202 -0
  19. package/dist/dedup/topk.js +60 -0
  20. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry-ext.js +30 -0
  21. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +4 -14
  22. package/dist/extensions/dashboard-server/api-contracts/vector-cortex.js +12 -0
  23. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +11 -0
  24. package/dist/extensions/dashboard-server/routes-vector-cortex.js +292 -0
  25. package/dist/extensions/dashboard-server/routes.js +1 -0
  26. package/dist/extensions/dashboard-server/server.js +9 -1
  27. package/dist/extensions/mega-events/context-handler.js +11 -0
  28. package/dist/extensions/mega-runtime/vector-cortex-ledger.js +131 -0
  29. package/dist/extensions/mega-runtime/vector-cortex-safety.js +100 -0
  30. package/dist/src/config/vector-cortex.js +91 -0
  31. package/dist/src/config.js +6 -0
  32. package/dist/src/dedup/l1-lsh-v2.js +60 -0
  33. package/dist/src/dedup/l1-minhash-v2.js +193 -0
  34. package/dist/src/dedup/normalize.js +1 -1
  35. package/dist/src/vector-cortex/conformance/emit.js +46 -0
  36. package/dist/src/vector-cortex/conformance/manifest.js +235 -0
  37. package/dist/src/vector-cortex/conformance/runner.js +111 -0
  38. package/dist/src/vector-cortex/conformance/triadB-reader.js +95 -0
  39. package/dist/src/vector-cortex/eval/annotations.js +41 -0
  40. package/dist/src/vector-cortex/eval/metrics.js +86 -0
  41. package/dist/src/vector-cortex/eval/observer.js +61 -0
  42. package/dist/src/vector-cortex/eval/persist.js +67 -0
  43. package/dist/src/vector-cortex/eval/reader.js +29 -0
  44. package/dist/src/vector-cortex/eval/types.js +33 -0
  45. package/dist/src/vector-cortex/ledger/adapter.js +55 -0
  46. package/dist/src/vector-cortex/ledger/compat-journal.js +206 -0
  47. package/dist/src/vector-cortex/ledger/emit.js +53 -0
  48. package/dist/src/vector-cortex/ledger/event-codec.js +72 -0
  49. package/dist/src/vector-cortex/ledger/event-codecB.js +76 -0
  50. package/dist/src/vector-cortex/ledger/sqlite.js +200 -0
  51. package/dist/src/vector-cortex/ledger/store.js +184 -0
  52. package/dist/src/vector-cortex/ledger/types.js +52 -0
  53. package/dist/src/vector-cortex/ledger/validator.js +117 -0
  54. package/dist/src/vector-cortex/migrations/effective-cut-v2.js +103 -0
  55. package/dist/src/vector-cortex/migrations/minhash-v2.js +209 -0
  56. package/dist/src/vector-cortex/migrations/occurrence-v2.js +139 -0
  57. package/dist/src/vector-cortex/replay/cut.js +123 -0
  58. package/dist/src/vector-cortex/replay/emit.js +58 -0
  59. package/dist/src/vector-cortex/replay/replay.js +260 -0
  60. package/dist/src/vector-cortex/replay/replayB.js +73 -0
  61. package/dist/src/vector-cortex/replay/types.js +52 -0
  62. package/dist/src/vector-cortex/resilience/breaker-core.js +359 -0
  63. package/dist/src/vector-cortex/resilience/breaker.js +12 -0
  64. package/dist/src/vector-cortex/resilience/emit.js +56 -0
  65. package/dist/src/vector-cortex/resilience/spool-core.js +347 -0
  66. package/dist/src/vector-cortex/resilience/spool.js +12 -0
  67. package/dist/src/vector-cortex/resilience/types.js +32 -0
  68. package/dist/vector-cortex/conformance/emit.js +46 -0
  69. package/dist/vector-cortex/conformance/manifest.js +235 -0
  70. package/dist/vector-cortex/conformance/runner.js +111 -0
  71. package/dist/vector-cortex/conformance/triadB-reader.js +95 -0
  72. package/dist/vector-cortex/eval/annotations.js +41 -0
  73. package/dist/vector-cortex/eval/metrics.js +86 -0
  74. package/dist/vector-cortex/eval/observer.js +61 -0
  75. package/dist/vector-cortex/eval/persist.js +67 -0
  76. package/dist/vector-cortex/eval/reader.js +29 -0
  77. package/dist/vector-cortex/eval/types.js +33 -0
  78. package/dist/vector-cortex/ledger/adapter.js +55 -0
  79. package/dist/vector-cortex/ledger/compat-journal.js +206 -0
  80. package/dist/vector-cortex/ledger/emit.js +53 -0
  81. package/dist/vector-cortex/ledger/event-codec.js +72 -0
  82. package/dist/vector-cortex/ledger/event-codecB.js +76 -0
  83. package/dist/vector-cortex/ledger/sqlite.js +200 -0
  84. package/dist/vector-cortex/ledger/store.js +184 -0
  85. package/dist/vector-cortex/ledger/types.js +52 -0
  86. package/dist/vector-cortex/ledger/validator.js +117 -0
  87. package/dist/vector-cortex/migrations/effective-cut-v2.js +103 -0
  88. package/dist/vector-cortex/migrations/minhash-v2.js +209 -0
  89. package/dist/vector-cortex/migrations/occurrence-v2.js +139 -0
  90. package/dist/vector-cortex/replay/cut.js +123 -0
  91. package/dist/vector-cortex/replay/emit.js +58 -0
  92. package/dist/vector-cortex/replay/replay.js +260 -0
  93. package/dist/vector-cortex/replay/replayB.js +73 -0
  94. package/dist/vector-cortex/replay/types.js +52 -0
  95. package/dist/vector-cortex/resilience/breaker-core.js +359 -0
  96. package/dist/vector-cortex/resilience/breaker.js +12 -0
  97. package/dist/vector-cortex/resilience/emit.js +56 -0
  98. package/dist/vector-cortex/resilience/spool-core.js +347 -0
  99. package/dist/vector-cortex/resilience/spool.js +12 -0
  100. package/dist/vector-cortex/resilience/types.js +32 -0
  101. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js → AreaChart-BQXFF7UI.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js.map → AreaChart-BQXFF7UI.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js → BarChart-BVtoz9Eg.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js.map → BarChart-BVtoz9Eg.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js → CacheTab-trI2TIGy.js} +2 -2
  106. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js.map → CacheTab-trI2TIGy.js.map} +1 -1
  107. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js → EventsTab-CNUbk9__.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js.map → EventsTab-CNUbk9__.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js → HealthTab-K-bd_CM-.js} +2 -2
  110. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js.map → HealthTab-K-bd_CM-.js.map} +1 -1
  111. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js → MaintenanceTab-CoEioKzx.js} +2 -2
  112. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js.map → MaintenanceTab-CoEioKzx.js.map} +1 -1
  113. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js → MemoryMapTab-CJo24ulv.js} +2 -2
  114. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js.map → MemoryMapTab-CJo24ulv.js.map} +1 -1
  115. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js → MetricsTab-DCNlQWWP.js} +2 -2
  116. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js.map → MetricsTab-DCNlQWWP.js.map} +1 -1
  117. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js → OverviewTab-CQQlyIQ6.js} +2 -2
  118. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js.map → OverviewTab-CQQlyIQ6.js.map} +1 -1
  119. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js → ReposTab-Cqm82_yn.js} +2 -2
  120. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js.map → ReposTab-Cqm82_yn.js.map} +1 -1
  121. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js → SessionsTab-A8xTAdET.js} +2 -2
  122. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js.map → SessionsTab-A8xTAdET.js.map} +1 -1
  123. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js → SetupTab-CqaSgaZl.js} +2 -2
  124. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js.map → SetupTab-CqaSgaZl.js.map} +1 -1
  125. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js → TimeSavedCard-C_f3q8GR.js} +2 -2
  126. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js.map → TimeSavedCard-C_f3q8GR.js.map} +1 -1
  127. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js → TurnsTab-BEsafACo.js} +2 -2
  128. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js.map → TurnsTab-BEsafACo.js.map} +1 -1
  129. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD1CMK80.js +2 -0
  130. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD1CMK80.js.map +1 -0
  131. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js → WikiTab-Bf01M2_Z.js} +2 -2
  132. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js.map → WikiTab-Bf01M2_Z.js.map} +1 -1
  133. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js → button-BEJPTk_E.js} +2 -2
  134. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js.map → button-BEJPTk_E.js.map} +1 -1
  135. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js → card-DK1V382s.js} +2 -2
  136. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js.map → card-DK1V382s.js.map} +1 -1
  137. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js → generateCategoricalChart-CRtaPphL.js} +2 -2
  138. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js.map → generateCategoricalChart-CRtaPphL.js.map} +1 -1
  139. package/extensions/dashboard-client/dist/assets/{index-B93cewiX.js → index-CB6BslE4.js} +34 -29
  140. package/extensions/dashboard-client/dist/assets/index-CB6BslE4.js.map +1 -0
  141. package/extensions/dashboard-client/dist/assets/index-CfvziyBi.css +1 -0
  142. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js → switch-C4FV6B9k.js} +2 -2
  143. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js.map → switch-C4FV6B9k.js.map} +1 -1
  144. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js → toggle-GBmeENMd.js} +2 -2
  145. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js.map → toggle-GBmeENMd.js.map} +1 -1
  146. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js → useSSE-BOKZGSn1.js} +2 -2
  147. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js.map → useSSE-BOKZGSn1.js.map} +1 -1
  148. package/extensions/dashboard-client/dist/index.html +2 -2
  149. package/extensions/dashboard-client/src/App.tsx +2 -0
  150. package/extensions/dashboard-client/src/api/vector-cortex.ts +54 -0
  151. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +272 -0
  152. package/extensions/dashboard-client/src/tabs/registry.ts +4 -1
  153. package/extensions/dashboard-client/src/types/vector-cortex.ts +84 -0
  154. package/extensions/dashboard-client/tsconfig.json +1 -1
  155. package/extensions/dashboard-server/api-contracts/endpoints/registry-ext.ts +67 -0
  156. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +4 -21
  157. package/extensions/dashboard-server/api-contracts/index.ts +3 -0
  158. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +136 -0
  159. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +41 -0
  160. package/extensions/dashboard-server/routes-vector-cortex.ts +332 -0
  161. package/extensions/dashboard-server/routes.ts +6 -0
  162. package/extensions/dashboard-server/server.ts +8 -0
  163. package/extensions/mega-events/context-handler.ts +16 -0
  164. package/extensions/mega-runtime/vector-cortex-ledger.ts +165 -0
  165. package/extensions/mega-runtime/vector-cortex-safety.ts +181 -0
  166. package/package.json +2 -1
  167. package/src/config/vector-cortex.ts +98 -0
  168. package/src/config.ts +28 -0
  169. package/src/dedup/l1-lsh-v2.ts +78 -0
  170. package/src/dedup/l1-minhash-v2.ts +221 -0
  171. package/src/dedup/normalize.ts +1 -1
  172. package/src/store/sqlite/game-scores.ts +2 -1
  173. package/src/vector-cortex/conformance/emit.ts +56 -0
  174. package/src/vector-cortex/conformance/manifest.ts +298 -0
  175. package/src/vector-cortex/conformance/runner.ts +165 -0
  176. package/src/vector-cortex/conformance/triadB-reader.ts +110 -0
  177. package/src/vector-cortex/eval/annotations.ts +70 -0
  178. package/src/vector-cortex/eval/metrics.ts +113 -0
  179. package/src/vector-cortex/eval/observer.ts +86 -0
  180. package/src/vector-cortex/eval/persist.ts +73 -0
  181. package/src/vector-cortex/eval/reader.ts +45 -0
  182. package/src/vector-cortex/eval/types.ts +89 -0
  183. package/src/vector-cortex/ledger/adapter.ts +71 -0
  184. package/src/vector-cortex/ledger/compat-journal.ts +234 -0
  185. package/src/vector-cortex/ledger/emit.ts +86 -0
  186. package/src/vector-cortex/ledger/event-codec.ts +79 -0
  187. package/src/vector-cortex/ledger/event-codecB.ts +87 -0
  188. package/src/vector-cortex/ledger/sqlite.ts +265 -0
  189. package/src/vector-cortex/ledger/store.ts +380 -0
  190. package/src/vector-cortex/ledger/types.ts +141 -0
  191. package/src/vector-cortex/ledger/validator.ts +124 -0
  192. package/src/vector-cortex/migrations/effective-cut-v2.ts +149 -0
  193. package/src/vector-cortex/migrations/minhash-v2.ts +263 -0
  194. package/src/vector-cortex/migrations/occurrence-v2.ts +192 -0
  195. package/src/vector-cortex/replay/cut.ts +169 -0
  196. package/src/vector-cortex/replay/emit.ts +89 -0
  197. package/src/vector-cortex/replay/replay.ts +308 -0
  198. package/src/vector-cortex/replay/replayB.ts +97 -0
  199. package/src/vector-cortex/replay/types.ts +145 -0
  200. package/src/vector-cortex/resilience/breaker-core.ts +444 -0
  201. package/src/vector-cortex/resilience/breaker.ts +22 -0
  202. package/src/vector-cortex/resilience/emit.ts +99 -0
  203. package/src/vector-cortex/resilience/spool-core.ts +411 -0
  204. package/src/vector-cortex/resilience/spool.ts +22 -0
  205. package/src/vector-cortex/resilience/types.ts +158 -0
  206. package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +0 -1
  207. package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +0 -1
@@ -0,0 +1,165 @@
1
+ /**
2
+ * vector-cortex/conformance/runner.ts — v2 conformance runner (VC1C).
3
+ *
4
+ * Dispatches a conformance case STRICTLY by domain/version (no inference, no
5
+ * partial output on unknown) and compares both the canonical success bytes and
6
+ * the expected failure code. Owns `DowngradeReport`.
7
+ *
8
+ * The runner is triad-shaped (mode A client):
9
+ * A = this manifest runner over the real algorithms;
10
+ * B = an independent exact fixture reader (invertible digest recheck);
11
+ * C = reject any unknown domain/version WITHOUT partial output.
12
+ *
13
+ * Downgrade export: `exporter` produces a new legacy copy that never edits
14
+ * authority data; the resulting `DowngradeReport` is deterministic — a second
15
+ * run yields a byte-identical report digest (CONF-DOWN-003).
16
+ *
17
+ * No network, no side effects on authority (PREVENT-PI-004 / PREVENT-011).
18
+ */
19
+
20
+ import { CONF_FAIL, type FixtureManifestEntry } from "./manifest.js";
21
+ import {
22
+ createConformanceReporter,
23
+ type ConformanceReporter,
24
+ } from "./emit.js";
25
+
26
+ /** A registered conformance algorithm handler for a (domain, tuple) key. */
27
+ export interface ConformanceHandler {
28
+ /**
29
+ * Run the case. `fixture` is the raw parsed fixture object. Returns ok with a
30
+ * canonical output digest (the success bytes), or a failure code.
31
+ */
32
+ run(
33
+ entry: FixtureManifestEntry,
34
+ fixture: unknown,
35
+ ): { ok: true; outputBytes: Uint8Array; outputDigest: string } | { ok: false; code: string };
36
+ }
37
+
38
+ /**
39
+ * The deterministic downgrade-export report. `reportDigest` is a SHA-256 over
40
+ * the canonical JSON of the report body, so a repeated export is byte-identical.
41
+ */
42
+ export interface DowngradeReport {
43
+ readonly schema: "downgrade-report-v1";
44
+ readonly exportedCopyId: string;
45
+ readonly copiedCount: number;
46
+ readonly unrepresentableIds: readonly string[];
47
+ readonly reportDigest: string;
48
+ }
49
+
50
+ /** A minimal exporter: produces a new legacy copy and its deterministic report. */
51
+ export interface DowngradeExporter {
52
+ /** Run the export once. Must never edit the authority data it reads. */
53
+ exportOnce(): DowngradeReport;
54
+ }
55
+
56
+ /**
57
+ * Result of dispatching a conformance case: success bytes (mode A/B) or the
58
+ * exact expected failure code (mode C rejects unknown without partial output).
59
+ */
60
+ export type CaseResult =
61
+ | { ok: true; outputDigest: string; algorithm: string }
62
+ | { ok: false; code: string; algorithm: string };
63
+
64
+ const UNKNOWN_DOMAIN = "CONF_UNKNOWN_DOMAIN";
65
+ const UNKNOWN_VERSION = "CONF_UNKNOWN_VERSION";
66
+ /**
67
+ * The runner-pinned expectation mismatch code: a dispatched handler returned an
68
+ * outcome that contradicts the manifest entry's frozen expectation (the wrong
69
+ * failure code, or a success/failure parity error). This is the runner's own
70
+ * cross-check (task 5: "compare both success bytes and expected failure code"),
71
+ * distinct from the domain's own failure codes.
72
+ */
73
+ const EXPECTATION_MISMATCH = "CONF_EXPECTATION_MISMATCH";
74
+
75
+ export { UNKNOWN_DOMAIN, UNKNOWN_VERSION, EXPECTATION_MISMATCH };
76
+
77
+ /**
78
+ * Run one conformance case by dispatching strictly on `(domain, algorithmTuple)`.
79
+ * If no handler is registered for the exact domain/algorithms, returns
80
+ * `CONF_UNKNOWN_DOMAIN`/`CONF_UNKNOWN_VERSION` WITHOUT partial output.
81
+ *
82
+ * After dispatch the runner cross-checks the handler's outcome against the
83
+ * manifest entry's frozen expectation (task 5):
84
+ * - failure expected (`entry.failureCode` set): the handler must return
85
+ * `ok:false` with that exact code, else `CONF_EXPECTATION_MISMATCH`;
86
+ * - success expected (`entry.expected === "ok"`): the handler must return
87
+ * `ok:true`, and when the manifest pins an `expectedOutputDigest` the
88
+ * handler's `outputDigest` must match it, else `CONF_DIGEST_DRIFT`.
89
+ */
90
+ export function runConformanceCase(
91
+ entry: FixtureManifestEntry,
92
+ handlers: ReadonlyMap<string, ConformanceHandler>,
93
+ fixture: unknown,
94
+ reporter: ConformanceReporter = createConformanceReporter(),
95
+ ): CaseResult {
96
+ const key = `${entry.domain}::${entry.algorithmTuple.join(";")}`;
97
+ const handler = handlers.get(key);
98
+ let result: CaseResult;
99
+ if (!handler) {
100
+ if (entry.domain === "") {
101
+ result = { ok: false, code: UNKNOWN_DOMAIN, algorithm: entry.algorithm };
102
+ } else {
103
+ result = { ok: false, code: UNKNOWN_VERSION, algorithm: entry.algorithm };
104
+ }
105
+ } else {
106
+ const out = handler.run(entry, fixture);
107
+ if (out.ok) {
108
+ // Success expected? The manifest entry pins the expected success bytes.
109
+ if (entry.expected !== "ok") {
110
+ result = { ok: false, code: EXPECTATION_MISMATCH, algorithm: entry.algorithm };
111
+ } else if (
112
+ entry.expectedOutputDigest !== undefined &&
113
+ out.outputDigest !== entry.expectedOutputDigest
114
+ ) {
115
+ result = { ok: false, code: CONF_FAIL.DIGEST_DRIFT, algorithm: entry.algorithm };
116
+ } else {
117
+ result = { ok: true, outputDigest: out.outputDigest, algorithm: entry.algorithm };
118
+ }
119
+ } else {
120
+ // Failure expected? The manifest entry pins the exact failure code.
121
+ if (entry.failureCode !== undefined && out.code === entry.failureCode) {
122
+ result = { ok: false, code: out.code, algorithm: entry.algorithm };
123
+ } else {
124
+ result = { ok: false, code: EXPECTATION_MISMATCH, algorithm: entry.algorithm };
125
+ }
126
+ }
127
+ }
128
+ reporter.caseChecked({
129
+ id: entry.id,
130
+ domain: entry.domain,
131
+ algorithm: entry.algorithm,
132
+ ok: result.ok,
133
+ ...(result.ok ? { outputDigest: result.outputDigest } : { code: result.code }),
134
+ });
135
+ return result;
136
+ }
137
+
138
+ /**
139
+ * Build a dispatch key for a (domain, algorithmTuple) pair. Callers register
140
+ * handlers under this key.
141
+ */
142
+ export function handlerKey(
143
+ domain: string,
144
+ algorithmTuple: readonly string[],
145
+ ): string {
146
+ return `${domain}::${algorithmTuple.join(";")}`;
147
+ }
148
+
149
+ /**
150
+ * Run a downgrade export and recompute its report digest deterministically.
151
+ * A second invocation produces a byte-identical report (CONF-DOWN-003).
152
+ */
153
+ export function runDowngradeExport(
154
+ exporter: DowngradeExporter,
155
+ reporter: ConformanceReporter = createConformanceReporter(),
156
+ ): DowngradeReport {
157
+ const report = exporter.exportOnce();
158
+ reporter.downgradeWritten({
159
+ exportedCopyId: report.exportedCopyId,
160
+ copiedCount: report.copiedCount,
161
+ unrepresentableIds: report.unrepresentableIds,
162
+ reportDigest: report.reportDigest,
163
+ });
164
+ return report;
165
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * conformance/triadB-reader.ts — TRIAD-B independent exact reader (VC1C).
3
+ *
4
+ * Deliberately shares NO code and NO cached-seed instance with the runner
5
+ * modules (l1-minhash-v2.ts / l1-lsh-v2.ts). It re-derives the minhash-v2
6
+ * signature and 64 bucket keys from first principles, using only the PUBLISHED
7
+ * (a,b) seed pairs passed in (read by the caller from
8
+ * conformance/.../minhash/seeds-v2.json — the table authored independently by
9
+ * scripts/gen-fixtures/minhash.mjs, the same process that produced the frozen
10
+ * fixture bytes). Because B shares no subroutine with the runner, a systematic
11
+ * error in the runner's splitmix64 / seed-derivation / signature arithmetic
12
+ * cannot satisfy both triad A and triad B.
13
+ *
14
+ * Pure compute, no deps, no storage, no network (PREVENT-PI-004 / PREVENT-011).
15
+ */
16
+
17
+ import { createHash } from "node:crypto";
18
+
19
+ const MASK64 = 0xffffffffffffffffn;
20
+ const P = (1n << 61n) - 1n; // 2^61 - 1
21
+ const SLOTS = 256;
22
+ const U64 = 8;
23
+ const BANDS = 64;
24
+
25
+ /** A published (a, b) seed pair as decimal strings (exactness beyond 2^53). */
26
+ export interface PublishedSeedPair {
27
+ readonly a: string;
28
+ readonly b: string;
29
+ }
30
+
31
+ /** 64-bit FNV-1a over UTF-8 bytes or a raw byte buffer (own, independent loop). */
32
+ function fnv1a64(input: string | Uint8Array): bigint {
33
+ let h = 0xcbf29ce484222325n;
34
+ for (const b of Buffer.from(input)) {
35
+ h = ((h ^ BigInt(b)) * 0x100000001b3n) & MASK64;
36
+ }
37
+ return h;
38
+ }
39
+
40
+ /**
41
+ * Re-derive the 2048-byte minhash-v2 signature and (optionally) the 64 LSH
42
+ * bucket keys for `text` within `session`, from published seed pairs only —
43
+ * never the runner module.
44
+ */
45
+ export function independentReaderV2(
46
+ text: string,
47
+ session: string,
48
+ seedPairs: readonly PublishedSeedPair[],
49
+ withBuckets: boolean,
50
+ ): { bytes: Uint8Array; digest: string; buckets: string[] } {
51
+ // Normalization: ANSI-strip, NFC, case-fold, CRLF->LF, whitespace collapse,
52
+ // 32K cap — mirrored from the frozen VC1C spec.
53
+ let norm = (text || "")
54
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
55
+ .normalize("NFC")
56
+ .toLowerCase()
57
+ .replace(/\r\n?/g, "\n")
58
+ .replace(/\s+/g, " ")
59
+ .trim();
60
+ if (norm.length > 32768) norm = norm.slice(0, 32768);
61
+
62
+ // Five-code-point shingles (deduped, capped at 50_000).
63
+ const grams: bigint[] = [];
64
+ if (norm.length > 0) {
65
+ const cps = Array.from(norm);
66
+ const seen = new Set<bigint>();
67
+ const short = cps.length < 5;
68
+ for (let i = 0; i < (short ? 1 : cps.length - 4); i++) {
69
+ const h = short ? fnv1a64(norm) : fnv1a64(cps.slice(i, i + 5).join(""));
70
+ if (!seen.has(h)) {
71
+ seen.add(h);
72
+ grams.push(h);
73
+ }
74
+ if (!short && seen.size >= 50000) break;
75
+ }
76
+ }
77
+
78
+ // Signature: slot i = min over shingles of (a_i*x + b_i) mod p (exact BigInt).
79
+ const sig = new Array<bigint>(SLOTS).fill(P);
80
+ for (let i = 0; i < SLOTS && grams.length > 0; i++) {
81
+ const pair = seedPairs[i] as PublishedSeedPair;
82
+ const a = BigInt(pair.a);
83
+ const b = BigInt(pair.b);
84
+ let min = P;
85
+ for (const x of grams) {
86
+ const h = (a * x + b) % P;
87
+ if (h < min) min = h;
88
+ }
89
+ sig[i] = min;
90
+ }
91
+
92
+ // Own 256 x u64 little-endian encoder.
93
+ const buf = Buffer.allocUnsafe(SLOTS * U64);
94
+ for (let i = 0; i < SLOTS; i++) buf.writeBigUInt64LE(sig[i] as bigint, i * U64);
95
+ const bytes = new Uint8Array(buf);
96
+ const digest = createHash("sha256").update(bytes).digest("hex");
97
+
98
+ // 64 buckets: FNV-1a over `<session>|v2|` + each 32-byte band (LE hex).
99
+ const buckets: string[] = [];
100
+ if (withBuckets) {
101
+ const prefix = Buffer.from(`${session}|v2|`, "utf8");
102
+ for (let band = 0; band < BANDS; band++) {
103
+ const bandBytes = Buffer.from(bytes.slice(band * 32, band * 32 + 32));
104
+ const out = Buffer.allocUnsafe(U64);
105
+ out.writeBigUInt64LE(fnv1a64(Buffer.concat([prefix, bandBytes])), 0);
106
+ buckets.push(`b${band}:${out.toString("hex")}`);
107
+ }
108
+ }
109
+ return { bytes, digest, buckets };
110
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * vector-cortex/eval/annotations.ts — redaction metadata + JSONL serialization.
3
+ *
4
+ * Payload bytes, prompts, and exact ledger text are NEVER serialized. Before a
5
+ * JSONL line is written, each such field is replaced by a SHA-256 digest + byte
6
+ * count + redaction kind (EVAL-REDACT-002: prompt bytes never appear in JSONL).
7
+ * The redaction journal is itself the AnnotationV1 output.
8
+ *
9
+ * Uses node:crypto for the digest (pure local math, PREVENT-PI-004 safe).
10
+ */
11
+
12
+ import { createHash } from "node:crypto";
13
+ import type { AnnotationV1 } from "./types.js";
14
+
15
+ export type RedactKind = "payload" | "prompt" | "ledger";
16
+
17
+ /** A raw content field awaiting redaction before serialization. */
18
+ export interface RawContent {
19
+ /** Field name in the original annotation. */
20
+ readonly field: string;
21
+ /** Kind of content — determines the redaction reason. */
22
+ readonly kind: RedactKind;
23
+ /** Original bytes; digested, never serialized. */
24
+ readonly bytes: Uint8Array;
25
+ }
26
+
27
+ /** Canonical serialization of an annotation: digest/count metadata only. */
28
+ export interface SerializedAnnotation {
29
+ readonly jsonl: string;
30
+ readonly annotation: AnnotationV1;
31
+ }
32
+
33
+ /** SHA-256 over bytes, unpadded base64 (conformance canonical binary form). */
34
+ export function digestBytes(bytes: Uint8Array): string {
35
+ return createHash("sha256").update(bytes).digest("base64");
36
+ }
37
+
38
+ /** Replace every raw content field with its digest/count metadata. */
39
+ export function redactAnnotation(
40
+ itemId: string,
41
+ contents: ReadonlyArray<RawContent>,
42
+ ): AnnotationV1 {
43
+ const redactions = contents.map((c) => ({
44
+ field: c.field,
45
+ digest: digestBytes(c.bytes),
46
+ bytes: c.bytes.byteLength,
47
+ kind: c.kind,
48
+ }));
49
+ return {
50
+ itemId,
51
+ redactions,
52
+ redactedCount: redactions.length,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Serialize one set of raw contents to a single redacted JSONL line. Returns
58
+ * the annotation with its digest metadata and the JSONL text; the raw bytes
59
+ * never appear in the returned string.
60
+ */
61
+ export function serializeRedactedJsonl(
62
+ itemId: string,
63
+ contents: ReadonlyArray<RawContent>,
64
+ ): SerializedAnnotation {
65
+ const annotation = redactAnnotation(itemId, contents);
66
+ return {
67
+ annotation,
68
+ jsonl: `${JSON.stringify(annotation)}\n`,
69
+ };
70
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * vector-cortex/eval/metrics.ts — canonical metric ordering + fixed histogram.
3
+ *
4
+ * 1. Stable sort rows by `(session, seq, event)` — session string byte order,
5
+ * then integer seq ascending, then event-name byte order as tiebreak
6
+ * (EVAL-ORDER-003: equal seq rows use event-name order).
7
+ * 2. Bucket latency values at exactly 1/5/10/25/50/100/250ms with inclusive
8
+ * boundaries (EVAL-BUCKET-001); values past 250ms kept in a separate
9
+ * overflow bucket. Histogram totals are permutation-stable.
10
+ *
11
+ * Pure/no side effects (PREVENT-PI-004).
12
+ */
13
+
14
+ import { LATENCY_BUCKETS } from "./types.js";
15
+ import type { MetricEventV1, EvalReject } from "./types.js";
16
+
17
+ /** Inclusive-boundary bucket edges, with a sentinel past the largest edge. */
18
+ const EDGES: readonly number[] = LATENCY_BUCKETS;
19
+ const OVERFLOW_INDEX = EDGES.length;
20
+
21
+ /** Histogram cell counts aligned to EDGES, plus a trailing overflow cell. */
22
+ export function emptyHistogram(): number[] {
23
+ return new Array<number>(EDGES.length + 1).fill(0);
24
+ }
25
+
26
+ /**
27
+ * Per-session histogram of `value` (when the sample is in `unit`), plus a
28
+ * non-latency count kept separate so totals are permutation-stable.
29
+ */
30
+ export interface LatencyHistogram {
31
+ /** Cells: one per bucket edge, then overflow. Sum == counted latency samples. */
32
+ cells: number[];
33
+ /** Bucket label for each cell (overflow label is "250+" passed via >250). */
34
+ edges: readonly number[];
35
+ /** Number of latency samples that landed past the largest edge. */
36
+ overflow: number;
37
+ /** Total latency samples bucketed (cells sum). */
38
+ total: number;
39
+ }
40
+
41
+ export interface MetricsResult {
42
+ /** Rows in canonical `(session, seq, event)` order. */
43
+ readonly rows: readonly MetricEventV1[];
44
+ /** Per-session latency histograms (mode A observed). */
45
+ readonly histogram: LatencyHistogram;
46
+ /** Rejections encountered while validating streaming input. */
47
+ readonly rejects: readonly EvalReject[];
48
+ }
49
+
50
+ /** True when a sample carries a latency (`unit === "ms"`) datum. */
51
+ export function isLatencySample(m: MetricEventV1): boolean {
52
+ return m.unit === "ms";
53
+ }
54
+
55
+ /** Bucket a latency ms value into a cell index (0..EDGES.length, last=overflow). */
56
+ export function bucketIndex(value: number): number {
57
+ if (!Number.isFinite(value) || value < 0) return OVERFLOW_INDEX;
58
+ for (let i = 0; i < EDGES.length; i++) {
59
+ if (value <= EDGES[i]) return i; // inclusive upper boundary
60
+ }
61
+ return OVERFLOW_INDEX;
62
+ }
63
+
64
+ /**
65
+ * Bucket latency sample values across all rows into the fixed histogram.
66
+ * Overflow is kept separate (counted in `overflow` and the trailing cell).
67
+ */
68
+ export function bucketHistogram(rows: readonly MetricEventV1[]): LatencyHistogram {
69
+ const cells = emptyHistogram();
70
+ let overflow = 0;
71
+ for (const m of rows) {
72
+ if (!isLatencySample(m)) continue;
73
+ const idx = bucketIndex(m.value);
74
+ cells[idx] += 1;
75
+ if (idx === EDGES.length) overflow += 1;
76
+ }
77
+ return {
78
+ cells,
79
+ edges: [...EDGES],
80
+ overflow,
81
+ total: cells.reduce((a, b) => a + b, 0),
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Stable sort MetricEventV1 rows by `(session, seq, event)`:
87
+ * - session: UTF-8 / JS string byte order;
88
+ * - seq: ascending numeric;
89
+ * - event: ascending string byte order (tiebreak for equal seq).
90
+ * Equal content stays separate occurrences (never deduplicated here).
91
+ */
92
+ export function sortCanonical(rows: readonly MetricEventV1[]): MetricEventV1[] {
93
+ const out = [...rows];
94
+ out.sort((a, b) => {
95
+ const s = a.session < b.session ? -1 : a.session > b.session ? 1 : 0;
96
+ if (s !== 0) return s;
97
+ if (a.seq !== b.seq) return a.seq - b.seq;
98
+ return a.event < b.event ? -1 : a.event > b.event ? 1 : 0;
99
+ });
100
+ return out;
101
+ }
102
+
103
+ /**
104
+ * Assisted sort plus histogram, returning both under one result.
105
+ * The observer (mode A) calls this before JSONL serialization.
106
+ */
107
+ export function buildMetrics(rows: readonly MetricEventV1[]): MetricsResult {
108
+ return {
109
+ rows: sortCanonical(rows),
110
+ histogram: bucketHistogram(rows),
111
+ rejects: [],
112
+ };
113
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * vector-cortex/eval/observer.ts — VC0A structured observer (triad mode A).
3
+ *
4
+ * Connects to the evaluation reader: accepts MetricEventV1 samples, maintains
5
+ * the canonical `(session, seq, event)` ordered set plus the fixed latency
6
+ * histogram, and emits two structured local events:
7
+ * - vector_cortex_eval_sample_recorded (one per accepted sample)
8
+ * - vector_cortex_eval_redaction_rejected (when a redaction fails)
9
+ *
10
+ * Mode C (observer absent) is the responsibility of the caller: no observes(),
11
+ * no writes, byte-identical to the predecessor. This module never touches the
12
+ * network (PREVENT-PI-004) — it only manipulates in-memory rows and emits
13
+ * structured log lines through an injected logging callback so it stays
14
+ * pi-agnostic and deterministically testable.
15
+ */
16
+
17
+ import { buildMetrics, sortCanonical } from "./metrics.js";
18
+ import type { MetricEventV1 } from "./types.js";
19
+
20
+ export interface EvalObserverOptions {
21
+ /** Callback emitting structured log lines (ts + event). Swallows errors. */
22
+ readonly emit: (event: string, fields: Record<string, unknown>) => void;
23
+ /**
24
+ * Optional persistence callback for one accepted sample (redacted metric
25
+ * row). The pure observer stays in-memory; a host supplies this to bridge
26
+ * the observer to disk JSONL for the separate-process dashboard reader.
27
+ * Swallows errors — best-effort, non-fatal.
28
+ */
29
+ readonly persist?: (sample: MetricEventV1) => void;
30
+ }
31
+
32
+ export interface EvalObserver {
33
+ /** Record one sample; emits vector_cortex_eval_sample_recorded. */
34
+ record(sample: MetricEventV1): void;
35
+ /** Report a redaction failure (raw content that could not be redacted). */
36
+ rejectRedaction(field: string, reason: string): void;
37
+ /** Canonical-ordered rows seen so far. */
38
+ rows(): readonly MetricEventV1[];
39
+ }
40
+
41
+ /**
42
+ * Build a structured observer (mode A). Best-effort: emitting never throws
43
+ * into the extension path (non-fatal observability).
44
+ */
45
+ export function createEvalObserver(opts: EvalObserverOptions): EvalObserver {
46
+ const rows: MetricEventV1[] = [];
47
+
48
+ function emit(event: string, fields: Record<string, unknown>): void {
49
+ try {
50
+ opts.emit(event, fields);
51
+ } catch {
52
+ /* non-fatal observability — never break the agent loop */
53
+ }
54
+ }
55
+
56
+ return {
57
+ record(sample) {
58
+ rows.push(sample);
59
+ if (opts.persist) {
60
+ try {
61
+ opts.persist(sample);
62
+ } catch {
63
+ /* non-fatal observability */
64
+ }
65
+ }
66
+ emit("vector_cortex_eval_sample_recorded", {
67
+ session: sample.session,
68
+ seq: sample.seq,
69
+ event: sample.event,
70
+ unit: sample.unit,
71
+ mode: sample.mode,
72
+ });
73
+ },
74
+ rejectRedaction(field, reason) {
75
+ emit("vector_cortex_eval_redaction_rejected", { field, reason });
76
+ },
77
+ rows() {
78
+ return sortCanonical(rows);
79
+ },
80
+ };
81
+ }
82
+
83
+ /** Aggregate metrics result from an observer's rows (canonical + histogram). */
84
+ export function observerMetrics(result: { rows: () => readonly MetricEventV1[] }) {
85
+ return buildMetrics(result.rows() as MetricEventV1[]);
86
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * vector-cortex/eval/persist.ts — redacted eval JSONL persistence (VC0A).
3
+ *
4
+ * Bridges the in-memory observer (mode A) and the dashboard reader (separate
5
+ * process) via a disk JSONL under the state dir. Writes are append-only and
6
+ * hold only MetricEventV1 metric rows plus redaction metadata — never payloads,
7
+ * prompts, or exact ledger text (EVAL-REDACT-002). Best-effort and non-fatal
8
+ * (PREVENT-PI-004: local filesystem only; no network).
9
+ *
10
+ * PREVENT-011: no `any` type.
11
+ */
12
+
13
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import type { MetricEventV1 } from "./types.js";
16
+
17
+ /** Location of the redacted evaluation JSONL under a state dir. */
18
+ export function evalJsonlPath(stateDir: string): string {
19
+ return join(stateDir, "vector-cortex", "evaluation.jsonl");
20
+ }
21
+
22
+ /** Append one redacted metric row as a canonical JSON line (append-only). */
23
+ export function appendEvalRow(
24
+ stateDir: string,
25
+ row: readonly MetricEventV1[],
26
+ ): void {
27
+ try {
28
+ const path = evalJsonlPath(stateDir);
29
+ mkdirSync(join(stateDir, "vector-cortex"), { recursive: true, mode: 0o700 });
30
+ // PER_EVAL_PERMISSION: 0600 file inherits state-dir permission policy.
31
+ writeFileSync(path, row.map((r) => `${JSON.stringify(r)}\n`).join(""), {
32
+ flag: "a",
33
+ mode: 0o600,
34
+ });
35
+ // guardrails-allow PREVENT-PI-004: local filesystem eval JSONL (no network)
36
+ } catch {
37
+ /* non-fatal observability — never break the agent loop */
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Read every redacted metric row from the JSONL. Malformed or truncated final
43
+ * lines are skipped (callers surface EVAL_JSONL_TRUNCATED); the caller decides
44
+ * whether that is fatal. Returns an empty array when the file is absent.
45
+ */
46
+ export function readEvalRows(stateDir: string): MetricEventV1[] {
47
+ const out: MetricEventV1[] = [];
48
+ let raw: string;
49
+ try {
50
+ raw = readFileSync(evalJsonlPath(stateDir), "utf8");
51
+ } catch {
52
+ return out;
53
+ }
54
+ for (const line of raw.split("\n")) {
55
+ if (line.trim().length === 0) continue;
56
+ try {
57
+ const parsed = JSON.parse(line) as Partial<MetricEventV1>;
58
+ if (
59
+ typeof parsed.session === "string" &&
60
+ typeof parsed.seq === "number" &&
61
+ typeof parsed.event === "string" &&
62
+ typeof parsed.value === "number" &&
63
+ typeof parsed.unit === "string" &&
64
+ (parsed.mode === "A" || parsed.mode === "B" || parsed.mode === "C")
65
+ ) {
66
+ out.push(parsed as MetricEventV1);
67
+ }
68
+ } catch {
69
+ /* malformed line — skip; EVAL_JSONL_TRUNCATED surfaced by caller */
70
+ }
71
+ }
72
+ return out;
73
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * vector-cortex/eval/reader.ts — evaluation reader (aggregate-only).
3
+ *
4
+ * Reader-only capability: `summarizeEvalRows` collapses a set of MetricEventV1
5
+ * rows into an aggregate summary for the dashboard `GET /api/vector-cortex/
6
+ * evaluation` endpoint. No writable capability — the observer writes via its
7
+ * own path; this reader never mutates. Predecessor (flag-off / mode C) emits
8
+ * an empty summary byte-identical to "no data".
9
+ *
10
+ * Pure + local, zero network (PREVENT-PI-004).
11
+ */
12
+
13
+ import { buildMetrics } from "./metrics.js";
14
+ import type { MetricEventV1 } from "./types.js";
15
+
16
+ export interface EvalSummary {
17
+ /** Samples aggregated. */
18
+ readonly samples: number;
19
+ /** Per-mode distribution (A/B/C). */
20
+ readonly byMode: Readonly<{ A: number; B: number; C: number }>;
21
+ /** Fixed latency histogram with its inclusive edges + separate overflow. */
22
+ readonly histogram: {
23
+ readonly edges: readonly number[];
24
+ readonly cells: readonly number[];
25
+ readonly overflow: number;
26
+ readonly total: number;
27
+ };
28
+ }
29
+
30
+ /** Aggregate rows into a reader-only summary (histogram + per-mode counts). */
31
+ export function summarizeEvalRows(rows: readonly MetricEventV1[]): EvalSummary {
32
+ const byMode = { A: 0, B: 0, C: 0 };
33
+ for (const r of rows) byMode[r.mode] += 1;
34
+ const metrics = buildMetrics(rows as MetricEventV1[]);
35
+ return {
36
+ samples: rows.length,
37
+ byMode,
38
+ histogram: {
39
+ edges: metrics.histogram.edges,
40
+ cells: metrics.histogram.cells,
41
+ overflow: metrics.histogram.overflow,
42
+ total: metrics.histogram.total,
43
+ },
44
+ };
45
+ }