pi-mega-compact 0.19.2 → 0.20.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 +24 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-cache.test/_helpers.js +2 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +2 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-helpers.js +100 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-query.js +46 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-shards.js +48 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex.js +2 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +41 -16
- package/dist/extensions/dashboard-server/tailscale.js +32 -0
- package/dist/src/config/vector-cortex.js +24 -0
- package/dist/src/config.js +1 -1
- package/dist/src/tieredRouter.js +8 -0
- package/dist/src/vector-cortex/migrations/router-generation-v2.js +252 -0
- package/dist/src/vector-cortex/shards/exact.js +134 -0
- package/dist/src/vector-cortex/shards/manifest.js +237 -0
- package/dist/src/vector-cortex/shards/semantic.js +137 -0
- package/dist/src/vector-cortex/shards/types.js +51 -0
- package/dist/src/vector-cortex/topology/query.js +342 -0
- package/dist/vector-cortex/migrations/router-generation-v2.js +252 -0
- package/dist/vector-cortex/shards/exact.js +134 -0
- package/dist/vector-cortex/shards/manifest.js +237 -0
- package/dist/vector-cortex/shards/semantic.js +137 -0
- package/dist/vector-cortex/shards/types.js +51 -0
- package/dist/vector-cortex/topology/query.js +342 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-CSaJZXru.js → AreaChart-n4z5w9Ox.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CSaJZXru.js.map → AreaChart-n4z5w9Ox.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-CQouNj1g.js → BarChart-BLGjWWBr.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-CQouNj1g.js.map → BarChart-BLGjWWBr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-m_eiCYoL.js → CacheTab-CAnrKKVK.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-m_eiCYoL.js.map → CacheTab-CAnrKKVK.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DRyOwjYN.js → EventsTab-D-GL7Ngq.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DRyOwjYN.js.map → EventsTab-D-GL7Ngq.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-Dj9k40TJ.js → HealthTab-C4x5qQ8s.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-Dj9k40TJ.js.map → HealthTab-C4x5qQ8s.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Bk9mhYfD.js → MaintenanceTab-BSPs0eq5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Bk9mhYfD.js.map → MaintenanceTab-BSPs0eq5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C7ve49VG.js → MemoryMapTab-BWEUtTSu.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C7ve49VG.js.map → MemoryMapTab-BWEUtTSu.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DzjhqZA9.js → MetricsTab-Dhm1c3Cx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DzjhqZA9.js.map → MetricsTab-Dhm1c3Cx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-CFQdfIm_.js → OverviewTab-Cm6cySiQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-CFQdfIm_.js.map → OverviewTab-Cm6cySiQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DyjwEFUr.js → ReposTab-DxLN1N4K.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DyjwEFUr.js.map → ReposTab-DxLN1N4K.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-njLIy7ab.js → SessionsTab-CZuI4ISW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-njLIy7ab.js.map → SessionsTab-CZuI4ISW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-UHIs8-Lh.js → SetupTab-C_0VOomm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-UHIs8-Lh.js.map → SetupTab-C_0VOomm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-inWHjCxg.js → TimeSavedCard-BhbaRLmi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-inWHjCxg.js.map → TimeSavedCard-BhbaRLmi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CXJYugBu.js → TurnsTab-CXJmbjje.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CXJYugBu.js.map → TurnsTab-CXJmbjje.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BGe9hu3r.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BGe9hu3r.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-BJ4A2oi9.js → WikiTab-C5uvMCzp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-BJ4A2oi9.js.map → WikiTab-C5uvMCzp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-BWl-moyn.js → button-Cl_fxJcg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-BWl-moyn.js.map → button-Cl_fxJcg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-C58RVPOk.js → card-BH8Le5YM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-C58RVPOk.js.map → card-BH8Le5YM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-vgeOQU3L.js → generateCategoricalChart-sZ6ZNtBi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-vgeOQU3L.js.map → generateCategoricalChart-sZ6ZNtBi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-lVrWQjpu.js → index-CX9zv8cJ.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-lVrWQjpu.js.map → index-CX9zv8cJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-CSeplFQT.js → switch-CXJhcjhM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-CSeplFQT.js.map → switch-CXJhcjhM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CQRxgVRi.js → toggle-CoLtIc6y.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CQRxgVRi.js.map → toggle-CoLtIc6y.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BUz6IOI-.js → useSSE-fSXho606.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BUz6IOI-.js.map → useSSE-fSXho606.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/vector-cortex.ts +18 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +62 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +25 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex.ts +41 -0
- package/extensions/dashboard-server/routes-cache.test/_helpers.ts +2 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +12 -0
- package/extensions/dashboard-server/routes-vector-cortex-helpers.ts +110 -0
- package/extensions/dashboard-server/routes-vector-cortex-query.ts +54 -0
- package/extensions/dashboard-server/routes-vector-cortex-shards.ts +57 -0
- package/extensions/dashboard-server/routes-vector-cortex.ts +2 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +46 -17
- package/extensions/dashboard-server/tailscale.ts +32 -0
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +26 -0
- package/src/config.ts +2 -0
- package/src/tieredRouter.ts +9 -0
- package/src/vector-cortex/migrations/router-generation-v2.ts +317 -0
- package/src/vector-cortex/shards/exact.ts +141 -0
- package/src/vector-cortex/shards/manifest.ts +276 -0
- package/src/vector-cortex/shards/semantic.ts +156 -0
- package/src/vector-cortex/shards/types.ts +212 -0
- package/src/vector-cortex/topology/query.ts +485 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DkS0Azx9.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DkS0Azx9.js.map +0 -1
|
@@ -110,6 +110,30 @@ export const VC3A_ENABLED = () => sprintFlag("MEGACOMPACT_VC3A");
|
|
|
110
110
|
* the topology build seam (build.ts) and the dashboard topology node/edge view.
|
|
111
111
|
*/
|
|
112
112
|
export const VC3B_ENABLED = () => sprintFlag("MEGACOMPACT_VC3B");
|
|
113
|
+
/**
|
|
114
|
+
* VC3C — topology query + router invalidation (TopologyQueryV1 / RouterKeyV2,
|
|
115
|
+
* M6 router-generation-v2 migration).
|
|
116
|
+
* Default ON. `MEGACOMPACT_VC3C=0` disables and is byte-identical to the
|
|
117
|
+
* predecessor (mode C: no length-delimited router key, no generation
|
|
118
|
+
* invalidation, zero `vector_cortex_router_generation_invalidated` /
|
|
119
|
+
* `vector_cortex_topology_query_demoted` emissions; the predecessor tiered
|
|
120
|
+
* router keying is unchanged). The real consumers are the topology query seam
|
|
121
|
+
* (query.ts), the M6 router-generation-v2 migration seam, and the reader-only
|
|
122
|
+
* dashboard topology query diagnostic view.
|
|
123
|
+
*/
|
|
124
|
+
export const VC3C_ENABLED = () => sprintFlag("MEGACOMPACT_VC3C");
|
|
125
|
+
/**
|
|
126
|
+
* VC4A — dual-tier shard contract (SemanticShardV1 / ExactShardV1 /
|
|
127
|
+
* ShardManifestV1).
|
|
128
|
+
* Default ON. `MEGACOMPACT_VC4A=0` disables and is byte-identical to the
|
|
129
|
+
* predecessor (mode C: exact anchors/current transcript only, no semantic or
|
|
130
|
+
* exact shard manifest is assembled or emitted, zero
|
|
131
|
+
* `vector_cortex_shard_manifest_built` / `vector_cortex_protected_span_rejected`
|
|
132
|
+
* emissions; the predecessor derived pointer and goldens are unchanged). The
|
|
133
|
+
* real consumers are the shard assembly seam (manifest.ts) and the reader-only
|
|
134
|
+
* dashboard shards aggregate view.
|
|
135
|
+
*/
|
|
136
|
+
export const VC4A_ENABLED = () => sprintFlag("MEGACOMPACT_VC4A");
|
|
113
137
|
// ---------------------------------------------------------------------------
|
|
114
138
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
|
|
115
139
|
// Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
|
package/dist/config.js
CHANGED
|
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
|
|
114
114
|
// default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
|
|
115
115
|
// so root consumers share one source of truth.
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
117
|
-
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
117
|
+
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
@@ -25,6 +25,7 @@ export function freshDir(prefix) {
|
|
|
25
25
|
}
|
|
26
26
|
export async function withServer(port, dir, fn) {
|
|
27
27
|
process.env.MEGACOMPACT_DASHBOARD_PORT = port;
|
|
28
|
+
process.env.MEGACOMPACT_DASHBOARD_HOST = "127.0.0.1";
|
|
28
29
|
const child = spawn(process.execPath, [SERVER_ENTRY, dir], { stdio: "ignore" });
|
|
29
30
|
try {
|
|
30
31
|
await waitFor(async () => {
|
|
@@ -43,6 +44,7 @@ export async function withServer(port, dir, fn) {
|
|
|
43
44
|
finally {
|
|
44
45
|
child.kill("SIGTERM");
|
|
45
46
|
delete process.env.MEGACOMPACT_DASHBOARD_PORT;
|
|
47
|
+
delete process.env.MEGACOMPACT_DASHBOARD_HOST;
|
|
46
48
|
rmSync(dir, { recursive: true, force: true });
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -145,6 +145,8 @@ export const SETTINGS = [
|
|
|
145
145
|
boolDirect("MEGACOMPACT_VC2C", "VC2C Encoder Qualification + Calibration", "QualifiedEncoderV1/CalibrationV1: calibration fit on the calibration split only (held-out labels prohibited) + atomic selection across MODEL_ASSET and per-head EVALUATION thresholds (any field failure demotes all of A). OFF = mode C, no qualification/calibration selection, byte-identical predecessor.", true),
|
|
146
146
|
boolDirect("MEGACOMPACT_VC3A", "VC3A Cortex Store", "Capability-gated derived cortex store (CortexReader/Writer/Admin + CortexRecordV1): additive, keyed (sourceHighWater, algorithmVersion, id), immutable records, deterministic generation rebuild + one root digest. OFF = mode C, no cortex records written, byte-identical predecessor.", true),
|
|
147
147
|
boolDirect("MEGACOMPACT_VC3B", "VC3B Deterministic Topology", "Deterministic cortical topology (TopologyV1/EdgeV1): per-(source,head) top-k=16/head calibrated-threshold edges, stable score-desc/then-target-ID sort, dependency directed + contradiction symmetric paired records, one stable generation digest. OFF = mode C, no topology graph built/emitted, predecessor-precise topology view, byte-identical predecessor.", true),
|
|
148
|
+
boolDirect("MEGACOMPACT_VC3C", "VC3C Topology Query + Router Invalidation", "TopologyQueryV1/RouterKeyV2 structured keys (length-delimited, unsigned-byte order, no prefix ambiguity), exact (session,generation) invalidation, stale-generation rejection (TOP_GENERATION_STALE), and the M6 router-generation-v2 copy/validate/switch migration. OFF = mode C, no structured router key / generation invalidation, byte-identical predecessor.", true),
|
|
149
|
+
boolDirect("MEGACOMPACT_VC4A", "VC4A Dual-Tier Shards", "SemanticShardV1/ExactShardV1/ShardManifestV1: partition a session ONLY at complete EventV2 boundaries; exact shards preserve every tool call/result pair, anchor and invalid UTF-8 event as original bytes (pairs never split across exact shards); manifest enforces disjoint sorted ranges + complete protected-span coverage. OFF = mode C, exact anchors/current transcript only, byte-identical predecessor.", true),
|
|
148
150
|
],
|
|
149
151
|
},
|
|
150
152
|
{
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes-vector-cortex-helpers.ts — shared spawn-and-fetch harness for the
|
|
3
|
+
* /api/vector-cortex/* route test files.
|
|
4
|
+
*
|
|
5
|
+
* The dashboard-server.js main block fires when the entry argv includes
|
|
6
|
+
* "dashboard-server", so tests under dist/extensions/dashboard-server/ MUST
|
|
7
|
+
* spawn the server as a child process rather than import launchDashboardServer.
|
|
8
|
+
* Extracted from routes-vector-cortex.test.ts so per-sprint route test files
|
|
9
|
+
* can share the harness without each re-implementing it.
|
|
10
|
+
*
|
|
11
|
+
* Reader-only: no payload bytes are ever returned by the routes under test.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { rmSync, readFileSync, existsSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { spawn } from "node:child_process";
|
|
18
|
+
/** Locate the repo root (the directory holding `conformance/vector-cortex`). */
|
|
19
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
export function repoRoot(from = HERE) {
|
|
21
|
+
let dir = from;
|
|
22
|
+
for (let i = 0; i < 8; i++) {
|
|
23
|
+
if (existsSync(join(dir, "conformance", "vector-cortex")))
|
|
24
|
+
return dir;
|
|
25
|
+
const next = dirname(dir);
|
|
26
|
+
if (next === dir)
|
|
27
|
+
break;
|
|
28
|
+
dir = next;
|
|
29
|
+
}
|
|
30
|
+
throw new Error("conformance corpus not found above " + from);
|
|
31
|
+
}
|
|
32
|
+
const REPO_ROOT = repoRoot();
|
|
33
|
+
/** SHA-256 of the REAL committed ModelManifestV1 — the health card's digest. */
|
|
34
|
+
export function realManifestDigest() {
|
|
35
|
+
// guardrails-allow PREVENT-PI-004: local committed asset filesystem read (loopback)
|
|
36
|
+
return createHash("sha256")
|
|
37
|
+
.update(readFileSync(join(REPO_ROOT, "assets", "vector-cortex", "encoder-v1", "manifest.json")))
|
|
38
|
+
.digest("hex");
|
|
39
|
+
}
|
|
40
|
+
export const SERVER_ENTRY = new URL("./server.js", import.meta.url).pathname;
|
|
41
|
+
export function waitFor(cond, timeoutMs = 6000) {
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const tick = async () => {
|
|
45
|
+
if (await cond())
|
|
46
|
+
return resolve();
|
|
47
|
+
if (Date.now() - start > timeoutMs)
|
|
48
|
+
return reject(new Error("timeout"));
|
|
49
|
+
setTimeout(tick, 50);
|
|
50
|
+
};
|
|
51
|
+
tick();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export async function withServer(port, dir, fn) {
|
|
55
|
+
process.env.MEGACOMPACT_DASHBOARD_PORT = port;
|
|
56
|
+
// Pin loopback so the spawn-and-fetch harness stays hermetic even on a
|
|
57
|
+
// machine with tailscale0 (the production auto-bind would otherwise grab the
|
|
58
|
+
// tailnet IP and the localhost probe would connection-refuse). Tests that
|
|
59
|
+
// need a different host can set MEGACOMPACT_DASHBOARD_HOST before calling.
|
|
60
|
+
process.env.MEGACOMPACT_DASHBOARD_HOST = "127.0.0.1";
|
|
61
|
+
const child = spawn(process.execPath, [SERVER_ENTRY, dir], {
|
|
62
|
+
stdio: "ignore",
|
|
63
|
+
env: {
|
|
64
|
+
...process.env,
|
|
65
|
+
MEGACOMPACT_INDEX_DIR: dir,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
await waitFor(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
|
|
72
|
+
const res = await fetch(`http://localhost:${raw.port}/api/version`);
|
|
73
|
+
return res.ok;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const raw = JSON.parse(readFileSync(join(dir, "port.pid"), "utf-8"));
|
|
80
|
+
return await fn(raw.port);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
child.kill("SIGTERM");
|
|
84
|
+
delete process.env.MEGACOMPACT_DASHBOARD_PORT;
|
|
85
|
+
delete process.env.MEGACOMPACT_DASHBOARD_HOST;
|
|
86
|
+
rmSync(dir, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Seed redacted metric rows directly on disk so the server's reader sees them. */
|
|
90
|
+
export async function seedEval(dir) {
|
|
91
|
+
const { appendEvalRow } = await import("../../src/vector-cortex/eval/persist.js");
|
|
92
|
+
// Buckets (inclusive): 1,5,10,25,50,100,250. 1ms→cell0, 12ms→cell3
|
|
93
|
+
// (<=25), 250ms→cell6, 300ms→overflow.
|
|
94
|
+
appendEvalRow(dir, [
|
|
95
|
+
{ session: "s1", seq: 1, event: "encode", value: 1, unit: "ms", mode: "A" },
|
|
96
|
+
{ session: "s1", seq: 2, event: "encode", value: 250, unit: "ms", mode: "A" },
|
|
97
|
+
{ session: "s1", seq: 3, event: "encode", value: 300, unit: "ms", mode: "A" },
|
|
98
|
+
{ session: "s1", seq: 4, event: "encode", value: 12, unit: "ms", mode: "A" },
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-vector-cortex-query.ts — VC3C vector-cortex query-layer
|
|
3
|
+
* diagnostics dashboard route.
|
|
4
|
+
*
|
|
5
|
+
* Reader-only GET /api/vector-cortex/query. Purely a flag-status + structural
|
|
6
|
+
* diagnostic: the VC3C query index is in-memory (not durable), so this route
|
|
7
|
+
* reports whether the VC3C flag is enabled, the router-generation v2 version
|
|
8
|
+
* constant, and a snapshot timestamp. It NEVER exposes payloads or prompts.
|
|
9
|
+
* Non-fatal: a missing state dir (or any internal error) degrades to
|
|
10
|
+
* `enabled:false`.
|
|
11
|
+
*
|
|
12
|
+
* Guardrails: PREVENT-PI-004 (local flag/config read only, no network),
|
|
13
|
+
* PREVENT-011 (no `any`), reader-only aggregate (never payloads/prompts).
|
|
14
|
+
*/
|
|
15
|
+
import { VC3C_ENABLED } from "../../src/config.js";
|
|
16
|
+
import { ROUTER_KEY_VERSION } from "../../src/vector-cortex/topology/query.js";
|
|
17
|
+
import { sendJson } from "./routes-vector-cortex-shared.js";
|
|
18
|
+
/**
|
|
19
|
+
* Reader-only GET /api/vector-cortex/query (VC3C query-layer diagnostics).
|
|
20
|
+
*/
|
|
21
|
+
export function handleVectorCortexQuery(req, res, _ctx) {
|
|
22
|
+
const url = req.url ?? "";
|
|
23
|
+
const path = url.split("?")[0] ?? url;
|
|
24
|
+
if (path !== "/api/vector-cortex/query")
|
|
25
|
+
return false;
|
|
26
|
+
if (req.method !== "GET") {
|
|
27
|
+
// Reader-only path: no mutation endpoint lives at /query.
|
|
28
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
let enabled = false;
|
|
32
|
+
try {
|
|
33
|
+
enabled = VC3C_ENABLED();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Non-fatal: a missing/unavailable flag degrades to `enabled:false`.
|
|
37
|
+
enabled = false;
|
|
38
|
+
}
|
|
39
|
+
const body = {
|
|
40
|
+
enabled,
|
|
41
|
+
routerVersion: ROUTER_KEY_VERSION,
|
|
42
|
+
updatedAt: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
sendJson(res, 200, body);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-vector-cortex-shards.ts — VC4A dual-tier shard
|
|
3
|
+
* dashboard route.
|
|
4
|
+
*
|
|
5
|
+
* Reader-only GET /api/vector-cortex/shards returning a COUNT/BYTE aggregate
|
|
6
|
+
* (semantic shards, exact shards, protected byte total) — never shard payloads,
|
|
7
|
+
* verbatim exact bytes, or prompt text. Flag-gated on MEGACOMPACT_VC4A:
|
|
8
|
+
* `enabled:false` when off (byte-identical to the pre-VC4A predecessor).
|
|
9
|
+
*
|
|
10
|
+
* The VC4A shard partition is PURE IN-MEMORY logic in this sprint (it has no
|
|
11
|
+
* durable manifest store yet), so the aggregate reports the current status
|
|
12
|
+
* truthfully: `enabled` reflects the flag, and the count/byte fields are zero
|
|
13
|
+
* until a future sprint persists a staged manifest. This route is the seam that
|
|
14
|
+
* sprint will populate. Non-fatal: a missing state dir degrades to
|
|
15
|
+
* `enabled:false`.
|
|
16
|
+
*
|
|
17
|
+
* Guardrails: PREVENT-PI-004 (local filesystem read only / in-process state),
|
|
18
|
+
* PREVENT-011 (no `any`), reader-only aggregate (counts/bytes only).
|
|
19
|
+
*/
|
|
20
|
+
import { VC4A_ENABLED } from "../../src/config.js";
|
|
21
|
+
import { sendJson } from "./routes-vector-cortex-shared.js";
|
|
22
|
+
/**
|
|
23
|
+
* Reader-only GET /api/vector-cortex/shards (VC4A).
|
|
24
|
+
*/
|
|
25
|
+
export function handleVectorCortexShards(req, res, _ctx) {
|
|
26
|
+
const url = req.url ?? "";
|
|
27
|
+
const path = url.split("?")[0] ?? url;
|
|
28
|
+
if (path !== "/api/vector-cortex/shards")
|
|
29
|
+
return false;
|
|
30
|
+
if (req.method !== "GET") {
|
|
31
|
+
// Reader-only path: cannot be "off" without a GET; it is genuinely read-only.
|
|
32
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const enabled = VC4A_ENABLED();
|
|
36
|
+
const body = {
|
|
37
|
+
enabled,
|
|
38
|
+
// No durable shard manifest is staged yet in this sprint (pure in-memory
|
|
39
|
+
// partition), so the aggregates are truthfully zero.
|
|
40
|
+
semanticCount: 0,
|
|
41
|
+
exactCount: 0,
|
|
42
|
+
byteTotal: 0,
|
|
43
|
+
protectedByteTotal: 0,
|
|
44
|
+
updatedAt: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
sendJson(res, 200, body);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
@@ -17,3 +17,5 @@ export { handleVectorCortexEvaluation } from "./routes-vector-cortex-eval.js";
|
|
|
17
17
|
export { handleVectorCortexHealth, handleVectorCortexBreakersReset } from "./routes-vector-cortex-health.js";
|
|
18
18
|
export { handleVectorCortexLedger } from "./routes-vector-cortex-ledger.js";
|
|
19
19
|
export { handleVectorCortexTopology } from "./routes-vector-cortex-topology.js";
|
|
20
|
+
export { handleVectorCortexQuery } from "./routes-vector-cortex-query.js";
|
|
21
|
+
export { handleVectorCortexShards } from "./routes-vector-cortex-shards.js";
|
|
@@ -23,4 +23,4 @@ export { handleRagSettings } from "./routes-rag-settings.js";
|
|
|
23
23
|
export { handleRagMetrics } from "./routes-rag-metrics.js";
|
|
24
24
|
export { handleModelThresholds } from "./routes-model-thresholds.js";
|
|
25
25
|
export { handleWiki } from "./routes-wiki.js";
|
|
26
|
-
export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, } from "./routes-vector-cortex.js";
|
|
26
|
+
export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, } from "./routes-vector-cortex.js";
|
|
@@ -13,7 +13,8 @@ import { fileURLToPath } from "node:url";
|
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
14
|
import { NEW_UI } from "../../src/config.js";
|
|
15
15
|
import { log, setLogPath, setDashboardServerVersion } from "./state.js";
|
|
16
|
-
import {
|
|
16
|
+
import { detectTailscaleIP } from "./tailscale.js";
|
|
17
|
+
import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handlePerfSamples, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleStatic, } from "./routes.js";
|
|
17
18
|
export async function launchDashboardServer(stateDir) {
|
|
18
19
|
// Our own package version — exposed at /api/version so the launcher can
|
|
19
20
|
// detect a stale server (started by an older build) and replace it on
|
|
@@ -166,13 +167,26 @@ export async function launchDashboardServer(stateDir) {
|
|
|
166
167
|
serveClientAsset,
|
|
167
168
|
detectCrossRepoDrift,
|
|
168
169
|
});
|
|
170
|
+
// Bind host resolution: explicit MEGACOMPACT_DASHBOARD_HOST override wins;
|
|
171
|
+
// otherwise auto-bind to the Tailscale interface (tailnet access); fall back
|
|
172
|
+
// to loopback when Tailscale is absent. 0.0.0.0 means "all interfaces" and
|
|
173
|
+
// opts into permissive CORS below.
|
|
174
|
+
const host = process.env.MEGACOMPACT_DASHBOARD_HOST ??
|
|
175
|
+
detectTailscaleIP() ??
|
|
176
|
+
"127.0.0.1";
|
|
177
|
+
const allInterfaces = host === "0.0.0.0";
|
|
178
|
+
// guardrails-allow PREVENT-PI-004: optional, user-triggered /dashboard server (tailnet-local via Tailscale mesh or loopback); no remote outbound calls.
|
|
169
179
|
const server = createServer((req, res) => {
|
|
170
|
-
//
|
|
171
|
-
//
|
|
180
|
+
// CORS for local access — restricted to loopback/same-origin tailnet
|
|
181
|
+
// origins (the dashboard server binds to localhost or the tailscale IP).
|
|
182
|
+
// With MEGACOMPACT_DASHBOARD_HOST=0.0.0.0 the user opted into
|
|
183
|
+
// all-interfaces binding, so we allow any origin.
|
|
172
184
|
const origin = req.headers.origin;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
const originAllowed = allInterfaces ||
|
|
186
|
+
(typeof origin === "string" &&
|
|
187
|
+
new RegExp(`^http://(localhost|127\\.0\\.0\\.1|::1|${host.replace(/\./g, "\\.")})(:\\d+)?$`).test(origin));
|
|
188
|
+
if (originAllowed) {
|
|
189
|
+
res.setHeader("Access-Control-Allow-Origin", origin ?? "*");
|
|
176
190
|
res.setHeader("Vary", "Origin");
|
|
177
191
|
}
|
|
178
192
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
|
|
@@ -251,6 +265,10 @@ export async function launchDashboardServer(stateDir) {
|
|
|
251
265
|
return;
|
|
252
266
|
if (handleVectorCortexTopology(req, res, ctx))
|
|
253
267
|
return;
|
|
268
|
+
if (handleVectorCortexQuery(req, res, ctx))
|
|
269
|
+
return;
|
|
270
|
+
if (handleVectorCortexShards(req, res, ctx))
|
|
271
|
+
return;
|
|
254
272
|
handleStatic(req, res, ctx);
|
|
255
273
|
});
|
|
256
274
|
// Bind base + range are env-configurable so tests can use a private,
|
|
@@ -272,26 +290,33 @@ export async function launchDashboardServer(stateDir) {
|
|
|
272
290
|
reject(err);
|
|
273
291
|
}
|
|
274
292
|
});
|
|
275
|
-
server.listen(port,
|
|
276
|
-
|
|
277
|
-
|
|
293
|
+
server.listen(port, host, () => {
|
|
294
|
+
// When bound to the loopback, keep the legacy localhost URL so the
|
|
295
|
+
// user's browser (which resolves localhost → ::1 or 127.0.0.1) works.
|
|
296
|
+
// When bound to a tailnet/tailscale IP, surface that address instead.
|
|
297
|
+
const url = host === "127.0.0.1"
|
|
298
|
+
? `http://localhost:${port}`
|
|
299
|
+
: `http://${host}:${port}`; // guardrails-allow PREVENT-PI-004: dashboard URL (tailnet-local via Tailscale mesh or loopback)
|
|
300
|
+
log("server running", { url, host });
|
|
278
301
|
// eslint-disable-next-line no-console
|
|
279
302
|
console.log(`[mega-compact] dashboard server running: ${url}`);
|
|
280
|
-
// v0.8.2: also bind the IPv6 loopback (::1)
|
|
281
|
-
// resolves to ::1 first (see
|
|
282
|
-
// browser hit ::1:port and
|
|
283
|
-
// (loopback-only) means BOTH
|
|
284
|
-
//
|
|
303
|
+
// v0.8.2: also bind the IPv6 loopback (::1) when bound to the IPv4
|
|
304
|
+
// loopback. On many systems `localhost` resolves to ::1 first (see
|
|
305
|
+
// /etc/hosts), so an IPv4-only bind makes the browser hit ::1:port and
|
|
306
|
+
// get connection refused. PREVENT-PI-004 (loopback-only) means BOTH
|
|
307
|
+
// 127.0.0.1 and ::1. When bound to a tailscale/0.0.0.0 host, we do NOT
|
|
308
|
+
// bind ::1 — the tailnet address is what the user reaches. Non-fatal:
|
|
309
|
+
// IPv4-only hosts or a ::1 already in use just skip the mirror.
|
|
285
310
|
let v6;
|
|
286
311
|
const v4Handler = server.listeners("request")[0];
|
|
287
|
-
if (v4Handler) {
|
|
312
|
+
if (host === "127.0.0.1" && v4Handler) {
|
|
288
313
|
v6 = createServer((r, s) => v4Handler.call(server, r, s));
|
|
289
314
|
v6.on("error", (e) => log("ipv6 loopback bind skipped", {
|
|
290
315
|
port,
|
|
291
316
|
code: e.code,
|
|
292
317
|
message: e.message,
|
|
293
318
|
}));
|
|
294
|
-
v6.listen(port, "::1", () => log("ipv6 loopback bound", { port })); // guardrails-allow PREVENT-PI-004: IPv6 loopback (::1) mirror of the
|
|
319
|
+
v6.listen(port, "::1", () => log("ipv6 loopback bound", { port })); // guardrails-allow PREVENT-PI-004: IPv6 loopback (::1) mirror of the dashboard server
|
|
295
320
|
}
|
|
296
321
|
// Write port.pid
|
|
297
322
|
try {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { networkInterfaces } from "node:os";
|
|
1
2
|
export function setupTailscaleServe(port = 3000, httpsPort = 443) {
|
|
2
3
|
// Tailscale serve: exposes localhost dashboard securely.
|
|
3
4
|
// Only activates when TAILSCALE_ENABLED=1 is set.
|
|
@@ -6,3 +7,34 @@ export function setupTailscaleServe(port = 3000, httpsPort = 443) {
|
|
|
6
7
|
console.log(`[tailscale] Serve enabled on port ${port} (https:${httpsPort})`);
|
|
7
8
|
return true;
|
|
8
9
|
}
|
|
10
|
+
// CGNAT block used by Tailscale for client addresses (100.64.0.0/10).
|
|
11
|
+
const TAILSCALE_CGNAT_PREFIX = "100.";
|
|
12
|
+
/**
|
|
13
|
+
* Detect the IPv4 address Tailscale has assigned to this host, so the dashboard
|
|
14
|
+
* server can auto-bind to the tailnet interface and be reachable from other
|
|
15
|
+
* devices on the tailnet. Returns null when Tailscale is not present.
|
|
16
|
+
*
|
|
17
|
+
* The default Tailscale interface name is `tailscale0` on Linux and `Tailscale`
|
|
18
|
+
* on macOS/Windows. We prefer an address inside Tailscale's CGNAT range
|
|
19
|
+
* (100.64.0.0/10) when one exists; otherwise we return the first IPv4 address
|
|
20
|
+
* found on the interface.
|
|
21
|
+
*/
|
|
22
|
+
export function detectTailscaleIP() {
|
|
23
|
+
const ifaces = networkInterfaces();
|
|
24
|
+
const candidates = [];
|
|
25
|
+
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
26
|
+
if (name !== "tailscale0" && name !== "Tailscale")
|
|
27
|
+
continue;
|
|
28
|
+
if (!addrs)
|
|
29
|
+
continue;
|
|
30
|
+
for (const addr of addrs) {
|
|
31
|
+
if (addr.family === "IPv4") {
|
|
32
|
+
candidates.push(addr.address);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (candidates.length === 0)
|
|
37
|
+
return null;
|
|
38
|
+
const cgnat = candidates.find((ip) => ip.startsWith(TAILSCALE_CGNAT_PREFIX));
|
|
39
|
+
return cgnat ?? candidates[0];
|
|
40
|
+
}
|
|
@@ -110,6 +110,30 @@ export const VC3A_ENABLED = () => sprintFlag("MEGACOMPACT_VC3A");
|
|
|
110
110
|
* the topology build seam (build.ts) and the dashboard topology node/edge view.
|
|
111
111
|
*/
|
|
112
112
|
export const VC3B_ENABLED = () => sprintFlag("MEGACOMPACT_VC3B");
|
|
113
|
+
/**
|
|
114
|
+
* VC3C — topology query + router invalidation (TopologyQueryV1 / RouterKeyV2,
|
|
115
|
+
* M6 router-generation-v2 migration).
|
|
116
|
+
* Default ON. `MEGACOMPACT_VC3C=0` disables and is byte-identical to the
|
|
117
|
+
* predecessor (mode C: no length-delimited router key, no generation
|
|
118
|
+
* invalidation, zero `vector_cortex_router_generation_invalidated` /
|
|
119
|
+
* `vector_cortex_topology_query_demoted` emissions; the predecessor tiered
|
|
120
|
+
* router keying is unchanged). The real consumers are the topology query seam
|
|
121
|
+
* (query.ts), the M6 router-generation-v2 migration seam, and the reader-only
|
|
122
|
+
* dashboard topology query diagnostic view.
|
|
123
|
+
*/
|
|
124
|
+
export const VC3C_ENABLED = () => sprintFlag("MEGACOMPACT_VC3C");
|
|
125
|
+
/**
|
|
126
|
+
* VC4A — dual-tier shard contract (SemanticShardV1 / ExactShardV1 /
|
|
127
|
+
* ShardManifestV1).
|
|
128
|
+
* Default ON. `MEGACOMPACT_VC4A=0` disables and is byte-identical to the
|
|
129
|
+
* predecessor (mode C: exact anchors/current transcript only, no semantic or
|
|
130
|
+
* exact shard manifest is assembled or emitted, zero
|
|
131
|
+
* `vector_cortex_shard_manifest_built` / `vector_cortex_protected_span_rejected`
|
|
132
|
+
* emissions; the predecessor derived pointer and goldens are unchanged). The
|
|
133
|
+
* real consumers are the shard assembly seam (manifest.ts) and the reader-only
|
|
134
|
+
* dashboard shards aggregate view.
|
|
135
|
+
*/
|
|
136
|
+
export const VC4A_ENABLED = () => sprintFlag("MEGACOMPACT_VC4A");
|
|
113
137
|
// ---------------------------------------------------------------------------
|
|
114
138
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
|
|
115
139
|
// Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
|
package/dist/src/config.js
CHANGED
|
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
|
|
114
114
|
// default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
|
|
115
115
|
// so root consumers share one source of truth.
|
|
116
116
|
// ---------------------------------------------------------------------------
|
|
117
|
-
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
|
117
|
+
export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
|
package/dist/src/tieredRouter.js
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
16
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
|
+
import { VC3C_ENABLED } from "./config.js";
|
|
19
|
+
import { Logger } from "./log.js";
|
|
20
|
+
import { routerGenerationInvalidationSeam } from "./vector-cortex/topology/query.js";
|
|
18
21
|
import { openStore } from "./store/sqlite/utils.js";
|
|
19
22
|
import { fts5SearchScoped } from "./store/sqlite/fts5-search.js";
|
|
20
23
|
import { listCheckpoints } from "./store/sqlite.js";
|
|
@@ -287,6 +290,11 @@ export class TieredRouter {
|
|
|
287
290
|
this.cacheMap.clear();
|
|
288
291
|
this.embeddingCache.clear();
|
|
289
292
|
}
|
|
293
|
+
/** VC3C narrow delegate: exact (session,generation) RouterKeyV2 invalidation.
|
|
294
|
+
* No routing rewrite; `MEGACOMPACT_VC3C=0` is a strict no-op, byte-identical. */
|
|
295
|
+
invalidateGeneration(key) {
|
|
296
|
+
routerGenerationInvalidationSeam(key, VC3C_ENABLED(), (e, f) => new Logger().info(e, f));
|
|
297
|
+
}
|
|
290
298
|
/** Snapshot of current metrics for dashboard / logging. */
|
|
291
299
|
getMetrics() {
|
|
292
300
|
const q = Math.max(this.totalQueries, 1);
|