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
@@ -1,2 +1,2 @@
1
- import{r as e}from"./index-D-lPLRWB.js";function R(i={}){const{maxEvents:c=500,maxReconnects:l=5}=i,[f,v]=e.useState([]),[E,n]=e.useState("connecting"),[S,p]=e.useState(0),[m,d]=e.useState(null),o=e.useRef(0),s=e.useRef(null),r=e.useCallback(()=>{s.current&&s.current.close(),n("connecting");const t=new EventSource("/api/events");s.current=t,t.onopen=()=>{n("connected"),o.current=0},t.onmessage=g=>{try{const x=JSON.parse(g.data);v(u=>{const a=[...u,x];return a.length>c?a.slice(-c):a}),p(u=>u+1),d(Date.now())}catch{}},t.onerror=()=>{t.close(),o.current<l?(o.current+=1,n("connecting"),setTimeout(r,1e3)):n("disconnected")}},[c,l]);return e.useEffect(()=>(r(),()=>{s.current?.close()}),[r]),{events:f,status:E,eventCount:S,lastEventAt:m}}export{R as u};
2
- //# sourceMappingURL=useSSE-ClbbcgIp.js.map
1
+ import{r as e}from"./index-mOlljTFI.js";function R(i={}){const{maxEvents:c=500,maxReconnects:l=5}=i,[f,v]=e.useState([]),[E,n]=e.useState("connecting"),[S,p]=e.useState(0),[m,d]=e.useState(null),o=e.useRef(0),s=e.useRef(null),r=e.useCallback(()=>{s.current&&s.current.close(),n("connecting");const t=new EventSource("/api/events");s.current=t,t.onopen=()=>{n("connected"),o.current=0},t.onmessage=g=>{try{const x=JSON.parse(g.data);v(u=>{const a=[...u,x];return a.length>c?a.slice(-c):a}),p(u=>u+1),d(Date.now())}catch{}},t.onerror=()=>{t.close(),o.current<l?(o.current+=1,n("connecting"),setTimeout(r,1e3)):n("disconnected")}},[c,l]);return e.useEffect(()=>(r(),()=>{s.current?.close()}),[r]),{events:f,status:E,eventCount:S,lastEventAt:m}}export{R as u};
2
+ //# sourceMappingURL=useSSE-BYOtv1-3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSSE-ClbbcgIp.js","sources":["../../src/hooks/useSSE.ts"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect"],"mappings":"wCA6BO,SAASA,EAAOC,EAAyB,GAAkB,CAChE,KAAM,CAAE,UAAAC,EAAY,IAAK,cAAAC,EAAgB,GAAMF,EACzC,CAACG,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,CAAA,CAAE,EAC7C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAoB,YAAY,EACtD,CAACG,EAAYC,CAAa,EAAIJ,EAAAA,SAAS,CAAC,EACxC,CAACK,EAAaC,CAAc,EAAIN,EAAAA,SAAwB,IAAI,EAC5DO,EAAiBC,EAAAA,OAAO,CAAC,EACzBC,EAAYD,EAAAA,OAA2B,IAAI,EAE3CE,EAAUC,EAAAA,YAAY,IAAM,CAC5BF,EAAU,SACZA,EAAU,QAAQ,MAAA,EAEpBP,EAAU,YAAY,EACtB,MAAMU,EAAS,IAAI,YAAY,aAAa,EAC5CH,EAAU,QAAUG,EAEpBA,EAAO,OAAS,IAAM,CACpBV,EAAU,WAAW,EACrBK,EAAe,QAAU,CAC3B,EAEAK,EAAO,UAAaC,GAAU,CAC5B,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,EAAM,IAAI,EACpCd,EAAUgB,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMD,CAAM,EAC7B,OAAOE,EAAK,OAASpB,EAAYoB,EAAK,MAAM,CAACpB,CAAS,EAAIoB,CAC5D,CAAC,EACDZ,EAAca,GAAKA,EAAI,CAAC,EACxBX,EAAe,KAAK,KAAK,CAC3B,MAAQ,CAER,CACF,EAEAM,EAAO,QAAU,IAAM,CACrBA,EAAO,MAAA,EACHL,EAAe,QAAUV,GAC3BU,EAAe,SAAW,EAC1BL,EAAU,YAAY,EAEtB,WAAWQ,EAAS,GAAI,GAExBR,EAAU,cAAc,CAE5B,CACF,EAAG,CAACN,EAAWC,CAAa,CAAC,EAE7BqB,OAAAA,EAAAA,UAAU,KACRR,EAAA,EACO,IAAM,CACXD,EAAU,SAAS,MAAA,CACrB,GACC,CAACC,CAAO,CAAC,EAEL,CAAE,OAAAZ,EAAQ,OAAAG,EAAQ,WAAAE,EAAY,YAAAE,CAAA,CACvC"}
1
+ {"version":3,"file":"useSSE-BYOtv1-3.js","sources":["../../src/hooks/useSSE.ts"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect"],"mappings":"wCA6BO,SAASA,EAAOC,EAAyB,GAAkB,CAChE,KAAM,CAAE,UAAAC,EAAY,IAAK,cAAAC,EAAgB,GAAMF,EACzC,CAACG,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,CAAA,CAAE,EAC7C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAoB,YAAY,EACtD,CAACG,EAAYC,CAAa,EAAIJ,EAAAA,SAAS,CAAC,EACxC,CAACK,EAAaC,CAAc,EAAIN,EAAAA,SAAwB,IAAI,EAC5DO,EAAiBC,EAAAA,OAAO,CAAC,EACzBC,EAAYD,EAAAA,OAA2B,IAAI,EAE3CE,EAAUC,EAAAA,YAAY,IAAM,CAC5BF,EAAU,SACZA,EAAU,QAAQ,MAAA,EAEpBP,EAAU,YAAY,EACtB,MAAMU,EAAS,IAAI,YAAY,aAAa,EAC5CH,EAAU,QAAUG,EAEpBA,EAAO,OAAS,IAAM,CACpBV,EAAU,WAAW,EACrBK,EAAe,QAAU,CAC3B,EAEAK,EAAO,UAAaC,GAAU,CAC5B,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,EAAM,IAAI,EACpCd,EAAUgB,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMD,CAAM,EAC7B,OAAOE,EAAK,OAASpB,EAAYoB,EAAK,MAAM,CAACpB,CAAS,EAAIoB,CAC5D,CAAC,EACDZ,EAAca,GAAKA,EAAI,CAAC,EACxBX,EAAe,KAAK,KAAK,CAC3B,MAAQ,CAER,CACF,EAEAM,EAAO,QAAU,IAAM,CACrBA,EAAO,MAAA,EACHL,EAAe,QAAUV,GAC3BU,EAAe,SAAW,EAC1BL,EAAU,YAAY,EAEtB,WAAWQ,EAAS,GAAI,GAExBR,EAAU,cAAc,CAE5B,CACF,EAAG,CAACN,EAAWC,CAAa,CAAC,EAE7BqB,OAAAA,EAAAA,UAAU,KACRR,EAAA,EACO,IAAM,CACXD,EAAU,SAAS,MAAA,CACrB,GACC,CAACC,CAAO,CAAC,EAEL,CAAE,OAAAZ,EAAQ,OAAAG,EAAQ,WAAAE,EAAY,YAAAE,CAAA,CACvC"}
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>mega-compact dashboard</title>
7
- <script type="module" crossorigin src="./assets/index-D-lPLRWB.js"></script>
7
+ <script type="module" crossorigin src="./assets/index-mOlljTFI.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="./assets/index-CUNn22iV.css">
9
9
  </head>
10
10
  <body>
@@ -17,7 +17,6 @@ import type { SnapshotResponse } from "@contracts";
17
17
  const OverviewTab = React.lazy(() => import("./tabs/OverviewTab"));
18
18
  const ReposTab = React.lazy(() => import("./tabs/ReposTab"));
19
19
  const EventsTab = React.lazy(() => import("./tabs/EventsTab"));
20
- const ConfigTab = React.lazy(() => import("./tabs/ConfigTab"));
21
20
  const SetupTab = React.lazy(() => import("./tabs/SetupTab"));
22
21
  const MetricsTab = React.lazy(() => import("./tabs/MetricsTab"));
23
22
  const CacheTab = React.lazy(() => import("./tabs/CacheTab"));
@@ -32,7 +31,6 @@ export type TabId =
32
31
  | "overview"
33
32
  | "repos"
34
33
  | "events"
35
- | "config"
36
34
  | "setup"
37
35
  | "metrics"
38
36
  | "cache"
@@ -56,7 +54,6 @@ const PRIMARY_TABS: TabDef[] = [
56
54
  const ADVANCED_TABS: TabDef[] = [
57
55
  { id: "repos", label: "Repos" },
58
56
  { id: "events", label: "Events" },
59
- { id: "config", label: "Config" },
60
57
  { id: "setup", label: "Setup" },
61
58
  { id: "metrics", label: "Metrics" },
62
59
  { id: "topics", label: "Topics" },
@@ -111,7 +108,6 @@ export default function App(): React.ReactElement {
111
108
  )}
112
109
  {activeTab === "repos" && <ReposTab />}
113
110
  {activeTab === "events" && <EventsTab />}
114
- {activeTab === "config" && <ConfigTab />}
115
111
  {activeTab === "setup" && <SetupTab />}
116
112
  {activeTab === "metrics" && <MetricsTab />}
117
113
  {activeTab === "cache" && <CacheTab />}
@@ -46,6 +46,8 @@ import type {
46
46
  SetupDetectResponse,
47
47
  SetupConfigureRequest,
48
48
  SetupConfigureResponse,
49
+ EmbedderHealthResponse,
50
+ RaptorTreeResponse,
49
51
  } from "@contracts";
50
52
 
51
53
  /** Error thrown when a dashboard API response is not 2xx. */
@@ -307,3 +309,17 @@ export function configureEmbedder(
307
309
  ): Promise<SetupConfigureResponse> {
308
310
  return postJson<SetupConfigureResponse>(ENDPOINTS.setupConfigure.path, body);
309
311
  }
312
+
313
+ // ─── RAPTOR tree (Part B) ─────────────────────────────────────────────
314
+
315
+ /** GET /api/raptor-tree — tree for a session (defaults to latest with nodes). */
316
+ export function fetchRaptorTree(sessionId?: string): Promise<RaptorTreeResponse> {
317
+ return getJson<RaptorTreeResponse>(
318
+ `${ENDPOINTS.raptorTree.path}${query({ sessionId })}`,
319
+ );
320
+ }
321
+
322
+ /** GET /api/embedder-health — round-trip a test embed through the active embedder. */
323
+ export function fetchEmbedderHealth(): Promise<EmbedderHealthResponse> {
324
+ return getJson<EmbedderHealthResponse>(ENDPOINTS.embedderHealth.path);
325
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx — Actions card.
3
+ *
4
+ * Extracted from MaintenanceTab.tsx (delegate-shell split). One-time
5
+ * maintenance operations (vacuum, checkpoint, reindex, prune, …) with a
6
+ * results history.
7
+ */
8
+ import type React from "react";
9
+ import { useState, useCallback } from "react";
10
+ import type {
11
+ MaintenanceAction,
12
+ MaintenanceActionResult,
13
+ } from "@contracts";
14
+ import { postMaintenanceAction } from "../../api/client";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Action config
18
+ // ---------------------------------------------------------------------------
19
+
20
+ interface ActionDef {
21
+ key: string;
22
+ label: string;
23
+ desc: string;
24
+ dangerous: boolean;
25
+ confirm?: string;
26
+ daysOld?: boolean;
27
+ }
28
+
29
+ const ACTIONS: ActionDef[] = [
30
+ {
31
+ key: "integrity-check",
32
+ label: "Integrity Check",
33
+ desc: "Run PRAGMA integrity_check",
34
+ dangerous: false,
35
+ },
36
+ {
37
+ key: "reconcile-dedup",
38
+ label: "Reconcile Dedup Mirror",
39
+ desc: "Fix orphan dedup rows + backfill refs",
40
+ dangerous: false,
41
+ confirm: "Reconcile dedup_mirror (safe read/write)?",
42
+ },
43
+ {
44
+ key: "checkpoint",
45
+ label: "WAL Checkpoint",
46
+ desc: "Merge WAL into main DB (read-only)",
47
+ dangerous: false,
48
+ },
49
+ {
50
+ key: "vacuum",
51
+ label: "VACUUM",
52
+ desc: "Rebuild DB reclaiming free pages",
53
+ dangerous: true,
54
+ confirm: "VACUUM rewrites the entire DB. Continue?",
55
+ },
56
+ {
57
+ key: "reindex",
58
+ label: "REINDEX",
59
+ desc: "Rebuild all indexes",
60
+ dangerous: true,
61
+ confirm: "REINDEX rebuilds indexes. Continue?",
62
+ },
63
+ {
64
+ key: "fts5-rebuild",
65
+ label: "FTS5 Rebuild",
66
+ desc: "Rebuild full-text search index",
67
+ dangerous: true,
68
+ confirm: "Rebuild FTS5 trigram index. Continue?",
69
+ },
70
+ {
71
+ key: "prune",
72
+ label: "Prune Old Rows",
73
+ desc: "Delete transcripts + epochs older than N days",
74
+ dangerous: true,
75
+ daysOld: true,
76
+ confirm: "Delete old raw_transcript rows. Continue?",
77
+ },
78
+ ];
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Actions card
82
+ // ---------------------------------------------------------------------------
83
+
84
+ export function ActionsCard(): React.ReactElement {
85
+ const [results, setResults] = useState<MaintenanceActionResult[]>([]);
86
+ const [running, setRunning] = useState<string | null>(null);
87
+ const [days, setDays] = useState("30");
88
+
89
+ const handleAction = useCallback(
90
+ async (def: ActionDef) => {
91
+ const action: MaintenanceAction = def.daysOld
92
+ ? { action: "prune", daysOld: Number(days) }
93
+ : ({ action: def.key } as MaintenanceAction);
94
+
95
+ setRunning(def.key);
96
+ try {
97
+ const res = await postMaintenanceAction(action);
98
+ setResults((prev) =>
99
+ [{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),
100
+ );
101
+ } catch (err) {
102
+ setResults((prev) =>
103
+ [
104
+ {
105
+ operation: def.key,
106
+ success: false,
107
+ affected: 0,
108
+ reclaimedBytes: 0,
109
+ summary: err instanceof Error ? err.message : String(err),
110
+ timestamp: Date.now(),
111
+ },
112
+ ...prev,
113
+ ].slice(0, 20),
114
+ );
115
+ } finally {
116
+ setRunning(null);
117
+ }
118
+ },
119
+ [days],
120
+ );
121
+
122
+ return (
123
+ <div className="card">
124
+ <div className="card-header">Actions</div>
125
+ <div className="card-body">
126
+ <div className="action-grid">
127
+ {ACTIONS.map((def) => {
128
+ const isRunning = running === def.key;
129
+ const lastResult = results.find((r) => r.operation === def.key);
130
+ return (
131
+ <div key={def.key} className="action-item">
132
+ <div className="action-info">
133
+ <div className="action-label">
134
+ {def.label}
135
+ {def.dangerous && (
136
+ <span className="badge badge-warn">DESTRUCTIVE</span>
137
+ )}
138
+ </div>
139
+ <div className="action-desc">{def.desc}</div>
140
+ {lastResult && (
141
+ <div
142
+ className={`action-result ${lastResult.success ? "text-ok" : "text-error"}`}
143
+ >
144
+ {lastResult.summary}
145
+ </div>
146
+ )}
147
+ </div>
148
+ <div className="action-controls">
149
+ {def.daysOld && (
150
+ <input
151
+ type="number"
152
+ min={1}
153
+ max={365}
154
+ value={days}
155
+ onChange={(e) => setDays(e.target.value)}
156
+ className="days-input"
157
+ disabled={isRunning}
158
+ />
159
+ )}
160
+ <button
161
+ className={`btn ${def.dangerous ? "btn-danger" : "btn-primary"}`}
162
+ disabled={running !== null}
163
+ onClick={() => {
164
+ if (def.confirm && !window.confirm(def.confirm)) return;
165
+ handleAction(def);
166
+ }}
167
+ >
168
+ {isRunning ? "Running…" : "Run"}
169
+ </button>
170
+ </div>
171
+ </div>
172
+ );
173
+ })}
174
+ </div>
175
+ </div>
176
+ </div>
177
+ );
178
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx — DB Stats card.
3
+ *
4
+ * Extracted from MaintenanceTab.tsx (delegate-shell split). Renders file sizes
5
+ * and table row counts from GET /api/maintenance.
6
+ */
7
+ import type React from "react";
8
+ import type { DbStatsResponse } from "@contracts";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Format helpers (local — matches html.ts fmtBytes)
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export function fmtBytes(b: number): string {
15
+ if (b < 1024) return `${b} B`;
16
+ if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;
17
+ return `${(b / (1024 * 1024)).toFixed(1)} MB`;
18
+ }
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // DB Stats card
22
+ // ---------------------------------------------------------------------------
23
+
24
+ function FileSizesCard({
25
+ files,
26
+ pageSize,
27
+ pageCount,
28
+ freelistPages,
29
+ }: {
30
+ files: DbStatsResponse["storage"]["files"];
31
+ pageSize: number;
32
+ pageCount: number;
33
+ freelistPages: number;
34
+ }): React.ReactElement {
35
+ const total = files.dbBytes + files.walBytes + files.shmBytes;
36
+ return (
37
+ <div className="stat-section">
38
+ <div className="stat-label">File Sizes</div>
39
+ <table className="compact-table">
40
+ <tbody>
41
+ <tr>
42
+ <td>sqlite.db</td>
43
+ <td className="num">{fmtBytes(files.dbBytes)}</td>
44
+ </tr>
45
+ {files.walBytes > 0 && (
46
+ <tr>
47
+ <td>.wal</td>
48
+ <td className="num">{fmtBytes(files.walBytes)}</td>
49
+ </tr>
50
+ )}
51
+ {files.shmBytes > 0 && (
52
+ <tr>
53
+ <td>.shm</td>
54
+ <td className="num">{fmtBytes(files.shmBytes)}</td>
55
+ </tr>
56
+ )}
57
+ <tr>
58
+ <td>Total</td>
59
+ <td className="num">{fmtBytes(total)}</td>
60
+ </tr>
61
+ <tr>
62
+ <td>Page size</td>
63
+ <td className="num">{fmtBytes(pageSize)}</td>
64
+ </tr>
65
+ <tr>
66
+ <td>Pages</td>
67
+ <td className="num">
68
+ {pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{" "}
69
+ free)
70
+ </td>
71
+ </tr>
72
+ </tbody>
73
+ </table>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ function TableRowCountsCard({
79
+ tables,
80
+ }: {
81
+ tables: DbStatsResponse["tables"];
82
+ }): React.ReactElement {
83
+ const totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);
84
+ return (
85
+ <div className="stat-section">
86
+ <div className="stat-label">
87
+ Table Row Counts ({totalRows.toLocaleString()} total)
88
+ </div>
89
+ <table className="compact-table">
90
+ <thead>
91
+ <tr>
92
+ <th>Table</th>
93
+ <th className="num">Rows</th>
94
+ </tr>
95
+ </thead>
96
+ <tbody>
97
+ {tables.map((t) => (
98
+ <tr key={t.table}>
99
+ <td>{t.table}</td>
100
+ <td className="num">
101
+ {t.rowCount >= 0 ? t.rowCount.toLocaleString() : "—"}
102
+ </td>
103
+ </tr>
104
+ ))}
105
+ </tbody>
106
+ </table>
107
+ </div>
108
+ );
109
+ }
110
+
111
+ export function DbStatsCard({
112
+ data,
113
+ loading,
114
+ error,
115
+ }: {
116
+ data: DbStatsResponse | null;
117
+ loading: boolean;
118
+ error: Error | null;
119
+ }): React.ReactElement {
120
+ return (
121
+ <div className="card">
122
+ <div className="card-header">DB Stats</div>
123
+ <div className="card-body">
124
+ {loading && !data && <span className="text-muted">Loading…</span>}
125
+ {error && !data && <span className="text-error">{error.message}</span>}
126
+ {data && (
127
+ <>
128
+ <FileSizesCard
129
+ files={data.storage.files}
130
+ pageSize={data.storage.pageSize}
131
+ pageCount={data.storage.pageCount}
132
+ freelistPages={data.storage.freelistPages}
133
+ />
134
+ <TableRowCountsCard tables={data.tables} />
135
+ </>
136
+ )}
137
+ </div>
138
+ </div>
139
+ );
140
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx — Debug Bundle card.
3
+ *
4
+ * Extracted from MaintenanceTab.tsx (delegate-shell split). Gather a diagnostic
5
+ * bundle (events, config, schema health, store stats) and let the user copy or
6
+ * download it for bug reports.
7
+ */
8
+ import type React from "react";
9
+ import { useState, useCallback } from "react";
10
+ import type { DebugBundleResponse } from "@contracts";
11
+ import { fetchDebugBundle } from "../../api/client";
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Debug bundle card — gather diagnostic info for bug reports
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export function DebugBundleCard(): React.ReactElement {
18
+ const [bundle, setBundle] = useState<DebugBundleResponse | null>(null);
19
+ const [loading, setLoading] = useState(false);
20
+ const [error, setError] = useState<string | null>(null);
21
+ const [showJson, setShowJson] = useState(false);
22
+
23
+ const gather = useCallback(async () => {
24
+ setLoading(true);
25
+ setError(null);
26
+ try {
27
+ const res = await fetchDebugBundle();
28
+ setBundle(res);
29
+ setShowJson(true);
30
+ } catch (err) {
31
+ setError(err instanceof Error ? err.message : String(err));
32
+ } finally {
33
+ setLoading(false);
34
+ }
35
+ }, []);
36
+
37
+ const copyToClipboard = useCallback(() => {
38
+ if (!bundle) return;
39
+ const text = JSON.stringify(bundle, null, 2);
40
+ void navigator.clipboard.writeText(text).catch(() => {});
41
+ }, [bundle]);
42
+
43
+ const downloadJson = useCallback(() => {
44
+ if (!bundle) return;
45
+ const text = JSON.stringify(bundle, null, 2);
46
+ const blob = new Blob([text], { type: "application/json" });
47
+ const url = URL.createObjectURL(blob);
48
+ const a = document.createElement("a");
49
+ a.href = url;
50
+ a.download = `mega-compact-debug-bundle-${bundle.builtAt}.json`;
51
+ a.click();
52
+ URL.revokeObjectURL(url);
53
+ }, [bundle]);
54
+
55
+ const criticalCount = bundle?.criticalEvents.length ?? 0;
56
+
57
+ return (
58
+ <div className="card">
59
+ <div className="card-header">
60
+ Debug Bundle
61
+ {criticalCount > 0 && (
62
+ <span className="text-warn" style={{ marginLeft: "1em" }}>
63
+ {criticalCount} critical event{criticalCount !== 1 ? "s" : ""} found
64
+ </span>
65
+ )}
66
+ </div>
67
+ <div className="card-body">
68
+ <p className="text-muted" style={{ marginBottom: "0.75em" }}>
69
+ Gather a diagnostic bundle (recent events, config flags, schema health,
70
+ store stats) to attach to a bug report. Critical/compaction events are
71
+ highlighted.
72
+ </p>
73
+ <button onClick={gather} disabled={loading} style={{ marginRight: "0.5em" }}>
74
+ {loading ? "Gathering…" : "Gather Debug Logs"}
75
+ </button>
76
+ {error && <div className="text-error">{error}</div>}
77
+ {bundle && showJson && (
78
+ <>
79
+ <div style={{ marginTop: "0.75em", marginBottom: "0.5em" }}>
80
+ <button onClick={copyToClipboard} style={{ marginRight: "0.5em" }}>
81
+ Copy to clipboard
82
+ </button>
83
+ <button onClick={downloadJson}>Download JSON</button>
84
+ <button
85
+ onClick={() => setShowJson((v) => !v)}
86
+ style={{ marginLeft: "0.5em" }}
87
+ >
88
+ {showJson ? "Hide" : "Show"}
89
+ </button>
90
+ </div>
91
+ {criticalCount > 0 && (
92
+ <div className="stat-section">
93
+ <div className="stat-label text-warn">
94
+ Critical events ({criticalCount})
95
+ </div>
96
+ <pre className="text-mono" style={{ maxHeight: "12em", overflow: "auto" }}>
97
+ {JSON.stringify(bundle.criticalEvents, null, 2)}
98
+ </pre>
99
+ </div>
100
+ )}
101
+ <pre className="text-mono" style={{ maxHeight: "24em", overflow: "auto" }}>
102
+ {JSON.stringify(bundle, null, 2)}
103
+ </pre>
104
+ </>
105
+ )}
106
+ </div>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx — Health
3
+ * Mitigation card.
4
+ *
5
+ * Extracted from MaintenanceTab.tsx (delegate-shell split). Toggles auto-
6
+ * mitigation for degraded context (forced compaction / prefix break).
7
+ */
8
+ import type React from "react";
9
+ import { useState, useCallback, useEffect } from "react";
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Health Mitigation card — toggle auto-mitigation for degraded context
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export function HealthMitigationCard(): React.ReactElement {
16
+ const [mitigate, setMitigate] = useState(false);
17
+ const [loading, setLoading] = useState(true);
18
+ const [saving, setSaving] = useState(false);
19
+ const [error, setError] = useState<string | null>(null);
20
+
21
+ const load = useCallback(async () => {
22
+ try {
23
+ const { fetchHealthSettings } = await import("../../api/health");
24
+ const s = await fetchHealthSettings();
25
+ setMitigate(s.mitigate);
26
+ } catch (e) {
27
+ setError(e instanceof Error ? e.message : String(e));
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ }, []);
32
+
33
+ useEffect(() => { void load(); }, [load]);
34
+
35
+ const toggle = useCallback(async () => {
36
+ setSaving(true);
37
+ setError(null);
38
+ try {
39
+ const { setHealthMitigate } = await import("../../api/health");
40
+ const next = !mitigate;
41
+ await setHealthMitigate(next);
42
+ setMitigate(next);
43
+ } catch (e) {
44
+ setError(e instanceof Error ? e.message : String(e));
45
+ } finally {
46
+ setSaving(false);
47
+ }
48
+ }, [mitigate]);
49
+
50
+ return (
51
+ <div className="stat-section maintenance-card">
52
+ <div className="stat-label">Context Health Mitigation</div>
53
+ {loading ? (
54
+ <p className="repo-none">Loading…</p>
55
+ ) : (
56
+ <>
57
+ <div className="health-mitigate-row">
58
+ <label className="health-mitigate-toggle">
59
+ <input
60
+ type="checkbox"
61
+ checked={mitigate}
62
+ onChange={toggle}
63
+ disabled={saving}
64
+ />
65
+ <span>Auto-mitigation {mitigate ? "ON" : "OFF"}</span>
66
+ </label>
67
+ </div>
68
+ <div className="health-mitigate-info">
69
+ <p>
70
+ When <strong>ON</strong>, the extension automatically intervenes when
71
+ context health degrades:
72
+ </p>
73
+ <ul>
74
+ <li><strong>Forced compaction</strong> — when the composite health score
75
+ drops below 0.4 (severe drift or garbled output), a compaction is
76
+ triggered to flush the degraded context and rebuild from clean
77
+ checkpoints, even if the context window isn&apos;t full.</li>
78
+ <li><strong>Prefix break</strong> — when KV cache poison is detected
79
+ (score &lt; 0.3), the cached prefix is invalidated to force a cache
80
+ miss on the next turn, bypassing the corrupted KV state.</li>
81
+ </ul>
82
+ <p className="health-mitigate-warning">
83
+ WARNING: both interventions have costs — forced compaction discards
84
+ live context, and prefix breaks kill cache savings. Only enable when
85
+ you&apos;re seeing degraded output (garbled text, hallucinations,
86
+ loss of coherence) and the Health tab confirms low scores.
87
+ </p>
88
+ <p>
89
+ Also settable via env var: <code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>.
90
+ The dashboard toggle persists to the store and takes precedence.
91
+ </p>
92
+ {error && <p className="health-mitigate-error">Error: {error}</p>}
93
+ </div>
94
+ </>
95
+ )}
96
+ </div>
97
+ );
98
+ }