pi-mega-compact 0.12.7 → 0.13.0

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 (51) hide show
  1. package/dist/extensions/dashboard-server/routes-setup.js +20 -1
  2. package/dist/extensions/mega-runtime/env-loader.js +6 -1
  3. package/dist/src/httpEmbedder.js +24 -11
  4. package/extensions/dashboard-client/dist/assets/{CacheTab-Bjge1v_l.js → CacheTab-BZNnKhh5.js} +2 -2
  5. package/extensions/dashboard-client/dist/assets/{CacheTab-Bjge1v_l.js.map → CacheTab-BZNnKhh5.js.map} +1 -1
  6. package/extensions/dashboard-client/dist/assets/{ConfigTab-BlbRWORC.js → ConfigTab-DG0WkuuG.js} +2 -2
  7. package/extensions/dashboard-client/dist/assets/{ConfigTab-BlbRWORC.js.map → ConfigTab-DG0WkuuG.js.map} +1 -1
  8. package/extensions/dashboard-client/dist/assets/{EventsTab-BrLTX5EP.js → EventsTab-CzF5L42U.js} +2 -2
  9. package/extensions/dashboard-client/dist/assets/{EventsTab-BrLTX5EP.js.map → EventsTab-CzF5L42U.js.map} +1 -1
  10. package/extensions/dashboard-client/dist/assets/{HealthTab-0LsKCRFE.js → HealthTab-BLpidMrX.js} +2 -2
  11. package/extensions/dashboard-client/dist/assets/{HealthTab-0LsKCRFE.js.map → HealthTab-BLpidMrX.js.map} +1 -1
  12. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-C4L2Ohea.js → MaintenanceTab-BQF7W7xX.js} +2 -2
  13. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-C4L2Ohea.js.map → MaintenanceTab-BQF7W7xX.js.map} +1 -1
  14. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DMMQBq5X.js → MemoryMapTab-CPtHLv0A.js} +2 -2
  15. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DMMQBq5X.js.map → MemoryMapTab-CPtHLv0A.js.map} +1 -1
  16. package/extensions/dashboard-client/dist/assets/{MetricsTab-BscQn0ej.js → MetricsTab-B0PCysqs.js} +2 -2
  17. package/extensions/dashboard-client/dist/assets/{MetricsTab-BscQn0ej.js.map → MetricsTab-B0PCysqs.js.map} +1 -1
  18. package/extensions/dashboard-client/dist/assets/{OverviewTab-D3ryhlRc.js → OverviewTab-DsOhqFu4.js} +2 -2
  19. package/extensions/dashboard-client/dist/assets/{OverviewTab-D3ryhlRc.js.map → OverviewTab-DsOhqFu4.js.map} +1 -1
  20. package/extensions/dashboard-client/dist/assets/{ReposTab-Ds9VkpDu.js → ReposTab-B3YKYuOf.js} +2 -2
  21. package/extensions/dashboard-client/dist/assets/{ReposTab-Ds9VkpDu.js.map → ReposTab-B3YKYuOf.js.map} +1 -1
  22. package/extensions/dashboard-client/dist/assets/{SessionsTab-D4Ez9XCI.js → SessionsTab-B96FWcrC.js} +2 -2
  23. package/extensions/dashboard-client/dist/assets/{SessionsTab-D4Ez9XCI.js.map → SessionsTab-B96FWcrC.js.map} +1 -1
  24. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js +2 -0
  25. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js.map +1 -0
  26. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-D6S_QMPY.js → TimeSavedCard-BfkUVTvA.js} +2 -2
  27. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-D6S_QMPY.js.map → TimeSavedCard-BfkUVTvA.js.map} +1 -1
  28. package/extensions/dashboard-client/dist/assets/{TopicsTab-DYMNWd0N.js → TopicsTab-DLRsZmXJ.js} +2 -2
  29. package/extensions/dashboard-client/dist/assets/{TopicsTab-DYMNWd0N.js.map → TopicsTab-DLRsZmXJ.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{TurnsTab-DpQgbzKj.js → TurnsTab-DKGU2Bf6.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{TurnsTab-DpQgbzKj.js.map → TurnsTab-DKGU2Bf6.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{index-m3rSvxMW.js → index-D-lPLRWB.js} +11 -11
  33. package/extensions/dashboard-client/dist/assets/{index-m3rSvxMW.js.map → index-D-lPLRWB.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/{useSSE-JtuzXAZy.js → useSSE-ClbbcgIp.js} +2 -2
  35. package/extensions/dashboard-client/dist/assets/{useSSE-JtuzXAZy.js.map → useSSE-ClbbcgIp.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/index.html +1 -1
  37. package/extensions/dashboard-client/src/App.tsx +0 -8
  38. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +118 -3
  39. package/extensions/dashboard-server/api-contracts/setup.ts +5 -3
  40. package/extensions/dashboard-server/routes-setup.ts +19 -2
  41. package/extensions/mega-runtime/env-loader.ts +6 -1
  42. package/package.json +3 -1
  43. package/src/httpEmbedder.ts +25 -11
  44. package/extensions/dashboard-client/dist/assets/AchievementTiles-B4KbY2gd.js +0 -2
  45. package/extensions/dashboard-client/dist/assets/AchievementTiles-B4KbY2gd.js.map +0 -1
  46. package/extensions/dashboard-client/dist/assets/AchievementsTab-CT2H3e9d.js +0 -2
  47. package/extensions/dashboard-client/dist/assets/AchievementsTab-CT2H3e9d.js.map +0 -1
  48. package/extensions/dashboard-client/dist/assets/GameTab-CYWnWrwC.js +0 -2
  49. package/extensions/dashboard-client/dist/assets/GameTab-CYWnWrwC.js.map +0 -1
  50. package/extensions/dashboard-client/dist/assets/SetupTab-DwXPFzgS.js +0 -2
  51. package/extensions/dashboard-client/dist/assets/SetupTab-DwXPFzgS.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EventsTab-BrLTX5EP.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
1
+ {"version":3,"file":"EventsTab-CzF5L42U.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as o,j as e}from"./index-m3rSvxMW.js";import{fetchContextHealth as u}from"./health-lSeDFTz7.js";function d(t){return t>.8?"#4ade80":t>=.5?"#facc15":"#f87171"}function f({score:t}){const a=2*Math.PI*70,h=a*(1-Math.max(0,Math.min(1,t))),n=d(t);return e.jsxs("div",{className:"health-gauge-wrap",children:[e.jsxs("svg",{width:"180",height:"180",viewBox:"0 0 180 180",children:[e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:"#1e293b",strokeWidth:"14"}),e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:n,strokeWidth:"14",strokeDasharray:a,strokeDashoffset:h,strokeLinecap:"round",transform:"rotate(-90 90 90)",style:{transition:"stroke-dashoffset 0.5s ease"}}),e.jsx("text",{x:"90",y:"85",textAnchor:"middle",fontSize:"32",fontWeight:"bold",fill:n,children:(t*100).toFixed(0)}),e.jsx("text",{x:"90",y:"108",textAnchor:"middle",fontSize:"14",fill:"#94a3b8",children:"/ 100"})]}),e.jsx("div",{className:"health-gauge-label",children:"Context Health"})]})}function p({trend:t}){if(t.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const l=400,a=60,h=1,n=0,x=t.map((r,j)=>{const s=j/(t.length-1)*l,m=a-(r-n)/(h-n)*a;return`${s.toFixed(1)},${m.toFixed(1)}`}),c=t[t.length-1];return e.jsx("svg",{width:l,height:a,viewBox:`0 0 ${l} ${a}`,className:"health-spark",children:e.jsx("polyline",{points:x.join(" "),fill:"none",stroke:d(c),strokeWidth:"2"})})}function i({label:t,value:l}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:t}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${l*100}%`,background:d(l)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(l*100).toFixed(0),"%"]})]})}function b(){const[t,l]=o.useState(null),[a,h]=o.useState(null),[n,x]=o.useState(!0),c=o.useCallback(()=>{u().then(l).catch(s=>h(s instanceof Error?s.message:String(s))).finally(()=>x(!1))},[]);if(o.useEffect(()=>{c();const s=setInterval(c,5e3);return()=>clearInterval(s)},[c]),n&&!t)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(a&&!t)return e.jsxs("div",{className:"health-error",children:["Error: ",a]});if(!t)return e.jsx("div",{className:"health-empty",children:"No health data available."});const r=t.latest,j=r?.composite??0;return e.jsxs("div",{className:"health-tab",children:[e.jsxs("div",{className:"health-top-row",children:[e.jsx(f,{score:j}),e.jsxs("div",{className:"health-subscores",children:[e.jsx("h3",{children:"Sub-Scores"}),e.jsx(i,{label:"Drift",value:r?.driftScore??0}),e.jsx(i,{label:"Output Quality",value:r?.outputQuality??0}),e.jsx(i,{label:"Error Rate",value:r?.errorScore??0}),e.jsx(i,{label:"Cache Health",value:r?.cacheHealth??0}),e.jsx(i,{label:"Cache Poison",value:r?.cachePoison??0})]})]}),e.jsxs("div",{className:"health-trend-section",children:[e.jsxs("h3",{children:["Health Trend (last ",t.trend.length," turns)"]}),e.jsx(p,{trend:t.trend})]}),t.alerts.length>0&&e.jsxs("div",{className:"health-alerts",children:[e.jsxs("h3",{children:["Recent Alerts (",t.alerts.length,")"]}),e.jsx("div",{className:"health-alert-list",children:t.alerts.slice(-10).map(s=>e.jsxs("div",{className:"health-alert-row",children:[e.jsx("span",{className:"health-alert-time",children:new Date(s.ts).toLocaleTimeString()}),e.jsx("span",{className:"health-alert-model",children:s.modelId??"(unknown)"}),e.jsx("span",{className:"health-alert-score",style:{color:d(s.composite)},children:(s.composite*100).toFixed(0)}),s.cachePoison<.3&&e.jsx("span",{className:"health-alert-tag",children:"CACHE POISON"})]},`${s.ts}-${s.turnIndex}`))})]}),t.perModel.length>0&&e.jsxs("div",{className:"health-per-model",children:[e.jsx("h3",{children:"Health by Model"}),e.jsxs("table",{className:"health-model-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Model"}),e.jsx("th",{children:"Avg Health"}),e.jsx("th",{children:"Samples"})]})}),e.jsx("tbody",{children:t.perModel.map(s=>e.jsxs("tr",{children:[e.jsx("td",{children:s.modelId}),e.jsxs("td",{style:{color:d(s.avgComposite)},children:[(s.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{children:s.sampleCount})]},s.modelId))})]})]})]})}export{b as default};
2
- //# sourceMappingURL=HealthTab-0LsKCRFE.js.map
1
+ import{r as o,j as e}from"./index-D-lPLRWB.js";import{fetchContextHealth as u}from"./health-lSeDFTz7.js";function d(t){return t>.8?"#4ade80":t>=.5?"#facc15":"#f87171"}function f({score:t}){const a=2*Math.PI*70,h=a*(1-Math.max(0,Math.min(1,t))),n=d(t);return e.jsxs("div",{className:"health-gauge-wrap",children:[e.jsxs("svg",{width:"180",height:"180",viewBox:"0 0 180 180",children:[e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:"#1e293b",strokeWidth:"14"}),e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:n,strokeWidth:"14",strokeDasharray:a,strokeDashoffset:h,strokeLinecap:"round",transform:"rotate(-90 90 90)",style:{transition:"stroke-dashoffset 0.5s ease"}}),e.jsx("text",{x:"90",y:"85",textAnchor:"middle",fontSize:"32",fontWeight:"bold",fill:n,children:(t*100).toFixed(0)}),e.jsx("text",{x:"90",y:"108",textAnchor:"middle",fontSize:"14",fill:"#94a3b8",children:"/ 100"})]}),e.jsx("div",{className:"health-gauge-label",children:"Context Health"})]})}function p({trend:t}){if(t.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const l=400,a=60,h=1,n=0,x=t.map((r,j)=>{const s=j/(t.length-1)*l,m=a-(r-n)/(h-n)*a;return`${s.toFixed(1)},${m.toFixed(1)}`}),c=t[t.length-1];return e.jsx("svg",{width:l,height:a,viewBox:`0 0 ${l} ${a}`,className:"health-spark",children:e.jsx("polyline",{points:x.join(" "),fill:"none",stroke:d(c),strokeWidth:"2"})})}function i({label:t,value:l}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:t}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${l*100}%`,background:d(l)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(l*100).toFixed(0),"%"]})]})}function b(){const[t,l]=o.useState(null),[a,h]=o.useState(null),[n,x]=o.useState(!0),c=o.useCallback(()=>{u().then(l).catch(s=>h(s instanceof Error?s.message:String(s))).finally(()=>x(!1))},[]);if(o.useEffect(()=>{c();const s=setInterval(c,5e3);return()=>clearInterval(s)},[c]),n&&!t)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(a&&!t)return e.jsxs("div",{className:"health-error",children:["Error: ",a]});if(!t)return e.jsx("div",{className:"health-empty",children:"No health data available."});const r=t.latest,j=r?.composite??0;return e.jsxs("div",{className:"health-tab",children:[e.jsxs("div",{className:"health-top-row",children:[e.jsx(f,{score:j}),e.jsxs("div",{className:"health-subscores",children:[e.jsx("h3",{children:"Sub-Scores"}),e.jsx(i,{label:"Drift",value:r?.driftScore??0}),e.jsx(i,{label:"Output Quality",value:r?.outputQuality??0}),e.jsx(i,{label:"Error Rate",value:r?.errorScore??0}),e.jsx(i,{label:"Cache Health",value:r?.cacheHealth??0}),e.jsx(i,{label:"Cache Poison",value:r?.cachePoison??0})]})]}),e.jsxs("div",{className:"health-trend-section",children:[e.jsxs("h3",{children:["Health Trend (last ",t.trend.length," turns)"]}),e.jsx(p,{trend:t.trend})]}),t.alerts.length>0&&e.jsxs("div",{className:"health-alerts",children:[e.jsxs("h3",{children:["Recent Alerts (",t.alerts.length,")"]}),e.jsx("div",{className:"health-alert-list",children:t.alerts.slice(-10).map(s=>e.jsxs("div",{className:"health-alert-row",children:[e.jsx("span",{className:"health-alert-time",children:new Date(s.ts).toLocaleTimeString()}),e.jsx("span",{className:"health-alert-model",children:s.modelId??"(unknown)"}),e.jsx("span",{className:"health-alert-score",style:{color:d(s.composite)},children:(s.composite*100).toFixed(0)}),s.cachePoison<.3&&e.jsx("span",{className:"health-alert-tag",children:"CACHE POISON"})]},`${s.ts}-${s.turnIndex}`))})]}),t.perModel.length>0&&e.jsxs("div",{className:"health-per-model",children:[e.jsx("h3",{children:"Health by Model"}),e.jsxs("table",{className:"health-model-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Model"}),e.jsx("th",{children:"Avg Health"}),e.jsx("th",{children:"Samples"})]})}),e.jsx("tbody",{children:t.perModel.map(s=>e.jsxs("tr",{children:[e.jsx("td",{children:s.modelId}),e.jsxs("td",{style:{color:d(s.avgComposite)},children:[(s.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{children:s.sampleCount})]},s.modelId))})]})]})]})}export{b as default};
2
+ //# sourceMappingURL=HealthTab-BLpidMrX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HealthTab-0LsKCRFE.js","sources":["../../src/tabs/HealthTab.tsx"],"sourcesContent":["/**\n * HealthTab.tsx — Context Health dashboard tab.\n * Shows composite health gauge, sparkline trend, sub-score bars,\n * alerts, and per-model comparison. Polls /api/context-health every 5s.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { fetchContextHealth, type ContextHealthResponse } from \"../api/health\";\n\nfunction healthColor(score: number): string {\n\tif (score > 0.8) return \"#4ade80\";\n\tif (score >= 0.5) return \"#facc15\";\n\treturn \"#f87171\";\n}\n\nfunction HealthGauge({ score }: { score: number }): React.ReactElement {\n\tconst r = 70;\n\tconst circ = 2 * Math.PI * r;\n\tconst offset = circ * (1 - Math.max(0, Math.min(1, score)));\n\tconst color = healthColor(score);\n\treturn (\n\t\t<div className=\"health-gauge-wrap\">\n\t\t\t<svg width=\"180\" height=\"180\" viewBox=\"0 0 180 180\">\n\t\t\t\t<circle cx=\"90\" cy=\"90\" r={r} fill=\"none\" stroke=\"#1e293b\" strokeWidth=\"14\" />\n\t\t\t\t<circle\n\t\t\t\t\tcx=\"90\"\n\t\t\t\t\tcy=\"90\"\n\t\t\t\t\tr={r}\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke={color}\n\t\t\t\t\tstrokeWidth=\"14\"\n\t\t\t\t\tstrokeDasharray={circ}\n\t\t\t\t\tstrokeDashoffset={offset}\n\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\ttransform=\"rotate(-90 90 90)\"\n\t\t\t\t\tstyle={{ transition: \"stroke-dashoffset 0.5s ease\" }}\n\t\t\t\t/>\n\t\t\t\t<text x=\"90\" y=\"85\" textAnchor=\"middle\" fontSize=\"32\" fontWeight=\"bold\" fill={color}>\n\t\t\t\t\t{(score * 100).toFixed(0)}\n\t\t\t\t</text>\n\t\t\t\t<text x=\"90\" y=\"108\" textAnchor=\"middle\" fontSize=\"14\" fill=\"#94a3b8\">/ 100</text>\n\t\t\t</svg>\n\t\t\t<div className=\"health-gauge-label\">Context Health</div>\n\t\t</div>\n\t);\n}\n\nfunction HealthSparkline({ trend }: { trend: number[] }): React.ReactElement {\n\tif (trend.length < 2) return <div className=\"health-spark-empty\">No trend data yet</div>;\n\tconst w = 400;\n\tconst h = 60;\n\tconst max = 1;\n\tconst min = 0;\n\tconst pts = trend.map((v, i) => {\n\t\tconst x = (i / (trend.length - 1)) * w;\n\t\tconst y = h - ((v - min) / (max - min)) * h;\n\t\treturn `${x.toFixed(1)},${y.toFixed(1)}`;\n\t});\n\tconst last = trend[trend.length - 1];\n\treturn (\n\t\t<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} className=\"health-spark\">\n\t\t\t<polyline points={pts.join(\" \")} fill=\"none\" stroke={healthColor(last)} strokeWidth=\"2\" />\n\t\t</svg>\n\t);\n}\n\nfunction SubScoreBar({ label, value }: { label: string; value: number }): React.ReactElement {\n\treturn (\n\t\t<div className=\"health-subscore\">\n\t\t\t<span className=\"health-subscore-label\">{label}</span>\n\t\t\t<div className=\"health-subscore-bar\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"health-subscore-fill\"\n\t\t\t\t\tstyle={{ width: `${value * 100}%`, background: healthColor(value) }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<span className=\"health-subscore-val\">{(value * 100).toFixed(0)}%</span>\n\t\t</div>\n\t);\n}\n\nexport default function HealthTab(): React.ReactElement {\n\tconst [data, setData] = useState<ContextHealthResponse | null>(null);\n\tconst [err, setError] = useState<string | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\n\tconst poll = useCallback(() => {\n\t\tfetchContextHealth()\n\t\t\t.then(setData)\n\t\t\t.catch((e: unknown) => setError(e instanceof Error ? e.message : String(e)))\n\t\t\t.finally(() => setLoading(false));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tpoll();\n\t\tconst id = setInterval(poll, 5000);\n\t\treturn () => clearInterval(id);\n\t}, [poll]);\n\n\tif (loading && !data) return <div className=\"health-loading\">Loading health data…</div>;\n\tif (err && !data) return <div className=\"health-error\">Error: {err}</div>;\n\tif (!data) return <div className=\"health-empty\">No health data available.</div>;\n\n\tconst latest = data.latest;\n\tconst composite = latest?.composite ?? 0;\n\n\treturn (\n\t\t<div className=\"health-tab\">\n\t\t\t<div className=\"health-top-row\">\n\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t<div className=\"health-subscores\">\n\t\t\t\t\t<h3>Sub-Scores</h3>\n\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"health-trend-section\">\n\t\t\t\t<h3>Health Trend (last {data.trend.length} turns)</h3>\n\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t</div>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<div className=\"health-alerts\">\n\t\t\t\t\t<h3>Recent Alerts ({data.alerts.length})</h3>\n\t\t\t\t\t<div className=\"health-alert-list\">\n\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t<div key={`${a.ts}-${a.turnIndex}`} className=\"health-alert-row\">\n\t\t\t\t\t\t\t\t<span className=\"health-alert-time\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t<span className=\"health-alert-model\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t<span className=\"health-alert-score\" style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <span className=\"health-alert-tag\">CACHE POISON</span>}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<div className=\"health-per-model\">\n\t\t\t\t\t<h3>Health by Model</h3>\n\t\t\t\t\t<table className=\"health-model-table\">\n\t\t\t\t\t\t<thead><tr><th>Model</th><th>Avg Health</th><th>Samples</th></tr></thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t<tr key={m.modelId}>\n\t\t\t\t\t\t\t\t\t<td>{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t<td style={{ color: healthColor(m.avgComposite) }}>\n\t\t\t\t\t\t\t\t\t\t{(m.avgComposite * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["healthColor","score","HealthGauge","circ","offset","color","jsxs","jsx","HealthSparkline","trend","w","h","max","min","pts","v","i","x","y","last","SubScoreBar","label","value","HealthTab","data","setData","useState","err","setError","loading","setLoading","poll","useCallback","fetchContextHealth","e","useEffect","id","latest","composite","a","m"],"mappings":"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"}
1
+ {"version":3,"file":"HealthTab-BLpidMrX.js","sources":["../../src/tabs/HealthTab.tsx"],"sourcesContent":["/**\n * HealthTab.tsx — Context Health dashboard tab.\n * Shows composite health gauge, sparkline trend, sub-score bars,\n * alerts, and per-model comparison. Polls /api/context-health every 5s.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { fetchContextHealth, type ContextHealthResponse } from \"../api/health\";\n\nfunction healthColor(score: number): string {\n\tif (score > 0.8) return \"#4ade80\";\n\tif (score >= 0.5) return \"#facc15\";\n\treturn \"#f87171\";\n}\n\nfunction HealthGauge({ score }: { score: number }): React.ReactElement {\n\tconst r = 70;\n\tconst circ = 2 * Math.PI * r;\n\tconst offset = circ * (1 - Math.max(0, Math.min(1, score)));\n\tconst color = healthColor(score);\n\treturn (\n\t\t<div className=\"health-gauge-wrap\">\n\t\t\t<svg width=\"180\" height=\"180\" viewBox=\"0 0 180 180\">\n\t\t\t\t<circle cx=\"90\" cy=\"90\" r={r} fill=\"none\" stroke=\"#1e293b\" strokeWidth=\"14\" />\n\t\t\t\t<circle\n\t\t\t\t\tcx=\"90\"\n\t\t\t\t\tcy=\"90\"\n\t\t\t\t\tr={r}\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke={color}\n\t\t\t\t\tstrokeWidth=\"14\"\n\t\t\t\t\tstrokeDasharray={circ}\n\t\t\t\t\tstrokeDashoffset={offset}\n\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\ttransform=\"rotate(-90 90 90)\"\n\t\t\t\t\tstyle={{ transition: \"stroke-dashoffset 0.5s ease\" }}\n\t\t\t\t/>\n\t\t\t\t<text x=\"90\" y=\"85\" textAnchor=\"middle\" fontSize=\"32\" fontWeight=\"bold\" fill={color}>\n\t\t\t\t\t{(score * 100).toFixed(0)}\n\t\t\t\t</text>\n\t\t\t\t<text x=\"90\" y=\"108\" textAnchor=\"middle\" fontSize=\"14\" fill=\"#94a3b8\">/ 100</text>\n\t\t\t</svg>\n\t\t\t<div className=\"health-gauge-label\">Context Health</div>\n\t\t</div>\n\t);\n}\n\nfunction HealthSparkline({ trend }: { trend: number[] }): React.ReactElement {\n\tif (trend.length < 2) return <div className=\"health-spark-empty\">No trend data yet</div>;\n\tconst w = 400;\n\tconst h = 60;\n\tconst max = 1;\n\tconst min = 0;\n\tconst pts = trend.map((v, i) => {\n\t\tconst x = (i / (trend.length - 1)) * w;\n\t\tconst y = h - ((v - min) / (max - min)) * h;\n\t\treturn `${x.toFixed(1)},${y.toFixed(1)}`;\n\t});\n\tconst last = trend[trend.length - 1];\n\treturn (\n\t\t<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} className=\"health-spark\">\n\t\t\t<polyline points={pts.join(\" \")} fill=\"none\" stroke={healthColor(last)} strokeWidth=\"2\" />\n\t\t</svg>\n\t);\n}\n\nfunction SubScoreBar({ label, value }: { label: string; value: number }): React.ReactElement {\n\treturn (\n\t\t<div className=\"health-subscore\">\n\t\t\t<span className=\"health-subscore-label\">{label}</span>\n\t\t\t<div className=\"health-subscore-bar\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"health-subscore-fill\"\n\t\t\t\t\tstyle={{ width: `${value * 100}%`, background: healthColor(value) }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<span className=\"health-subscore-val\">{(value * 100).toFixed(0)}%</span>\n\t\t</div>\n\t);\n}\n\nexport default function HealthTab(): React.ReactElement {\n\tconst [data, setData] = useState<ContextHealthResponse | null>(null);\n\tconst [err, setError] = useState<string | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\n\tconst poll = useCallback(() => {\n\t\tfetchContextHealth()\n\t\t\t.then(setData)\n\t\t\t.catch((e: unknown) => setError(e instanceof Error ? e.message : String(e)))\n\t\t\t.finally(() => setLoading(false));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tpoll();\n\t\tconst id = setInterval(poll, 5000);\n\t\treturn () => clearInterval(id);\n\t}, [poll]);\n\n\tif (loading && !data) return <div className=\"health-loading\">Loading health data…</div>;\n\tif (err && !data) return <div className=\"health-error\">Error: {err}</div>;\n\tif (!data) return <div className=\"health-empty\">No health data available.</div>;\n\n\tconst latest = data.latest;\n\tconst composite = latest?.composite ?? 0;\n\n\treturn (\n\t\t<div className=\"health-tab\">\n\t\t\t<div className=\"health-top-row\">\n\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t<div className=\"health-subscores\">\n\t\t\t\t\t<h3>Sub-Scores</h3>\n\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"health-trend-section\">\n\t\t\t\t<h3>Health Trend (last {data.trend.length} turns)</h3>\n\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t</div>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<div className=\"health-alerts\">\n\t\t\t\t\t<h3>Recent Alerts ({data.alerts.length})</h3>\n\t\t\t\t\t<div className=\"health-alert-list\">\n\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t<div key={`${a.ts}-${a.turnIndex}`} className=\"health-alert-row\">\n\t\t\t\t\t\t\t\t<span className=\"health-alert-time\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t<span className=\"health-alert-model\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t<span className=\"health-alert-score\" style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <span className=\"health-alert-tag\">CACHE POISON</span>}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<div className=\"health-per-model\">\n\t\t\t\t\t<h3>Health by Model</h3>\n\t\t\t\t\t<table className=\"health-model-table\">\n\t\t\t\t\t\t<thead><tr><th>Model</th><th>Avg Health</th><th>Samples</th></tr></thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t<tr key={m.modelId}>\n\t\t\t\t\t\t\t\t\t<td>{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t<td style={{ color: healthColor(m.avgComposite) }}>\n\t\t\t\t\t\t\t\t\t\t{(m.avgComposite * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["healthColor","score","HealthGauge","circ","offset","color","jsxs","jsx","HealthSparkline","trend","w","h","max","min","pts","v","i","x","y","last","SubScoreBar","label","value","HealthTab","data","setData","useState","err","setError","loading","setLoading","poll","useCallback","fetchContextHealth","e","useEffect","id","latest","composite","a","m"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{u as p,r,B as f,C as v,j as e,D as C,E as w,_ as N}from"./index-m3rSvxMW.js";const k=[{key:"integrity-check",label:"Integrity Check",desc:"Run PRAGMA integrity_check",dangerous:!1},{key:"reconcile-dedup",label:"Reconcile Dedup Mirror",desc:"Fix orphan dedup rows + backfill refs",dangerous:!1,confirm:"Reconcile dedup_mirror (safe read/write)?"},{key:"checkpoint",label:"WAL Checkpoint",desc:"Merge WAL into main DB (read-only)",dangerous:!1},{key:"vacuum",label:"VACUUM",desc:"Rebuild DB reclaiming free pages",dangerous:!0,confirm:"VACUUM rewrites the entire DB. Continue?"},{key:"reindex",label:"REINDEX",desc:"Rebuild all indexes",dangerous:!0,confirm:"REINDEX rebuilds indexes. Continue?"},{key:"fts5-rebuild",label:"FTS5 Rebuild",desc:"Rebuild full-text search index",dangerous:!0,confirm:"Rebuild FTS5 trigram index. Continue?"},{key:"prune",label:"Prune Old Rows",desc:"Delete transcripts + epochs older than N days",dangerous:!0,daysOld:!0,confirm:"Delete old raw_transcript rows. Continue?"}];function g(s){return s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(1)} MB`}function S({files:s,pageSize:l,pageCount:t,freelistPages:i}){const o=s.dbBytes+s.walBytes+s.shmBytes;return e.jsxs("div",{className:"stat-section",children:[e.jsx("div",{className:"stat-label",children:"File Sizes"}),e.jsx("table",{className:"compact-table",children:e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{children:"sqlite.db"}),e.jsx("td",{className:"num",children:g(s.dbBytes)})]}),s.walBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".wal"}),e.jsx("td",{className:"num",children:g(s.walBytes)})]}),s.shmBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".shm"}),e.jsx("td",{className:"num",children:g(s.shmBytes)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Total"}),e.jsx("td",{className:"num",children:g(o)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Page size"}),e.jsx("td",{className:"num",children:g(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Pages"}),e.jsxs("td",{className:"num",children:[t.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function R({tables:s}){const l=s.reduce((t,i)=>t+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{className:"num",children:"Rows"})]})}),e.jsx("tbody",{children:s.map(t=>e.jsxs("tr",{children:[e.jsx("td",{children:t.table}),e.jsx("td",{className:"num",children:t.rowCount>=0?t.rowCount.toLocaleString():"—"})]},t.table))})]})]})}function E({data:s,loading:l,error:t}){return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"DB Stats"}),e.jsxs("div",{className:"card-body",children:[l&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),t&&!s&&e.jsx("span",{className:"text-error",children:t.message}),s&&e.jsxs(e.Fragment,{children:[e.jsx(S,{files:s.storage.files,pageSize:s.storage.pageSize,pageCount:s.storage.pageCount,freelistPages:s.storage.freelistPages}),e.jsx(R,{tables:s.tables})]})]})]})}function D({columns:s}){const l=s.filter(t=>!t.present);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Column Audit (",s.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Decl"}),e.jsx("th",{children:"Status"})]})}),e.jsx("tbody",{children:s.map(t=>e.jsxs("tr",{children:[e.jsx("td",{children:t.table}),e.jsx("td",{children:t.column}),e.jsx("td",{className:"text-mono",children:t.expectedDecl}),e.jsx("td",{className:t.present?"text-ok":"text-error",children:t.present?"✓":"MISSING"})]},`${t.table}.${t.column}`))})]})]})}function A({data:s,loading:l,error:t}){return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Schema Health",s&&e.jsx("span",{className:`badge ${s.healthy?"badge-ok":"badge-bad"}`,children:s.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs("div",{className:"card-body",children:[l&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),t&&!s&&e.jsx("span",{className:"text-error",children:t.message}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Schema version"}),e.jsx("span",{className:"num",children:s.schemaVersion})]}),e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Integrity"}),e.jsx("span",{className:s.integrity[0]==="ok"?"text-ok":"text-error",children:s.integrity.join("; ")})]}),e.jsx(D,{columns:s.columns}),s.fkCheck.length>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["FK Check (",s.fkCheck.length,")"]}),s.fkCheck.map((i,o)=>e.jsx("div",{className:"text-warn text-mono",children:i},o))]})]})]})]})}function B(){const[s,l]=r.useState([]),[t,i]=r.useState(null),[o,m]=r.useState("30"),x=r.useCallback(async a=>{const u=a.daysOld?{action:"prune",daysOld:Number(o)}:{action:a.key};i(a.key);try{const d=await C(u);l(n=>[{...d,timestamp:Date.now()},...n].slice(0,20))}catch(d){l(n=>[{operation:a.key,success:!1,affected:0,reclaimedBytes:0,summary:d instanceof Error?d.message:String(d),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[o]);return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Actions"}),e.jsx("div",{className:"card-body",children:e.jsx("div",{className:"action-grid",children:k.map(a=>{const u=t===a.key,d=s.find(n=>n.operation===a.key);return e.jsxs("div",{className:"action-item",children:[e.jsxs("div",{className:"action-info",children:[e.jsxs("div",{className:"action-label",children:[a.label,a.dangerous&&e.jsx("span",{className:"badge badge-warn",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"action-desc",children:a.desc}),d&&e.jsx("div",{className:`action-result ${d.success?"text-ok":"text-error"}`,children:d.summary})]}),e.jsxs("div",{className:"action-controls",children:[a.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:o,onChange:n=>m(n.target.value),className:"days-input",disabled:u}),e.jsx("button",{className:`btn ${a.dangerous?"btn-danger":"btn-primary"}`,disabled:t!==null,onClick:()=>{a.confirm&&!window.confirm(a.confirm)||x(a)},children:u?"Running…":"Run"})]})]},a.key)})})})]})}function L(){const[s,l]=r.useState(null),[t,i]=r.useState(!1),[o,m]=r.useState(null),[x,a]=r.useState(!1),u=r.useCallback(async()=>{i(!0),m(null);try{const c=await w();l(c),a(!0)}catch(c){m(c instanceof Error?c.message:String(c))}finally{i(!1)}},[]),d=r.useCallback(()=>{if(!s)return;const c=JSON.stringify(s,null,2);navigator.clipboard.writeText(c).catch(()=>{})},[s]),n=r.useCallback(()=>{if(!s)return;const c=JSON.stringify(s,null,2),y=new Blob([c],{type:"application/json"}),b=URL.createObjectURL(y),j=document.createElement("a");j.href=b,j.download=`mega-compact-debug-bundle-${s.builtAt}.json`,j.click(),URL.revokeObjectURL(b)},[s]),h=s?.criticalEvents.length??0;return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Debug Bundle",h>0&&e.jsxs("span",{className:"text-warn",style:{marginLeft:"1em"},children:[h," critical event",h!==1?"s":""," found"]})]}),e.jsxs("div",{className:"card-body",children:[e.jsx("p",{className:"text-muted",style:{marginBottom:"0.75em"},children:"Gather a diagnostic bundle (recent events, config flags, schema health, store stats) to attach to a bug report. Critical/compaction events are highlighted."}),e.jsx("button",{onClick:u,disabled:t,style:{marginRight:"0.5em"},children:t?"Gathering…":"Gather Debug Logs"}),o&&e.jsx("div",{className:"text-error",children:o}),s&&x&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:{marginTop:"0.75em",marginBottom:"0.5em"},children:[e.jsx("button",{onClick:d,style:{marginRight:"0.5em"},children:"Copy to clipboard"}),e.jsx("button",{onClick:n,children:"Download JSON"}),e.jsx("button",{onClick:()=>a(c=>!c),style:{marginLeft:"0.5em"},children:x?"Hide":"Show"})]}),h>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["Critical events (",h,")"]}),e.jsx("pre",{className:"text-mono",style:{maxHeight:"12em",overflow:"auto"},children:JSON.stringify(s.criticalEvents,null,2)})]}),e.jsx("pre",{className:"text-mono",style:{maxHeight:"24em",overflow:"auto"},children:JSON.stringify(s,null,2)})]})]})]})}function T(){const[s,l]=r.useState(!1),[t,i]=r.useState(!0),[o,m]=r.useState(!1),[x,a]=r.useState(null),u=r.useCallback(async()=>{try{const{fetchHealthSettings:n}=await N(async()=>{const{fetchHealthSettings:c}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:c}},[],import.meta.url),h=await n();l(h.mitigate)}catch(n){a(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);r.useEffect(()=>{u()},[u]);const d=r.useCallback(async()=>{m(!0),a(null);try{const{setHealthMitigate:n}=await N(async()=>{const{setHealthMitigate:c}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:c}},[],import.meta.url),h=!s;await n(h),l(h)}catch(n){a(n instanceof Error?n.message:String(n))}finally{m(!1)}},[s]);return e.jsxs("div",{className:"stat-section maintenance-card",children:[e.jsx("div",{className:"stat-label",children:"Context Health Mitigation"}),t?e.jsx("p",{className:"repo-none",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"health-mitigate-row",children:e.jsxs("label",{className:"health-mitigate-toggle",children:[e.jsx("input",{type:"checkbox",checked:s,onChange:d,disabled:o}),e.jsxs("span",{children:["Auto-mitigation ",s?"ON":"OFF"]})]})}),e.jsxs("div",{className:"health-mitigate-info",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Forced compaction"})," — when the composite health score drops below 0.4 (severe drift or garbled output), a compaction is triggered to flush the degraded context and rebuild from clean checkpoints, even if the context window isn't full."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Prefix break"})," — when KV cache poison is detected (score < 0.3), the cached prefix is invalidated to force a cache miss on the next turn, bypassing the corrupted KV state."]})]}),e.jsx("p",{className:"health-mitigate-warning",children:"WARNING: both interventions have costs — forced compaction discards live context, and prefix breaks kill cache savings. Only enable when you're seeing degraded output (garbled text, hallucinations, loss of coherence) and the Health tab confirms low scores."}),e.jsxs("p",{children:["Also settable via env var: ",e.jsx("code",{children:"MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1"}),". The dashboard toggle persists to the store and takes precedence."]}),x&&e.jsxs("p",{className:"health-mitigate-error",children:["Error: ",x]})]})]})]})}function _(){const{data:s,loading:l,error:t}=p(r.useCallback(()=>f(),[]),{pollInterval:15e3}),{data:i,loading:o,error:m}=p(r.useCallback(()=>v(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"maintenance-tab",children:[e.jsxs("div",{className:"card-grid",children:[e.jsx(A,{data:i,loading:o,error:m}),e.jsx(E,{data:s,loading:l,error:t})]}),e.jsx(B,{}),e.jsx(L,{}),e.jsx(T,{})]})}export{_ as default};
2
- //# sourceMappingURL=MaintenanceTab-C4L2Ohea.js.map
1
+ import{u as p,r,B as f,C as v,j as e,D as C,E as w,_ as N}from"./index-D-lPLRWB.js";const k=[{key:"integrity-check",label:"Integrity Check",desc:"Run PRAGMA integrity_check",dangerous:!1},{key:"reconcile-dedup",label:"Reconcile Dedup Mirror",desc:"Fix orphan dedup rows + backfill refs",dangerous:!1,confirm:"Reconcile dedup_mirror (safe read/write)?"},{key:"checkpoint",label:"WAL Checkpoint",desc:"Merge WAL into main DB (read-only)",dangerous:!1},{key:"vacuum",label:"VACUUM",desc:"Rebuild DB reclaiming free pages",dangerous:!0,confirm:"VACUUM rewrites the entire DB. Continue?"},{key:"reindex",label:"REINDEX",desc:"Rebuild all indexes",dangerous:!0,confirm:"REINDEX rebuilds indexes. Continue?"},{key:"fts5-rebuild",label:"FTS5 Rebuild",desc:"Rebuild full-text search index",dangerous:!0,confirm:"Rebuild FTS5 trigram index. Continue?"},{key:"prune",label:"Prune Old Rows",desc:"Delete transcripts + epochs older than N days",dangerous:!0,daysOld:!0,confirm:"Delete old raw_transcript rows. Continue?"}];function g(s){return s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(1)} MB`}function S({files:s,pageSize:l,pageCount:t,freelistPages:i}){const o=s.dbBytes+s.walBytes+s.shmBytes;return e.jsxs("div",{className:"stat-section",children:[e.jsx("div",{className:"stat-label",children:"File Sizes"}),e.jsx("table",{className:"compact-table",children:e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{children:"sqlite.db"}),e.jsx("td",{className:"num",children:g(s.dbBytes)})]}),s.walBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".wal"}),e.jsx("td",{className:"num",children:g(s.walBytes)})]}),s.shmBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".shm"}),e.jsx("td",{className:"num",children:g(s.shmBytes)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Total"}),e.jsx("td",{className:"num",children:g(o)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Page size"}),e.jsx("td",{className:"num",children:g(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Pages"}),e.jsxs("td",{className:"num",children:[t.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function R({tables:s}){const l=s.reduce((t,i)=>t+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{className:"num",children:"Rows"})]})}),e.jsx("tbody",{children:s.map(t=>e.jsxs("tr",{children:[e.jsx("td",{children:t.table}),e.jsx("td",{className:"num",children:t.rowCount>=0?t.rowCount.toLocaleString():"—"})]},t.table))})]})]})}function E({data:s,loading:l,error:t}){return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"DB Stats"}),e.jsxs("div",{className:"card-body",children:[l&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),t&&!s&&e.jsx("span",{className:"text-error",children:t.message}),s&&e.jsxs(e.Fragment,{children:[e.jsx(S,{files:s.storage.files,pageSize:s.storage.pageSize,pageCount:s.storage.pageCount,freelistPages:s.storage.freelistPages}),e.jsx(R,{tables:s.tables})]})]})]})}function D({columns:s}){const l=s.filter(t=>!t.present);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Column Audit (",s.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Decl"}),e.jsx("th",{children:"Status"})]})}),e.jsx("tbody",{children:s.map(t=>e.jsxs("tr",{children:[e.jsx("td",{children:t.table}),e.jsx("td",{children:t.column}),e.jsx("td",{className:"text-mono",children:t.expectedDecl}),e.jsx("td",{className:t.present?"text-ok":"text-error",children:t.present?"✓":"MISSING"})]},`${t.table}.${t.column}`))})]})]})}function A({data:s,loading:l,error:t}){return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Schema Health",s&&e.jsx("span",{className:`badge ${s.healthy?"badge-ok":"badge-bad"}`,children:s.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs("div",{className:"card-body",children:[l&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),t&&!s&&e.jsx("span",{className:"text-error",children:t.message}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Schema version"}),e.jsx("span",{className:"num",children:s.schemaVersion})]}),e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Integrity"}),e.jsx("span",{className:s.integrity[0]==="ok"?"text-ok":"text-error",children:s.integrity.join("; ")})]}),e.jsx(D,{columns:s.columns}),s.fkCheck.length>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["FK Check (",s.fkCheck.length,")"]}),s.fkCheck.map((i,o)=>e.jsx("div",{className:"text-warn text-mono",children:i},o))]})]})]})]})}function B(){const[s,l]=r.useState([]),[t,i]=r.useState(null),[o,m]=r.useState("30"),x=r.useCallback(async a=>{const u=a.daysOld?{action:"prune",daysOld:Number(o)}:{action:a.key};i(a.key);try{const d=await C(u);l(n=>[{...d,timestamp:Date.now()},...n].slice(0,20))}catch(d){l(n=>[{operation:a.key,success:!1,affected:0,reclaimedBytes:0,summary:d instanceof Error?d.message:String(d),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[o]);return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Actions"}),e.jsx("div",{className:"card-body",children:e.jsx("div",{className:"action-grid",children:k.map(a=>{const u=t===a.key,d=s.find(n=>n.operation===a.key);return e.jsxs("div",{className:"action-item",children:[e.jsxs("div",{className:"action-info",children:[e.jsxs("div",{className:"action-label",children:[a.label,a.dangerous&&e.jsx("span",{className:"badge badge-warn",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"action-desc",children:a.desc}),d&&e.jsx("div",{className:`action-result ${d.success?"text-ok":"text-error"}`,children:d.summary})]}),e.jsxs("div",{className:"action-controls",children:[a.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:o,onChange:n=>m(n.target.value),className:"days-input",disabled:u}),e.jsx("button",{className:`btn ${a.dangerous?"btn-danger":"btn-primary"}`,disabled:t!==null,onClick:()=>{a.confirm&&!window.confirm(a.confirm)||x(a)},children:u?"Running…":"Run"})]})]},a.key)})})})]})}function L(){const[s,l]=r.useState(null),[t,i]=r.useState(!1),[o,m]=r.useState(null),[x,a]=r.useState(!1),u=r.useCallback(async()=>{i(!0),m(null);try{const c=await w();l(c),a(!0)}catch(c){m(c instanceof Error?c.message:String(c))}finally{i(!1)}},[]),d=r.useCallback(()=>{if(!s)return;const c=JSON.stringify(s,null,2);navigator.clipboard.writeText(c).catch(()=>{})},[s]),n=r.useCallback(()=>{if(!s)return;const c=JSON.stringify(s,null,2),y=new Blob([c],{type:"application/json"}),b=URL.createObjectURL(y),j=document.createElement("a");j.href=b,j.download=`mega-compact-debug-bundle-${s.builtAt}.json`,j.click(),URL.revokeObjectURL(b)},[s]),h=s?.criticalEvents.length??0;return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Debug Bundle",h>0&&e.jsxs("span",{className:"text-warn",style:{marginLeft:"1em"},children:[h," critical event",h!==1?"s":""," found"]})]}),e.jsxs("div",{className:"card-body",children:[e.jsx("p",{className:"text-muted",style:{marginBottom:"0.75em"},children:"Gather a diagnostic bundle (recent events, config flags, schema health, store stats) to attach to a bug report. Critical/compaction events are highlighted."}),e.jsx("button",{onClick:u,disabled:t,style:{marginRight:"0.5em"},children:t?"Gathering…":"Gather Debug Logs"}),o&&e.jsx("div",{className:"text-error",children:o}),s&&x&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:{marginTop:"0.75em",marginBottom:"0.5em"},children:[e.jsx("button",{onClick:d,style:{marginRight:"0.5em"},children:"Copy to clipboard"}),e.jsx("button",{onClick:n,children:"Download JSON"}),e.jsx("button",{onClick:()=>a(c=>!c),style:{marginLeft:"0.5em"},children:x?"Hide":"Show"})]}),h>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["Critical events (",h,")"]}),e.jsx("pre",{className:"text-mono",style:{maxHeight:"12em",overflow:"auto"},children:JSON.stringify(s.criticalEvents,null,2)})]}),e.jsx("pre",{className:"text-mono",style:{maxHeight:"24em",overflow:"auto"},children:JSON.stringify(s,null,2)})]})]})]})}function T(){const[s,l]=r.useState(!1),[t,i]=r.useState(!0),[o,m]=r.useState(!1),[x,a]=r.useState(null),u=r.useCallback(async()=>{try{const{fetchHealthSettings:n}=await N(async()=>{const{fetchHealthSettings:c}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:c}},[],import.meta.url),h=await n();l(h.mitigate)}catch(n){a(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);r.useEffect(()=>{u()},[u]);const d=r.useCallback(async()=>{m(!0),a(null);try{const{setHealthMitigate:n}=await N(async()=>{const{setHealthMitigate:c}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:c}},[],import.meta.url),h=!s;await n(h),l(h)}catch(n){a(n instanceof Error?n.message:String(n))}finally{m(!1)}},[s]);return e.jsxs("div",{className:"stat-section maintenance-card",children:[e.jsx("div",{className:"stat-label",children:"Context Health Mitigation"}),t?e.jsx("p",{className:"repo-none",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"health-mitigate-row",children:e.jsxs("label",{className:"health-mitigate-toggle",children:[e.jsx("input",{type:"checkbox",checked:s,onChange:d,disabled:o}),e.jsxs("span",{children:["Auto-mitigation ",s?"ON":"OFF"]})]})}),e.jsxs("div",{className:"health-mitigate-info",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Forced compaction"})," — when the composite health score drops below 0.4 (severe drift or garbled output), a compaction is triggered to flush the degraded context and rebuild from clean checkpoints, even if the context window isn't full."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Prefix break"})," — when KV cache poison is detected (score < 0.3), the cached prefix is invalidated to force a cache miss on the next turn, bypassing the corrupted KV state."]})]}),e.jsx("p",{className:"health-mitigate-warning",children:"WARNING: both interventions have costs — forced compaction discards live context, and prefix breaks kill cache savings. Only enable when you're seeing degraded output (garbled text, hallucinations, loss of coherence) and the Health tab confirms low scores."}),e.jsxs("p",{children:["Also settable via env var: ",e.jsx("code",{children:"MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1"}),". The dashboard toggle persists to the store and takes precedence."]}),x&&e.jsxs("p",{className:"health-mitigate-error",children:["Error: ",x]})]})]})]})}function _(){const{data:s,loading:l,error:t}=p(r.useCallback(()=>f(),[]),{pollInterval:15e3}),{data:i,loading:o,error:m}=p(r.useCallback(()=>v(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"maintenance-tab",children:[e.jsxs("div",{className:"card-grid",children:[e.jsx(A,{data:i,loading:o,error:m}),e.jsx(E,{data:s,loading:l,error:t})]}),e.jsx(B,{}),e.jsx(L,{}),e.jsx(T,{})]})}export{_ as default};
2
+ //# sourceMappingURL=MaintenanceTab-BQF7W7xX.js.map
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":["ACTIONS","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","jsxs","jsx","TableRowCountsCard","tables","totalRows","s","t","DbStatsCard","data","loading","error","Fragment","ColumnAuditCard","columns","missing","c","SchemaHealthCard","line","i","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","DebugBundleCard","bundle","setBundle","setLoading","setError","showJson","setShowJson","gather","fetchDebugBundle","copyToClipboard","text","downloadJson","blob","url","a","criticalCount","v","HealthMitigationCard","mitigate","setMitigate","saving","setSaving","load","fetchHealthSettings","__vitePreload","useEffect","toggle","setHealthMitigate","next","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"ignoreList":[],"sources":["../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Three cards:\n * 1. Schema Health (schema version + integrity + column audit)\n * 2. DB Stats (file sizes + table row counts)\n * 3. Actions (one-time maintenance operations with results history)\n *\n * All three fetch independently; failures in one do not block the others.\n */\n\nimport type React from \"react\";\nimport { useState, useCallback, useEffect } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n\tDebugBundleResponse,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchDbStats,\n\tfetchSchemaHealth,\n\tpostMaintenanceAction,\n\tfetchDebugBundle,\n} from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Action config\n// ---------------------------------------------------------------------------\n\ninterface ActionDef {\n\tkey: string;\n\tlabel: string;\n\tdesc: string;\n\tdangerous: boolean;\n\tconfirm?: string;\n\tdaysOld?: boolean;\n}\n\nconst ACTIONS: ActionDef[] = [\n\t{\n\t\tkey: \"integrity-check\",\n\t\tlabel: \"Integrity Check\",\n\t\tdesc: \"Run PRAGMA integrity_check\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"reconcile-dedup\",\n\t\tlabel: \"Reconcile Dedup Mirror\",\n\t\tdesc: \"Fix orphan dedup rows + backfill refs\",\n\t\tdangerous: false,\n\t\tconfirm: \"Reconcile dedup_mirror (safe read/write)?\",\n\t},\n\t{\n\t\tkey: \"checkpoint\",\n\t\tlabel: \"WAL Checkpoint\",\n\t\tdesc: \"Merge WAL into main DB (read-only)\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"vacuum\",\n\t\tlabel: \"VACUUM\",\n\t\tdesc: \"Rebuild DB reclaiming free pages\",\n\t\tdangerous: true,\n\t\tconfirm: \"VACUUM rewrites the entire DB. Continue?\",\n\t},\n\t{\n\t\tkey: \"reindex\",\n\t\tlabel: \"REINDEX\",\n\t\tdesc: \"Rebuild all indexes\",\n\t\tdangerous: true,\n\t\tconfirm: \"REINDEX rebuilds indexes. Continue?\",\n\t},\n\t{\n\t\tkey: \"fts5-rebuild\",\n\t\tlabel: \"FTS5 Rebuild\",\n\t\tdesc: \"Rebuild full-text search index\",\n\t\tdangerous: true,\n\t\tconfirm: \"Rebuild FTS5 trigram index. Continue?\",\n\t},\n\t{\n\t\tkey: \"prune\",\n\t\tlabel: \"Prune Old Rows\",\n\t\tdesc: \"Delete transcripts + epochs older than N days\",\n\t\tdangerous: true,\n\t\tdaysOld: true,\n\t\tconfirm: \"Delete old raw_transcript rows. Continue?\",\n\t},\n];\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nfunction fmtBytes(b: number): string {\n\tif (b < 1024) return `${b} B`;\n\tif (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;\n\treturn `${(b / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// DB Stats card\n// ---------------------------------------------------------------------------\n\nfunction FileSizesCard({\n\tfiles,\n\tpageSize,\n\tpageCount,\n\tfreelistPages,\n}: {\n\tfiles: DbStatsResponse[\"storage\"][\"files\"];\n\tpageSize: number;\n\tpageCount: number;\n\tfreelistPages: number;\n}): React.ReactElement {\n\tconst total = files.dbBytes + files.walBytes + files.shmBytes;\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">File Sizes</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>sqlite.db</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.dbBytes)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{files.walBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.wal</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.walBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.shm</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.shmBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Total</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(total)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Page size</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(pageSize)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Pages</td>\n\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{\" \"}\n\t\t\t\t\t\t\tfree)\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction TableRowCountsCard({\n\ttables,\n}: {\n\ttables: DbStatsResponse[\"tables\"];\n}): React.ReactElement {\n\tconst totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th className=\"num\">Rows</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{tables.map((t) => (\n\t\t\t\t\t\t<tr key={t.table}>\n\t\t\t\t\t\t\t<td>{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t{t.rowCount >= 0 ? t.rowCount.toLocaleString() : \"—\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction DbStatsCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: DbStatsResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">DB Stats</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<FileSizesCard\n\t\t\t\t\t\t\tfiles={data.storage.files}\n\t\t\t\t\t\t\tpageSize={data.storage.pageSize}\n\t\t\t\t\t\t\tpageCount={data.storage.pageCount}\n\t\t\t\t\t\t\tfreelistPages={data.storage.freelistPages}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<TableRowCountsCard tables={data.tables} />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Schema Health card\n// ---------------------------------------------------------------------------\n\nfunction ColumnAuditCard({\n\tcolumns,\n}: {\n\tcolumns: SchemaHealthResponse[\"columns\"];\n}): React.ReactElement {\n\tconst missing = columns.filter((c) => !c.present);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t<th>Decl</th>\n\t\t\t\t\t\t<th>Status</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{columns.map((c) => (\n\t\t\t\t\t\t<tr key={`${c.table}.${c.column}`}>\n\t\t\t\t\t\t\t<td>{c.table}</td>\n\t\t\t\t\t\t\t<td>{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"text-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={c.present ? \"text-ok\" : \"text-error\"}>\n\t\t\t\t\t\t\t\t{c.present ? \"✓\" : \"MISSING\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction SchemaHealthCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: SchemaHealthResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tSchema Health\n\t\t\t\t{data && (\n\t\t\t\t\t<span className={`badge ${data.healthy ? \"badge-ok\" : \"badge-bad\"}`}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"num\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Integrity</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\tdata.integrity[0] === \"ok\" ? \"text-ok\" : \"text-error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{data.integrity.join(\"; \")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ColumnAuditCard columns={data.columns} />\n\t\t\t\t\t\t{data.fkCheck.length > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tFK Check ({data.fkCheck.length})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{data.fkCheck.map((line, i) => (\n\t\t\t\t\t\t\t\t\t<div key={i} className=\"text-warn text-mono\">\n\t\t\t\t\t\t\t\t\t\t{line}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Actions card\n// ---------------------------------------------------------------------------\n\nfunction ActionsCard(): React.ReactElement {\n\tconst [results, setResults] = useState<MaintenanceActionResult[]>([]);\n\tconst [running, setRunning] = useState<string | null>(null);\n\tconst [days, setDays] = useState(\"30\");\n\n\tconst handleAction = useCallback(\n\t\tasync (def: ActionDef) => {\n\t\t\tconst action: MaintenanceAction = def.daysOld\n\t\t\t\t? { action: \"prune\", daysOld: Number(days) }\n\t\t\t\t: ({ action: def.key } as MaintenanceAction);\n\n\t\t\tsetRunning(def.key);\n\t\t\ttry {\n\t\t\t\tconst res = await postMaintenanceAction(action);\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toperation: def.key,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\taffected: 0,\n\t\t\t\t\t\t\treclaimedBytes: 0,\n\t\t\t\t\t\t\tsummary: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...prev,\n\t\t\t\t\t].slice(0, 20),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tsetRunning(null);\n\t\t\t}\n\t\t},\n\t\t[days],\n\t);\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">Actions</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<div className=\"action-grid\">\n\t\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={def.key} className=\"action-item\">\n\t\t\t\t\t\t\t\t<div className=\"action-info\">\n\t\t\t\t\t\t\t\t\t<div className=\"action-label\">\n\t\t\t\t\t\t\t\t\t\t{def.label}\n\t\t\t\t\t\t\t\t\t\t{def.dangerous && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"badge badge-warn\">DESTRUCTIVE</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"action-desc\">{def.desc}</div>\n\t\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclassName={`action-result ${lastResult.success ? \"text-ok\" : \"text-error\"}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"action-controls\">\n\t\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\t\tvalue={days}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setDays(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"days-input\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName={`btn ${def.dangerous ? \"btn-danger\" : \"btn-primary\"}`}\n\t\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tif (def.confirm && !window.confirm(def.confirm)) return;\n\t\t\t\t\t\t\t\t\t\t\thandleAction(def);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Debug bundle card — gather diagnostic info for bug reports\n// ---------------------------------------------------------------------------\n\nfunction DebugBundleCard(): React.ReactElement {\n\tconst [bundle, setBundle] = useState<DebugBundleResponse | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [showJson, setShowJson] = useState(false);\n\n\tconst gather = useCallback(async () => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst res = await fetchDebugBundle();\n\t\t\tsetBundle(res);\n\t\t\tsetShowJson(true);\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tconst copyToClipboard = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tvoid navigator.clipboard.writeText(text).catch(() => {});\n\t}, [bundle]);\n\n\tconst downloadJson = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tconst blob = new Blob([text], { type: \"application/json\" });\n\t\tconst url = URL.createObjectURL(blob);\n\t\tconst a = document.createElement(\"a\");\n\t\ta.href = url;\n\t\ta.download = `mega-compact-debug-bundle-${bundle.builtAt}.json`;\n\t\ta.click();\n\t\tURL.revokeObjectURL(url);\n\t}, [bundle]);\n\n\tconst criticalCount = bundle?.criticalEvents.length ?? 0;\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tDebug Bundle\n\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t<span className=\"text-warn\" style={{ marginLeft: \"1em\" }}>\n\t\t\t\t\t\t{criticalCount} critical event{criticalCount !== 1 ? \"s\" : \"\"} found\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<p className=\"text-muted\" style={{ marginBottom: \"0.75em\" }}>\n\t\t\t\t\tGather a diagnostic bundle (recent events, config flags, schema health,\n\t\t\t\t\tstore stats) to attach to a bug report. Critical/compaction events are\n\t\t\t\t\thighlighted.\n\t\t\t\t</p>\n\t\t\t\t<button onClick={gather} disabled={loading} style={{ marginRight: \"0.5em\" }}>\n\t\t\t\t\t{loading ? \"Gathering…\" : \"Gather Debug Logs\"}\n\t\t\t\t</button>\n\t\t\t\t{error && <div className=\"text-error\">{error}</div>}\n\t\t\t\t{bundle && showJson && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div style={{ marginTop: \"0.75em\", marginBottom: \"0.5em\" }}>\n\t\t\t\t\t\t\t<button onClick={copyToClipboard} style={{ marginRight: \"0.5em\" }}>\n\t\t\t\t\t\t\t\tCopy to clipboard\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button onClick={downloadJson}>Download JSON</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tonClick={() => setShowJson((v) => !v)}\n\t\t\t\t\t\t\t\tstyle={{ marginLeft: \"0.5em\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{showJson ? \"Hide\" : \"Show\"}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tCritical events ({criticalCount})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<pre className=\"text-mono\" style={{ maxHeight: \"12em\", overflow: \"auto\" }}>\n\t\t\t\t\t\t\t\t\t{JSON.stringify(bundle.criticalEvents, null, 2)}\n\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<pre className=\"text-mono\" style={{ maxHeight: \"24em\", overflow: \"auto\" }}>\n\t\t\t\t\t\t\t{JSON.stringify(bundle, null, 2)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Health Mitigation card — toggle auto-mitigation for degraded context\n// ---------------------------------------------------------------------------\n\nfunction HealthMitigationCard(): React.ReactElement {\n\tconst [mitigate, setMitigate] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [saving, setSaving] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst load = useCallback(async () => {\n\t\ttry {\n\t\t\tconst { fetchHealthSettings } = await import(\"../api/health\");\n\t\t\tconst s = await fetchHealthSettings();\n\t\t\tsetMitigate(s.mitigate);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => { void load(); }, [load]);\n\n\tconst toggle = useCallback(async () => {\n\t\tsetSaving(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst { setHealthMitigate } = await import(\"../api/health\");\n\t\t\tconst next = !mitigate;\n\t\t\tawait setHealthMitigate(next);\n\t\t\tsetMitigate(next);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t}, [mitigate]);\n\n\treturn (\n\t\t<div className=\"stat-section maintenance-card\">\n\t\t\t<div className=\"stat-label\">Context Health Mitigation</div>\n\t\t\t{loading ? (\n\t\t\t\t<p className=\"repo-none\">Loading…</p>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"health-mitigate-row\">\n\t\t\t\t\t\t<label className=\"health-mitigate-toggle\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={mitigate}\n\t\t\t\t\t\t\t\tonChange={toggle}\n\t\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>Auto-mitigation {mitigate ? \"ON\" : \"OFF\"}</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"health-mitigate-info\">\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tWhen <strong>ON</strong>, the extension automatically intervenes when\n\t\t\t\t\t\t\tcontext health degrades:\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li><strong>Forced compaction</strong> — when the composite health score\n\t\t\t\t\t\t\tdrops below 0.4 (severe drift or garbled output), a compaction is\n\t\t\t\t\t\t\ttriggered to flush the degraded context and rebuild from clean\n\t\t\t\t\t\t\tcheckpoints, even if the context window isn&apos;t full.</li>\n\t\t\t\t\t\t\t<li><strong>Prefix break</strong> — when KV cache poison is detected\n\t\t\t\t\t\t\t(score &lt; 0.3), the cached prefix is invalidated to force a cache\n\t\t\t\t\t\t\tmiss on the next turn, bypassing the corrupted KV state.</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t<p className=\"health-mitigate-warning\">\n\t\t\t\t\t\t\tWARNING: both interventions have costs — forced compaction discards\n\t\t\t\t\t\t\tlive context, and prefix breaks kill cache savings. Only enable when\n\t\t\t\t\t\t\tyou&apos;re seeing degraded output (garbled text, hallucinations,\n\t\t\t\t\t\t\tloss of coherence) and the Health tab confirms low scores.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tAlso settable via env var: <code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>.\n\t\t\t\t\t\t\tThe dashboard toggle persists to the store and takes precedence.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{error && <p className=\"health-mitigate-error\">Error: {error}</p>}\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main tab component\n// ---------------------------------------------------------------------------\n\nexport default function MaintenanceTab(): React.ReactElement {\n\tconst {\n\t\tdata: dbStats,\n\t\tloading: statsLoading,\n\t\terror: statsError,\n\t} = useApi<DbStatsResponse>(\n\t\tuseCallback(() => fetchDbStats(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\tconst {\n\t\tdata: schemaHealth,\n\t\tloading: healthLoading,\n\t\terror: healthError,\n\t} = useApi<SchemaHealthResponse>(\n\t\tuseCallback(() => fetchSchemaHealth(), []),\n\t\t{ pollInterval: 10000 },\n\t);\n\n\treturn (\n\t\t<div className=\"maintenance-tab\">\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<SchemaHealthCard\n\t\t\t\t\tdata={schemaHealth}\n\t\t\t\t\tloading={healthLoading}\n\t\t\t\t\terror={healthError}\n\t\t\t\t/>\n\t\t\t\t<DbStatsCard data={dbStats} loading={statsLoading} error={statsError} />\n\t\t\t</div>\n\t\t\t<ActionsCard />\n\t\t\t<DebugBundleCard />\n\t\t\t<HealthMitigationCard />\n\t\t</div>\n\t);\n}\n"],"file":"assets/MaintenanceTab-C4L2Ohea.js"}
1
+ {"version":3,"mappings":"oFAyCA,MAAMA,EAAuB,CAC5B,CACC,IAAK,kBACL,MAAO,kBACP,KAAM,6BACN,UAAW,IAEZ,CACC,IAAK,kBACL,MAAO,yBACP,KAAM,wCACN,UAAW,GACX,QAAS,6CAEV,CACC,IAAK,aACL,MAAO,iBACP,KAAM,qCACN,UAAW,IAEZ,CACC,IAAK,SACL,MAAO,SACP,KAAM,mCACN,UAAW,GACX,QAAS,4CAEV,CACC,IAAK,UACL,MAAO,UACP,KAAM,sBACN,UAAW,GACX,QAAS,uCAEV,CACC,IAAK,eACL,MAAO,eACP,KAAM,iCACN,UAAW,GACX,QAAS,yCAEV,CACC,IAAK,QACL,MAAO,iBACP,KAAM,gDACN,UAAW,GACX,QAAS,GACT,QAAS,4CAEX,EAMA,SAASC,EAASC,EAAmB,CACpC,OAAIA,EAAI,KAAa,GAAGA,CAAC,KACrBA,EAAI,KAAO,KAAa,IAAIA,EAAI,MAAM,QAAQ,CAAC,CAAC,MAC7C,IAAIA,GAAK,KAAO,OAAO,QAAQ,CAAC,CAAC,KACzC,CAMA,SAASC,EAAc,CACtB,MAAAC,EACA,SAAAC,EACA,UAAAC,EACA,cAAAC,CACD,EAKuB,CACtB,MAAMC,EAAQJ,EAAM,QAAUA,EAAM,SAAWA,EAAM,SACrD,OACCK,OAAC,OAAI,UAAU,eACd,UAAAC,MAAC,OAAI,UAAU,aAAa,sBAAU,EACtCA,MAAC,SAAM,UAAU,gBAChB,gBAAC,SACA,UAAAD,OAAC,MACA,UAAAC,MAAC,MAAG,qBAAS,QACZ,MAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,OAAO,EAAE,GAC9C,EACCA,EAAM,SAAW,GACjBK,OAAC,MACA,UAAAC,MAAC,MAAG,gBAAI,QACP,MAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,QAAQ,EAAE,GAC/C,EAEAA,EAAM,SAAW,GACjBK,OAAC,MACA,UAAAC,MAAC,MAAG,gBAAI,QACP,MAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,QAAQ,EAAE,GAC/C,SAEA,MACA,UAAAM,MAAC,MAAG,iBAAK,QACR,MAAG,UAAU,MAAO,SAAAT,EAASO,CAAK,EAAE,GACtC,SACC,MACA,UAAAE,MAAC,MAAG,qBAAS,QACZ,MAAG,UAAU,MAAO,SAAAT,EAASI,CAAQ,EAAE,GACzC,SACC,MACA,UAAAK,MAAC,MAAG,iBAAK,EACTD,OAAC,MAAG,UAAU,MACZ,UAAAH,EAAU,iBAAiB,KAAGC,EAAc,iBAAkB,IAAI,SAEpE,GACD,GACD,EACD,GACD,CAEF,CAEA,SAASI,EAAmB,CAC3B,OAAAC,CACD,EAEuB,CACtB,MAAMC,EAAYD,EAAO,OAAO,CAACE,EAAGC,IAAMD,EAAI,KAAK,IAAI,EAAGC,EAAE,QAAQ,EAAG,CAAC,EACxE,OACCN,OAAC,OAAI,UAAU,eACd,UAAAA,OAAC,OAAI,UAAU,aAAa,+BACRI,EAAU,iBAAiB,WAC/C,EACAJ,OAAC,SAAM,UAAU,gBAChB,UAAAC,MAAC,SACA,gBAAC,MACA,UAAAA,MAAC,MAAG,iBAAK,EACTA,MAAC,MAAG,UAAU,MAAM,gBAAI,GACzB,EACD,QACC,SACC,SAAAE,EAAO,IAAK,UACX,MACA,UAAAF,MAAC,MAAI,WAAE,MAAM,EACbA,MAAC,MAAG,UAAU,MACZ,WAAE,UAAY,EAAI,EAAE,SAAS,iBAAmB,IAClD,IAJQ,EAAE,KAKX,CACA,EACF,GACD,GACD,CAEF,CAEA,SAASM,EAAY,CACpB,KAAAC,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,OACCV,OAAC,OAAI,UAAU,OACd,UAAAC,MAAC,OAAI,UAAU,cAAc,oBAAQ,EACrCD,OAAC,OAAI,UAAU,YACb,UAAAS,GAAW,CAACD,GAAQP,MAAC,QAAK,UAAU,aAAa,oBAAQ,EACzDS,GAAS,CAACF,GAAQP,MAAC,QAAK,UAAU,aAAc,WAAM,QAAQ,EAC9DO,GACAR,OAAAW,WAAA,CACC,UAAAV,MAACP,EAAA,CACA,MAAOc,EAAK,QAAQ,MACpB,SAAUA,EAAK,QAAQ,SACvB,UAAWA,EAAK,QAAQ,UACxB,cAAeA,EAAK,QAAQ,gBAE7BP,MAACC,EAAA,CAAmB,OAAQM,EAAK,OAAQ,GAC1C,GAEF,GACD,CAEF,CAMA,SAASI,EAAgB,CACxB,QAAAC,CACD,EAEuB,CACtB,MAAMC,EAAUD,EAAQ,OAAQE,GAAM,CAACA,EAAE,OAAO,EAChD,OACCf,OAAC,OAAI,UAAU,eACd,UAAAA,OAAC,OAAI,UAAU,aAAa,2BACZa,EAAQ,OAAO,aAAWC,EAAQ,OAAO,aACzD,EACAd,OAAC,SAAM,UAAU,gBAChB,UAAAC,MAAC,SACA,gBAAC,MACA,UAAAA,MAAC,MAAG,iBAAK,EACTA,MAAC,MAAG,kBAAM,EACVA,MAAC,MAAG,gBAAI,EACRA,MAAC,MAAG,kBAAM,GACX,EACD,QACC,SACC,SAAAY,EAAQ,IAAKE,UACZ,MACA,UAAAd,MAAC,MAAI,WAAE,MAAM,EACbA,MAAC,MAAI,SAAAc,EAAE,OAAO,EACdd,MAAC,MAAG,UAAU,YAAa,WAAE,aAAa,EAC1CA,MAAC,MAAG,UAAWc,EAAE,QAAU,UAAY,aACrC,SAAAA,EAAE,QAAU,IAAM,UACpB,IANQ,GAAGA,EAAE,KAAK,IAAIA,EAAE,MAAM,EAO/B,CACA,EACF,GACD,GACD,CAEF,CAEA,SAASC,EAAiB,CACzB,KAAAR,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,OACCV,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,cAAc,0BAE3BQ,GACAP,MAAC,QAAK,UAAW,SAASO,EAAK,QAAU,WAAa,WAAW,GAC/D,SAAAA,EAAK,QAAU,UAAY,WAC7B,GAEF,EACAR,OAAC,OAAI,UAAU,YACb,UAAAS,GAAW,CAACD,GAAQP,MAAC,QAAK,UAAU,aAAa,oBAAQ,EACzDS,GAAS,CAACF,GAAQP,MAAC,QAAK,UAAU,aAAc,WAAM,QAAQ,EAC9DO,GACAR,OAAAW,WAAA,CACC,UAAAX,OAAC,OAAI,UAAU,WACd,UAAAC,MAAC,QAAK,UAAU,aAAa,0BAAc,EAC3CA,MAAC,QAAK,UAAU,MAAO,WAAK,cAAc,GAC3C,EACAD,OAAC,OAAI,UAAU,WACd,UAAAC,MAAC,QAAK,UAAU,aAAa,qBAAS,EACtCA,MAAC,QACA,UACCO,EAAK,UAAU,CAAC,IAAM,KAAO,UAAY,aAGzC,SAAAA,EAAK,UAAU,KAAK,IAAI,GAC1B,EACD,EACAP,MAACW,EAAA,CAAgB,QAASJ,EAAK,QAAS,EACvCA,EAAK,QAAQ,OAAS,GACtBR,OAAC,OAAI,UAAU,eACd,UAAAA,OAAC,OAAI,UAAU,uBAAuB,uBAC1BQ,EAAK,QAAQ,OAAO,KAChC,EACCA,EAAK,QAAQ,IAAI,CAACS,EAAMC,IACxBjB,MAAC,OAAY,UAAU,sBACrB,SAAAgB,CAAA,EADQC,CAEV,CACA,GACF,GAEF,GAEF,GACD,CAEF,CAMA,SAASC,GAAkC,CAC1C,KAAM,CAACC,EAASC,CAAU,EAAIC,WAAoC,EAAE,EAC9D,CAACC,EAASC,CAAU,EAAIF,WAAwB,IAAI,EACpD,CAACG,EAAMC,CAAO,EAAIJ,WAAS,IAAI,EAE/BK,EAAeC,cACpB,MAAOC,GAAmB,CACzB,MAAMC,EAA4BD,EAAI,QACnC,CAAE,OAAQ,QAAS,QAAS,OAAOJ,CAAI,GACtC,CAAE,OAAQI,EAAI,KAElBL,EAAWK,EAAI,GAAG,EAClB,GAAI,CACH,MAAME,EAAM,MAAMC,EAAsBF,CAAM,EAC9CT,EAAYY,GACX,CAAC,CAAE,GAAGF,EAAK,UAAW,KAAK,KAAI,EAAK,GAAGE,CAAI,EAAE,MAAM,EAAG,EAAE,EAE1D,OAASC,EAAK,CACbb,EAAYY,GACX,CACC,CACC,UAAWJ,EAAI,IACf,QAAS,GACT,SAAU,EACV,eAAgB,EAChB,QAASK,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EACxD,UAAW,KAAK,KAAI,EAErB,GAAGD,CAAA,EACF,MAAM,EAAG,EAAE,EAEf,SACCT,EAAW,IAAI,CAChB,CACD,EACA,CAACC,CAAI,GAGN,OACCzB,OAAC,OAAI,UAAU,OACd,UAAAC,MAAC,OAAI,UAAU,cAAc,mBAAO,EACpCA,MAAC,OAAI,UAAU,YACd,SAAAA,MAAC,OAAI,UAAU,cACb,SAAAV,EAAQ,IAAKsC,GAAQ,CACrB,MAAMM,EAAYZ,IAAYM,EAAI,IAC5BO,EAAahB,EAAQ,KAAMiB,GAAMA,EAAE,YAAcR,EAAI,GAAG,EAC9D,OACC7B,OAAC,OAAkB,UAAU,cAC5B,UAAAA,OAAC,OAAI,UAAU,cACd,UAAAA,OAAC,OAAI,UAAU,eACb,UAAA6B,EAAI,MACJA,EAAI,WACJ5B,MAAC,QAAK,UAAU,mBAAmB,uBAAW,GAEhD,EACAA,MAAC,OAAI,UAAU,cAAe,WAAI,KAAK,EACtCmC,GACAnC,MAAC,OACA,UAAW,iBAAiBmC,EAAW,QAAU,UAAY,YAAY,GAExE,SAAAA,EAAW,SACb,EAEF,EACApC,OAAC,OAAI,UAAU,kBACb,UAAA6B,EAAI,SACJ5B,MAAC,SACA,KAAK,SACL,IAAK,EACL,IAAK,IACL,MAAOwB,EACP,SAAWa,GAAMZ,EAAQY,EAAE,OAAO,KAAK,EACvC,UAAU,aACV,SAAUH,CAAA,GAGZlC,MAAC,UACA,UAAW,OAAO4B,EAAI,UAAY,aAAe,aAAa,GAC9D,SAAUN,IAAY,KACtB,QAAS,IAAM,CACVM,EAAI,SAAW,CAAC,OAAO,QAAQA,EAAI,OAAO,GAC9CF,EAAaE,CAAG,CACjB,EAEC,WAAY,WAAa,OAC3B,EACD,IAvCSA,EAAI,GAwCd,CAEF,CAAC,EACF,EACD,GACD,CAEF,CAMA,SAASU,GAAsC,CAC9C,KAAM,CAACC,EAAQC,CAAS,EAAInB,WAAqC,IAAI,EAC/D,CAACb,EAASiC,CAAU,EAAIpB,WAAS,EAAK,EACtC,CAACZ,EAAOiC,CAAQ,EAAIrB,WAAwB,IAAI,EAChD,CAACsB,EAAUC,CAAW,EAAIvB,WAAS,EAAK,EAExCwB,EAASlB,cAAY,SAAY,CACtCc,EAAW,EAAI,EACfC,EAAS,IAAI,EACb,GAAI,CACH,MAAMZ,EAAM,MAAMgB,EAAA,EAClBN,EAAUV,CAAG,EACbc,EAAY,EAAI,CACjB,OAASX,EAAK,CACbS,EAAST,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,CAC1D,SACCQ,EAAW,EAAK,CACjB,CACD,EAAG,EAAE,EAECM,EAAkBpB,cAAY,IAAM,CACzC,GAAI,CAACY,EAAQ,OACb,MAAMS,EAAO,KAAK,UAAUT,EAAQ,KAAM,CAAC,EACtC,UAAU,UAAU,UAAUS,CAAI,EAAE,MAAM,IAAM,CAAC,CAAC,CACxD,EAAG,CAACT,CAAM,CAAC,EAELU,EAAetB,cAAY,IAAM,CACtC,GAAI,CAACY,EAAQ,OACb,MAAMS,EAAO,KAAK,UAAUT,EAAQ,KAAM,CAAC,EACrCW,EAAO,IAAI,KAAK,CAACF,CAAI,EAAG,CAAE,KAAM,mBAAoB,EACpDG,EAAM,IAAI,gBAAgBD,CAAI,EAC9BE,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOD,EACTC,EAAE,SAAW,6BAA6Bb,EAAO,OAAO,QACxDa,EAAE,QACF,IAAI,gBAAgBD,CAAG,CACxB,EAAG,CAACZ,CAAM,CAAC,EAELc,EAAgBd,GAAQ,eAAe,QAAU,EAEvD,OACCxC,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,cAAc,yBAE3BsD,EAAgB,GAChBtD,OAAC,QAAK,UAAU,YAAY,MAAO,CAAE,WAAY,OAC/C,UAAAsD,EAAc,kBAAgBA,IAAkB,EAAI,IAAM,GAAG,UAC/D,GAEF,EACAtD,OAAC,OAAI,UAAU,YACd,UAAAC,MAAC,KAAE,UAAU,aAAa,MAAO,CAAE,aAAc,UAAY,uKAI7D,EACAA,MAAC,UAAO,QAAS6C,EAAQ,SAAUrC,EAAS,MAAO,CAAE,YAAa,SAChE,SAAAA,EAAU,aAAe,oBAC3B,EACCC,GAAST,MAAC,OAAI,UAAU,aAAc,SAAAS,EAAM,EAC5C8B,GAAUI,GACV5C,OAAAW,WAAA,CACC,UAAAX,OAAC,OAAI,MAAO,CAAE,UAAW,SAAU,aAAc,SAChD,UAAAC,MAAC,UAAO,QAAS+C,EAAiB,MAAO,CAAE,YAAa,SAAW,6BAEnE,EACA/C,MAAC,UAAO,QAASiD,EAAc,yBAAa,EAC5CjD,MAAC,UACA,QAAS,IAAM4C,EAAaU,GAAM,CAACA,CAAC,EACpC,MAAO,CAAE,WAAY,SAEpB,WAAW,OAAS,QACtB,EACD,EACCD,EAAgB,GAChBtD,OAAC,OAAI,UAAU,eACd,UAAAA,OAAC,OAAI,UAAU,uBAAuB,8BACnBsD,EAAc,KACjC,QACC,OAAI,UAAU,YAAY,MAAO,CAAE,UAAW,OAAQ,SAAU,QAC/D,cAAK,UAAUd,EAAO,eAAgB,KAAM,CAAC,EAC/C,GACD,QAEA,OAAI,UAAU,YAAY,MAAO,CAAE,UAAW,OAAQ,SAAU,QAC/D,cAAK,UAAUA,EAAQ,KAAM,CAAC,EAChC,GACD,GAEF,GACD,CAEF,CAMA,SAASgB,GAA2C,CACnD,KAAM,CAACC,EAAUC,CAAW,EAAIpC,WAAS,EAAK,EACxC,CAACb,EAASiC,CAAU,EAAIpB,WAAS,EAAI,EACrC,CAACqC,EAAQC,CAAS,EAAItC,WAAS,EAAK,EACpC,CAACZ,EAAOiC,CAAQ,EAAIrB,WAAwB,IAAI,EAEhDuC,EAAOjC,cAAY,SAAY,CACpC,GAAI,CACH,KAAM,CAAE,oBAAAkC,CAAA,EAAwB,MAAAC,EAAA,oCAAAD,GAAA,KAAM,QAAO,sBAAe,6BAAAA,CAAA,uBACtDzD,EAAI,MAAMyD,EAAA,EAChBJ,EAAYrD,EAAE,QAAQ,CACvB,OAASiC,EAAG,CACXK,EAASL,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,CACpD,SACCI,EAAW,EAAK,CACjB,CACD,EAAG,EAAE,EAELsB,YAAU,IAAM,CAAOH,EAAA,CAAQ,EAAG,CAACA,CAAI,CAAC,EAExC,MAAMI,EAASrC,cAAY,SAAY,CACtCgC,EAAU,EAAI,EACdjB,EAAS,IAAI,EACb,GAAI,CACH,KAAM,CAAE,kBAAAuB,CAAA,EAAsB,MAAAH,EAAA,kCAAAG,GAAA,KAAM,QAAO,sBAAe,2BAAAA,CAAA,uBACpDC,EAAO,CAACV,EACd,MAAMS,EAAkBC,CAAI,EAC5BT,EAAYS,CAAI,CACjB,OAAS7B,EAAG,CACXK,EAASL,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,CACpD,SACCsB,EAAU,EAAK,CAChB,CACD,EAAG,CAACH,CAAQ,CAAC,EAEb,OACCzD,OAAC,OAAI,UAAU,gCACd,UAAAC,MAAC,OAAI,UAAU,aAAa,qCAAyB,EACpDQ,EACAR,MAAC,KAAE,UAAU,YAAY,oBAAQ,EAEjCD,OAAAW,WAAA,CACC,UAAAV,MAAC,OAAI,UAAU,sBACd,SAAAD,OAAC,SAAM,UAAU,yBAChB,UAAAC,MAAC,SACA,KAAK,WACL,QAASwD,EACT,SAAUQ,EACV,SAAUN,CAAA,UAEV,QAAK,6BAAiBF,EAAW,KAAO,OAAM,GAChD,EACD,EACAzD,OAAC,OAAI,UAAU,uBACd,UAAAA,OAAC,KAAE,kBACGC,MAAC,UAAO,cAAE,EAAS,0EAEzB,SACC,MACA,UAAAD,OAAC,MAAG,UAAAC,MAAC,UAAO,6BAAiB,EAAS,2NAGkB,SACvD,MAAG,UAAAA,MAAC,UAAO,wBAAY,EAAS,iKAEuB,GACzD,EACAA,MAAC,KAAE,UAAU,0BAA0B,4QAKvC,SACC,KAAE,wCACyBA,MAAC,QAAK,iDAAqC,EAAO,sEAE9E,EACCS,GAASV,OAAC,KAAE,UAAU,wBAAwB,oBAAQU,CAAA,EAAM,GAC9D,GACD,GAEF,CAEF,CAMA,SAAwB0D,GAAqC,CAC5D,KAAM,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJC,EACH5C,cAAY,IAAM6C,EAAA,EAAgB,EAAE,EACpC,CAAE,aAAc,KAAM,EAGjB,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJJ,EACH5C,cAAY,IAAMiD,EAAA,EAAqB,EAAE,EACzC,CAAE,aAAc,IAAM,EAGvB,OACC7E,OAAC,OAAI,UAAU,kBACd,UAAAA,OAAC,OAAI,UAAU,YACd,UAAAC,MAACe,EAAA,CACA,KAAM0D,EACN,QAASC,EACT,MAAOC,CAAA,SAEPrE,EAAA,CAAY,KAAM8D,EAAS,QAASC,EAAc,MAAOC,CAAA,CAAY,GACvE,QACCpD,EAAA,EAAY,QACZoB,EAAA,EAAgB,QAChBiB,EAAA,EAAqB,GACvB,CAEF","names":["ACTIONS","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","jsxs","jsx","TableRowCountsCard","tables","totalRows","s","t","DbStatsCard","data","loading","error","Fragment","ColumnAuditCard","columns","missing","c","SchemaHealthCard","line","i","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","DebugBundleCard","bundle","setBundle","setLoading","setError","showJson","setShowJson","gather","fetchDebugBundle","copyToClipboard","text","downloadJson","blob","url","a","criticalCount","v","HealthMitigationCard","mitigate","setMitigate","saving","setSaving","load","fetchHealthSettings","__vitePreload","useEffect","toggle","setHealthMitigate","next","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"ignoreList":[],"sources":["../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Three cards:\n * 1. Schema Health (schema version + integrity + column audit)\n * 2. DB Stats (file sizes + table row counts)\n * 3. Actions (one-time maintenance operations with results history)\n *\n * All three fetch independently; failures in one do not block the others.\n */\n\nimport type React from \"react\";\nimport { useState, useCallback, useEffect } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n\tDebugBundleResponse,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchDbStats,\n\tfetchSchemaHealth,\n\tpostMaintenanceAction,\n\tfetchDebugBundle,\n} from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Action config\n// ---------------------------------------------------------------------------\n\ninterface ActionDef {\n\tkey: string;\n\tlabel: string;\n\tdesc: string;\n\tdangerous: boolean;\n\tconfirm?: string;\n\tdaysOld?: boolean;\n}\n\nconst ACTIONS: ActionDef[] = [\n\t{\n\t\tkey: \"integrity-check\",\n\t\tlabel: \"Integrity Check\",\n\t\tdesc: \"Run PRAGMA integrity_check\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"reconcile-dedup\",\n\t\tlabel: \"Reconcile Dedup Mirror\",\n\t\tdesc: \"Fix orphan dedup rows + backfill refs\",\n\t\tdangerous: false,\n\t\tconfirm: \"Reconcile dedup_mirror (safe read/write)?\",\n\t},\n\t{\n\t\tkey: \"checkpoint\",\n\t\tlabel: \"WAL Checkpoint\",\n\t\tdesc: \"Merge WAL into main DB (read-only)\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"vacuum\",\n\t\tlabel: \"VACUUM\",\n\t\tdesc: \"Rebuild DB reclaiming free pages\",\n\t\tdangerous: true,\n\t\tconfirm: \"VACUUM rewrites the entire DB. Continue?\",\n\t},\n\t{\n\t\tkey: \"reindex\",\n\t\tlabel: \"REINDEX\",\n\t\tdesc: \"Rebuild all indexes\",\n\t\tdangerous: true,\n\t\tconfirm: \"REINDEX rebuilds indexes. Continue?\",\n\t},\n\t{\n\t\tkey: \"fts5-rebuild\",\n\t\tlabel: \"FTS5 Rebuild\",\n\t\tdesc: \"Rebuild full-text search index\",\n\t\tdangerous: true,\n\t\tconfirm: \"Rebuild FTS5 trigram index. Continue?\",\n\t},\n\t{\n\t\tkey: \"prune\",\n\t\tlabel: \"Prune Old Rows\",\n\t\tdesc: \"Delete transcripts + epochs older than N days\",\n\t\tdangerous: true,\n\t\tdaysOld: true,\n\t\tconfirm: \"Delete old raw_transcript rows. Continue?\",\n\t},\n];\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nfunction fmtBytes(b: number): string {\n\tif (b < 1024) return `${b} B`;\n\tif (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;\n\treturn `${(b / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// DB Stats card\n// ---------------------------------------------------------------------------\n\nfunction FileSizesCard({\n\tfiles,\n\tpageSize,\n\tpageCount,\n\tfreelistPages,\n}: {\n\tfiles: DbStatsResponse[\"storage\"][\"files\"];\n\tpageSize: number;\n\tpageCount: number;\n\tfreelistPages: number;\n}): React.ReactElement {\n\tconst total = files.dbBytes + files.walBytes + files.shmBytes;\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">File Sizes</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>sqlite.db</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.dbBytes)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{files.walBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.wal</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.walBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.shm</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.shmBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Total</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(total)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Page size</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(pageSize)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Pages</td>\n\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{\" \"}\n\t\t\t\t\t\t\tfree)\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction TableRowCountsCard({\n\ttables,\n}: {\n\ttables: DbStatsResponse[\"tables\"];\n}): React.ReactElement {\n\tconst totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th className=\"num\">Rows</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{tables.map((t) => (\n\t\t\t\t\t\t<tr key={t.table}>\n\t\t\t\t\t\t\t<td>{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t{t.rowCount >= 0 ? t.rowCount.toLocaleString() : \"—\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction DbStatsCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: DbStatsResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">DB Stats</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<FileSizesCard\n\t\t\t\t\t\t\tfiles={data.storage.files}\n\t\t\t\t\t\t\tpageSize={data.storage.pageSize}\n\t\t\t\t\t\t\tpageCount={data.storage.pageCount}\n\t\t\t\t\t\t\tfreelistPages={data.storage.freelistPages}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<TableRowCountsCard tables={data.tables} />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Schema Health card\n// ---------------------------------------------------------------------------\n\nfunction ColumnAuditCard({\n\tcolumns,\n}: {\n\tcolumns: SchemaHealthResponse[\"columns\"];\n}): React.ReactElement {\n\tconst missing = columns.filter((c) => !c.present);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t<th>Decl</th>\n\t\t\t\t\t\t<th>Status</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{columns.map((c) => (\n\t\t\t\t\t\t<tr key={`${c.table}.${c.column}`}>\n\t\t\t\t\t\t\t<td>{c.table}</td>\n\t\t\t\t\t\t\t<td>{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"text-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={c.present ? \"text-ok\" : \"text-error\"}>\n\t\t\t\t\t\t\t\t{c.present ? \"✓\" : \"MISSING\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction SchemaHealthCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: SchemaHealthResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tSchema Health\n\t\t\t\t{data && (\n\t\t\t\t\t<span className={`badge ${data.healthy ? \"badge-ok\" : \"badge-bad\"}`}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"num\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Integrity</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\tdata.integrity[0] === \"ok\" ? \"text-ok\" : \"text-error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{data.integrity.join(\"; \")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ColumnAuditCard columns={data.columns} />\n\t\t\t\t\t\t{data.fkCheck.length > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tFK Check ({data.fkCheck.length})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{data.fkCheck.map((line, i) => (\n\t\t\t\t\t\t\t\t\t<div key={i} className=\"text-warn text-mono\">\n\t\t\t\t\t\t\t\t\t\t{line}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Actions card\n// ---------------------------------------------------------------------------\n\nfunction ActionsCard(): React.ReactElement {\n\tconst [results, setResults] = useState<MaintenanceActionResult[]>([]);\n\tconst [running, setRunning] = useState<string | null>(null);\n\tconst [days, setDays] = useState(\"30\");\n\n\tconst handleAction = useCallback(\n\t\tasync (def: ActionDef) => {\n\t\t\tconst action: MaintenanceAction = def.daysOld\n\t\t\t\t? { action: \"prune\", daysOld: Number(days) }\n\t\t\t\t: ({ action: def.key } as MaintenanceAction);\n\n\t\t\tsetRunning(def.key);\n\t\t\ttry {\n\t\t\t\tconst res = await postMaintenanceAction(action);\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toperation: def.key,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\taffected: 0,\n\t\t\t\t\t\t\treclaimedBytes: 0,\n\t\t\t\t\t\t\tsummary: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...prev,\n\t\t\t\t\t].slice(0, 20),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tsetRunning(null);\n\t\t\t}\n\t\t},\n\t\t[days],\n\t);\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">Actions</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<div className=\"action-grid\">\n\t\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={def.key} className=\"action-item\">\n\t\t\t\t\t\t\t\t<div className=\"action-info\">\n\t\t\t\t\t\t\t\t\t<div className=\"action-label\">\n\t\t\t\t\t\t\t\t\t\t{def.label}\n\t\t\t\t\t\t\t\t\t\t{def.dangerous && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"badge badge-warn\">DESTRUCTIVE</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"action-desc\">{def.desc}</div>\n\t\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclassName={`action-result ${lastResult.success ? \"text-ok\" : \"text-error\"}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"action-controls\">\n\t\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\t\tvalue={days}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setDays(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"days-input\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName={`btn ${def.dangerous ? \"btn-danger\" : \"btn-primary\"}`}\n\t\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tif (def.confirm && !window.confirm(def.confirm)) return;\n\t\t\t\t\t\t\t\t\t\t\thandleAction(def);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Debug bundle card — gather diagnostic info for bug reports\n// ---------------------------------------------------------------------------\n\nfunction DebugBundleCard(): React.ReactElement {\n\tconst [bundle, setBundle] = useState<DebugBundleResponse | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [showJson, setShowJson] = useState(false);\n\n\tconst gather = useCallback(async () => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst res = await fetchDebugBundle();\n\t\t\tsetBundle(res);\n\t\t\tsetShowJson(true);\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tconst copyToClipboard = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tvoid navigator.clipboard.writeText(text).catch(() => {});\n\t}, [bundle]);\n\n\tconst downloadJson = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tconst blob = new Blob([text], { type: \"application/json\" });\n\t\tconst url = URL.createObjectURL(blob);\n\t\tconst a = document.createElement(\"a\");\n\t\ta.href = url;\n\t\ta.download = `mega-compact-debug-bundle-${bundle.builtAt}.json`;\n\t\ta.click();\n\t\tURL.revokeObjectURL(url);\n\t}, [bundle]);\n\n\tconst criticalCount = bundle?.criticalEvents.length ?? 0;\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tDebug Bundle\n\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t<span className=\"text-warn\" style={{ marginLeft: \"1em\" }}>\n\t\t\t\t\t\t{criticalCount} critical event{criticalCount !== 1 ? \"s\" : \"\"} found\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<p className=\"text-muted\" style={{ marginBottom: \"0.75em\" }}>\n\t\t\t\t\tGather a diagnostic bundle (recent events, config flags, schema health,\n\t\t\t\t\tstore stats) to attach to a bug report. Critical/compaction events are\n\t\t\t\t\thighlighted.\n\t\t\t\t</p>\n\t\t\t\t<button onClick={gather} disabled={loading} style={{ marginRight: \"0.5em\" }}>\n\t\t\t\t\t{loading ? \"Gathering…\" : \"Gather Debug Logs\"}\n\t\t\t\t</button>\n\t\t\t\t{error && <div className=\"text-error\">{error}</div>}\n\t\t\t\t{bundle && showJson && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div style={{ marginTop: \"0.75em\", marginBottom: \"0.5em\" }}>\n\t\t\t\t\t\t\t<button onClick={copyToClipboard} style={{ marginRight: \"0.5em\" }}>\n\t\t\t\t\t\t\t\tCopy to clipboard\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button onClick={downloadJson}>Download JSON</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tonClick={() => setShowJson((v) => !v)}\n\t\t\t\t\t\t\t\tstyle={{ marginLeft: \"0.5em\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{showJson ? \"Hide\" : \"Show\"}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tCritical events ({criticalCount})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<pre className=\"text-mono\" style={{ maxHeight: \"12em\", overflow: \"auto\" }}>\n\t\t\t\t\t\t\t\t\t{JSON.stringify(bundle.criticalEvents, null, 2)}\n\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<pre className=\"text-mono\" style={{ maxHeight: \"24em\", overflow: \"auto\" }}>\n\t\t\t\t\t\t\t{JSON.stringify(bundle, null, 2)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Health Mitigation card — toggle auto-mitigation for degraded context\n// ---------------------------------------------------------------------------\n\nfunction HealthMitigationCard(): React.ReactElement {\n\tconst [mitigate, setMitigate] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [saving, setSaving] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst load = useCallback(async () => {\n\t\ttry {\n\t\t\tconst { fetchHealthSettings } = await import(\"../api/health\");\n\t\t\tconst s = await fetchHealthSettings();\n\t\t\tsetMitigate(s.mitigate);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => { void load(); }, [load]);\n\n\tconst toggle = useCallback(async () => {\n\t\tsetSaving(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst { setHealthMitigate } = await import(\"../api/health\");\n\t\t\tconst next = !mitigate;\n\t\t\tawait setHealthMitigate(next);\n\t\t\tsetMitigate(next);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t}, [mitigate]);\n\n\treturn (\n\t\t<div className=\"stat-section maintenance-card\">\n\t\t\t<div className=\"stat-label\">Context Health Mitigation</div>\n\t\t\t{loading ? (\n\t\t\t\t<p className=\"repo-none\">Loading…</p>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"health-mitigate-row\">\n\t\t\t\t\t\t<label className=\"health-mitigate-toggle\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={mitigate}\n\t\t\t\t\t\t\t\tonChange={toggle}\n\t\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>Auto-mitigation {mitigate ? \"ON\" : \"OFF\"}</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"health-mitigate-info\">\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tWhen <strong>ON</strong>, the extension automatically intervenes when\n\t\t\t\t\t\t\tcontext health degrades:\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<ul>\n\t\t\t\t\t\t\t<li><strong>Forced compaction</strong> — when the composite health score\n\t\t\t\t\t\t\tdrops below 0.4 (severe drift or garbled output), a compaction is\n\t\t\t\t\t\t\ttriggered to flush the degraded context and rebuild from clean\n\t\t\t\t\t\t\tcheckpoints, even if the context window isn&apos;t full.</li>\n\t\t\t\t\t\t\t<li><strong>Prefix break</strong> — when KV cache poison is detected\n\t\t\t\t\t\t\t(score &lt; 0.3), the cached prefix is invalidated to force a cache\n\t\t\t\t\t\t\tmiss on the next turn, bypassing the corrupted KV state.</li>\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t<p className=\"health-mitigate-warning\">\n\t\t\t\t\t\t\tWARNING: both interventions have costs — forced compaction discards\n\t\t\t\t\t\t\tlive context, and prefix breaks kill cache savings. Only enable when\n\t\t\t\t\t\t\tyou&apos;re seeing degraded output (garbled text, hallucinations,\n\t\t\t\t\t\t\tloss of coherence) and the Health tab confirms low scores.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tAlso settable via env var: <code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>.\n\t\t\t\t\t\t\tThe dashboard toggle persists to the store and takes precedence.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{error && <p className=\"health-mitigate-error\">Error: {error}</p>}\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main tab component\n// ---------------------------------------------------------------------------\n\nexport default function MaintenanceTab(): React.ReactElement {\n\tconst {\n\t\tdata: dbStats,\n\t\tloading: statsLoading,\n\t\terror: statsError,\n\t} = useApi<DbStatsResponse>(\n\t\tuseCallback(() => fetchDbStats(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\tconst {\n\t\tdata: schemaHealth,\n\t\tloading: healthLoading,\n\t\terror: healthError,\n\t} = useApi<SchemaHealthResponse>(\n\t\tuseCallback(() => fetchSchemaHealth(), []),\n\t\t{ pollInterval: 10000 },\n\t);\n\n\treturn (\n\t\t<div className=\"maintenance-tab\">\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<SchemaHealthCard\n\t\t\t\t\tdata={schemaHealth}\n\t\t\t\t\tloading={healthLoading}\n\t\t\t\t\terror={healthError}\n\t\t\t\t/>\n\t\t\t\t<DbStatsCard data={dbStats} loading={statsLoading} error={statsError} />\n\t\t\t</div>\n\t\t\t<ActionsCard />\n\t\t\t<DebugBundleCard />\n\t\t\t<HealthMitigationCard />\n\t\t</div>\n\t);\n}\n"],"file":"assets/MaintenanceTab-BQF7W7xX.js"}
@@ -1,2 +1,2 @@
1
- import{j as s,r as p}from"./index-m3rSvxMW.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},q={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function V(o,n){return`M 0,-${n} L ${o},0 L 0,${n} L -${o},0 Z`}function z(o){return!o||o==="active"?1:.5}function B(o,n,c,m,t,d,l){const h=d?S[m]??"#6366f1":"#d1d5db",i=t?"#ef4444":"none",u=z(l);switch(m){case"checkpoint":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0});case"turn":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u});case"turn-content":return s.jsxs("g",{children:[s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u}),s.jsx("circle",{cx:o,cy:n,r:Math.max(3,c*.3),fill:h,opacity:u})]});case"memory":const x=c*.85,j=c*.85;return s.jsx("path",{d:V(x,j),transform:`translate(${o}, ${n})`,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0})}}const U=5e3,A=.005,P=.01,_=.9,Y=.1,$=50;function Q(o){const n=o.nodes.map((t,d)=>{const l=2*Math.PI*d/Math.max(o.nodes.length,1),h=200;return{...t,pos:{x:Math.cos(l)*h+300,y:Math.sin(l)*h+200},vx:0,vy:0,pinned:!1}}),c=new Map;n.forEach((t,d)=>c.set(t.id,d));const m=o.edges.map(t=>{const d=c.get(t.source),l=c.get(t.target);return d===void 0||l===void 0||d===l?null:{source:d,target:l,weight:t.weight,type:t.type}}).filter(t=>t!==null);return{nodes:n,edges:m}}function X(o){const{nodes:n,edges:c}=o,m=n.length;if(m!==0)for(let t=0;t<m;t++){if(n[t].pinned)continue;let d=0,l=0;for(let i=0;i<m;i++){if(t===i)continue;const u=n[t].pos.x-n[i].pos.x,x=n[t].pos.y-n[i].pos.y,j=u*u+x*x||1,b=U/j,g=Math.sqrt(j);d+=u/g*b,l+=x/g*b}for(const i of c){let u;if(i.source===t)u=i.target;else if(i.target===t)u=i.source;else continue;const x=n[u].pos.x-n[t].pos.x,j=n[u].pos.y-n[t].pos.y;d+=x*A*i.weight,l+=j*A*i.weight}d-=n[t].pos.x*P,l-=n[t].pos.y*P,n[t].vx=(n[t].vx+d)*_,n[t].vy=(n[t].vy+l)*_;const h=Math.sqrt(n[t].vx*n[t].vx+n[t].vy*n[t].vy);h>$&&(n[t].vx=n[t].vx/h*$,n[t].vy=n[t].vy/h*$),h>Y&&(n[t].pos.x+=n[t].vx,n[t].pos.y+=n[t].vy)}}const Z=300,J=.3,k=900,w=600,y=40;function M(o){switch(o){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:o,shape:n,label:c})=>{const m={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:n.startsWith("hollow")||n==="filled-circle"?"50%":void 0};let t;return n==="line"?s.jsx("span",{style:{color:o,marginRight:"6px"},children:c}):(n==="filled-circle"?t=s.jsx("span",{style:{...m,backgroundColor:o}}):n==="hollow-circle"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent"}}):n==="hollow-ring"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent",position:"relative"},children:s.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:o,margin:"2px auto"}})}):t=s.jsx("span",{style:{...m,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:o}}),s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"memory-map-legend-item",children:[t,c]})," "]}))},ee=()=>{const[o,n]=p.useState(null),[c,m]=p.useState(!0),[t,d]=p.useState(null),[l,h]=p.useState(null),[i,u]=p.useState(""),[x]=p.useState(""),[j,b]=p.useState(0),g=p.useRef(null),C=p.useRef(0);p.useEffect(()=>{let e=!1;const r=new URLSearchParams;return x&&r.set("sessionId",x),r.set("threshold","0.7"),r.set("maxEdgesPerNode","4"),m(!0),d(null),fetch(`/api/memory-map?${r.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{e||(n(a),g.current=Q(a),b(1),m(!1))}).catch(a=>{e||(d(a instanceof Error?a.message:"Unknown error"),m(!1))}),()=>{e=!0,cancelAnimationFrame(C.current)}},[x]),p.useEffect(()=>{if(!g.current||j===0)return;const e=g.current;let r=0;const a=()=>{r>=Z||(X(e),r++,b(r),C.current=requestAnimationFrame(a))};return C.current=requestAnimationFrame(a),()=>cancelAnimationFrame(C.current)},[j]);const W=p.useCallback((e,r)=>{const a=g.current;a&&(a.nodes[r].pinned=!a.nodes[r].pinned,a.nodes[r].pinned||(a.nodes[r].vx=0,a.nodes[r].vy=0),h(f=>f?.id===e.id?null:e),b(f=>f+1))},[]),L=p.useMemo(()=>{const e=g.current;if(!e||!i)return null;const r=i.toLowerCase();return new Set(e.nodes.map((a,f)=>a.summaryTruncated.toLowerCase().includes(r)||a.label.toLowerCase().includes(r)?f:-1).filter(a=>a>=0))},[i]),E=p.useMemo(()=>{const e=o?.validation;return e?e.gatesPassed<e.gatesRun?{level:"red",label:"Gate failure"}:e.warnings.length>0?{level:"yellow",label:`${e.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[o?.validation]),D={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},F=p.useMemo(()=>{const e=o?.validation;if(!e)return`${o?.metadata.totalNodes??0} nodes`;const r=[];return e.sources.checkpoint>0&&r.push(`${e.sources.checkpoint} checkpoints`),e.sources.turn>0&&r.push(`${e.sources.turn} turns`),e.sources.turnContent>0&&r.push(`${e.sources.turnContent} turn-content`),e.sources.memory>0&&r.push(`${e.sources.memory} memories`),r.length===0&&r.push(`${o?.metadata.totalNodes??0} nodes`),r.join(" · ")},[o?.validation,o?.metadata.totalNodes]),I=p.useMemo(()=>{const e=o?.validation;if(!e)return"";const r=[];return r.push(e.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),r.push(e.sources.turn>0?"✓ turns":"✗ turns"),r.push(e.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),r.push(e.sources.memory>0?"✓ memories":"✗ memories"),r.join(" | ")},[o?.validation]),O=p.useMemo(()=>{const e=o?.validation;return e?e.sources.checkpoint===0&&e.sources.turn===0&&e.sources.turnContent===0&&e.sources.memory===0:o?.metadata.totalNodes===0},[o?.validation,o?.metadata.totalNodes]);if(c)return s.jsx("div",{className:"memory-map-loading",children:"Loading memory graph..."});if(t)return s.jsxs("div",{className:"memory-map-error",children:[s.jsxs("p",{children:["Failed to load memory map: ",t]}),s.jsx("button",{className:"memory-map-retry-btn",onClick:()=>b(e=>e+1),children:"Retry"})]});if(!o||!g.current||O)return s.jsx("div",{className:"memory-map-empty",children:s.jsx("p",{children:"Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner."})});const v=g.current;return s.jsxs("div",{className:"memory-map-container",children:[s.jsxs("div",{className:"memory-map-toolbar",children:[s.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:e=>u(e.target.value),className:"memory-map-search"}),s.jsxs("span",{className:"memory-map-stats",children:[s.jsx("span",{className:"memory-map-count-badge",children:F}),s.jsx("br",{}),s.jsxs("span",{className:"memory-map-edges",children:[o.edges.length," edges"]})]}),s.jsx("span",{className:"memory-map-health-badge",style:{backgroundColor:D[E.level]??"#6b7280",color:"#fff",padding:"2px 8px",borderRadius:"10px",fontSize:"11px",fontWeight:600,marginLeft:"8px"},title:`Validation: ${E.label}`,children:E.level==="green"?"Healthy":E.level==="yellow"?"Warnings":"Critical"})]}),I?s.jsx("div",{className:"memory-map-source-avail",style:{fontSize:"11px",padding:"2px 12px",color:"#6b7280"},children:I}):null,s.jsxs("div",{className:"memory-map-legend",children:[s.jsx("span",{className:"memory-map-legend-section",children:"Nodes:"}),s.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),s.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),s.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),s.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),s.jsx("span",{className:"memory-map-legend-sep",children:"|"}),s.jsx("span",{className:"memory-map-legend-section",children:"Edges:"}),s.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),s.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),s.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),s.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[v.edges.map((e,r)=>{const a=v.nodes[e.source],f=v.nodes[e.target],T=M(e.type),R=Math.max(1,e.weight*2.5);return s.jsx("line",{x1:y+a.pos.x/(v.nodes.length||1)*(k-2*y),y1:y+a.pos.y/(v.nodes.length||1)*(w-2*y),x2:y+f.pos.x/(v.nodes.length||1)*(k-2*y),y2:y+f.pos.y/(v.nodes.length||1)*(w-2*y),stroke:T,strokeWidth:R,opacity:J},`edge-${r}`)}),v.nodes.map((e,r)=>{const a=y+e.pos.x/(v.nodes.length||1)*(k-2*y),f=y+e.pos.y/(v.nodes.length||1)*(w-2*y),T=Math.min(20,8+e.tokenEstimate/500),R=L?L.has(r):!0,G=l?.id===e.id,H=e.label.length>20?e.label.slice(0,18)+"...":e.label;return s.jsxs("g",{className:"memory-map-node-group",onClick:()=>W(e,r),style:{cursor:"pointer"},children:[B(a,f,T,e.nodeType,G,R,e.dedupStatus),s.jsx("text",{x:a,y:f+T+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:H})]},`node-${r}`)})]}),l?s.jsxs("div",{className:"memory-map-detail",children:[s.jsxs("h3",{children:[s.jsx("span",{className:"memory-map-detail-type",style:{display:"inline-block",fontSize:"12px",fontWeight:400,color:"#6b7280",marginRight:"6px"},children:q[l.nodeType]}),l.label]}),s.jsx("p",{children:l.summaryTruncated}),l.topicSummary?s.jsxs("p",{className:"memory-map-topic",children:["Topic: ",l.topicSummary]}):null,s.jsxs("p",{children:["Tokens: ",l.tokenEstimate," | Decisions: ",l.decisionCount]}),s.jsxs("p",{children:["Session: ",l.sessionId]}),s.jsxs("p",{children:["Raptor Level: ",l.raptorLevel]}),s.jsx("p",{className:"memory-map-snippet",children:l.textSnippet})]}):null]})};export{ee as default};
2
- //# sourceMappingURL=MemoryMapTab-DMMQBq5X.js.map
1
+ import{j as s,r as p}from"./index-D-lPLRWB.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},q={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function V(o,n){return`M 0,-${n} L ${o},0 L 0,${n} L -${o},0 Z`}function z(o){return!o||o==="active"?1:.5}function B(o,n,c,m,t,d,l){const h=d?S[m]??"#6366f1":"#d1d5db",i=t?"#ef4444":"none",u=z(l);switch(m){case"checkpoint":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0});case"turn":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u});case"turn-content":return s.jsxs("g",{children:[s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u}),s.jsx("circle",{cx:o,cy:n,r:Math.max(3,c*.3),fill:h,opacity:u})]});case"memory":const x=c*.85,j=c*.85;return s.jsx("path",{d:V(x,j),transform:`translate(${o}, ${n})`,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0})}}const U=5e3,A=.005,P=.01,_=.9,Y=.1,$=50;function Q(o){const n=o.nodes.map((t,d)=>{const l=2*Math.PI*d/Math.max(o.nodes.length,1),h=200;return{...t,pos:{x:Math.cos(l)*h+300,y:Math.sin(l)*h+200},vx:0,vy:0,pinned:!1}}),c=new Map;n.forEach((t,d)=>c.set(t.id,d));const m=o.edges.map(t=>{const d=c.get(t.source),l=c.get(t.target);return d===void 0||l===void 0||d===l?null:{source:d,target:l,weight:t.weight,type:t.type}}).filter(t=>t!==null);return{nodes:n,edges:m}}function X(o){const{nodes:n,edges:c}=o,m=n.length;if(m!==0)for(let t=0;t<m;t++){if(n[t].pinned)continue;let d=0,l=0;for(let i=0;i<m;i++){if(t===i)continue;const u=n[t].pos.x-n[i].pos.x,x=n[t].pos.y-n[i].pos.y,j=u*u+x*x||1,b=U/j,g=Math.sqrt(j);d+=u/g*b,l+=x/g*b}for(const i of c){let u;if(i.source===t)u=i.target;else if(i.target===t)u=i.source;else continue;const x=n[u].pos.x-n[t].pos.x,j=n[u].pos.y-n[t].pos.y;d+=x*A*i.weight,l+=j*A*i.weight}d-=n[t].pos.x*P,l-=n[t].pos.y*P,n[t].vx=(n[t].vx+d)*_,n[t].vy=(n[t].vy+l)*_;const h=Math.sqrt(n[t].vx*n[t].vx+n[t].vy*n[t].vy);h>$&&(n[t].vx=n[t].vx/h*$,n[t].vy=n[t].vy/h*$),h>Y&&(n[t].pos.x+=n[t].vx,n[t].pos.y+=n[t].vy)}}const Z=300,J=.3,k=900,w=600,y=40;function M(o){switch(o){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:o,shape:n,label:c})=>{const m={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:n.startsWith("hollow")||n==="filled-circle"?"50%":void 0};let t;return n==="line"?s.jsx("span",{style:{color:o,marginRight:"6px"},children:c}):(n==="filled-circle"?t=s.jsx("span",{style:{...m,backgroundColor:o}}):n==="hollow-circle"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent"}}):n==="hollow-ring"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent",position:"relative"},children:s.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:o,margin:"2px auto"}})}):t=s.jsx("span",{style:{...m,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:o}}),s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"memory-map-legend-item",children:[t,c]})," "]}))},ee=()=>{const[o,n]=p.useState(null),[c,m]=p.useState(!0),[t,d]=p.useState(null),[l,h]=p.useState(null),[i,u]=p.useState(""),[x]=p.useState(""),[j,b]=p.useState(0),g=p.useRef(null),C=p.useRef(0);p.useEffect(()=>{let e=!1;const r=new URLSearchParams;return x&&r.set("sessionId",x),r.set("threshold","0.7"),r.set("maxEdgesPerNode","4"),m(!0),d(null),fetch(`/api/memory-map?${r.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{e||(n(a),g.current=Q(a),b(1),m(!1))}).catch(a=>{e||(d(a instanceof Error?a.message:"Unknown error"),m(!1))}),()=>{e=!0,cancelAnimationFrame(C.current)}},[x]),p.useEffect(()=>{if(!g.current||j===0)return;const e=g.current;let r=0;const a=()=>{r>=Z||(X(e),r++,b(r),C.current=requestAnimationFrame(a))};return C.current=requestAnimationFrame(a),()=>cancelAnimationFrame(C.current)},[j]);const W=p.useCallback((e,r)=>{const a=g.current;a&&(a.nodes[r].pinned=!a.nodes[r].pinned,a.nodes[r].pinned||(a.nodes[r].vx=0,a.nodes[r].vy=0),h(f=>f?.id===e.id?null:e),b(f=>f+1))},[]),L=p.useMemo(()=>{const e=g.current;if(!e||!i)return null;const r=i.toLowerCase();return new Set(e.nodes.map((a,f)=>a.summaryTruncated.toLowerCase().includes(r)||a.label.toLowerCase().includes(r)?f:-1).filter(a=>a>=0))},[i]),E=p.useMemo(()=>{const e=o?.validation;return e?e.gatesPassed<e.gatesRun?{level:"red",label:"Gate failure"}:e.warnings.length>0?{level:"yellow",label:`${e.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[o?.validation]),D={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},F=p.useMemo(()=>{const e=o?.validation;if(!e)return`${o?.metadata.totalNodes??0} nodes`;const r=[];return e.sources.checkpoint>0&&r.push(`${e.sources.checkpoint} checkpoints`),e.sources.turn>0&&r.push(`${e.sources.turn} turns`),e.sources.turnContent>0&&r.push(`${e.sources.turnContent} turn-content`),e.sources.memory>0&&r.push(`${e.sources.memory} memories`),r.length===0&&r.push(`${o?.metadata.totalNodes??0} nodes`),r.join(" · ")},[o?.validation,o?.metadata.totalNodes]),I=p.useMemo(()=>{const e=o?.validation;if(!e)return"";const r=[];return r.push(e.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),r.push(e.sources.turn>0?"✓ turns":"✗ turns"),r.push(e.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),r.push(e.sources.memory>0?"✓ memories":"✗ memories"),r.join(" | ")},[o?.validation]),O=p.useMemo(()=>{const e=o?.validation;return e?e.sources.checkpoint===0&&e.sources.turn===0&&e.sources.turnContent===0&&e.sources.memory===0:o?.metadata.totalNodes===0},[o?.validation,o?.metadata.totalNodes]);if(c)return s.jsx("div",{className:"memory-map-loading",children:"Loading memory graph..."});if(t)return s.jsxs("div",{className:"memory-map-error",children:[s.jsxs("p",{children:["Failed to load memory map: ",t]}),s.jsx("button",{className:"memory-map-retry-btn",onClick:()=>b(e=>e+1),children:"Retry"})]});if(!o||!g.current||O)return s.jsx("div",{className:"memory-map-empty",children:s.jsx("p",{children:"Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner."})});const v=g.current;return s.jsxs("div",{className:"memory-map-container",children:[s.jsxs("div",{className:"memory-map-toolbar",children:[s.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:e=>u(e.target.value),className:"memory-map-search"}),s.jsxs("span",{className:"memory-map-stats",children:[s.jsx("span",{className:"memory-map-count-badge",children:F}),s.jsx("br",{}),s.jsxs("span",{className:"memory-map-edges",children:[o.edges.length," edges"]})]}),s.jsx("span",{className:"memory-map-health-badge",style:{backgroundColor:D[E.level]??"#6b7280",color:"#fff",padding:"2px 8px",borderRadius:"10px",fontSize:"11px",fontWeight:600,marginLeft:"8px"},title:`Validation: ${E.label}`,children:E.level==="green"?"Healthy":E.level==="yellow"?"Warnings":"Critical"})]}),I?s.jsx("div",{className:"memory-map-source-avail",style:{fontSize:"11px",padding:"2px 12px",color:"#6b7280"},children:I}):null,s.jsxs("div",{className:"memory-map-legend",children:[s.jsx("span",{className:"memory-map-legend-section",children:"Nodes:"}),s.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),s.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),s.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),s.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),s.jsx("span",{className:"memory-map-legend-sep",children:"|"}),s.jsx("span",{className:"memory-map-legend-section",children:"Edges:"}),s.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),s.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),s.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),s.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[v.edges.map((e,r)=>{const a=v.nodes[e.source],f=v.nodes[e.target],T=M(e.type),R=Math.max(1,e.weight*2.5);return s.jsx("line",{x1:y+a.pos.x/(v.nodes.length||1)*(k-2*y),y1:y+a.pos.y/(v.nodes.length||1)*(w-2*y),x2:y+f.pos.x/(v.nodes.length||1)*(k-2*y),y2:y+f.pos.y/(v.nodes.length||1)*(w-2*y),stroke:T,strokeWidth:R,opacity:J},`edge-${r}`)}),v.nodes.map((e,r)=>{const a=y+e.pos.x/(v.nodes.length||1)*(k-2*y),f=y+e.pos.y/(v.nodes.length||1)*(w-2*y),T=Math.min(20,8+e.tokenEstimate/500),R=L?L.has(r):!0,G=l?.id===e.id,H=e.label.length>20?e.label.slice(0,18)+"...":e.label;return s.jsxs("g",{className:"memory-map-node-group",onClick:()=>W(e,r),style:{cursor:"pointer"},children:[B(a,f,T,e.nodeType,G,R,e.dedupStatus),s.jsx("text",{x:a,y:f+T+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:H})]},`node-${r}`)})]}),l?s.jsxs("div",{className:"memory-map-detail",children:[s.jsxs("h3",{children:[s.jsx("span",{className:"memory-map-detail-type",style:{display:"inline-block",fontSize:"12px",fontWeight:400,color:"#6b7280",marginRight:"6px"},children:q[l.nodeType]}),l.label]}),s.jsx("p",{children:l.summaryTruncated}),l.topicSummary?s.jsxs("p",{className:"memory-map-topic",children:["Topic: ",l.topicSummary]}):null,s.jsxs("p",{children:["Tokens: ",l.tokenEstimate," | Decisions: ",l.decisionCount]}),s.jsxs("p",{children:["Session: ",l.sessionId]}),s.jsxs("p",{children:["Raptor Level: ",l.raptorLevel]}),s.jsx("p",{className:"memory-map-snippet",children:l.textSnippet})]}):null]})};export{ee as default};
2
+ //# sourceMappingURL=MemoryMapTab-CPtHLv0A.js.map