pi-mega-compact 0.20.4 → 0.20.5

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 (104) hide show
  1. package/dist/config/vector-cortex.js +12 -0
  2. package/dist/config.js +1 -1
  3. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +1 -0
  4. package/dist/extensions/dashboard-server/routes-vector-cortex-render.js +40 -0
  5. package/dist/extensions/dashboard-server/routes-vector-cortex.js +1 -0
  6. package/dist/extensions/dashboard-server/routes.js +1 -1
  7. package/dist/extensions/dashboard-server/server.js +3 -1
  8. package/dist/extensions/mega-context/vector-cortex.js +128 -0
  9. package/dist/src/config/vector-cortex.js +12 -0
  10. package/dist/src/config.js +1 -1
  11. package/dist/src/store/sqlite/global-sessions.js +16 -4
  12. package/dist/src/vector-cortex/provider/registry.js +112 -0
  13. package/dist/src/vector-cortex/provider/types.js +30 -0
  14. package/dist/src/vector-cortex/render/_acceptance-fixture.js +58 -0
  15. package/dist/src/vector-cortex/render/_acceptance-helpers.js +17 -0
  16. package/dist/src/vector-cortex/render/_acceptance-provider.js +29 -0
  17. package/dist/src/vector-cortex/render/_acceptance-scenario.js +259 -0
  18. package/dist/src/vector-cortex/render/renderer.js +119 -0
  19. package/dist/src/vector-cortex/render/types.js +29 -0
  20. package/dist/src/vector-cortex/render/validator.js +90 -0
  21. package/dist/vector-cortex/provider/registry.js +112 -0
  22. package/dist/vector-cortex/provider/types.js +30 -0
  23. package/dist/vector-cortex/render/_acceptance-fixture.js +58 -0
  24. package/dist/vector-cortex/render/_acceptance-helpers.js +17 -0
  25. package/dist/vector-cortex/render/_acceptance-provider.js +29 -0
  26. package/dist/vector-cortex/render/_acceptance-scenario.js +259 -0
  27. package/dist/vector-cortex/render/renderer.js +119 -0
  28. package/dist/vector-cortex/render/types.js +29 -0
  29. package/dist/vector-cortex/render/validator.js +90 -0
  30. package/extensions/dashboard-client/dist/assets/{AreaChart-Cam9-nhT.js → AreaChart-CGL29Tcl.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{AreaChart-Cam9-nhT.js.map → AreaChart-CGL29Tcl.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{BarChart-DzIY8aM1.js → BarChart-OxhalIZa.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{BarChart-DzIY8aM1.js.map → BarChart-OxhalIZa.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/{CacheTab-CKIiYsG_.js → CacheTab-VcMFwL-6.js} +2 -2
  35. package/extensions/dashboard-client/dist/assets/{CacheTab-CKIiYsG_.js.map → CacheTab-VcMFwL-6.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/{EventsTab-xaaBJLiK.js → EventsTab-DePofXDM.js} +2 -2
  37. package/extensions/dashboard-client/dist/assets/{EventsTab-xaaBJLiK.js.map → EventsTab-DePofXDM.js.map} +1 -1
  38. package/extensions/dashboard-client/dist/assets/{HealthTab-BGtUKOqG.js → HealthTab-nQd1tjSF.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{HealthTab-BGtUKOqG.js.map → HealthTab-nQd1tjSF.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-ABgQnTZ9.js → MaintenanceTab-CzvA8-F6.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-ABgQnTZ9.js.map → MaintenanceTab-CzvA8-F6.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-ChR2GAN-.js → MemoryMapTab-3lxgWBvU.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-ChR2GAN-.js.map → MemoryMapTab-3lxgWBvU.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{MetricsTab-DdCLAgcR.js → MetricsTab-D_FcA6kN.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{MetricsTab-DdCLAgcR.js.map → MetricsTab-D_FcA6kN.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{OverviewTab-DtemzJt6.js → OverviewTab-BU6R4aEs.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{OverviewTab-DtemzJt6.js.map → OverviewTab-BU6R4aEs.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{ReposTab-B9mvD9Ep.js → ReposTab-DFjy1isz.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{ReposTab-B9mvD9Ep.js.map → ReposTab-DFjy1isz.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/{SessionsTab-BcO38XLS.js → SessionsTab-UGIzIH25.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{SessionsTab-BcO38XLS.js.map → SessionsTab-UGIzIH25.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{SetupTab-D_TBNP09.js → SetupTab-CECrgMKI.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{SetupTab-D_TBNP09.js.map → SetupTab-CECrgMKI.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-TjHpbUOo.js → TimeSavedCard-Dyy4SCDk.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-TjHpbUOo.js.map → TimeSavedCard-Dyy4SCDk.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{TurnsTab-DprxndCF.js → TurnsTab-B0LPcVs1.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{TurnsTab-DprxndCF.js.map → TurnsTab-B0LPcVs1.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/VectorCortexTab-BWi3u_gp.js +2 -0
  59. package/extensions/dashboard-client/dist/assets/VectorCortexTab-BWi3u_gp.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/{WikiTab-OKgSo15G.js → WikiTab-DWk9Qvut.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{WikiTab-OKgSo15G.js.map → WikiTab-DWk9Qvut.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{button-D_Qz4q7L.js → button-O59wPr5H.js} +2 -2
  63. package/extensions/dashboard-client/dist/assets/{button-D_Qz4q7L.js.map → button-O59wPr5H.js.map} +1 -1
  64. package/extensions/dashboard-client/dist/assets/{card-CsI6Jm3p.js → card-C-DxXv1-.js} +2 -2
  65. package/extensions/dashboard-client/dist/assets/{card-CsI6Jm3p.js.map → card-C-DxXv1-.js.map} +1 -1
  66. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CvHKUdhX.js → generateCategoricalChart-Dw5YnE5s.js} +2 -2
  67. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CvHKUdhX.js.map → generateCategoricalChart-Dw5YnE5s.js.map} +1 -1
  68. package/extensions/dashboard-client/dist/assets/{index-0Ye_un22.js → index-i9oaPE0a.js} +3 -3
  69. package/extensions/dashboard-client/dist/assets/{index-0Ye_un22.js.map → index-i9oaPE0a.js.map} +1 -1
  70. package/extensions/dashboard-client/dist/assets/{switch-VQEoJl8Q.js → switch-B69QY3IM.js} +2 -2
  71. package/extensions/dashboard-client/dist/assets/{switch-VQEoJl8Q.js.map → switch-B69QY3IM.js.map} +1 -1
  72. package/extensions/dashboard-client/dist/assets/{toggle-CAPm4HJo.js → toggle-BJ4Kwggu.js} +2 -2
  73. package/extensions/dashboard-client/dist/assets/{toggle-CAPm4HJo.js.map → toggle-BJ4Kwggu.js.map} +1 -1
  74. package/extensions/dashboard-client/dist/assets/{useSSE-pxsNTVkh.js → useSSE-BpuA8sPd.js} +2 -2
  75. package/extensions/dashboard-client/dist/assets/{useSSE-pxsNTVkh.js.map → useSSE-BpuA8sPd.js.map} +1 -1
  76. package/extensions/dashboard-client/dist/index.html +1 -1
  77. package/extensions/dashboard-client/src/api/vector-cortex.ts +9 -0
  78. package/extensions/dashboard-client/src/tabs/VectorCortexMetric.tsx +10 -0
  79. package/extensions/dashboard-client/src/tabs/VectorCortexPlansCard.tsx +39 -0
  80. package/extensions/dashboard-client/src/tabs/VectorCortexRenderCard.tsx +38 -0
  81. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +11 -38
  82. package/extensions/dashboard-client/src/types/vector-cortex.ts +9 -0
  83. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +20 -0
  84. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +6 -0
  85. package/extensions/dashboard-server/routes-vector-cortex-render.ts +49 -0
  86. package/extensions/dashboard-server/routes-vector-cortex.ts +1 -0
  87. package/extensions/dashboard-server/routes.ts +1 -0
  88. package/extensions/dashboard-server/server.ts +2 -0
  89. package/extensions/mega-context/vector-cortex.ts +163 -0
  90. package/package.json +1 -1
  91. package/src/config/vector-cortex.ts +13 -0
  92. package/src/config.ts +1 -0
  93. package/src/store/sqlite/global-sessions.ts +14 -2
  94. package/src/vector-cortex/provider/registry.ts +137 -0
  95. package/src/vector-cortex/provider/types.ts +163 -0
  96. package/src/vector-cortex/render/_acceptance-fixture.ts +128 -0
  97. package/src/vector-cortex/render/_acceptance-helpers.ts +39 -0
  98. package/src/vector-cortex/render/_acceptance-provider.ts +45 -0
  99. package/src/vector-cortex/render/_acceptance-scenario.ts +319 -0
  100. package/src/vector-cortex/render/renderer.ts +142 -0
  101. package/src/vector-cortex/render/types.ts +174 -0
  102. package/src/vector-cortex/render/validator.ts +120 -0
  103. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Bym21yVQ.js +0 -2
  104. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Bym21yVQ.js.map +0 -1
@@ -15,6 +15,7 @@ import {
15
15
  fetchVectorCortexQuery,
16
16
  fetchVectorCortexReconstruct,
17
17
  fetchVectorCortexPlans,
18
+ fetchVectorCortexRender,
18
19
  fetchVectorCortexShards,
19
20
  fetchVectorCortexTopology,
20
21
  resetVectorCortexBreaker,
@@ -24,11 +25,15 @@ import {
24
25
  type VectorCortexQueryView,
25
26
  type VectorCortexReconstructView,
26
27
  type VectorCortexPlansView,
28
+ type VectorCortexRenderView,
27
29
  type VectorCortexShardsView,
28
30
  type VectorCortexTopologyView,
29
31
  } from "../api/vector-cortex";
30
32
  import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
31
33
  import { Badge } from "../components/ui/badge";
34
+ import { Metric } from "./VectorCortexMetric";
35
+ import { VectorCortexRenderCard } from "./VectorCortexRenderCard";
36
+ import { VectorCortexPlansCard } from "./VectorCortexPlansCard";
32
37
 
33
38
  function ModeChip({ mode, count }: { mode: string; count: number }): React.ReactElement {
34
39
  return (
@@ -39,15 +44,6 @@ function ModeChip({ mode, count }: { mode: string; count: number }): React.React
39
44
  );
40
45
  }
41
46
 
42
- function Metric({ label, value }: { label: string; value: string }): React.ReactElement {
43
- return (
44
- <div>
45
- <div className="text-xs text-muted-foreground">{label}</div>
46
- <div className="font-mono text-sm">{value}</div>
47
- </div>
48
- );
49
- }
50
-
51
47
  export default function VectorCortexTab(): React.ReactElement {
52
48
  const [data, setData] = useState<VectorCortexEvaluationSummary | null>(null);
53
49
  const [err, setError] = useState<string | null>(null);
@@ -60,6 +56,7 @@ export default function VectorCortexTab(): React.ReactElement {
60
56
  const [shards, setShards] = useState<VectorCortexShardsView | null>(null);
61
57
  const [reconstruct, setReconstruct] = useState<VectorCortexReconstructView | null>(null);
62
58
  const [plans, setPlans] = useState<VectorCortexPlansView | null>(null);
59
+ const [render, setRender] = useState<VectorCortexRenderView | null>(null);
63
60
 
64
61
  const poll = useCallback(() => {
65
62
  fetchVectorCortexEvaluation()
@@ -89,6 +86,9 @@ export default function VectorCortexTab(): React.ReactElement {
89
86
  fetchVectorCortexPlans().then(setPlans).catch(() => {
90
87
  /* plan manifest card is best-effort (VC5A) */
91
88
  });
89
+ fetchVectorCortexRender().then(setRender).catch(() => {
90
+ /* render conformance card is best-effort (VC5B) */
91
+ });
92
92
  }, []);
93
93
 
94
94
  useEffect(() => {
@@ -403,35 +403,8 @@ export default function VectorCortexTab(): React.ReactElement {
403
403
  )}
404
404
  </CardContent>
405
405
  </Card>
406
- <Card>
407
- <CardHeader>
408
- <div className="flex items-center justify-between">
409
- <CardTitle>Plan Manifests (VC5A)</CardTitle>
410
- {plans?.enabled ? (
411
- <Badge variant="success">ACTIVE</Badge>
412
- ) : (
413
- <Badge variant="danger">OFF</Badge>
414
- )}
415
- </div>
416
- </CardHeader>
417
- <CardContent>
418
- {!plans?.enabled ? (
419
- <div className="vc-empty">PromptDagV1 + budgeted planner disabled (VC5A off).</div>
420
- ) : (
421
- <>
422
- <div className="grid grid-cols-2 gap-4 md:grid-cols-4">
423
- <Metric label="DAG fixtures" value={String(plans.dagCount)} />
424
- <Metric label="Plan fixtures" value={String(plans.plannerCount)} />
425
- <Metric label="Plans exposed" value={String(plans.plans.length)} />
426
- </div>
427
- <div className="mt-3 text-xs text-muted-foreground">
428
- Reader-only plan manifests only — no session payloads or prompt text.
429
- Per-run plan outputs are staged in-memory this sprint.
430
- </div>
431
- </>
432
- )}
433
- </CardContent>
434
- </Card>
406
+ <VectorCortexPlansCard view={plans} />
407
+ <VectorCortexRenderCard view={render} />
435
408
  <Card>
436
409
  <CardHeader>
437
410
  <div className="flex items-center justify-between">
@@ -182,3 +182,12 @@ export interface VectorCortexPlansView {
182
182
  plans: VectorCortexPlanManifest[];
183
183
  updatedAt: string;
184
184
  }
185
+
186
+ /** Reader-only render + provider-profile view (VC5B). */
187
+ export interface VectorCortexRenderView {
188
+ enabled: boolean;
189
+ renderCount: number;
190
+ providerCount: number;
191
+ knownProfiles: string[];
192
+ updatedAt: string;
193
+ }
@@ -296,6 +296,26 @@ export interface VectorCortexPlansView {
296
296
  readonly updatedAt: string;
297
297
  }
298
298
 
299
+ /**
300
+ * Reader-only render/profile view for GET /api/vector-cortex/render (VC5B).
301
+ * Reports the registered render (REN-001..) and provider-profile (PRO-001..)
302
+ * identifier counts + the known provider-profile keys. NEVER exposes rendered
303
+ * node bytes, prompt text, or the canonical outbound request (reader-only,
304
+ * SECURITY_PRIVACY: the exact ledger is not training data).
305
+ */
306
+ export interface VectorCortexRenderView {
307
+ /** Whether the VC5B render + provider-profile flag is enabled. */
308
+ readonly enabled: boolean;
309
+ /** Count of registered render fixtures (REN-001..). */
310
+ readonly renderCount: number;
311
+ /** Count of registered provider-profile fixtures (PRO-001..). */
312
+ readonly providerCount: number;
313
+ /** Known base provider-profile keys (provider//model), reader-only. */
314
+ readonly knownProfiles: readonly string[];
315
+ /** ISO timestamp of the snapshot. */
316
+ readonly updatedAt: string;
317
+ }
318
+
299
319
  /**
300
320
  * Reader-only occurrence-ledger view for GET /api/vector-cortex/ledger (VC1B).
301
321
  * Built on the LedgerReader capability surface. Exposes occurrence IDENTITY
@@ -373,6 +373,12 @@ export const SETTINGS: ReadonlyArray<{
373
373
  "Single-session DAG (PromptDagV1) + budgeted 0/1 portfolio planner: builds a stable Kahn-ordered DAG, computes the mandatory dependency/tool/anchor closure before optional selection, returns MANDATORY_CLOSURE_OVER_BUDGET with evidence preserved on overflow, and runs a utility-per-token portfolio that never exceeds the remaining budget. Framing is owned here, not in VC4C. OFF = byte-identical predecessor (VC4C).",
374
374
  true,
375
375
  ),
376
+ boolDirect(
377
+ "MEGACOMPACT_VC5B",
378
+ "VC5B Validated Renderer + Provider Profiles",
379
+ "Validated prompt renderer: replays VC5A's stable Kahn order verbatim, preserves exact tool bytes (PREVENT-PI-002), places compacted context via the host before_agent_start prepend seam — never role:system (PREVENT-PI-003) — and SHA-256 hashes the entire canonical outbound request before provider invocation. Unknown provider/model cleanly bypasses to the predecessor prompt path. OFF = byte-identical predecessor (VC5A).",
380
+ true,
381
+ ),
376
382
  ],
377
383
  },
378
384
  {
@@ -0,0 +1,49 @@
1
+ /**
2
+ * dashboard-server/routes-vector-cortex-render.ts — VC5B render + provider-profile
3
+ * dashboard route.
4
+ *
5
+ * Reader-only GET /api/vector-cortex/render returning the registered render
6
+ * (REN-001..020) and provider-profile (PRO-001..015) identifier counts plus the
7
+ * known base provider-profile keys. NEVER exposes rendered node bytes, prompt
8
+ * text, or the canonical outbound request (reader-only, SECURITY_PRIVACY).
9
+ * Flag-gated on MEGACOMPACT_VC5B: `enabled:false` when off (byte-identical to
10
+ * the pre-VC5B predecessor).
11
+ *
12
+ * Guardrails: PREVENT-PI-004 (local in-process state only), PREVENT-011 (no
13
+ * `any`), reader-only aggregate (counts + profile keys only).
14
+ */
15
+
16
+ import type { IncomingMessage, ServerResponse } from "node:http";
17
+ import type { RouteContext } from "./routes-core.js";
18
+ import { VC5B_ENABLED } from "../../src/config.js";
19
+ import { REN_IDS } from "../../src/vector-cortex/render/types.js";
20
+ import { PRO_IDS } from "../../src/vector-cortex/provider/types.js";
21
+ import { KNOWN_PROVIDER_KEYS } from "../../src/vector-cortex/provider/registry.js";
22
+ import { sendJson } from "./routes-vector-cortex-shared.js";
23
+ import type { VectorCortexRenderView } from "./api-contracts/vector-cortex.js";
24
+
25
+ /** Reader-only GET /api/vector-cortex/render (VC5B). */
26
+ export function handleVectorCortexRender(
27
+ req: IncomingMessage,
28
+ res: ServerResponse,
29
+ _ctx: RouteContext,
30
+ ): boolean {
31
+ const url = req.url ?? "";
32
+ const path = url.split("?")[0] ?? url;
33
+ if (path !== "/api/vector-cortex/render") return false;
34
+ if (req.method !== "GET") {
35
+ sendJson(res, 405, { error: "method_not_allowed" });
36
+ return true;
37
+ }
38
+
39
+ const enabled = VC5B_ENABLED();
40
+ const body: VectorCortexRenderView = {
41
+ enabled,
42
+ renderCount: enabled ? REN_IDS.length : 0,
43
+ providerCount: enabled ? PRO_IDS.length : 0,
44
+ knownProfiles: enabled ? KNOWN_PROVIDER_KEYS : [],
45
+ updatedAt: new Date().toISOString(),
46
+ };
47
+ sendJson(res, 200, body);
48
+ return true;
49
+ }
@@ -23,3 +23,4 @@ export { handleVectorCortexShards } from "./routes-vector-cortex-shards.js";
23
23
  export { handleVectorCortexResidual } from "./routes-vector-cortex-residual.js";
24
24
  export { handleVectorCortexReconstruct } from "./routes-vector-cortex-reconstruct.js";
25
25
  export { handleVectorCortexPlans } from "./routes-vector-cortex-plans.js";
26
+ export { handleVectorCortexRender } from "./routes-vector-cortex-render.js";
@@ -51,4 +51,5 @@ export {
51
51
  handleVectorCortexResidual,
52
52
  handleVectorCortexReconstruct,
53
53
  handleVectorCortexPlans,
54
+ handleVectorCortexRender,
54
55
  } from "./routes-vector-cortex.js";
@@ -67,6 +67,7 @@ import {
67
67
  handleVectorCortexResidual,
68
68
  handleVectorCortexReconstruct,
69
69
  handleVectorCortexPlans,
70
+ handleVectorCortexRender,
70
71
  handleStatic,
71
72
  } from "./routes.js";
72
73
 
@@ -313,6 +314,7 @@ export async function launchDashboardServer(
313
314
  if (handleVectorCortexResidual(req, res, ctx)) return;
314
315
  if (handleVectorCortexReconstruct(req, res, ctx)) return;
315
316
  if (handleVectorCortexPlans(req, res, ctx)) return;
317
+ if (handleVectorCortexRender(req, res, ctx)) return;
316
318
  handleStatic(req, res, ctx);
317
319
  });
318
320
 
@@ -0,0 +1,163 @@
1
+ /**
2
+ * mega-context/vector-cortex.ts — VC5B delegation seam (the sprint's production
3
+ * ownership: `extensions/mega-context/vector-cortex.ts`).
4
+ *
5
+ * This is the ONLY extension-side surface VC5B owns. It delegates to the pi-
6
+ * agnostic `src/vector-cortex/render` + `src/vector-cortex/provider` logic and
7
+ * wires the two structured events the sprint emits:
8
+ * - `vector_cortex_render_validated` — a render passed validation and will be
9
+ * prepended via the host `before_agent_start` systemPrompt seam.
10
+ * - `vector_cortex_provider_bypassed` — an unknown provider/profile forced a
11
+ * clean bypass (triad C, the predecessor prompt path).
12
+ *
13
+ * GUARDRAIL PREVENT-PI-003 (critical): the rendered compacted context is placed
14
+ * via the host `before_agent_start` systemPrompt PREPEND seam — it is NEVER
15
+ * emitted as a `role:"system"` conversation message. The renderer returns a
16
+ * `RenderManifestV1`, not a raw system-role payload; this seam composes the
17
+ * manifest's prepend into `event.systemPrompt`.
18
+ *
19
+ * The context handler (extensions/mega-events/context-handler.ts) is 228 lines
20
+ * (already a delegate shell) and under the extension hard limit, so NO split was
21
+ * required — VC5B delegates from here rather than modifying that handler.
22
+ *
23
+ * Pi runtime adapter: imports only pi runtime types + the VC5B logic. Flag-gated
24
+ * on `VC5B_ENABLED()`; flag OFF is byte-identical to the predecessor (VC5A state
25
+ * — the DAG/plan path), so this seam returns the host's unmodified systemPrompt.
26
+ */
27
+
28
+ import type { ExtensionAPI, ExtensionContext, BeforeAgentStartEvent } from "@earendil-works/pi-coding-agent";
29
+
30
+ import { VC5B_ENABLED } from "../../src/config/vector-cortex.js";
31
+ import { resolveProviderProfile } from "../../src/vector-cortex/provider/registry.js";
32
+ import { renderPrompt } from "../../src/vector-cortex/render/renderer.js";
33
+ import { validateRender } from "../../src/vector-cortex/render/validator.js";
34
+ import type { RenderInput, RenderManifestV1, CanonicalOutboundRequest } from "../../src/vector-cortex/render/types.js";
35
+ import type { ProviderProfileBundle } from "../../src/vector-cortex/provider/types.js";
36
+
37
+ /** An injected emit callback — the same (event, fields) shape as the src seams. */
38
+ export type VectorCortexEmit = (event: string, fields: Record<string, unknown>) => void;
39
+
40
+ /** The VC5B seam result handed back to the before_agent_start handler. */
41
+ export interface VectorCortexRenderOutcome {
42
+ readonly validated: boolean;
43
+ readonly bypassed: boolean;
44
+ readonly manifest: RenderManifestV1 | null;
45
+ /** The text to prepend to the host systemPrompt (empty when not VC5B). */
46
+ readonly systemPromptPrepend: string;
47
+ readonly triad: "A" | "B" | "C";
48
+ }
49
+
50
+ /**
51
+ * Run the full VC5B render+validate pipeline. Returns a structured outcome the
52
+ * `before_agent_start` handler composes into the host systemPrompt. On a clean
53
+ * bypass (unknown profile) it emits `vector_cortex_provider_bypassed` and the
54
+ * host uses the predecessor prompt path (triad C). On a validated render it
55
+ * emits `vector_cortex_render_validated` (triad A).
56
+ *
57
+ * All emits are best-effort and non-fatal; the flag gates zero observability
58
+ * writes when OFF (byte-identical to predecessor).
59
+ */
60
+ export function runVectorCortexRender(
61
+ input: RenderInput,
62
+ emit?: VectorCortexEmit,
63
+ ): VectorCortexRenderOutcome {
64
+ const fire = (event: string, fields: Record<string, unknown>): void => {
65
+ try {
66
+ if (VC5B_ENABLED()) emit?.(event, fields);
67
+ } catch {
68
+ /* non-fatal observability — never break the agent loop */
69
+ }
70
+ };
71
+
72
+ const rendered = renderPrompt(input);
73
+ if (!rendered.ok) {
74
+ // Clean bypass (unknown profile) or a hard order mismatch. Bypass emits the
75
+ // provider-bypassed event and defers to the predecessor prompt path.
76
+ fire("vector_cortex_provider_bypassed", {
77
+ code: rendered.code,
78
+ triad: rendered.triad,
79
+ });
80
+ return {
81
+ validated: false,
82
+ bypassed: true,
83
+ manifest: null,
84
+ systemPromptPrepend: "",
85
+ triad: rendered.triad,
86
+ };
87
+ }
88
+
89
+ // Validate the canonical request BEFORE invocation. The profile at validation
90
+ // time must match the manifest profile (the UNIQUE injection swaps it → C).
91
+ const sourceToolBytes = new Map<string, string>();
92
+ for (const t of rendered.request.tools) sourceToolBytes.set(t.id, t.bytes);
93
+ const pinnedToolLengths = new Map<string, number>();
94
+ for (const t of rendered.request.tools) {
95
+ pinnedToolLengths.set(t.id, Buffer.byteLength(t.bytes, "utf8"));
96
+ }
97
+ const check = validateRender(
98
+ rendered.manifest,
99
+ rendered.request,
100
+ input.profile,
101
+ sourceToolBytes,
102
+ pinnedToolLengths,
103
+ );
104
+
105
+ if (!check.ok) {
106
+ fire("vector_cortex_provider_bypassed", {
107
+ code: check.code,
108
+ triad: check.triad,
109
+ });
110
+ return {
111
+ validated: false,
112
+ bypassed: true,
113
+ manifest: rendered.manifest,
114
+ systemPromptPrepend: "",
115
+ triad: check.triad,
116
+ };
117
+ }
118
+
119
+ fire("vector_cortex_render_validated", {
120
+ profileId: rendered.manifest.profileId,
121
+ requestDigest: rendered.manifest.requestDigest,
122
+ nodeCount: rendered.manifest.nodeOrder.length,
123
+ triad: "A",
124
+ });
125
+ return {
126
+ validated: true,
127
+ bypassed: false,
128
+ manifest: rendered.manifest,
129
+ systemPromptPrepend: rendered.request.systemPromptPrepend,
130
+ triad: "A",
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Register the VC5B `before_agent_start` prepend seam on the host. Flag OFF →
136
+ * byte-identical to the predecessor (returns the host's unmodified systemPrompt).
137
+ * The composed prepend is placed on the HOST systemPrompt, never as role:system.
138
+ */
139
+ export function registerVectorCortexRender(
140
+ pi: ExtensionAPI,
141
+ _ctx: ExtensionContext,
142
+ emit?: VectorCortexEmit,
143
+ ): void {
144
+ pi.on("before_agent_start", (event: BeforeAgentStartEvent, ctx: ExtensionContext) => {
145
+ if (!VC5B_ENABLED()) return; // byte-identical predecessor behavior
146
+ // The host supplies the active (provider, model) so the seam can resolve the
147
+ // profile. Until the host passes a render input, the seam stays inert.
148
+ const pending = (ctx as unknown as { megaCompactVectorCortexRender?: RenderInput }).megaCompactVectorCortexRender;
149
+ if (pending === undefined) return;
150
+ const outcome = runVectorCortexRender(pending, emit);
151
+ if (!outcome.validated) return; // bypass / mismatch → predecessor path
152
+ return { systemPrompt: `${event.systemPrompt}\n\n${outcome.systemPromptPrepend}` };
153
+ });
154
+ }
155
+
156
+ /** Resolve a provider profile by (provider, model) for the host to pass in. */
157
+ export function resolveProfile(provider: string, model: string): ProviderProfileBundle | null {
158
+ const res = resolveProviderProfile(provider, model);
159
+ return res.ok ? res.bundle : null;
160
+ }
161
+
162
+ /** Re-export the core types the host adapter consumes (contract-first). */
163
+ export type { RenderInput, RenderManifestV1, CanonicalOutboundRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.20.4",
3
+ "version": "0.20.5",
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",
@@ -188,6 +188,19 @@ export const VC4C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC4C");
188
188
  */
189
189
  export const VC5A_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5A");
190
190
 
191
+ /**
192
+ * VC5B — validated prompt renderer + provider profiles. Default ON.
193
+ * `MEGACOMPACT_VC5B=0` disables and is byte-identical to the predecessor
194
+ * (VC5A): no render manifest is produced, no canonical request is hashed, the
195
+ * `vector_cortex_render_validated` / `vector_cortex_provider_bypassed` events are
196
+ * never emitted, and the prompt continues to be built by the predecessor VC5A
197
+ * DAG/plan path (its goldens are unchanged). The render/validate functions are
198
+ * PURE — flag OFF gates the reporter + dashboard seam, never the arithmetic.
199
+ * This flag MUST also be a dashboard SETTINGS toggle (visible in config UI, never
200
+ * in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A.
201
+ */
202
+ export const VC5B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_VC5B");
203
+
191
204
  // ---------------------------------------------------------------------------
192
205
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
193
206
  // Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
package/src/config.ts CHANGED
@@ -167,6 +167,7 @@ export {
167
167
  VC4B_ENABLED,
168
168
  VC4C_ENABLED,
169
169
  VC5A_ENABLED,
170
+ VC5B_ENABLED,
170
171
  BREAKER_WINDOW_MS,
171
172
  BREAKER_MIN_ATTEMPTS,
172
173
  BREAKER_PERF_FAILURES,
@@ -82,9 +82,15 @@ export function recordSessionHeartbeat(
82
82
  stateDir: string,
83
83
  ctxWindow: number,
84
84
  indexDir: string = getIndexDir(),
85
+ // Optional clock override for deterministic tests only. Production callers
86
+ // omit it → Date.now(), byte-identical behavior. The S39 prune test pins
87
+ // heartbeats to a known-old timestamp so pruneStaleSessions' Date.now()
88
+ // cutoff unambiguously exceeds them (no same-millisecond last_seen ==
89
+ // cutoff race that made pruned !== 2).
90
+ nowMs: number = Date.now(),
85
91
  ): void {
86
92
  const db = openIndexStore(indexDir);
87
- const now = Date.now();
93
+ const now = nowMs;
88
94
  db.prepare(
89
95
  `INSERT INTO session_heartbeats (pid, session_id, repo_root, state_dir, ctx_window, last_seen)
90
96
  VALUES (@pid, @session_id, @repo_root, @state_dir, @ctx_window, @last_seen)
@@ -127,9 +133,15 @@ export function appendTokenSample(
127
133
  ctxWindow: number,
128
134
  eventsLogPath: string | null,
129
135
  indexDir: string = getIndexDir(),
136
+ // Optional clock override for deterministic tests only. Production callers
137
+ // omit it → falls back to Date.now(), byte-identical behavior. The S39
138
+ // timeseries test injects distinct timestamps so the per-ts totals map is
139
+ // not collapsed by same-millisecond sample coincidence (the pre-existing
140
+ // flake where 3 samples in one ms made totals.length == 1).
141
+ nowMs: number = Date.now(),
130
142
  ): void {
131
143
  const db = openIndexStore(indexDir);
132
- const now = Date.now();
144
+ const now = nowMs;
133
145
  db.prepare(
134
146
  `INSERT INTO token_samples (session_id, repo_root, tokens, percent, ctx_window, ts)
135
147
  VALUES (@session_id, @repo_root, @tokens, @percent, @ctx_window, @ts)`,
@@ -0,0 +1,137 @@
1
+ /**
2
+ * vector-cortex/provider/registry.ts — fixture-backed base provider profile
3
+ * registry (VC5B, task 2).
4
+ *
5
+ * The registry resolves a `(provider, model)` pair to a `ProviderProfileBundle`
6
+ * (CONTRACTS §ProviderProfileV1 + role/tool/cache rules). Unknown provider/model
7
+ * returns `PRO_PROFILE_UNKNOWN` and the renderer CLEANLY bypasses vector-cortex
8
+ * rendering (NOT an error — the host uses the predecessor prompt path).
9
+ *
10
+ * Profiles are fixture-backed: each base profile is declared here as a constant
11
+ * and is also pinned as a conformance fixture under `conformance/vector-cortex/v2/
12
+ * provider/` (registered by `scripts/gen-fixtures/provider.mjs`). The registry is
13
+ * the REAL resolver the acceptance suite drives; the fixtures are the committed
14
+ * canonical corpus that proves each profile's cache-identity behavior.
15
+ *
16
+ * Pi-agnostic, dependency-free, zero network (PREVENT-PI-004 / PREVENT-011).
17
+ */
18
+
19
+ import {
20
+ PRO_PROFILE_UNKNOWN,
21
+ type ProviderProfileBundle,
22
+ type ProviderProfileResult,
23
+ type ProviderProfileV1,
24
+ } from "./types.js";
25
+
26
+ /** A registry key is `provider` + `model`. Lookup is exact-match. */
27
+ function key(provider: string, model: string): string {
28
+ return `${provider}//${model}`;
29
+ }
30
+
31
+ /**
32
+ * The base role rule every VC5B profile shares: the renderer MUST use the host
33
+ * `before_agent_start` prepend seam and MUST NOT inject `role:"system"`
34
+ * (PREVENT-PI-003, critical).
35
+ */
36
+ const BASE_ROLE = {
37
+ useHostPrependSeam: true,
38
+ forbidSystemRoleInjection: true,
39
+ } as const;
40
+
41
+ /**
42
+ * The base tool rule every VC5B profile shares: exact tool bytes preserved
43
+ * (PREVENT-PI-002) and the renderer never reorders away from the validator order.
44
+ */
45
+ const BASE_TOOL = {
46
+ preserveExactToolBytes: true,
47
+ forbidToolReorder: true,
48
+ } as const;
49
+
50
+ /** Build a base cache rule (entire-canonical-request hashing by default). */
51
+ function baseCache(stableFields: readonly string[]): ProviderProfileBundle["cache"] {
52
+ return {
53
+ hashMode: "entire-canonical-request",
54
+ stableFields,
55
+ };
56
+ }
57
+
58
+ /** Build a contract-profile shape pinned as a conformance fixture too. */
59
+ function profile(
60
+ id: string,
61
+ version: string,
62
+ excludedJsonPointers: ProviderProfileV1["excludedJsonPointers"],
63
+ ): ProviderProfileV1 {
64
+ return {
65
+ schema: "provider-profile-v1",
66
+ id,
67
+ version,
68
+ hashMode: "entire-canonical-request",
69
+ excludedJsonPointers,
70
+ };
71
+ }
72
+
73
+ /**
74
+ * The fixture-backed base profiles. Each entry is the REAL bundle the renderer
75
+ * resolves; the parallel conformance fixtures prove the cache-identity behavior.
76
+ */
77
+ const BASE_PROFILES: readonly ProviderProfileBundle[] = [
78
+ {
79
+ profile: profile("anthropic-claude-opus", "v1", []),
80
+ role: BASE_ROLE,
81
+ tool: BASE_TOOL,
82
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
83
+ },
84
+ {
85
+ profile: profile("anthropic-claude-sonnet", "v1", []),
86
+ role: BASE_ROLE,
87
+ tool: BASE_TOOL,
88
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
89
+ },
90
+ {
91
+ profile: profile("openai-gpt", "v1", []),
92
+ role: BASE_ROLE,
93
+ tool: BASE_TOOL,
94
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
95
+ },
96
+ {
97
+ // A profile that proves a fixture-excluded pointer: a provider whose
98
+ // request-id header cannot affect cache identity. Excluded + versioned.
99
+ profile: profile("google-gemini", "v1", [
100
+ {
101
+ pointer: "/requestId",
102
+ fixtureId: "PRO-EXCLUDE-010",
103
+ proofDigest: "sha256:excluded-request-id-proof",
104
+ },
105
+ ]),
106
+ role: BASE_ROLE,
107
+ tool: BASE_TOOL,
108
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
109
+ },
110
+ ];
111
+
112
+ const REGISTRY: ReadonlyMap<string, ProviderProfileBundle> = new Map(
113
+ BASE_PROFILES.map((b) => [key(b.profile.id, b.profile.version), b] as const),
114
+ );
115
+
116
+ /**
117
+ * Resolve a `(provider, model)` pair to a `ProviderProfileBundle`. Unknown
118
+ * provider / model / version → a CLEAN bypass (`PRO_PROFILE_UNKNOWN`), never an
119
+ * error. The renderer consults this and, on `ok:false`, selects triad C (the
120
+ * predecessor prompt path).
121
+ */
122
+ export function resolveProviderProfile(
123
+ provider: string,
124
+ model: string,
125
+ ): ProviderProfileResult {
126
+ const bundle = REGISTRY.get(key(provider, model));
127
+ if (bundle === undefined) {
128
+ return { ok: false, code: PRO_PROFILE_UNKNOWN };
129
+ }
130
+ return { ok: true, bundle };
131
+ }
132
+
133
+ /** The fixture-backed base profiles (exposed for tests / dashboard introspection). */
134
+ export const BASE_PROVIDER_PROFILES: readonly ProviderProfileBundle[] = BASE_PROFILES;
135
+
136
+ /** The set of known `(provider, model)` keys, for conformance registration. */
137
+ export const KNOWN_PROVIDER_KEYS: readonly string[] = Array.from(REGISTRY.keys());