pi-mega-compact 0.16.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/config/vector-cortex.js +81 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry-ext.js +30 -0
  3. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +4 -14
  4. package/dist/extensions/dashboard-server/api-contracts/vector-cortex.js +12 -0
  5. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +10 -0
  6. package/dist/extensions/dashboard-server/routes-vector-cortex.js +292 -0
  7. package/dist/extensions/dashboard-server/routes.js +1 -0
  8. package/dist/extensions/dashboard-server/server.js +9 -1
  9. package/dist/extensions/mega-events/context-handler.js +11 -0
  10. package/dist/extensions/mega-runtime/vector-cortex-ledger.js +131 -0
  11. package/dist/extensions/mega-runtime/vector-cortex-safety.js +100 -0
  12. package/dist/src/config/vector-cortex.js +81 -0
  13. package/dist/src/config.js +6 -0
  14. package/dist/src/httpEmbedder.js +136 -1
  15. package/dist/src/vector-cortex/eval/annotations.js +41 -0
  16. package/dist/src/vector-cortex/eval/metrics.js +86 -0
  17. package/dist/src/vector-cortex/eval/observer.js +61 -0
  18. package/dist/src/vector-cortex/eval/persist.js +67 -0
  19. package/dist/src/vector-cortex/eval/reader.js +29 -0
  20. package/dist/src/vector-cortex/eval/types.js +33 -0
  21. package/dist/src/vector-cortex/ledger/adapter.js +55 -0
  22. package/dist/src/vector-cortex/ledger/compat-journal.js +206 -0
  23. package/dist/src/vector-cortex/ledger/emit.js +53 -0
  24. package/dist/src/vector-cortex/ledger/event-codec.js +72 -0
  25. package/dist/src/vector-cortex/ledger/event-codecB.js +76 -0
  26. package/dist/src/vector-cortex/ledger/sqlite.js +200 -0
  27. package/dist/src/vector-cortex/ledger/store.js +184 -0
  28. package/dist/src/vector-cortex/ledger/types.js +52 -0
  29. package/dist/src/vector-cortex/ledger/validator.js +117 -0
  30. package/dist/src/vector-cortex/migrations/effective-cut-v2.js +103 -0
  31. package/dist/src/vector-cortex/migrations/occurrence-v2.js +139 -0
  32. package/dist/src/vector-cortex/replay/cut.js +123 -0
  33. package/dist/src/vector-cortex/replay/emit.js +58 -0
  34. package/dist/src/vector-cortex/replay/replay.js +260 -0
  35. package/dist/src/vector-cortex/replay/replayB.js +73 -0
  36. package/dist/src/vector-cortex/replay/types.js +52 -0
  37. package/dist/src/vector-cortex/resilience/breaker-core.js +359 -0
  38. package/dist/src/vector-cortex/resilience/breaker.js +12 -0
  39. package/dist/src/vector-cortex/resilience/emit.js +56 -0
  40. package/dist/src/vector-cortex/resilience/spool-core.js +347 -0
  41. package/dist/src/vector-cortex/resilience/spool.js +12 -0
  42. package/dist/src/vector-cortex/resilience/types.js +32 -0
  43. package/dist/vector-cortex/eval/annotations.js +41 -0
  44. package/dist/vector-cortex/eval/metrics.js +86 -0
  45. package/dist/vector-cortex/eval/observer.js +61 -0
  46. package/dist/vector-cortex/eval/persist.js +67 -0
  47. package/dist/vector-cortex/eval/reader.js +29 -0
  48. package/dist/vector-cortex/eval/types.js +33 -0
  49. package/dist/vector-cortex/ledger/adapter.js +55 -0
  50. package/dist/vector-cortex/ledger/compat-journal.js +206 -0
  51. package/dist/vector-cortex/ledger/emit.js +53 -0
  52. package/dist/vector-cortex/ledger/event-codec.js +72 -0
  53. package/dist/vector-cortex/ledger/event-codecB.js +76 -0
  54. package/dist/vector-cortex/ledger/sqlite.js +200 -0
  55. package/dist/vector-cortex/ledger/store.js +184 -0
  56. package/dist/vector-cortex/ledger/types.js +52 -0
  57. package/dist/vector-cortex/ledger/validator.js +117 -0
  58. package/dist/vector-cortex/migrations/effective-cut-v2.js +103 -0
  59. package/dist/vector-cortex/migrations/occurrence-v2.js +139 -0
  60. package/dist/vector-cortex/replay/cut.js +123 -0
  61. package/dist/vector-cortex/replay/emit.js +58 -0
  62. package/dist/vector-cortex/replay/replay.js +260 -0
  63. package/dist/vector-cortex/replay/replayB.js +73 -0
  64. package/dist/vector-cortex/replay/types.js +52 -0
  65. package/dist/vector-cortex/resilience/breaker-core.js +359 -0
  66. package/dist/vector-cortex/resilience/breaker.js +12 -0
  67. package/dist/vector-cortex/resilience/emit.js +56 -0
  68. package/dist/vector-cortex/resilience/spool-core.js +347 -0
  69. package/dist/vector-cortex/resilience/spool.js +12 -0
  70. package/dist/vector-cortex/resilience/types.js +32 -0
  71. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js → AreaChart-CAnWomhw.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js.map → AreaChart-CAnWomhw.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js → BarChart-lZ2-s71k.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js.map → BarChart-lZ2-s71k.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js → CacheTab-BL2Thh66.js} +2 -2
  76. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js.map → CacheTab-BL2Thh66.js.map} +1 -1
  77. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js → EventsTab-DEh2Z9fp.js} +2 -2
  78. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js.map → EventsTab-DEh2Z9fp.js.map} +1 -1
  79. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js → HealthTab-DTwKclxk.js} +2 -2
  80. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js.map → HealthTab-DTwKclxk.js.map} +1 -1
  81. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js → MaintenanceTab-DDfBEdbX.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js.map → MaintenanceTab-DDfBEdbX.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js → MemoryMapTab-Dgfmg-0Q.js} +2 -2
  84. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js.map → MemoryMapTab-Dgfmg-0Q.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js → MetricsTab-DOcMm0Tx.js} +2 -2
  86. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js.map → MetricsTab-DOcMm0Tx.js.map} +1 -1
  87. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js → OverviewTab-DHT5aoHg.js} +2 -2
  88. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js.map → OverviewTab-DHT5aoHg.js.map} +1 -1
  89. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js → ReposTab-CGAmtR_c.js} +2 -2
  90. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js.map → ReposTab-CGAmtR_c.js.map} +1 -1
  91. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js → SessionsTab-BIgx6iKT.js} +2 -2
  92. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js.map → SessionsTab-BIgx6iKT.js.map} +1 -1
  93. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js → SetupTab-ADghKuEI.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js.map → SetupTab-ADghKuEI.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js → TimeSavedCard-C33UiiOM.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js.map → TimeSavedCard-C33UiiOM.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js → TurnsTab-DdbL9wPN.js} +2 -2
  98. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js.map → TurnsTab-DdbL9wPN.js.map} +1 -1
  99. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js +2 -0
  100. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js.map +1 -0
  101. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js → WikiTab-Ch00OFKs.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js.map → WikiTab-Ch00OFKs.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js → button-ByeEdo2g.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js.map → button-ByeEdo2g.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js → card-DesoLXvc.js} +2 -2
  106. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js.map → card-DesoLXvc.js.map} +1 -1
  107. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js → generateCategoricalChart-CCAB7Iz4.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js.map → generateCategoricalChart-CCAB7Iz4.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/assets/{index-B93cewiX.js → index-B6cgyAPr.js} +34 -29
  110. package/extensions/dashboard-client/dist/assets/index-B6cgyAPr.js.map +1 -0
  111. package/extensions/dashboard-client/dist/assets/index-CfvziyBi.css +1 -0
  112. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js → switch-DIZC-oe-.js} +2 -2
  113. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js.map → switch-DIZC-oe-.js.map} +1 -1
  114. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js → toggle-CIoWaH2e.js} +2 -2
  115. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js.map → toggle-CIoWaH2e.js.map} +1 -1
  116. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js → useSSE-Bv4943l1.js} +2 -2
  117. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js.map → useSSE-Bv4943l1.js.map} +1 -1
  118. package/extensions/dashboard-client/dist/index.html +2 -2
  119. package/extensions/dashboard-client/src/App.tsx +2 -0
  120. package/extensions/dashboard-client/src/api/vector-cortex.ts +54 -0
  121. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +272 -0
  122. package/extensions/dashboard-client/src/tabs/registry.ts +4 -1
  123. package/extensions/dashboard-client/src/types/vector-cortex.ts +84 -0
  124. package/extensions/dashboard-server/api-contracts/endpoints/registry-ext.ts +67 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +4 -21
  126. package/extensions/dashboard-server/api-contracts/index.ts +3 -0
  127. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +136 -0
  128. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +35 -0
  129. package/extensions/dashboard-server/routes-vector-cortex.ts +332 -0
  130. package/extensions/dashboard-server/routes.ts +6 -0
  131. package/extensions/dashboard-server/server.ts +8 -0
  132. package/extensions/mega-events/context-handler.ts +16 -0
  133. package/extensions/mega-runtime/vector-cortex-ledger.ts +165 -0
  134. package/extensions/mega-runtime/vector-cortex-safety.ts +181 -0
  135. package/package.json +2 -1
  136. package/src/config/vector-cortex.ts +87 -0
  137. package/src/config.ts +27 -0
  138. package/src/httpEmbedder.ts +134 -1
  139. package/src/vector-cortex/eval/annotations.ts +70 -0
  140. package/src/vector-cortex/eval/metrics.ts +113 -0
  141. package/src/vector-cortex/eval/observer.ts +86 -0
  142. package/src/vector-cortex/eval/persist.ts +73 -0
  143. package/src/vector-cortex/eval/reader.ts +45 -0
  144. package/src/vector-cortex/eval/types.ts +89 -0
  145. package/src/vector-cortex/ledger/adapter.ts +71 -0
  146. package/src/vector-cortex/ledger/compat-journal.ts +234 -0
  147. package/src/vector-cortex/ledger/emit.ts +86 -0
  148. package/src/vector-cortex/ledger/event-codec.ts +79 -0
  149. package/src/vector-cortex/ledger/event-codecB.ts +87 -0
  150. package/src/vector-cortex/ledger/sqlite.ts +265 -0
  151. package/src/vector-cortex/ledger/store.ts +380 -0
  152. package/src/vector-cortex/ledger/types.ts +141 -0
  153. package/src/vector-cortex/ledger/validator.ts +124 -0
  154. package/src/vector-cortex/migrations/effective-cut-v2.ts +149 -0
  155. package/src/vector-cortex/migrations/occurrence-v2.ts +192 -0
  156. package/src/vector-cortex/replay/cut.ts +169 -0
  157. package/src/vector-cortex/replay/emit.ts +89 -0
  158. package/src/vector-cortex/replay/replay.ts +308 -0
  159. package/src/vector-cortex/replay/replayB.ts +97 -0
  160. package/src/vector-cortex/replay/types.ts +145 -0
  161. package/src/vector-cortex/resilience/breaker-core.ts +444 -0
  162. package/src/vector-cortex/resilience/breaker.ts +22 -0
  163. package/src/vector-cortex/resilience/emit.ts +99 -0
  164. package/src/vector-cortex/resilience/spool-core.ts +411 -0
  165. package/src/vector-cortex/resilience/spool.ts +22 -0
  166. package/src/vector-cortex/resilience/types.ts +158 -0
  167. package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +0 -1
  168. package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +0 -1
@@ -272,6 +272,41 @@ export const SETTINGS: ReadonlyArray<{
272
272
  num("MEGACOMPACT_EMBED_CACHE", "Embed Cache Size", "Embedding cache entries (0 = disabled)", 256, 0, 10000),
273
273
  ],
274
274
  },
275
+ {
276
+ name: "Vector Cortex",
277
+ settings: [
278
+ boolDirect(
279
+ "MEGACOMPACT_VC0A",
280
+ "VC0A Baseline Observability",
281
+ "Structured evaluation observer (MetricEventV1 + latency histogram). OFF = mode C, byte-identical to predecessor.",
282
+ true,
283
+ ),
284
+ boolDirect(
285
+ "MEGACOMPACT_VC0B",
286
+ "VC0B Replay Correctness",
287
+ "ReplayCutV2 effective-cut (min of boundary-safe/commit/capture high-water + pair retreat + anchor floor) and M3 effective-cut-v2 migration. OFF = legacy capped replay, byte-identical.",
288
+ true,
289
+ ),
290
+ boolDirect(
291
+ "MEGACOMPACT_VC1A",
292
+ "VC1A Canonical Byte Events",
293
+ "EventV2 byte-authority ledger codec (original bytes + SHA-256, strict UTF-8, derived NFC) and canonical validator (EVT_DIGEST_MISMATCH / EVT_UTF8_TAG_INVALID / EVT_DUPLICATE_ID). OFF = mode C, transcript codec unchanged, byte-identical.",
294
+ true,
295
+ ),
296
+ boolDirect(
297
+ "MEGACOMPACT_VC1B",
298
+ "VC1B Occurrence Ledger + Tool Identity",
299
+ "Neutral occurrence ledger (LedgerReader/Writer/Admin + CompatJournalV1): per-session monotonic seq, tool result references one earlier call, uniqueness by (eventId,digest) only, and the M2 copy-validate-switch downgrade journal. OFF = mode C, ledger unwritten, byte-identical.",
300
+ true,
301
+ ),
302
+ boolDirect(
303
+ "MEGACOMPACT_VC0C",
304
+ "VC0C Live Safety Envelope",
305
+ "TriadResult/Breaker live circuit breaker (60s window, 20 attempts, 30s cooldown, 3 probes, 5min healthy residence) + durable spool before provider invocation; manual reset clears cooldown but never evidence. OFF = mode C, unchanged transcript, byte-identical.",
306
+ true,
307
+ ),
308
+ ],
309
+ },
275
310
  {
276
311
  name: "Cost API",
277
312
  settings: [
@@ -0,0 +1,332 @@
1
+ /**
2
+ * dashboard-server/routes-vector-cortex.ts — vector-cortex dashboard routes.
3
+ *
4
+ * VC0A ships a single reader-only GET /api/vector-cortex/evaluation that
5
+ * aggregates redacted evaluation rows (persisted JSONL under the state dir)
6
+ * via the reader. No writable capability — no reset/mutation here; VC0C adds
7
+ * breaker health + reset endpoints into this same file, keeping it additive
8
+ * and within extension limits.
9
+ *
10
+ * VC0C (task 5) adds:
11
+ * GET /api/vector-cortex/health — reader-only aggregate health card
12
+ * POST /api/vector-cortex/breakers/reset — explicit admin capability
13
+ *
14
+ * Guardrails: PREVENT-PI-004 (local filesystem read only), PREVENT-011 (no
15
+ * `any`), reader-only aggregate (never payloads/prompts/ledger — EVAL-REDACT-002).
16
+ */
17
+
18
+ import type { IncomingMessage, ServerResponse } from "node:http";
19
+ import type { RouteContext } from "./routes-core.js";
20
+ import { VC0A_ENABLED, VC0C_ENABLED, VC1B_ENABLED } from "../../src/config.js";
21
+ import { readEvalRows } from "../../src/vector-cortex/eval/persist.js";
22
+ import { summarizeEvalRows } from "../../src/vector-cortex/eval/reader.js";
23
+ import { createVectorCortexSafety } from "../mega-runtime/vector-cortex-safety.js";
24
+ import { createLedgerStore } from "../../src/vector-cortex/ledger/store.js";
25
+ import type {
26
+ VectorCortexEvaluationSummary,
27
+ VectorCortexHealthCard,
28
+ VectorCortexLedgerView,
29
+ VectorCortexResetResult,
30
+ } from "./api-contracts/vector-cortex.js";
31
+
32
+ /** Admin capability: an actor-supplied subsystem selector for breaker reset. */
33
+ function readJsonBody(
34
+ req: IncomingMessage,
35
+ cb: (
36
+ result: { ok: true; value: Record<string, unknown> } | { ok: false; error: string },
37
+ ) => void,
38
+ ): void {
39
+ let body = "";
40
+ let tooBig = false;
41
+ req.on("data", (chunk: Buffer) => {
42
+ if (body.length > 65536) {
43
+ tooBig = true;
44
+ return;
45
+ }
46
+ body += chunk.toString();
47
+ });
48
+ req.on("end", () => {
49
+ if (tooBig) return cb({ ok: false, error: "body_too_large" });
50
+ try {
51
+ const v = body ? JSON.parse(body) : {};
52
+ if (typeof v !== "object" || v === null || Array.isArray(v)) {
53
+ return cb({ ok: false, error: "invalid_object" });
54
+ }
55
+ cb({ ok: true, value: v as Record<string, unknown> });
56
+ } catch {
57
+ cb({ ok: false, error: "invalid_json" });
58
+ }
59
+ });
60
+ }
61
+
62
+ function sendJson(res: ServerResponse, status: number, body: unknown): void {
63
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
64
+ res.writeHead(status, { "Content-Type": "application/json" });
65
+ res.end(JSON.stringify(body));
66
+ }
67
+
68
+ /** Reader-only aggregate GET /api/vector-cortex/evaluation. */
69
+ export function handleVectorCortexEvaluation(
70
+ req: IncomingMessage,
71
+ res: ServerResponse,
72
+ ctx: RouteContext,
73
+ ): boolean {
74
+ const url = req.url ?? "";
75
+ if (url !== "/api/vector-cortex/evaluation") return false;
76
+ if (req.method !== "GET") {
77
+ // Reader-only: no mutation endpoints exist on this path.
78
+ // guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
79
+ res.writeHead(405, { "Content-Type": "application/json" });
80
+ res.end(JSON.stringify({ error: "method_not_allowed" }));
81
+ return true;
82
+ }
83
+
84
+ const enabled = VC0A_ENABLED();
85
+ const rows = enabled ? readEvalRows(ctx.stateDir) : [];
86
+ const summary = summarizeEvalRows(rows);
87
+
88
+ const body: VectorCortexEvaluationSummary = {
89
+ enabled,
90
+ mode: enabled ? "A" : "C",
91
+ samples: summary.samples,
92
+ byMode: {
93
+ A: summary.byMode.A,
94
+ B: summary.byMode.B,
95
+ C: summary.byMode.C,
96
+ },
97
+ histogram: {
98
+ edges: [...summary.histogram.edges],
99
+ cells: [...summary.histogram.cells],
100
+ overflow: summary.histogram.overflow,
101
+ total: summary.histogram.total,
102
+ },
103
+ rejects: [], // VC0C wires live breaker/reject telemetry here
104
+ updatedAt: new Date().toISOString(),
105
+ };
106
+ sendJson(res, 200, body);
107
+ return true;
108
+ }
109
+
110
+ /**
111
+ * Reader-only GET /api/vector-cortex/health (VC0C).
112
+ *
113
+ * Aggregate health card: breaker state (window/probe/backoff), durable spool
114
+ * frontier/authority/lag, and a worst-state aggregate. Constructs the safety
115
+ * adapter over the state dir — durable spool state (frontier-frozen, lag) is
116
+ * read from disk, so it reflects restart. Purely read: no mutation, never
117
+ * payloads/prompts/ledger.
118
+ */
119
+ export function handleVectorCortexHealth(
120
+ req: IncomingMessage,
121
+ res: ServerResponse,
122
+ ctx: RouteContext,
123
+ ): boolean {
124
+ const url = req.url ?? "";
125
+ if (url !== "/api/vector-cortex/health") return false;
126
+ if (req.method !== "GET") {
127
+ // Reader-only path: no mutation endpoint lives at /health.
128
+ sendJson(res, 405, { error: "method_not_allowed" });
129
+ return true;
130
+ }
131
+
132
+ const enabled = VC0C_ENABLED();
133
+ const safety = enabled ? createVectorCortexSafety({ stateDir: ctx.stateDir }) : null;
134
+ const card = safety ? safety.health() : null;
135
+
136
+ const fallback: VectorCortexHealthCard = {
137
+ enabled: false,
138
+ mode: "C",
139
+ state: "CLOSED_A",
140
+ subsystem: "provider",
141
+ sinceMs: 0,
142
+ windowMs: 0,
143
+ probeCount: 0,
144
+ backoffDelayMs: 0,
145
+ frontierFrozen: false,
146
+ authorityOutage: false,
147
+ spoolLag: 0,
148
+ attempts: 0,
149
+ failures: 0,
150
+ p95Ms: 0,
151
+ failureRate: 0,
152
+ updatedAt: new Date().toISOString(),
153
+ aggregate: "CLOSED_A",
154
+ stateSource: "ephemeral",
155
+ };
156
+ if (!card) {
157
+ sendJson(res, 200, fallback);
158
+ return true;
159
+ }
160
+
161
+ const mode: "A" | "B" | "C" =
162
+ card.state === "CLOSED_A" ? "A" : card.state === "MANUAL_HALT" ? "C" : "B";
163
+ const aggregate =
164
+ card.state === "MANUAL_HALT"
165
+ ? "MANUAL_HALT"
166
+ : card.state === "OPEN_C"
167
+ ? "OPEN_C"
168
+ : card.state === "OPEN_B"
169
+ ? "OPEN_B"
170
+ : card.state === "PROBE_B" || card.state === "PROBE_A"
171
+ ? "PROBE"
172
+ : "CLOSED_A";
173
+
174
+ const body: VectorCortexHealthCard = {
175
+ enabled,
176
+ mode,
177
+ state: card.state,
178
+ subsystem: card.subsystem,
179
+ sinceMs: card.sinceMs,
180
+ reason: card.reason,
181
+ windowMs: card.windowMs,
182
+ probeCount: card.probeCount,
183
+ backoffDelayMs: card.backoffDelayMs,
184
+ frontierFrozen: card.frontierFrozen,
185
+ authorityOutage: card.authorityOutage,
186
+ spoolLag: card.spoolLag,
187
+ attempts: card.attempts,
188
+ failures: card.failures,
189
+ p95Ms: card.p95Ms,
190
+ failureRate: card.failureRate,
191
+ updatedAt: new Date().toISOString(),
192
+ aggregate,
193
+ stateSource: card.stateSource,
194
+ };
195
+ sendJson(res, 200, body);
196
+ return true;
197
+ }
198
+
199
+ /**
200
+ * Admin POST /api/vector-cortex/breakers/reset (VC0C).
201
+ *
202
+ * Explicit admin capability (separate from the reader GET). Clears a breaker's
203
+ * cooldown — or unwires a MANUAL_HALT — but NEVER evidence (attempts/failures
204
+ * are retained, per TRIAD_RESILIENCE). The subsystem is actor-supplied via the
205
+ * JSON body `{ subsystem }`. Every reset is documented in the returned record
206
+ * (state, retained evidence, updatedAt) — an auditable mutation surface; the
207
+ * GET path can never mutate.
208
+ */
209
+ export function handleVectorCortexBreakersReset(
210
+ req: IncomingMessage,
211
+ res: ServerResponse,
212
+ ctx: RouteContext,
213
+ ): boolean {
214
+ const url = req.url ?? "";
215
+ if (url !== "/api/vector-cortex/breakers/reset") return false;
216
+ if (req.method !== "POST") {
217
+ sendJson(res, 405, { error: "method_not_allowed" });
218
+ return true;
219
+ }
220
+ if (!VC0C_ENABLED()) {
221
+ sendJson(res, 409, { error: "vco_c_disabled" });
222
+ return true;
223
+ }
224
+
225
+ readJsonBody(req, (parsed) => {
226
+ if (!parsed.ok) {
227
+ sendJson(res, 400, { error: parsed.error });
228
+ return;
229
+ }
230
+ const subsystem = parsed.value.subsystem;
231
+ if (typeof subsystem !== "string" || subsystem.length === 0) {
232
+ sendJson(res, 400, { error: "subsystem_required" });
233
+ return;
234
+ }
235
+ try {
236
+ const safety = createVectorCortexSafety({ stateDir: ctx.stateDir });
237
+ const rec = safety.reset(subsystem);
238
+ const body: VectorCortexResetResult = {
239
+ subsystem: rec.subsystem,
240
+ state: rec.state,
241
+ cooldownCleared: true,
242
+ attempts: rec.attempts,
243
+ failures: rec.failures,
244
+ probeCount: rec.probeCount,
245
+ manualReason: rec.manualReason,
246
+ updatedAt: rec.updatedAt,
247
+ };
248
+ sendJson(res, 200, body);
249
+ } catch {
250
+ sendJson(res, 500, { error: "reset_failed" });
251
+ }
252
+ });
253
+ return true;
254
+ }
255
+
256
+ /**
257
+ * Reader-only GET /api/vector-cortex/ledger (VC1B).
258
+ *
259
+ * Built on the LedgerReader capability: opens the occurrence-v2 ledger for this
260
+ * repo's state dir and returns the session's occurrence IDENTITY rows
261
+ * (seq/eventId/kind/digest/toolCallId) plus high-water/count. NEVER ships
262
+ * sourceBytes or prompt text (reader-only no-ledger-text rule). Optional
263
+ * `?session=<id>` query selects the session (default "default").
264
+ */
265
+ export function handleVectorCortexLedger(
266
+ req: IncomingMessage,
267
+ res: ServerResponse,
268
+ ctx: RouteContext,
269
+ ): boolean {
270
+ const url = req.url ?? "";
271
+ const path = url.split("?")[0] ?? url;
272
+ if (path !== "/api/vector-cortex/ledger") return false;
273
+ if (req.method !== "GET") {
274
+ // Reader-only path: no mutation endpoint lives at /ledger.
275
+ sendJson(res, 405, { error: "method_not_allowed" });
276
+ return true;
277
+ }
278
+
279
+ // Parse the optional session query parameter.
280
+ let session = "default";
281
+ const qs = url.split("?")[1];
282
+ if (qs !== undefined) {
283
+ for (const pair of qs.split("&")) {
284
+ const [k, v] = pair.split("=");
285
+ if (k === "session" && v !== undefined && v.length > 0) {
286
+ session = decodeURIComponent(v);
287
+ }
288
+ }
289
+ }
290
+
291
+ const enabled = VC1B_ENABLED();
292
+ let active = enabled;
293
+ let highWater = "0";
294
+ let count = 0;
295
+ let occurrences: VectorCortexLedgerView["occurrences"] = [];
296
+
297
+ if (enabled) {
298
+ try {
299
+ const store = createLedgerStore({ stateDir: ctx.stateDir });
300
+ try {
301
+ const reader = store.reader();
302
+ highWater = reader.highWater(session).toString();
303
+ const rows = reader.readSession(session);
304
+ count = rows.length;
305
+ // Identity only: never sourceBytes or prompt text (reader-only rule).
306
+ occurrences = rows.slice(-500).map((occ) => ({
307
+ seq: occ.seq.toString(),
308
+ eventId: occ.eventId,
309
+ kind: occ.kind,
310
+ digest: occ.digest,
311
+ ...(occ.toolCallId !== undefined ? { toolCallId: occ.toolCallId } : {}),
312
+ }));
313
+ } finally {
314
+ store.close();
315
+ }
316
+ } catch {
317
+ // Non-fatal: a missing/corrupt ledger DB degrades to `enabled:false`.
318
+ active = false;
319
+ }
320
+ }
321
+
322
+ const body: VectorCortexLedgerView = {
323
+ enabled: active,
324
+ session,
325
+ highWater,
326
+ count,
327
+ occurrences,
328
+ updatedAt: new Date().toISOString(),
329
+ };
330
+ sendJson(res, 200, body);
331
+ return true;
332
+ }
@@ -40,3 +40,9 @@ export { handleRagSettings } from "./routes-rag-settings.js";
40
40
  export { handleRagMetrics } from "./routes-rag-metrics.js";
41
41
  export { handleModelThresholds } from "./routes-model-thresholds.js";
42
42
  export { handleWiki } from "./routes-wiki.js";
43
+ export {
44
+ handleVectorCortexEvaluation,
45
+ handleVectorCortexHealth,
46
+ handleVectorCortexBreakersReset,
47
+ handleVectorCortexLedger,
48
+ } from "./routes-vector-cortex.js";
@@ -56,6 +56,10 @@ import {
56
56
  handleRagMetrics,
57
57
  handleModelThresholds,
58
58
  handleWiki,
59
+ handleVectorCortexEvaluation,
60
+ handleVectorCortexHealth,
61
+ handleVectorCortexBreakersReset,
62
+ handleVectorCortexLedger,
59
63
  handleStatic,
60
64
  } from "./routes.js";
61
65
 
@@ -275,6 +279,10 @@ export async function launchDashboardServer(
275
279
  if (handleRagMetrics(req, res, ctx)) return;
276
280
  if (handleModelThresholds(req, res, ctx)) return;
277
281
  if (handleWiki(req, res, ctx)) return;
282
+ if (handleVectorCortexEvaluation(req, res, ctx)) return;
283
+ if (handleVectorCortexHealth(req, res, ctx)) return;
284
+ if (handleVectorCortexBreakersReset(req, res, ctx)) return;
285
+ if (handleVectorCortexLedger(req, res, ctx)) return;
278
286
  handleStatic(req, res, ctx);
279
287
  });
280
288
 
@@ -35,6 +35,7 @@ import {
35
35
  type MegaConfig,
36
36
  } from "../mega-config.js";
37
37
  import { appendMirrorMessages } from "./mirror-append.js";
38
+ import { appendMessagesToLedger } from "../mega-runtime/vector-cortex-ledger.js";
38
39
  import { epochIdFor } from "../../src/mirror/epoch.js";
39
40
  import { computeLiveTrimCut, liveTrimSummaryMessage } from "../mega-trim.js";
40
41
  import { messageContentText } from "./context-handler/messageText.js";
@@ -149,6 +150,21 @@ export function registerContextHandler(
149
150
  }
150
151
  }
151
152
 
153
+ // VC1B (S1): canonical messages -> v2 ledger occurrences. Flag-OFF opens
154
+ // no DB (byte-identical predecessor); non-fatal. onFailure surfaces
155
+ // per-append rejections (e.g. EVT_SEQ_REGRESSION on rewind/fork) as
156
+ // structured warnings rather than swallowing them silently.
157
+ try {
158
+ appendMessagesToLedger(
159
+ runtime.currentStateDir,
160
+ runtime.rt.sessionId,
161
+ messages,
162
+ runtime.logger,
163
+ );
164
+ } catch (e) {
165
+ runtime.logger.warn("vc1b-ledger-append-fail", { error: String(e) });
166
+ }
167
+
152
168
  // S52 / v0.16.1: per-model threshold override. The user can tune the
153
169
  // fire point + safety margin PER MODEL (different providers' models range
154
170
  // 8K-1M+ context, so one global tier % is wrong). Falls back to env/default
@@ -0,0 +1,165 @@
1
+ /**
2
+ * vector-cortex-ledger.ts — VC1B occurrence-ledger writer wiring (S1).
3
+ *
4
+ * Wires ONLY the `LedgerWriter` capability into message ingestion: each
5
+ * canonical message that flows through the compaction pipeline becomes an
6
+ * occurrence in the v2 ledger. The wiring is writer-only (never reader/admin
7
+ * here — the dashboard GET owns the reader), matching the capability-gated
8
+ * CONTRACTS §Store rule. A tool RESULT occurrence references exactly one
9
+ * earlier call via `toolCallId` (the ledger enforces EVT_TOOL_CALL_MISSING).
10
+ *
11
+ * When MEGACOMPACT_VC1B is OFF the seam opens NO ledger DB at all — and even if
12
+ * a caller already holds a store, its write path is inert (S2 flag-gate in
13
+ * store.ts) — so the OFF behavior is byte-identical to the pre-VC1B predecessor.
14
+ *
15
+ * Local-only (PREVENT-PI-004), no `any` (PREVENT-011), no console.log (events
16
+ * go through the ledger emit seam); non-fatal — a ledger write failure never
17
+ * breaks the agent loop.
18
+ */
19
+ import { createHash } from "node:crypto";
20
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
21
+ import { VC1B_ENABLED } from "../../src/config/vector-cortex.js";
22
+ import {
23
+ createLedgerStore,
24
+ type LedgerWriter,
25
+ } from "../../src/vector-cortex/ledger/store.js";
26
+
27
+ /** A single canonical message mapped to a ledger occurrence input. */
28
+ export interface LedgerMessageInput {
29
+ readonly session: string;
30
+ readonly seq: bigint;
31
+ readonly eventId: string;
32
+ readonly kind: string;
33
+ readonly toolCallId?: string;
34
+ readonly sourceBytes: Uint8Array;
35
+ }
36
+
37
+ /** The writer handle handed to ingestion. */
38
+ export interface LedgerWriterHandle {
39
+ readonly writer: LedgerWriter;
40
+ readonly close: () => void;
41
+ }
42
+
43
+ /** Open the ledger writer for ingestion, or null when VC1B is disabled. */
44
+ export function openLedgerWriter(stateDir: string): LedgerWriterHandle | null {
45
+ if (!VC1B_ENABLED()) return null;
46
+ const store = createLedgerStore({ stateDir });
47
+ return { writer: store.writer(), close: () => store.close() };
48
+ }
49
+
50
+ /**
51
+ * Append one occurrence for a canonical message. `kind` is the neutral kind;
52
+ * when `toolCallId` is present the ledger enforces exactly-one reference to an
53
+ * earlier call. Flag-OFF (null handle) and the writer's own inert path are
54
+ * no-ops. Non-fatal: never throws to the loop — a failure is surfaced as
55
+ * `{ ok: false, code }` so the caller can log the reason (Q02).
56
+ */
57
+ export function appendLedgerMessage(
58
+ handle: LedgerWriterHandle,
59
+ input: LedgerMessageInput,
60
+ ): { ok: boolean; code?: string } {
61
+ try {
62
+ const res = handle.writer.append(input);
63
+ return res.ok ? { ok: true } : { ok: false, code: res.code };
64
+ } catch (e) {
65
+ return { ok: false, code: String(e) };
66
+ }
67
+ }
68
+
69
+ /** Structured logger sink for seam append failures (wired by the extension). */
70
+ export interface LedgerAppendLogger {
71
+ readonly warn: (event: string, fields: Record<string, unknown>) => void;
72
+ }
73
+
74
+ /**
75
+ * Append one occurrence per canonical message in `messages` (session-scoped,
76
+ * sequence = 1-based index). Opens the writer, appends each, closes the handle.
77
+ * Returns the number of accepted occurrences (0 when disabled OR when every
78
+ * append was a non-fatal no-op). This is the S1 ingestion seam.
79
+ *
80
+ * Monotonic-prefix assumption (Q02): the FIRST accepted append establishes the
81
+ * per-session high-water, and each subsequent append must carry `seq =
82
+ * highWater + 1`. Append order here follows `messages` array order with 1-based
83
+ * indices, so a shorter/mid-removal/reordered `messages` list after a prior
84
+ * taller run (rewind/fork) can drive `seq` backwards and be rejected with
85
+ * `EVT_SEQ_REGRESSION`. Such appends fail on the ledger and, without `onFailure`,
86
+ * are swallowed silently — so the caller should pass `onFailure` to log them.
87
+ */
88
+ export function appendMessagesToLedger(
89
+ stateDir: string,
90
+ sessionId: string,
91
+ messages: AgentMessage[],
92
+ onFailure?: Readonly<LedgerAppendLogger>,
93
+ ): number {
94
+ const ledger = openLedgerWriter(stateDir);
95
+ if (!ledger) return 0;
96
+ try {
97
+ let accepted = 0;
98
+ for (let i = 0; i < messages.length; i++) {
99
+ const input = messageToLedgerInput(messages[i]!, sessionId, i);
100
+ const res = appendLedgerMessage(ledger, input);
101
+ if (res.ok) {
102
+ accepted++;
103
+ } else if (onFailure) {
104
+ onFailure.warn("vc1b-ledger-append-skip", {
105
+ session: sessionId,
106
+ seq: input.seq.toString(),
107
+ eventId: input.eventId,
108
+ kind: input.kind,
109
+ reason: res.code ?? "unknown",
110
+ });
111
+ }
112
+ }
113
+ return accepted;
114
+ } finally {
115
+ ledger.close();
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Map an AgentMessage to a ledger occurrence input. `eventId` is the stable
121
+ * content digest so re-observed identical messages are (event_id,digest)-deduped;
122
+ * `kind` mirrors the canonical role. `toolCallId` is intentionally NOT set here:
123
+ * pi discriminates messages by `role` only and the tool-call id lives in
124
+ * variant-specific `.content`/`.tool_calls` we must not reach into, so the
125
+ * exactly-one tool back-edge (EVT_TOOL_CALL_MISSING) is exercised at the
126
+ * store/acceptance level and the full live mapping is deferred to the VC1
127
+ * producer-wiring sprint.
128
+ */
129
+ export function messageToLedgerInput(
130
+ msg: AgentMessage,
131
+ session: string,
132
+ indexInSession: number,
133
+ ): LedgerMessageInput {
134
+ const m = msg as { role?: string; id?: string };
135
+ const text = messageText(msg);
136
+ const role = m.role ?? "unknown";
137
+ const digest = `sha256:${createHash("sha256").update(text).digest("hex")}`;
138
+ return {
139
+ session,
140
+ seq: BigInt(indexInSession + 1),
141
+ eventId: m.id ?? digest,
142
+ kind: role,
143
+ sourceBytes: new TextEncoder().encode(text),
144
+ };
145
+ }
146
+
147
+ /** Best-effort text extraction from an AgentMessage (string or part array). */
148
+ function messageText(msg: AgentMessage): string {
149
+ const content = (msg as { content?: unknown }).content;
150
+ if (content == null) return "";
151
+ if (typeof content === "string") return content;
152
+ if (Array.isArray(content)) {
153
+ return content
154
+ .map((p) => {
155
+ const part = p as { type?: string; text?: string };
156
+ return typeof part.text === "string" ? part.text : "";
157
+ })
158
+ .join("\n");
159
+ }
160
+ if (typeof content === "object") {
161
+ const maybe = (content as Record<string, unknown>).text;
162
+ if (typeof maybe === "string") return maybe;
163
+ }
164
+ return "";
165
+ }