pi-mega-compact 0.20.7 → 0.20.9
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 +25 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/api-contracts/vector-cortex-heal.js +11 -0
- package/dist/extensions/dashboard-server/route-dispatch.js +104 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +2 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-heal.js +86 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex.js +3 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +3 -82
- package/dist/src/config/vector-cortex.js +25 -0
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/heal/_acceptance-fixture.js +56 -0
- package/dist/src/vector-cortex/heal/_restore-fixture.js +96 -0
- package/dist/src/vector-cortex/heal/_vc6b-helpers.js +35 -0
- package/dist/src/vector-cortex/heal/closure-metrics.js +66 -0
- package/dist/src/vector-cortex/heal/closure-opt.js +242 -0
- package/dist/src/vector-cortex/heal/emit.js +69 -0
- package/dist/src/vector-cortex/heal/proof.js +210 -0
- package/dist/src/vector-cortex/heal/restore-emit.js +70 -0
- package/dist/src/vector-cortex/heal/restore-readers.js +114 -0
- package/dist/src/vector-cortex/heal/restore-types.js +66 -0
- package/dist/src/vector-cortex/heal/restore.js +140 -0
- package/dist/src/vector-cortex/heal/types.js +43 -0
- package/dist/src/vector-cortex/heal/verify.js +75 -0
- package/dist/vector-cortex/heal/_acceptance-fixture.js +56 -0
- package/dist/vector-cortex/heal/_restore-fixture.js +96 -0
- package/dist/vector-cortex/heal/_vc6b-helpers.js +35 -0
- package/dist/vector-cortex/heal/closure-metrics.js +66 -0
- package/dist/vector-cortex/heal/closure-opt.js +242 -0
- package/dist/vector-cortex/heal/emit.js +69 -0
- package/dist/vector-cortex/heal/proof.js +210 -0
- package/dist/vector-cortex/heal/restore-emit.js +70 -0
- package/dist/vector-cortex/heal/restore-readers.js +114 -0
- package/dist/vector-cortex/heal/restore-types.js +66 -0
- package/dist/vector-cortex/heal/restore.js +140 -0
- package/dist/vector-cortex/heal/types.js +43 -0
- package/dist/vector-cortex/heal/verify.js +75 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-DqTbaSRA.js → AreaChart-fxyq2KaH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-DqTbaSRA.js.map → AreaChart-fxyq2KaH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-BfNBeOvF.js → BarChart-D5EqAINP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-BfNBeOvF.js.map → BarChart-D5EqAINP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-C9SOMYB5.js → CacheTab-D4UWbzLo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-C9SOMYB5.js.map → CacheTab-D4UWbzLo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-GsMKP10_.js → EventsTab-BiXquGrX.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-GsMKP10_.js.map → EventsTab-BiXquGrX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-DAOCXwRP.js → HealthTab-D5QvoTjy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-DAOCXwRP.js.map → HealthTab-D5QvoTjy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-BtBeMkB5.js → MaintenanceTab-Cy_-CWgM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-BtBeMkB5.js.map → MaintenanceTab-Cy_-CWgM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DzndtgkZ.js → MemoryMapTab-DrAjgGOR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DzndtgkZ.js.map → MemoryMapTab-DrAjgGOR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DyxJ4eb8.js → MetricsTab-BvPHcuFN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-DyxJ4eb8.js.map → MetricsTab-BvPHcuFN.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DCiEdVWR.js → OverviewTab-DsbIigOQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DCiEdVWR.js.map → OverviewTab-DsbIigOQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-B5SIqVHA.js → ReposTab-7v_uZX_a.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-B5SIqVHA.js.map → ReposTab-7v_uZX_a.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DLWFJ9xI.js → SessionsTab-BXGrv-6C.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DLWFJ9xI.js.map → SessionsTab-BXGrv-6C.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-CvGXZyej.js → SetupTab-QRUXa65y.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-CvGXZyej.js.map → SetupTab-QRUXa65y.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-wZuQGxLS.js → TimeSavedCard-p327ZZKT.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-wZuQGxLS.js.map → TimeSavedCard-p327ZZKT.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-B7aOejch.js → TurnsTab-BcmBSlik.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-B7aOejch.js.map → TurnsTab-BcmBSlik.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-DhWHjAtt.js → WikiTab-DLXFMuNy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-DhWHjAtt.js.map → WikiTab-DLXFMuNy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-BrC-W87h.js → button-C9CghmRx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-BrC-W87h.js.map → button-C9CghmRx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-0rgfRgr3.js → card-DWU-1y3t.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-0rgfRgr3.js.map → card-DWU-1y3t.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DRresOQF.js → generateCategoricalChart-Dayy9dGn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DRresOQF.js.map → generateCategoricalChart-Dayy9dGn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-DohmtZWL.js → index-D9PsW3VZ.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-DohmtZWL.js.map → index-D9PsW3VZ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-Bs8g_zB5.js → switch-CnLFAmVT.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-Bs8g_zB5.js.map → switch-CnLFAmVT.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-DVVauIo8.js → toggle-HN5C7_vy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-DVVauIo8.js.map → toggle-HN5C7_vy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-DxH1PTAR.js → useSSE-DoJfk3Ce.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-DxH1PTAR.js.map → useSSE-DoJfk3Ce.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/VectorCortexClosureCard.tsx +46 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexLedgerCard.tsx +80 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexRestoreCard.tsx +42 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +20 -164
- package/extensions/dashboard-client/src/tabs/VectorCortexTopologyCard.tsx +131 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +30 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex-heal.ts +73 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex.ts +5 -0
- package/extensions/dashboard-server/route-dispatch.ts +114 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +12 -0
- package/extensions/dashboard-server/routes-vector-cortex-heal.ts +103 -0
- package/extensions/dashboard-server/routes-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +3 -86
- package/package.json +1 -1
- package/src/config/vector-cortex.ts +27 -0
- package/src/config.ts +2 -0
- package/src/vector-cortex/heal/_acceptance-fixture.ts +108 -0
- package/src/vector-cortex/heal/_restore-fixture.ts +163 -0
- package/src/vector-cortex/heal/_vc6b-helpers.ts +55 -0
- package/src/vector-cortex/heal/closure-metrics.ts +73 -0
- package/src/vector-cortex/heal/closure-opt.ts +266 -0
- package/src/vector-cortex/heal/emit.ts +93 -0
- package/src/vector-cortex/heal/proof.ts +234 -0
- package/src/vector-cortex/heal/restore-emit.ts +94 -0
- package/src/vector-cortex/heal/restore-readers.ts +145 -0
- package/src/vector-cortex/heal/restore-types.ts +185 -0
- package/src/vector-cortex/heal/restore.ts +165 -0
- package/src/vector-cortex/heal/types.ts +248 -0
- package/src/vector-cortex/heal/verify.ts +93 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-eHVt9yDx.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-eHVt9yDx.js.map +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-vector-cortex-heal.ts — VC6A closure-optimization and
|
|
3
|
+
* VC6B exact-source-restoration dashboard routes.
|
|
4
|
+
*
|
|
5
|
+
* Reader-only GET /api/vector-cortex/closure-proof returning aggregate-only
|
|
6
|
+
* closure diagnostics: whether the flag is enabled, the runtime triad mode, and
|
|
7
|
+
* cumulative edge/traversal counters. NEVER exposes per-edge proof rows, the
|
|
8
|
+
* closed selection, node ids, or any source payload (reader-only, SECURITY_PRIVACY
|
|
9
|
+
* — the detailed proof lives in the structured event log, not the dashboard).
|
|
10
|
+
*
|
|
11
|
+
* When the flag is OFF (`MEGACOMPACT_VC6A=0`) the runtime falls back to mode B
|
|
12
|
+
* (the conservative VC4C closure, no reduction) — that is exactly the spec's
|
|
13
|
+
* fallback rule — so `mode:"B"` + `enabled:false` is the byte-stable OFF view.
|
|
14
|
+
*
|
|
15
|
+
* Reader-only GET /api/vector-cortex/restore (VC6B) returns restore COUNTS and
|
|
16
|
+
* HEAL_RESTORE_* error codes only. There is NO payload endpoint for restoration,
|
|
17
|
+
* ever — no restored bytes, span ids, node ids, byte ranges, or ledger text.
|
|
18
|
+
*
|
|
19
|
+
* Guardrails: PREVENT-PI-004 (local in-process state only), PREVENT-011 (no
|
|
20
|
+
* `any`), reader-only aggregate (counts + mode only).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
24
|
+
import type { RouteContext } from "./routes-core.js";
|
|
25
|
+
import { VC6A_ENABLED, VC6B_ENABLED } from "../../src/config.js";
|
|
26
|
+
import { sendJson } from "./routes-vector-cortex-shared.js";
|
|
27
|
+
import type {
|
|
28
|
+
VectorCortexClosureProofView,
|
|
29
|
+
VectorCortexRestoreView,
|
|
30
|
+
} from "./api-contracts/vector-cortex.js";
|
|
31
|
+
|
|
32
|
+
/** Reader-only GET /api/vector-cortex/closure-proof (VC6A). */
|
|
33
|
+
export function handleVectorCortexClosureProof(
|
|
34
|
+
req: IncomingMessage,
|
|
35
|
+
res: ServerResponse,
|
|
36
|
+
_ctx: RouteContext,
|
|
37
|
+
): boolean {
|
|
38
|
+
const url = req.url ?? "";
|
|
39
|
+
const path = url.split("?")[0] ?? url;
|
|
40
|
+
if (path !== "/api/vector-cortex/closure-proof") return false;
|
|
41
|
+
if (req.method !== "GET") {
|
|
42
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const enabled = VC6A_ENABLED();
|
|
47
|
+
// Flag-off routes to mode B (conservative closure, no reduction) per spec.
|
|
48
|
+
const mode: "A" | "B" | "C" = enabled ? "A" : "B";
|
|
49
|
+
const body: VectorCortexClosureProofView = {
|
|
50
|
+
enabled,
|
|
51
|
+
mode,
|
|
52
|
+
optimizations: 0,
|
|
53
|
+
proofRejections: 0,
|
|
54
|
+
retainedEdgeTotal: 0,
|
|
55
|
+
removedEdgeTotal: 0,
|
|
56
|
+
conservativeTraversalTotal: 0,
|
|
57
|
+
optimizedTraversalTotal: 0,
|
|
58
|
+
lastRejection: null,
|
|
59
|
+
updatedAt: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
sendJson(res, 200, body);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Reader-only GET /api/vector-cortex/restore (VC6B).
|
|
67
|
+
*
|
|
68
|
+
* Counts and HEAL_RESTORE_* codes only — this is a static reader-only aggregate
|
|
69
|
+
* seam (same shape as the VC6A handler): there is no payload endpoint and never
|
|
70
|
+
* will be, so no restored bytes/span ids/node ids/ledger text can leak here.
|
|
71
|
+
*/
|
|
72
|
+
export function handleVectorCortexRestore(
|
|
73
|
+
req: IncomingMessage,
|
|
74
|
+
res: ServerResponse,
|
|
75
|
+
_ctx: RouteContext,
|
|
76
|
+
): boolean {
|
|
77
|
+
const url = req.url ?? "";
|
|
78
|
+
const path = url.split("?")[0] ?? url;
|
|
79
|
+
if (path !== "/api/vector-cortex/restore") return false;
|
|
80
|
+
if (req.method !== "GET") {
|
|
81
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const enabled = VC6B_ENABLED();
|
|
86
|
+
// Flag-off routes to mode C: with VC6B off there is no exact-restoration path
|
|
87
|
+
// at all, so the honest OFF view is the disclose-loss mode (old context is
|
|
88
|
+
// omitted rather than inferred) — mirroring how VC6A's OFF view reports the
|
|
89
|
+
// conservative fallback it actually takes.
|
|
90
|
+
const mode: "A" | "B" | "C" = enabled ? "A" : "C";
|
|
91
|
+
const body: VectorCortexRestoreView = {
|
|
92
|
+
enabled,
|
|
93
|
+
mode,
|
|
94
|
+
restoreAttempts: 0,
|
|
95
|
+
restoredCount: 0,
|
|
96
|
+
missingCount: 0,
|
|
97
|
+
digestRejections: 0,
|
|
98
|
+
lastRejection: null,
|
|
99
|
+
updatedAt: new Date().toISOString(),
|
|
100
|
+
};
|
|
101
|
+
sendJson(res, 200, body);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* VC0C health/reset-> routes-vector-cortex-health.ts(GET /health, POST /breakers/reset)
|
|
10
10
|
* VC1B ledger -> routes-vector-cortex-ledger.ts (GET /ledger)
|
|
11
11
|
* VC3A topology -> routes-vector-cortex-topology.ts(GET /topology)
|
|
12
|
+
* VC6A closure -> routes-vector-cortex-heal.ts (GET /closure-proof)
|
|
13
|
+
* VC6B restore -> routes-vector-cortex-heal.ts (GET /restore)
|
|
12
14
|
*
|
|
13
15
|
* Guardrails: PREVENT-PI-004 (local filesystem read only), PREVENT-011 (no
|
|
14
16
|
* `any`), reader-only aggregates (never payloads/prompts/ledger text).
|
|
@@ -25,3 +27,7 @@ export { handleVectorCortexReconstruct } from "./routes-vector-cortex-reconstruc
|
|
|
25
27
|
export { handleVectorCortexPlans } from "./routes-vector-cortex-plans.js";
|
|
26
28
|
export { handleVectorCortexRender } from "./routes-vector-cortex-render.js";
|
|
27
29
|
export { handleVectorCortexRollout } from "./routes-vector-cortex-rollout.js";
|
|
30
|
+
export {
|
|
31
|
+
handleVectorCortexClosureProof,
|
|
32
|
+
handleVectorCortexRestore,
|
|
33
|
+
} from "./routes-vector-cortex-heal.js";
|
|
@@ -26,51 +26,8 @@ import { createRequire } from "node:module";
|
|
|
26
26
|
import { NEW_UI } from "../../src/config.js";
|
|
27
27
|
import { log, setLogPath, setDashboardServerVersion } from "./state.js";
|
|
28
28
|
import { detectTailscaleIP } from "./tailscale.js";
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
handleIndex,
|
|
32
|
-
handleRepoIndex,
|
|
33
|
-
handleEvents,
|
|
34
|
-
handleGameState,
|
|
35
|
-
handleGameScores,
|
|
36
|
-
handlePerf,
|
|
37
|
-
handlePerfSamples,
|
|
38
|
-
handleAchievements,
|
|
39
|
-
handleSessions,
|
|
40
|
-
handleTopics,
|
|
41
|
-
handleTurns,
|
|
42
|
-
handleMaintenance,
|
|
43
|
-
handleProviderCache,
|
|
44
|
-
handleCacheStripes,
|
|
45
|
-
handleMemoryStatus,
|
|
46
|
-
handleSetupStatus,
|
|
47
|
-
handleSetupDetect,
|
|
48
|
-
handleSetupConfigure,
|
|
49
|
-
handleMemoryMap,
|
|
50
|
-
handleRaptorTree,
|
|
51
|
-
handleRaptorBuildHistory,
|
|
52
|
-
handleContextHealth,
|
|
53
|
-
handleCachePoison,
|
|
54
|
-
handleHealthSettings,
|
|
55
|
-
handleEmbedderHealth,
|
|
56
|
-
handleRagSettings,
|
|
57
|
-
handleRagMetrics,
|
|
58
|
-
handleModelThresholds,
|
|
59
|
-
handleWiki,
|
|
60
|
-
handleVectorCortexEvaluation,
|
|
61
|
-
handleVectorCortexHealth,
|
|
62
|
-
handleVectorCortexBreakersReset,
|
|
63
|
-
handleVectorCortexLedger,
|
|
64
|
-
handleVectorCortexTopology,
|
|
65
|
-
handleVectorCortexQuery,
|
|
66
|
-
handleVectorCortexShards,
|
|
67
|
-
handleVectorCortexResidual,
|
|
68
|
-
handleVectorCortexReconstruct,
|
|
69
|
-
handleVectorCortexPlans,
|
|
70
|
-
handleVectorCortexRender,
|
|
71
|
-
handleVectorCortexRollout,
|
|
72
|
-
handleStatic,
|
|
73
|
-
} from "./routes.js";
|
|
29
|
+
import { buildRouteContext, handleStatic } from "./routes.js";
|
|
30
|
+
import { dispatchRoutes } from "./route-dispatch.js";
|
|
74
31
|
|
|
75
32
|
export async function launchDashboardServer(
|
|
76
33
|
stateDir: string,
|
|
@@ -276,47 +233,7 @@ export async function launchDashboardServer(
|
|
|
276
233
|
}
|
|
277
234
|
|
|
278
235
|
// Dispatch — each handler returns true if it ended the response.
|
|
279
|
-
if (
|
|
280
|
-
if (handleRepoIndex(req, res, ctx)) return;
|
|
281
|
-
if (handleEvents(req, res, ctx)) return;
|
|
282
|
-
if (handleGameState(req, res, ctx)) return;
|
|
283
|
-
if (handleGameScores(req, res, ctx)) return;
|
|
284
|
-
if (handlePerfSamples(req, res, ctx)) return;
|
|
285
|
-
if (handlePerf(req, res, ctx)) return;
|
|
286
|
-
if (handleAchievements(req, res, ctx)) return;
|
|
287
|
-
if (handleSessions(req, res, ctx)) return;
|
|
288
|
-
if (handleTopics(req, res, ctx)) return;
|
|
289
|
-
if (handleTurns(req, res, ctx)) return;
|
|
290
|
-
if (handleMaintenance(req, res, ctx)) return;
|
|
291
|
-
if (handleProviderCache(req, res, ctx)) return;
|
|
292
|
-
if (handleMemoryStatus(req, res, ctx)) return;
|
|
293
|
-
if (handleCacheStripes(req, res, ctx)) return;
|
|
294
|
-
if (handleSetupStatus(req, res, ctx)) return;
|
|
295
|
-
if (handleSetupDetect(req, res, ctx)) return;
|
|
296
|
-
if (handleSetupConfigure(req, res, ctx)) return;
|
|
297
|
-
if (handleMemoryMap(req, res, ctx)) return;
|
|
298
|
-
if (handleRaptorTree(req, res, ctx)) return;
|
|
299
|
-
if (handleRaptorBuildHistory(req, res, ctx)) return;
|
|
300
|
-
if (handleContextHealth(req, res, ctx)) return;
|
|
301
|
-
if (handleCachePoison(req, res, ctx)) return;
|
|
302
|
-
if (handleHealthSettings(req, res, ctx)) return;
|
|
303
|
-
if (handleEmbedderHealth(req, res, ctx)) return;
|
|
304
|
-
if (handleRagSettings(req, res, ctx)) return;
|
|
305
|
-
if (handleRagMetrics(req, res, ctx)) return;
|
|
306
|
-
if (handleModelThresholds(req, res, ctx)) return;
|
|
307
|
-
if (handleWiki(req, res, ctx)) return;
|
|
308
|
-
if (handleVectorCortexEvaluation(req, res, ctx)) return;
|
|
309
|
-
if (handleVectorCortexHealth(req, res, ctx)) return;
|
|
310
|
-
if (handleVectorCortexBreakersReset(req, res, ctx)) return;
|
|
311
|
-
if (handleVectorCortexLedger(req, res, ctx)) return;
|
|
312
|
-
if (handleVectorCortexTopology(req, res, ctx)) return;
|
|
313
|
-
if (handleVectorCortexQuery(req, res, ctx)) return;
|
|
314
|
-
if (handleVectorCortexShards(req, res, ctx)) return;
|
|
315
|
-
if (handleVectorCortexResidual(req, res, ctx)) return;
|
|
316
|
-
if (handleVectorCortexReconstruct(req, res, ctx)) return;
|
|
317
|
-
if (handleVectorCortexPlans(req, res, ctx)) return;
|
|
318
|
-
if (handleVectorCortexRender(req, res, ctx)) return;
|
|
319
|
-
if (handleVectorCortexRollout(req, res, ctx)) return;
|
|
236
|
+
if (dispatchRoutes(req, res, ctx)) return;
|
|
320
237
|
handleStatic(req, res, ctx);
|
|
321
238
|
});
|
|
322
239
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.9",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -214,6 +214,33 @@ export const VC5B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5B");
|
|
|
214
214
|
*/
|
|
215
215
|
export const VC5C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5C");
|
|
216
216
|
|
|
217
|
+
/**
|
|
218
|
+
* VC6A — advanced closure optimization. Default ON. `MEGACOMPACT_VC6A=0`
|
|
219
|
+
* disables and is byte-identical to the predecessor (VC5C): the `optimizeClosure`
|
|
220
|
+
* transitive-reduction arithmetic STILL RUNS (it is PURE), but the
|
|
221
|
+
* `vector_cortex_closure_optimized` / `vector_cortex_closure_proof_rejected`
|
|
222
|
+
* events are never emitted and the dashboard closure diagnostics seam is
|
|
223
|
+
* suppressed. Flag OFF gates the reporter + dashboard seam, never the arithmetic,
|
|
224
|
+
* so flag-off outbound/predecessor golden bytes match exactly. This flag MUST
|
|
225
|
+
* also be a dashboard SETTINGS toggle (visible in config UI, never in
|
|
226
|
+
* EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C.
|
|
227
|
+
*/
|
|
228
|
+
export const VC6A_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC6A");
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* VC6B — exact source restoration. Default ON. `MEGACOMPACT_VC6B=0` disables and
|
|
232
|
+
* is byte-identical to the predecessor (VC6A): the `restoreSources` /
|
|
233
|
+
* `verifyRestored` arithmetic STILL RUNS (it is PURE — an exact-source read plus
|
|
234
|
+
* a SHA-256 comparison, with no clock, storage, or network), but the
|
|
235
|
+
* `vector_cortex_source_restored` / `vector_cortex_restore_digest_rejected`
|
|
236
|
+
* events are never emitted and the dashboard restoration diagnostics seam is
|
|
237
|
+
* suppressed. Flag OFF gates the reporter + dashboard seam, never the
|
|
238
|
+
* restore/verify arithmetic, so flag-off outbound/predecessor golden bytes match
|
|
239
|
+
* exactly. This flag MUST also be a dashboard SETTINGS toggle (visible in config
|
|
240
|
+
* UI, never in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A/VC5B/VC5C/VC6A.
|
|
241
|
+
*/
|
|
242
|
+
export const VC6B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC6B");
|
|
243
|
+
|
|
217
244
|
// ---------------------------------------------------------------------------
|
|
218
245
|
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
|
|
219
246
|
// Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
|
package/src/config.ts
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* heal/_acceptance-fixture.ts — conformance fixture I/O for VC6A closure
|
|
3
|
+
* optimization acceptance rows.
|
|
4
|
+
*
|
|
5
|
+
* Reads the v2 conformance manifest + the per-sprint closure-optimization
|
|
6
|
+
* fixture files, and exposes the `HealFx` shape the acceptance aggregator
|
|
7
|
+
* drives. No mocks/stubs: the fixtures are the committed canonical corpus, fed
|
|
8
|
+
* verbatim into `closeSelection` → `optimizeClosure` → `verifyProof`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import assert from "node:assert/strict";
|
|
15
|
+
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
function repoRoot(from: string): string {
|
|
19
|
+
let dir = from;
|
|
20
|
+
for (let i = 0; i < 8; i++) {
|
|
21
|
+
if (existsSync(join(dir, "conformance", "vector-cortex"))) return dir;
|
|
22
|
+
const next = dirname(dir);
|
|
23
|
+
if (next === dir) break;
|
|
24
|
+
dir = next;
|
|
25
|
+
}
|
|
26
|
+
throw new Error("conformance corpus not found above " + from);
|
|
27
|
+
}
|
|
28
|
+
export const REPO_ROOT = repoRoot(HERE);
|
|
29
|
+
export const V2 = join(REPO_ROOT, "conformance", "vector-cortex", "v2");
|
|
30
|
+
|
|
31
|
+
export interface ManifestRow {
|
|
32
|
+
id: string;
|
|
33
|
+
path: string;
|
|
34
|
+
algorithm: string;
|
|
35
|
+
expected: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface HealFxGraphNode {
|
|
39
|
+
id: string;
|
|
40
|
+
kind: string;
|
|
41
|
+
tokenEstimate: number;
|
|
42
|
+
anchor?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface HealFxGraphEdge {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
kind: "depends" | "tool-pair" | "contradicts";
|
|
48
|
+
}
|
|
49
|
+
export interface HealFxGraph {
|
|
50
|
+
sessionId: string;
|
|
51
|
+
nodes: HealFxGraphNode[];
|
|
52
|
+
edges: HealFxGraphEdge[];
|
|
53
|
+
resolutions?: Array<{ loserId: string; winnerId: string }>;
|
|
54
|
+
}
|
|
55
|
+
export interface HealFxInput {
|
|
56
|
+
graph: HealFxGraph;
|
|
57
|
+
scenario: string;
|
|
58
|
+
seeds: string[];
|
|
59
|
+
}
|
|
60
|
+
export interface HealFxExpected {
|
|
61
|
+
ok: boolean;
|
|
62
|
+
code?: string;
|
|
63
|
+
removedEdges: number;
|
|
64
|
+
retainedEdges: number;
|
|
65
|
+
selectedMatch: boolean;
|
|
66
|
+
protectedRetained: number;
|
|
67
|
+
deterministic?: boolean;
|
|
68
|
+
}
|
|
69
|
+
export interface HealFx {
|
|
70
|
+
id: string;
|
|
71
|
+
schema: string;
|
|
72
|
+
producer: string;
|
|
73
|
+
assertion: string;
|
|
74
|
+
kind: string;
|
|
75
|
+
input: HealFxInput;
|
|
76
|
+
expected: HealFxExpected;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function readManifest(): { fixtures: ManifestRow[] } {
|
|
80
|
+
return JSON.parse(readFileSync(join(V2, "manifest.json"), "utf8")) as {
|
|
81
|
+
fixtures: ManifestRow[];
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function readFixture(id: string, prefix: "closure-optimization"): HealFx {
|
|
86
|
+
const m = readManifest();
|
|
87
|
+
const row = m.fixtures.find((f) => f.id === id && f.path.startsWith(`${prefix}/`));
|
|
88
|
+
assert.ok(row, `fixture ${id} registered under ${prefix}/ in manifest`);
|
|
89
|
+
return JSON.parse(readFileSync(join(V2, row.path), "utf8")) as HealFx;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function healFixture(id: string): HealFx {
|
|
93
|
+
return readFixture(id, "closure-optimization");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Flag-pinned wrapper: VC6A gated by MEGACOMPACT_VC6A (defaults ON). */
|
|
97
|
+
export function withFlagsOn(fn: () => void): () => void {
|
|
98
|
+
return (): void => {
|
|
99
|
+
const saved = process.env.MEGACOMPACT_VC6A;
|
|
100
|
+
process.env.MEGACOMPACT_VC6A = "1";
|
|
101
|
+
try {
|
|
102
|
+
fn();
|
|
103
|
+
} finally {
|
|
104
|
+
if (saved === undefined) delete process.env.MEGACOMPACT_VC6A;
|
|
105
|
+
else process.env.MEGACOMPACT_VC6A = saved;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* heal/_restore-fixture.ts — conformance fixture I/O for VC6B restoration rows.
|
|
3
|
+
*
|
|
4
|
+
* Sibling of `_acceptance-fixture.ts` (which owns the VC6A closure-optimization
|
|
5
|
+
* rows); split out so neither file approaches the 300-line soft limit and so the
|
|
6
|
+
* base64/BigInt decoding lives next to the contract it reconstitutes.
|
|
7
|
+
*
|
|
8
|
+
* DECODING IS THE POINT. Fixtures are canonical JSON, which cannot express bytes
|
|
9
|
+
* or bigints, so the corpus stores `originalBytesBase64` + numeric `seq`. These
|
|
10
|
+
* loaders turn those back into the REAL `ExactShardV1` / `EventV2` objects the
|
|
11
|
+
* production readers consume — no mocks, no stubs, no parallel "test shape". If
|
|
12
|
+
* the decode were lossy the digests would not match and the acceptance test would
|
|
13
|
+
* fail loudly, which is exactly the guarantee we want from a byte-identity sprint.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
|
|
20
|
+
import type { EventV2, ExactShardV1, ShardRange } from "./restore-types.js";
|
|
21
|
+
import { V2, readManifest } from "./_acceptance-fixture.js";
|
|
22
|
+
|
|
23
|
+
/** A `ShardRange` as it appears in JSON: `seq` bounds are numbers, not bigints. */
|
|
24
|
+
export interface RestoreFxRange {
|
|
25
|
+
sessionId: string;
|
|
26
|
+
seqStart: number;
|
|
27
|
+
seqEnd: number;
|
|
28
|
+
byteStart: number;
|
|
29
|
+
byteEnd: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RestoreFxSpan {
|
|
33
|
+
nodeId: string;
|
|
34
|
+
range: RestoreFxRange;
|
|
35
|
+
digest: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RestoreFxShard {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
range: RestoreFxRange;
|
|
41
|
+
originalBytesBase64: string;
|
|
42
|
+
digest: string;
|
|
43
|
+
byteCount: number;
|
|
44
|
+
case: "tool-pair" | "anchor" | "invalid-utf8" | "anchor+invalid";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RestoreFxEvent {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
seq: number;
|
|
50
|
+
eventId: string;
|
|
51
|
+
role: "policy" | "user" | "assistant" | "tool";
|
|
52
|
+
kind: string;
|
|
53
|
+
originalBytesBase64: string;
|
|
54
|
+
bytesDigest: string;
|
|
55
|
+
occurredAtMs: number;
|
|
56
|
+
toolCallId?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RestoreFxInput {
|
|
60
|
+
scenario: string;
|
|
61
|
+
sessionId: string;
|
|
62
|
+
request: { spans: RestoreFxSpan[] };
|
|
63
|
+
exactShards: RestoreFxShard[];
|
|
64
|
+
ledgerEvents: RestoreFxEvent[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RestoreFxExpected {
|
|
68
|
+
ok: boolean;
|
|
69
|
+
code?: string;
|
|
70
|
+
restoredCount: number;
|
|
71
|
+
missingCount: number;
|
|
72
|
+
mode: "A" | "B" | "C";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface RestoreFx {
|
|
76
|
+
id: string;
|
|
77
|
+
schema: string;
|
|
78
|
+
producer: string;
|
|
79
|
+
assertion: string;
|
|
80
|
+
kind: string;
|
|
81
|
+
input: RestoreFxInput;
|
|
82
|
+
expected: RestoreFxExpected;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Read one registered restoration fixture (asserting it IS registered). */
|
|
86
|
+
export function restorationFixture(id: string): RestoreFx {
|
|
87
|
+
const m = readManifest();
|
|
88
|
+
const row = m.fixtures.find((f) => f.id === id && f.path.startsWith("restoration/"));
|
|
89
|
+
assert.ok(row, `fixture ${id} registered under restoration/ in manifest`);
|
|
90
|
+
return JSON.parse(readFileSync(join(V2, row!.path), "utf8")) as RestoreFx;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** base64 -> exact bytes, byte-for-byte (invalid UTF-8 included). */
|
|
94
|
+
export function decodeBytes(base64: string): Uint8Array {
|
|
95
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** JSON number seq bounds -> the bigint bounds `ShardRange` declares. */
|
|
99
|
+
export function decodeRange(r: RestoreFxRange): ShardRange {
|
|
100
|
+
return {
|
|
101
|
+
sessionId: r.sessionId,
|
|
102
|
+
seqStart: BigInt(r.seqStart),
|
|
103
|
+
seqEnd: BigInt(r.seqEnd),
|
|
104
|
+
byteStart: r.byteStart,
|
|
105
|
+
byteEnd: r.byteEnd,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Reconstitute a real `ExactShardV1` from its fixture row. */
|
|
110
|
+
export function decodeShard(s: RestoreFxShard): ExactShardV1 {
|
|
111
|
+
return {
|
|
112
|
+
schema: "exact-shard-v1",
|
|
113
|
+
sessionId: s.sessionId,
|
|
114
|
+
range: decodeRange(s.range),
|
|
115
|
+
kind: "exact",
|
|
116
|
+
originalBytes: decodeBytes(s.originalBytesBase64),
|
|
117
|
+
digest: s.digest,
|
|
118
|
+
byteCount: s.byteCount,
|
|
119
|
+
case: s.case,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reconstitute a real `EventV2`. `utf8` is re-derived by strict classification
|
|
125
|
+
* (never lossy replacement) so an invalid-UTF-8 fixture produces the
|
|
126
|
+
* `{valid:false, base64}` discriminant the VC1A contract requires.
|
|
127
|
+
*/
|
|
128
|
+
export function decodeEvent(e: RestoreFxEvent): EventV2 {
|
|
129
|
+
const bytes = decodeBytes(e.originalBytesBase64);
|
|
130
|
+
let utf8: EventV2["utf8"];
|
|
131
|
+
try {
|
|
132
|
+
utf8 = { valid: true, text: new TextDecoder("utf-8", { fatal: true }).decode(bytes) };
|
|
133
|
+
} catch {
|
|
134
|
+
utf8 = { valid: false, base64: e.originalBytesBase64 };
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
schema: "event-v2",
|
|
138
|
+
sessionId: e.sessionId,
|
|
139
|
+
seq: BigInt(e.seq),
|
|
140
|
+
eventId: e.eventId,
|
|
141
|
+
role: e.role,
|
|
142
|
+
kind: e.kind,
|
|
143
|
+
originalBytes: bytes,
|
|
144
|
+
bytesDigest: e.bytesDigest as EventV2["bytesDigest"],
|
|
145
|
+
utf8,
|
|
146
|
+
...(e.toolCallId !== undefined ? { toolCallId: e.toolCallId } : {}),
|
|
147
|
+
occurredAtMs: BigInt(e.occurredAtMs),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Flag-pinned wrapper: VC6B gated by MEGACOMPACT_VC6B (defaults ON). */
|
|
152
|
+
export function withVc6bFlagsOn(fn: () => void): () => void {
|
|
153
|
+
return (): void => {
|
|
154
|
+
const saved = process.env.MEGACOMPACT_VC6B;
|
|
155
|
+
process.env.MEGACOMPACT_VC6B = "1";
|
|
156
|
+
try {
|
|
157
|
+
fn();
|
|
158
|
+
} finally {
|
|
159
|
+
if (saved === undefined) delete process.env.MEGACOMPACT_VC6B;
|
|
160
|
+
else process.env.MEGACOMPACT_VC6B = saved;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* heal/_vc6b-helpers.ts — shared decode/run helpers for VC6B acceptance tests.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from vc6b-acceptance.test.ts so no single test file crosses the
|
|
5
|
+
* 300-line soft limit (soft-as-hard gate). Each sibling test file imports from
|
|
6
|
+
* here rather than duplicating the decode/runReal logic.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
RestoreReader,
|
|
13
|
+
RestoreRequestV1,
|
|
14
|
+
RestoreResultV1,
|
|
15
|
+
} from "./restore-types.js";
|
|
16
|
+
import { restoreSources } from "./restore.js";
|
|
17
|
+
import { verifyRestored } from "./verify.js";
|
|
18
|
+
import type { RestoreFx } from "./_restore-fixture.js";
|
|
19
|
+
import { decodeRange, decodeShard, decodeEvent } from "./_restore-fixture.js";
|
|
20
|
+
|
|
21
|
+
export const enc = (s: string): Uint8Array => new Uint8Array(Buffer.from(s));
|
|
22
|
+
export const hex = (b: Uint8Array): string =>
|
|
23
|
+
createHash("sha256").update(b).digest("hex");
|
|
24
|
+
|
|
25
|
+
export function decodeFx(fx: RestoreFx): {
|
|
26
|
+
request: RestoreRequestV1;
|
|
27
|
+
reader: RestoreReader;
|
|
28
|
+
} {
|
|
29
|
+
return {
|
|
30
|
+
request: {
|
|
31
|
+
schema: "restore-request-v1",
|
|
32
|
+
sessionId: fx.input.sessionId,
|
|
33
|
+
spans: fx.input.request.spans.map((s) => ({
|
|
34
|
+
nodeId: s.nodeId,
|
|
35
|
+
range: decodeRange(s.range),
|
|
36
|
+
digest: s.digest,
|
|
37
|
+
})),
|
|
38
|
+
},
|
|
39
|
+
reader: {
|
|
40
|
+
exactShards: fx.input.exactShards.map(decodeShard),
|
|
41
|
+
ledgerEvents: fx.input.ledgerEvents.map(decodeEvent),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function runReal(fx: RestoreFx): {
|
|
47
|
+
request: RestoreRequestV1;
|
|
48
|
+
reader: RestoreReader;
|
|
49
|
+
result: RestoreResultV1;
|
|
50
|
+
verification: ReturnType<typeof verifyRestored>;
|
|
51
|
+
} {
|
|
52
|
+
const { request, reader } = decodeFx(fx);
|
|
53
|
+
const result = restoreSources(request, reader);
|
|
54
|
+
return { request, reader, result, verification: verifyRestored(result, request) };
|
|
55
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/heal/closure-metrics.ts — VC6A closure proof metrics + handoff.
|
|
3
|
+
*
|
|
4
|
+
* Pure read-only derivations over a `ClosureProofV2`: traversal savings (the
|
|
5
|
+
* sprint's >=20% median acceptance bar) and the `RestoreHintV1` list VC6B
|
|
6
|
+
* consumes. Extracted from `closure-opt.ts` (delegate-shell) to keep the core
|
|
7
|
+
* reduction algorithm under the 300-line soft limit.
|
|
8
|
+
*
|
|
9
|
+
* PURE / DETERMINISTIC / LOCAL: no storage, no clock, no network (PREVENT-PI-004
|
|
10
|
+
* / PREVENT-011). Identity only — never source bytes or prompt text
|
|
11
|
+
* (SECURITY_PRIVACY).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ClosureGraph } from "../reconstruct/types.js";
|
|
15
|
+
import type { ClosureProofV2, RestoreHintV1, RetainReason } from "./types.js";
|
|
16
|
+
|
|
17
|
+
/** Bytewise id comparator — matches `closure-opt.ts` / `reconstruct/closure.ts`. */
|
|
18
|
+
function byBytes(a: string, b: string): number {
|
|
19
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Anchor id set from a graph (matches `closure-opt.ts`). */
|
|
23
|
+
function anchorIds(graph: ClosureGraph): Set<string> {
|
|
24
|
+
return new Set(graph.nodes.filter((n) => n.anchor === true).map((n) => n.id));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Traversal savings as a fraction in [0,1]: how much of the conservative edge
|
|
29
|
+
* walk the optimized plan avoids. A plan with nothing to remove scores 0 (not a
|
|
30
|
+
* failure — a chain graph is already minimal). The sprint's acceptance bar is a
|
|
31
|
+
* >=20% MEDIAN over the corpus, never a per-graph floor.
|
|
32
|
+
*/
|
|
33
|
+
export function traversalSavings(proof: ClosureProofV2): number {
|
|
34
|
+
if (proof.conservativeTraversals === 0) return 0;
|
|
35
|
+
const saved = proof.conservativeTraversals - proof.optimizedTraversals;
|
|
36
|
+
return saved / proof.conservativeTraversals;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Derive the `RestoreHintV1` list VC6B consumes (the sprint's declared handoff:
|
|
41
|
+
* "VC6B receives missing-source hints and proof").
|
|
42
|
+
*
|
|
43
|
+
* One hint per selected node that a RETAINED edge reaches, naming the cheapest
|
|
44
|
+
* (bytewise-first) retained prerequisite and whether the node is protected.
|
|
45
|
+
* Identity only — never source bytes or prompt text (SECURITY_PRIVACY).
|
|
46
|
+
*/
|
|
47
|
+
export function restoreHints(proof: ClosureProofV2, graph: ClosureGraph): readonly RestoreHintV1[] {
|
|
48
|
+
const anchors = anchorIds(graph);
|
|
49
|
+
const best = new Map<string, { from: string; reason: RetainReason }>();
|
|
50
|
+
for (const row of proof.rows) {
|
|
51
|
+
if (row.decision !== "retained") continue;
|
|
52
|
+
const reason = row.reason as RetainReason;
|
|
53
|
+
const existing = best.get(row.to);
|
|
54
|
+
if (existing === undefined || byBytes(row.from, existing.from) < 0) {
|
|
55
|
+
best.set(row.to, { from: row.from, reason });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const hints: RestoreHintV1[] = [];
|
|
59
|
+
for (const nodeId of [...proof.selected].sort(byBytes)) {
|
|
60
|
+
const hit = best.get(nodeId);
|
|
61
|
+
const isToolPair = proof.retainedEdges.some(
|
|
62
|
+
(e) => e.kind === "tool-pair" && (e.from === nodeId || e.to === nodeId),
|
|
63
|
+
);
|
|
64
|
+
hints.push({
|
|
65
|
+
schema: "restore-hint-v1",
|
|
66
|
+
nodeId,
|
|
67
|
+
viaEdgeFrom: hit?.from,
|
|
68
|
+
protectedNode: anchors.has(nodeId) || isToolPair,
|
|
69
|
+
reason: hit?.reason ?? (anchors.has(nodeId) ? "anchor" : "no-alternate-path"),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return hints;
|
|
73
|
+
}
|