pi-mega-compact 0.20.3 → 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 +29 -22
  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 +31 -22
  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
@@ -172,6 +172,18 @@ export const VC4C_ENABLED = () => sprintFlag("MEGACOMPACT_VC4C");
172
172
  * mirroring VC4A/VC4B/VC4C.
173
173
  */
174
174
  export const VC5A_ENABLED = () => sprintFlag("MEGACOMPACT_VC5A");
175
+ /**
176
+ * VC5B — validated prompt renderer + provider profiles. Default ON.
177
+ * `MEGACOMPACT_VC5B=0` disables and is byte-identical to the predecessor
178
+ * (VC5A): no render manifest is produced, no canonical request is hashed, the
179
+ * `vector_cortex_render_validated` / `vector_cortex_provider_bypassed` events are
180
+ * never emitted, and the prompt continues to be built by the predecessor VC5A
181
+ * DAG/plan path (its goldens are unchanged). The render/validate functions are
182
+ * PURE — flag OFF gates the reporter + dashboard seam, never the arithmetic.
183
+ * This flag MUST also be a dashboard SETTINGS toggle (visible in config UI, never
184
+ * in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A.
185
+ */
186
+ export const VC5B_ENABLED = () => sprintFlag("MEGACOMPACT_VC5B");
175
187
  // ---------------------------------------------------------------------------
176
188
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
177
189
  // Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
package/dist/config.js CHANGED
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
114
114
  // default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
115
115
  // so root consumers share one source of truth.
116
116
  // ---------------------------------------------------------------------------
117
- export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
117
+ export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
@@ -150,6 +150,7 @@ export const SETTINGS = [
150
150
  boolDirect("MEGACOMPACT_VC4B", "VC4B Residual Basis Parity", "Residual codec: orthonormal DCT-II basis + int16 block quantization + block-scoped exact correction stream + (9,6) Reed-Solomon parity shards with SHA-256 corruption detection; admission gates on encodedSize <= 95% of exact-compressed size. OFF = mode C, no residual artifact produced, byte-identical predecessor.", true),
151
151
  boolDirect("MEGACOMPACT_VC4C", "VC4C Reconstruction Fidelity", "Conservative closure + source-order assembly + reconstruction validator: recursively closes dependencies and whole tool pairs to a fixed point, resolves contradictions by retaining the later exact source resolution, assembles spans solely by source range, and rejects missing anchors / split pairs / digest mismatch / unresolved contradiction. Mandatory token estimate is content-only and handed unchanged to VC5A. OFF = mode C, no closure/validator, byte-identical predecessor (VC4B).", true),
152
152
  boolDirect("MEGACOMPACT_VC5A", "VC5A PromptDagV1 + Budgeted Planner", "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).", true),
153
+ boolDirect("MEGACOMPACT_VC5B", "VC5B Validated Renderer + Provider Profiles", "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).", true),
153
154
  ],
154
155
  },
155
156
  {
@@ -0,0 +1,40 @@
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
+ import { VC5B_ENABLED } from "../../src/config.js";
16
+ import { REN_IDS } from "../../src/vector-cortex/render/types.js";
17
+ import { PRO_IDS } from "../../src/vector-cortex/provider/types.js";
18
+ import { KNOWN_PROVIDER_KEYS } from "../../src/vector-cortex/provider/registry.js";
19
+ import { sendJson } from "./routes-vector-cortex-shared.js";
20
+ /** Reader-only GET /api/vector-cortex/render (VC5B). */
21
+ export function handleVectorCortexRender(req, res, _ctx) {
22
+ const url = req.url ?? "";
23
+ const path = url.split("?")[0] ?? url;
24
+ if (path !== "/api/vector-cortex/render")
25
+ return false;
26
+ if (req.method !== "GET") {
27
+ sendJson(res, 405, { error: "method_not_allowed" });
28
+ return true;
29
+ }
30
+ const enabled = VC5B_ENABLED();
31
+ const body = {
32
+ enabled,
33
+ renderCount: enabled ? REN_IDS.length : 0,
34
+ providerCount: enabled ? PRO_IDS.length : 0,
35
+ knownProfiles: enabled ? KNOWN_PROVIDER_KEYS : [],
36
+ updatedAt: new Date().toISOString(),
37
+ };
38
+ sendJson(res, 200, body);
39
+ return true;
40
+ }
@@ -22,3 +22,4 @@ export { handleVectorCortexShards } from "./routes-vector-cortex-shards.js";
22
22
  export { handleVectorCortexResidual } from "./routes-vector-cortex-residual.js";
23
23
  export { handleVectorCortexReconstruct } from "./routes-vector-cortex-reconstruct.js";
24
24
  export { handleVectorCortexPlans } from "./routes-vector-cortex-plans.js";
25
+ export { handleVectorCortexRender } from "./routes-vector-cortex-render.js";
@@ -23,4 +23,4 @@ export { handleRagSettings } from "./routes-rag-settings.js";
23
23
  export { handleRagMetrics } from "./routes-rag-metrics.js";
24
24
  export { handleModelThresholds } from "./routes-model-thresholds.js";
25
25
  export { handleWiki } from "./routes-wiki.js";
26
- export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, } from "./routes-vector-cortex.js";
26
+ export { handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, } from "./routes-vector-cortex.js";
@@ -14,7 +14,7 @@ import { createRequire } from "node:module";
14
14
  import { NEW_UI } from "../../src/config.js";
15
15
  import { log, setLogPath, setDashboardServerVersion } from "./state.js";
16
16
  import { detectTailscaleIP } from "./tailscale.js";
17
- import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handlePerfSamples, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleStatic, } from "./routes.js";
17
+ import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handlePerfSamples, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleRaptorBuildHistory, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleModelThresholds, handleWiki, handleVectorCortexEvaluation, handleVectorCortexHealth, handleVectorCortexBreakersReset, handleVectorCortexLedger, handleVectorCortexTopology, handleVectorCortexQuery, handleVectorCortexShards, handleVectorCortexResidual, handleVectorCortexReconstruct, handleVectorCortexPlans, handleVectorCortexRender, handleStatic, } from "./routes.js";
18
18
  export async function launchDashboardServer(stateDir) {
19
19
  // Our own package version — exposed at /api/version so the launcher can
20
20
  // detect a stale server (started by an older build) and replace it on
@@ -167,14 +167,16 @@ export async function launchDashboardServer(stateDir) {
167
167
  serveClientAsset,
168
168
  detectCrossRepoDrift,
169
169
  });
170
- // Bind host resolution: explicit MEGACOMPACT_DASHBOARD_HOST override wins;
171
- // otherwise auto-bind to the Tailscale interface (tailnet access); fall back
172
- // to loopback when Tailscale is absent. 0.0.0.0 means "all interfaces" and
173
- // opts into permissive CORS below.
174
- const host = process.env.MEGACOMPACT_DASHBOARD_HOST ??
175
- detectTailscaleIP() ??
176
- "127.0.0.1";
170
+ // Bind host resolution. Default: ALL interfaces (0.0.0.0) so the dashboard
171
+ // is reachable on loopback (localhost — the launcher's health check + the
172
+ // local browser), the tailnet (Tailscale), and any LAN interface. The
173
+ // network-denial gate was removed (the user opted into open access);
174
+ // MEGACOMPACT_DASHBOARD_HOST overrides for anyone who wants to restrict to
175
+ // a single interface. The tailnet IP is still detected so we can surface it
176
+ // as the remote access address. 0.0.0.0 opts into permissive CORS below.
177
+ const host = process.env.MEGACOMPACT_DASHBOARD_HOST ?? "0.0.0.0";
177
178
  const allInterfaces = host === "0.0.0.0";
179
+ const tailscaleIP = allInterfaces ? detectTailscaleIP() : null;
178
180
  // guardrails-allow PREVENT-PI-004: optional, user-triggered /dashboard server (tailnet-local via Tailscale mesh or loopback); no remote outbound calls.
179
181
  const server = createServer((req, res) => {
180
182
  // CORS for local access — restricted to loopback/same-origin tailnet
@@ -275,6 +277,8 @@ export async function launchDashboardServer(stateDir) {
275
277
  return;
276
278
  if (handleVectorCortexPlans(req, res, ctx))
277
279
  return;
280
+ if (handleVectorCortexRender(req, res, ctx))
281
+ return;
278
282
  handleStatic(req, res, ctx);
279
283
  });
280
284
  // Bind base + range are env-configurable so tests can use a private,
@@ -297,25 +301,28 @@ export async function launchDashboardServer(stateDir) {
297
301
  }
298
302
  });
299
303
  server.listen(port, host, () => {
300
- // When bound to the loopback, keep the legacy localhost URL so the
301
- // user's browser (which resolves localhost ::1 or 127.0.0.1) works.
302
- // When bound to a tailnet/tailscale IP, surface that address instead.
304
+ // Surface localhost for local reach (the launcher health-checks
305
+ // localhost + the local browser uses it), and when bound to all
306
+ // interfaces with a tailnet present — also surface the tailnet
307
+ // address for remote devices on the mesh.
308
+ const localUrl = `http://localhost:${port}`; // guardrails-allow PREVENT-PI-004: dashboard URL (loopback/all-interfaces, user-opted open access)
309
+ const tailnetUrl = allInterfaces && tailscaleIP ? `http://${tailscaleIP}:${port}` : null; // guardrails-allow PREVENT-PI-004: dashboard URL (tailnet, user-opted open access)
303
310
  const url = host === "127.0.0.1"
304
- ? `http://localhost:${port}`
305
- : `http://${host}:${port}`; // guardrails-allow PREVENT-PI-004: dashboard URL (tailnet-local via Tailscale mesh or loopback)
306
- log("server running", { url, host });
311
+ ? localUrl
312
+ : tailnetUrl ?? `http://${host}:${port}`;
313
+ log("server running", { url, host, tailnetUrl });
307
314
  // eslint-disable-next-line no-console
308
- console.log(`[mega-compact] dashboard server running: ${url}`);
309
- // v0.8.2: also bind the IPv6 loopback (::1) when bound to the IPv4
310
- // loopback. On many systems `localhost` resolves to ::1 first (see
311
- // /etc/hosts), so an IPv4-only bind makes the browser hit ::1:port and
312
- // get connection refused. PREVENT-PI-004 (loopback-only) means BOTH
313
- // 127.0.0.1 and ::1. When bound to a tailscale/0.0.0.0 host, we do NOT
314
- // bind ::1 the tailnet address is what the user reaches. Non-fatal:
315
+ console.log(`[mega-compact] dashboard server running: ${url}` +
316
+ (tailnetUrl ? ` (also ${localUrl})` : ""));
317
+ // v0.8.2: also bind the IPv6 loopback (::1) when localhost must work.
318
+ // On many systems `localhost` resolves to ::1 first (see /etc/hosts),
319
+ // so an IPv4-only bind makes the browser hit ::1:port and get refused.
320
+ // Bind ::1 for the explicit loopback bind AND the all-interfaces bind
321
+ // (a browser resolving localhost ::1 must still connect). Non-fatal:
315
322
  // IPv4-only hosts or a ::1 already in use just skip the mirror.
316
323
  let v6;
317
324
  const v4Handler = server.listeners("request")[0];
318
- if (host === "127.0.0.1" && v4Handler) {
325
+ if ((host === "127.0.0.1" || allInterfaces) && v4Handler) {
319
326
  v6 = createServer((r, s) => v4Handler.call(server, r, s));
320
327
  v6.on("error", (e) => log("ipv6 loopback bind skipped", {
321
328
  port,
@@ -0,0 +1,128 @@
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
+ import { VC5B_ENABLED } from "../../src/config/vector-cortex.js";
28
+ import { resolveProviderProfile } from "../../src/vector-cortex/provider/registry.js";
29
+ import { renderPrompt } from "../../src/vector-cortex/render/renderer.js";
30
+ import { validateRender } from "../../src/vector-cortex/render/validator.js";
31
+ /**
32
+ * Run the full VC5B render+validate pipeline. Returns a structured outcome the
33
+ * `before_agent_start` handler composes into the host systemPrompt. On a clean
34
+ * bypass (unknown profile) it emits `vector_cortex_provider_bypassed` and the
35
+ * host uses the predecessor prompt path (triad C). On a validated render it
36
+ * emits `vector_cortex_render_validated` (triad A).
37
+ *
38
+ * All emits are best-effort and non-fatal; the flag gates zero observability
39
+ * writes when OFF (byte-identical to predecessor).
40
+ */
41
+ export function runVectorCortexRender(input, emit) {
42
+ const fire = (event, fields) => {
43
+ try {
44
+ if (VC5B_ENABLED())
45
+ emit?.(event, fields);
46
+ }
47
+ catch {
48
+ /* non-fatal observability — never break the agent loop */
49
+ }
50
+ };
51
+ const rendered = renderPrompt(input);
52
+ if (!rendered.ok) {
53
+ // Clean bypass (unknown profile) or a hard order mismatch. Bypass emits the
54
+ // provider-bypassed event and defers to the predecessor prompt path.
55
+ fire("vector_cortex_provider_bypassed", {
56
+ code: rendered.code,
57
+ triad: rendered.triad,
58
+ });
59
+ return {
60
+ validated: false,
61
+ bypassed: true,
62
+ manifest: null,
63
+ systemPromptPrepend: "",
64
+ triad: rendered.triad,
65
+ };
66
+ }
67
+ // Validate the canonical request BEFORE invocation. The profile at validation
68
+ // time must match the manifest profile (the UNIQUE injection swaps it → C).
69
+ const sourceToolBytes = new Map();
70
+ for (const t of rendered.request.tools)
71
+ sourceToolBytes.set(t.id, t.bytes);
72
+ const pinnedToolLengths = new Map();
73
+ for (const t of rendered.request.tools) {
74
+ pinnedToolLengths.set(t.id, Buffer.byteLength(t.bytes, "utf8"));
75
+ }
76
+ const check = validateRender(rendered.manifest, rendered.request, input.profile, sourceToolBytes, pinnedToolLengths);
77
+ if (!check.ok) {
78
+ fire("vector_cortex_provider_bypassed", {
79
+ code: check.code,
80
+ triad: check.triad,
81
+ });
82
+ return {
83
+ validated: false,
84
+ bypassed: true,
85
+ manifest: rendered.manifest,
86
+ systemPromptPrepend: "",
87
+ triad: check.triad,
88
+ };
89
+ }
90
+ fire("vector_cortex_render_validated", {
91
+ profileId: rendered.manifest.profileId,
92
+ requestDigest: rendered.manifest.requestDigest,
93
+ nodeCount: rendered.manifest.nodeOrder.length,
94
+ triad: "A",
95
+ });
96
+ return {
97
+ validated: true,
98
+ bypassed: false,
99
+ manifest: rendered.manifest,
100
+ systemPromptPrepend: rendered.request.systemPromptPrepend,
101
+ triad: "A",
102
+ };
103
+ }
104
+ /**
105
+ * Register the VC5B `before_agent_start` prepend seam on the host. Flag OFF →
106
+ * byte-identical to the predecessor (returns the host's unmodified systemPrompt).
107
+ * The composed prepend is placed on the HOST systemPrompt, never as role:system.
108
+ */
109
+ export function registerVectorCortexRender(pi, _ctx, emit) {
110
+ pi.on("before_agent_start", (event, ctx) => {
111
+ if (!VC5B_ENABLED())
112
+ return; // byte-identical predecessor behavior
113
+ // The host supplies the active (provider, model) so the seam can resolve the
114
+ // profile. Until the host passes a render input, the seam stays inert.
115
+ const pending = ctx.megaCompactVectorCortexRender;
116
+ if (pending === undefined)
117
+ return;
118
+ const outcome = runVectorCortexRender(pending, emit);
119
+ if (!outcome.validated)
120
+ return; // bypass / mismatch → predecessor path
121
+ return { systemPrompt: `${event.systemPrompt}\n\n${outcome.systemPromptPrepend}` };
122
+ });
123
+ }
124
+ /** Resolve a provider profile by (provider, model) for the host to pass in. */
125
+ export function resolveProfile(provider, model) {
126
+ const res = resolveProviderProfile(provider, model);
127
+ return res.ok ? res.bundle : null;
128
+ }
@@ -172,6 +172,18 @@ export const VC4C_ENABLED = () => sprintFlag("MEGACOMPACT_VC4C");
172
172
  * mirroring VC4A/VC4B/VC4C.
173
173
  */
174
174
  export const VC5A_ENABLED = () => sprintFlag("MEGACOMPACT_VC5A");
175
+ /**
176
+ * VC5B — validated prompt renderer + provider profiles. Default ON.
177
+ * `MEGACOMPACT_VC5B=0` disables and is byte-identical to the predecessor
178
+ * (VC5A): no render manifest is produced, no canonical request is hashed, the
179
+ * `vector_cortex_render_validated` / `vector_cortex_provider_bypassed` events are
180
+ * never emitted, and the prompt continues to be built by the predecessor VC5A
181
+ * DAG/plan path (its goldens are unchanged). The render/validate functions are
182
+ * PURE — flag OFF gates the reporter + dashboard seam, never the arithmetic.
183
+ * This flag MUST also be a dashboard SETTINGS toggle (visible in config UI, never
184
+ * in EXCLUDED_SETTINGS), mirroring VC4A/VC4B/VC4C/VC5A.
185
+ */
186
+ export const VC5B_ENABLED = () => sprintFlag("MEGACOMPACT_VC5B");
175
187
  // ---------------------------------------------------------------------------
176
188
  // Breaker state machine constants (TRIAD_RESILIENCE.md §breaker).
177
189
  // Rolled numbers for one 60s window; VC0C consumes these at its breaker seam.
@@ -114,4 +114,4 @@ export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
114
114
  // default ON, `=0`/`_DISABLED` off. Re-exported from src/config/vector-cortex.ts
115
115
  // so root consumers share one source of truth.
116
116
  // ---------------------------------------------------------------------------
117
- export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
117
+ export { VC0A_ENABLED, VC0B_ENABLED, VC1A_ENABLED, VC0C_ENABLED, VC1B_ENABLED, VC1C_ENABLED, VC2A_ENABLED, VC2B_ENABLED, VC2C_ENABLED, VC3A_ENABLED, VC3B_ENABLED, VC3C_ENABLED, VC4A_ENABLED, VC4B_ENABLED, VC4C_ENABLED, VC5A_ENABLED, VC5B_ENABLED, BREAKER_WINDOW_MS, BREAKER_MIN_ATTEMPTS, BREAKER_PERF_FAILURES, BREAKER_PERF_FAILURE_RATE, BREAKER_CORRECTNESS_FAILURES, BREAKER_COOLDOWN_MS, BREAKER_PROBE_COUNT, BREAKER_RETRY_BASE_MS, BREAKER_RETRY_CAP_MS, BREAKER_RETRY_JITTER, BREAKER_HYSTERESIS_FAILURE_RATE, BREAKER_HYSTERESIS_BUDGET_P95_MS, BREAKER_MIN_HEALTHY_RESIDENCE_MS, } from "./config/vector-cortex.js";
@@ -39,9 +39,15 @@ function sessionColor(sessionId) {
39
39
  * change. PRIMARY KEY (pid, session_id) means concurrent pi processes each get
40
40
  * their own row. Non-fatal on conflict (INSERT ... ON CONFLICT DO UPDATE).
41
41
  */
42
- export function recordSessionHeartbeat(pid, sessionId, repoRoot, stateDir, ctxWindow, indexDir = getIndexDir()) {
42
+ export function recordSessionHeartbeat(pid, sessionId, repoRoot, stateDir, ctxWindow, indexDir = getIndexDir(),
43
+ // Optional clock override for deterministic tests only. Production callers
44
+ // omit it → Date.now(), byte-identical behavior. The S39 prune test pins
45
+ // heartbeats to a known-old timestamp so pruneStaleSessions' Date.now()
46
+ // cutoff unambiguously exceeds them (no same-millisecond last_seen ==
47
+ // cutoff race that made pruned !== 2).
48
+ nowMs = Date.now()) {
43
49
  const db = openIndexStore(indexDir);
44
- const now = Date.now();
50
+ const now = nowMs;
45
51
  db.prepare(`INSERT INTO session_heartbeats (pid, session_id, repo_root, state_dir, ctx_window, last_seen)
46
52
  VALUES (@pid, @session_id, @repo_root, @state_dir, @ctx_window, @last_seen)
47
53
  ON CONFLICT(pid, session_id) DO UPDATE SET
@@ -71,9 +77,15 @@ export function recordSessionHeartbeat(pid, sessionId, repoRoot, stateDir, ctxWi
71
77
  * (for SSE real-time push via /api/events). The eventsLogPath is optional —
72
78
  * callers without an events.log (e.g. tests) can omit it.
73
79
  */
74
- export function appendTokenSample(sessionId, repoRoot, tokens, percent, ctxWindow, eventsLogPath, indexDir = getIndexDir()) {
80
+ export function appendTokenSample(sessionId, repoRoot, tokens, percent, ctxWindow, eventsLogPath, indexDir = getIndexDir(),
81
+ // Optional clock override for deterministic tests only. Production callers
82
+ // omit it → falls back to Date.now(), byte-identical behavior. The S39
83
+ // timeseries test injects distinct timestamps so the per-ts totals map is
84
+ // not collapsed by same-millisecond sample coincidence (the pre-existing
85
+ // flake where 3 samples in one ms made totals.length == 1).
86
+ nowMs = Date.now()) {
75
87
  const db = openIndexStore(indexDir);
76
- const now = Date.now();
88
+ const now = nowMs;
77
89
  db.prepare(`INSERT INTO token_samples (session_id, repo_root, tokens, percent, ctx_window, ts)
78
90
  VALUES (@session_id, @repo_root, @tokens, @percent, @ctx_window, @ts)`).run({
79
91
  session_id: sessionId,
@@ -0,0 +1,112 @@
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
+ import { PRO_PROFILE_UNKNOWN, } from "./types.js";
19
+ /** A registry key is `provider` + `model`. Lookup is exact-match. */
20
+ function key(provider, model) {
21
+ return `${provider}//${model}`;
22
+ }
23
+ /**
24
+ * The base role rule every VC5B profile shares: the renderer MUST use the host
25
+ * `before_agent_start` prepend seam and MUST NOT inject `role:"system"`
26
+ * (PREVENT-PI-003, critical).
27
+ */
28
+ const BASE_ROLE = {
29
+ useHostPrependSeam: true,
30
+ forbidSystemRoleInjection: true,
31
+ };
32
+ /**
33
+ * The base tool rule every VC5B profile shares: exact tool bytes preserved
34
+ * (PREVENT-PI-002) and the renderer never reorders away from the validator order.
35
+ */
36
+ const BASE_TOOL = {
37
+ preserveExactToolBytes: true,
38
+ forbidToolReorder: true,
39
+ };
40
+ /** Build a base cache rule (entire-canonical-request hashing by default). */
41
+ function baseCache(stableFields) {
42
+ return {
43
+ hashMode: "entire-canonical-request",
44
+ stableFields,
45
+ };
46
+ }
47
+ /** Build a contract-profile shape pinned as a conformance fixture too. */
48
+ function profile(id, version, excludedJsonPointers) {
49
+ return {
50
+ schema: "provider-profile-v1",
51
+ id,
52
+ version,
53
+ hashMode: "entire-canonical-request",
54
+ excludedJsonPointers,
55
+ };
56
+ }
57
+ /**
58
+ * The fixture-backed base profiles. Each entry is the REAL bundle the renderer
59
+ * resolves; the parallel conformance fixtures prove the cache-identity behavior.
60
+ */
61
+ const BASE_PROFILES = [
62
+ {
63
+ profile: profile("anthropic-claude-opus", "v1", []),
64
+ role: BASE_ROLE,
65
+ tool: BASE_TOOL,
66
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
67
+ },
68
+ {
69
+ profile: profile("anthropic-claude-sonnet", "v1", []),
70
+ role: BASE_ROLE,
71
+ tool: BASE_TOOL,
72
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
73
+ },
74
+ {
75
+ profile: profile("openai-gpt", "v1", []),
76
+ role: BASE_ROLE,
77
+ tool: BASE_TOOL,
78
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
79
+ },
80
+ {
81
+ // A profile that proves a fixture-excluded pointer: a provider whose
82
+ // request-id header cannot affect cache identity. Excluded + versioned.
83
+ profile: profile("google-gemini", "v1", [
84
+ {
85
+ pointer: "/requestId",
86
+ fixtureId: "PRO-EXCLUDE-010",
87
+ proofDigest: "sha256:excluded-request-id-proof",
88
+ },
89
+ ]),
90
+ role: BASE_ROLE,
91
+ tool: BASE_TOOL,
92
+ cache: baseCache(["systemPromptPrepend", "tools", "nodes"]),
93
+ },
94
+ ];
95
+ const REGISTRY = new Map(BASE_PROFILES.map((b) => [key(b.profile.id, b.profile.version), b]));
96
+ /**
97
+ * Resolve a `(provider, model)` pair to a `ProviderProfileBundle`. Unknown
98
+ * provider / model / version → a CLEAN bypass (`PRO_PROFILE_UNKNOWN`), never an
99
+ * error. The renderer consults this and, on `ok:false`, selects triad C (the
100
+ * predecessor prompt path).
101
+ */
102
+ export function resolveProviderProfile(provider, model) {
103
+ const bundle = REGISTRY.get(key(provider, model));
104
+ if (bundle === undefined) {
105
+ return { ok: false, code: PRO_PROFILE_UNKNOWN };
106
+ }
107
+ return { ok: true, bundle };
108
+ }
109
+ /** The fixture-backed base profiles (exposed for tests / dashboard introspection). */
110
+ export const BASE_PROVIDER_PROFILES = BASE_PROFILES;
111
+ /** The set of known `(provider, model)` keys, for conformance registration. */
112
+ export const KNOWN_PROVIDER_KEYS = Array.from(REGISTRY.keys());
@@ -0,0 +1,30 @@
1
+ /**
2
+ * vector-cortex/provider/types.ts — provider profile contract (VC5B, task 1).
3
+ *
4
+ * A `ProviderProfileV1` describes how a given provider/model caches and what the
5
+ * renderer may or may not fold into the outbound prompt without breaking the
6
+ * provider's KV-cache identity (CONTRACTS §ProviderProfileV1 + crystals).
7
+ *
8
+ * The renderer hashes the ENTIRE canonical outbound request by default
9
+ * (`hashMode: "entire-canonical-request"`). A profile may EXCLUDE a field only
10
+ * when a fixture proves that field cannot affect provider cache identity; every
11
+ * exclusion is versioned and listed in the manifest. Unknown provider / profile /
12
+ * version → the renderer bypasses vector-cortex rendering (a clean bypass, NOT an
13
+ * error) and the host falls back to the predecessor prompt path.
14
+ *
15
+ * Pure types + registered conformance IDs: no storage, no console, no network
16
+ * (PREVENT-PI-004 / PREVENT-011).
17
+ */
18
+ /**
19
+ * The sentinel returned for an unknown provider/model. The renderer sees this as
20
+ * a CLEAN bypass decision, not an error, and emits `vector_cortex_provider_bypassed`.
21
+ */
22
+ export const PRO_PROFILE_UNKNOWN = "PRO_PROFILE_UNKNOWN";
23
+ /**
24
+ * Registered PRO conformance ID range (PRO-001..015). The acceptance aggregator
25
+ * reads these rows from the v2 manifest and asserts each returns its manifest
26
+ * `ok`/`code` / pinned `requestDigest`.
27
+ */
28
+ export const PRO_IDS = Array.from({ length: 15 }, (_v, i) => `PRO-${String(i + 1).padStart(3, "0")}`);
29
+ /** Named VC5B provider conformance assertions (the sprint's headline rows). */
30
+ export const PRO_NAMED_IDS = ["PRO-UNKNOWN-003"];
@@ -0,0 +1,58 @@
1
+ /**
2
+ * render/_acceptance-fixture.ts — conformance fixture I/O for VC5B render +
3
+ * provider acceptance rows.
4
+ *
5
+ * Reads the v2 conformance manifest + the per-sprint fixture files, and exposes
6
+ * the `RenderFx` / `ProviderFx` shapes the acceptance aggregator drives. No
7
+ * mocks/stubs: the fixtures are the committed canonical corpus.
8
+ */
9
+ import { readFileSync, existsSync } from "node:fs";
10
+ import { join, dirname } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import assert from "node:assert/strict";
13
+ const HERE = dirname(fileURLToPath(import.meta.url));
14
+ function repoRoot(from) {
15
+ let dir = from;
16
+ for (let i = 0; i < 8; i++) {
17
+ if (existsSync(join(dir, "conformance", "vector-cortex")))
18
+ return dir;
19
+ const next = dirname(dir);
20
+ if (next === dir)
21
+ break;
22
+ dir = next;
23
+ }
24
+ throw new Error("conformance corpus not found above " + from);
25
+ }
26
+ export const REPO_ROOT = repoRoot(HERE);
27
+ export const V2 = join(REPO_ROOT, "conformance", "vector-cortex", "v2");
28
+ export function readManifest() {
29
+ return JSON.parse(readFileSync(join(V2, "manifest.json"), "utf8"));
30
+ }
31
+ function readFixture(id, prefix) {
32
+ const m = readManifest();
33
+ const row = m.fixtures.find((f) => f.id === id && f.path.startsWith(`${prefix}/`));
34
+ assert.ok(row, `fixture ${id} registered under ${prefix}/ in manifest`);
35
+ return JSON.parse(readFileSync(join(V2, row.path), "utf8"));
36
+ }
37
+ export function renderFixture(id) {
38
+ return readFixture(id, "render");
39
+ }
40
+ export function providerFixture(id) {
41
+ return readFixture(id, "provider");
42
+ }
43
+ /** Flag-pinned wrapper: VC5B gated by MEGACOMPACT_VC5B (defaults ON). */
44
+ export function withFlagsOn(fn) {
45
+ return () => {
46
+ const saved = process.env.MEGACOMPACT_VC5B;
47
+ process.env.MEGACOMPACT_VC5B = "1";
48
+ try {
49
+ fn();
50
+ }
51
+ finally {
52
+ if (saved === undefined)
53
+ delete process.env.MEGACOMPACT_VC5B;
54
+ else
55
+ process.env.MEGACOMPACT_VC5B = saved;
56
+ }
57
+ };
58
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * render/_acceptance-helpers.ts — barrel re-export for the VC5B render + provider
3
+ * acceptance rows.
4
+ *
5
+ * The implementation is split across focused sibling files to keep each under
6
+ * the src/ 500-line hard limit; this file is the single import surface the
7
+ * acceptance aggregator uses:
8
+ * - `_acceptance-fixture.ts` — manifest I/O + RenderFx/ProviderFx shapes + withFlagsOn
9
+ * - `_acceptance-scenario.ts` — declarative graph materialization + runRenderScenario
10
+ * - `_acceptance-provider.ts` — provider-registry runner + KNOWN_KEYS
11
+ *
12
+ * Every function drives the REAL render / validate / provider logic — no mocks,
13
+ * no stubs.
14
+ */
15
+ export { REPO_ROOT, V2, readManifest, renderFixture, providerFixture, withFlagsOn, } from "./_acceptance-fixture.js";
16
+ export { materializeGraph, runRenderScenario } from "./_acceptance-scenario.js";
17
+ export { runProviderScenario, KNOWN_KEYS } from "./_acceptance-provider.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * render/_acceptance-provider.ts — the REAL provider-registry runner for VC5B
3
+ * provider acceptance rows. Drives `resolveProviderProfile` (no mocks).
4
+ */
5
+ import { resolveProviderProfile, KNOWN_PROVIDER_KEYS } from "../provider/registry.js";
6
+ /** Run a provider fixture through the REAL registry resolver. */
7
+ export function runProviderScenario(fx) {
8
+ const r = resolveProviderProfile(fx.input.provider, fx.input.model);
9
+ if (!r.ok) {
10
+ return { ok: false, code: r.code, bypassClean: true };
11
+ }
12
+ const p = r.bundle.profile;
13
+ const cacheStable = fx.input.scenario === "resolve-excluded-cache" &&
14
+ p.excludedJsonPointers.length === 1 &&
15
+ p.excludedJsonPointers[0].pointer === "/requestId";
16
+ // deterministic: a second identical lookup returns the same profile id.
17
+ const r2 = resolveProviderProfile(fx.input.provider, fx.input.model);
18
+ const deterministic = r2.ok && r2.bundle.profile.id === p.id;
19
+ return {
20
+ ok: true,
21
+ profileId: p.id,
22
+ hashMode: p.hashMode,
23
+ excludedPointers: p.excludedJsonPointers.map((e) => e.pointer),
24
+ cacheStable,
25
+ deterministic,
26
+ };
27
+ }
28
+ /** Exposed for the acceptance registration check. */
29
+ export const KNOWN_KEYS = KNOWN_PROVIDER_KEYS;