pi-mega-compact 0.17.0 → 0.18.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/dist/config/vector-cortex.js +20 -0
- package/dist/dedup/digest.js +30 -0
- package/dist/dedup/l1-lsh-v2.js +60 -0
- package/dist/dedup/l1-lsh.js +52 -0
- package/dist/dedup/l1-minhash-v2.js +193 -0
- package/dist/dedup/l1-minhash.js +91 -0
- package/dist/dedup/l1-verify.js +54 -0
- package/dist/dedup/mmr.js +45 -0
- package/dist/dedup/normalize.js +39 -0
- package/dist/dedup/raptor/buildHistory.js +164 -0
- package/dist/dedup/raptor/guardrails.js +83 -0
- package/dist/dedup/raptor/incremental.js +304 -0
- package/dist/dedup/raptor/index.js +190 -0
- package/dist/dedup/raptor/kmeans.js +152 -0
- package/dist/dedup/raptor/multilevel.js +184 -0
- package/dist/dedup/raptor/retrieval.js +93 -0
- package/dist/dedup/raptor/summarizer.js +86 -0
- package/dist/dedup/raptor/tree.js +202 -0
- package/dist/dedup/topk.js +60 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +2 -0
- package/dist/extensions/mega-events/context-handler/dbMirrorAppend.js +63 -0
- package/dist/extensions/mega-events/context-handler/gateCheck.js +59 -0
- package/dist/extensions/mega-events/context-handler/liveTrim.js +178 -0
- package/dist/extensions/mega-events/context-handler/pipelineRun.js +37 -0
- package/dist/extensions/mega-events/context-handler.js +39 -305
- package/dist/src/config/vector-cortex.js +20 -0
- package/dist/src/config.js +1 -1
- package/dist/src/dedup/l1-lsh-v2.js +60 -0
- package/dist/src/dedup/l1-minhash-v2.js +193 -0
- package/dist/src/dedup/normalize.js +1 -1
- package/dist/src/vector-cortex/conformance/emit.js +46 -0
- package/dist/src/vector-cortex/conformance/manifest.js +235 -0
- package/dist/src/vector-cortex/conformance/runner.js +111 -0
- package/dist/src/vector-cortex/conformance/triadB-reader.js +95 -0
- package/dist/src/vector-cortex/encoder/asset.js +142 -0
- package/dist/src/vector-cortex/encoder/emit.js +42 -0
- package/dist/src/vector-cortex/encoder/runtime.js +228 -0
- package/dist/src/vector-cortex/encoder/types.js +85 -0
- package/dist/src/vector-cortex/migrations/minhash-v2.js +209 -0
- package/dist/vector-cortex/conformance/emit.js +46 -0
- package/dist/vector-cortex/conformance/manifest.js +235 -0
- package/dist/vector-cortex/conformance/runner.js +111 -0
- package/dist/vector-cortex/conformance/triadB-reader.js +95 -0
- package/dist/vector-cortex/encoder/asset.js +142 -0
- package/dist/vector-cortex/encoder/emit.js +42 -0
- package/dist/vector-cortex/encoder/runtime.js +228 -0
- package/dist/vector-cortex/encoder/types.js +85 -0
- package/dist/vector-cortex/migrations/minhash-v2.js +209 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-CAnWomhw.js → AreaChart-BQXFF7UI.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CAnWomhw.js.map → AreaChart-BQXFF7UI.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-lZ2-s71k.js → BarChart-BVtoz9Eg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-lZ2-s71k.js.map → BarChart-BVtoz9Eg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-BL2Thh66.js → CacheTab-trI2TIGy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-BL2Thh66.js.map → CacheTab-trI2TIGy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DEh2Z9fp.js → EventsTab-CNUbk9__.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DEh2Z9fp.js.map → EventsTab-CNUbk9__.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-DTwKclxk.js → HealthTab-K-bd_CM-.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-DTwKclxk.js.map → HealthTab-K-bd_CM-.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DDfBEdbX.js → MaintenanceTab-CoEioKzx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DDfBEdbX.js.map → MaintenanceTab-CoEioKzx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dgfmg-0Q.js → MemoryMapTab-CJo24ulv.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dgfmg-0Q.js.map → MemoryMapTab-CJo24ulv.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DOcMm0Tx.js → MetricsTab-DCNlQWWP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DOcMm0Tx.js.map → MetricsTab-DCNlQWWP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DHT5aoHg.js → OverviewTab-CQQlyIQ6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DHT5aoHg.js.map → OverviewTab-CQQlyIQ6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-CGAmtR_c.js → ReposTab-Cqm82_yn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-CGAmtR_c.js.map → ReposTab-Cqm82_yn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BIgx6iKT.js → SessionsTab-A8xTAdET.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BIgx6iKT.js.map → SessionsTab-A8xTAdET.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-ADghKuEI.js → SetupTab-CqaSgaZl.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-ADghKuEI.js.map → SetupTab-CqaSgaZl.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-C33UiiOM.js → TimeSavedCard-C_f3q8GR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-C33UiiOM.js.map → TimeSavedCard-C_f3q8GR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DdbL9wPN.js → TurnsTab-BEsafACo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DdbL9wPN.js.map → TurnsTab-BEsafACo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD1CMK80.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD1CMK80.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-Ch00OFKs.js → WikiTab-Bf01M2_Z.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-Ch00OFKs.js.map → WikiTab-Bf01M2_Z.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-ByeEdo2g.js → button-BEJPTk_E.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-ByeEdo2g.js.map → button-BEJPTk_E.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-DesoLXvc.js → card-DK1V382s.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-DesoLXvc.js.map → card-DK1V382s.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CCAB7Iz4.js → generateCategoricalChart-CRtaPphL.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CCAB7Iz4.js.map → generateCategoricalChart-CRtaPphL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-B6cgyAPr.js → index-CB6BslE4.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-B6cgyAPr.js.map → index-CB6BslE4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-DIZC-oe-.js → switch-C4FV6B9k.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-DIZC-oe-.js.map → switch-C4FV6B9k.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CIoWaH2e.js → toggle-GBmeENMd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CIoWaH2e.js.map → toggle-GBmeENMd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-Bv4943l1.js → useSSE-BOKZGSn1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-Bv4943l1.js.map → useSSE-BOKZGSn1.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +1 -1
- package/extensions/dashboard-client/tsconfig.json +1 -1
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +12 -0
- package/extensions/mega-events/context-handler/dbMirrorAppend.ts +93 -0
- package/extensions/mega-events/context-handler/gateCheck.ts +101 -0
- package/extensions/mega-events/context-handler/liveTrim.ts +241 -0
- package/extensions/mega-events/context-handler/pipelineRun.ts +79 -0
- package/extensions/mega-events/context-handler.ts +45 -347
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +22 -0
- package/src/config.ts +2 -0
- package/src/dedup/l1-lsh-v2.ts +78 -0
- package/src/dedup/l1-minhash-v2.ts +221 -0
- package/src/dedup/normalize.ts +1 -1
- package/src/store/sqlite/game-scores.ts +2 -1
- package/src/vector-cortex/conformance/emit.ts +56 -0
- package/src/vector-cortex/conformance/manifest.ts +298 -0
- package/src/vector-cortex/conformance/runner.ts +165 -0
- package/src/vector-cortex/conformance/triadB-reader.ts +110 -0
- package/src/vector-cortex/encoder/asset.ts +155 -0
- package/src/vector-cortex/encoder/emit.ts +51 -0
- package/src/vector-cortex/encoder/runtime.ts +283 -0
- package/src/vector-cortex/encoder/types.ts +165 -0
- package/src/vector-cortex/migrations/minhash-v2.ts +263 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js.map +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,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/asset.ts — VC2A asset verification (task 2).
|
|
3
|
+
*
|
|
4
|
+
* Verifies a ModelManifestV1 before any allocation: SHA-256 the ONNX and
|
|
5
|
+
* tokenizer against the manifest digests, require opset 17, batch exactly 1 and
|
|
6
|
+
* maximum 512 tokens, and confirm the current platform is in the supported
|
|
7
|
+
* matrix. On ANY of these the caller demotes to mode B (asset-free trigram) —
|
|
8
|
+
* never a remote fetch (PREVENT-PI-004). A truncated/unreadable asset during
|
|
9
|
+
* the digest read demotes with ENC_ASSET_UNREADABLE.
|
|
10
|
+
*
|
|
11
|
+
* Pi-agnostic. Filesystem reads only, zero network (PREVENT-PI-004).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import { readFileSync, statSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import {
|
|
18
|
+
ENC_FAIL,
|
|
19
|
+
ENCODER_BATCH,
|
|
20
|
+
ENCODER_MAX_TOKENS,
|
|
21
|
+
ENCODER_OPSET,
|
|
22
|
+
ENCODER_SUPPORTED_PLATFORMS,
|
|
23
|
+
type EncoderPlatform,
|
|
24
|
+
type ModelManifestV1,
|
|
25
|
+
} from "./types.js";
|
|
26
|
+
|
|
27
|
+
export type AssetVerifyResult =
|
|
28
|
+
| { ok: true; embeddedBytes: number; maxTokens: number; onnxDigest: string; tokenizerDigest: string }
|
|
29
|
+
| { ok: false; code: string };
|
|
30
|
+
|
|
31
|
+
/** True when `p` is a single basename: non-empty, no path separators, no "..",
|
|
32
|
+
* no leading dot-segment traversal. Keeps manifest-controlled asset paths
|
|
33
|
+
* confined to the asset directory (no path-traversal via join()). */
|
|
34
|
+
function isBasename(p: string): boolean {
|
|
35
|
+
if (!p || p.length === 0 || p.includes("/") || p.includes("\\")) return false;
|
|
36
|
+
if (p === "." || p === "..") return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Digest the on-disk bytes of one asset file; "" on unreadable (truncated). */
|
|
41
|
+
function digestFile(path: string): string | null {
|
|
42
|
+
try {
|
|
43
|
+
const buf = readFileSync(path);
|
|
44
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
45
|
+
} catch {
|
|
46
|
+
// Truncated / unreadable during the digest read -> ENC_ASSET_UNREADABLE.
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect the current platform (MODEL_ASSET supported matrix). Unrecognized
|
|
53
|
+
* hosts return null so verification demotes to mode B (unsupported platform).
|
|
54
|
+
*/
|
|
55
|
+
export function detectPlatform(host = process.platform, arch = process.arch): EncoderPlatform | null {
|
|
56
|
+
if (!host || !arch) return null;
|
|
57
|
+
// Normalize "win32"/"win32"/"linux"/"darwin" + "x64"/"arm64".
|
|
58
|
+
const plat = host === "win32" ? "win32" : host === "darwin" ? "darwin" : host === "linux" ? "linux" : "";
|
|
59
|
+
const a = arch === "x64" ? "x64" : arch === "arm64" ? "arm64" : "";
|
|
60
|
+
if (!plat || !a) return null;
|
|
61
|
+
const candidate = `${plat}-${a}` as EncoderPlatform;
|
|
62
|
+
return (ENCODER_SUPPORTED_PLATFORMS as readonly string[]).includes(candidate)
|
|
63
|
+
? candidate
|
|
64
|
+
: null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isManifest(m: unknown): m is ModelManifestV1 {
|
|
68
|
+
const o = m as ModelManifestV1;
|
|
69
|
+
return (
|
|
70
|
+
!!o &&
|
|
71
|
+
typeof o === "object" &&
|
|
72
|
+
o.schema === "model-manifest-v1" &&
|
|
73
|
+
typeof o.opset === "number" &&
|
|
74
|
+
typeof o.batch === "number" &&
|
|
75
|
+
typeof o.maxTokens === "number" &&
|
|
76
|
+
typeof o.platform === "string" &&
|
|
77
|
+
!!o.onnx &&
|
|
78
|
+
!!o.tokenizer &&
|
|
79
|
+
typeof o.onnx.path === "string" &&
|
|
80
|
+
typeof o.onnx.sha256 === "string" &&
|
|
81
|
+
typeof o.tokenizer.path === "string" &&
|
|
82
|
+
typeof o.tokenizer.sha256 === "string"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Verify the asset manifest + digest + constraints BEFORE allocation.
|
|
88
|
+
*
|
|
89
|
+
* - manifest parse/shape failure -> ENC_MANIFEST_INVALID -> mode B
|
|
90
|
+
* - unsupported platform -> ENC_PLATFORM_UNSUPPORTED -> mode B
|
|
91
|
+
* - opset != 17 -> ENC_OPSET_INVALID -> mode B
|
|
92
|
+
* - batch != 1 -> ENC_BATCH_INVALID -> mode B
|
|
93
|
+
* - maxTokens > 512 -> ENC_TOKENS_EXCEEDED -> mode B
|
|
94
|
+
* - on-disk digest != manifest digest -> ENC_DIGEST_MISMATCH (one-byte mutation)
|
|
95
|
+
* - unreadable/truncated file -> ENC_ASSET_UNREADABLE
|
|
96
|
+
*
|
|
97
|
+
* Returns ok only when EVERY constraint passes and both files hash to the
|
|
98
|
+
* declared digests (the "only batch1/max512 verified assets reach inference"
|
|
99
|
+
* invariant). The ok result surfaces the verified manifest's `maxTokens`
|
|
100
|
+
* (<= 512) so the runtime can enforce the per-manifest token capacity at
|
|
101
|
+
* inference (Q03), not just the global 512 ceiling.
|
|
102
|
+
*/
|
|
103
|
+
export function verifyEncoderAsset(
|
|
104
|
+
assetDir: string,
|
|
105
|
+
manifest: unknown,
|
|
106
|
+
platform: EncoderPlatform | null = detectPlatform(),
|
|
107
|
+
): AssetVerifyResult {
|
|
108
|
+
if (typeof manifest !== "object" || manifest === null || !isManifest(manifest)) {
|
|
109
|
+
return { ok: false, code: ENC_FAIL.MANIFEST_INVALID };
|
|
110
|
+
}
|
|
111
|
+
if (!platform) return { ok: false, code: ENC_FAIL.PLATFORM_UNSUPPORTED };
|
|
112
|
+
// The manifest's declared platform must match the runtime host (per-platform
|
|
113
|
+
// asset pinning): a bundle cross-shipped to the wrong arch is not qualified.
|
|
114
|
+
if (manifest.platform !== platform) return { ok: false, code: ENC_FAIL.PLATFORM_UNSUPPORTED };
|
|
115
|
+
if (manifest.opset !== ENCODER_OPSET) return { ok: false, code: ENC_FAIL.OPSET_INVALID };
|
|
116
|
+
if (manifest.batch !== ENCODER_BATCH) return { ok: false, code: ENC_FAIL.BATCH_INVALID };
|
|
117
|
+
if (manifest.maxTokens > ENCODER_MAX_TOKENS) return { ok: false, code: ENC_FAIL.TOKENS_EXCEEDED };
|
|
118
|
+
|
|
119
|
+
// Constrain asset paths to basenames (no separators / no '..') so a forged
|
|
120
|
+
// manifest cannot read digests from arbitrary paths off the asset dir.
|
|
121
|
+
if (!isBasename(manifest.onnx.path) || !isBasename(manifest.tokenizer.path)) {
|
|
122
|
+
return { ok: false, code: ENC_FAIL.MANIFEST_INVALID };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const onnxPath = join(assetDir, manifest.onnx.path);
|
|
126
|
+
const onnxDigest = digestFile(onnxPath);
|
|
127
|
+
if (onnxDigest === null) return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
128
|
+
if (onnxDigest !== manifest.onnx.sha256) return { ok: false, code: ENC_FAIL.DIGEST_MISMATCH };
|
|
129
|
+
|
|
130
|
+
const tokPath = join(assetDir, manifest.tokenizer.path);
|
|
131
|
+
const tokDigest = digestFile(tokPath);
|
|
132
|
+
if (tokDigest === null) return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
133
|
+
if (tokDigest !== manifest.tokenizer.sha256) return { ok: false, code: ENC_FAIL.DIGEST_MISMATCH };
|
|
134
|
+
|
|
135
|
+
let embeddedBytes = 0;
|
|
136
|
+
try {
|
|
137
|
+
embeddedBytes = statSync(onnxPath).size + statSync(tokPath).size;
|
|
138
|
+
} catch {
|
|
139
|
+
return { ok: false, code: ENC_FAIL.ASSET_UNREADABLE };
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, embeddedBytes, maxTokens: manifest.maxTokens, onnxDigest, tokenizerDigest: tokDigest };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Read + shape-check a committed ModelManifestV1 from an asset directory.
|
|
146
|
+
* Returns the parsed manifest or null when the file is absent/malformed.
|
|
147
|
+
*/
|
|
148
|
+
export function readEncoderManifest(assetDir: string): ModelManifestV1 | null {
|
|
149
|
+
try {
|
|
150
|
+
const raw = JSON.parse(readFileSync(join(assetDir, "manifest.json"), "utf8")) as unknown;
|
|
151
|
+
return isManifest(raw) ? raw : null;
|
|
152
|
+
} catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/encoder/emit.ts — VC2A observability seam.
|
|
3
|
+
*
|
|
4
|
+
* Emits the two VC2A structured events, gated on `MEGACOMPACT_VC2A` (mode C
|
|
5
|
+
* parity: flag OFF => zero emissions). Every event is a JSON line with `ts` +
|
|
6
|
+
* `event` (ENGINEERING_PRACTICES §8); the emitters are never fatal on consumer
|
|
7
|
+
* failure (non-fatal observability, never breaks the agent loop).
|
|
8
|
+
*
|
|
9
|
+
* vector_cortex_encoder_asset_verified — a qualified manifest+digest load
|
|
10
|
+
* vector_cortex_encoder_runtime_demoted — a demotion to mode B or C
|
|
11
|
+
*
|
|
12
|
+
* No network, no side effects beyond the supplied emit callback
|
|
13
|
+
* (PREVENT-PI-004 / PREVENT-011).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { VC2A_ENABLED } from "../../config/vector-cortex.js";
|
|
17
|
+
|
|
18
|
+
export type EncoderEmit = (event: string, fields: Record<string, unknown>) => void;
|
|
19
|
+
|
|
20
|
+
/** The two-event reporter surface consumed by the runtime seams. */
|
|
21
|
+
export interface EncoderReporter {
|
|
22
|
+
readonly assetVerified: (fields: Record<string, unknown>) => void;
|
|
23
|
+
readonly runtimeDemoted: (fields: Record<string, unknown>) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A flag-gated no-op reporter (zero emissions, default when none injected). */
|
|
27
|
+
export const NOOP_ENCODER_REPORTER: EncoderReporter = {
|
|
28
|
+
assetVerified: () => {},
|
|
29
|
+
runtimeDemoted: () => {},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Flag-gated emit: no-op when VC2A is off or no emitter is supplied. The
|
|
34
|
+
* returned reporter is itself flag-gated (`VC2A_ENABLED`), so wiring it into a
|
|
35
|
+
* runtime seam yields zero emissions when `MEGACOMPACT_VC2A=0` (byte-identical
|
|
36
|
+
* to the predecessor).
|
|
37
|
+
*/
|
|
38
|
+
export function createEncoderReporter(emit?: EncoderEmit): EncoderReporter {
|
|
39
|
+
const fire = (event: string, fields: Record<string, unknown>): void => {
|
|
40
|
+
if (!VC2A_ENABLED()) return;
|
|
41
|
+
try {
|
|
42
|
+
emit?.(event, { ...fields, ts: new Date().toISOString() });
|
|
43
|
+
} catch {
|
|
44
|
+
/* non-fatal observability */
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
assetVerified: (fields) => fire("vector_cortex_encoder_asset_verified", fields),
|
|
49
|
+
runtimeDemoted: (fields) => fire("vector_cortex_encoder_runtime_demoted", fields),
|
|
50
|
+
};
|
|
51
|
+
}
|