pi-mega-compact 0.20.23 → 0.20.25

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 (138) hide show
  1. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +2 -0
  2. package/dist/extensions/dashboard-server/routes-vector-cortex-crystals.js +6 -0
  3. package/dist/extensions/dashboard-server/routes-vector-cortex-diagnostics.js +6 -0
  4. package/dist/extensions/dashboard-server/routes-vector-cortex-economics.js +6 -0
  5. package/dist/extensions/dashboard-server/routes-vector-cortex-eval.js +2 -0
  6. package/dist/extensions/dashboard-server/routes-vector-cortex-heal.js +9 -0
  7. package/dist/extensions/dashboard-server/routes-vector-cortex-health.js +4 -0
  8. package/dist/extensions/dashboard-server/routes-vector-cortex-ledger.js +2 -0
  9. package/dist/extensions/dashboard-server/routes-vector-cortex-outcomes.js +10 -2
  10. package/dist/extensions/dashboard-server/routes-vector-cortex-plans.js +2 -0
  11. package/dist/extensions/dashboard-server/routes-vector-cortex-platform.js +13 -2
  12. package/dist/extensions/dashboard-server/routes-vector-cortex-policy.js +6 -0
  13. package/dist/extensions/dashboard-server/routes-vector-cortex-query.js +2 -0
  14. package/dist/extensions/dashboard-server/routes-vector-cortex-reconstruct.js +5 -0
  15. package/dist/extensions/dashboard-server/routes-vector-cortex-render.js +2 -0
  16. package/dist/extensions/dashboard-server/routes-vector-cortex-repair.js +5 -0
  17. package/dist/extensions/dashboard-server/routes-vector-cortex-residual.js +2 -0
  18. package/dist/extensions/dashboard-server/routes-vector-cortex-rollout.js +56 -3
  19. package/dist/extensions/dashboard-server/routes-vector-cortex-shards.js +5 -0
  20. package/dist/extensions/dashboard-server/routes-vector-cortex-topology.js +2 -0
  21. package/dist/extensions/dashboard-server/vc-status.js +17 -0
  22. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +15 -4
  23. package/dist/extensions/mega-events/context-handler/afterCompact.js +25 -6
  24. package/dist/extensions/mega-events/context-handler/dbMirrorAppend.js +35 -7
  25. package/dist/extensions/mega-events/context-handler/pipelineRun.js +17 -2
  26. package/dist/extensions/mega-events/context-handler/tailResult.js +45 -12
  27. package/dist/extensions/mega-events/register.js +17 -0
  28. package/dist/extensions/mega-pipeline/recall.js +11 -0
  29. package/dist/src/cache-stripe-impl.js +52 -214
  30. package/dist/src/cache-stripe-score.js +115 -0
  31. package/extensions/dashboard-client/dist/assets/{AreaChart-BzEedilo.js → AreaChart-DRErPez1.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{AreaChart-BzEedilo.js.map → AreaChart-DRErPez1.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/{BarChart-CzAVg1_J.js → BarChart-Cwxel3M7.js} +2 -2
  34. package/extensions/dashboard-client/dist/assets/{BarChart-CzAVg1_J.js.map → BarChart-Cwxel3M7.js.map} +1 -1
  35. package/extensions/dashboard-client/dist/assets/{CacheTab-D2-rzTuX.js → CacheTab-D4j3vwFu.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{CacheTab-D2-rzTuX.js.map → CacheTab-D4j3vwFu.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{EventsTab-DB7g1Szy.js → EventsTab-BA7t_fsH.js} +2 -2
  38. package/extensions/dashboard-client/dist/assets/{EventsTab-DB7g1Szy.js.map → EventsTab-BA7t_fsH.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/{HealthTab-Cvkmi-R3.js → HealthTab-Ci9gtYDP.js} +2 -2
  40. package/extensions/dashboard-client/dist/assets/{HealthTab-Cvkmi-R3.js.map → HealthTab-Ci9gtYDP.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-g_udcOO7.js → MaintenanceTab-BeqWn-xu.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-g_udcOO7.js.map → MaintenanceTab-BeqWn-xu.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DAjBaf8P.js → MemoryMapTab-BreZegCV.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DAjBaf8P.js.map → MemoryMapTab-BreZegCV.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{MetricsTab-13dYYocB.js → MetricsTab-DLbgfJT2.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{MetricsTab-13dYYocB.js.map → MetricsTab-DLbgfJT2.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{OverviewTab-CLpCyLvx.js → OverviewTab-BtcXdrnC.js} +2 -2
  48. package/extensions/dashboard-client/dist/assets/{OverviewTab-CLpCyLvx.js.map → OverviewTab-BtcXdrnC.js.map} +1 -1
  49. package/extensions/dashboard-client/dist/assets/{ReposTab-EdAqUrwG.js → ReposTab-DpDLEzV9.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{ReposTab-EdAqUrwG.js.map → ReposTab-DpDLEzV9.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/{SessionsTab-C21ZuOL_.js → SessionsTab-CMe5jSrZ.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{SessionsTab-C21ZuOL_.js.map → SessionsTab-CMe5jSrZ.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{SetupTab-CP7lE3L_.js → SetupTab-C3OY8mc7.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{SetupTab-CP7lE3L_.js.map → SetupTab-C3OY8mc7.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DZ3QR8qt.js → TimeSavedCard-BFPJUQzs.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DZ3QR8qt.js.map → TimeSavedCard-BFPJUQzs.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/{TurnsTab-CNchE5rD.js → TurnsTab-Ujt29pKI.js} +2 -2
  58. package/extensions/dashboard-client/dist/assets/{TurnsTab-CNchE5rD.js.map → TurnsTab-Ujt29pKI.js.map} +1 -1
  59. package/extensions/dashboard-client/dist/assets/VectorCortexTab-CfxZW91A.js +2 -0
  60. package/extensions/dashboard-client/dist/assets/VectorCortexTab-CfxZW91A.js.map +1 -0
  61. package/extensions/dashboard-client/dist/assets/{WikiTab-BIe5Z9Nc.js → WikiTab-BiKXVwGT.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{WikiTab-BIe5Z9Nc.js.map → WikiTab-BiKXVwGT.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{button-BrVreSt5.js → button-DJwGcw0u.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{button-BrVreSt5.js.map → button-DJwGcw0u.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{card-D9F4wbqs.js → card-CgjBf5LZ.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{card-D9F4wbqs.js.map → card-CgjBf5LZ.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-mkdzCnMP.js → generateCategoricalChart-CTQIl2qs.js} +2 -2
  68. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-mkdzCnMP.js.map → generateCategoricalChart-CTQIl2qs.js.map} +1 -1
  69. package/extensions/dashboard-client/dist/assets/{index-CtlmOlj1.js → index-Bu8CZUMX.js} +3 -3
  70. package/extensions/dashboard-client/dist/assets/{index-CtlmOlj1.js.map → index-Bu8CZUMX.js.map} +1 -1
  71. package/extensions/dashboard-client/dist/assets/{switch-BXmp5iwA.js → switch-uUiS1Raq.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{switch-BXmp5iwA.js.map → switch-uUiS1Raq.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/{toggle-BgawNwW9.js → toggle-Dv3OEscH.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{toggle-BgawNwW9.js.map → toggle-Dv3OEscH.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/assets/{useSSE-DZKX73Ea.js → useSSE-n6ymAic3.js} +2 -2
  76. package/extensions/dashboard-client/dist/assets/{useSSE-DZKX73Ea.js.map → useSSE-n6ymAic3.js.map} +1 -1
  77. package/extensions/dashboard-client/dist/index.html +1 -1
  78. package/extensions/dashboard-client/src/tabs/VcStatusBadge.tsx +24 -0
  79. package/extensions/dashboard-client/src/tabs/VectorCortexClosureCard.tsx +12 -6
  80. package/extensions/dashboard-client/src/tabs/VectorCortexCrystalsCard.tsx +12 -6
  81. package/extensions/dashboard-client/src/tabs/VectorCortexDiagnosticsCard.tsx +12 -6
  82. package/extensions/dashboard-client/src/tabs/VectorCortexEconomicsCard.tsx +12 -6
  83. package/extensions/dashboard-client/src/tabs/VectorCortexLedgerCard.tsx +12 -6
  84. package/extensions/dashboard-client/src/tabs/VectorCortexOutcomesCard.tsx +12 -6
  85. package/extensions/dashboard-client/src/tabs/VectorCortexPlansCard.tsx +12 -6
  86. package/extensions/dashboard-client/src/tabs/VectorCortexPlatformCard.tsx +12 -6
  87. package/extensions/dashboard-client/src/tabs/VectorCortexPolicyCard.tsx +12 -6
  88. package/extensions/dashboard-client/src/tabs/VectorCortexRenderCard.tsx +12 -6
  89. package/extensions/dashboard-client/src/tabs/VectorCortexRepairCard.tsx +12 -6
  90. package/extensions/dashboard-client/src/tabs/VectorCortexRestoreCard.tsx +12 -6
  91. package/extensions/dashboard-client/src/tabs/VectorCortexRolloutCard.tsx +12 -6
  92. package/extensions/dashboard-client/src/tabs/VectorCortexShardsCard.tsx +23 -11
  93. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +5 -7
  94. package/extensions/dashboard-client/src/tabs/VectorCortexTopologyCard.tsx +28 -14
  95. package/extensions/dashboard-client/src/types/vector-cortex-vc7.ts +6 -0
  96. package/extensions/dashboard-client/src/types/vector-cortex-vc8.ts +6 -0
  97. package/extensions/dashboard-client/src/types/vector-cortex.ts +26 -0
  98. package/extensions/dashboard-server/api-contracts/vector-cortex-cache.ts +2 -0
  99. package/extensions/dashboard-server/api-contracts/vector-cortex-diagnostics.ts +2 -0
  100. package/extensions/dashboard-server/api-contracts/vector-cortex-economics.ts +2 -0
  101. package/extensions/dashboard-server/api-contracts/vector-cortex-heal.ts +6 -0
  102. package/extensions/dashboard-server/api-contracts/vector-cortex-outcomes.ts +2 -0
  103. package/extensions/dashboard-server/api-contracts/vector-cortex-platform.ts +2 -0
  104. package/extensions/dashboard-server/api-contracts/vector-cortex-policy.ts +2 -0
  105. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +19 -0
  106. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +12 -0
  107. package/extensions/dashboard-server/routes-vector-cortex-crystals.ts +6 -0
  108. package/extensions/dashboard-server/routes-vector-cortex-diagnostics.ts +6 -0
  109. package/extensions/dashboard-server/routes-vector-cortex-economics.ts +6 -0
  110. package/extensions/dashboard-server/routes-vector-cortex-eval.ts +2 -0
  111. package/extensions/dashboard-server/routes-vector-cortex-heal.ts +9 -0
  112. package/extensions/dashboard-server/routes-vector-cortex-health.ts +4 -0
  113. package/extensions/dashboard-server/routes-vector-cortex-ledger.ts +2 -0
  114. package/extensions/dashboard-server/routes-vector-cortex-outcomes.ts +11 -2
  115. package/extensions/dashboard-server/routes-vector-cortex-plans.ts +2 -0
  116. package/extensions/dashboard-server/routes-vector-cortex-platform.ts +14 -2
  117. package/extensions/dashboard-server/routes-vector-cortex-policy.ts +6 -0
  118. package/extensions/dashboard-server/routes-vector-cortex-query.ts +2 -0
  119. package/extensions/dashboard-server/routes-vector-cortex-reconstruct.ts +5 -0
  120. package/extensions/dashboard-server/routes-vector-cortex-render.ts +2 -0
  121. package/extensions/dashboard-server/routes-vector-cortex-repair.ts +5 -0
  122. package/extensions/dashboard-server/routes-vector-cortex-residual.ts +2 -0
  123. package/extensions/dashboard-server/routes-vector-cortex-rollout.ts +58 -3
  124. package/extensions/dashboard-server/routes-vector-cortex-shards.ts +5 -0
  125. package/extensions/dashboard-server/routes-vector-cortex-topology.ts +2 -0
  126. package/extensions/dashboard-server/vc-status.ts +22 -0
  127. package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +13 -4
  128. package/extensions/mega-events/context-handler/afterCompact.ts +25 -6
  129. package/extensions/mega-events/context-handler/dbMirrorAppend.ts +39 -7
  130. package/extensions/mega-events/context-handler/pipelineRun.ts +18 -3
  131. package/extensions/mega-events/context-handler/tailResult.ts +56 -15
  132. package/extensions/mega-events/register.ts +14 -1
  133. package/extensions/mega-pipeline/recall.ts +10 -0
  134. package/package.json +1 -1
  135. package/src/cache-stripe-impl.ts +73 -277
  136. package/src/cache-stripe-score.ts +170 -0
  137. package/extensions/dashboard-client/dist/assets/VectorCortexTab-B3WuPeyn.js +0 -2
  138. package/extensions/dashboard-client/dist/assets/VectorCortexTab-B3WuPeyn.js.map +0 -1
@@ -26,6 +26,7 @@ import type { RouteContext } from "./routes-core.js";
26
26
  import { VC6C_ENABLED } from "../../src/config.js";
27
27
  import { sendJson } from "./routes-vector-cortex-shared.js";
28
28
  import { countVcEvents, vcCount } from "./vc-event-counts.js";
29
+ import { deriveVcStatus } from "./vc-status.js";
29
30
  import type { VectorCortexRepairView } from "./api-contracts/vector-cortex.js";
30
31
 
31
32
  // Actual event names emitted by src/vector-cortex/heal/repair-emit.ts.
@@ -75,6 +76,10 @@ export function handleVectorCortexRepair(
75
76
  lastBackoffMs: null,
76
77
  lastFailure: null,
77
78
  updatedAt: new Date().toISOString(),
79
+ status: deriveVcStatus({
80
+ enabled,
81
+ hasData: vcCount(counts, "vector_cortex_repair_planned") > 0,
82
+ }),
78
83
  };
79
84
  sendJson(res, 200, body);
80
85
  return true;
@@ -24,6 +24,7 @@ import type { RouteContext } from "./routes-core.js";
24
24
  import { VC4B_ENABLED } from "../../src/config.js";
25
25
  import { sendJson } from "./routes-vector-cortex-shared.js";
26
26
  import type { VectorCortexResidualView } from "./api-contracts/vector-cortex.js";
27
+ import { deriveVcStatus } from "./vc-status.js";
27
28
 
28
29
  /**
29
30
  * Reader-only GET /api/vector-cortex/residual (VC4B).
@@ -54,6 +55,7 @@ export function handleVectorCortexResidual(
54
55
  encodedByteTotal: 0,
55
56
  exactByteTotal: 0,
56
57
  updatedAt: new Date().toISOString(),
58
+ status: deriveVcStatus({ enabled, hasData: false, structuralOnly: true }),
57
59
  };
58
60
  sendJson(res, 200, body);
59
61
  return true;
@@ -13,17 +13,29 @@
13
13
  */
14
14
 
15
15
  import type { IncomingMessage, ServerResponse } from "node:http";
16
+ import { readFileSync } from "node:fs";
17
+ import { join } from "node:path";
16
18
  import type { RouteContext } from "./routes-core.js";
17
19
  import { VC5C_ENABLED } from "../../src/config.js";
18
20
  import { ROLLOUT_GATES, ROLLOUT_BUCKETS } from "../../src/vector-cortex/rollout/types.js";
19
21
  import { sendJson } from "./routes-vector-cortex-shared.js";
22
+ import { countVcEvents, vcCount } from "./vc-event-counts.js";
23
+ import { deriveVcStatus } from "./vc-status.js";
20
24
  import type { VectorCortexRolloutView } from "./api-contracts/vector-cortex.js";
21
25
 
26
+ // Rollout events: assigned/promotion-blocked from rollout/emit.ts, plus the
27
+ // explicit decision event appended by pipelineRun.ts:79 after decideLivePath.
28
+ const ROLLOUT_EVENTS = [
29
+ "vector_cortex_rollout_assigned",
30
+ "vector_cortex_rollout_promotion_blocked",
31
+ "vector_cortex_rollout_decision",
32
+ ] as const;
33
+
22
34
  /** Reader-only GET /api/vector-cortex/rollout (VC5C). */
23
35
  export function handleVectorCortexRollout(
24
36
  req: IncomingMessage,
25
37
  res: ServerResponse,
26
- _ctx: RouteContext,
38
+ ctx: RouteContext,
27
39
  ): boolean {
28
40
  const url = req.url ?? "";
29
41
  const path = url.split("?")[0] ?? url;
@@ -34,6 +46,12 @@ export function handleVectorCortexRollout(
34
46
  }
35
47
 
36
48
  const enabled = VC5C_ENABLED();
49
+ const counts = countVcEvents(ctx.stateDir, ROLLOUT_EVENTS);
50
+ const eventCount =
51
+ vcCount(counts, "vector_cortex_rollout_assigned") +
52
+ vcCount(counts, "vector_cortex_rollout_promotion_blocked") +
53
+ vcCount(counts, "vector_cortex_rollout_decision");
54
+ const sessionCount = enabled ? distinctRolloutSessions(ctx.stateDir) : 0;
37
55
  const gateIndex = 0; // Ephemeral rollout state: no durable gate in this sprint.
38
56
  const gatePct = ROLLOUT_GATES[gateIndex]!;
39
57
  const body: VectorCortexRolloutView = {
@@ -42,11 +60,48 @@ export function handleVectorCortexRollout(
42
60
  gatePct: enabled ? gatePct : 0,
43
61
  buckets: ROLLOUT_BUCKETS,
44
62
  bucketCount: enabled ? gatePct * 100 : 0,
45
- events: 0,
46
- sessions: 0,
63
+ events: enabled ? eventCount : 0,
64
+ sessions: enabled ? sessionCount : 0,
47
65
  promotionBlocked: false,
48
66
  updatedAt: new Date().toISOString(),
67
+ status: deriveVcStatus({ enabled, hasData: eventCount > 0 }),
49
68
  };
50
69
  sendJson(res, 200, body);
51
70
  return true;
52
71
  }
72
+
73
+ /**
74
+ * Count distinct session ids among rollout_assigned events in the tail of
75
+ * events.log. Best-effort: a missing/unreadable log or malformed lines yield 0.
76
+ * Only the `sessionId` field of rollout_assigned lines is inspected (aggregate
77
+ * only, never session payloads/prompts).
78
+ */
79
+ function distinctRolloutSessions(stateDir: string): number {
80
+ const seen = new Set<string>();
81
+ let raw: string;
82
+ try {
83
+ raw = readFileSync(join(stateDir, "events.log"), "utf-8");
84
+ } catch {
85
+ return 0;
86
+ }
87
+ const lines = raw.split("\n");
88
+ const start = Math.max(0, lines.length - 10000);
89
+ for (let i = start; i < lines.length; i++) {
90
+ const line = lines[i] as string;
91
+ if (line.length === 0) continue;
92
+ let ev: { event?: unknown; sessionId?: unknown } | null = null;
93
+ try {
94
+ ev = JSON.parse(line) as { event?: unknown; sessionId?: unknown };
95
+ } catch {
96
+ continue;
97
+ }
98
+ if (
99
+ ev?.event === "vector_cortex_rollout_assigned" &&
100
+ typeof ev.sessionId === "string" &&
101
+ ev.sessionId.length > 0
102
+ ) {
103
+ seen.add(ev.sessionId);
104
+ }
105
+ }
106
+ return seen.size;
107
+ }
@@ -24,6 +24,7 @@ import { VC4A_ENABLED } from "../../src/config.js";
24
24
  import { sendJson } from "./routes-vector-cortex-shared.js";
25
25
  import { countVcEvents, vcCount } from "./vc-event-counts.js";
26
26
  import type { VectorCortexShardsView } from "./api-contracts/vector-cortex.js";
27
+ import { deriveVcStatus } from "./vc-status.js";
27
28
 
28
29
  // Actual events emitted by src/vector-cortex/shards/manifest.ts. VC4A has no
29
30
  // per-tier (semantic/exact) write event — the manifest build is the single
@@ -60,6 +61,10 @@ export function handleVectorCortexShards(
60
61
  byteTotal: 0,
61
62
  protectedByteTotal: 0,
62
63
  updatedAt: new Date().toISOString(),
64
+ status: deriveVcStatus({
65
+ enabled,
66
+ hasData: vcCount(counts, "vector_cortex_shard_manifest_built") > 0,
67
+ }),
63
68
  };
64
69
  sendJson(res, 200, body);
65
70
  return true;
@@ -32,6 +32,7 @@ import type { TopologyCandidate, TopologyEmit } from "../../src/vector-cortex/to
32
32
  import type { CortexRecordV1 } from "../../src/vector-cortex/cortex/types.js";
33
33
  import { sendJson } from "./routes-vector-cortex-shared.js";
34
34
  import type { VectorCortexTopologyView } from "./api-contracts/vector-cortex.js";
35
+ import { deriveVcStatus } from "./vc-status.js";
35
36
 
36
37
  /** Well-known cortex record kind that carries a canonical candidate payload. */
37
38
  const TOPOLOGY_RECORD_KIND = "topology";
@@ -112,6 +113,7 @@ export function handleVectorCortexTopology(
112
113
  ? { nodes, edges, generationDigest }
113
114
  : {}),
114
115
  updatedAt: new Date().toISOString(),
116
+ status: deriveVcStatus({ enabled: active, hasData: summary.recordCount > 0 }),
115
117
  };
116
118
  sendJson(res, 200, body);
117
119
  return true;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * dashboard-server/vc-status.ts — shared status derivation for Vector Cortex
3
+ * dashboard routes. Every VC route computes a status field so the client can
4
+ * show meaningful state (live / awaiting data / deferred / structural) instead
5
+ * of bare zeros.
6
+ *
7
+ * Guardrails: PREVENT-011 (no `any`).
8
+ */
9
+
10
+ export type VcStatus = "live" | "awaiting_data" | "deferred" | "structural" | "off";
11
+
12
+ export function deriveVcStatus(opts: {
13
+ enabled: boolean;
14
+ deferredReason?: string;
15
+ hasData: boolean;
16
+ structuralOnly?: boolean;
17
+ }): VcStatus {
18
+ if (!opts.enabled) return "off";
19
+ if (opts.deferredReason) return "deferred";
20
+ if (opts.structuralOnly) return "structural";
21
+ return opts.hasData ? "live" : "awaiting_data";
22
+ }
@@ -28,12 +28,21 @@ export function cacheStripe(
28
28
  const embedder = defaultEmbedder();
29
29
 
30
30
  // (a) Extract text from the assistant's response for topic embedding.
31
+ // pi's assistant content may be a plain string OR an array of parts.
32
+ // Discriminate by type and only reach into `.text` after confirming the
33
+ // part is an object carrying a string `.text` — never assume shape.
31
34
  let textToEmbed = "";
32
35
  const msg = event.message;
33
- if (msg.role === "assistant" && Array.isArray(msg.content)) {
34
- for (const part of msg.content) {
35
- if ("text" in part && typeof part.text === "string") {
36
- textToEmbed += part.text + " ";
36
+ if (msg.role === "assistant") {
37
+ const c = msg.content;
38
+ if (typeof c === "string") {
39
+ textToEmbed = c;
40
+ } else if (Array.isArray(c)) {
41
+ for (const part of c) {
42
+ if (part && typeof part === "object" && "text" in part) {
43
+ const t = (part as { text?: unknown }).text;
44
+ if (typeof t === "string") textToEmbed += t + " ";
45
+ }
37
46
  }
38
47
  }
39
48
  }
@@ -50,9 +50,12 @@ export async function persistEpochAndMaintain(
50
50
  config: MegaConfig,
51
51
  ran: { result: CompactResult },
52
52
  ): Promise<void> {
53
- // S27 DB-mirror: write checkpoint_epoch with deterministic nonce.
54
- // This makes the cache key stable across identical compactions.
55
- if (config.dbMirror) {
53
+ // PLAN_V2 cache-striping epoch row. buildCacheOptimizedPrompt's stripe
54
+ // lookup reads the most-recent checkpoint_epochs row, so emit it whenever
55
+ // PLAN_V2 flags are live. Separate from the legacy dbMirror block below so
56
+ // PLAN_V2 can run with dbMirror OFF (flag-on is a genuine additive; flag-OFF
57
+ // = byte-identical to the predecessor, since this block no-ops then).
58
+ if (config.dbMirror || config.messageSeparation || config.cacheStriping) {
56
59
  try {
57
60
  const db = openStore(runtime.currentStateDir);
58
61
  const cpId = ran.result.checkpointId ?? `epoch-${Date.now()}`;
@@ -67,6 +70,22 @@ export async function persistEpochAndMaintain(
67
70
  createdAt: Date.now(),
68
71
  };
69
72
  writeCheckpointEpoch(db, epoch);
73
+ } catch (e) {
74
+ runtime.logger.warn("planv2-epoch-fail", { error: String(e) });
75
+ }
76
+ }
77
+
78
+ // S27 DB-mirror downstream: stamp turns, rebuild wiki, seed topics, dedup.
79
+ // Remains on `config.dbMirror` only — these are DB-mirror maintenance and
80
+ // must NOT run for PLAN_V2-only configurations.
81
+ if (config.dbMirror) {
82
+ try {
83
+ const db = openStore(runtime.currentStateDir);
84
+ const cpId = ran.result.checkpointId ?? `epoch-${Date.now()}`;
85
+ // epoch + writeCheckpointEpoch moved to the PLAN_V2 block above (it
86
+ // gates the wider set of flags); reuse epochIdFor(cpId) here so the
87
+ // stamps below land in the same epoch row.
88
+ const epochId = epochIdFor(cpId);
70
89
  // S50B: link this session's turns to the epoch that just compacted
71
90
  // them (compression-by-conversation-epoch metrics). Isolated-store
72
91
  // only; best-effort + non-fatal.
@@ -74,7 +93,7 @@ export async function persistEpochAndMaintain(
74
93
  stampTurnsEpochFor(
75
94
  config,
76
95
  runtime.rt.sessionId,
77
- epoch.epochId,
96
+ epochId,
78
97
  runtime.currentStateDir,
79
98
  );
80
99
  } catch {
@@ -290,9 +309,9 @@ export async function persistEpochAndMaintain(
290
309
  runtime.appendEvent(name, payload as Record<string, unknown>),
291
310
  {
292
311
  subsystem: "post_compact",
293
- generation: 1,
312
+ generation: runtime.rt.compactCount,
294
313
  backoffMs: 0,
295
- gapSize: 0,
314
+ gapSize: ran.result.compactedFrom,
296
315
  },
297
316
  );
298
317
  } catch {
@@ -16,6 +16,21 @@ import type { MegaRuntime } from "../../mega-runtime.js";
16
16
  import type { MegaConfig } from "../../mega-config.js";
17
17
  import { messageContentText } from "./messageText.js";
18
18
 
19
+ /**
20
+ * Best-effort tool_call_id for the tool_results insert. The toolResult variant
21
+ * carries a top-level toolCallId (read via an `unknown`-narrowed cast — never
22
+ * reach into `.content`, which is variant-specific and requires narrowing).
23
+ * bashExecution has no toolCallId, so fall back to a stable synthetic id keyed
24
+ * on (turn, index) to satisfy the NOT NULL column. No `any` (PREVENT-011).
25
+ */
26
+ function toolCallIdOf(m: AgentMessage, fallback: string): string {
27
+ if (m.role === "toolResult") {
28
+ const id = (m as unknown as { toolCallId?: unknown }).toolCallId;
29
+ if (typeof id === "string" && id.length > 0) return id;
30
+ }
31
+ return fallback;
32
+ }
33
+
19
34
  /**
20
35
  * Append incoming messages to the DB mirror (raw_transcript + thread/tool
21
36
  * tables) and the v2 ledger. Gated on config.dbMirror for the mirror; the VC1B
@@ -49,25 +64,42 @@ export function appendMirrorAndLedger(
49
64
  // separation (buildSeparatedPrompt / buildCacheOptimizedPrompt in
50
65
  // tailResult) is sufficient for the prompt-construction path;
51
66
  // these DB writes persist the split for post-hoc analysis, dashboard
52
- // queries, and future readers. Non-fatal — failure here never breaks
53
- // the agent loop (PREVENT-PI-004: zero network, local SQLite only).
54
- {
67
+ // queries, and future readers. Gated on (messageSeparation ||
68
+ // cacheStriping) so flag-OFF remains byte-identical to the
69
+ // predecessor — when both flags are OFF the live prompt is never
70
+ // separated, and growing these tables would be dead state.
71
+ // Non-fatal — failure here never breaks the agent loop
72
+ // (PREVENT-PI-004: zero network, local SQLite only).
73
+ if (config.messageSeparation || config.cacheStriping) {
55
74
  const sid = runtime.rt.sessionId;
56
75
  const turn = runtime.currentTurn;
57
76
  const now = Date.now();
58
77
  const threadStmt = db.prepare(
59
78
  "INSERT OR IGNORE INTO conversation_thread (conversation_id, role, content, turn_index, timestamp) VALUES (?, ?, ?, ?, ?)",
60
79
  );
80
+ // Schema (plan-v2.ts) is (conversation_id, tool_call_id,
81
+ // tool_result, turn_index, timestamp) — NOT role/content.
61
82
  const toolStmt = db.prepare(
62
- "INSERT OR IGNORE INTO tool_results (conversation_id, role, content, turn_index, timestamp) VALUES (?, ?, ?, ?, ?)",
83
+ "INSERT OR IGNORE INTO tool_results (conversation_id, tool_call_id, tool_result, turn_index, timestamp) VALUES (?, ?, ?, ?, ?)",
84
+ );
85
+ const toolHas = db.prepare(
86
+ "SELECT 1 FROM tool_results WHERE conversation_id = ? AND turn_index = ? AND tool_call_id = ? AND tool_result = ? LIMIT 1",
87
+ );
88
+ const threadHas = db.prepare(
89
+ "SELECT 1 FROM conversation_thread WHERE conversation_id = ? AND turn_index = ? AND role = ? AND content = ? LIMIT 1",
63
90
  );
64
- for (const m of messages) {
91
+ for (const [idx, m] of messages.entries()) {
65
92
  const role = m.role;
66
93
  const content = messageContentText(m);
67
94
  if (role === "user" || role === "assistant") {
68
- threadStmt.run(sid, role, content, turn, now);
95
+ if (threadHas.get(sid, turn, role, content) == null) {
96
+ threadStmt.run(sid, role, content, turn, now);
97
+ }
69
98
  } else if (role === "toolResult" || role === "bashExecution") {
70
- toolStmt.run(sid, role, content, turn, now);
99
+ const toolCallId = toolCallIdOf(m, `bash:${turn}:${idx}`);
100
+ if (toolHas.get(sid, turn, toolCallId, content) == null) {
101
+ toolStmt.run(sid, toolCallId, content, turn, now);
102
+ }
71
103
  }
72
104
  }
73
105
  }
@@ -21,6 +21,7 @@ import type { MegaConfig } from "../../mega-config.js";
21
21
  import type { TailResultFn } from "./gateCheck.js";
22
22
  import { recordCompactLatency } from "../../mega-runtime/vc-observer.js";
23
23
  import { decideLivePath } from "../../mega-runtime/vector-cortex-live.js";
24
+ import { defaultClock, type RolloutEvidence } from "../../../src/vector-cortex/rollout/gate.js";
24
25
  import { VC5C_ENABLED } from "../../../src/config/vector-cortex.js";
25
26
 
26
27
  /** The non-skipped variant of the runCompact result. */
@@ -53,13 +54,27 @@ export function invokePipeline(
53
54
  // rollout shapes. Best-effort + non-fatal; decision does NOT gate behavior yet.
54
55
  if (VC5C_ENABLED()) {
55
56
  try {
57
+ const clock = defaultClock();
58
+ // Conservative evidence: fresh in-memory window (elapsed=0), not
59
+ // powered, so the gate can't advance — but events/sessions carry real
60
+ // values so the decision events reflect actual runtime activity rather
61
+ // than degrading every epoch to an empty mode-C read.
62
+ const evidence: RolloutEvidence = {
63
+ windowStartMs: clock.now(),
64
+ powered: false,
65
+ events: runtime.rt.compactCount,
66
+ sessions: 1,
67
+ hardFaults:
68
+ (runtime.rt as { vcHardFaults?: import("../../../src/vector-cortex/rollout/types.js").RolloutHardFault[] })
69
+ .vcHardFaults ?? [],
70
+ };
56
71
  const decision = decideLivePath(runtime.rt.sessionId, {
57
72
  emit: runtime.appendEvent.bind(runtime),
73
+ clock,
58
74
  currentGate:
59
75
  (runtime.rt as { vcGate?: 0 | 1 | 2 | 3 | 4 }).vcGate ?? 0,
60
- hardFaults:
61
- (runtime.rt as { vcHardFaults?: import("../../../src/vector-cortex/rollout/types.js").RolloutHardFault[] })
62
- .vcHardFaults ?? [],
76
+ evidence,
77
+ hardFaults: evidence.hardFaults,
63
78
  });
64
79
  runtime.appendEvent("vector_cortex_rollout_decision", {
65
80
  sessionId: runtime.rt.sessionId,
@@ -12,6 +12,16 @@ import type { MegaConfig } from "../../mega-config.js";
12
12
  import { stagedForTail, withRecallTail } from "../recall-tail.js";
13
13
  import { buildSeparatedPrompt, buildCacheOptimizedPrompt } from "../separated-prompt.js";
14
14
  import { messageContentText } from "./messageText.js";
15
+ import { computeContentDigest } from "../../../src/dedup/digest.js";
16
+
17
+ // P2.5: per-runtime cross-turn prompt-prefix footprints for stable-prefix
18
+ // measurement. WeakMap keyed by MegaRuntime so the entry dies with the runtime
19
+ // (same lifecycle as a session boundary in practice — a new session re-creates
20
+ // the pipeline over the same runtime, but the prior turn's fingerprints must
21
+ // still be visible for cross-turn compare; a fresh runtime has an empty map and
22
+ // the sessionId guard below blocks cross-session compare).
23
+ type TurnPrefix = { sessionId: string; turn: number; fingerprints: string[] };
24
+ const lastTurnPrefix = new WeakMap<MegaRuntime, TurnPrefix>();
15
25
 
16
26
  /**
17
27
  * Build the tail injection closure. Returns undefined when nothing is staged
@@ -39,23 +49,54 @@ export function buildTailResult(
39
49
  } else if (config.messageSeparation) {
40
50
  result = buildSeparatedPrompt(result);
41
51
  }
42
- // P2.5: log prefix stability (fire-and-forget, non-fatal).
43
- // tailResult is sync, so use .then().catch() on the dynamic import.
52
+ // P2.5: log cross-turn stable-prefix length (cache-hit proxy). After the
53
+ // prompt is built (separated or cache-optimized), fingerprint each leading
54
+ // message and count how many are byte-identical to the previous turn's
55
+ // prompt, in order. A high stablePrefix = the provider KV-cache prefix is
56
+ // re-used (cache hit). Fire-and-forget + non-fatal.
57
+ //
58
+ // tailResult may run several times per turn (gate return / replay /
59
+ // debounce / live-trim), so the sessionId+turn guard measures once per
60
+ // turn — compare against the PREVIOUS turn's stored footprint, then store
61
+ // this turn's for the next comparison. Cross-session compares are skipped
62
+ // via the stored sessionId check (belt-and-suspenders; the WeakMap entry
63
+ // is scoped to this runtime, which owns the session).
44
64
  if (result.length > 1) {
45
- import("../../../src/cache-stripe-impl.js").then(({ computeStabilityScore }) => {
46
- const stableScore = computeStabilityScore(
47
- { content: messageContentText(result[0] ?? result[0]), chunkId: "prefix", accessCount: 0, lastAccessedAt: 0 },
48
- result.slice(0, 2).map((m) => ({ content: messageContentText(m), chunkId: "prefix", accessCount: 0, lastAccessedAt: 0 })),
49
- );
50
- runtime.logger.info("prefix_stability", {
51
- stableScore: Number.isFinite(stableScore) ? stableScore : 0,
52
- prefixMessages: result.length,
53
- separation: config.messageSeparation ? "v2" : "off",
54
- striping: config.cacheStriping ? "v3" : "off",
55
- });
56
- }).catch(() => {
65
+ try {
66
+ const sessionId = runtime.rt.sessionId;
67
+ const turn = runtime.currentTurn;
68
+ const prev = lastTurnPrefix.get(runtime);
69
+ const isNewTurn =
70
+ !prev || prev.sessionId !== sessionId || prev.turn !== turn;
71
+ if (isNewTurn) {
72
+ const fingerprints = result.map((m) =>
73
+ // Role prefix disambiguates identical text across variants
74
+ // (roles are a fixed enum — no realistic fingerprint collision).
75
+ computeContentDigest(`${m.role}|${messageContentText(m)}`).contentHash,
76
+ );
77
+ let stablePrefix = 0;
78
+ if (prev && prev.sessionId === sessionId) {
79
+ for (let i = 0; i < fingerprints.length; i++) {
80
+ if (
81
+ i >= prev.fingerprints.length ||
82
+ prev.fingerprints[i] !== fingerprints[i]
83
+ ) {
84
+ break;
85
+ }
86
+ stablePrefix++;
87
+ }
88
+ }
89
+ lastTurnPrefix.set(runtime, { sessionId, turn, fingerprints });
90
+ runtime.logger.info("prefix_stability", {
91
+ stablePrefix,
92
+ totalMessages: result.length,
93
+ separation: config.messageSeparation ? "v2" : "off",
94
+ striping: config.cacheStriping ? "v3" : "off",
95
+ });
96
+ }
97
+ } catch {
57
98
  // Non-fatal: stability logging is best-effort.
58
- });
99
+ }
59
100
  }
60
101
  return { messages: result };
61
102
  };
@@ -5,7 +5,7 @@
5
5
  * `registerEventHandlers` which delegates to the focused sub-registration
6
6
  * functions in session/agent/context/compact handlers.
7
7
  */
8
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
9
  import type { MegaRuntime } from "../mega-runtime.js";
10
10
  import type { MegaConfig } from "../mega-config.js";
11
11
  import { registerSessionHandlers } from "./session-handlers.js";
@@ -13,6 +13,7 @@ import { registerAgentHandlers } from "./agent-handlers.js";
13
13
  import { registerContextHandler } from "./context-handler.js";
14
14
  import { registerCompactHandlers } from "./compact-handlers.js";
15
15
  import { registerPerfHandler } from "./perf-handler.js";
16
+ import { registerVectorCortexRender } from "../mega-context/vector-cortex.js";
16
17
 
17
18
  /**
18
19
  * DIAG accessor for the headless test harness: the most recently constructed
@@ -30,6 +31,18 @@ export function registerEventHandlers(
30
31
  config: MegaConfig,
31
32
  ): void {
32
33
  lastRuntime = runtime;
34
+ // VC5B: wire the render+validate seam into the before_agent_start lifecycle.
35
+ // Registered FIRST so its no-op return (no render input on ctx) doesn't
36
+ // clobber a systemPrompt that a later before_agent_start handler (e.g. the
37
+ // legacy recall prepend in session-handlers) returns. When VC5B does
38
+ // prepend, it returns a new systemPrompt; the later handlers run on the
39
+ // recallTailInject=ON path and return undefined, so VC5B's return survives.
40
+ // The _ctx param is unused by the seam; pass empty for the registered seam.
41
+ try {
42
+ registerVectorCortexRender(pi, undefined as unknown as ExtensionContext, (event, fields) => {
43
+ try { runtime.appendEvent(event, fields); } catch { /* non-fatal */ }
44
+ });
45
+ } catch { /* non-fatal: VC5B registration never breaks startup */ }
33
46
  // ---- Session lifecycle (state reset points) -------------------------------
34
47
  registerSessionHandlers(pi, runtime, config);
35
48
  registerAgentHandlers(pi, runtime, config);
@@ -26,6 +26,7 @@ import {
26
26
  recordRecallWrite,
27
27
  } from "../mega-turn-store.js";
28
28
  import { type MegaRuntime, C } from "../mega-runtime.js";
29
+ import { recordRecallLatency } from "../mega-runtime/vc-observer.js";
29
30
  import type { MegaConfig } from "../mega-config.js";
30
31
 
31
32
  /**
@@ -46,6 +47,7 @@ export function doRecall(
46
47
  // are already resident in the session, so recall never re-injects context the
47
48
  // model can already see. Best-effort — an empty window just skips dedupe.
48
49
  const liveWindow = config.windowDedupe ? extractLiveWindow(ctx) : undefined;
50
+ const recallStartMs = Date.now();
49
51
  const result = recallAndInline(
50
52
  {
51
53
  sessionId: sid,
@@ -165,6 +167,14 @@ export function doRecall(
165
167
  /* non-fatal: recall provenance never breaks the recall path */
166
168
  }
167
169
  }
170
+ // VC0A: record recall latency on the eval observer (mode A) so the dashboard
171
+ // histogram reflects real data. No-op when the observer is absent (flag off /
172
+ // construction failure).
173
+ try {
174
+ recordRecallLatency(runtime, Date.now() - recallStartMs, sid, 0);
175
+ } catch {
176
+ /* non-fatal: latency recording never breaks recall */
177
+ }
168
178
  return result;
169
179
  }
170
180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.20.23",
3
+ "version": "0.20.25",
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",