pi-mega-compact 0.13.0 → 0.13.2

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 (212) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/embedder-health.js +11 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +225 -0
  3. package/dist/extensions/dashboard-server/api-contracts/endpoints/types.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +5 -224
  5. package/dist/extensions/dashboard-server/api-contracts/raptor.js +8 -0
  6. package/dist/extensions/dashboard-server/api-contracts.test/_helpers.js +32 -0
  7. package/dist/extensions/dashboard-server/routes-cache.test/_helpers.js +48 -0
  8. package/dist/extensions/dashboard-server/routes-embedder-health.js +67 -0
  9. package/dist/extensions/dashboard-server/routes-raptor.js +88 -0
  10. package/dist/extensions/dashboard-server/routes-setup.js +1 -1
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +5 -1
  13. package/dist/extensions/mega-cache-replay.test/_helpers.js +175 -0
  14. package/dist/extensions/mega-commands/dataCommands.js +151 -0
  15. package/dist/extensions/mega-commands/helpers.js +61 -0
  16. package/dist/extensions/mega-commands/historyCommands.js +97 -0
  17. package/dist/extensions/mega-commands/setupCommand.js +200 -0
  18. package/dist/extensions/mega-commands.js +14 -499
  19. package/dist/extensions/mega-compact-s38.test/_helpers.js +194 -0
  20. package/dist/extensions/mega-compact.js +12 -2
  21. package/dist/extensions/mega-compact.test/_helpers.js +225 -0
  22. package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +211 -0
  23. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +49 -0
  24. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.js +33 -0
  25. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +387 -0
  26. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/event.js +7 -0
  27. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.js +55 -0
  28. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.js +12 -0
  29. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.js +22 -0
  30. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +38 -0
  31. package/dist/extensions/mega-events/agent-handlers/turnEndHandler.js +36 -0
  32. package/dist/extensions/mega-events/agent-handlers.js +4 -799
  33. package/dist/extensions/mega-events/context-handler/afterCompact.js +163 -0
  34. package/dist/extensions/mega-events/context-handler/messageText.js +12 -0
  35. package/dist/extensions/mega-events/context-handler/tailResult.js +46 -0
  36. package/dist/extensions/mega-events/context-handler.js +11 -198
  37. package/dist/extensions/mega-events/separated-prompt.test/_helpers.js +25 -0
  38. package/dist/src/config.js +2 -0
  39. package/dist/src/dedup-engine.test/_helpers.js +55 -0
  40. package/dist/src/e2e.test/_helpers.js +52 -0
  41. package/dist/src/embedder.js +1 -0
  42. package/dist/src/httpEmbedder.js +6 -0
  43. package/dist/src/hyde.js +146 -0
  44. package/dist/src/importance.test/_helpers.js +5 -0
  45. package/dist/src/memoryGraph/sources/checkpoints.js +78 -0
  46. package/dist/src/memoryGraph/sources/edges.js +17 -0
  47. package/dist/src/memoryGraph/sources/flags.js +22 -0
  48. package/dist/src/memoryGraph/sources/helpers.js +14 -0
  49. package/dist/src/memoryGraph/sources/memories.js +88 -0
  50. package/dist/src/memoryGraph/sources/raptor.js +26 -0
  51. package/dist/src/memoryGraph/sources/turns.js +176 -0
  52. package/dist/src/memoryGraph/sources.js +15 -441
  53. package/dist/src/memoryGraph-gates.test/_helpers.js +120 -0
  54. package/dist/src/ratio.bench.test/_helpers.js +198 -0
  55. package/dist/src/recall/async.js +112 -0
  56. package/dist/src/recall/format.js +149 -0
  57. package/dist/src/recall/memory.js +62 -0
  58. package/dist/src/recall/reformulate.js +82 -0
  59. package/dist/src/recall/sync.js +169 -0
  60. package/dist/src/recall/types.js +1 -0
  61. package/dist/src/recall.js +13 -530
  62. package/dist/src/store/sqlite/schema/core.js +232 -0
  63. package/dist/src/store/sqlite/schema/game.js +65 -0
  64. package/dist/src/store/sqlite/schema/plan-v2.js +107 -0
  65. package/dist/src/store/sqlite/schema/turns.js +59 -0
  66. package/dist/src/store/sqlite/schema.js +8 -441
  67. package/dist/src/store/turns/contract-compliance.test/_helpers.js +21 -0
  68. package/dist/src/store/turns/sqlite-store/admin.js +171 -0
  69. package/dist/src/store/turns/sqlite-store/ctx.js +1 -0
  70. package/dist/src/store/turns/sqlite-store/read.js +111 -0
  71. package/dist/src/store/turns/sqlite-store/rows.js +42 -0
  72. package/dist/src/store/turns/sqlite-store/write.js +102 -0
  73. package/dist/src/store/turns/sqlite-store.js +31 -367
  74. package/dist/src/vectorStore/class.js +356 -0
  75. package/dist/src/vectorStore/hash.js +10 -0
  76. package/dist/src/vectorStore/types.js +2 -0
  77. package/dist/src/vectorStore.js +8 -359
  78. package/dist/src/vectorStore.test/_helpers.js +17 -0
  79. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js → CacheTab-DPRHjYZ0.js} +2 -2
  80. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js.map → CacheTab-DPRHjYZ0.js.map} +1 -1
  81. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js → EventsTab-CgwYq-Qf.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js.map → EventsTab-CgwYq-Qf.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js → HealthTab-BgOCjVzR.js} +2 -2
  84. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js.map → HealthTab-BgOCjVzR.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js +2 -0
  86. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js.map +1 -0
  87. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js +2 -0
  88. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js.map +1 -0
  89. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js → MetricsTab-CcH9fP8P.js} +2 -2
  90. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js.map → MetricsTab-CcH9fP8P.js.map} +1 -1
  91. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js → OverviewTab-BOs8ZNev.js} +2 -2
  92. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js.map → OverviewTab-BOs8ZNev.js.map} +1 -1
  93. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js → ReposTab-B-MfSPvg.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js.map → ReposTab-B-MfSPvg.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js → SessionsTab-BmeN649Q.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js.map → SessionsTab-BmeN649Q.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js +2 -0
  98. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js.map +1 -0
  99. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js → TimeSavedCard-DnPg0L0z.js} +2 -2
  100. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js.map → TimeSavedCard-DnPg0L0z.js.map} +1 -1
  101. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js → TopicsTab-BODHOg_b.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js.map → TopicsTab-BODHOg_b.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js → TurnsTab-BK0I7hKr.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js.map → TurnsTab-BK0I7hKr.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{index-D-lPLRWB.js → index-mOlljTFI.js} +10 -10
  106. package/extensions/dashboard-client/dist/assets/index-mOlljTFI.js.map +1 -0
  107. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js → useSSE-BYOtv1-3.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js.map → useSSE-BYOtv1-3.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/index.html +1 -1
  110. package/extensions/dashboard-client/src/App.tsx +0 -4
  111. package/extensions/dashboard-client/src/api/client.ts +16 -0
  112. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +178 -0
  113. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +140 -0
  114. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +109 -0
  115. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +98 -0
  116. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +108 -0
  117. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +12 -598
  118. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +476 -0
  119. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +163 -0
  120. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +56 -466
  121. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderHealthCard.tsx +109 -0
  122. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +468 -0
  123. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +23 -484
  124. package/extensions/dashboard-server/api-contracts/embedder-health.ts +32 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +360 -0
  126. package/extensions/dashboard-server/api-contracts/endpoints/types.ts +350 -0
  127. package/extensions/dashboard-server/api-contracts/endpoints.ts +27 -648
  128. package/extensions/dashboard-server/api-contracts/index.ts +6 -0
  129. package/extensions/dashboard-server/api-contracts/raptor.ts +49 -0
  130. package/extensions/dashboard-server/api-contracts.test/_helpers.ts +47 -0
  131. package/extensions/dashboard-server/routes-cache.test/_helpers.ts +94 -0
  132. package/extensions/dashboard-server/routes-embedder-health.ts +77 -0
  133. package/extensions/dashboard-server/routes-raptor.ts +128 -0
  134. package/extensions/dashboard-server/routes-setup.ts +1 -1
  135. package/extensions/dashboard-server/routes.ts +2 -0
  136. package/extensions/dashboard-server/server.ts +4 -0
  137. package/extensions/mega-cache-replay.test/_helpers.ts +188 -0
  138. package/extensions/mega-commands/dataCommands.ts +169 -0
  139. package/extensions/mega-commands/helpers.ts +62 -0
  140. package/extensions/mega-commands/historyCommands.ts +117 -0
  141. package/extensions/mega-commands/setupCommand.ts +235 -0
  142. package/extensions/mega-commands.ts +25 -538
  143. package/extensions/mega-compact-s38.test/_helpers.ts +201 -0
  144. package/extensions/mega-compact.test/_helpers.ts +237 -0
  145. package/extensions/mega-compact.ts +12 -2
  146. package/extensions/mega-events/agent-handlers/agentEndHandler.ts +238 -0
  147. package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +74 -0
  148. package/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.ts +50 -0
  149. package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +421 -0
  150. package/extensions/mega-events/agent-handlers/turnEndHandler/event.ts +16 -0
  151. package/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.ts +73 -0
  152. package/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.ts +29 -0
  153. package/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.ts +42 -0
  154. package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +61 -0
  155. package/extensions/mega-events/agent-handlers/turnEndHandler.ts +64 -0
  156. package/extensions/mega-events/agent-handlers.ts +15 -861
  157. package/extensions/mega-events/context-handler/afterCompact.ts +213 -0
  158. package/extensions/mega-events/context-handler/messageText.ts +20 -0
  159. package/extensions/mega-events/context-handler/tailResult.ts +63 -0
  160. package/extensions/mega-events/context-handler.ts +17 -235
  161. package/extensions/mega-events/separated-prompt.test/_helpers.ts +35 -0
  162. package/package.json +1 -1
  163. package/src/config.ts +4 -0
  164. package/src/dedup-engine.test/_helpers.ts +79 -0
  165. package/src/e2e.test/_helpers.ts +59 -0
  166. package/src/embedder.ts +4 -0
  167. package/src/httpEmbedder.ts +7 -0
  168. package/src/hyde.ts +162 -0
  169. package/src/importance.test/_helpers.ts +5 -0
  170. package/src/memoryGraph/sources/checkpoints.ts +99 -0
  171. package/src/memoryGraph/sources/edges.ts +23 -0
  172. package/src/memoryGraph/sources/flags.ts +26 -0
  173. package/src/memoryGraph/sources/helpers.ts +13 -0
  174. package/src/memoryGraph/sources/memories.ts +108 -0
  175. package/src/memoryGraph/sources/raptor.ts +37 -0
  176. package/src/memoryGraph/sources/turns.ts +226 -0
  177. package/src/memoryGraph/sources.ts +19 -544
  178. package/src/memoryGraph-gates.test/_helpers.ts +164 -0
  179. package/src/ratio.bench.test/_helpers.ts +268 -0
  180. package/src/recall/async.ts +142 -0
  181. package/src/recall/format.ts +173 -0
  182. package/src/recall/memory.ts +90 -0
  183. package/src/recall/reformulate.ts +111 -0
  184. package/src/recall/sync.ts +196 -0
  185. package/src/recall/types.ts +63 -0
  186. package/src/recall.ts +25 -712
  187. package/src/store/sqlite/schema/core.ts +233 -0
  188. package/src/store/sqlite/schema/game.ts +66 -0
  189. package/src/store/sqlite/schema/plan-v2.ts +108 -0
  190. package/src/store/sqlite/schema/turns.ts +60 -0
  191. package/src/store/sqlite/schema.ts +13 -441
  192. package/src/store/turns/contract-compliance.test/_helpers.ts +38 -0
  193. package/src/store/turns/sqlite-store/admin.ts +249 -0
  194. package/src/store/turns/sqlite-store/ctx.ts +14 -0
  195. package/src/store/turns/sqlite-store/read.ts +174 -0
  196. package/src/store/turns/sqlite-store/rows.ts +55 -0
  197. package/src/store/turns/sqlite-store/write.ts +193 -0
  198. package/src/store/turns/sqlite-store.ts +33 -514
  199. package/src/vectorStore/class.ts +433 -0
  200. package/src/vectorStore/hash.ts +11 -0
  201. package/src/vectorStore/types.ts +58 -0
  202. package/src/vectorStore.test/_helpers.ts +20 -0
  203. package/src/vectorStore.ts +13 -489
  204. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-D-lPLRWB.js.map +0 -1
@@ -0,0 +1,238 @@
1
+ /**
2
+ * agent-handlers/agentEndHandler.ts — the pi `agent_end` handler body.
3
+ *
4
+ * Extracted from agent-handlers.ts (delegate-shell split) to keep every source
5
+ * file under the extensions limit. Contains the live-agent status-line updates,
6
+ * the S16/S38.5 mid-run durable trim (race-guarded, deferred ctx.compact), the
7
+ * S28 length-stop nudge, and the R4-R11 error-retry safety net.
8
+ */
9
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
10
+ import type { MegaRuntime } from "../../mega-runtime.js";
11
+ import { piCompactWouldNoop } from "../../mega-pipeline.js";
12
+ import type { MegaConfig } from "../../mega-config.js";
13
+ import { safeSendUserMessage } from "../send-safe.js";
14
+
15
+ /** Handle the `agent_end` pi event. Non-fatal end-to-end. */
16
+ export async function handleAgentEnd(
17
+ ctx: ExtensionContext,
18
+ pi: ExtensionAPI,
19
+ runtime: MegaRuntime,
20
+ config: MegaConfig,
21
+ ): Promise<void> {
22
+ runtime.activeAgents = Math.max(0, runtime.activeAgents - 1);
23
+ runtime.dashboard.event("agent_end", {
24
+ activeAgents: runtime.activeAgents,
25
+ });
26
+ if (runtime.activeAgents > 0) {
27
+ runtime.setStatus(
28
+ ctx,
29
+ `mega-compact: ▶ ${runtime.activeAgents} agent${runtime.activeAgents === 1 ? "" : "s"}`,
30
+ );
31
+ } else {
32
+ runtime.setStatus(
33
+ ctx,
34
+ config.auto ? "mega-compact: ready" : "mega-compact: manual only",
35
+ );
36
+ }
37
+ // S16 continuation fallback: if the turn settled idle right after a live-trim
38
+ // compaction AND there is queued work AND we haven't nudged recently, nudge
39
+ // once so the agent continues (the live trim should make this rare). Guarded
40
+ // to never busy-loop: one nudge per 30s, only when truly idle + queued.
41
+ if (
42
+ (config.auto || config.autoContinueLengthStop) &&
43
+ runtime.activeAgents === 0
44
+ ) {
45
+ try {
46
+ const idle = ctx.isIdle?.() ?? true;
47
+ const queued = ctx.hasPendingMessages?.() ?? false;
48
+ const now = Date.now();
49
+ // S38.5: read LIVE pressure from ctx.getContextUsage() instead of the
50
+ // stale runtime.lastCtxTokens (only updated by the `context` event).
51
+ // agent_end may fire without a preceding context event this turn (e.g.
52
+ // a sub-agent settling), so the cached value can be null/stale and the
53
+ // durable-trim branch would be unreachable. ctx.getContextUsage() is the
54
+ // authoritative live reading (mirrors context-handler.ts:86). Fall back
55
+ // to the cached value only if the ctx omits it.
56
+ const liveUsage = ctx.getContextUsage?.();
57
+ const liveTokens =
58
+ typeof liveUsage?.tokens === "number"
59
+ ? liveUsage.tokens
60
+ : (runtime.lastCtxTokens ?? 0);
61
+ // Keep the cache fresh for snapshot()/diag regardless of which source we use.
62
+ if (typeof liveUsage?.tokens === "number") {
63
+ runtime.lastCtxTokens = liveUsage.tokens;
64
+ }
65
+ if (typeof liveUsage?.percent === "number") {
66
+ runtime.lastCtxPercent = liveUsage.percent;
67
+ }
68
+ if (typeof liveUsage?.contextWindow === "number") {
69
+ runtime.lastCtxWindow = liveUsage.contextWindow;
70
+ }
71
+ // DIAG (team-run relief): surface whether the agent is idle + over
72
+ // threshold at agent_end so we can see if a mid-run durable-trim trigger
73
+ // *should* have fired but didn't.
74
+ const overThreshold = liveTokens >= runtime.effectiveThreshold;
75
+ runtime.diagAgentEndIdle++;
76
+ runtime.logger.info("agent-end-idle", {
77
+ sessionId: runtime.rt.sessionId,
78
+ idle,
79
+ queued,
80
+ overThreshold,
81
+ ctxPct: runtime.lastCtxPercent,
82
+ ctxTokens: runtime.lastCtxTokens,
83
+ thresholdTokens: config.thresholdTokens,
84
+ wouldNudge:
85
+ idle &&
86
+ (queued || overThreshold) &&
87
+ now >= runtime.resumeNudgeUntil,
88
+ });
89
+ // S16+S24: MID-RUN DURABLE TRIM. During a long team run (sub-agents),
90
+ // pi's native durable compaction only fires from _checkCompaction at
91
+ // PARENT settle (agent-session.js:760/844), so the on-disk transcript +
92
+ // context meter balloon to ~150k and never relieve until the very end
93
+ // ("compacts but doesn't resume"). agent_end with activeAgents===0 is a
94
+ // SAFE, settled point: calling ctx.compact() here does NOT abort an
95
+ // in-flight turn (the S16 danger is only mid-turn). ctx.compact() runs
96
+ // pi's flow, which fires our session_before_compact handler to supply
97
+ // the durable trim (truncates the transcript from firstKeptEntryId).
98
+ // Guarded three ways: only when truly idle + over threshold, only when
99
+ // pi would actually compact (piCompactWouldNoop skips the user-facing
100
+ // no-op throw), and debounced (one durable trim per 2s) to avoid
101
+ // thrashing the transcript while sub-agents keep settling.
102
+ //
103
+ // FIX "compacts but doesn't resume": the manual ctx.compact() path
104
+ // STOPS the agent loop (agent-session.js:1345). The old resume-nudge
105
+ // was gated on `queued`, so when a sub-agent settled with no
106
+ // *immediately* queued message, the trim fired but the nudge did not,
107
+ // and the (stopped) session hung. The trim still fires on
108
+ // `idle && overThreshold` — we intentionally do NOT add a `!queued`
109
+ // guard, because that would suppress mid-run relief exactly during
110
+ // team-run waves where queued is usually true and relief is needed
111
+ // most. Instead we DECOUPLE the nudge from `queued`: after a durable
112
+ // trim we ALWAYS nudge so the agent reliably restarts. Debounced 30s.
113
+ let didDurableTrim = false;
114
+ if (
115
+ config.auto &&
116
+ idle &&
117
+ overThreshold &&
118
+ now >= runtime.debounceUntil
119
+ ) {
120
+ // COMPACT-DEDUP FIX: skip the manual durable-trim trigger when pi's
121
+ // NATIVE auto-compaction just fired (or is in-flight). pi emits
122
+ // agent_end BEFORE its own _checkCompaction (per its docstring:
123
+ // "Called after agent_end and before prompt submission"), so a
124
+ // synchronous `piCompactWouldNoop` branch check misses a native
125
+ // compaction that hasn't appended its entry yet — calling
126
+ // ctx.compact() then races with pi and throws "Already compacted"
127
+ // to the user. The `lastNativeCompactAt` cooldown (updated by the
128
+ // session_compact listener for EVERY compaction, native or
129
+ // extension-supplied) closes that race window.
130
+ //
131
+ // S38.5: strict race guard widens the cooldown 10s -> 30s AND defers
132
+ // ctx.compact() via setTimeout(500) with a re-check, so pi's
133
+ // about-to-run native _checkCompaction can append its `compaction`
134
+ // branch entry first (closes the first-race-in-burst window). Gated
135
+ // by MEGACOMPACT_RACE_GUARD_STRICT (default true); false reverts to
136
+ // the v0.7.4 synchronous 10s guard. Mirrors the legacy path in
137
+ // context-handler.ts:258-287 so both call sites stay in sync.
138
+ const cooldownMs = config.raceGuardStrict ? 30_000 : 10_000;
139
+ const sinceCompact = now - (runtime.rt.lastNativeCompactAt ?? 0);
140
+ // CRITICAL-OVER ESCAPE HATCH: when context is critically over the
141
+ // window (>= 90%), force the durable ctx.compact() even if
142
+ // piCompactWouldNoop says it would no-op ("Already compacted").
143
+ // Without this, the on-disk transcript never truncates and the
144
+ // session stays overflowed on every resume — the "Already
145
+ // compacted" + overflow death-spiral (2026-08-01 incident). The
146
+ // cooldown still applies (we don't spam compact), but the no-op
147
+ // gate is bypassed when critical. pi's ctx.compact() may throw
148
+ // "Already compacted" in the truly-no-op case — wrapped in
149
+ // try/catch so the throw is non-fatal.
150
+ const criticalOver = (runtime.lastCtxPercent ?? 0) >= 90;
151
+ if (sinceCompact < cooldownMs) {
152
+ runtime.diagAgentEndDurableSkipRecent++;
153
+ } else if (criticalOver || !piCompactWouldNoop(ctx)) {
154
+ runtime.debounceUntil = now + 2000;
155
+ runtime.diagAgentEndDurable++;
156
+ runtime.logger.info("agent-end-durable-trigger", {
157
+ sessionId: runtime.rt.sessionId,
158
+ ctxTokens: runtime.lastCtxTokens,
159
+ thresholdTokens: config.thresholdTokens,
160
+ queued,
161
+ });
162
+ if (config.raceGuardStrict) {
163
+ // Strict: defer ctx.compact() with a re-check so pi's
164
+ // about-to-run native _checkCompaction can append its
165
+ // `compaction` branch entry first. setTimeout(500) — pi's
166
+ // compaction-summary append is async I/O, so queueMicrotask
167
+ // would re-check before it lands.
168
+ const stamp = runtime.rt.lastNativeCompactAt;
169
+ const liveSid = runtime.rt.sessionId;
170
+ const liveCritical = criticalOver;
171
+ setTimeout(() => {
172
+ try {
173
+ if (runtime.rt.sessionId !== liveSid) return; // session reset
174
+ // IMPORTANT: recompute Date.now() inside the timeout.
175
+ // `now` above is the durable-trim handler's timestamp and can be
176
+ // ~500ms stale by the time this callback runs.
177
+ const since2 = Date.now() - (runtime.rt.lastNativeCompactAt ?? 0);
178
+ if (
179
+ runtime.rt.lastNativeCompactAt !== stamp &&
180
+ since2 < cooldownMs
181
+ )
182
+ return;
183
+ // CRITICAL-OVER ESCAPE HATCH: bypass the no-op gate when
184
+ // critical (captured at handler time) so the durable trim fires.
185
+ if (!liveCritical && piCompactWouldNoop(ctx)) return;
186
+ ctx.compact({
187
+ customInstructions: undefined,
188
+ }); // guardrails-allow PREVENT-PI-004: local ctx.compact() — no network; deferred + re-validated. May throw "Already compacted" when the escape hatch forces it — caught below.
189
+ } catch {
190
+ /* non-fatal: "Already compacted" throws are expected when the critical-over hatch forces a no-op compact */
191
+ }
192
+ }, 500);
193
+ } else {
194
+ try {
195
+ ctx.compact({ customInstructions: undefined }); // guardrails-allow PREVENT-PI-004: local ctx.compact() — no network; agent settled so no in-flight abort. May throw "Already compacted" when the critical-over escape hatch forces it — caught.
196
+ } catch {
197
+ /* non-fatal */
198
+ }
199
+ }
200
+ didDurableTrim = true;
201
+ }
202
+ }
203
+ // Restart the agent after a mid-run durable trim (which stopped it), or
204
+ // when it settled idle with queued work. Decoupled from `queued` for the
205
+ // durable-trim case — see FIX note above. Debounced 30s; never blocks.
206
+ const lengthStop =
207
+ config.autoContinueLengthStop && runtime.rt.lengthStopPending;
208
+ if (
209
+ idle &&
210
+ now >= runtime.resumeNudgeUntil &&
211
+ ((config.auto && (didDurableTrim || queued)) || lengthStop)
212
+ ) {
213
+ runtime.resumeNudgeUntil = now + 30_000;
214
+ if (runtime.rt.lengthStopPending) {
215
+ runtime.rt.lengthStopPending = false; // one-shot: never re-fire for same stop
216
+ runtime.dashboard.event("length_stop_continue", {
217
+ turnIndex: runtime.currentTurn,
218
+ });
219
+ runtime.logger.info("length_stop_continue", {
220
+ sessionId: runtime.rt.sessionId,
221
+ didDurableTrim,
222
+ queued,
223
+ });
224
+ }
225
+ // S28: when a length-stop (max-output-token truncation) fired WITHOUT a durable trim, do NOT claim a compaction happened
226
+ // (nothing was compacted on the low-pressure length path). Branch the message so the nudge matches reality.
227
+ const nudgeMsg =
228
+ lengthStop && !didDurableTrim
229
+ ? "[mega-compact] the last response hit the output-token cap; continue from where it stopped."
230
+ : "[mega-compact] continue from the compacted context above.";
231
+ await safeSendUserMessage(pi, nudgeMsg);
232
+ }
233
+ } catch {
234
+ /* non-fatal: a failed nudge never blocks */
235
+ }
236
+ }
237
+ runtime.snapshot(ctx);
238
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * turnEndHandler/cacheStripe.ts — P3.5 topic-shift stripe refresh.
3
+ *
4
+ * Extracted from turnEndHandler.ts (delegate-shell split) to keep every source
5
+ * file under the extensions limit. When MEGACOMPACT_CACHE_STRIPING is ON, embed
6
+ * the current turn, detect topic shift vs. the previous turn, and refresh cache
7
+ * stripe assignments when shifted (or first turn). Best-effort: failures log +
8
+ * continue, never break the agent loop.
9
+ */
10
+ import type { MegaRuntime } from "../../../mega-runtime.js";
11
+ import type { MegaConfig } from "../../../mega-config.js";
12
+ import { defaultEmbedder } from "../../../../src/embedder.js";
13
+ import { storeTopicEmbedding, loadTopicEmbedding, detectTopicShift } from "../../separated-prompt.js";
14
+ import { refreshStripeAssignments as writeStripeAssignments } from "../../../../src/cache-stripe-impl.js";
15
+ import type { TurnEndEvent } from "./event.js";
16
+
17
+ /** P3.5: topic-shift stripe refresh. Best-effort + non-fatal. */
18
+ export function cacheStripe(
19
+ event: TurnEndEvent,
20
+ runtime: MegaRuntime,
21
+ config: MegaConfig,
22
+ ): void {
23
+ if (config.cacheStriping) {
24
+ try {
25
+ const stateDir = runtime.currentStateDir;
26
+ const sessionId = runtime.rt.sessionId;
27
+ const currentTurn = event.turnIndex;
28
+ const embedder = defaultEmbedder();
29
+
30
+ // (a) Extract text from the assistant's response for topic embedding.
31
+ let textToEmbed = "";
32
+ 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 + " ";
37
+ }
38
+ }
39
+ }
40
+ textToEmbed = textToEmbed.trim() || `turn-${currentTurn}`;
41
+ const embedding = new Float32Array(embedder.embed(textToEmbed));
42
+ storeTopicEmbedding(stateDir, sessionId, currentTurn, embedding);
43
+
44
+ // (b) Load previous turn's embedding and detect topic shift.
45
+ const prevEmb = currentTurn > 0
46
+ ? loadTopicEmbedding(stateDir, sessionId, currentTurn - 1)
47
+ : null;
48
+ const shifted = currentTurn === 0 || detectTopicShift(embedding, prevEmb);
49
+
50
+ // (c) If shifted (or first turn), refresh stripe assignments (WRITE path).
51
+ let chunkCount = 0;
52
+ if (shifted) {
53
+ chunkCount = writeStripeAssignments(
54
+ stateDir,
55
+ undefined,
56
+ embedder,
57
+ (detail) => runtime.logger.info("stripe_refresh_detail", {
58
+ detail,
59
+ turnIndex: currentTurn,
60
+ }),
61
+ );
62
+ }
63
+
64
+ // (d) Log the refresh outcome.
65
+ runtime.logger.info("stripe_refresh", {
66
+ turnIndex: currentTurn,
67
+ shifted,
68
+ chunkCount,
69
+ });
70
+ } catch {
71
+ /* non-fatal: stripe refresh never breaks the agent loop */
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * turnEndHandler/contextHealth.ts — v0.12 context-health mitigation.
3
+ *
4
+ * Extracted from turnEndHandler.ts (delegate-shell split) to keep every source
5
+ * file under the extensions limit. Computes + persists the health score and
6
+ * acts on mitigation signals (force-compact / prefix-break) here at turn_end,
7
+ * where ctx is in scope. Non-fatal.
8
+ */
9
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
10
+ import type { MegaRuntime } from "../../../mega-runtime.js";
11
+ import { piCompactWouldNoop } from "../../../mega-pipeline.js";
12
+ import type { MegaConfig } from "../../../mega-config.js";
13
+ import { handleTurnEndHealth } from "../../health-handler.js";
14
+ import type { TurnEndEvent } from "./event.js";
15
+
16
+ /** v0.12: context-health mitigation. Non-fatal end-to-end. */
17
+ export function contextHealth(
18
+ event: TurnEndEvent,
19
+ ctx: ExtensionContext,
20
+ runtime: MegaRuntime,
21
+ config: MegaConfig,
22
+ ): void {
23
+ // v0.12: Context Health — compute + persist health score. Non-fatal.
24
+ // Mitigation signals are acted on here (ctx is in scope at turn_end).
25
+ const healthSignal = handleTurnEndHealth(event, runtime, config);
26
+ if (healthSignal.forceCompact) {
27
+ runtime.logger?.info("health_mitigate_compact", {
28
+ composite: healthSignal.composite,
29
+ turnIndex: event.turnIndex,
30
+ });
31
+ try {
32
+ // Force a compaction to flush degraded context. Same race-guarded
33
+ // deferred path as the critical-over escape hatch above.
34
+ if (!piCompactWouldNoop(ctx)) {
35
+ ctx.compact();
36
+ }
37
+ } catch {
38
+ /* non-fatal: mitigation never breaks the agent loop */
39
+ }
40
+ }
41
+ if (healthSignal.breakPrefix) {
42
+ runtime.logger?.info("health_mitigate_prefix_break", {
43
+ turnIndex: event.turnIndex,
44
+ });
45
+ // Invalidate the cached trim so the next context event regenerates
46
+ // from scratch — the prefix change forces a cache miss, bypassing
47
+ // the corrupted KV state.
48
+ runtime.trimCache = null;
49
+ }
50
+ }