pi-mega-compact 0.20.11 → 0.20.13
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-breakers.js +31 -0
- package/dist/config/vector-cortex.js +17 -26
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/api-contracts/vector-cortex-cache.js +11 -0
- package/dist/extensions/dashboard-server/route-dispatch.js +6 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/extensions/dashboard-server/routes-vector-cortex-crystals.js +65 -0
- package/dist/src/config/vector-cortex-breakers.js +31 -0
- package/dist/src/config/vector-cortex.js +17 -26
- package/dist/src/config.js +1 -1
- package/dist/src/vector-cortex/cache/_crystal-fixture.js +71 -0
- package/dist/src/vector-cortex/cache/crystal-emit.js +76 -0
- package/dist/src/vector-cortex/cache/crystal.js +201 -0
- package/dist/src/vector-cortex/cache/store.js +196 -0
- package/dist/vector-cortex/cache/_crystal-fixture.js +71 -0
- package/dist/vector-cortex/cache/crystal-emit.js +76 -0
- package/dist/vector-cortex/cache/crystal.js +201 -0
- package/dist/vector-cortex/cache/store.js +196 -0
- package/dist/vector-cortex/cache/types.js +73 -0
- package/extensions/dashboard-client/dist/assets/{AreaChart-BDMjyRQp.js → AreaChart-CB-U7ViX.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-BDMjyRQp.js.map → AreaChart-CB-U7ViX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-CqzPzOkj.js → BarChart-DnLw0fxA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-CqzPzOkj.js.map → BarChart-DnLw0fxA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bo8SH9q8.js → CacheTab-CjyTVDiP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bo8SH9q8.js.map → CacheTab-CjyTVDiP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DIEs6zC-.js → EventsTab-Bz3QEWam.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DIEs6zC-.js.map → EventsTab-Bz3QEWam.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-3a8IjwIK.js → HealthTab-CUylEvEY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-3a8IjwIK.js.map → HealthTab-CUylEvEY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CdKwAcXo.js → MaintenanceTab-Cz_hj_E7.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CdKwAcXo.js.map → MaintenanceTab-Cz_hj_E7.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-D2hHuNj-.js → MemoryMapTab-Dh8brIR1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-D2hHuNj-.js.map → MemoryMapTab-Dh8brIR1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CFhIhqrr.js → MetricsTab-CkqSXc6e.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CFhIhqrr.js.map → MetricsTab-CkqSXc6e.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-Dzp5ZoiA.js → OverviewTab-DGbJcx6d.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-Dzp5ZoiA.js.map → OverviewTab-DGbJcx6d.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-CFJT9mDR.js → ReposTab-BawS3nfF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-CFJT9mDR.js.map → ReposTab-BawS3nfF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BhP1z6_y.js → SessionsTab-CUVnmYks.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BhP1z6_y.js.map → SessionsTab-CUVnmYks.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-2Glh7MKk.js → SetupTab-cxTuyZNk.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-2Glh7MKk.js.map → SetupTab-cxTuyZNk.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DmwBDhkw.js → TimeSavedCard-Cv5SENkE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DmwBDhkw.js.map → TimeSavedCard-Cv5SENkE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CYIaOUUZ.js → TurnsTab-yl6BBG_F.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CYIaOUUZ.js.map → TurnsTab-yl6BBG_F.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-94fqVjat.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-94fqVjat.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-CP-JEd17.js → WikiTab-DKv-0xCQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-CP-JEd17.js.map → WikiTab-DKv-0xCQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-B1RhLsGs.js → button-CVPrO4UU.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-B1RhLsGs.js.map → button-CVPrO4UU.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-BLPT2-8G.js → card-BFLnQJEo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-BLPT2-8G.js.map → card-BFLnQJEo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DVOMJzL2.js → generateCategoricalChart-B-IUoLd1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DVOMJzL2.js.map → generateCategoricalChart-B-IUoLd1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-CuLdiHRl.js → index-Do749WlW.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-CuLdiHRl.js.map → index-Do749WlW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-C5tqzhcl.js → switch-3uFnZmtq.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-C5tqzhcl.js.map → switch-3uFnZmtq.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-HcM6W2Yl.js → toggle-C1rYLeXe.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-HcM6W2Yl.js.map → toggle-C1rYLeXe.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BtWUs2kL.js → useSSE-D1qzLzgR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BtWUs2kL.js.map → useSSE-D1qzLzgR.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexCrystalsCard.tsx +46 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +8 -0
- package/extensions/dashboard-client/src/types/vector-cortex.ts +20 -0
- package/extensions/dashboard-server/api-contracts/vector-cortex-cache.ts +53 -0
- package/extensions/dashboard-server/route-dispatch.ts +5 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes-vector-cortex-crystals.ts +74 -0
- package/package.json +1 -1
- package/src/config/vector-cortex-breakers.ts +32 -0
- package/src/config/vector-cortex.ts +31 -26
- package/src/config.ts +1 -0
- package/src/vector-cortex/cache/_crystal-fixture.ts +126 -0
- package/src/vector-cortex/cache/crystal-emit.ts +99 -0
- package/src/vector-cortex/cache/crystal.ts +217 -0
- package/src/vector-cortex/cache/store.ts +213 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD7vGaRS.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DD7vGaRS.js.map +0 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-contracts/vector-cortex-cache.ts — VC7A frozen-range-crystal API contract.
|
|
3
|
+
*
|
|
4
|
+
* Split from vector-cortex-heal.ts (a separate concern, not a size overflow):
|
|
5
|
+
* crystals are a derived CACHE, not a repair path, and keeping the contract in
|
|
6
|
+
* its own file leaves both well under the 400-line extension limit as VC7B lands.
|
|
7
|
+
*
|
|
8
|
+
* PREVENT-PI-004: type definitions only, no network code.
|
|
9
|
+
* PREVENT-011: no `any` type.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reader-only frozen-crystal cache diagnostics view for
|
|
14
|
+
* GET /api/vector-cortex/cache-crystals (VC7A).
|
|
15
|
+
*
|
|
16
|
+
* COUNTS + BYTES ONLY. A crystal IS a rendered prompt, which makes this the one
|
|
17
|
+
* surface where an unguarded payload field would dump the entire framed
|
|
18
|
+
* conversation into the dashboard. So the view carries no frozen bytes, no
|
|
19
|
+
* covered source ranges, no span or covered digests, no session ids, and no
|
|
20
|
+
* request digests (SECURITY_PRIVACY — the exact ledger is not diagnostic data).
|
|
21
|
+
* Only aggregate counters and the observable byte volumes are exposed; per-key
|
|
22
|
+
* detail lives in the structured event log, not here.
|
|
23
|
+
*/
|
|
24
|
+
export interface VectorCortexCrystalsView {
|
|
25
|
+
/** Whether the VC7A frozen-range-crystal flag is enabled. */
|
|
26
|
+
readonly enabled: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Runtime triad mode: "A" crystal store hit, "B" miss/collision forced a
|
|
29
|
+
* fresh deterministic render, "C" store unavailable so the cache is bypassed
|
|
30
|
+
* and nothing is served from it.
|
|
31
|
+
*/
|
|
32
|
+
readonly mode: "A" | "B" | "C";
|
|
33
|
+
/** Distinct crystals currently held by the store. */
|
|
34
|
+
readonly crystalCount: number;
|
|
35
|
+
/** Aggregate frozen bytes held across all crystals. */
|
|
36
|
+
readonly totalBytes: number;
|
|
37
|
+
/** Reads answered from the store (mode A). */
|
|
38
|
+
readonly hits: number;
|
|
39
|
+
/** Reads with no stored crystal, forcing a fresh render (mode B). */
|
|
40
|
+
readonly misses: number;
|
|
41
|
+
/** Bytes served from hits — the cache's observable benefit. */
|
|
42
|
+
readonly hitBytes: number;
|
|
43
|
+
/** First writes that actually published a crystal. */
|
|
44
|
+
readonly writes: number;
|
|
45
|
+
/** Idempotent re-writes of byte-identical crystals. */
|
|
46
|
+
readonly duplicateWrites: number;
|
|
47
|
+
/** Same-key, different-bytes writes refused (CRY_KEY_COLLISION). */
|
|
48
|
+
readonly collisions: number;
|
|
49
|
+
/** Last failure reason (a CRY_* code), or null if none yet. */
|
|
50
|
+
readonly lastFailure: string | null;
|
|
51
|
+
/** ISO timestamp of the snapshot. */
|
|
52
|
+
readonly updatedAt: string;
|
|
53
|
+
}
|
|
@@ -60,6 +60,10 @@ import {
|
|
|
60
60
|
// VC6C repair lives in its own module (routes-vector-cortex-repair.ts) so the
|
|
61
61
|
// heal route file stays well under the 400-line extension soft limit.
|
|
62
62
|
import { handleVectorCortexRepair } from "./routes-vector-cortex-repair.js";
|
|
63
|
+
// VC7A frozen-range crystals likewise get their own module so the cache seam
|
|
64
|
+
// stays independent of the heal/repair handlers and every file stays well under
|
|
65
|
+
// the 400-line extension limit.
|
|
66
|
+
import { handleVectorCortexCrystals } from "./routes-vector-cortex-crystals.js";
|
|
63
67
|
|
|
64
68
|
/**
|
|
65
69
|
* Dispatch a request through every registered route handler.
|
|
@@ -114,5 +118,6 @@ export function dispatchRoutes(
|
|
|
114
118
|
if (handleVectorCortexClosureProof(req, res, ctx)) return true;
|
|
115
119
|
if (handleVectorCortexRestore(req, res, ctx)) return true;
|
|
116
120
|
if (handleVectorCortexRepair(req, res, ctx)) return true;
|
|
121
|
+
if (handleVectorCortexCrystals(req, res, ctx)) return true;
|
|
117
122
|
return false;
|
|
118
123
|
}
|
|
@@ -156,5 +156,11 @@ export const VECTOR_CORTEX_SETTINGS: SettingGroup = {
|
|
|
156
156
|
"Self-healing derived-state controller: detects gaps between each derived subsystem's high-water mark and the durable authority high-water, then rebuilds derived state by copy -> root-digest verification -> atomic pointer switch, so a partially rebuilt subsystem is never made visible. A targeted single-subsystem rebuild is mode A; an ambiguous gap escalates to a full deterministic rebuild (mode B); if both rebuild paths fail the derived state is disabled rather than served stale (mode C). Rebuilds are rate-limited to one per subsystem per 5 minutes with deterministic exponential backoff, so a persistently failing subsystem cannot spin. OFF = byte-identical predecessor (VC6B); no controller runs, so the dashboard reports mode C.",
|
|
157
157
|
true,
|
|
158
158
|
),
|
|
159
|
+
boolDirect(
|
|
160
|
+
"MEGACOMPACT_VC7A",
|
|
161
|
+
"VC7A Frozen Range Crystals",
|
|
162
|
+
"Frozen range crystals: caches a rendered prompt under an IMMUTABLE key built from the source ranges it covers, the digest of those covered bytes, the validated durable dependency high-water, and the renderer + provider profile — and deliberately NOT the global ledger frontier, so an unrelated append leaves the key unchanged instead of invalidating every crystal on every turn. Any covered-byte, dependency, renderer, or profile change invalidates 100%. Ranges are sorted by source start and overlapping ranges are rejected (CRY_RANGE_OVERLAP) rather than merged. The store is content-addressed and write-once: identical bytes re-written are idempotent, but a same-key/different-bytes write returns CRY_KEY_COLLISION and never overwrites, surfacing renderer non-determinism instead of hiding it. A store hit is mode A, a miss/collision forces a fresh deterministic render (mode B), and an unavailable store bypasses the cache entirely (mode C). OFF = byte-identical predecessor (VC6C); the crystal/store arithmetic still runs, only the reporter + dashboard seam is suppressed.",
|
|
163
|
+
true,
|
|
164
|
+
),
|
|
159
165
|
],
|
|
160
166
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-vector-cortex-crystals.ts — VC7A frozen-range-crystal
|
|
3
|
+
* dashboard route.
|
|
4
|
+
*
|
|
5
|
+
* Reader-only GET /api/vector-cortex/cache-crystals returning the crystal
|
|
6
|
+
* store's aggregate diagnostics: whether the flag is enabled, the runtime triad
|
|
7
|
+
* mode, how many crystals are held and at what byte volume, hit/miss/hit-byte
|
|
8
|
+
* counters, write/duplicate/collision counters, and the last CRY_* failure code.
|
|
9
|
+
*
|
|
10
|
+
* COUNTS + BYTES + ERROR CODES ONLY. A crystal is a FROZEN RENDERED PROMPT, so
|
|
11
|
+
* this is the surface where a careless payload field would leak the whole framed
|
|
12
|
+
* conversation. It NEVER exposes frozen bytes, covered ranges, span or covered
|
|
13
|
+
* digests, request digests, session ids, or key digests (reader-only,
|
|
14
|
+
* SECURITY_PRIVACY — per-key detail belongs in the structured event log). There
|
|
15
|
+
* is no mutation seam either: crystals are written by the render path, never by
|
|
16
|
+
* a dashboard request, and the store is write-once by construction, so a
|
|
17
|
+
* dashboard-driven write could not overwrite anything even if it existed.
|
|
18
|
+
* Non-GET is rejected outright.
|
|
19
|
+
*
|
|
20
|
+
* Split into its own file (rather than grown into routes-vector-cortex-repair.ts)
|
|
21
|
+
* to keep every extensions/ file well under the 400-line soft-as-hard limit.
|
|
22
|
+
*
|
|
23
|
+
* Guardrails: PREVENT-PI-004 (local in-process state only), PREVENT-011 (no
|
|
24
|
+
* `any`), reader-only aggregate (counts + codes only).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
28
|
+
import type { RouteContext } from "./routes-core.js";
|
|
29
|
+
import { VC7A_ENABLED } from "../../src/config.js";
|
|
30
|
+
import { sendJson } from "./routes-vector-cortex-shared.js";
|
|
31
|
+
import type { VectorCortexCrystalsView } from "./api-contracts/vector-cortex-cache.js";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Reader-only GET /api/vector-cortex/cache-crystals (VC7A).
|
|
35
|
+
*
|
|
36
|
+
* Counts, byte volumes, and CRY_* codes only — a static reader-only aggregate
|
|
37
|
+
* seam with the same shape as the VC6A/VC6B/VC6C handlers.
|
|
38
|
+
*/
|
|
39
|
+
export function handleVectorCortexCrystals(
|
|
40
|
+
req: IncomingMessage,
|
|
41
|
+
res: ServerResponse,
|
|
42
|
+
_ctx: RouteContext,
|
|
43
|
+
): boolean {
|
|
44
|
+
const url = req.url ?? "";
|
|
45
|
+
const path = url.split("?")[0] ?? url;
|
|
46
|
+
if (path !== "/api/vector-cortex/cache-crystals") return false;
|
|
47
|
+
if (req.method !== "GET") {
|
|
48
|
+
sendJson(res, 405, { error: "method_not_allowed" });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const enabled = VC7A_ENABLED();
|
|
53
|
+
// Flag-off routes to mode C: with VC7A off nothing is served from the crystal
|
|
54
|
+
// cache, which is exactly the spec's "cache bypass" outcome. Reporting A (hit)
|
|
55
|
+
// or B (fresh render forced by a miss) would imply a cache path that is not
|
|
56
|
+
// wired at all. Mirrors how VC6C's OFF view reports the mode it actually takes.
|
|
57
|
+
const mode: "A" | "B" | "C" = enabled ? "A" : "C";
|
|
58
|
+
const body: VectorCortexCrystalsView = {
|
|
59
|
+
enabled,
|
|
60
|
+
mode,
|
|
61
|
+
crystalCount: 0,
|
|
62
|
+
totalBytes: 0,
|
|
63
|
+
hits: 0,
|
|
64
|
+
misses: 0,
|
|
65
|
+
hitBytes: 0,
|
|
66
|
+
writes: 0,
|
|
67
|
+
duplicateWrites: 0,
|
|
68
|
+
collisions: 0,
|
|
69
|
+
lastFailure: null,
|
|
70
|
+
updatedAt: new Date().toISOString(),
|
|
71
|
+
};
|
|
72
|
+
sendJson(res, 200, body);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.13",
|
|
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",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/vector-cortex-breakers.ts — breaker state machine constants.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from vector-cortex.ts to keep that file under the 300-line soft
|
|
5
|
+
* limit (soft-as-hard gate). These constants are normative in
|
|
6
|
+
* TRIAD_RESILIENCE.md §breaker and consumed by VC0C's breaker seam.
|
|
7
|
+
*
|
|
8
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Rolling eligibility window (milliseconds). */
|
|
12
|
+
export const BREAKER_WINDOW_MS = 60_000;
|
|
13
|
+
/** Minimum attempts before a breaker may trip or promote. */
|
|
14
|
+
export const BREAKER_MIN_ATTEMPTS = 20;
|
|
15
|
+
/** Performance trip: ≥ this many failures in window, or ≥ this fraction. */
|
|
16
|
+
export const BREAKER_PERF_FAILURES = 5;
|
|
17
|
+
export const BREAKER_PERF_FAILURE_RATE = 0.1;
|
|
18
|
+
/** Correctness trip trips on the first correctness failure. */
|
|
19
|
+
export const BREAKER_CORRECTNESS_FAILURES = 1;
|
|
20
|
+
/** Cooldown before an open breaker may probe (milliseconds). */
|
|
21
|
+
export const BREAKER_COOLDOWN_MS = 30_000;
|
|
22
|
+
/** Consecutive successful probes required to advance a state. */
|
|
23
|
+
export const BREAKER_PROBE_COUNT = 3;
|
|
24
|
+
/** Exponential retry base: 30s * 2^attempt, capped, ±10% jitter. */
|
|
25
|
+
export const BREAKER_RETRY_BASE_MS = 30_000;
|
|
26
|
+
export const BREAKER_RETRY_CAP_MS = 15 * 60_000;
|
|
27
|
+
export const BREAKER_RETRY_JITTER = 0.1;
|
|
28
|
+
/** Promotion hysteresis: failure rate must be < this and p95 within budget. */
|
|
29
|
+
export const BREAKER_HYSTERESIS_FAILURE_RATE = 0.02;
|
|
30
|
+
export const BREAKER_HYSTERESIS_BUDGET_P95_MS = 50;
|
|
31
|
+
/** Minimum healthy residence before a further promotion (milliseconds). */
|
|
32
|
+
export const BREAKER_MIN_HEALTHY_RESIDENCE_MS = 5 * 60_000;
|
|
@@ -258,30 +258,35 @@ export const VC6B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC6B");
|
|
|
258
258
|
*/
|
|
259
259
|
export const VC6C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC6C");
|
|
260
260
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
/**
|
|
262
|
+
* VC7A — frozen range crystals. Default ON. `MEGACOMPACT_VC7A=0` disables and is
|
|
263
|
+
* byte-identical to the predecessor (VC6C): the `encodeCrystalKey` /
|
|
264
|
+
* `CrystalStore` arithmetic STILL RUNS (it is PURE — a canonical length-prefixed
|
|
265
|
+
* encoding plus SHA-256, with no clock, storage, or network), so a crystal is
|
|
266
|
+
* keyed identically and a same-key/different-bytes write is still refused with
|
|
267
|
+
* the flag off. The flag gates ONLY the `vector_cortex_crystal_written` /
|
|
268
|
+
* `vector_cortex_crystal_collision` events and the cache-crystals dashboard seam,
|
|
269
|
+
* which reports `enabled:false` + mode C when off. Flag OFF never gates the
|
|
270
|
+
* crystal/store arithmetic, so flag-off outbound/predecessor golden bytes match
|
|
271
|
+
* exactly. This flag MUST also be a dashboard SETTINGS toggle (visible in config
|
|
272
|
+
* UI, never in EXCLUDED_SETTINGS), mirroring VC4A..VC6C.
|
|
273
|
+
*/
|
|
274
|
+
export const VC7A_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC7A");
|
|
265
275
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
/** Promotion hysteresis: failure rate must be < this and p95 within budget. */
|
|
284
|
-
export const BREAKER_HYSTERESIS_FAILURE_RATE = 0.02;
|
|
285
|
-
export const BREAKER_HYSTERESIS_BUDGET_P95_MS = 50;
|
|
286
|
-
/** Minimum healthy residence before a further promotion (milliseconds). */
|
|
287
|
-
export const BREAKER_MIN_HEALTHY_RESIDENCE_MS = 5 * 60_000;
|
|
276
|
+
// Breaker state machine constants (TRIAD_RESILIENCE.md §breaker) extracted to
|
|
277
|
+
// vector-cortex-breakers.ts to keep this file under the 300-line soft limit.
|
|
278
|
+
export {
|
|
279
|
+
BREAKER_WINDOW_MS,
|
|
280
|
+
BREAKER_MIN_ATTEMPTS,
|
|
281
|
+
BREAKER_PERF_FAILURES,
|
|
282
|
+
BREAKER_PERF_FAILURE_RATE,
|
|
283
|
+
BREAKER_CORRECTNESS_FAILURES,
|
|
284
|
+
BREAKER_COOLDOWN_MS,
|
|
285
|
+
BREAKER_PROBE_COUNT,
|
|
286
|
+
BREAKER_RETRY_BASE_MS,
|
|
287
|
+
BREAKER_RETRY_CAP_MS,
|
|
288
|
+
BREAKER_RETRY_JITTER,
|
|
289
|
+
BREAKER_HYSTERESIS_FAILURE_RATE,
|
|
290
|
+
BREAKER_HYSTERESIS_BUDGET_P95_MS,
|
|
291
|
+
BREAKER_MIN_HEALTHY_RESIDENCE_MS,
|
|
292
|
+
} from "./vector-cortex-breakers.js";
|
package/src/config.ts
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cache/_crystal-fixture.ts — conformance fixture I/O for VC7A crystal rows.
|
|
3
|
+
*
|
|
4
|
+
* Sibling of `../heal/_restore-fixture.ts`, same job for a different corpus:
|
|
5
|
+
* turn canonical JSON back into the REAL production types the cache modules
|
|
6
|
+
* consume. Fixtures cannot express bigints, so `dependencyHighWater` and the
|
|
7
|
+
* span seq bounds are stored as numbers and converted here — if that conversion
|
|
8
|
+
* were lossy the encoded keys would diverge and the acceptance rows would fail
|
|
9
|
+
* loudly, which is exactly the guarantee an identity sprint needs.
|
|
10
|
+
*
|
|
11
|
+
* No mocks, no stubs, no parallel "test shape": the decoded objects ARE
|
|
12
|
+
* `CrystalKeyV1` / `DagSpan` and are fed verbatim into `encodeCrystalKey` and
|
|
13
|
+
* `CrystalStore`.
|
|
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 { CrystalKeyV1, DagSpan } from "./types.js";
|
|
21
|
+
import { computeCoveredDigest } from "./crystal.js";
|
|
22
|
+
import { V2, readManifest } from "../heal/_acceptance-fixture.js";
|
|
23
|
+
|
|
24
|
+
/** A `DagSpan` as it appears in JSON: seq bounds are numbers, not bigints. */
|
|
25
|
+
export interface CrystalFxSpan {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
startSeq: number;
|
|
28
|
+
endSeq: number;
|
|
29
|
+
startByte: number;
|
|
30
|
+
endByte: number;
|
|
31
|
+
digest: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CrystalFxKey {
|
|
35
|
+
profileId: string;
|
|
36
|
+
profileVersion: string;
|
|
37
|
+
requestDigest: string;
|
|
38
|
+
rendererVersion: string;
|
|
39
|
+
dependencyHighWater: number;
|
|
40
|
+
sourceRanges: CrystalFxSpan[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CrystalFxInput {
|
|
44
|
+
scenario: "key" | "compare" | "store";
|
|
45
|
+
mode: string;
|
|
46
|
+
key: CrystalFxKey;
|
|
47
|
+
other?: CrystalFxKey;
|
|
48
|
+
unrelatedAppend?: CrystalFxSpan;
|
|
49
|
+
bytes?: string;
|
|
50
|
+
secondBytes?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CrystalFxExpected {
|
|
54
|
+
ok: boolean;
|
|
55
|
+
code?: string;
|
|
56
|
+
sameKey?: boolean;
|
|
57
|
+
written?: boolean;
|
|
58
|
+
crystalCount?: number;
|
|
59
|
+
rangeCount?: number;
|
|
60
|
+
sortedSessions?: string[];
|
|
61
|
+
sortedStartBytes?: number[];
|
|
62
|
+
mode?: "A" | "B" | "C";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface CrystalFx {
|
|
66
|
+
id: string;
|
|
67
|
+
schema: string;
|
|
68
|
+
producer: string;
|
|
69
|
+
assertion: string;
|
|
70
|
+
kind: string;
|
|
71
|
+
input: CrystalFxInput;
|
|
72
|
+
expected: CrystalFxExpected;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Read one registered cache-crystal fixture (asserting it IS registered). */
|
|
76
|
+
export function crystalFixture(id: string): CrystalFx {
|
|
77
|
+
const m = readManifest();
|
|
78
|
+
const row = m.fixtures.find((f) => f.id === id && f.path.startsWith("cache-crystals/"));
|
|
79
|
+
assert.ok(row, `fixture ${id} registered under cache-crystals/ in manifest`);
|
|
80
|
+
return JSON.parse(readFileSync(join(V2, row!.path), "utf8")) as CrystalFx;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** JSON number seq bounds -> the bigint bounds `DagSpan` declares. */
|
|
84
|
+
export function decodeSpan(s: CrystalFxSpan): DagSpan {
|
|
85
|
+
return {
|
|
86
|
+
sessionId: s.sessionId,
|
|
87
|
+
startSeq: BigInt(s.startSeq),
|
|
88
|
+
endSeq: BigInt(s.endSeq),
|
|
89
|
+
startByte: s.startByte,
|
|
90
|
+
endByte: s.endByte,
|
|
91
|
+
digest: s.digest as DagSpan["digest"],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reconstitute a real `CrystalKeyV1`. `coveredDigest` is DERIVED from the ranges
|
|
97
|
+
* rather than carried in the corpus: the covered digest is a function of the
|
|
98
|
+
* ranges, so storing it would let a fixture assert a self-inconsistent identity
|
|
99
|
+
* that the encoder would silently re-derive anyway.
|
|
100
|
+
*/
|
|
101
|
+
export function decodeKey(k: CrystalFxKey): CrystalKeyV1 {
|
|
102
|
+
const sourceRanges = k.sourceRanges.map(decodeSpan);
|
|
103
|
+
return {
|
|
104
|
+
profileId: k.profileId,
|
|
105
|
+
profileVersion: k.profileVersion,
|
|
106
|
+
requestDigest: k.requestDigest,
|
|
107
|
+
rendererVersion: k.rendererVersion,
|
|
108
|
+
dependencyHighWater: BigInt(k.dependencyHighWater),
|
|
109
|
+
sourceRanges,
|
|
110
|
+
coveredDigest: computeCoveredDigest(sourceRanges),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Flag-pinned wrapper: VC7A gated by MEGACOMPACT_VC7A (defaults ON). */
|
|
115
|
+
export function withVc7aFlag(value: string, fn: () => void): () => void {
|
|
116
|
+
return (): void => {
|
|
117
|
+
const saved = process.env.MEGACOMPACT_VC7A;
|
|
118
|
+
process.env.MEGACOMPACT_VC7A = value;
|
|
119
|
+
try {
|
|
120
|
+
fn();
|
|
121
|
+
} finally {
|
|
122
|
+
if (saved === undefined) delete process.env.MEGACOMPACT_VC7A;
|
|
123
|
+
else process.env.MEGACOMPACT_VC7A = saved;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vector-cortex/cache/crystal-emit.ts — VC7A event reporter seam.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `../heal/restore-emit.ts`: a thin `safe()` wrapper around an optional
|
|
5
|
+
* injected `emit` (unit tests pass `undefined` and stay pure), and the two event
|
|
6
|
+
* names the sprint spec requires verbatim:
|
|
7
|
+
* - `vector_cortex_crystal_written` — a crystal was frozen and published.
|
|
8
|
+
* - `vector_cortex_crystal_collision` — a same-key, different-bytes write was
|
|
9
|
+
* refused.
|
|
10
|
+
*
|
|
11
|
+
* FLAG SEMANTICS. `encodeCrystalKey` and every `CrystalStore` operation are PURE
|
|
12
|
+
* and run REGARDLESS of `MEGACOMPACT_VC7A`. The flag gates ONLY this reporting +
|
|
13
|
+
* dashboard seam: with the flag off we still key crystals identically, still
|
|
14
|
+
* write once, and still refuse collisions — we just do not announce it under the
|
|
15
|
+
* VC7A event namespace, and the dashboard reports `enabled:false` + mode C. That
|
|
16
|
+
* is what makes flag-off byte-identical to VC6C: the arithmetic is never skipped,
|
|
17
|
+
* only the emission.
|
|
18
|
+
*
|
|
19
|
+
* PAYLOAD DISCIPLINE. These events carry the KEY DIGEST, byte COUNTS, and the
|
|
20
|
+
* failure code — never the frozen bytes, never covered source text, never the
|
|
21
|
+
* span digests of user content. A crystal is a rendered prompt: an unguarded
|
|
22
|
+
* `payload` field here would dump the entire framed conversation into a log file
|
|
23
|
+
* (SECURITY_PRIVACY — the exact ledger is not diagnostic data).
|
|
24
|
+
*
|
|
25
|
+
* No console, no storage, no network (PREVENT-PI-004 / PREVENT-011). Every line
|
|
26
|
+
* is a structured JSON event with `ts` + `event`.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { VC7A_ENABLED } from "../../config/vector-cortex.js";
|
|
30
|
+
import type { CrystalEventName, CrystalFailureCode, CrystalMode } from "./types.js";
|
|
31
|
+
|
|
32
|
+
/** Optional emit fn injected by the runtime; tests pass `undefined`. */
|
|
33
|
+
export type CrystalEmit = (name: string, payload: unknown) => void;
|
|
34
|
+
|
|
35
|
+
/** Run `fn` only when an emit exists; a reporting failure is never fatal. */
|
|
36
|
+
function safe(emit: CrystalEmit | undefined, fn: (emit: CrystalEmit) => void): void {
|
|
37
|
+
if (emit === undefined) return;
|
|
38
|
+
try {
|
|
39
|
+
fn(emit);
|
|
40
|
+
} catch {
|
|
41
|
+
// Non-fatal: a reporting failure must never break the agent loop.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The event names VC7A emits, exported for the dashboard seam and tests. */
|
|
46
|
+
export const CRYSTAL_EVENT_NAMES: readonly CrystalEventName[] = [
|
|
47
|
+
"vector_cortex_crystal_written",
|
|
48
|
+
"vector_cortex_crystal_collision",
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Report a published crystal. Key digest + byte count + mode only — enough to
|
|
53
|
+
* see whether the cache is filling and being hit, without disclosing what was
|
|
54
|
+
* frozen.
|
|
55
|
+
*/
|
|
56
|
+
export function reportCrystalWritten(
|
|
57
|
+
emit: CrystalEmit | undefined,
|
|
58
|
+
payload: {
|
|
59
|
+
readonly keyDigest: string;
|
|
60
|
+
readonly byteCount: number;
|
|
61
|
+
readonly mode: CrystalMode;
|
|
62
|
+
},
|
|
63
|
+
): void {
|
|
64
|
+
if (!VC7A_ENABLED()) return;
|
|
65
|
+
safe(emit, (e) =>
|
|
66
|
+
e("vector_cortex_crystal_written", {
|
|
67
|
+
ts: undefined,
|
|
68
|
+
event: "vector_cortex_crystal_written",
|
|
69
|
+
keyDigest: payload.keyDigest,
|
|
70
|
+
byteCount: payload.byteCount,
|
|
71
|
+
mode: payload.mode,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Report a refused write. A collision means two renders of the SAME identity
|
|
78
|
+
* disagreed — a determinism bug — so the code is surfaced rather than swallowed.
|
|
79
|
+
* Suppressed under flag-off.
|
|
80
|
+
*/
|
|
81
|
+
export function reportCrystalCollision(
|
|
82
|
+
emit: CrystalEmit | undefined,
|
|
83
|
+
payload: {
|
|
84
|
+
readonly keyDigest: string;
|
|
85
|
+
readonly code: CrystalFailureCode;
|
|
86
|
+
readonly mode: CrystalMode;
|
|
87
|
+
},
|
|
88
|
+
): void {
|
|
89
|
+
if (!VC7A_ENABLED()) return;
|
|
90
|
+
safe(emit, (e) =>
|
|
91
|
+
e("vector_cortex_crystal_collision", {
|
|
92
|
+
ts: undefined,
|
|
93
|
+
event: "vector_cortex_crystal_collision",
|
|
94
|
+
keyDigest: payload.keyDigest,
|
|
95
|
+
code: payload.code,
|
|
96
|
+
mode: payload.mode,
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
}
|