pi-mega-compact 0.20.59 → 0.20.60

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 (78) hide show
  1. package/dist/config/vector-cortex-dash-0c.js +23 -0
  2. package/dist/config/vector-cortex.js +2 -0
  3. package/dist/config.js +1 -1
  4. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  5. package/dist/src/config/vector-cortex-dash-0c.js +23 -0
  6. package/dist/src/config/vector-cortex.js +2 -0
  7. package/dist/src/config.js +1 -1
  8. package/extensions/dashboard-client/dist/assets/{AreaChart-GTwQxOAm.js → AreaChart-B5Is2_EN.js} +2 -2
  9. package/extensions/dashboard-client/dist/assets/{AreaChart-GTwQxOAm.js.map → AreaChart-B5Is2_EN.js.map} +1 -1
  10. package/extensions/dashboard-client/dist/assets/{BarChart-BFqUD5L4.js → BarChart-Bj2oT871.js} +2 -2
  11. package/extensions/dashboard-client/dist/assets/{BarChart-BFqUD5L4.js.map → BarChart-Bj2oT871.js.map} +1 -1
  12. package/extensions/dashboard-client/dist/assets/CacheTab-DuhLeKHW.js +2 -0
  13. package/extensions/dashboard-client/dist/assets/CacheTab-DuhLeKHW.js.map +1 -0
  14. package/extensions/dashboard-client/dist/assets/{EventsTab-BYLh6cud.js → EventsTab-BG6L8uWh.js} +2 -2
  15. package/extensions/dashboard-client/dist/assets/{EventsTab-BYLh6cud.js.map → EventsTab-BG6L8uWh.js.map} +1 -1
  16. package/extensions/dashboard-client/dist/assets/{HealthTab-CkJXEbjn.js → HealthTab-CZmGCUJh.js} +2 -2
  17. package/extensions/dashboard-client/dist/assets/{HealthTab-CkJXEbjn.js.map → HealthTab-CZmGCUJh.js.map} +1 -1
  18. package/extensions/dashboard-client/dist/assets/{HydeDetailPanel-Dt9T7kc5.js → HydeDetailPanel-B-P1jTtk.js} +2 -2
  19. package/extensions/dashboard-client/dist/assets/{HydeDetailPanel-Dt9T7kc5.js.map → HydeDetailPanel-B-P1jTtk.js.map} +1 -1
  20. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Lm3v0nJO.js → MaintenanceTab-CoZ1mSjU.js} +2 -2
  21. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-Lm3v0nJO.js.map → MaintenanceTab-CoZ1mSjU.js.map} +1 -1
  22. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C8AeBIQi.js → MemoryMapTab-BwVaWniY.js} +2 -2
  23. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C8AeBIQi.js.map → MemoryMapTab-BwVaWniY.js.map} +1 -1
  24. package/extensions/dashboard-client/dist/assets/{MetricsTab-m410T4vk.js → MetricsCards-BCJoHe_n.js} +2 -2
  25. package/extensions/dashboard-client/dist/assets/MetricsCards-BCJoHe_n.js.map +1 -0
  26. package/extensions/dashboard-client/dist/assets/MetricsTab-UQSqUGXe.js +2 -0
  27. package/extensions/dashboard-client/dist/assets/MetricsTab-UQSqUGXe.js.map +1 -0
  28. package/extensions/dashboard-client/dist/assets/{OverviewTab-BfDfMFXx.js → OverviewTab-qi12IYvs.js} +2 -2
  29. package/extensions/dashboard-client/dist/assets/{OverviewTab-BfDfMFXx.js.map → OverviewTab-qi12IYvs.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{ReposTab-CsFxxqJ6.js → ReposTab-CcHZPzOO.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{ReposTab-CsFxxqJ6.js.map → ReposTab-CcHZPzOO.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{SessionsTab-DstDOzwu.js → SessionsTab-nzd1408Z.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{SessionsTab-DstDOzwu.js.map → SessionsTab-nzd1408Z.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/SetupTab-DPrG3s9V.js +3 -0
  35. package/extensions/dashboard-client/dist/assets/{SetupTab-DVT4PrH0.js.map → SetupTab-DPrG3s9V.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BkRzjyRm.js → TimeSavedCard-DhfdRM3_.js} +2 -2
  37. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BkRzjyRm.js.map → TimeSavedCard-DhfdRM3_.js.map} +1 -1
  38. package/extensions/dashboard-client/dist/assets/{TurnsTab-D5QTnv52.js → TurnsTab-oalBNLVr.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{TurnsTab-D5QTnv52.js.map → TurnsTab-oalBNLVr.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-B8B8RStV.js → VcStatusBadge-XGMW6u62.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-B8B8RStV.js.map → VcStatusBadge-XGMW6u62.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-CrGANNd_.js → VectorCortexTab-BACHKLjU.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-CrGANNd_.js.map → VectorCortexTab-BACHKLjU.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{WikiTab-DyD0yGzI.js → WikiTab-CGQWHuRq.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{WikiTab-DyD0yGzI.js.map → WikiTab-CGQWHuRq.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{button-Bynl3ONj.js → button-SyOWCGkp.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{button-Bynl3ONj.js.map → button-SyOWCGkp.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{card-Daf3pQIu.js → card-69DCTMel.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{card-Daf3pQIu.js.map → card-69DCTMel.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/{client-extra-DqAYZBW1.js → client-extra-CgeDg1Md.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{client-extra-DqAYZBW1.js.map → client-extra-CgeDg1Md.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-B-fe5jjr.js → generateCategoricalChart-BwTtrqm5.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-B-fe5jjr.js.map → generateCategoricalChart-BwTtrqm5.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{index-Bjvv23bp.js → index-BZcYzeCr.js} +3 -3
  55. package/extensions/dashboard-client/dist/assets/{index-Bjvv23bp.js.map → index-BZcYzeCr.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{switch-CivyERPS.js → switch-DhQfmSFD.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{switch-CivyERPS.js.map → switch-DhQfmSFD.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/{toggle-qmdkrAgF.js → toggle-B3gNAVog.js} +2 -2
  59. package/extensions/dashboard-client/dist/assets/{toggle-qmdkrAgF.js.map → toggle-B3gNAVog.js.map} +1 -1
  60. package/extensions/dashboard-client/dist/assets/{useSSE-uNuduyGe.js → useSSE-BkkjZ4bO.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{useSSE-uNuduyGe.js.map → useSSE-BkkjZ4bO.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/index.html +1 -1
  63. package/extensions/dashboard-client/src/tabs/AdminTab.tsx +81 -0
  64. package/extensions/dashboard-client/src/tabs/CacheTab/MetricsCards.tsx +66 -0
  65. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +46 -0
  66. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +10 -51
  67. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +33 -4
  68. package/extensions/dashboard-client/src/tabs/index.ts +7 -6
  69. package/extensions/dashboard-client/src/tabs/registry.ts +17 -0
  70. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  71. package/package.json +1 -1
  72. package/src/config/vector-cortex-dash-0c.ts +25 -0
  73. package/src/config/vector-cortex.ts +2 -0
  74. package/src/config.ts +1 -0
  75. package/extensions/dashboard-client/dist/assets/CacheTab-BLM6_PQQ.js +0 -2
  76. package/extensions/dashboard-client/dist/assets/CacheTab-BLM6_PQQ.js.map +0 -1
  77. package/extensions/dashboard-client/dist/assets/MetricsTab-m410T4vk.js.map +0 -1
  78. package/extensions/dashboard-client/dist/assets/SetupTab-DVT4PrH0.js +0 -3
@@ -1,2 +1,2 @@
1
- import{r as o,j as r,B as g}from"./index-Bjvv23bp.js";import{u as m}from"./useSSE-uNuduyGe.js";import{T as u}from"./toggle-qmdkrAgF.js";const p={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},_=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function h(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`;default:return`event=${e.type??"unknown"}`}}function f(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function x({events:e}){const[t,n]=o.useState("all"),[a,i]=o.useState(null),l=o.useMemo(()=>(t==="all"?e:e.filter(c=>c.type===t)).slice(-200).reverse(),[e,t]);return r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsxs("div",{className:"flex flex-wrap items-center gap-2",role:"toolbar",children:[_.map(s=>r.jsx(u,{pressed:t===s,onClick:()=>n(s),children:s==="all"?"all":s.replace(/_/g," ")},s)),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.length," buffered"]})]}),r.jsxs("ul",{className:"flex flex-col gap-1 font-mono text-xs",children:[l.length===0&&r.jsx("li",{className:"text-muted-foreground",children:"No events yet."}),l.map((s,c)=>{const d=p[s.type]??"ev-default";return r.jsxs("li",{className:`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${d}`,onClick:()=>i(a===c?null:c),children:[r.jsx("span",{className:"ev-time",children:f(s.ts)})," ",r.jsx("span",{className:"ev-type",children:s.type})," ",r.jsx("span",{className:"ev-summary",children:h(s)}),a===c&&r.jsx("pre",{className:"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs",children:JSON.stringify(s,null,2)})]},`${s.ts}-${c}`)})]})]})}const $=["all","compact","recall","config","crew","game"];function w({active:e,onFilter:t}){return r.jsx("div",{className:"flex flex-wrap gap-2",role:"toolbar","aria-label":"Event category filter",children:$.map(n=>r.jsx(u,{pressed:e===n,onClick:()=>t(n),children:n},n))})}const j=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),k=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),y=new Set(["recall_inject","hyde_executed","recall_metrics"]),S=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),E=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,t){if(t==="all")return!0;const n=e.type;return t==="compact"?j.has(n):t==="recall"?y.has(n):t==="config"?k.has(n):t==="crew"?S.has(n):E.has(n)}function P(){const{events:e,status:t,eventCount:n}=m(),[a,i]=o.useState("all"),l=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),s=t==="connected"?"success":t==="error"?"danger":t==="connecting"?"warning":"outline",c=t==="connected"?"connected":t==="connecting"?"connecting…":t==="error"?"error":"disconnected";return r.jsxs("div",{className:"flex flex-col gap-4",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs(g,{variant:s,role:"status",children:[r.jsx("span",{className:"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"}),c]}),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[n," events received"]})]}),r.jsx(w,{active:a,onFilter:i}),r.jsx(x,{events:l})]})}export{P as default};
2
- //# sourceMappingURL=EventsTab-BYLh6cud.js.map
1
+ import{r as o,j as r,B as g}from"./index-BZcYzeCr.js";import{u as m}from"./useSSE-BkkjZ4bO.js";import{T as u}from"./toggle-B3gNAVog.js";const p={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},_=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function h(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`;default:return`event=${e.type??"unknown"}`}}function f(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function x({events:e}){const[t,n]=o.useState("all"),[a,i]=o.useState(null),l=o.useMemo(()=>(t==="all"?e:e.filter(c=>c.type===t)).slice(-200).reverse(),[e,t]);return r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsxs("div",{className:"flex flex-wrap items-center gap-2",role:"toolbar",children:[_.map(s=>r.jsx(u,{pressed:t===s,onClick:()=>n(s),children:s==="all"?"all":s.replace(/_/g," ")},s)),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.length," buffered"]})]}),r.jsxs("ul",{className:"flex flex-col gap-1 font-mono text-xs",children:[l.length===0&&r.jsx("li",{className:"text-muted-foreground",children:"No events yet."}),l.map((s,c)=>{const d=p[s.type]??"ev-default";return r.jsxs("li",{className:`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${d}`,onClick:()=>i(a===c?null:c),children:[r.jsx("span",{className:"ev-time",children:f(s.ts)})," ",r.jsx("span",{className:"ev-type",children:s.type})," ",r.jsx("span",{className:"ev-summary",children:h(s)}),a===c&&r.jsx("pre",{className:"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs",children:JSON.stringify(s,null,2)})]},`${s.ts}-${c}`)})]})]})}const $=["all","compact","recall","config","crew","game"];function w({active:e,onFilter:t}){return r.jsx("div",{className:"flex flex-wrap gap-2",role:"toolbar","aria-label":"Event category filter",children:$.map(n=>r.jsx(u,{pressed:e===n,onClick:()=>t(n),children:n},n))})}const j=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),k=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),y=new Set(["recall_inject","hyde_executed","recall_metrics"]),S=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),E=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,t){if(t==="all")return!0;const n=e.type;return t==="compact"?j.has(n):t==="recall"?y.has(n):t==="config"?k.has(n):t==="crew"?S.has(n):E.has(n)}function P(){const{events:e,status:t,eventCount:n}=m(),[a,i]=o.useState("all"),l=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),s=t==="connected"?"success":t==="error"?"danger":t==="connecting"?"warning":"outline",c=t==="connected"?"connected":t==="connecting"?"connecting…":t==="error"?"error":"disconnected";return r.jsxs("div",{className:"flex flex-col gap-4",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs(g,{variant:s,role:"status",children:[r.jsx("span",{className:"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"}),c]}),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[n," events received"]})]}),r.jsx(w,{active:a,onFilter:i}),r.jsx(x,{events:l})]})}export{P as default};
2
+ //# sourceMappingURL=EventsTab-BG6L8uWh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventsTab-BYLh6cud.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\";\nimport { Toggle } from \"../components/ui/toggle\";\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\tdefault:\n\t\t\treturn `event=${ev.type ?? \"unknown\"}`;\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=\"flex flex-col gap-3\">\n\t\t\t<div className=\"flex flex-wrap items-center gap-2\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<Toggle\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\tpressed={filter === t}\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</Toggle>\n\t\t\t\t))}\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"flex flex-col gap-1 font-mono text-xs\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"text-muted-foreground\">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={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${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=\"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(ev, null, 2)}\n\t\t\t\t\t\t\t\t</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\";\nimport { Toggle } from \"../components/ui/toggle\";\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=\"flex flex-wrap gap-2\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<Toggle\n\t\t\t\t\tkey={cat}\n\t\t\t\t\tpressed={active === cat}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</Toggle>\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\";\nimport { Badge } from \"../components/ui/badge\";\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 RECALL_TYPES = new Set([\n\t\"recall_inject\",\n\t\"hyde_executed\",\n\t\"recall_metrics\",\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 RECALL_TYPES.has(t);\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 statusVariant =\n\t\tstatus === \"connected\"\n\t\t\t? (\"success\" as const)\n\t\t\t: status === \"error\"\n\t\t\t\t? (\"danger\" as const)\n\t\t\t\t: status === \"connecting\"\n\t\t\t\t\t? (\"warning\" as const)\n\t\t\t\t\t: (\"outline\" as const);\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Badge variant={statusVariant} role=\"status\">\n\t\t\t\t\t<span className=\"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</Badge>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{eventCount} events received\n\t\t\t\t</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","Toggle","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","RECALL_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusVariant","statusLabel","Badge"],"mappings":"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"}
1
+ {"version":3,"file":"EventsTab-BG6L8uWh.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\";\nimport { Toggle } from \"../components/ui/toggle\";\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\tdefault:\n\t\t\treturn `event=${ev.type ?? \"unknown\"}`;\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=\"flex flex-col gap-3\">\n\t\t\t<div className=\"flex flex-wrap items-center gap-2\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<Toggle\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\tpressed={filter === t}\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</Toggle>\n\t\t\t\t))}\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"flex flex-col gap-1 font-mono text-xs\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"text-muted-foreground\">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={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${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=\"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(ev, null, 2)}\n\t\t\t\t\t\t\t\t</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\";\nimport { Toggle } from \"../components/ui/toggle\";\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=\"flex flex-wrap gap-2\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<Toggle\n\t\t\t\t\tkey={cat}\n\t\t\t\t\tpressed={active === cat}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</Toggle>\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\";\nimport { Badge } from \"../components/ui/badge\";\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 RECALL_TYPES = new Set([\n\t\"recall_inject\",\n\t\"hyde_executed\",\n\t\"recall_metrics\",\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 RECALL_TYPES.has(t);\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 statusVariant =\n\t\tstatus === \"connected\"\n\t\t\t? (\"success\" as const)\n\t\t\t: status === \"error\"\n\t\t\t\t? (\"danger\" as const)\n\t\t\t\t: status === \"connecting\"\n\t\t\t\t\t? (\"warning\" as const)\n\t\t\t\t\t: (\"outline\" as const);\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Badge variant={statusVariant} role=\"status\">\n\t\t\t\t\t<span className=\"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</Badge>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{eventCount} events received\n\t\t\t\t</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","Toggle","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","RECALL_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusVariant","statusLabel","Badge"],"mappings":"wIAiBA,MAAMA,EAAsC,CAC3C,cAAe,aACf,YAAa,aACb,gBAAiB,aACjB,aAAc,aACd,aAAc,UACd,cAAe,UACf,gBAAiB,UACjB,qBAAsB,gBACtB,cAAe,YACf,gBAAiB,YACjB,eAAgB,YAChB,cAAe,YACf,sBAAuB,UACvB,kBAAmB,UACnB,mBAAoB,UACpB,eAAgB,YAChB,kBAAmB,UACnB,kBAAmB,UACnB,gBAAiB,UACjB,kBAAmB,UACnB,YAAa,SACd,EAGMC,EAAe,CACpB,MACA,gBACA,cACA,gBACA,sBACD,EAKA,SAASC,EAAUC,EAAsB,CACxC,OAAQA,EAAG,KAAA,CACV,IAAK,gBACJ,MAAO,WAAWA,EAAG,OAAO,YAAYA,EAAG,UAAU,MAAM,EAAG,CAAC,CAAC,GACjE,IAAK,cACJ,MAAO,SAASA,EAAG,WAAW,OAAOA,EAAG,OAAO,OAAOA,EAAG,aAAa,MAAM,EAAG,CAAC,CAAC,GAClF,IAAK,kBACJ,MAAO,YAAYA,EAAG,QAAQ,eAAeA,EAAG,SAAS,WAAWA,EAAG,KAAK,GAC7E,IAAK,eACJ,MAAO,UAAUA,EAAG,MAAM,GAC3B,IAAK,eACJ,MAAO,GAAGA,EAAG,IAAI,MAAMA,EAAG,EAAE,QAAQA,EAAG,UAAU,IAClD,IAAK,gBACJ,MAAO,GAAGA,EAAG,YAAY,IAAIA,EAAG,KAAK,GACtC,IAAK,kBACJ,MAAO,GAAGA,EAAG,SAAS,OAAOA,EAAG,QAAQ,IACzC,IAAK,uBACJ,MAAO,MAAMA,EAAG,aAAa,MAAM,EAAG,CAAC,CAAC,eAAeA,EAAG,aAAa,GACxE,IAAK,gBACJ,MAAO,MAAMA,EAAG,MAAM,MAAM,EAAG,EAAE,CAAC,YAAYA,EAAG,MAAM,QAAQA,EAAG,MAAM,GACzE,IAAK,kBACJ,MAAO,SAASA,EAAG,KAAK,WAAWA,EAAG,MAAM,GAC7C,IAAK,iBACJ,MAAO,OAAOA,EAAG,GAAG,GACrB,IAAK,gBACJ,MAAO,UAAUA,EAAG,MAAM,GAC3B,IAAK,wBACJ,MAAO,UAAUA,EAAG,YAAY,SAASA,EAAG,WAAW,GACxD,IAAK,oBACJ,MAAO,QAAQA,EAAG,SAAS,UAAUA,EAAG,SAAS,GAClD,IAAK,qBACJ,MAAO,SAASA,EAAG,WAAW,UAAUA,EAAG,KAAK,WAAWA,EAAG,MAAM,GACrE,IAAK,iBACJ,MAAO,QAAQA,EAAG,UAAU,MAAM,EAAG,CAAC,CAAC,QAAQA,EAAG,OAAO,gBAAgB,QAAQ,KAAK,MAAMA,EAAG,OAAO,CAAC,IACxG,IAAK,oBACL,IAAK,oBACL,IAAK,kBACL,IAAK,oBACL,IAAK,cACJ,MAAO,SAASA,EAAG,IAAI,GACxB,QACC,MAAO,SAASA,EAAG,MAAQ,SAAS,EAAA,CAEvC,CAEA,SAASC,EAASC,EAAoB,CACrC,GAAI,CAEH,OADU,IAAI,KAAKA,CAAE,EACZ,mBAAA,CACV,MAAQ,CACP,OAAOA,CACR,CACD,CAEO,SAASC,EAAY,CAAE,OAAAC,GAAgD,CAC7E,KAAM,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,KAAK,EAChD,CAACC,EAAUC,CAAW,EAAIF,EAAAA,SAAwB,IAAI,EAEtDG,EAAWC,EAAAA,QAAQ,KAEvBN,IAAW,MAAQD,EAASA,EAAO,OAAQQ,GAAMA,EAAE,OAASP,CAAM,GAEvD,MAAM,IAAc,EAAE,QAAA,EAChC,CAACD,EAAQC,CAAM,CAAC,EAEnB,OACCQ,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oCAAoC,KAAK,UACtD,SAAA,CAAAf,EAAa,IAAKgB,GAClBC,EAAAA,IAACC,EAAA,CAEA,QAASX,IAAWS,EACpB,QAAS,IAAMR,EAAUQ,CAAC,EAEzB,aAAM,MAAQ,MAAQA,EAAE,QAAQ,KAAM,GAAG,CAAA,EAJrCA,CAAA,CAMN,EACDD,EAAAA,KAAC,OAAA,CAAK,UAAU,gCAAiC,SAAA,CAAAT,EAAO,OAAO,WAAA,CAAA,CAAS,CAAA,EACzE,EACAS,EAAAA,KAAC,KAAA,CAAG,UAAU,wCACZ,SAAA,CAAAH,EAAS,SAAW,GACpBK,MAAC,KAAA,CAAG,UAAU,wBAAwB,SAAA,iBAAc,EAEpDL,EAAS,IAAI,CAACV,EAAIiB,IAAQ,CAC1B,MAAMC,EAAMrB,EAAYG,EAAG,IAAI,GAAK,aACpC,OACCa,EAAAA,KAAC,KAAA,CAEA,UAAW,oEAAoEK,CAAG,GAClF,QAAS,IAAMT,EAAYD,IAAaS,EAAM,KAAOA,CAAG,EAExD,SAAA,CAAAF,MAAC,QAAK,UAAU,UAAW,SAAAd,EAASD,EAAG,EAAE,EAAE,EAAQ,IACnDe,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,WAAG,KAAK,EAAQ,UAC1C,OAAA,CAAK,UAAU,aAAc,SAAAhB,EAAUC,CAAE,EAAE,EAC3CQ,IAAaS,GACbF,EAAAA,IAAC,MAAA,CAAI,UAAU,gEACb,SAAA,KAAK,UAAUf,EAAI,KAAM,CAAC,CAAA,CAC5B,CAAA,CAAA,EAVI,GAAGA,EAAG,EAAE,IAAIiB,CAAG,EAAA,CAcvB,CAAC,CAAA,CAAA,CACF,CAAA,EACD,CAEF,CC5IA,MAAME,EAAuC,CAC5C,MACA,UACA,SACA,SACA,OACA,MACD,EASO,SAASC,EAAoB,CACnC,OAAAC,EACA,SAAAC,CACD,EAAiD,CAChD,OACCP,EAAAA,IAAC,MAAA,CAAI,UAAU,uBAAuB,KAAK,UAAU,aAAW,wBAC9D,SAAAI,EAAW,IAAKI,GAChBR,EAAAA,IAACC,EAAA,CAEA,QAASK,IAAWE,EACpB,QAAS,IAAMD,EAASC,CAAG,EAE1B,SAAAA,CAAA,EAJIA,CAAA,CAMN,EACF,CAEF,CChCA,MAAMC,MAAoB,IAAI,CAC7B,gBACA,cACA,kBACA,cACD,CAAC,EACKC,MAAmB,IAAI,CAC5B,iBACA,gBACA,eACA,gBACA,kBACA,kBACA,sBACD,CAAC,EACKC,MAAmB,IAAI,CAC5B,gBACA,gBACA,gBACD,CAAC,EACKC,MAAiB,IAAI,CAC1B,wBACA,oBACA,oBACD,CAAC,EACKC,MAAiB,IAAI,CAC1B,oBACA,oBACA,kBACA,oBACA,aACD,CAAC,EAED,SAASC,EAAgB7B,EAAcuB,EAA6B,CACnE,GAAIA,IAAQ,MAAO,MAAO,GAC1B,MAAMT,EAAId,EAAG,KACb,OAAIuB,IAAQ,UAAkBC,EAAc,IAAIV,CAAC,EAC7CS,IAAQ,SAAiBG,EAAa,IAAIZ,CAAC,EAC3CS,IAAQ,SAAiBE,EAAa,IAAIX,CAAC,EAC3CS,IAAQ,OAAeI,EAAW,IAAIb,CAAC,EACpCc,EAAW,IAAId,CAAC,CACxB,CAEA,SAAwBgB,GAAgC,CACvD,KAAM,CAAE,OAAA1B,EAAQ,OAAA2B,EAAQ,WAAAC,CAAA,EAAeC,EAAA,EACjC,CAACC,EAAUC,CAAW,EAAI5B,EAAAA,SAAwB,KAAK,EAEvDG,EAAWC,EAAAA,QAChB,IAAMP,EAAO,OAAQQ,GAAMiB,EAAgBjB,EAAGsB,CAAQ,CAAC,EACvD,CAAC9B,EAAQ8B,CAAQ,CAAA,EAGZE,EACLL,IAAW,YACP,UACDA,IAAW,QACT,SACDA,IAAW,aACT,UACA,UACDM,EACLN,IAAW,YACR,YACAA,IAAW,aACV,cACAA,IAAW,QACV,QACA,eAEN,OACClB,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oCACd,SAAA,CAAAA,EAAAA,KAACyB,EAAA,CAAM,QAASF,EAAe,KAAK,SACnC,SAAA,CAAArB,EAAAA,IAAC,OAAA,CAAK,UAAU,wDAAwD,cAAY,OAAO,EAC1FsB,CAAA,EACF,EACAxB,EAAAA,KAAC,OAAA,CAAK,UAAU,gCACd,SAAA,CAAAmB,EAAW,kBAAA,CAAA,CACb,CAAA,EACD,EACAjB,EAAAA,IAACK,EAAA,CAAoB,OAAQc,EAAU,SAAUC,EAAa,EAC9DpB,EAAAA,IAACZ,EAAA,CAAY,OAAQO,CAAA,CAAU,CAAA,EAChC,CAEF"}
@@ -1,2 +1,2 @@
1
- import{r as d,j as e,B as g}from"./index-Bjvv23bp.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-Daf3pQIu.js";function m(s){return s>.8?"#4ade80":s>=.5?"#facc15":"#f87171"}function v({score:s}){const l=2*Math.PI*70,o=l*(1-Math.max(0,Math.min(1,s))),n=m(s);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:l,strokeDashoffset:o,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:(s*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 y({trend:s}){if(s.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const r=400,l=60,o=1,n=0,f=s.map((a,p)=>{const t=p/(s.length-1)*r,b=l-(a-n)/(o-n)*l;return`${t.toFixed(1)},${b.toFixed(1)}`}),c=s[s.length-1];return e.jsx("svg",{width:r,height:l,viewBox:`0 0 ${r} ${l}`,className:"health-spark",children:e.jsx("polyline",{points:f.join(" "),fill:"none",stroke:m(c),strokeWidth:"2"})})}function x({label:s,value:r}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:s}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${r*100}%`,background:m(r)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(r*100).toFixed(0),"%"]})]})}function H(){const[s,r]=d.useState(null),[l,o]=d.useState(null),[n,f]=d.useState(!0),c=d.useCallback(()=>{N().then(r).catch(t=>o(t instanceof Error?t.message:String(t))).finally(()=>f(!1))},[]);if(d.useEffect(()=>{c();const t=setInterval(c,5e3);return()=>clearInterval(t)},[c]),n&&!s)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(l&&!s)return e.jsxs("div",{className:"health-error",children:["Error: ",l]});if(!s)return e.jsx("div",{className:"health-empty",children:"No health data available."});const a=s.latest,p=a?.composite??0;return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(i,{children:e.jsx(h,{className:"flex justify-center",children:e.jsx(v,{score:p})})}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Sub-Scores"})}),e.jsxs(h,{children:[e.jsx(x,{label:"Drift",value:a?.driftScore??0}),e.jsx(x,{label:"Output Quality",value:a?.outputQuality??0}),e.jsx(x,{label:"Error Rate",value:a?.errorScore??0}),e.jsx(x,{label:"Cache Health",value:a?.cacheHealth??0}),e.jsx(x,{label:"Cache Poison",value:a?.cachePoison??0})]})]})]}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Health Trend (last ",s.trend.length," turns)"]})}),e.jsx(h,{children:e.jsx(y,{trend:s.trend})})]}),s.alerts.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Recent Alerts (",s.alerts.length,")"]})}),e.jsx(h,{children:e.jsx("div",{className:"flex flex-col gap-1",children:s.alerts.slice(-10).map(t=>e.jsxs("div",{className:"flex items-center gap-3 border-b border-border/50 py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:new Date(t.ts).toLocaleTimeString()}),e.jsx("span",{className:"text-muted-foreground",children:t.modelId??"(unknown)"}),e.jsx("span",{style:{color:m(t.composite)},children:(t.composite*100).toFixed(0)}),t.cachePoison<.3&&e.jsx(g,{variant:"danger",children:"CACHE POISON"})]},`${t.ts}-${t.turnIndex}`))})})]}),s.perModel.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Health by Model"})}),e.jsx(h,{children:e.jsxs("table",{className:"w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Model"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Avg Health"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Samples"})]})}),e.jsx("tbody",{children:s.perModel.map(t=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-3 py-2",children:t.modelId}),e.jsxs("td",{className:"px-3 py-2",style:{color:m(t.avgComposite)},children:[(t.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{className:"px-3 py-2",children:t.sampleCount})]},t.modelId))})]})})]})]})}export{H as default};
2
- //# sourceMappingURL=HealthTab-CkJXEbjn.js.map
1
+ import{r as d,j as e,B as g}from"./index-BZcYzeCr.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-69DCTMel.js";function m(s){return s>.8?"#4ade80":s>=.5?"#facc15":"#f87171"}function v({score:s}){const l=2*Math.PI*70,o=l*(1-Math.max(0,Math.min(1,s))),n=m(s);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:l,strokeDashoffset:o,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:(s*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 y({trend:s}){if(s.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const r=400,l=60,o=1,n=0,f=s.map((a,p)=>{const t=p/(s.length-1)*r,b=l-(a-n)/(o-n)*l;return`${t.toFixed(1)},${b.toFixed(1)}`}),c=s[s.length-1];return e.jsx("svg",{width:r,height:l,viewBox:`0 0 ${r} ${l}`,className:"health-spark",children:e.jsx("polyline",{points:f.join(" "),fill:"none",stroke:m(c),strokeWidth:"2"})})}function x({label:s,value:r}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:s}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${r*100}%`,background:m(r)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(r*100).toFixed(0),"%"]})]})}function H(){const[s,r]=d.useState(null),[l,o]=d.useState(null),[n,f]=d.useState(!0),c=d.useCallback(()=>{N().then(r).catch(t=>o(t instanceof Error?t.message:String(t))).finally(()=>f(!1))},[]);if(d.useEffect(()=>{c();const t=setInterval(c,5e3);return()=>clearInterval(t)},[c]),n&&!s)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(l&&!s)return e.jsxs("div",{className:"health-error",children:["Error: ",l]});if(!s)return e.jsx("div",{className:"health-empty",children:"No health data available."});const a=s.latest,p=a?.composite??0;return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(i,{children:e.jsx(h,{className:"flex justify-center",children:e.jsx(v,{score:p})})}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Sub-Scores"})}),e.jsxs(h,{children:[e.jsx(x,{label:"Drift",value:a?.driftScore??0}),e.jsx(x,{label:"Output Quality",value:a?.outputQuality??0}),e.jsx(x,{label:"Error Rate",value:a?.errorScore??0}),e.jsx(x,{label:"Cache Health",value:a?.cacheHealth??0}),e.jsx(x,{label:"Cache Poison",value:a?.cachePoison??0})]})]})]}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Health Trend (last ",s.trend.length," turns)"]})}),e.jsx(h,{children:e.jsx(y,{trend:s.trend})})]}),s.alerts.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Recent Alerts (",s.alerts.length,")"]})}),e.jsx(h,{children:e.jsx("div",{className:"flex flex-col gap-1",children:s.alerts.slice(-10).map(t=>e.jsxs("div",{className:"flex items-center gap-3 border-b border-border/50 py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:new Date(t.ts).toLocaleTimeString()}),e.jsx("span",{className:"text-muted-foreground",children:t.modelId??"(unknown)"}),e.jsx("span",{style:{color:m(t.composite)},children:(t.composite*100).toFixed(0)}),t.cachePoison<.3&&e.jsx(g,{variant:"danger",children:"CACHE POISON"})]},`${t.ts}-${t.turnIndex}`))})})]}),s.perModel.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Health by Model"})}),e.jsx(h,{children:e.jsxs("table",{className:"w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Model"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Avg Health"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Samples"})]})}),e.jsx("tbody",{children:s.perModel.map(t=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-3 py-2",children:t.modelId}),e.jsxs("td",{className:"px-3 py-2",style:{color:m(t.avgComposite)},children:[(t.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{className:"px-3 py-2",children:t.sampleCount})]},t.modelId))})]})})]})]})}export{H as default};
2
+ //# sourceMappingURL=HealthTab-CZmGCUJh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HealthTab-CkJXEbjn.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\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent className=\"flex justify-center\">\n\t\t\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Sub-Scores</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>Health Trend (last {data.trend.length} turns)</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recent Alerts ({data.alerts.length})</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${a.ts}-${a.turnIndex}`}\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3 border-b border-border/50 py-1 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t\t<span style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <Badge variant=\"danger\">CACHE POISON</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Health by Model</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Model</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg Health</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Samples</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t\t<tr key={m.modelId} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" style={{ color: healthColor(m.avgComposite) }}>\n\t\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\t</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\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","Card","CardContent","CardHeader","CardTitle","a","Badge","m"],"mappings":"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"}
1
+ {"version":3,"file":"HealthTab-CZmGCUJh.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\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent className=\"flex justify-center\">\n\t\t\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Sub-Scores</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>Health Trend (last {data.trend.length} turns)</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recent Alerts ({data.alerts.length})</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${a.ts}-${a.turnIndex}`}\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3 border-b border-border/50 py-1 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t\t<span style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <Badge variant=\"danger\">CACHE POISON</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Health by Model</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Model</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg Health</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Samples</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t\t<tr key={m.modelId} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" style={{ color: healthColor(m.avgComposite) }}>\n\t\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\t</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\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","Card","CardContent","CardHeader","CardTitle","a","Badge","m"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import{u as f,r as c,f as g,j as e,B as d}from"./index-Bjvv23bp.js";import{C as p}from"./card-Daf3pQIu.js";function x(s,r){return`${s}:${r}`}function j(s,r){const n=s.split(`
1
+ import{u as f,r as c,f as g,j as e,B as d}from"./index-BZcYzeCr.js";import{C as p}from"./card-69DCTMel.js";function x(s,r){return`${s}:${r}`}function j(s,r){const n=s.split(`
2
2
  `);return n.length>r?n.slice(0,r).join(`
3
3
  `):s}function y({row:s}){return s.recallScore>0||s.recallPass===1?e.jsxs("div",{className:"mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2",children:[e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsx("span",{className:"font-medium text-muted-foreground",children:"Recall quality"}),e.jsx(d,{variant:s.recallPass===1?"success":"warning",children:s.recallPass===1?"pass":"fail"}),e.jsxs("span",{children:["score ",e.jsx("strong",{className:"text-foreground",children:s.recallScore.toFixed(2)})]})]}),e.jsxs("div",{className:"mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground",children:[e.jsxs("span",{children:["relevance ",s.recallRelevance.toFixed(2)]}),e.jsxs("span",{children:["coverage ",s.recallCoverage.toFixed(2)]}),e.jsxs("span",{children:["diversity ",s.recallDiversity.toFixed(2)]}),e.jsxs("span",{children:["specificity ",s.recallSpecificity.toFixed(2)]})]})]}):e.jsx("div",{className:"mt-2 text-xs text-muted-foreground",children:"No recall-quality metrics for this turn."})}const H=({conversationId:s,turnIndex:r})=>{const{data:n}=f(c.useMemo(()=>()=>g(),[]),{pollInterval:3e4}),[o,m]=c.useState(!1),t=c.useMemo(()=>n?.recent.find(l=>x(l.conversationId,l.turnIndex)===x(s,r))??null,[n,s,r]);if(!n)return e.jsx("div",{className:"text-xs text-muted-foreground",children:"Loading HyDE stats…"});if(!t)return e.jsx("div",{className:"text-xs text-muted-foreground",children:"No HyDE/recall telemetry recorded for this turn."});const a=t.hydeDoc??"",i=a.split(`
4
4
  `).length>3,h=o||!i?a:j(a,3);return e.jsxs(p,{className:"border border-border/60 bg-bg-elevated/20 p-3",children:[t.hydeRan===1?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-center gap-2 text-xs",children:[e.jsx(d,{variant:"accent",children:"HyDE ran"}),e.jsxs("span",{className:"text-muted-foreground",children:["gen ",e.jsxs("strong",{className:"text-foreground",children:[t.hydeGenerationMs,"ms"]})]}),e.jsxs("span",{className:"text-muted-foreground",children:["raw ",e.jsx("strong",{className:"text-foreground",children:t.hydeRawCount}),e.jsx(u,{}),"hyde ",e.jsx("strong",{className:"text-foreground",children:t.hydeHydeCount}),e.jsx(u,{}),"fused ",e.jsx("strong",{className:"text-foreground",children:t.hydeFusedCount})]}),e.jsxs("span",{className:"text-muted-foreground",children:["lift"," ",e.jsxs("strong",{className:"text-neon",children:[t.hydeLift.toFixed(2),"×"]})]})]}),a&&e.jsx("pre",{className:"max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground",children:h}),i&&e.jsx("button",{type:"button",className:"mt-1 text-xs text-accent underline",onClick:()=>m(l=>!l),children:o?"Show less":"Show full hypothetical doc"})]}):e.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[e.jsx(d,{variant:"warning",children:"HyDE skipped"}),e.jsx("span",{className:"text-muted-foreground",children:N(t)})]}),e.jsx(y,{row:t})]})};function u(){return e.jsx("span",{className:"text-muted-foreground/60",children:" → "})}function N(s){switch(s.hydeReason){case"no-llm":return"No LLM embedder available (requires Ollama/HTTP embedder).";case"generation-failed":return"Hypothetical doc generation failed this turn.";case"disabled":return"HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).";default:return"HyDE was not invoked for this turn."}}export{H};
5
- //# sourceMappingURL=HydeDetailPanel-Dt9T7kc5.js.map
5
+ //# sourceMappingURL=HydeDetailPanel-B-P1jTtk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HydeDetailPanel-Dt9T7kc5.js","sources":["../../src/components/HydeDetailPanel.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/HydeDetailPanel.tsx — per-turn HyDE + recall\n * quality detail (H3.1).\n *\n * Fetches /api/rag-metrics once and indexes telemetry rows by the composite\n * `conversationId:turnIndex` key (the dashboard TurnRow has no DB id, so the\n * pair is the stable identifier). Renders one of three HyDE states:\n * - HyDE ran: hypothetical doc (collapsible), raw/hyde/fused hit counts,\n * lift multiplier, generation latency.\n * - HyDE skipped: reason (disabled / no-llm / generation-failed).\n * - No telemetry: nothing (the parent only renders us when flagged).\n * Plus a CRAG-style mini score line when recall metrics exist.\n *\n * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.\n */\n\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchRagMetrics } from \"../api/client\";\nimport type { RagMetricsResponse } from \"@contracts\";\nimport { Card } from \"./ui/card\";\nimport { Badge } from \"./ui/badge\";\n\n/** Per-turn telemetry row shape derived from the rag-metrics contract. */\ntype TelemetryRow = RagMetricsResponse[\"recent\"][number];\n\nexport interface HydeDetailPanelProps {\n\tconversationId: string;\n\tturnIndex: number;\n}\n\n/** Composite key matching a telemetry row to a rendered turn. */\nfunction rowKey(conversationId: string, turnIndex: number): string {\n\treturn `${conversationId}:${turnIndex}`;\n}\n\n/** First N lines of the hypothetical doc (or full when expanded). */\nfunction truncateDoc(doc: string, maxLines: number): string {\n\tconst lines = doc.split(\"\\n\");\n\treturn lines.length > maxLines ? lines.slice(0, maxLines).join(\"\\n\") : doc;\n}\n\n/** CRAG-style mini score line when recall metrics were captured. */\nfunction ScoreLine({ row }: { row: TelemetryRow }): React.ReactElement {\n\tconst hasScore = row.recallScore > 0 || row.recallPass === 1;\n\tif (!hasScore) {\n\t\treturn (\n\t\t\t<div className=\"mt-2 text-xs text-muted-foreground\">\n\t\t\t\tNo recall-quality metrics for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div className=\"mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2\">\n\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t<span className=\"font-medium text-muted-foreground\">Recall quality</span>\n\t\t\t\t<Badge variant={row.recallPass === 1 ? \"success\" : \"warning\"}>\n\t\t\t\t\t{row.recallPass === 1 ? \"pass\" : \"fail\"}\n\t\t\t\t</Badge>\n\t\t\t\t<span>\n\t\t\t\t\tscore <strong className=\"text-foreground\">{row.recallScore.toFixed(2)}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground\">\n\t\t\t\t<span>relevance {row.recallRelevance.toFixed(2)}</span>\n\t\t\t\t<span>coverage {row.recallCoverage.toFixed(2)}</span>\n\t\t\t\t<span>diversity {row.recallDiversity.toFixed(2)}</span>\n\t\t\t\t<span>specificity {row.recallSpecificity.toFixed(2)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport const HydeDetailPanel: React.FC<HydeDetailPanelProps> = ({\n\tconversationId,\n\tturnIndex,\n}) => {\n\tconst { data: metrics } = useApi<RagMetricsResponse>(\n\t\tuseMemo(() => () => fetchRagMetrics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [showFullDoc, setShowFullDoc] = useState(false);\n\n\t// Rebuild the index whenever metrics arrive; rare enough that a full\n\t// rebuild on each render is cheaper than memoizing with a mutable dep.\n\tconst row = useMemo(\n\t\t() =>\n\t\t\tmetrics?.recent.find(\n\t\t\t\t(t) => rowKey(t.conversationId, t.turnIndex) === rowKey(conversationId, turnIndex),\n\t\t\t) ?? null,\n\t\t[metrics, conversationId, turnIndex],\n\t);\n\n\tif (!metrics) {\n\t\treturn <div className=\"text-xs text-muted-foreground\">Loading HyDE stats…</div>;\n\t}\n\tif (!row) {\n\t\treturn (\n\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\tNo HyDE/recall telemetry recorded for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst doc = row.hydeDoc ?? \"\";\n\tconst docLines = doc.split(\"\\n\").length;\n\tconst showDoc = docLines > 3;\n\tconst visibleDoc = showFullDoc || !showDoc ? doc : truncateDoc(doc, 3);\n\n\treturn (\n\t\t<Card className=\"border border-border/60 bg-bg-elevated/20 p-3\">\n\t\t\t{row.hydeRan === 1 ? (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"mb-2 flex flex-wrap items-center gap-2 text-xs\">\n\t\t\t\t\t\t<Badge variant=\"accent\">HyDE ran</Badge>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tgen <strong className=\"text-foreground\">{row.hydeGenerationMs}ms</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\traw <strong className=\"text-foreground\">{row.hydeRawCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\thyde <strong className=\"text-foreground\">{row.hydeHydeCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\tfused <strong className=\"text-foreground\">{row.hydeFusedCount}</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tlift{\" \"}\n\t\t\t\t\t\t\t<strong className=\"text-neon\">{row.hydeLift.toFixed(2)}×</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{doc && (\n\t\t\t\t\t\t<pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t{visibleDoc}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t)}\n\t\t\t\t\t{showDoc && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"mt-1 text-xs text-accent underline\"\n\t\t\t\t\t\t\tonClick={() => setShowFullDoc((v) => !v)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{showFullDoc ? \"Show less\" : \"Show full hypothetical doc\"}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t\t<Badge variant=\"warning\">HyDE skipped</Badge>\n\t\t\t\t\t<span className=\"text-muted-foreground\">{skipReason(row)}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<ScoreLine row={row} />\n\t\t</Card>\n\t);\n};\n\nfunction Arrow(): React.ReactElement {\n\treturn <span className=\"text-muted-foreground/60\"> → </span>;\n}\n\nfunction skipReason(row: TelemetryRow): string {\n\tswitch (row.hydeReason) {\n\t\tcase \"no-llm\":\n\t\t\treturn \"No LLM embedder available (requires Ollama/HTTP embedder).\";\n\t\tcase \"generation-failed\":\n\t\t\treturn \"Hypothetical doc generation failed this turn.\";\n\t\tcase \"disabled\":\n\t\t\treturn \"HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).\";\n\t\tdefault:\n\t\t\treturn \"HyDE was not invoked for this turn.\";\n\t}\n}\n"],"names":["rowKey","conversationId","turnIndex","truncateDoc","doc","maxLines","lines","ScoreLine","row","jsxs","jsx","Badge","HydeDetailPanel","metrics","useApi","useMemo","fetchRagMetrics","showFullDoc","setShowFullDoc","useState","t","showDoc","visibleDoc","Card","Fragment","Arrow","v","skipReason"],"mappings":"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"}
1
+ {"version":3,"file":"HydeDetailPanel-B-P1jTtk.js","sources":["../../src/components/HydeDetailPanel.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/HydeDetailPanel.tsx — per-turn HyDE + recall\n * quality detail (H3.1).\n *\n * Fetches /api/rag-metrics once and indexes telemetry rows by the composite\n * `conversationId:turnIndex` key (the dashboard TurnRow has no DB id, so the\n * pair is the stable identifier). Renders one of three HyDE states:\n * - HyDE ran: hypothetical doc (collapsible), raw/hyde/fused hit counts,\n * lift multiplier, generation latency.\n * - HyDE skipped: reason (disabled / no-llm / generation-failed).\n * - No telemetry: nothing (the parent only renders us when flagged).\n * Plus a CRAG-style mini score line when recall metrics exist.\n *\n * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.\n */\n\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchRagMetrics } from \"../api/client\";\nimport type { RagMetricsResponse } from \"@contracts\";\nimport { Card } from \"./ui/card\";\nimport { Badge } from \"./ui/badge\";\n\n/** Per-turn telemetry row shape derived from the rag-metrics contract. */\ntype TelemetryRow = RagMetricsResponse[\"recent\"][number];\n\nexport interface HydeDetailPanelProps {\n\tconversationId: string;\n\tturnIndex: number;\n}\n\n/** Composite key matching a telemetry row to a rendered turn. */\nfunction rowKey(conversationId: string, turnIndex: number): string {\n\treturn `${conversationId}:${turnIndex}`;\n}\n\n/** First N lines of the hypothetical doc (or full when expanded). */\nfunction truncateDoc(doc: string, maxLines: number): string {\n\tconst lines = doc.split(\"\\n\");\n\treturn lines.length > maxLines ? lines.slice(0, maxLines).join(\"\\n\") : doc;\n}\n\n/** CRAG-style mini score line when recall metrics were captured. */\nfunction ScoreLine({ row }: { row: TelemetryRow }): React.ReactElement {\n\tconst hasScore = row.recallScore > 0 || row.recallPass === 1;\n\tif (!hasScore) {\n\t\treturn (\n\t\t\t<div className=\"mt-2 text-xs text-muted-foreground\">\n\t\t\t\tNo recall-quality metrics for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div className=\"mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2\">\n\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t<span className=\"font-medium text-muted-foreground\">Recall quality</span>\n\t\t\t\t<Badge variant={row.recallPass === 1 ? \"success\" : \"warning\"}>\n\t\t\t\t\t{row.recallPass === 1 ? \"pass\" : \"fail\"}\n\t\t\t\t</Badge>\n\t\t\t\t<span>\n\t\t\t\t\tscore <strong className=\"text-foreground\">{row.recallScore.toFixed(2)}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground\">\n\t\t\t\t<span>relevance {row.recallRelevance.toFixed(2)}</span>\n\t\t\t\t<span>coverage {row.recallCoverage.toFixed(2)}</span>\n\t\t\t\t<span>diversity {row.recallDiversity.toFixed(2)}</span>\n\t\t\t\t<span>specificity {row.recallSpecificity.toFixed(2)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport const HydeDetailPanel: React.FC<HydeDetailPanelProps> = ({\n\tconversationId,\n\tturnIndex,\n}) => {\n\tconst { data: metrics } = useApi<RagMetricsResponse>(\n\t\tuseMemo(() => () => fetchRagMetrics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [showFullDoc, setShowFullDoc] = useState(false);\n\n\t// Rebuild the index whenever metrics arrive; rare enough that a full\n\t// rebuild on each render is cheaper than memoizing with a mutable dep.\n\tconst row = useMemo(\n\t\t() =>\n\t\t\tmetrics?.recent.find(\n\t\t\t\t(t) => rowKey(t.conversationId, t.turnIndex) === rowKey(conversationId, turnIndex),\n\t\t\t) ?? null,\n\t\t[metrics, conversationId, turnIndex],\n\t);\n\n\tif (!metrics) {\n\t\treturn <div className=\"text-xs text-muted-foreground\">Loading HyDE stats…</div>;\n\t}\n\tif (!row) {\n\t\treturn (\n\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\tNo HyDE/recall telemetry recorded for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst doc = row.hydeDoc ?? \"\";\n\tconst docLines = doc.split(\"\\n\").length;\n\tconst showDoc = docLines > 3;\n\tconst visibleDoc = showFullDoc || !showDoc ? doc : truncateDoc(doc, 3);\n\n\treturn (\n\t\t<Card className=\"border border-border/60 bg-bg-elevated/20 p-3\">\n\t\t\t{row.hydeRan === 1 ? (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"mb-2 flex flex-wrap items-center gap-2 text-xs\">\n\t\t\t\t\t\t<Badge variant=\"accent\">HyDE ran</Badge>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tgen <strong className=\"text-foreground\">{row.hydeGenerationMs}ms</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\traw <strong className=\"text-foreground\">{row.hydeRawCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\thyde <strong className=\"text-foreground\">{row.hydeHydeCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\tfused <strong className=\"text-foreground\">{row.hydeFusedCount}</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tlift{\" \"}\n\t\t\t\t\t\t\t<strong className=\"text-neon\">{row.hydeLift.toFixed(2)}×</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{doc && (\n\t\t\t\t\t\t<pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t{visibleDoc}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t)}\n\t\t\t\t\t{showDoc && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"mt-1 text-xs text-accent underline\"\n\t\t\t\t\t\t\tonClick={() => setShowFullDoc((v) => !v)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{showFullDoc ? \"Show less\" : \"Show full hypothetical doc\"}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t\t<Badge variant=\"warning\">HyDE skipped</Badge>\n\t\t\t\t\t<span className=\"text-muted-foreground\">{skipReason(row)}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<ScoreLine row={row} />\n\t\t</Card>\n\t);\n};\n\nfunction Arrow(): React.ReactElement {\n\treturn <span className=\"text-muted-foreground/60\"> → </span>;\n}\n\nfunction skipReason(row: TelemetryRow): string {\n\tswitch (row.hydeReason) {\n\t\tcase \"no-llm\":\n\t\t\treturn \"No LLM embedder available (requires Ollama/HTTP embedder).\";\n\t\tcase \"generation-failed\":\n\t\t\treturn \"Hypothetical doc generation failed this turn.\";\n\t\tcase \"disabled\":\n\t\t\treturn \"HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).\";\n\t\tdefault:\n\t\t\treturn \"HyDE was not invoked for this turn.\";\n\t}\n}\n"],"names":["rowKey","conversationId","turnIndex","truncateDoc","doc","maxLines","lines","ScoreLine","row","jsxs","jsx","Badge","HydeDetailPanel","metrics","useApi","useMemo","fetchRagMetrics","showFullDoc","setShowFullDoc","useState","t","showDoc","visibleDoc","Card","Fragment","Arrow","v","skipReason"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{j as e,B as k,r as a,V as E,W as R,_ as C,u as w,X as D,Y as A}from"./index-Bjvv23bp.js";import{C as p,a as j,b as f,c as y}from"./card-Daf3pQIu.js";import{B as g}from"./button-Bynl3ONj.js";import{S as B}from"./switch-CivyERPS.js";function T({columns:t}){const l=t.filter(s=>!s.present);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Column Audit (",t.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Column"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Decl"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Status"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1",children:s.column}),e.jsx("td",{className:"px-2 py-1 font-mono",children:s.expectedDecl}),e.jsx("td",{className:`px-2 py-1 ${s.present?"text-emerald-400":"text-red-400"}`,children:s.present?"✓":"MISSING"})]},`${s.table}.${s.column}`))})]})]})}function L({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Schema Health"}),t&&e.jsx(k,{variant:t.healthy?"success":"danger",children:t.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Schema version"}),e.jsx("span",{className:"font-semibold",children:t.schemaVersion})]}),e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Integrity"}),e.jsx("span",{className:t.integrity[0]==="ok"?"text-emerald-400":"text-red-400",children:t.integrity.join("; ")})]}),e.jsx(T,{columns:t.columns}),t.fkCheck.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["FK Check (",t.fkCheck.length,")"]}),t.fkCheck.map((i,d)=>e.jsx("div",{className:"font-mono text-xs text-amber-400",children:i},d))]})]})]})]})}function b(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function O({files:t,pageSize:l,pageCount:s,freelistPages:i}){const d=t.dbBytes+t.walBytes+t.shmBytes;return e.jsxs("div",{className:"mt-3",children:[e.jsx("div",{className:"text-xs font-semibold text-muted-foreground",children:"File Sizes"}),e.jsx("table",{className:"mt-1 w-full border-collapse text-sm",children:e.jsxs("tbody",{children:[e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"sqlite.db"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.dbBytes)})]}),t.walBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".wal"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.walBytes)})]}),t.shmBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".shm"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.shmBytes)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Total"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(d)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Page size"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"px-2 py-1",children:"Pages"}),e.jsxs("td",{className:"px-2 py-1 text-right font-semibold",children:[s.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function _({tables:t}){const l=t.reduce((s,i)=>s+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground",children:"Rows"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1 text-right",children:s.rowCount>=0?s.rowCount.toLocaleString():"—"})]},s.table))})]})]})}function M({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"DB Stats"})}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsx(O,{files:t.storage.files,pageSize:t.storage.pageSize,pageCount:t.storage.pageCount,freelistPages:t.storage.freelistPages}),e.jsx(_,{tables:t.tables})]})]})]})}const H=[{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 F(){const[t,l]=a.useState([]),[s,i]=a.useState(null),[d,m]=a.useState("30"),u=a.useCallback(async r=>{const h=r.daysOld?{action:"prune",daysOld:Number(d)}:{action:r.key};i(r.key);try{const c=await E(h);l(n=>[{...c,timestamp:Date.now()},...n].slice(0,20))}catch(c){l(n=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[d]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Actions"})}),e.jsx("div",{className:"grid grid-cols-1 gap-3 lg:grid-cols-2",children:H.map(r=>{const h=s===r.key,c=t.find(n=>n.operation===r.key);return e.jsxs("div",{className:"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-semibold",children:r.label}),r.dangerous&&e.jsx(k,{variant:"danger",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:r.desc}),c&&e.jsx("div",{className:`mt-1 text-xs ${c.success?"text-emerald-400":"text-red-400"}`,children:c.summary})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[r.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:d,onChange:n=>m(n.target.value),className:"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary",disabled:h}),e.jsx(g,{type:"button",variant:r.dangerous?"ghost":"default",className:r.dangerous?"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300":void 0,disabled:s!==null,onClick:()=>{r.confirm&&!window.confirm(r.confirm)||u(r)},children:h?"Running…":"Run"})]})]},r.key)})})]})}function I(){const[t,l]=a.useState(null),[s,i]=a.useState(!1),[d,m]=a.useState(null),[u,r]=a.useState(!1),h=a.useCallback(async()=>{i(!0),m(null);try{const o=await R();l(o),r(!0)}catch(o){m(o instanceof Error?o.message:String(o))}finally{i(!1)}},[]),c=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),n=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2),S=new Blob([o],{type:"application/json"}),v=URL.createObjectURL(S),N=document.createElement("a");N.href=v,N.download=`mega-compact-debug-bundle-${t.builtAt}.json`,N.click(),URL.revokeObjectURL(v)},[t]),x=t?.criticalEvents.length??0;return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Debug Bundle"}),x>0&&e.jsxs("span",{className:"text-xs text-amber-400",children:[x," critical event",x!==1?"s":""," found"]})]}),e.jsxs(y,{children:[e.jsx("p",{className:"mb-3 text-sm text-muted-foreground",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(g,{type:"button",variant:"glass",onClick:h,disabled:s,children:s?"Gathering…":"Gather Debug Logs"}),d&&e.jsx("div",{className:"mt-2 text-sm text-red-400",children:d}),t&&u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 mt-3 flex gap-2",children:[e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:c,children:"Copy to clipboard"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:n,children:"Download JSON"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:()=>r(o=>!o),children:u?"Hide":"Show"})]}),x>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["Critical events (",x,")"]}),e.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t.criticalEvents,null,2)})]}),e.jsx("pre",{className:"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t,null,2)})]})]})]})}function P(){const[t,l]=a.useState(!1),[s,i]=a.useState(!0),[d,m]=a.useState(!1),[u,r]=a.useState(null),h=a.useCallback(async()=>{try{const{fetchHealthSettings:n}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await n();l(x.mitigate)}catch(n){r(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);a.useEffect(()=>{h()},[h]);const c=a.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:n}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await n(x),l(x)}catch(n){r(n instanceof Error?n.message:String(n))}finally{m(!1)}},[t]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Context Health Mitigation"})}),e.jsx(y,{children:s?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(B,{checked:t,onCheckedChange:c,disabled:d,"aria-label":"Context health auto-mitigation"}),e.jsxs("span",{className:"text-sm",children:["Auto-mitigation ",t?"ON":"OFF"]})]}),e.jsxs("div",{className:"mt-3 space-y-2 text-sm",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{className:"list-disc space-y-1 pl-5",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:"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400",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",{className:"text-muted-foreground",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."]}),u&&e.jsxs("p",{className:"text-sm text-red-400",children:["Error: ",u]})]})]})})]})}function J(){const{data:t,loading:l,error:s}=w(a.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(a.useCallback(()=>A(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(L,{data:i,loading:d,error:m}),e.jsx(M,{data:t,loading:l,error:s})]}),e.jsx(F,{}),e.jsx(I,{}),e.jsx(P,{})]})}export{J as default};
2
- //# sourceMappingURL=MaintenanceTab-Lm3v0nJO.js.map
1
+ import{j as e,B as k,r as a,V as E,W as R,_ as C,u as w,X as D,Y as A}from"./index-BZcYzeCr.js";import{C as p,a as j,b as f,c as y}from"./card-69DCTMel.js";import{B as g}from"./button-SyOWCGkp.js";import{S as B}from"./switch-DhQfmSFD.js";function T({columns:t}){const l=t.filter(s=>!s.present);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Column Audit (",t.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Column"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Decl"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Status"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1",children:s.column}),e.jsx("td",{className:"px-2 py-1 font-mono",children:s.expectedDecl}),e.jsx("td",{className:`px-2 py-1 ${s.present?"text-emerald-400":"text-red-400"}`,children:s.present?"✓":"MISSING"})]},`${s.table}.${s.column}`))})]})]})}function L({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Schema Health"}),t&&e.jsx(k,{variant:t.healthy?"success":"danger",children:t.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Schema version"}),e.jsx("span",{className:"font-semibold",children:t.schemaVersion})]}),e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Integrity"}),e.jsx("span",{className:t.integrity[0]==="ok"?"text-emerald-400":"text-red-400",children:t.integrity.join("; ")})]}),e.jsx(T,{columns:t.columns}),t.fkCheck.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["FK Check (",t.fkCheck.length,")"]}),t.fkCheck.map((i,d)=>e.jsx("div",{className:"font-mono text-xs text-amber-400",children:i},d))]})]})]})]})}function b(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function O({files:t,pageSize:l,pageCount:s,freelistPages:i}){const d=t.dbBytes+t.walBytes+t.shmBytes;return e.jsxs("div",{className:"mt-3",children:[e.jsx("div",{className:"text-xs font-semibold text-muted-foreground",children:"File Sizes"}),e.jsx("table",{className:"mt-1 w-full border-collapse text-sm",children:e.jsxs("tbody",{children:[e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"sqlite.db"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.dbBytes)})]}),t.walBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".wal"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.walBytes)})]}),t.shmBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".shm"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.shmBytes)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Total"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(d)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Page size"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"px-2 py-1",children:"Pages"}),e.jsxs("td",{className:"px-2 py-1 text-right font-semibold",children:[s.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function _({tables:t}){const l=t.reduce((s,i)=>s+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground",children:"Rows"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1 text-right",children:s.rowCount>=0?s.rowCount.toLocaleString():"—"})]},s.table))})]})]})}function M({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"DB Stats"})}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsx(O,{files:t.storage.files,pageSize:t.storage.pageSize,pageCount:t.storage.pageCount,freelistPages:t.storage.freelistPages}),e.jsx(_,{tables:t.tables})]})]})]})}const H=[{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 F(){const[t,l]=a.useState([]),[s,i]=a.useState(null),[d,m]=a.useState("30"),u=a.useCallback(async r=>{const h=r.daysOld?{action:"prune",daysOld:Number(d)}:{action:r.key};i(r.key);try{const c=await E(h);l(n=>[{...c,timestamp:Date.now()},...n].slice(0,20))}catch(c){l(n=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[d]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Actions"})}),e.jsx("div",{className:"grid grid-cols-1 gap-3 lg:grid-cols-2",children:H.map(r=>{const h=s===r.key,c=t.find(n=>n.operation===r.key);return e.jsxs("div",{className:"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-semibold",children:r.label}),r.dangerous&&e.jsx(k,{variant:"danger",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:r.desc}),c&&e.jsx("div",{className:`mt-1 text-xs ${c.success?"text-emerald-400":"text-red-400"}`,children:c.summary})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[r.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:d,onChange:n=>m(n.target.value),className:"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary",disabled:h}),e.jsx(g,{type:"button",variant:r.dangerous?"ghost":"default",className:r.dangerous?"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300":void 0,disabled:s!==null,onClick:()=>{r.confirm&&!window.confirm(r.confirm)||u(r)},children:h?"Running…":"Run"})]})]},r.key)})})]})}function I(){const[t,l]=a.useState(null),[s,i]=a.useState(!1),[d,m]=a.useState(null),[u,r]=a.useState(!1),h=a.useCallback(async()=>{i(!0),m(null);try{const o=await R();l(o),r(!0)}catch(o){m(o instanceof Error?o.message:String(o))}finally{i(!1)}},[]),c=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),n=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2),S=new Blob([o],{type:"application/json"}),v=URL.createObjectURL(S),N=document.createElement("a");N.href=v,N.download=`mega-compact-debug-bundle-${t.builtAt}.json`,N.click(),URL.revokeObjectURL(v)},[t]),x=t?.criticalEvents.length??0;return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Debug Bundle"}),x>0&&e.jsxs("span",{className:"text-xs text-amber-400",children:[x," critical event",x!==1?"s":""," found"]})]}),e.jsxs(y,{children:[e.jsx("p",{className:"mb-3 text-sm text-muted-foreground",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(g,{type:"button",variant:"glass",onClick:h,disabled:s,children:s?"Gathering…":"Gather Debug Logs"}),d&&e.jsx("div",{className:"mt-2 text-sm text-red-400",children:d}),t&&u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 mt-3 flex gap-2",children:[e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:c,children:"Copy to clipboard"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:n,children:"Download JSON"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:()=>r(o=>!o),children:u?"Hide":"Show"})]}),x>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["Critical events (",x,")"]}),e.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t.criticalEvents,null,2)})]}),e.jsx("pre",{className:"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t,null,2)})]})]})]})}function P(){const[t,l]=a.useState(!1),[s,i]=a.useState(!0),[d,m]=a.useState(!1),[u,r]=a.useState(null),h=a.useCallback(async()=>{try{const{fetchHealthSettings:n}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await n();l(x.mitigate)}catch(n){r(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);a.useEffect(()=>{h()},[h]);const c=a.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:n}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await n(x),l(x)}catch(n){r(n instanceof Error?n.message:String(n))}finally{m(!1)}},[t]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Context Health Mitigation"})}),e.jsx(y,{children:s?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(B,{checked:t,onCheckedChange:c,disabled:d,"aria-label":"Context health auto-mitigation"}),e.jsxs("span",{className:"text-sm",children:["Auto-mitigation ",t?"ON":"OFF"]})]}),e.jsxs("div",{className:"mt-3 space-y-2 text-sm",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{className:"list-disc space-y-1 pl-5",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:"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400",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",{className:"text-muted-foreground",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."]}),u&&e.jsxs("p",{className:"text-sm text-red-400",children:["Error: ",u]})]})]})})]})}function J(){const{data:t,loading:l,error:s}=w(a.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(a.useCallback(()=>A(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(L,{data:i,loading:d,error:m}),e.jsx(M,{data:t,loading:l,error:s})]}),e.jsx(F,{}),e.jsx(I,{}),e.jsx(P,{})]})}export{J as default};
2
+ //# sourceMappingURL=MaintenanceTab-CoZ1mSjU.js.map