pi-mega-compact 0.20.8 → 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.
Files changed (96) hide show
  1. package/dist/config/vector-cortex.js +13 -0
  2. package/dist/config.js +1 -1
  3. package/dist/extensions/dashboard-server/route-dispatch.js +3 -1
  4. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  5. package/dist/extensions/dashboard-server/routes-vector-cortex-heal.js +42 -3
  6. package/dist/extensions/dashboard-server/routes-vector-cortex.js +3 -1
  7. package/dist/extensions/dashboard-server/routes.js +1 -1
  8. package/dist/src/config/vector-cortex.js +13 -0
  9. package/dist/src/config.js +1 -1
  10. package/dist/src/vector-cortex/heal/_restore-fixture.js +96 -0
  11. package/dist/src/vector-cortex/heal/_vc6b-helpers.js +35 -0
  12. package/dist/src/vector-cortex/heal/restore-emit.js +70 -0
  13. package/dist/src/vector-cortex/heal/restore-readers.js +114 -0
  14. package/dist/src/vector-cortex/heal/restore-types.js +66 -0
  15. package/dist/src/vector-cortex/heal/restore.js +140 -0
  16. package/dist/src/vector-cortex/heal/types.js +1 -0
  17. package/dist/src/vector-cortex/heal/verify.js +75 -0
  18. package/dist/vector-cortex/heal/_restore-fixture.js +96 -0
  19. package/dist/vector-cortex/heal/_vc6b-helpers.js +35 -0
  20. package/dist/vector-cortex/heal/restore-emit.js +70 -0
  21. package/dist/vector-cortex/heal/restore-readers.js +114 -0
  22. package/dist/vector-cortex/heal/restore-types.js +66 -0
  23. package/dist/vector-cortex/heal/restore.js +140 -0
  24. package/dist/vector-cortex/heal/types.js +1 -0
  25. package/dist/vector-cortex/heal/verify.js +75 -0
  26. package/extensions/dashboard-client/dist/assets/{AreaChart-5yxaI8IO.js → AreaChart-fxyq2KaH.js} +2 -2
  27. package/extensions/dashboard-client/dist/assets/{AreaChart-5yxaI8IO.js.map → AreaChart-fxyq2KaH.js.map} +1 -1
  28. package/extensions/dashboard-client/dist/assets/{BarChart-BoHhCqH-.js → BarChart-D5EqAINP.js} +2 -2
  29. package/extensions/dashboard-client/dist/assets/{BarChart-BoHhCqH-.js.map → BarChart-D5EqAINP.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{CacheTab-Di6L3CsF.js → CacheTab-D4UWbzLo.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{CacheTab-Di6L3CsF.js.map → CacheTab-D4UWbzLo.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{EventsTab-Cdff3h6s.js → EventsTab-BiXquGrX.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{EventsTab-Cdff3h6s.js.map → EventsTab-BiXquGrX.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/{HealthTab-MthYYVLw.js → HealthTab-D5QvoTjy.js} +2 -2
  35. package/extensions/dashboard-client/dist/assets/{HealthTab-MthYYVLw.js.map → HealthTab-D5QvoTjy.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D9G4ntsG.js → MaintenanceTab-Cy_-CWgM.js} +2 -2
  37. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-D9G4ntsG.js.map → MaintenanceTab-Cy_-CWgM.js.map} +1 -1
  38. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CN8Lr5Qf.js → MemoryMapTab-DrAjgGOR.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CN8Lr5Qf.js.map → MemoryMapTab-DrAjgGOR.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/{MetricsTab-CgO32kSo.js → MetricsTab-BvPHcuFN.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{MetricsTab-CgO32kSo.js.map → MetricsTab-BvPHcuFN.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{OverviewTab-Cb4kR0h1.js → OverviewTab-DsbIigOQ.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{OverviewTab-Cb4kR0h1.js.map → OverviewTab-DsbIigOQ.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{ReposTab-Dt1FGnMR.js → ReposTab-7v_uZX_a.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{ReposTab-Dt1FGnMR.js.map → ReposTab-7v_uZX_a.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{SessionsTab-D7D2RW6U.js → SessionsTab-BXGrv-6C.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{SessionsTab-D7D2RW6U.js.map → SessionsTab-BXGrv-6C.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{SetupTab-CgV5xCzz.js → SetupTab-QRUXa65y.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{SetupTab-CgV5xCzz.js.map → SetupTab-QRUXa65y.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CCNuJjyO.js → TimeSavedCard-p327ZZKT.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CCNuJjyO.js.map → TimeSavedCard-p327ZZKT.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cv1OCttJ.js → TurnsTab-BcmBSlik.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{TurnsTab-Cv1OCttJ.js.map → TurnsTab-BcmBSlik.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/VectorCortexTab-D2fjKNyT.js.map +1 -0
  56. package/extensions/dashboard-client/dist/assets/{WikiTab-B48QnQrK.js → WikiTab-DLXFMuNy.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{WikiTab-B48QnQrK.js.map → WikiTab-DLXFMuNy.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/{button-BBQl2MiF.js → button-C9CghmRx.js} +2 -2
  59. package/extensions/dashboard-client/dist/assets/{button-BBQl2MiF.js.map → button-C9CghmRx.js.map} +1 -1
  60. package/extensions/dashboard-client/dist/assets/{card-BYbefUur.js → card-DWU-1y3t.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{card-BYbefUur.js.map → card-DWU-1y3t.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-swm0YZe0.js → generateCategoricalChart-Dayy9dGn.js} +2 -2
  63. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-swm0YZe0.js.map → generateCategoricalChart-Dayy9dGn.js.map} +1 -1
  64. package/extensions/dashboard-client/dist/assets/{index-DeIky7xm.js → index-D9PsW3VZ.js} +3 -3
  65. package/extensions/dashboard-client/dist/assets/{index-DeIky7xm.js.map → index-D9PsW3VZ.js.map} +1 -1
  66. package/extensions/dashboard-client/dist/assets/{switch-CctU78ww.js → switch-CnLFAmVT.js} +2 -2
  67. package/extensions/dashboard-client/dist/assets/{switch-CctU78ww.js.map → switch-CnLFAmVT.js.map} +1 -1
  68. package/extensions/dashboard-client/dist/assets/{toggle-BRCNCoiI.js → toggle-HN5C7_vy.js} +2 -2
  69. package/extensions/dashboard-client/dist/assets/{toggle-BRCNCoiI.js.map → toggle-HN5C7_vy.js.map} +1 -1
  70. package/extensions/dashboard-client/dist/assets/{useSSE-WfOiWDH_.js → useSSE-DoJfk3Ce.js} +2 -2
  71. package/extensions/dashboard-client/dist/assets/{useSSE-WfOiWDH_.js.map → useSSE-DoJfk3Ce.js.map} +1 -1
  72. package/extensions/dashboard-client/dist/index.html +1 -1
  73. package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
  74. package/extensions/dashboard-client/src/tabs/VectorCortexRestoreCard.tsx +42 -0
  75. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +8 -0
  76. package/extensions/dashboard-client/src/types/vector-cortex.ts +16 -0
  77. package/extensions/dashboard-server/api-contracts/vector-cortex-heal.ts +30 -0
  78. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +4 -1
  79. package/extensions/dashboard-server/route-dispatch.ts +2 -0
  80. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  81. package/extensions/dashboard-server/routes-vector-cortex-heal.ts +51 -4
  82. package/extensions/dashboard-server/routes-vector-cortex.ts +6 -1
  83. package/extensions/dashboard-server/routes.ts +1 -0
  84. package/package.json +1 -1
  85. package/src/config/vector-cortex.ts +14 -0
  86. package/src/config.ts +1 -0
  87. package/src/vector-cortex/heal/_restore-fixture.ts +163 -0
  88. package/src/vector-cortex/heal/_vc6b-helpers.ts +55 -0
  89. package/src/vector-cortex/heal/restore-emit.ts +94 -0
  90. package/src/vector-cortex/heal/restore-readers.ts +145 -0
  91. package/src/vector-cortex/heal/restore-types.ts +185 -0
  92. package/src/vector-cortex/heal/restore.ts +165 -0
  93. package/src/vector-cortex/heal/types.ts +22 -0
  94. package/src/vector-cortex/heal/verify.ts +93 -0
  95. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DTabQo6h.js +0 -2
  96. package/extensions/dashboard-client/dist/assets/VectorCortexTab-DTabQo6h.js.map +0 -1
@@ -0,0 +1,185 @@
1
+ /**
2
+ * vector-cortex/heal/restore-types.ts — VC6B exact source restoration contract.
3
+ *
4
+ * VC6A optimized WHICH EDGES the closure plan walks. VC6B answers the next
5
+ * question: when the plan needs a node whose bytes are no longer in the live
6
+ * window, WHERE do those bytes come from? The answer is deliberately narrow —
7
+ * an EXACT source, or nothing.
8
+ *
9
+ * THE CARDINAL RULE. Restored bytes are ONLY ever read from an exact source:
10
+ * 1. an `ExactShardV1` whose range and digest both match the request, or
11
+ * 2. a scan of the `EventV2` occurrence ledger over the requested seq range.
12
+ * Bytes are NEVER inferred from an embedding, a semantic shard, a RAPTOR
13
+ * summary, or any other derived/lossy representation. A semantic tier can tell
14
+ * you what a span was ABOUT; it cannot tell you what the span WAS. Attempting to
15
+ * "restore" from a derived source would silently fabricate transcript history,
16
+ * so VC6B has no code path that can do it: `RestoreReader` exposes exactly the
17
+ * two exact sources and nothing else.
18
+ *
19
+ * THE VERIFICATION RULE. Every restored span must hash to the SHA-256 digest the
20
+ * REQUEST pinned, checked immediately before insertion. A source that matches by
21
+ * range but not by hash is rejected (`HEAL_RESTORE_DIGEST_MISMATCH`) — never
22
+ * "close enough". This is what makes a swapped shard file, a truncated read, or a
23
+ * corrupted ledger record fail loudly instead of poisoning the reconstruction.
24
+ *
25
+ * DIGEST PINNING (three fields exist; do not confuse them).
26
+ * - `ReconstructionSpan.digest` and `ExactShardV1.digest` are SHA-256 in
27
+ * LOWERCASE HEX with NO prefix.
28
+ * - `EventV2.bytesDigest` is `sha256:<hex>`, WITH the prefix.
29
+ * `RestoreSpanRequest.digest` works at SPAN level and uses the FORMER: bare
30
+ * lowercase hex, matching `ExactShardV1.digest` / `ReconstructionSpan.digest`.
31
+ * The prefixed `EventV2.bytesDigest` is used ONLY for per-event verification
32
+ * inside the ledger-scan path and is never the request-level digest. Mixing the
33
+ * two would make every ledger restoration fail (or, worse, make a prefix-stripped
34
+ * comparison accidentally succeed against the wrong granularity).
35
+ *
36
+ * BOUNDS. A restore request is attacker-shaped input: it names spans and byte
37
+ * ranges. `RESTORE_LIMIT_SPANS` / `RESTORE_LIMIT_BYTES` bound it BEFORE any
38
+ * reader is consulted, so an oversized request cannot be used to make the
39
+ * restorer walk the whole ledger (HEAL-LIMIT-002).
40
+ *
41
+ * Pure types + registered conformance IDs: no storage, no console, no network
42
+ * (PREVENT-PI-004 / PREVENT-011).
43
+ */
44
+
45
+ import type { ExactShardV1, ShardRange } from "../shards/types.js";
46
+ import type { EventV2 } from "../ledger/types.js";
47
+
48
+ /**
49
+ * One requested span: the node it restores, the byte/seq window that IDENTIFIES
50
+ * it, and the digest that AUTHENTICATES it.
51
+ *
52
+ * `range` is identity — it says which slice of the canonical stream to look for.
53
+ * `digest` is authentication — it says which bytes are acceptable. Both are
54
+ * required: identity alone would accept a shard file that was swapped in place,
55
+ * and a digest alone would require scanning every source.
56
+ */
57
+ export interface RestoreSpanRequest {
58
+ /** The closure node these bytes belong to (echoed back in the result). */
59
+ readonly nodeId: string;
60
+ /** The span's identity: session + inclusive seq bounds + half-open bytes. */
61
+ readonly range: ShardRange;
62
+ /**
63
+ * SHA-256 of the span's original bytes, LOWERCASE HEX, NO `sha256:` prefix
64
+ * (matches `ExactShardV1.digest` / `ReconstructionSpan.digest`).
65
+ */
66
+ readonly digest: string;
67
+ }
68
+
69
+ /** A batch of span restorations for one session. */
70
+ export interface RestoreRequestV1 {
71
+ readonly schema: "restore-request-v1";
72
+ readonly sessionId: string;
73
+ /** Requested spans, restored in this order (the result preserves it). */
74
+ readonly spans: readonly RestoreSpanRequest[];
75
+ }
76
+
77
+ /**
78
+ * One successfully restored span. `source` records WHICH exact tier answered so
79
+ * the triad mode can be derived and so an operator can see whether the shard
80
+ * index is doing its job or every read is falling through to a ledger scan.
81
+ */
82
+ export interface RestoreSpanResult {
83
+ readonly nodeId: string;
84
+ /** Which exact source produced the bytes (never a derived/semantic tier). */
85
+ readonly source: "exact-shard" | "ledger-scan";
86
+ /** The EXACT original bytes, verbatim — invalid UTF-8 included, unnormalized. */
87
+ readonly bytes: Uint8Array;
88
+ /** The verified SHA-256 (bare lowercase hex) — equals the request's digest. */
89
+ readonly digest: string;
90
+ }
91
+
92
+ /**
93
+ * The restoration outcome for one request.
94
+ *
95
+ * `mode` mirrors TRIAD_RESILIENCE:
96
+ * A — every span came from an indexed exact shard (the fast, normal path);
97
+ * B — every span was restored, but at least one required a ledger range scan
98
+ * (an INDEPENDENT code path: no shard index involved, bytes rebuilt by
99
+ * concatenating verified occurrence records);
100
+ * C — at least one span could not be restored from ANY exact source. Mode C
101
+ * OMITS the span and DISCLOSES the loss (`semanticLossStated`) rather than
102
+ * substituting derived text.
103
+ */
104
+ export interface RestoreResultV1 {
105
+ readonly schema: "restore-result-v1";
106
+ readonly sessionId: string;
107
+ readonly mode: "A" | "B" | "C";
108
+ /** Restored spans in request order (only digest-verified spans appear here). */
109
+ readonly restored: readonly RestoreSpanResult[];
110
+ /** Node ids that could not be restored (identity only — never bytes). */
111
+ readonly missing: readonly string[];
112
+ /** Set in mode C: the caller MUST be told the old context is gone. */
113
+ readonly semanticLossStated: boolean;
114
+ /** Deduplicated failure codes in deterministic order. */
115
+ readonly codes: readonly RestoreFailureCode[];
116
+ }
117
+
118
+ /** VC6B failure codes (registered HEAL codes). */
119
+ export type RestoreFailureCode =
120
+ /** The request exceeds the span-count or aggregate-byte bound. */
121
+ | "HEAL_RESTORE_LIMIT"
122
+ /** A source's bytes do not hash to the pinned digest — nothing is inserted. */
123
+ | "HEAL_RESTORE_DIGEST_MISMATCH"
124
+ /** Neither an exact shard nor the ledger covers the requested span. */
125
+ | "HEAL_RESTORE_SOURCE_MISSING"
126
+ /** A restored span does not correspond to a requested span/digest. */
127
+ | "HEAL_RESTORE_RANGE_MISMATCH";
128
+
129
+ /**
130
+ * Post-restoration verdict. `verifyRestored` re-derives every digest from the
131
+ * bytes actually carried in the result, so a result object that was mutated
132
+ * after `restoreSources` returned still fails before insertion.
133
+ */
134
+ export type RestoreVerification =
135
+ | { readonly ok: true }
136
+ | { readonly ok: false; readonly codes: readonly RestoreFailureCode[] };
137
+
138
+ /**
139
+ * The ONLY sources a restoration may read. Deliberately exhaustive: there is no
140
+ * embedding, semantic shard, or summary field here, so "restore from a derived
141
+ * source" is not merely forbidden by policy — it is unrepresentable.
142
+ */
143
+ export interface RestoreReader {
144
+ /** Indexed exact shards (mode A). */
145
+ readonly exactShards: readonly ExactShardV1[];
146
+ /** Raw occurrence records for the ledger range scan (mode B). */
147
+ readonly ledgerEvents: readonly EventV2[];
148
+ }
149
+
150
+ /**
151
+ * Maximum spans in one request. A request naming more spans than this is
152
+ * rejected outright — the bound exists so a single call cannot be turned into an
153
+ * unbounded traversal of the ledger.
154
+ */
155
+ export const RESTORE_LIMIT_SPANS = 64;
156
+
157
+ /**
158
+ * Maximum aggregate requested bytes (4 MiB), summed from the REQUEST's ranges
159
+ * (`byteEnd - byteStart`) so the bound is enforceable without reading anything.
160
+ */
161
+ export const RESTORE_LIMIT_BYTES = 4 * 1024 * 1024;
162
+
163
+ /**
164
+ * Registered VC6B conformance ID range (HEAL-016..030), continuing VC6A's
165
+ * HEAL-001..015. The acceptance test reads these rows from the v2 manifest and
166
+ * asserts each returns its manifest `ok`/`code`.
167
+ */
168
+ export const RESTORE_IDS: readonly string[] = Array.from(
169
+ { length: 15 },
170
+ (_v, i) => `HEAL-${String(i + 16).padStart(3, "0")}`,
171
+ );
172
+
173
+ /** Named VC6B conformance assertions (the sprint's headline rows). */
174
+ export const RESTORE_NAMED_IDS = [
175
+ "HEAL-SPAN-001",
176
+ "HEAL-LIMIT-002",
177
+ "HEAL-DIGEST-003",
178
+ ] as const;
179
+
180
+ /** The two structured events the VC6B reporter emits. */
181
+ export type RestoreEventName =
182
+ | "vector_cortex_source_restored"
183
+ | "vector_cortex_restore_digest_rejected";
184
+
185
+ export type { EventV2, ExactShardV1, ShardRange };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * vector-cortex/heal/restore.ts — VC6B exact source restoration orchestrator.
3
+ *
4
+ * Turns a `RestoreRequestV1` into a `RestoreResultV1` by consulting, per span,
5
+ * the two EXACT sources in `restore-readers.ts` — an indexed exact shard first,
6
+ * then a ledger range scan — and inserting bytes only after they hash to the
7
+ * digest the request pinned.
8
+ *
9
+ * ORDER OF OPERATIONS IS THE CONTRACT.
10
+ *
11
+ * 1. BOUNDS, BEFORE ANY READER TOUCH. The span count and the aggregate
12
+ * requested byte span are computed PURELY FROM THE REQUEST (`byteEnd -
13
+ * byteStart`, which needs no source at all) and checked first. On breach we
14
+ * return immediately, having never read `reader.exactShards` or
15
+ * `reader.ledgerEvents`. This is HEAL-LIMIT-002: an oversized request must
16
+ * not be able to make the restorer walk the ledger even once, so the check
17
+ * cannot be "inside the loop, before the read" — it must be before the loop
18
+ * exists. The acceptance corpus proves it by passing EMPTY readers with an
19
+ * over-limit request: a reader-touching implementation would still return
20
+ * "missing" rather than "limit".
21
+ *
22
+ * 2. EXACT SHARD, then LEDGER, then MISSING. Sources are tried strongest-first.
23
+ * A digest mismatch anywhere is recorded and the span is NOT restored — the
24
+ * restorer never downgrades to "closest available bytes".
25
+ *
26
+ * 3. MODE from what actually happened, not from what was attempted. A = every
27
+ * span came from a shard; B = all restored, at least one via ledger scan;
28
+ * C = something is missing, and mode C STATES its semantic loss rather than
29
+ * hiding an incomplete restoration behind a successful-looking result.
30
+ *
31
+ * PURE. No storage, no console, no clock, no network — `node:crypto` (a Node
32
+ * built-in) is the only dependency beyond types (PREVENT-PI-004 / PREVENT-011).
33
+ * The reporter seam in `restore-emit.ts` is flag-gated; THIS arithmetic is not,
34
+ * so flag-off is byte-identical.
35
+ */
36
+
37
+ import type {
38
+ RestoreFailureCode,
39
+ RestoreReader,
40
+ RestoreRequestV1,
41
+ RestoreResultV1,
42
+ RestoreSpanRequest,
43
+ RestoreSpanResult,
44
+ } from "./restore-types.js";
45
+ import { RESTORE_LIMIT_BYTES, RESTORE_LIMIT_SPANS } from "./restore-types.js";
46
+ import { readExactShard, readLedgerSpan } from "./restore-readers.js";
47
+
48
+ /**
49
+ * Fixed code ordering so a result's `codes` array is deterministic regardless of
50
+ * which span failed first. Deterministic output is what lets the conformance
51
+ * corpus pin an exact expected value.
52
+ */
53
+ const CODE_ORDER: readonly RestoreFailureCode[] = [
54
+ "HEAL_RESTORE_LIMIT",
55
+ "HEAL_RESTORE_DIGEST_MISMATCH",
56
+ "HEAL_RESTORE_SOURCE_MISSING",
57
+ "HEAL_RESTORE_RANGE_MISMATCH",
58
+ ] as const;
59
+
60
+ /** Deduplicate + sort codes into the fixed priority order. */
61
+ export function orderCodes(
62
+ codes: readonly RestoreFailureCode[],
63
+ ): readonly RestoreFailureCode[] {
64
+ const seen = new Set(codes);
65
+ return CODE_ORDER.filter((c) => seen.has(c));
66
+ }
67
+
68
+ /**
69
+ * Total bytes the request ASKS for, derived from the ranges alone. Negative or
70
+ * inverted ranges contribute 0 rather than reducing the total — an inverted span
71
+ * must never be usable to smuggle a large request under the bound.
72
+ */
73
+ function requestedBytes(spans: readonly RestoreSpanRequest[]): number {
74
+ let total = 0;
75
+ for (const s of spans) {
76
+ const len = s.range.byteEnd - s.range.byteStart;
77
+ total += len > 0 ? len : 0;
78
+ }
79
+ return total;
80
+ }
81
+
82
+ /** The immediate over-limit result: nothing read, everything missing. */
83
+ function limitExceeded(request: RestoreRequestV1): RestoreResultV1 {
84
+ return {
85
+ schema: "restore-result-v1",
86
+ sessionId: request.sessionId,
87
+ mode: "C",
88
+ restored: [],
89
+ missing: request.spans.map((s) => s.nodeId),
90
+ semanticLossStated: true,
91
+ codes: ["HEAL_RESTORE_LIMIT"],
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Restore the exact original bytes for every requested span.
97
+ *
98
+ * Never throws: a request naming spans no source covers yields a mode-C result
99
+ * that discloses the loss, not an exception (PRACTICES: non-fatal — a failed
100
+ * restoration must degrade the prompt, never break the agent loop).
101
+ */
102
+ export function restoreSources(
103
+ request: RestoreRequestV1,
104
+ reader: RestoreReader,
105
+ ): RestoreResultV1 {
106
+ // (1) Bounds first — computed from the request only. `reader` is not touched
107
+ // on this path, which is exactly what HEAL-LIMIT-002 asserts.
108
+ if (
109
+ request.spans.length > RESTORE_LIMIT_SPANS ||
110
+ requestedBytes(request.spans) > RESTORE_LIMIT_BYTES
111
+ ) {
112
+ return limitExceeded(request);
113
+ }
114
+
115
+ const restored: RestoreSpanResult[] = [];
116
+ const missing: string[] = [];
117
+ const codes: RestoreFailureCode[] = [];
118
+ let usedLedger = false;
119
+
120
+ // (2) Per span, strongest source first, in request order.
121
+ for (const span of request.spans) {
122
+ const exact = readExactShard(reader.exactShards, span.range, span.digest);
123
+ if (exact.kind === "bytes") {
124
+ restored.push({
125
+ nodeId: span.nodeId,
126
+ source: "exact-shard",
127
+ bytes: exact.bytes,
128
+ digest: span.digest,
129
+ });
130
+ continue;
131
+ }
132
+ if (exact.kind === "digest-mismatch") codes.push("HEAL_RESTORE_DIGEST_MISMATCH");
133
+
134
+ const ledger = readLedgerSpan(reader.ledgerEvents, span.range, span.digest);
135
+ if (ledger.kind === "bytes") {
136
+ usedLedger = true;
137
+ restored.push({
138
+ nodeId: span.nodeId,
139
+ source: "ledger-scan",
140
+ bytes: ledger.bytes,
141
+ digest: span.digest,
142
+ });
143
+ continue;
144
+ }
145
+ if (ledger.kind === "digest-mismatch") codes.push("HEAL_RESTORE_DIGEST_MISMATCH");
146
+
147
+ // (3) Neither exact source answered. Identity only — never bytes.
148
+ missing.push(span.nodeId);
149
+ codes.push("HEAL_RESTORE_SOURCE_MISSING");
150
+ }
151
+
152
+ const complete = missing.length === 0 && restored.length === request.spans.length;
153
+ const mode: RestoreResultV1["mode"] = !complete ? "C" : usedLedger ? "B" : "A";
154
+
155
+ return {
156
+ schema: "restore-result-v1",
157
+ sessionId: request.sessionId,
158
+ mode,
159
+ restored,
160
+ missing,
161
+ // Mode C omits spans, so it MUST disclose that the old context is gone.
162
+ semanticLossStated: mode === "C",
163
+ codes: orderCodes(codes),
164
+ };
165
+ }
@@ -224,3 +224,25 @@ export const HEAL_NAMED_IDS = [
224
224
  ] as const;
225
225
 
226
226
  export type { ClosureEdge, ClosureEdgeKind };
227
+
228
+ // ---------------------------------------------------------------------------
229
+ // VC6B exact source restoration (restore-types.ts). Re-exported here so the
230
+ // heal contract has ONE entry point: `RestoreHintV1` above names the node VC6B
231
+ // must fetch, and these types describe the fetch itself.
232
+ // ---------------------------------------------------------------------------
233
+
234
+ export type {
235
+ RestoreRequestV1,
236
+ RestoreSpanRequest,
237
+ RestoreResultV1,
238
+ RestoreSpanResult,
239
+ RestoreFailureCode,
240
+ RestoreReader,
241
+ RestoreVerification,
242
+ } from "./restore-types.js";
243
+ export {
244
+ RESTORE_IDS,
245
+ RESTORE_NAMED_IDS,
246
+ RESTORE_LIMIT_SPANS,
247
+ RESTORE_LIMIT_BYTES,
248
+ } from "./restore-types.js";
@@ -0,0 +1,93 @@
1
+ /**
2
+ * vector-cortex/heal/verify.ts — VC6B pre-insertion verification (task 3).
3
+ *
4
+ * `restoreSources` already hashes every span before it puts it in the result, so
5
+ * why hash again here?
6
+ *
7
+ * Because the result is a VALUE that travels. Between restoration and insertion
8
+ * it is passed across module boundaries, possibly cached, possibly assembled from
9
+ * more than one restore call, possibly reordered. `verifyRestored` is the gate
10
+ * immediately before bytes enter the reconstruction, and it re-derives its facts
11
+ * from scratch: it hashes the bytes the result ACTUALLY CARRIES and cross-checks
12
+ * them against the digest THE REQUEST asked for. A result object mutated after
13
+ * `restoreSources` returned — the classic "verified then swapped" injection —
14
+ * fails here.
15
+ *
16
+ * TWO DISTINCT INVARIANTS, TWO DISTINCT CODES.
17
+ * - `HEAL_RESTORE_DIGEST_MISMATCH` — the bytes do not hash to their OWN stated
18
+ * digest. The span is internally inconsistent (tampering or truncation).
19
+ * - `HEAL_RESTORE_RANGE_MISMATCH` — the bytes are internally consistent but do
20
+ * not answer the question that was asked: the nodeId was never requested, or
21
+ * the digest disagrees with the digest the request pinned for that nodeId.
22
+ * This is the "right bytes, wrong span" failure — substituting one real,
23
+ * correctly-hashed span for another would otherwise pass a naive check.
24
+ *
25
+ * WHOLESALE, NOT PER-SPAN. `insertable` returns the restored spans only when the
26
+ * entire result verifies. Inserting the good half of a result whose other half
27
+ * failed verification would splice a partially-corrupt transcript into the
28
+ * reconstruction without the caller ever seeing a mode-C disclosure.
29
+ *
30
+ * Pure: `node:crypto` only, no storage/console/network (PREVENT-PI-004 /
31
+ * PREVENT-011).
32
+ */
33
+
34
+ import type {
35
+ RestoreFailureCode,
36
+ RestoreRequestV1,
37
+ RestoreResultV1,
38
+ RestoreSpanResult,
39
+ RestoreVerification,
40
+ } from "./restore-types.js";
41
+ import { sha256Hex } from "./restore-readers.js";
42
+ import { orderCodes } from "./restore.js";
43
+
44
+ /**
45
+ * Re-verify every restored span against the request that asked for it.
46
+ *
47
+ * Returns `{ok:true}` only when EVERY restored span hashes to its own digest AND
48
+ * that digest is the one the request pinned for that node.
49
+ */
50
+ export function verifyRestored(
51
+ result: RestoreResultV1,
52
+ request: RestoreRequestV1,
53
+ ): RestoreVerification {
54
+ const requestedDigests = new Map<string, string>();
55
+ for (const span of request.spans) requestedDigests.set(span.nodeId, span.digest);
56
+
57
+ const codes: RestoreFailureCode[] = [];
58
+
59
+ for (const span of result.restored) {
60
+ // (1) Internal consistency: do these bytes hash to the digest they claim?
61
+ if (sha256Hex(span.bytes) !== span.digest) {
62
+ codes.push("HEAL_RESTORE_DIGEST_MISMATCH");
63
+ // A span that fails its own digest cannot meaningfully be checked against
64
+ // the request's digest as well; one code per real defect keeps the verdict
65
+ // readable.
66
+ continue;
67
+ }
68
+
69
+ // (2) Provenance: was this node requested, and with THIS digest? An
70
+ // unrequested node or a digest swap is a range mismatch, not a hash failure.
71
+ const wanted = requestedDigests.get(span.nodeId);
72
+ if (wanted === undefined || wanted !== span.digest) {
73
+ codes.push("HEAL_RESTORE_RANGE_MISMATCH");
74
+ }
75
+ }
76
+
77
+ if (codes.length === 0) return { ok: true };
78
+ return { ok: false, codes: orderCodes(codes) };
79
+ }
80
+
81
+ /**
82
+ * The spans that may be inserted: all of them, or none.
83
+ *
84
+ * A caller should never need to decide which half of a partially-verified result
85
+ * is safe — that decision is the whole point of the digest contract, so it is
86
+ * made here, once, in the strict direction.
87
+ */
88
+ export function insertable(
89
+ result: RestoreResultV1,
90
+ request: RestoreRequestV1,
91
+ ): readonly RestoreSpanResult[] {
92
+ return verifyRestored(result, request).ok ? result.restored : [];
93
+ }
@@ -1,2 +0,0 @@
1
- import{j as e,B as a,r as c}from"./index-DeIky7xm.js";import{C as o,a as i,b as d,c as x}from"./card-BYbefUur.js";async function q(){const s=await fetch("/api/vector-cortex/evaluation");if(!s.ok)throw new Error(`vector-cortex evaluation: ${s.status}`);return s.json()}async function y(){const s=await fetch("/api/vector-cortex/health");if(!s.ok)throw new Error(`vector-cortex health: ${s.status}`);return s.json()}async function Q(){const s=await fetch("/api/vector-cortex/topology");if(!s.ok)throw new Error(`vector-cortex topology: ${s.status}`);return s.json()}async function W(){const s=await fetch("/api/vector-cortex/query");if(!s.ok)throw new Error(`vector-cortex query: ${s.status}`);return s.json()}async function U(){const s=await fetch("/api/vector-cortex/shards");if(!s.ok)throw new Error(`vector-cortex shards: ${s.status}`);return s.json()}async function Y(){const s=await fetch("/api/vector-cortex/reconstruct");if(!s.ok)throw new Error(`vector-cortex reconstruct: ${s.status}`);return s.json()}async function Z(){const s=await fetch("/api/vector-cortex/plans");if(!s.ok)throw new Error(`vector-cortex plans: ${s.status}`);return s.json()}async function J(){const s=await fetch("/api/vector-cortex/render");if(!s.ok)throw new Error(`vector-cortex render: ${s.status}`);return s.json()}async function K(){const s=await fetch("/api/vector-cortex/rollout");if(!s.ok)throw new Error(`vector-cortex rollout: ${s.status}`);return s.json()}async function _(){const s=await fetch("/api/vector-cortex/closure-proof");if(!s.ok)throw new Error(`vector-cortex closure-proof: ${s.status}`);return s.json()}async function X(s="default"){const r=await fetch(`/api/vector-cortex/ledger?session=${encodeURIComponent(s)}`);if(!r.ok)throw new Error(`vector-cortex ledger: ${r.status}`);return r.json()}async function ee(s){const r=await fetch("/api/vector-cortex/breakers/reset",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({subsystem:s})});if(!r.ok)throw new Error(`vector-cortex reset: ${r.status}`);return r.json()}function t({label:s,value:r}){return e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground",children:s}),e.jsx("div",{className:"font-mono text-sm",children:r})]})}function se({view:s}){return e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Validated Prompt Renderer (VC5B)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Render fixtures",value:String(s.renderCount)}),e.jsx(t,{label:"Provider profiles",value:String(s.providerCount)}),e.jsx(t,{label:"Known profiles",value:String(s.knownProfiles.length)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only render conformance route — no prompt text or session payloads exposed."})]}):e.jsx("div",{className:"vc-empty",children:"Renderer disabled (VC5B off)."})})]})}function te({view:s}){return e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Live Graduated Rollout (VC5C)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Gate",value:`${s.gatePct}%`}),e.jsx(t,{label:"Exposed buckets",value:String(s.bucketCount)}),e.jsx(t,{label:"Total buckets",value:String(s.buckets)}),e.jsx(t,{label:"Promotion",value:s.promotionBlocked?"FROZEN":"OPEN"}),e.jsx(t,{label:"Events",value:String(s.events)}),e.jsx(t,{label:"Sessions",value:String(s.sessions)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only rollout route — no session payloads or bucket→session mappings exposed."})]}):e.jsx("div",{className:"vc-empty",children:"Rollout disabled (VC5C off)."})})]})}function re({view:s}){return e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Closure Optimization (VC6A)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Mode",value:s.mode}),e.jsx(t,{label:"Optimizations",value:String(s.optimizations)}),e.jsx(t,{label:"Proof rejections",value:String(s.proofRejections)}),e.jsx(t,{label:"Last rejection",value:s.lastRejection??"none"}),e.jsx(t,{label:"Retained edges",value:String(s.retainedEdgeTotal)}),e.jsx(t,{label:"Removed edges",value:String(s.removedEdgeTotal)}),e.jsx(t,{label:"Conservative walks",value:String(s.conservativeTraversalTotal)}),e.jsx(t,{label:"Optimized walks",value:String(s.optimizedTraversalTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only closure diagnostics — aggregate counts only; per-edge proof rows, node ids, and source payloads are never exposed (SECURITY_PRIVACY)."})]}):e.jsx("div",{className:"vc-empty",children:"Closure optimization disabled (VC6A off) — conservative VC4C closure used directly (mode B)."})})]})}function ae({view:s}){return e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Plan Manifests (VC5A)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"DAG fixtures",value:String(s.dagCount)}),e.jsx(t,{label:"Plan fixtures",value:String(s.plannerCount)}),e.jsx(t,{label:"Plans exposed",value:String(s.plans.length)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only plan manifests only — no session payloads or prompt text. Per-run plan outputs are staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"PromptDagV1 + budgeted planner disabled (VC5A off)."})})]})}function ne({topology:s,query:r}){return e.jsxs(e.Fragment,{children:[e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Derived Cortex Store (VC3A)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-3",children:[e.jsx(t,{label:"Generation",value:s.generationId??"—"}),e.jsx(t,{label:"Ordinal",value:s.ordinal??"—"}),e.jsx(t,{label:"Records",value:String(s.recordCount)}),e.jsx(t,{label:"Frontier (HW)",value:s.sourceHighWater}),e.jsx(t,{label:"Root digest",value:s.rootDigest?s.rootDigest.slice(0,16):"—"}),s.nodes!==void 0&&e.jsxs(e.Fragment,{children:[e.jsx(t,{label:"Nodes",value:String(s.nodes?.length??0)}),e.jsx(t,{label:"Edges",value:String(s.edges?.length??0)}),e.jsx(t,{label:"Graph digest",value:s.generationDigest?s.generationDigest.slice(0,16):"—"})]})]}),s.nodes!==void 0&&(s.edges?.length??0)>0&&e.jsxs("div",{className:"mt-4 max-h-56 overflow-y-auto",children:[e.jsx("div",{className:"mb-1 text-xs font-medium text-muted-foreground",children:"Topology edges (VC3B)"}),e.jsxs("table",{className:"w-full text-left text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border/50 text-muted-foreground",children:[e.jsx("th",{className:"py-1 pr-2",children:"source"}),e.jsx("th",{className:"py-1 pr-2",children:"target"}),e.jsx("th",{className:"py-1 pr-2",children:"head"}),e.jsx("th",{className:"py-1 pr-2",children:"dir"}),e.jsx("th",{className:"py-1",children:"score"})]})}),e.jsx("tbody",{children:s.edges?.map((u,m)=>e.jsxs("tr",{className:"border-b border-border/30",children:[e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.source}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.target}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:u.head}),e.jsx("td",{className:"py-1 pr-2",children:u.direction}),e.jsx("td",{className:"py-1 font-mono",children:u.score})]},`${u.source}-${u.target}-${u.head}-${m}`))})]})]})]}):e.jsx("div",{className:"vc-empty",children:"Cortex store disabled (VC3A off)."})})]}),e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Topology Query Diagnostics (VC3C)"}),r?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:r?.enabled?e.jsx("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-3",children:e.jsx(t,{label:"Router version",value:String(r.routerVersion)})}):e.jsx("div",{className:"vc-empty",children:"Query diagnostics unavailable (VC3C off)."})})]})]})}function le({ledger:s}){return e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Occurrence Ledger (VC1B)"}),s?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:s?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 grid grid-cols-2 gap-4 md:grid-cols-3",children:[e.jsx(t,{label:"Session",value:s.session}),e.jsx(t,{label:"High-water",value:s.highWater}),e.jsx(t,{label:"Occurrences",value:String(s.count)})]}),s.occurrences.length===0?e.jsx("div",{className:"vc-empty",children:"No occurrences recorded."}):e.jsx("div",{className:"max-h-64 overflow-y-auto",children:e.jsxs("table",{className:"w-full text-left text-xs",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border/50 text-muted-foreground",children:[e.jsx("th",{className:"py-1 pr-2",children:"seq"}),e.jsx("th",{className:"py-1 pr-2",children:"eventId"}),e.jsx("th",{className:"py-1 pr-2",children:"kind"}),e.jsx("th",{className:"py-1 pr-2",children:"toolCall"}),e.jsx("th",{className:"py-1",children:"digest"})]})}),e.jsx("tbody",{children:s.occurrences.map(r=>e.jsxs("tr",{className:"border-b border-border/30",children:[e.jsx("td",{className:"py-1 pr-2 font-mono",children:r.seq}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:r.eventId}),e.jsx("td",{className:"py-1 pr-2",children:r.kind}),e.jsx("td",{className:"py-1 pr-2 font-mono",children:r.toolCallId??"—"}),e.jsx("td",{className:"max-w-[180px] truncate font-mono text-muted-foreground",children:r.digest})]},`${r.seq}-${r.eventId}`))})]})})]}):e.jsx("div",{className:"vc-empty",children:"Ledger disabled (VC1B off)."})})]})}function v({mode:s,count:r}){return e.jsxs("div",{className:"flex items-center justify-between border-b border-border/50 py-1 text-sm",children:[e.jsxs("span",{className:"text-muted-foreground",children:["Mode ",s]}),e.jsx("span",{className:"font-mono",children:r})]})}function ie(){const[s,r]=c.useState(null),[u,m]=c.useState(null),[S,V]=c.useState(!0),[l,f]=c.useState(null),[p,b]=c.useState(null),[E,w]=c.useState(null),[R,F]=c.useState(null),[k,T]=c.useState(null),[j,$]=c.useState(null),[h,A]=c.useState(null),[P,O]=c.useState(null),[I,M]=c.useState(null),[B,D]=c.useState(null),[L,H]=c.useState(null),g=c.useCallback(()=>{q().then(r).catch(n=>m(n instanceof Error?n.message:String(n))).finally(()=>V(!1)),y().then(f).catch(()=>{}),X().then(w).catch(()=>{}),Q().then(F).catch(()=>{}),W().then(T).catch(()=>{}),U().then($).catch(()=>{}),Y().then(A).catch(()=>{}),Z().then(O).catch(()=>{}),J().then(M).catch(()=>{}),K().then(D).catch(()=>{}),_().then(H).catch(()=>{})},[]);c.useEffect(()=>{g();const n=setInterval(g,5e3);return()=>clearInterval(n)},[g]);const z=()=>{ee("provider").then(n=>(b(`Breaker "${n.subsystem}" ${n.state}; evidence retained: ${n.failures} failures / ${n.attempts} attempts`),y())).then(f).catch(n=>b(n instanceof Error?n.message:String(n)))};if(S&&!s)return e.jsx("div",{className:"vc-loading",children:"Loading vector-cortex evaluation…"});if(u&&!s)return e.jsxs("div",{className:"vc-error",children:["Error: ",u]});if(!s)return e.jsx("div",{className:"vc-empty",children:"No evaluation data available."});const C=Math.max(1,...s.histogram.cells,s.histogram.overflow);return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-lg font-semibold",children:"Vector Cortex Evaluation"}),s.enabled?e.jsx(a,{variant:"warning",children:"OBSERVER PENDING (A)"}):e.jsx(a,{variant:"danger",children:"MODE C (OFF)"})]}),e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-3",children:[e.jsxs(o,{children:[e.jsx(i,{children:e.jsx(d,{children:"Samples"})}),e.jsxs(x,{children:[e.jsx("div",{className:"text-3xl font-bold",children:s.samples}),e.jsxs("div",{className:"mt-2 flex flex-col",children:[e.jsx(v,{mode:"A",count:s.byMode.A}),e.jsx(v,{mode:"B",count:s.byMode.B}),e.jsx(v,{mode:"C",count:s.byMode.C})]})]})]}),e.jsxs(o,{className:"md:col-span-2",children:[e.jsx(i,{children:e.jsx(d,{children:"Latency Histogram (ms, inclusive edges)"})}),e.jsxs(x,{children:[e.jsxs("div",{className:"flex items-end gap-2",style:{height:160},children:[s.histogram.edges.map((n,N)=>{const G=s.histogram.cells[N]/C*140;return e.jsxs("div",{className:"flex flex-1 flex-col items-center gap-1",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:s.histogram.cells[N]}),e.jsx("div",{className:"w-full rounded-t",style:{height:Math.max(2,G),background:"#6366f1"}}),e.jsxs("span",{className:"text-[10px] text-muted-foreground",children:["≤",n]})]},n)}),e.jsxs("div",{className:"flex flex-1 flex-col items-center gap-1",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:s.histogram.overflow}),e.jsx("div",{className:"w-full rounded-t",style:{height:Math.max(2,s.histogram.overflow/C*140),background:"#f87171"}}),e.jsx("span",{className:"text-[10px] text-muted-foreground",children:">250"})]})]}),e.jsxs("div",{className:"mt-1 text-xs text-muted-foreground",children:["Total: ",s.histogram.total," · Rejects: ",s.rejects.length]})]})]})]}),e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Live Safety Envelope (VC0C)"}),e.jsxs("div",{className:"flex items-center gap-2",children:[l?.stateSource==="ephemeral"&&e.jsx(a,{variant:"outline",children:"EPHEMERAL (non-live)"}),e.jsx("button",{onClick:z,disabled:!l?.enabled,className:"rounded border border-border px-2 py-1 text-xs disabled:opacity-50",children:"Reset Cooldown"})]})]})}),e.jsxs(x,{children:[p&&e.jsx("div",{className:"mb-2 text-xs text-muted-foreground",children:p}),l?e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"State",value:l.state}),e.jsx(t,{label:"Mode",value:l.mode}),e.jsx(t,{label:"Window",value:`${l.windowMs}ms`}),e.jsx(t,{label:"Probes",value:String(l.probeCount)}),e.jsx(t,{label:"Backoff",value:`${l.backoffDelayMs}ms`}),e.jsx(t,{label:"Failures",value:`${l.failures}/${l.attempts}`}),e.jsx(t,{label:"Frontier",value:l.stateSource==="ephemeral"?"non-live":l.frontierFrozen?"FROZEN":"LIVE"}),e.jsx(t,{label:"Spool lag",value:String(l.spoolLag)}),e.jsx(t,{label:"Encoder mode",value:l.encoderMode}),e.jsx(t,{label:"Encoder asset",value:l.encoderAssetDigest?l.encoderAssetDigest.slice(0,12):"none"})]}):e.jsx("div",{className:"vc-empty",children:"Health unavailable (VC0C off)."})]})]}),e.jsx(ne,{topology:R,query:k}),e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Dual-Tier Shards (VC4A)"}),j?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:j?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Semantic shards",value:String(j.semanticCount)}),e.jsx(t,{label:"Exact shards",value:String(j.exactCount)}),e.jsx(t,{label:"Byte total",value:String(j.byteTotal)}),e.jsx(t,{label:"Protected bytes",value:String(j.protectedByteTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only count/byte aggregate; staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"Dual-tier shards disabled (VC4A off)."})})]}),e.jsxs(o,{children:[e.jsx(i,{children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(d,{children:"Reconstruction Fidelity (VC4C)"}),h?.enabled?e.jsx(a,{variant:"success",children:"ACTIVE"}):e.jsx(a,{variant:"danger",children:"OFF"})]})}),e.jsx(x,{children:h?.enabled?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(t,{label:"Closure attempts",value:String(h.closureAttempts)}),e.jsx(t,{label:"Closure rejections",value:String(h.closureRejections)}),e.jsx(t,{label:"Validated",value:String(h.validatedCount)}),e.jsx(t,{label:"Invalidated",value:String(h.invalidatedCount)}),e.jsx(t,{label:"Span total",value:String(h.spanTotal)}),e.jsx(t,{label:"Byte total",value:String(h.byteTotal)})]}),e.jsx("div",{className:"mt-3 text-xs text-muted-foreground",children:"Reader-only closure/validation aggregate; staged in-memory this sprint."})]}):e.jsx("div",{className:"vc-empty",children:"Reconstruction fidelity disabled (VC4C off)."})})]}),e.jsx(ae,{view:P}),e.jsx(se,{view:I}),e.jsx(te,{view:B}),e.jsx(re,{view:L}),e.jsx(le,{ledger:E})]})}export{ie as default};
2
- //# sourceMappingURL=VectorCortexTab-DTabQo6h.js.map