pi-mega-compact 0.20.58 → 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.
- package/dist/config/vector-cortex-dash-0b.js +21 -0
- package/dist/config/vector-cortex-dash-0c.js +23 -0
- package/dist/config/vector-cortex.js +4 -0
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +2 -0
- package/dist/src/config/vector-cortex-dash-0b.js +21 -0
- package/dist/src/config/vector-cortex-dash-0c.js +23 -0
- package/dist/src/config/vector-cortex.js +4 -0
- package/dist/src/config.js +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-CtJ6dSMT.js → AreaChart-B5Is2_EN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CtJ6dSMT.js.map → AreaChart-B5Is2_EN.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-DJ3j3xDz.js → BarChart-Bj2oT871.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-DJ3j3xDz.js.map → BarChart-Bj2oT871.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-DuhLeKHW.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-DuhLeKHW.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{EventsTab-DF-Q3o7x.js → EventsTab-BG6L8uWh.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DF-Q3o7x.js.map → EventsTab-BG6L8uWh.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-Dr1HGOkl.js → HealthTab-CZmGCUJh.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-Dr1HGOkl.js.map → HealthTab-CZmGCUJh.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/HydeDetailPanel-B-P1jTtk.js +5 -0
- package/extensions/dashboard-client/dist/assets/HydeDetailPanel-B-P1jTtk.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-zKhR18_s.js → MaintenanceTab-CoZ1mSjU.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-zKhR18_s.js.map → MaintenanceTab-CoZ1mSjU.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DekIzfn2.js → MemoryMapTab-BwVaWniY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DekIzfn2.js.map → MemoryMapTab-BwVaWniY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CEKajY38.js → MetricsCards-BCJoHe_n.js} +2 -2
- package/extensions/dashboard-client/dist/assets/MetricsCards-BCJoHe_n.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-UQSqUGXe.js +2 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-UQSqUGXe.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{OverviewTab-24sG8xiE.js → OverviewTab-qi12IYvs.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-24sG8xiE.js.map → OverviewTab-qi12IYvs.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-70gkV8l9.js → ReposTab-CcHZPzOO.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-70gkV8l9.js.map → ReposTab-CcHZPzOO.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-nzd1408Z.js +2 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-nzd1408Z.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-DPrG3s9V.js +3 -0
- package/extensions/dashboard-client/dist/assets/{SetupTab-tbPrrfYx.js.map → SetupTab-DPrG3s9V.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B6mdtCWE.js → TimeSavedCard-DhfdRM3_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B6mdtCWE.js.map → TimeSavedCard-DhfdRM3_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-oalBNLVr.js +2 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-oalBNLVr.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-SDThp0R0.js → VcStatusBadge-XGMW6u62.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-SDThp0R0.js.map → VcStatusBadge-XGMW6u62.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BACHKLjU.js +2 -0
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-BACHKLjU.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-Cki48_8N.js → WikiTab-CGQWHuRq.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-Cki48_8N.js.map → WikiTab-CGQWHuRq.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-CojFYd7q.js → button-SyOWCGkp.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-CojFYd7q.js.map → button-SyOWCGkp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Cxeeje9o.js → card-69DCTMel.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Cxeeje9o.js.map → card-69DCTMel.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{client-extra-DLxyGxW8.js → client-extra-CgeDg1Md.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{client-extra-DLxyGxW8.js.map → client-extra-CgeDg1Md.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DRlqBks2.js → generateCategoricalChart-BwTtrqm5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DRlqBks2.js.map → generateCategoricalChart-BwTtrqm5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-CkbdlPBm.js → index-BZcYzeCr.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-CkbdlPBm.js.map → index-BZcYzeCr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-Cx_nl-Ba.js → switch-DhQfmSFD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-Cx_nl-Ba.js.map → switch-DhQfmSFD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CfIFzp6P.js → toggle-B3gNAVog.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CfIFzp6P.js.map → toggle-B3gNAVog.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-B9_N7ccG.js → useSSE-BkkjZ4bO.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-B9_N7ccG.js.map → useSSE-BkkjZ4bO.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/tabs/AdminTab.tsx +81 -0
- package/extensions/dashboard-client/src/tabs/CacheTab/MetricsCards.tsx +66 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +46 -0
- package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +10 -51
- package/extensions/dashboard-client/src/tabs/SessionsTab/TurnMemoryView.tsx +383 -0
- package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +77 -6
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +33 -4
- package/extensions/dashboard-client/src/tabs/VectorCortexTab/sections.tsx +201 -0
- package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +168 -122
- package/extensions/dashboard-client/src/tabs/index.ts +31 -0
- package/extensions/dashboard-client/src/tabs/registry.ts +34 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +12 -0
- package/package.json +1 -1
- package/src/config/vector-cortex-dash-0b.ts +23 -0
- package/src/config/vector-cortex-dash-0c.ts +25 -0
- package/src/config/vector-cortex.ts +4 -0
- package/src/config.ts +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-CXU1_9lR.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-CXU1_9lR.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-CEKajY38.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-B3quvYPZ.js +0 -2
- package/extensions/dashboard-client/dist/assets/SessionsTab-B3quvYPZ.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-tbPrrfYx.js +0 -3
- package/extensions/dashboard-client/dist/assets/TurnsTab-CmhRJJqr.js +0 -5
- package/extensions/dashboard-client/dist/assets/TurnsTab-CmhRJJqr.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DDjCkTjk.js +0 -2
- package/extensions/dashboard-client/dist/assets/VectorCortexTab-DDjCkTjk.js.map +0 -1
|
@@ -58,3 +58,37 @@ export const ADVANCED_TABS: TabDef[] = [
|
|
|
58
58
|
export const ADVANCED_TAB_IDS: ReadonlySet<TabId> = new Set(
|
|
59
59
|
ADVANCED_TABS.map((t) => t.id),
|
|
60
60
|
);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The fixed 7 dashboard navigation SURFACE ids from the DASH-0a merge plan
|
|
64
|
+
* (plan.ts `DASHSurface`). These are the consolidated surfaces consumed by the
|
|
65
|
+
* additive `tabs/index.ts` barrel. The existent 13-tab `TabId` union above is
|
|
66
|
+
* LEFT UNCHANGED (rollback-safe) — this constant documents the 7 surfaces the
|
|
67
|
+
* DASH-0d rewire will navigate, and deliberately does NOT replace TabId.
|
|
68
|
+
*/
|
|
69
|
+
export const DASH_SURFACE_IDS: readonly string[] = [
|
|
70
|
+
"overview",
|
|
71
|
+
"sessions",
|
|
72
|
+
"cache-perf",
|
|
73
|
+
"memory-graph",
|
|
74
|
+
"diagnostics",
|
|
75
|
+
"setup",
|
|
76
|
+
"admin",
|
|
77
|
+
] as const;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* DASH-0c: labels for the consolidated surfaces. Additive — the 13-tab
|
|
81
|
+
* `PRIMARY_TABS`/`ADVANCED_TABS` labels above stay untouched (rollback-safe;
|
|
82
|
+
* the live 13-tab UI renders as today until the DASH-0d rewire). `cache-perf`
|
|
83
|
+
* takes the "Cache+Performance" label once MetricsTab folds in as a Performance
|
|
84
|
+
* section; `admin` hosts the combined Maintenance+Config AdminTab.
|
|
85
|
+
*/
|
|
86
|
+
export const DASH_SURFACE_LABELS: Readonly<Record<string, string>> = {
|
|
87
|
+
overview: "Overview",
|
|
88
|
+
sessions: "Sessions",
|
|
89
|
+
"cache-perf": "Cache+Performance",
|
|
90
|
+
"memory-graph": "Memory Graph",
|
|
91
|
+
diagnostics: "Diagnostics",
|
|
92
|
+
setup: "Setup",
|
|
93
|
+
admin: "Admin",
|
|
94
|
+
} as const;
|
|
@@ -336,5 +336,17 @@ export const VECTOR_CORTEX_SETTINGS: SettingGroup = {
|
|
|
336
336
|
"DASH-0a dashboard tab-audit + consolidation merge plan: records (as typed constants) the collapse of the 13 current TabIds onto the fixed 7 navigational surfaces (Overview, Sessions, Cache+Performance, Memory Graph, Diagnostics, Setup, Admin), plus the deep-link map, a11y nav-map, and responsive plan. Contract-planning only — no component moves, no route change. OFF = the plan module is inert (never imported by the shell); all 13 tab/section top-level files render exactly as today (byte-identical predecessor).",
|
|
337
337
|
true,
|
|
338
338
|
),
|
|
339
|
+
boolDirect(
|
|
340
|
+
"MEGACOMPACT_DASH_0B",
|
|
341
|
+
"Dashboard Consolidation — Sessions/Cortex sections",
|
|
342
|
+
"DASH-0b dashboard consolidation execution: (1) the Sessions surface absorbs TurnsTab as a drill-down — a SessionsViews toggle renders the existent session summary/chart/table or the turns view (TurnMemoryView); (2) VectorCortexTab re-groups its flat 14-card layout under 4 <section aria-labelledby> headers (Cortex status / repair / cache / adaptive). OFF = SessionsTab renders only its own body (no toggle, no TurnMemoryView) and VectorCortexTab keeps its flat card layout (byte-identical predecessor).",
|
|
343
|
+
true,
|
|
344
|
+
),
|
|
345
|
+
boolDirect(
|
|
346
|
+
"MEGACOMPACT_DASH_0C",
|
|
347
|
+
"Dashboard Consolidation — Cache+Performance / Admin",
|
|
348
|
+
"DASH-0c dashboard consolidation execution: (1) the Cache surface absorbs MetricsTab as a Performance section (CacheTab label becomes 'Cache+Performance'); (2) MaintenanceTab + ConfigTab combine under a new AdminTab delegate-shell with an AdminViews toggle (maintenance/config), and SetupTab hides its Config sub-tab. OFF = CacheTab renders only its cache sections, MetricsTab/MaintenanceTab/ConfigTab render as standalone surfaces, SetupTab keeps its Config sub-tab (byte-identical predecessor).",
|
|
349
|
+
true,
|
|
350
|
+
),
|
|
339
351
|
],
|
|
340
352
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.60",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/vector-cortex-dash-0b.ts — DASH-0b dashboard consolidation execution flag.
|
|
3
|
+
*
|
|
4
|
+
* Extracted to this sibling (per the vector-cortex-dash-0a.ts precedent) so
|
|
5
|
+
* src/config/vector-cortex.ts stays under the 300-line soft limit. The flag is
|
|
6
|
+
* positive, defaults ON, and `MEGACOMPACT_DASH_0B=0` is byte-identical to the
|
|
7
|
+
* predecessor: SessionsTab and TurnsTab render as two independent top-level
|
|
8
|
+
* surfaces and VectorCortexTab keeps its flat card layout. Registered as a
|
|
9
|
+
* visible VECTOR_CORTEX_SETTINGS boolDirect toggle, never EXCLUDED.
|
|
10
|
+
*
|
|
11
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { sprintFlag } from "./vector-cortex-flag.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* DASH-0b — first execution sprint of the DASH consolidation plan:
|
|
18
|
+
* (1) Sessions surface absorbs TurnsTab as a drill-down via TurnMemoryView,
|
|
19
|
+
* (2) VectorCortexTab re-groups its flat cards under 4 `<section>` headers.
|
|
20
|
+
* `MEGACOMPACT_DASH_0B=0` renders the pre-DASH surfaces independently and the
|
|
21
|
+
* flat 14-card VectorCortexTab layout — byte-identical predecessor.
|
|
22
|
+
*/
|
|
23
|
+
export const DASH_0B_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_DASH_0B");
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/vector-cortex-dash-0c.ts — DASH-0c dashboard consolidation execution flag.
|
|
3
|
+
*
|
|
4
|
+
* Extracted to this sibling (per the vector-cortex-dash-0a.ts precedent) so
|
|
5
|
+
* src/config/vector-cortex.ts stays under the 300-line soft limit. The flag is
|
|
6
|
+
* positive, defaults ON, and `MEGACOMPACT_DASH_0C=0` is byte-identical to the
|
|
7
|
+
* predecessor: CacheTab and MetricsTab render as two independent top-level
|
|
8
|
+
* surfaces, MaintenanceTab and ConfigTab render exactly as today, and SetupTab
|
|
9
|
+
* keeps its Config sub-tab. Registered as a visible VECTOR_CORTEX_SETTINGS
|
|
10
|
+
* boolDirect toggle, never EXCLUDED.
|
|
11
|
+
*
|
|
12
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { sprintFlag } from "./vector-cortex-flag.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* DASH-0c — second execution sprint of the DASH consolidation plan:
|
|
19
|
+
* (1) Cache+Performance — CacheTab absorbs MetricsTab as a Performance section
|
|
20
|
+
* via MetricsCards; (2) Admin — MaintenanceTab + ConfigTab combine under a
|
|
21
|
+
* new AdminTab delegate-shell with an AdminViews toggle, and SetupTab hides
|
|
22
|
+
* its Config sub-tab. `MEGACOMPACT_DASH_0C=0` renders the predecessor
|
|
23
|
+
* standalone surfaces — byte-identical.
|
|
24
|
+
*/
|
|
25
|
+
export const DASH_0C_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_DASH_0C");
|
|
@@ -71,6 +71,10 @@ export { COSINE_FP_REAL_ENABLED } from "./vector-cortex-cosfp-real.js";
|
|
|
71
71
|
export { REPO_CORPUS_ENABLED } from "./repo-corpus.js";
|
|
72
72
|
// DASH-0a dashboard consolidation tab-plan flag extracted to vector-cortex-dash-0a.ts.
|
|
73
73
|
export { DASH_0A_ENABLED } from "./vector-cortex-dash-0a.js";
|
|
74
|
+
// DASH-0b dashboard consolidation execution flag extracted to vector-cortex-dash-0b.ts.
|
|
75
|
+
export { DASH_0B_ENABLED } from "./vector-cortex-dash-0b.js";
|
|
76
|
+
// DASH-0c dashboard consolidation execution flag extracted to vector-cortex-dash-0c.ts.
|
|
77
|
+
export { DASH_0C_ENABLED } from "./vector-cortex-dash-0c.js";
|
|
74
78
|
export { ENC_1A_ENABLED } from "./vector-cortex-enc1a.js";
|
|
75
79
|
export {
|
|
76
80
|
ENC_1B_ENABLED,
|
package/src/config.ts
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,r as h,u as m,y as I,z as k,x as w,l as D}from"./index-CkbdlPBm.js";import{C as W,T as F}from"./TimeSavedCard-B6mdtCWE.js";import{b as u,c as v,d as _,e as O,f as x}from"./format-COjDIVGj.js";import{l as G}from"./pricing-DkYp8-vi.js";import{C as f,a as C,b as S,c as N}from"./card-Cxeeje9o.js";import{B}from"./button-CojFYd7q.js";import{a as z}from"./client-extra-DLxyGxW8.js";function L(t){return t>=80?"ov-stat-value-green":t>=50?"ov-stat-value-yellow":"ov-stat-value-red"}function K(t){return t==null?"":t>=0?"ov-stat-value-green":"ov-stat-value-red"}function n({label:t,value:a,className:s,title:l}){return e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",title:l,children:t}),e.jsx("span",{className:`font-semibold${s?` ${s}`:""}`,children:a})]})}function U(t){return e.jsxs(f,{className:"electric-hover",children:[e.jsx(C,{children:e.jsx(S,{children:"Provider Prompt Cache"})}),e.jsxs(N,{children:[e.jsx(n,{label:"Cache Hit Rate",value:`${t.hitPct.toFixed(1)}%`,className:L(t.hitPct)}),e.jsx(n,{label:"Turns Recorded",value:String(t.turnCount)}),e.jsx(n,{label:"Cache Read Tokens",value:u(t.totalCacheRead)}),e.jsx(n,{label:"Cache Write Tokens",value:u(t.totalCacheWrite)}),e.jsx(n,{label:"Input Tokens",value:u(t.totalInput)}),e.jsx(n,{label:"$ Saved (reads)",value:v(t.cacheReadSaved)}),e.jsx(n,{label:"Write Investment",value:v(t.cacheWriteCost)}),e.jsx(n,{label:"Net Saved",value:v(t.netSaved),className:K(t.netSaved)}),e.jsx(n,{label:"Model",value:t.modelLabel??"—"}),e.jsx(n,{label:"Tracked Since",value:_(t.firstTurnAt)}),e.jsx(n,{label:"Last Updated",value:O(t.latestTurnAt)}),t.byModel.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"mt-2 text-xs font-semibold text-muted-foreground",children:"Per Model"}),t.byModel.map(a=>{const s=G(a.model),l=s!=null?a.totalCacheRead/1e6*s:null,r=s!=null?a.totalCacheWrite/1e6*s:null;return e.jsxs("div",{className:"mt-2",children:[e.jsx(n,{label:"Model",value:a.model}),e.jsx(n,{label:"Hit Rate",value:`${a.hitPct.toFixed(1)}%`,className:L(a.hitPct)}),e.jsx(n,{label:"Cache Read",value:u(a.totalCacheRead)}),e.jsx(n,{label:"Cache Write",value:u(a.totalCacheWrite)}),e.jsx(n,{label:"Samples",value:String(a.sampleCount)}),e.jsx(n,{label:"$ Saved (reads)",value:l!=null?v(l):"—"}),e.jsx(n,{label:"Write Investment",value:r!=null?v(r):"—"})]},a.model)})]})]})]})}const V=["ov-bar-green","ov-bar-blue","ov-bar-teal","ov-bar-yellow","ov-bar-gray"],q=["Permanent","Epoch","Topic","Thread","Volatile"],J={good:"ov-text-green",fair:"ov-text-yellow",degraded:"ov-text-orange",poor:"ov-text-red"};function Q({data:t}){const a=Math.max(...t.buckets.map(l=>l.count),1),s=J[t.health.label]??"ov-text-gray";return e.jsxs("div",{className:"ov-card",children:[e.jsx("h3",{className:"ov-card-title",children:"Cache Stripe Distribution"}),e.jsxs("div",{className:"ov-stripe-health-summary",children:[e.jsx("span",{className:`ov-stripe-health-badge ${s}`,children:t.health.label}),e.jsx("span",{className:"ov-stripe-health-score",children:x(t.health.score)}),e.jsxs("span",{className:"ov-stripe-churn",children:["Churn: ",x(t.health.churnRate)]}),e.jsxs("span",{className:"ov-stripe-total",children:[t.totalChunks," total chunks"]})]}),e.jsx("div",{className:"ov-stripe-bars",children:t.buckets.map(l=>{const r=a>0?Math.max(0,Math.min(100,l.count/a*100)):0,i=V[l.stripe]??"ov-bar-gray",o=l.label||(q[l.stripe]??`Stripe ${l.stripe}`);return e.jsxs("div",{className:"ov-stripe-row",children:[e.jsx("span",{className:"ov-stripe-label",title:o,children:o}),e.jsx("div",{className:"ov-bar-track",children:e.jsx("div",{className:`ov-bar-fill ${i}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"ov-stripe-count",children:l.count}),e.jsx("span",{className:"ov-stripe-stability",title:`min ${x(l.minStability)} / max ${x(l.maxStability)}`,children:x(l.avgStability)})]},l.stripe)})}),e.jsxs("div",{className:"ov-stripe-footer",children:["Epoch: ",t.epochId??"—","· Dominant tier: ",x(t.health.dominantTier)]})]})}function g(t){return`${t.toFixed(1)}%`}function X(t){return t>=80?"ov-bar-green":t>=60?"ov-bar-teal":t>=40?"ov-bar-yellow":t>=20?"ov-bar-orange":"ov-bar-red"}const Y=({perf:t})=>{const{cache_hit_pct:a}=t,s=a.samples&&a.samples.length>0?a.samples:[];return e.jsxs(f,{className:"electric-hover",children:[e.jsx(C,{children:e.jsx(S,{children:"Cache Hit-Rate Trend"})}),e.jsxs(N,{children:[e.jsxs("div",{className:"mb-3 flex gap-4 text-sm text-muted-foreground",children:[e.jsxs("div",{children:["Avg: ",e.jsx("strong",{className:"text-foreground",children:g(a.avg)})]}),e.jsxs("div",{children:["Latest: ",e.jsx("strong",{className:"text-foreground",children:g(a.latest)})]}),e.jsxs("div",{children:["Samples: ",e.jsx("strong",{className:"text-foreground",children:a.n})]})]}),s.length>0?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"ov-sparkline",role:"img","aria-label":`Cache hit rate trend: ${a.n} samples, average ${g(a.avg)}, latest ${g(a.latest)}`,children:s.map((l,r)=>{const i=Math.max(2,Math.round(l.pct*.6)),o=X(l.pct),c=r===s.length-1;return e.jsx("div",{className:`ov-sparkline-bar ${o}${c?" ov-bar-latest":""}`,style:{height:`${i}px`},title:`${g(l.pct)} at ${new Date(l.ts).toLocaleTimeString()}`},r)})}),e.jsxs("div",{className:"mt-1 text-xs text-muted-foreground",children:[s.length," sample",s.length!==1?"s":""," over"," ",s.length>1?`${Math.round((s[s.length-1].ts-s[0].ts)/1e3)}s`:"latest turn"]})]}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No cache-hit samples yet"})]})]})};function T(t){return`${(t*100).toFixed(1)}%`}function Z(t){const a=t*100;return a>=80?"ov-bar-green":a>=60?"ov-bar-teal":a>=40?"ov-bar-yellow":a>=20?"ov-bar-orange":"ov-bar-red"}const A={improving:"Improving",stable:"Stable",degrading:"Degrading"},ee=({data:t})=>{const{turns:a,avgRatio:s,trend:l}=t;return e.jsxs(f,{className:"electric-hover",children:[e.jsx(C,{children:e.jsx(S,{children:"Per-Turn Stable Prefix"})}),e.jsxs(N,{children:[e.jsxs("div",{className:"mb-3 flex gap-4 text-sm text-muted-foreground",children:[e.jsxs("div",{children:["Avg:"," ",e.jsx("strong",{className:"text-foreground",children:T(s)})]}),e.jsxs("div",{children:["Trend:"," ",e.jsx("strong",{className:"text-foreground",children:A[l]})]}),e.jsxs("div",{children:["Turns: ",e.jsx("strong",{className:"text-foreground",children:a.length})]})]}),a.length>0?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"ov-sparkline",role:"img","aria-label":`Per-turn stable-prefix ratio trend: ${a.length} turns, average ${T(s)}, trend ${A[l]}`,children:a.map((r,i)=>{const o=Math.max(2,Math.round(r.ratio*60)),c=Z(r.ratio),p=i===a.length-1;return e.jsx("div",{className:`ov-sparkline-bar ${c}${p?" ov-bar-latest":""}`,style:{height:`${o}px`},title:`Turn ${r.turnIndex}: ${T(r.ratio)} (${r.stablePrefix}/${r.totalMessages} msg) @ ${new Date(r.timestamp).toLocaleTimeString()}`},i)})}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:"Shared prompt-cache prefix ratio per turn — higher is better."})]}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No stable-prefix samples yet"})]})]})};function oe(){const[t,a]=h.useState(!1),{data:s,loading:l,error:r}=m(h.useCallback(()=>I(),[]),{pollInterval:5e3}),{data:i,loading:o,error:c}=m(h.useCallback(()=>k(),[]),{pollInterval:15e3}),{data:p,loading:E,error:y}=m(h.useCallback(()=>w({minutes:30}),[]),{pollInterval:15e3}),{data:P,loading:M}=m(h.useCallback(()=>z(50),[]),{pollInterval:15e3}),{data:d,loading:H,error:$}=m(h.useCallback(()=>D(),[]),{pollInterval:5e3});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{children:[e.jsx(B,{type:"button",variant:"outline",onClick:()=>a(!t),children:t?"Hide Guidance":"Show Cache-Friendly Prompt Ordering Guidance"}),t&&e.jsxs(f,{className:"mt-3",children:[e.jsx(C,{children:e.jsx(S,{children:"Cache-Friendly Prompt Ordering"})}),e.jsxs(N,{children:[e.jsxs("ul",{className:"list-disc space-y-1 pl-5",children:[e.jsx("li",{children:"Keep system prompts and stable context at the top of your conversation — the provider caches the leading prefix."}),e.jsxs("li",{children:["Tool results and volatile content are automatically moved to the tail by message separation (",e.jsx("code",{children:"MEGACOMPACT_MESSAGE_SEPARATION"}),") so they don't invalidate the cache prefix."]}),e.jsxs("li",{children:["Cache striping (",e.jsx("code",{children:"MEGACOMPACT_CACHE_STRIPING"}),") further orders stable context by a stability score so the most durable chunks lead."]}),e.jsx("li",{children:"Avoid inserting new instructions mid-conversation — prepend them instead."})]}),d&&e.jsxs("div",{className:"mt-2 text-sm",children:[e.jsx("strong",{children:"Status:"})," ",d.config.auto?"Auto-compaction Enabled":"Auto-compaction Disabled"," |"," ","fast-gate ",d.config.fastGatePct,"% / threshold"," ",d.config.thresholdTokens.toLocaleString()," tokens"]})]})]})]}),e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Provider Prompt Cache"}),l?e.jsx("p",{className:"tab-stub",children:"Loading..."}):r?e.jsxs("p",{className:"tab-stub",children:["Provider cache unavailable: ",r.message]}):s?e.jsx(U,{hitPct:s.cache.avgHitPct,turnCount:s.cache.turnCount,totalCacheRead:s.cache.totalCacheRead,totalCacheWrite:s.cache.totalCacheWrite,totalInput:s.cache.totalInput,cacheReadSaved:s.savings?.cacheReadSaved??null,cacheWriteCost:s.savings?.cacheWriteCost??null,netSaved:s.savings?.netSaved??null,modelLabel:s.savings?.model??null,firstTurnAt:s.cache.firstTurnAt,latestTurnAt:s.cache.latestTurnAt,byModel:s.cache.byModel??[]}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Cache Stripe Distribution"}),o?e.jsx("p",{className:"tab-stub",children:"Loading..."}):c?e.jsxs("p",{className:"tab-stub",children:["Cache stripe data unavailable: ",c.message]}):i?e.jsx(Q,{data:i}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Cache Hit-Rate Trend"}),E?e.jsx("p",{className:"tab-stub",children:"Loading..."}):y?e.jsxs("p",{className:"tab-stub",children:["Hit-rate trend unavailable: ",y.message]}):p?e.jsx(Y,{perf:p}):null,P&&!M?e.jsxs("div",{children:[e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Per-Turn Stable Prefix"}),e.jsx(ee,{data:P})]}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Mega-Compact Dedup Cache"}),H?e.jsx("p",{className:"tab-stub",children:"Loading..."}):$?e.jsxs("p",{className:"tab-stub",children:["Snapshot unavailable: ",$.message]}):d&&(()=>{const{cacheHits:b,compacts:R,timeSaved:j}=d;return e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(W,{cacheHitsSession:b.session,cacheHitsTotal:b.total,tokensSavedSession:b.sessionTokensSaved,tokensSavedTotal:b.totalTokensSaved,compactionsSession:R.session,compactionsTotal:R.total}),e.jsx(F,{compactSessionSec:j.compact.sessionSec,compactTotalSec:j.compact.totalSec,cacheHitSessionSec:j.cacheHit.sessionSec,cacheHitTotalSec:j.cacheHit.totalSec})]})})()]})}export{oe as default};
|
|
2
|
-
//# sourceMappingURL=CacheTab-CXU1_9lR.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CacheTab-CXU1_9lR.js","sources":["../../src/components/ProviderCacheCard.tsx","../../src/components/StripeDistributionCard.tsx","../../src/components/CacheHitRateTrendCard.tsx","../../src/components/PrefixStabilityCard.tsx","../../src/tabs/CacheTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/ProviderCacheCard.tsx — Provider prompt cache stats card.\n *\n * Displays lifetime provider prompt cache hit-rate aggregates + estimated\n * dollar savings from the /api/provider-cache endpoint.\n * Reuses the `ov-stat-row` / `StatRow` pattern from CacheHitsCard.tsx.\n *\n * B.2 spec: Cache Hit Rate with color classes, humanized token counts,\n * priced $ rows with green/red net, model name, locale date + relative time.\n */\n\nimport type React from \"react\";\nimport {\n\tfmtTokens,\n\tfmtDollars,\n\tfmtDate,\n\tfmtRelativeTime,\n} from \"../utils/format\";\nimport { lookupModelInputRate } from \"@pricing\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Per-model breakdown type (mirrors the API contract).\n// ---------------------------------------------------------------------------\n\nexport interface ByModelEntry {\n\tmodel: string;\n\thitPct: number;\n\ttotalCacheRead: number;\n\ttotalCacheWrite: number;\n\tsampleCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Flattened props — destructured from ProviderCacheResponse in CacheTab.\n// ---------------------------------------------------------------------------\n\nexport interface ProviderCacheCardProps {\n\t/** Cache hit rate percentage (0–100). */\n\thitPct: number;\n\t/** Number of turns with cache_hit_pct samples. */\n\tturnCount: number;\n\t/** Total cache-read tokens across all sampled turns. */\n\ttotalCacheRead: number;\n\t/** Total cache-write tokens across all sampled turns. */\n\ttotalCacheWrite: number;\n\t/** Total input tokens across all sampled turns. */\n\ttotalInput: number;\n\t/** $ saved from cache reads (null when unpriced). */\n\tcacheReadSaved: number | null;\n\t/** $ spent on cache writes (null when unpriced). */\n\tcacheWriteCost: number | null;\n\t/** netSaved = cacheReadSaved - cacheWriteCost (null when unpriced). */\n\tnetSaved: number | null;\n\t/** Model display name (modelName || modelId, or null). */\n\tmodelLabel: string | null;\n\t/** ISO timestamp of first recorded turn. */\n\tfirstTurnAt: string | null;\n\t/** ISO timestamp of latest recorded turn. */\n\tlatestTurnAt: string | null;\n\t/** Per-model breakdown (F4). */\n\tbyModel: ByModelEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Hit-rate color: green ≥80, yellow ≥50, red <50. */\nfunction hitPctClass(pct: number): string {\n\tif (pct >= 80) return \"ov-stat-value-green\";\n\tif (pct >= 50) return \"ov-stat-value-yellow\";\n\treturn \"ov-stat-value-red\";\n}\n\n/** Net-saved color: green positive, red negative. */\nfunction netClass(v: number | null): string {\n\tif (v == null) return \"\";\n\treturn v >= 0 ? \"ov-stat-value-green\" : \"ov-stat-value-red\";\n}\n\nfunction StatRow({\n\tlabel,\n\tvalue,\n\tclassName,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\tclassName?: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t<span className=\"text-xs text-muted-foreground\" title={title}>\n\t\t\t\t{label}\n\t\t\t</span>\n\t\t\t<span className={`font-semibold${className ? ` ${className}` : \"\"}`}>\n\t\t\t\t{value}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\nexport function ProviderCacheCard(\n\tprops: ProviderCacheCardProps,\n): React.ReactElement {\n\treturn (\n\t\t<Card className=\"electric-hover\">\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Provider Prompt Cache</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t{/* --- Hit Rate + Turns --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hit Rate\"\n\t\t\t\tvalue={`${props.hitPct.toFixed(1)}%`}\n\t\t\t\tclassName={hitPctClass(props.hitPct)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Turns Recorded\" value={String(props.turnCount)} />\n\n\t\t\t{/* --- Token counts --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Read Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheRead)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Write Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheWrite)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Input Tokens\" value={fmtTokens(props.totalInput)} />\n\n\t\t\t{/* --- Dollar rows (null when unpriced) --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"$ Saved (reads)\"\n\t\t\t\tvalue={fmtDollars(props.cacheReadSaved)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Write Investment\"\n\t\t\t\tvalue={fmtDollars(props.cacheWriteCost)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Net Saved\"\n\t\t\t\tvalue={fmtDollars(props.netSaved)}\n\t\t\t\tclassName={netClass(props.netSaved)}\n\t\t\t/>\n\n\t\t\t{/* --- Model + timestamps --- */}\n\t\t\t<StatRow label=\"Model\" value={props.modelLabel ?? \"\\u2014\"} />\n\t\t\t<StatRow label=\"Tracked Since\" value={fmtDate(props.firstTurnAt)} />\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Last Updated\"\n\t\t\t\tvalue={fmtRelativeTime(props.latestTurnAt)}\n\t\t\t/>\n\n\t\t\t{/* --- Per-model breakdown (F4) --- */}\n\t\t\t{props.byModel.length > 0 && (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"mt-2 text-xs font-semibold text-muted-foreground\">\n\t\t\t\t\t\tPer Model\n\t\t\t\t\t</div>\n\t\t\t\t\t{props.byModel.map((m) => {\n\t\t\t\t\t\tconst rate = lookupModelInputRate(m.model);\n\t\t\t\t\t\tconst cr =\n\t\t\t\t\t\t\trate != null ? (m.totalCacheRead / 1_000_000) * rate : null;\n\t\t\t\t\t\tconst cw =\n\t\t\t\t\t\t\trate != null ? (m.totalCacheWrite / 1_000_000) * rate : null;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={m.model} className=\"mt-2\">\n\t\t\t\t\t\t\t\t<StatRow label=\"Model\" value={m.model} />\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Hit Rate\"\n\t\t\t\t\t\t\t\t\tvalue={`${m.hitPct.toFixed(1)}%`}\n\t\t\t\t\t\t\t\t\tclassName={hitPctClass(m.hitPct)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Cache Read\"\n\t\t\t\t\t\t\t\t\tvalue={fmtTokens(m.totalCacheRead)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Cache Write\"\n\t\t\t\t\t\t\t\t\tvalue={fmtTokens(m.totalCacheWrite)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow label=\"Samples\" value={String(m.sampleCount)} />\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"$ Saved (reads)\"\n\t\t\t\t\t\t\t\t\tvalue={cr != null ? fmtDollars(cr) : \"\\u2014\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Write Investment\"\n\t\t\t\t\t\t\t\t\tvalue={cw != null ? fmtDollars(cw) : \"\\u2014\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</>\n\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/components/StripeDistributionCard.tsx — Cache stripe\n * distribution visualization (A3, PLAN_V2 Phase 4).\n *\n * Renders a horizontal bar chart of stripe buckets (0–4) with stability\n * indicators, plus a health-score summary box.\n *\n * Uses only CSS-based bar rendering — no external charting dependencies.\n */\n\nimport type React from \"react\";\nimport type { CacheStripesResponse } from \"@contracts\";\nimport { fmtPctFromFraction } from \"../utils/format\";\n\nconst STRIPE_COLORS = [\n\t\"ov-bar-green\", // 0 permanent\n\t\"ov-bar-blue\", // 1 epoch\n\t\"ov-bar-teal\", // 2 topic\n\t\"ov-bar-yellow\", // 3 thread\n\t\"ov-bar-gray\", // 4 volatile\n];\n\nconst STRIPE_LABELS = [\n\t\"Permanent\",\n\t\"Epoch\",\n\t\"Topic\",\n\t\"Thread\",\n\t\"Volatile\",\n];\n\nconst HEALTH_LABEL_COLOR: Record<string, string> = {\n\tgood: \"ov-text-green\",\n\tfair: \"ov-text-yellow\",\n\tdegraded: \"ov-text-orange\",\n\tpoor: \"ov-text-red\",\n};\n\nexport interface StripeDistributionCardProps {\n\tdata: CacheStripesResponse;\n}\n\nexport function StripeDistributionCard({\n\tdata,\n}: StripeDistributionCardProps): React.ReactElement {\n\tconst maxCount = Math.max(...data.buckets.map((b) => b.count), 1);\n\tconst healthColor = HEALTH_LABEL_COLOR[data.health.label] ?? \"ov-text-gray\";\n\n\treturn (\n\t\t<div className=\"ov-card\">\n\t\t\t<h3 className=\"ov-card-title\">Cache Stripe Distribution</h3>\n\n\t\t\t{/* Health score summary */}\n\t\t\t<div className=\"ov-stripe-health-summary\">\n\t\t\t\t<span className={`ov-stripe-health-badge ${healthColor}`}>\n\t\t\t\t\t{data.health.label}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"ov-stripe-health-score\">\n\t\t\t\t\t{fmtPctFromFraction(data.health.score)}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"ov-stripe-churn\">\n\t\t\t\t\tChurn: {fmtPctFromFraction(data.health.churnRate)}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"ov-stripe-total\">\n\t\t\t\t\t{data.totalChunks} total chunks\n\t\t\t\t</span>\n\t\t\t</div>\n\n\t\t\t{/* Bar chart — one row per stripe */}\n\t\t\t<div className=\"ov-stripe-bars\">\n\t\t\t\t{data.buckets.map((bucket) => {\n\t\t\t\t\tconst pct =\n\t\t\t\t\t\tmaxCount > 0\n\t\t\t\t\t\t\t? Math.max(0, Math.min(100, (bucket.count / maxCount) * 100))\n\t\t\t\t\t\t\t: 0;\n\t\t\t\t\tconst colorClass =\n\t\t\t\t\t\tSTRIPE_COLORS[bucket.stripe] ?? \"ov-bar-gray\";\n\t\t\t\t\tconst label =\n\t\t\t\t\t\tbucket.label || (STRIPE_LABELS[bucket.stripe] ?? `Stripe ${bucket.stripe}`);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div key={bucket.stripe} className=\"ov-stripe-row\">\n\t\t\t\t\t\t\t<span className=\"ov-stripe-label\" title={label}>\n\t\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<div className=\"ov-bar-track\">\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName={`ov-bar-fill ${colorClass}`}\n\t\t\t\t\t\t\t\t\tstyle={{ width: `${pct}%` }}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"ov-stripe-count\">{bucket.count}</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"ov-stripe-stability\"\n\t\t\t\t\t\t\t\ttitle={`min ${fmtPctFromFraction(bucket.minStability)} / max ${fmtPctFromFraction(bucket.maxStability)}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{fmtPctFromFraction(bucket.avgStability)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\n\t\t\t{/* Epoch info */}\n\t\t\t<div className=\"ov-stripe-footer\">\n\t\t\t\tEpoch: {data.epochId ?? \"—\"}\n\t\t\t\t·\n\t\t\t\tDominant tier: {fmtPctFromFraction(data.health.dominantTier)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/CacheHitRateTrendCard.tsx -- Cache hit-rate\n * time-series trend chart (A3, PLAN_V2 Phase 4).\n *\n * Renders a sparkline view of cache hit rate from the /api/perf endpoint using\n * real cache_hit_pct samples with timestamps. Each bar represents one sample.\n * Shows the latest, average, and sample count.\n *\n * Uses .ov-sparkline / .ov-sparkline-bar CSS classes from cache.css.\n * No external charting dependencies.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse, CacheHitSample } from \"@contracts\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\n\nexport interface CacheHitRateTrendCardProps {\n\tperf: PerfResponse;\n}\n\nfunction fmtPct(n: number): string {\n\treturn `${n.toFixed(1)}%`;\n}\n\n/**\n * Map a percentage (0–100) to a CSS color class:\n * >= 80: green (healthy)\n * >= 60: teal (fair)\n * >= 40: yellow (degraded)\n * >= 20: orange (poor)\n * < 20: red (critical)\n */\nfunction pctColorClass(pct: number): string {\n\tif (pct >= 80) return \"ov-bar-green\";\n\tif (pct >= 60) return \"ov-bar-teal\";\n\tif (pct >= 40) return \"ov-bar-yellow\";\n\tif (pct >= 20) return \"ov-bar-orange\";\n\treturn \"ov-bar-red\";\n}\n\nexport const CacheHitRateTrendCard: React.FC<CacheHitRateTrendCardProps> = ({\n\tperf,\n}) => {\n\tconst { cache_hit_pct: c } = perf;\n\tconst samples: CacheHitSample[] =\n\t\tc.samples && c.samples.length > 0 ? c.samples : [];\n\n\treturn (\n\t\t<Card className=\"electric-hover\">\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Cache Hit-Rate Trend</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t{/* Metric summary */}\n\t\t\t<div className=\"mb-3 flex gap-4 text-sm text-muted-foreground\">\n\t\t\t\t<div>\n\t\t\t\t\tAvg: <strong className=\"text-foreground\">{fmtPct(c.avg)}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\tLatest: <strong className=\"text-foreground\">{fmtPct(c.latest)}</strong>\n\t\t\t\t</div>\n\t\t\t\t<div>\n\t\t\t\t\tSamples: <strong className=\"text-foreground\">{c.n}</strong>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Sparkline bars from real samples */}\n\t\t\t{samples.length > 0 ? (\n\t\t\t\t<>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"ov-sparkline\"\n\t\t\t\t\t\trole=\"img\"\n\t\t\t\t\t\taria-label={`Cache hit rate trend: ${c.n} samples, average ${fmtPct(c.avg)}, latest ${fmtPct(c.latest)}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t{samples.map((s, i) => {\n\t\t\t\t\t\t\tconst barH = Math.max(2, Math.round(s.pct * 0.6));\n\t\t\t\t\t\t\tconst colorClass = pctColorClass(s.pct);\n\t\t\t\t\t\t\tconst isLatest = i === samples.length - 1;\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\t\t\tclassName={`ov-sparkline-bar ${colorClass}${isLatest ? \" ov-bar-latest\" : \"\"}`}\n\t\t\t\t\t\t\t\t\tstyle={{ height: `${barH}px` }}\n\t\t\t\t\t\t\t\t\ttitle={`${fmtPct(s.pct)} at ${new Date(s.ts).toLocaleTimeString()}`}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t{samples.length} sample{samples.length !== 1 ? \"s\" : \"\"} over{\" \"}\n\t\t\t\t\t\t{samples.length > 1\n\t\t\t\t\t\t\t? `${Math.round(\n\t\t\t\t\t\t\t\t\t(samples[samples.length - 1].ts - samples[0].ts) / 1000,\n\t\t\t\t\t\t\t\t)}s`\n\t\t\t\t\t\t\t: \"latest turn\"}\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<div className=\"text-sm text-muted-foreground\">No cache-hit samples yet</div>\n\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n};\n","/**\n * dashboard-client/src/components/PrefixStabilityCard.tsx -- Prompt-cache per-turn\n * stable-prefix ratio trend (PC-C).\n *\n * Renders a sparkline of the per-turn stable-prefix ratio (stablePrefix /\n * totalMessages) read from /api/prefix-stability, with the average ratio and a\n * three-point trend badge (\"improving\" | \"stable\" | \"degrading\"). Each bar is one\n * turn. Uses the same .ov-sparkline / .ov-bar-* CSS classes as CacheHitRateTrendCard.\n * No external charting dependencies.\n */\n\nimport type React from \"react\";\nimport type { PrefixStabilityResponse } from \"@contracts\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\n\nexport interface PrefixStabilityCardProps {\n\tdata: PrefixStabilityResponse;\n}\n\nfunction fmtRatio(n: number): string {\n\treturn `${(n * 100).toFixed(1)}%`;\n}\n\n/** Map a ratio (0–1) to a CSS color class (mirrors pctColorClass thresholds). */\nfunction ratioColorClass(ratio: number): string {\n\tconst pct = ratio * 100;\n\tif (pct >= 80) return \"ov-bar-green\";\n\tif (pct >= 60) return \"ov-bar-teal\";\n\tif (pct >= 40) return \"ov-bar-yellow\";\n\tif (pct >= 20) return \"ov-bar-orange\";\n\treturn \"ov-bar-red\";\n}\n\nconst TREND_LABEL: Record<PrefixStabilityResponse[\"trend\"], string> = {\n\timproving: \"Improving\",\n\tstable: \"Stable\",\n\tdegrading: \"Degrading\",\n};\n\nexport const PrefixStabilityCard: React.FC<PrefixStabilityCardProps> = ({\n\tdata,\n}) => {\n\tconst { turns, avgRatio, trend } = data;\n\n\treturn (\n\t\t<Card className=\"electric-hover\">\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Per-Turn Stable Prefix</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<div className=\"mb-3 flex gap-4 text-sm text-muted-foreground\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\tAvg:{\" \"}\n\t\t\t\t\t\t<strong className=\"text-foreground\">{fmtRatio(avgRatio)}</strong>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\tTrend:{\" \"}\n\t\t\t\t\t\t<strong className=\"text-foreground\">{TREND_LABEL[trend]}</strong>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\tTurns: <strong className=\"text-foreground\">{turns.length}</strong>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{turns.length > 0 ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"ov-sparkline\"\n\t\t\t\t\t\t\trole=\"img\"\n\t\t\t\t\t\t\taria-label={`Per-turn stable-prefix ratio trend: ${turns.length} turns, average ${fmtRatio(avgRatio)}, trend ${TREND_LABEL[trend]}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{turns.map((t, i) => {\n\t\t\t\t\t\t\t\tconst barH = Math.max(2, Math.round(t.ratio * 60));\n\t\t\t\t\t\t\t\tconst colorClass = ratioColorClass(t.ratio);\n\t\t\t\t\t\t\t\tconst isLatest = i === turns.length - 1;\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\t\t\t\tclassName={`ov-sparkline-bar ${colorClass}${isLatest ? \" ov-bar-latest\" : \"\"}`}\n\t\t\t\t\t\t\t\t\t\tstyle={{ height: `${barH}px` }}\n\t\t\t\t\t\t\t\t\t\ttitle={`Turn ${t.turnIndex}: ${fmtRatio(t.ratio)} (${t.stablePrefix}/${t.totalMessages} msg) @ ${new Date(t.timestamp).toLocaleTimeString()}`}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\tShared prompt-cache prefix ratio per turn — higher is better.\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\tNo stable-prefix samples yet\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n};\n","/**\n * dashboard-client/src/tabs/CacheTab.tsx -- Cache tab (B.3 + A3).\n *\n * Sections:\n * 1. Provider Prompt Cache -> ProviderCacheCard (/api/provider-cache)\n * 2. Cache Stripe Distribution -> StripeDistributionCard (/api/cache-stripes)\n * 3. Cache Hit-Rate Trend -> CacheHitRateTrendCard (/api/perf)\n * 4. Mega-Compact Dedup Cache -> CacheHitsCard + TimeSavedCard (/api/snapshot)\n *\n * Each section fetches independently; failures in one do not affect others.\n * 5s polling on provider cache and activity snapshot; 15s on stripes + trend.\n */\n\nimport { useState, useCallback } from \"react\";\nimport type {\n\tSnapshotResponse,\n\tProviderCacheResponse,\n\tCacheStripesResponse,\n\tPerfResponse,\n\tPrefixStabilityResponse,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchSnapshot,\n\tfetchProviderCache,\n\tfetchCacheStripes,\n\tfetchPrefixStability,\n\tfetchPerf,\n} from \"../api/client\";\nimport { CacheHitsCard } from \"../components/CacheHitsCard\";\nimport { TimeSavedCard } from \"../components/TimeSavedCard\";\nimport { ProviderCacheCard } from \"../components/ProviderCacheCard\";\nimport { StripeDistributionCard } from \"../components/StripeDistributionCard\";\nimport { CacheHitRateTrendCard } from \"../components/CacheHitRateTrendCard\";\nimport { PrefixStabilityCard } from \"../components/PrefixStabilityCard\";\nimport { Button } from \"../components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\n\nexport default function CacheTab(): React.ReactElement {\n\tconst [infoExpanded, setInfoExpanded] = useState(false);\n\t/* --- Provider prompt cache (5s poll) --- */\n\tconst {\n\t\tdata: providerCache,\n\t\tloading: pcLoading,\n\t\terror: pcError,\n\t} = useApi<ProviderCacheResponse>(\n\t\tuseCallback(() => fetchProviderCache(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\t/* --- Cache stripe distribution (15s poll) --- */\n\tconst {\n\t\tdata: stripes,\n\t\tloading: stripesLoading,\n\t\terror: stripesError,\n\t} = useApi<CacheStripesResponse>(\n\t\tuseCallback(() => fetchCacheStripes(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\t/* --- Cache hit-rate trend from perf samples (15s poll) --- */\n\tconst {\n\t\tdata: perf,\n\t\tloading: perfLoading,\n\t\terror: perfError,\n\t} = useApi<PerfResponse>(\n\t\tuseCallback(() => fetchPerf({ minutes: 30 }), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\t/* --- Per-turn stable-prefix ratio trend (PC-C, 15s poll) --- */\n\tconst {\n\t\tdata: prefixStability,\n\t\tloading: psLoading,\n\t} = useApi<PrefixStabilityResponse>(\n\t\tuseCallback(() => fetchPrefixStability(50), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\t/* --- Dedup cache snapshot (5s poll) --- */\n\tconst {\n\t\tdata: snapshot,\n\t\tloading: snapLoading,\n\t\terror: snapError,\n\t} = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div>\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tonClick={() => setInfoExpanded(!infoExpanded)}\n\t\t\t\t>\n\t\t\t\t\t{infoExpanded ? \"Hide Guidance\" : \"Show Cache-Friendly Prompt Ordering Guidance\"}\n\t\t\t\t</Button>\n\t\t\t\t{infoExpanded && (\n\t\t\t\t\t<Card className=\"mt-3\">\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Cache-Friendly Prompt Ordering</CardTitle>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t\t<ul className=\"list-disc space-y-1 pl-5\">\n\t\t\t\t\t\t\t\t<li>Keep system prompts and stable context at the top of your conversation — the provider caches the leading prefix.</li>\n\t\t\t\t\t\t\t\t<li>Tool results and volatile content are automatically moved to the tail by message separation (<code>MEGACOMPACT_MESSAGE_SEPARATION</code>) so they don't invalidate the cache prefix.</li>\n\t\t\t\t\t\t\t\t<li>Cache striping (<code>MEGACOMPACT_CACHE_STRIPING</code>) further orders stable context by a stability score so the most durable chunks lead.</li>\n\t\t\t\t\t\t\t\t<li>Avoid inserting new instructions mid-conversation — prepend them instead.</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t{snapshot && (\n\t\t\t\t\t\t\t\t<div className=\"mt-2 text-sm\">\n\t\t\t\t\t\t\t\t\t<strong>Status:</strong>{\" \"}\n\t\t\t\t\t\t\t\t\t{snapshot.config.auto ? \"Auto-compaction Enabled\" : \"Auto-compaction Disabled\"} |{\" \"}\n\t\t\t\t\t\t\t\t\tfast-gate {snapshot.config.fastGatePct}% / threshold{\" \"}\n\t\t\t\t\t\t\t\t\t{snapshot.config.thresholdTokens.toLocaleString()} tokens\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</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{/* ==============================================================\n\t\t\t Section 1 -- Provider Prompt Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Provider Prompt Cache</h2>\n\t\t\t{pcLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading...</p>\n\t\t\t) : pcError ? (\n\t\t\t\t<p className=\"tab-stub\">\n\t\t\t\t\tProvider cache unavailable: {pcError.message}\n\t\t\t\t</p>\n\t\t\t) : providerCache ? (\n\t\t\t\t<ProviderCacheCard\n\t\t\t\t\thitPct={providerCache.cache.avgHitPct}\n\t\t\t\t\tturnCount={providerCache.cache.turnCount}\n\t\t\t\t\ttotalCacheRead={providerCache.cache.totalCacheRead}\n\t\t\t\t\ttotalCacheWrite={providerCache.cache.totalCacheWrite}\n\t\t\t\t\ttotalInput={providerCache.cache.totalInput}\n\t\t\t\t\tcacheReadSaved={providerCache.savings?.cacheReadSaved ?? null}\n\t\t\t\t\tcacheWriteCost={providerCache.savings?.cacheWriteCost ?? null}\n\t\t\t\t\tnetSaved={providerCache.savings?.netSaved ?? null}\n\t\t\t\t\tmodelLabel={providerCache.savings?.model ?? null}\n\t\t\t\t\tfirstTurnAt={providerCache.cache.firstTurnAt}\n\t\t\t\t\tlatestTurnAt={providerCache.cache.latestTurnAt}\n\t\t\t\t\tbyModel={providerCache.cache.byModel ?? []}\n\t\t\t\t/>\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 2 -- Cache Stripe Distribution\n\t\t\t ============================================================== */}\n\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Cache Stripe Distribution</h2>\n\t\t\t{stripesLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading...</p>\n\t\t\t) : stripesError ? (\n\t\t\t\t<p className=\"tab-stub\">\n\t\t\t\t\tCache stripe data unavailable: {stripesError.message}\n\t\t\t\t</p>\n\t\t\t) : stripes ? (\n\t\t\t\t<StripeDistributionCard data={stripes} />\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 3 -- Cache Hit-Rate Trend\n\t\t\t ============================================================== */}\n\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Cache Hit-Rate Trend</h2>\n\t\t\t{perfLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading...</p>\n\t\t\t) : perfError ? (\n\t\t\t\t<p className=\"tab-stub\">\n\t\t\t\t\tHit-rate trend unavailable: {perfError.message}\n\t\t\t\t</p>\n\t\t\t) : perf ? (\n\t\t\t\t<CacheHitRateTrendCard perf={perf} />\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 3b -- Per-Turn Stable Prefix (PC-C)\n\t\t\t Omitted when flag-off: the endpoint 404s, so psError is set and\n\t\t\t this section stays empty. No sample/time-series data, only\n\t\t\t per-turn prefix ratios read from the local events log.\n\t\t\t ============================================================== */}\n\t\t\t{prefixStability && !psLoading ? (\n\t\t\t\t<div>\n\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">\n\t\t\t\t\t\tPer-Turn Stable Prefix\n\t\t\t\t\t</h2>\n\t\t\t\t\t<PrefixStabilityCard data={prefixStability} />\n\t\t\t\t</div>\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 4 -- Mega-Compact Dedup Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Mega-Compact Dedup Cache</h2>\n\t\t\t{snapLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading...</p>\n\t\t\t) : snapError ? (\n\t\t\t\t<p className=\"tab-stub\">Snapshot unavailable: {snapError.message}</p>\n\t\t\t) : (\n\t\t\t\tsnapshot &&\n\t\t\t\t(() => {\n\t\t\t\t\tconst { cacheHits, compacts, timeSaved } = snapshot;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n\t\t\t\t\t\t\t<CacheHitsCard\n\t\t\t\t\t\t\t\tcacheHitsSession={cacheHits.session}\n\t\t\t\t\t\t\t\tcacheHitsTotal={cacheHits.total}\n\t\t\t\t\t\t\t\ttokensSavedSession={cacheHits.sessionTokensSaved}\n\t\t\t\t\t\t\t\ttokensSavedTotal={cacheHits.totalTokensSaved}\n\t\t\t\t\t\t\t\tcompactionsSession={compacts.session}\n\t\t\t\t\t\t\t\tcompactionsTotal={compacts.total}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<TimeSavedCard\n\t\t\t\t\t\t\t\tcompactSessionSec={timeSaved.compact.sessionSec}\n\t\t\t\t\t\t\t\tcompactTotalSec={timeSaved.compact.totalSec}\n\t\t\t\t\t\t\t\tcacheHitSessionSec={timeSaved.cacheHit.sessionSec}\n\t\t\t\t\t\t\t\tcacheHitTotalSec={timeSaved.cacheHit.totalSec}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})()\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["hitPctClass","pct","netClass","v","StatRow","label","value","className","title","jsxs","jsx","ProviderCacheCard","props","Card","CardHeader","CardTitle","CardContent","fmtTokens","fmtDollars","fmtDate","fmtRelativeTime","Fragment","m","rate","lookupModelInputRate","cr","cw","STRIPE_COLORS","STRIPE_LABELS","HEALTH_LABEL_COLOR","StripeDistributionCard","data","maxCount","b","healthColor","fmtPctFromFraction","bucket","colorClass","fmtPct","n","pctColorClass","CacheHitRateTrendCard","perf","c","samples","s","i","barH","isLatest","fmtRatio","ratioColorClass","ratio","TREND_LABEL","PrefixStabilityCard","turns","avgRatio","trend","t","CacheTab","infoExpanded","setInfoExpanded","useState","providerCache","pcLoading","pcError","useApi","useCallback","fetchProviderCache","stripes","stripesLoading","stripesError","fetchCacheStripes","perfLoading","perfError","fetchPerf","prefixStability","psLoading","fetchPrefixStability","snapshot","snapLoading","snapError","fetchSnapshot","Button","cacheHits","compacts","timeSaved","CacheHitsCard","TimeSavedCard"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MetricsTab-CEKajY38.js","sources":["../../src/components/PerfChart.tsx","../../src/components/PerfCards.tsx","../../src/components/ModelBadge.tsx","../../src/components/RagDashboard.tsx","../../src/tabs/MetricsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/PerfChart.tsx — perf stat chart.\n *\n * The /api/perf endpoint returns rolling-window aggregates (not time-series),\n * so this renders a stat-card \"chart\": p50/p95 latency bars, TPS, cache hit %,\n * db recompute + disk write. Each metric shows a normalized bar + numeric.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\n\nexport interface PerfChartProps {\n\tperf: PerfResponse;\n}\n\nfunction fmtMs(n: number): string {\n\treturn n >= 1000 ? `${(n / 1000).toFixed(1)}s` : `${n.toFixed(0)}ms`;\n}\n\ninterface BarProps {\n\t/** Current value. */\n\tvalue: number;\n\t/** Value that represents 100% bar fill (the \"max\" reference). */\n\tmax: number;\n\t/** Color class. */\n\tcolorClass: string;\n}\n\nfunction Bar({ value, max, colorClass }: BarProps): React.ReactElement {\n\tconst pct = max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0;\n\treturn (\n\t\t<div className=\"perf-bar-track\">\n\t\t\t<div\n\t\t\t\tclassName={`perf-bar-fill ${colorClass}`}\n\t\t\t\tstyle={{ width: `${pct}%` }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport function PerfChart({ perf }: PerfChartProps): React.ReactElement {\n\t// Reference maxes: p95 is the upper bound for latency bars; rates scale to a\n\t// reasonable reference (tok/s) so bars are visually meaningful even with\n\t// small samples. cache_hit_pct is already 0-100.\n\tconst latencyMax = Math.max(\n\t\tperf.turn_latency_ms.p95,\n\t\tperf.provider_latency_ms.p95,\n\t\t1,\n\t);\n\tconst tpsRef = Math.max(perf.tps.avg, 1);\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader className=\"flex-row items-center justify-between space-y-0\">\n\t\t\t\t<CardTitle>Performance</CardTitle>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{perf.windowMinutes}min window · {perf.sampleCount} samples\n\t\t\t\t</span>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t<div className=\"perf-grid\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Turn latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.turn_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-blue\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Provider latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.provider_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-purple\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Tokens/sec (avg)</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.tps.avg.toFixed(1)}</span>\n\t\t\t\t\t<Bar value={perf.tps.avg} max={tpsRef} colorClass=\"perf-green\" />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Cache hit (avg / latest)</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.cache_hit_pct.avg.toFixed(1)}% /{\" \"}\n\t\t\t\t\t\t{perf.cache_hit_pct.latest.toFixed(1)}%\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.cache_hit_pct.avg}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tcolorClass=\"perf-green\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">DB recompute p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.db_recompute_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.db_recompute_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Disk write p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.disk_write_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.disk_write_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"perf-resources\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">RSS</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.rss_mb.latest.toFixed(0)} MB</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Heap</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.heap_mb.latest.toFixed(0)} MB\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU user</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_user_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU sys</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_sys_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{perf.diag && (\n\t\t\t\t<div className=\"perf-diag\">\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tFast-gate fires: {perf.diag.ctxFastGate}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim fires: {perf.diag.liveTrimFires}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim replays: {perf.diag.liveTrimReplays}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/components/PerfCards.tsx — 5 perf metric cards.\n *\n * Mirrors the old html.ts perf grid: model latency, throughput, process,\n * snapshot cost, and TUI lag proxy. Uses the existing perf-metric CSS\n * classes from repos-metrics.css plus metrics-extra.css for card headers.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\nimport {\n\tCard,\n\tCardContent,\n\tCardHeader,\n\tCardTitle,\n} from \"../components/ui/card\";\n\n/** Format milliseconds (em-dash for null/undefined). */\nfunction fmtMs(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : v >= 100 ? `${Math.round(v)}ms` : `${v.toFixed(1)}ms`;\n}\n\n/** Format a number with fixed decimals (em-dash for null/non-number). */\nfunction fmtNum(v: number | null | undefined, dec: number): string {\n\treturn v == null || typeof v !== \"number\" ? \"\\u2014\" : v.toFixed(dec);\n}\n\n/** Format a diag counter (em-dash for null). */\nfunction fmtDiag(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : String(v);\n}\n\ninterface PerfCardsProps {\n\tperf: PerfResponse;\n}\n\n/** A single stat row inside a perf card. */\nfunction Stat({ label, value }: { label: string; value: string }): React.ReactElement {\n\treturn (\n\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t<span className=\"text-xs text-muted-foreground\">{label}</span>\n\t\t\t<span className=\"font-semibold\">{value}</span>\n\t\t</div>\n\t);\n}\n\n/** Card wrapper with a title. */\nfunction PerfCard({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {\n\treturn (\n\t\t<Card className=\"electric-hover\">\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>{title}</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>{children}</CardContent>\n\t\t</Card>\n\t);\n}\n\nexport function PerfCards({ perf }: PerfCardsProps): React.ReactElement {\n\tconst cpuTxt = `${fmtNum(perf.cpu_user_ms.latest, 1)} / ${fmtNum(perf.cpu_sys_ms.latest, 1)} ms`;\n\n\treturn (\n\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n\t\t\t<PerfCard title=\"Model latency\">\n\t\t\t\t<Stat label=\"Turn p50\" value={fmtMs(perf.turn_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Turn p95\" value={fmtMs(perf.turn_latency_ms.p95)} />\n\t\t\t\t<Stat label=\"Provider p50\" value={fmtMs(perf.provider_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Provider p95\" value={fmtMs(perf.provider_latency_ms.p95)} />\n\t\t\t</PerfCard>\n\n\t\t\t<PerfCard title=\"Throughput\">\n\t\t\t\t<Stat label=\"TPS (avg)\" value={fmtNum(perf.tps.avg, 1)} />\n\t\t\t\t<Stat\n\t\t\t\t\tlabel=\"Cache hit %\"\n\t\t\t\t\tvalue={`${fmtNum(perf.cache_hit_pct.avg, 1)}%`}\n\t\t\t\t/>\n\t\t\t</PerfCard>\n\n\t\t\t<PerfCard title=\"Process\">\n\t\t\t\t<Stat label=\"RSS\" value={`${fmtNum(perf.rss_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"Heap\" value={`${fmtNum(perf.heap_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"CPU user / sys\" value={cpuTxt} />\n\t\t\t</PerfCard>\n\n\t\t\t<PerfCard title=\"Snapshot cost\">\n\t\t\t\t<Stat label=\"DB recompute p50\" value={fmtMs(perf.db_recompute_ms.p50)} />\n\t\t\t\t<Stat label=\"DB recompute p95\" value={fmtMs(perf.db_recompute_ms.p95)} />\n\t\t\t\t<Stat label=\"Disk write p50\" value={fmtMs(perf.disk_write_ms.p50)} />\n\t\t\t</PerfCard>\n\n\t\t\t<PerfCard title=\"TUI lag proxy\">\n\t\t\t\t<Stat label=\"Live-trim fires\" value={fmtDiag(perf.diag?.liveTrimFires)} />\n\t\t\t\t<Stat label=\"Cache replays\" value={fmtDiag(perf.diag?.liveTrimReplays)} />\n\t\t\t\t<Stat label=\"Fast-gate skips\" value={fmtDiag(perf.diag?.ctxFastGate)} />\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">skip vs recompute vs replay cadence</span>\n\t\t\t</PerfCard>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ModelBadge.tsx — model + provider + rates.\n *\n * Shows the active model name, provider, and input/output rates.\n * Tooltips copied verbatim from html.ts. Used in the Metrics tab header.\n */\n\nimport type React from \"react\";\n\nexport interface ModelBadgeProps {\n\t/** Model name/identifier. */\n\tname: string;\n\t/** Human-readable provider name. */\n\tproviderName: string;\n\t/** Machine-readable provider identifier. */\n\tprovider: string;\n\t/** USD per input token (from model pricing). */\n\tinputRate: number;\n\t/** USD per output token (from model pricing). */\n\toutputRate: number;\n}\n\nfunction Field({\n\tlabel,\n\tvalue,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"model-field\" title={title}>\n\t\t\t<span className=\"model-field-label\">{label}</span>\n\t\t\t<span className=\"model-field-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\nexport function ModelBadge({\n\tname,\n\tproviderName,\n\tprovider,\n\tinputRate,\n\toutputRate,\n}: ModelBadgeProps): React.ReactElement {\n\tconst providerDisplay = providerName || provider || \"—\";\n\treturn (\n\t\t<div className=\"model-badge\">\n\t\t\t<Field\n\t\t\t\tlabel=\"Model\"\n\t\t\t\tvalue={name}\n\t\t\t\ttitle=\"The model pi is currently using — its pricing drives the cost figure.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Provider\"\n\t\t\t\tvalue={providerDisplay}\n\t\t\t\ttitle=\"The provider serving the model.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Input Rate\"\n\t\t\t\tvalue={`$${inputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per input token, from the model's pricing.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Output Rate\"\n\t\t\t\tvalue={`$${outputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per output token, from the model's pricing.\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RagDashboard.tsx — full RAG metrics section\n * (H3.2). Rendered in the Metrics tab.\n *\n * Fetches /api/rag-metrics and renders:\n * 1. Stat row: telemetry turns, HyDE-ran count, avg lift, pass rate.\n * 2. HyDE Recall Lift Bar (BarChart over daily avg lift).\n * 3. CRAG Quality Line (LineChart over daily avg score).\n * 4. Per-flag status dots (hydeEnabled, recallMetricsEnabled).\n * 5. Recall Latency Stacked Bar (hyde vs base turn counts) + Hit-Rate Area.\n *\n * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.\n * recharts is bundled into this localhost-served static bundle (no runtime\n * network calls).\n */\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchRagMetrics } from \"../api/client\";\nimport type { RagMetricsResponse } from \"@contracts\";\n\n/** Daily-aggregate row shape (derived from the rag-metrics contract). */\ntype DailyTelemetry = RagMetricsResponse[\"daily\"][number];\nimport {\n\tResponsiveContainer,\n\tBarChart,\n\tBar,\n\tLineChart,\n\tLine,\n\tAreaChart,\n\tArea,\n\tXAxis,\n\tYAxis,\n\tCartesianGrid,\n\tTooltip,\n\tLegend,\n} from \"recharts\";\nimport { Card, CardHeader, CardTitle, CardContent } from \"./ui/card\";\nimport { Badge } from \"./ui/badge\";\n\nconst AXIS = \"#8b949e\";\nconst GRID = \"#30363d\";\nconst TOOLTIP = {\n\tbackground: \"#161b22\",\n\tborder: \"1px solid #30363d\",\n\tborderRadius: \"6px\",\n\tcolor: \"#e6edf3\",\n\tfontSize: 12,\n} as const;\n\n/** One row per day, with lead/lag-safe derived series for the charts. */\ninterface DailyRow {\n\tday: string;\n\tavgLift: number;\n\tavgScore: number;\n\tavgGenMs: number;\n\tpassRate: number;\n\thydeTurns: number;\n\tbaseTurns: number;\n\trecallCount: number;\n}\n\nfunction toRows(daily: DailyTelemetry[]): DailyRow[] {\n\treturn daily.map((d) => {\n\t\tconst baseTurns = Math.max(0, d.recallCount - d.hydeRanCount);\n\t\treturn {\n\t\t\tday: d.day.slice(5), // \"MM-DD\"\n\t\t\tavgLift: d.avgLift ?? 0,\n\t\t\tavgScore: d.avgScore ?? 0,\n\t\t\tavgGenMs: d.avgGenMs ?? 0,\n\t\t\tpassRate: d.avgScore == null ? 0 : d.avgScore,\n\t\t\thydeTurns: d.hydeRanCount,\n\t\t\tbaseTurns,\n\t\t\trecallCount: d.recallCount,\n\t\t};\n\t});\n}\n\nexport interface RagDashboardProps {\n\t/** Optional pre-fetched metrics; when absent, the card fetches inline. */\n\tmetrics?: RagMetricsResponse | null;\n}\n\nexport const RagDashboard: React.FC<RagDashboardProps> = ({ metrics }) => {\n\tconst { data, loading } = useApi<RagMetricsResponse>(\n\t\tuseMemo(() => () => fetchRagMetrics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst m = metrics ?? data;\n\tconst rows = useMemo(() => (m ? toRows(m.daily) : []), [m]);\n\n\tif (loading && !m) {\n\t\treturn <div className=\"text-sm text-muted-foreground\">Loading RAG metrics…</div>;\n\t}\n\tif (!m) {\n\t\treturn (\n\t\t\t<div className=\"text-sm text-muted-foreground\">\n\t\t\t\tNo RAG metrics available yet. They appear once turns record HyDE/recall\n\t\t\t\ttelemetry.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst { totals, flags } = m;\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<StatRow totals={totals} flags={flags} />\n\n\t\t\t<div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>HyDE Recall Lift</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ResponsiveContainer width=\"100%\" height={200}>\n\t\t\t\t\t\t\t<BarChart data={rows} margin={{ top: 8, right: 12, left: 0, bottom: 0 }}>\n\t\t\t\t\t\t\t\t<CartesianGrid strokeDasharray=\"3 3\" stroke={GRID} />\n\t\t\t\t\t\t\t\t<XAxis dataKey=\"day\" stroke={AXIS} tick={{ fontSize: 11 }} />\n\t\t\t\t\t\t\t\t<YAxis stroke={AXIS} tick={{ fontSize: 11 }} width={40} />\n\t\t\t\t\t\t\t\t<Tooltip contentStyle={TOOLTIP} />\n\t\t\t\t\t\t\t\t<Bar dataKey=\"avgLift\" name=\"avg lift\" fill=\"#58a6ff\" isAnimationActive={false} />\n\t\t\t\t\t\t\t</BarChart>\n\t\t\t\t\t\t</ResponsiveContainer>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>CRAG Quality</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ResponsiveContainer width=\"100%\" height={200}>\n\t\t\t\t\t\t\t<LineChart data={rows} margin={{ top: 8, right: 12, left: 0, bottom: 0 }}>\n\t\t\t\t\t\t\t\t<CartesianGrid strokeDasharray=\"3 3\" stroke={GRID} />\n\t\t\t\t\t\t\t\t<XAxis dataKey=\"day\" stroke={AXIS} tick={{ fontSize: 11 }} />\n\t\t\t\t\t\t\t\t<YAxis stroke={AXIS} tick={{ fontSize: 11 }} width={40} domain={[0, 1]} />\n\t\t\t\t\t\t\t\t<Tooltip contentStyle={TOOLTIP} />\n\t\t\t\t\t\t\t\t<Line type=\"monotone\" dataKey=\"avgScore\" name=\"avg score\" stroke=\"#3fb950\" dot={false} strokeWidth={2} isAnimationActive={false} />\n\t\t\t\t\t\t\t</LineChart>\n\t\t\t\t\t\t</ResponsiveContainer>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recall Latency (avg gen ms)</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ResponsiveContainer width=\"100%\" height={200}>\n\t\t\t\t\t\t\t<BarChart data={rows} margin={{ top: 8, right: 12, left: 0, bottom: 0 }}>\n\t\t\t\t\t\t\t\t<CartesianGrid strokeDasharray=\"3 3\" stroke={GRID} />\n\t\t\t\t\t\t\t\t<XAxis dataKey=\"day\" stroke={AXIS} tick={{ fontSize: 11 }} />\n\t\t\t\t\t\t\t\t<YAxis stroke={AXIS} tick={{ fontSize: 11 }} width={40} />\n\t\t\t\t\t\t\t\t<Tooltip contentStyle={TOOLTIP} />\n\t\t\t\t\t\t\t\t<Bar dataKey=\"avgGenMs\" name=\"gen ms\" fill=\"#d29922\" isAnimationActive={false} />\n\t\t\t\t\t\t\t</BarChart>\n\t\t\t\t\t\t</ResponsiveContainer>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>HyDE vs Base Turn Volume</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ResponsiveContainer width=\"100%\" height={200}>\n\t\t\t\t\t\t\t<AreaChart data={rows} margin={{ top: 8, right: 12, left: 0, bottom: 0 }}>\n\t\t\t\t\t\t\t\t<CartesianGrid strokeDasharray=\"3 3\" stroke={GRID} />\n\t\t\t\t\t\t\t\t<XAxis dataKey=\"day\" stroke={AXIS} tick={{ fontSize: 11 }} />\n\t\t\t\t\t\t\t\t<YAxis stroke={AXIS} tick={{ fontSize: 11 }} width={40} />\n\t\t\t\t\t\t\t\t<Tooltip contentStyle={TOOLTIP} />\n\t\t\t\t\t\t\t\t<Legend wrapperStyle={{ fontSize: 11 }} />\n\t\t\t\t\t\t\t\t<Area type=\"monotone\" dataKey=\"hydeTurns\" name=\"hyde turns\" stackId=\"1\" stroke=\"#58a6ff\" fill=\"#58a6ff\" fillOpacity={0.5} isAnimationActive={false} />\n\t\t\t\t\t\t\t\t<Area type=\"monotone\" dataKey=\"baseTurns\" name=\"base turns\" stackId=\"1\" stroke=\"#8b949e\" fill=\"#8b949e\" fillOpacity={0.4} isAnimationActive={false} />\n\t\t\t\t\t\t\t</AreaChart>\n\t\t\t\t\t\t</ResponsiveContainer>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nfunction StatRow({\n\ttotals,\n\tflags,\n}: {\n\ttotals: RagMetricsResponse[\"totals\"];\n\tflags: RagMetricsResponse[\"flags\"];\n}): React.ReactElement {\n\tconst stats: Array<{ label: string; value: string; hint: string }> = [\n\t\t{\n\t\t\tlabel: \"Telemetry turns\",\n\t\t\tvalue: String(totals.telemetryTurns),\n\t\t\thint: \"turns with HyDE or recall data\",\n\t\t},\n\t\t{\n\t\t\tlabel: \"HyDE ran\",\n\t\t\tvalue: String(totals.hydeRanTurns),\n\t\t\thint: \"turns where HyDE generated a doc\",\n\t\t},\n\t\t{\n\t\t\tlabel: \"Avg lift\",\n\t\t\tvalue: `${totals.avgLift.toFixed(2)}×`,\n\t\t\thint: \"fused / raw hit breadth\",\n\t\t},\n\t\t{\n\t\t\tlabel: \"Pass rate\",\n\t\t\tvalue: `${Math.round(totals.recentPassRate * 100)}%`,\n\t\t\thint: totals.avgScore == null ? \"no scored turns\" : `avg score ${totals.avgScore.toFixed(2)}`,\n\t\t},\n\t];\n\n\treturn (\n\t\t<div className=\"grid grid-cols-2 gap-4 lg:grid-cols-5\">\n\t\t\t{stats.map((s) => (\n\t\t\t\t<Card key={s.label}>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"text-xs text-muted-foreground\">{s.label}</div>\n\t\t\t\t\t\t<div className=\"font-heading text-2xl font-semibold text-foreground\">\n\t\t\t\t\t\t\t{s.value}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-0.5 text-xs text-muted-foreground\">{s.hint}</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t))}\n\t\t\t<Card>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<div className=\"text-xs text-muted-foreground\">Flags</div>\n\t\t\t\t\t<div className=\"mt-1 flex flex-col gap-1 text-xs\">\n\t\t\t\t\t\t<FlagDot label=\"HyDE\" on={flags.hydeEnabled} />\n\t\t\t\t\t\t<FlagDot label=\"Recall metrics\" on={flags.recallMetricsEnabled} />\n\t\t\t\t\t</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n\nfunction FlagDot({ label, on }: { label: string; on: boolean }): React.ReactElement {\n\treturn (\n\t\t<span className=\"flex items-center gap-1.5\">\n\t\t\t<span\n\t\t\t\tclassName={`inline-block h-2 w-2 rounded-full ${on ? \"bg-success\" : \"bg-danger\"}`}\n\t\t\t\taria-hidden=\"true\"\n\t\t\t/>\n\t\t\t{label}:{\" \"}\n\t\t\t<Badge variant={on ? \"success\" : \"danger\"}>{on ? \"on\" : \"off\"}</Badge>\n\t\t</span>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MetricsTab.tsx — Metrics tab (C2).\n *\n * Fetches /api/perf (rolling-window aggregates) + /api/snapshot (for model).\n * Renders ModelBadge + PerfChart + 5 perf cards (latency, throughput, process,\n * snapshot cost, TUI lag proxy). Polls every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchPerf, fetchSnapshot } from \"../api/client\";\nimport type { PerfResponse, SnapshotResponse } from \"@contracts\";\nimport { NEW_UI } from \"../config\";\nimport { PerfChart } from \"../components/PerfChart\";\nimport { PerfCards } from \"../components/PerfCards\";\nimport { ModelBadge } from \"../components/ModelBadge\";\nimport { RagDashboard } from \"../components/RagDashboard\";\n\nexport default function MetricsTab(): React.ReactElement {\n\tconst { data: perf, error: perfErr } = useApi<PerfResponse>(\n\t\tuseCallback(() => fetchPerf({ minutes: 30 }), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\tconst { data: snapshot } = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (perfErr && !perf) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading perf: {perfErr.message}</div>\n\t\t);\n\t}\n\tif (!perf) {\n\t\treturn <div className=\"tab-stub\">Loading perf…</div>;\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t{snapshot?.model && (\n\t\t\t\t<ModelBadge\n\t\t\t\t\tname={snapshot.model.name}\n\t\t\t\t\tproviderName={snapshot.model.providerName}\n\t\t\t\t\tprovider={snapshot.model.provider}\n\t\t\t\t\tinputRate={snapshot.model.inputRate}\n\t\t\t\t\toutputRate={snapshot.model.outputRate}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<PerfChart perf={perf} />\n\t\t\t<PerfCards perf={perf} />\n\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\t{perf.sampleCount} samples · updated {perf.updatedAt}\n\t\t\t</div>\n\t\t\t{NEW_UI() && <RagDashboard />}\n\t\t</div>\n\t);\n}\n"],"names":["fmtMs","n","Bar","value","max","colorClass","pct","jsx","PerfChart","perf","latencyMax","tpsRef","Card","jsxs","CardHeader","CardTitle","CardContent","v","fmtNum","dec","fmtDiag","Stat","label","PerfCard","title","children","PerfCards","cpuTxt","Field","ModelBadge","name","providerName","provider","inputRate","outputRate","providerDisplay","AXIS","GRID","TOOLTIP","toRows","daily","d","baseTurns","RagDashboard","metrics","data","loading","useApi","useMemo","fetchRagMetrics","m","rows","totals","flags","StatRow","ResponsiveContainer","BarChart","CartesianGrid","XAxis","YAxis","Tooltip","LineChart","Line","AreaChart","Legend","Area","stats","s","FlagDot","on","Badge","MetricsTab","perfErr","useCallback","fetchPerf","snapshot","fetchSnapshot","NEW_UI"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as d,j as e,u as b,b as M,D as A}from"./index-CkbdlPBm.js";import{u as C}from"./useSSE-B9_N7ccG.js";import{C as c,c as m}from"./card-Cxeeje9o.js";import{R as I,C as T,X as D,Y as L,T as E,a as F,L as W}from"./generateCategoricalChart-DRlqBks2.js";import{A as R,a as z}from"./AreaChart-CtJ6dSMT.js";import{T as P}from"./toggle-CfIFzp6P.js";function O(s,i){const n=new Map,o=s.map(t=>t.sessionId);for(const t of s)for(const r of t.data){let a=n.get(r.ts);if(!a){a={ts:r.ts,total:0};for(const l of o)a[l]=0;n.set(r.ts,a)}a[t.sessionId]=r.tokens}for(const t of i){let r=n.get(t.ts);if(!r){r={ts:t.ts,total:0};for(const a of o)r[a]=0;n.set(t.ts,r)}r.total=t.tokens}return[...n.values()].sort((t,r)=>t.ts-r.ts)}function v(s){return new Date(s).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}function k(s){const i=Number(s);return Number.isFinite(i)?i.toLocaleString():String(s)}function N(s){return`grad-${s.replace(/[^a-zA-Z0-9_-]/g,"_")}`}function G({series:s,totals:i}){const n=d.useMemo(()=>O(s,i),[s,i]);return s.length===0?e.jsx(c,{children:e.jsx(m,{children:"No token samples in this window yet."})}):e.jsx(c,{children:e.jsx(m,{children:e.jsx(I,{width:"100%",height:360,children:e.jsxs(R,{data:n,margin:{top:8,right:16,left:0,bottom:0},children:[e.jsx("defs",{children:s.map(o=>e.jsxs("linearGradient",{id:N(o.sessionId),x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:o.color,stopOpacity:.7}),e.jsx("stop",{offset:"95%",stopColor:o.color,stopOpacity:.25})]},`${o.sessionId}-grad`))}),e.jsx(T,{strokeDasharray:"3 3",stroke:"#30363d"}),e.jsx(D,{dataKey:"ts",type:"number",domain:["dataMin","dataMax"],tickFormatter:o=>v(o),stroke:"#8b949e",tick:{fontSize:11},scale:"time"}),e.jsx(L,{stroke:"#8b949e",tick:{fontSize:11},width:56,tickFormatter:o=>k(o)}),e.jsx(E,{labelFormatter:o=>v(o),formatter:o=>[k(o),"tokens"],contentStyle:{background:"#161b22",border:"1px solid #30363d",borderRadius:"6px",color:"#e6edf3"},labelStyle:{color:"#8b949e",fontSize:12}}),e.jsx(F,{wrapperStyle:{fontSize:12}}),s.map(o=>e.jsx(z,{type:"monotone",dataKey:o.sessionId,name:o.label,stackId:"1",stroke:o.color,fill:`url(#${N(o.sessionId)})`,fillOpacity:1,isAnimationActive:!1,dot:!1},o.sessionId)),e.jsx(W,{type:"monotone",dataKey:"total",name:"Total",stroke:"#e6edf3",strokeWidth:1.5,strokeDasharray:"4 2",dot:!1,isAnimationActive:!1})]})})})})}const y=["PID","Repo","Model","Context %","Tokens / Window","Last Heartbeat","State Dir","Trend"];function K(s){const i=Math.max(0,Math.round(s/1e3));if(i<60)return`${i}s ago`;const n=Math.floor(i/60);return n<60?`${n}m ${i%60}s ago`:`${Math.floor(n/60)}h ${n%60}m ago`}function S(s){return s==null?"—":s>=1e5?`${(s/1e3).toFixed(0)}k`:s>=1e3?`${(s/1e3).toFixed(1)}k`:s.toLocaleString()}function X(s){return s==null?"sessions-pct-ok":s>=85?"sessions-pct-err":s>=60?"sessions-pct-warn":"sessions-pct-ok"}function _(s){return s==null?"—":`${Math.round(s)}%`}function B(s,i){return s==null?"—":`${S(s)} / ${S(i)}`}function H({points:s,color:i}){if(s.length<2)return e.jsx("span",{className:"sessions-sparkline-empty",children:"—"});const n=Math.min(...s),t=Math.max(...s)-n||1,r=64,a=18,l=r/(s.length-1),x=s.map((u,h)=>{const j=h*l,p=a-(u-n)/t*a;return`${h===0?"M":"L"} ${j.toFixed(1)} ${p.toFixed(1)}`});return e.jsx("svg",{className:"sessions-sparkline",width:r,height:a,viewBox:`0 0 ${r} ${a}`,role:"img","aria-label":"token trend",children:e.jsx("path",{d:x.join(" "),fill:"none",stroke:i,strokeWidth:"1.5",strokeLinejoin:"round",strokeLinecap:"round"})})}function Y(s){const i=new Map;for(const n of s)i.set(n.sessionId,n);return i}function Z({sessions:s,series:i}){const n=Y(i),o=Date.now();return e.jsx(c,{children:e.jsx(m,{children:e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsx("tr",{children:y.map((t,r)=>e.jsx("th",{className:`border-b border-border px-3 py-2 text-left font-medium text-muted-foreground${r>=3&&r!==6?" text-right":""}`,children:t},t))})}),e.jsxs("tbody",{children:[s.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:y.length,className:"px-3 py-4 text-muted-foreground",children:"No active sessions."})}),s.map(t=>{const r=n.get(t.sessionId),a=r?.data.map(x=>x.tokens)??[],l=r?.color??"#58a6ff";return e.jsxs("tr",{className:"border-b border-border/50 hover:bg-bg-elevated/40",children:[e.jsx("td",{className:"px-3 py-2 text-right font-mono",children:t.pid}),e.jsx("td",{className:"px-3 py-2",title:t.repoRoot??void 0,children:t.displayName||t.repoRoot||"—"}),e.jsx("td",{className:"px-3 py-2",children:t.model??"—"}),e.jsx("td",{className:"px-3 py-2 text-right",children:e.jsx("span",{className:`sessions-pct-pill ${X(t.percent)}`,children:_(t.percent)})}),e.jsx("td",{className:"px-3 py-2 text-right",children:B(t.tokens,t.ctxWindow)}),e.jsx("td",{className:"px-3 py-2 text-right",children:K(o-t.lastSeen)}),e.jsx("td",{title:t.stateDir??void 0,className:"px-3 py-2 font-mono text-muted-foreground",children:t.stateDir?t.stateDir.split("/").pop()||t.stateDir:"—"}),e.jsx("td",{className:"px-3 py-2",children:e.jsx(H,{points:a,color:l})})]},`${t.pid}:${t.sessionId}`)})]})]})})})})}function q(s){return s.type==="session_sample"}const w=[{value:5,label:"5m"},{value:15,label:"15m"},{value:30,label:"30m"},{value:60,label:"60m"}];function J({sessions:s}){const i=s.sessions.length,n=s.sessions.reduce((r,a)=>r+(a.tokens??0),0),o=s.sessions.reduce((r,a)=>Math.max(r,a.ctxWindow),0),t=o>0?n/o*100:0;return e.jsxs("div",{className:"grid grid-cols-2 gap-4 md:grid-cols-4",children:[e.jsx(c,{children:e.jsxs(m,{children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Active sessions"}),e.jsx("div",{className:"text-xl font-semibold",children:i})]})}),e.jsx(c,{children:e.jsxs(m,{children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Combined tokens"}),e.jsx("div",{className:"text-xl font-semibold",children:n>=1e5?`${(n/1e3).toFixed(0)}k`:n.toLocaleString()})]})}),e.jsx(c,{children:e.jsxs(m,{children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Combined % of max window"}),e.jsxs("div",{className:"text-xl font-semibold",children:[Math.round(t),"%"]})]})}),e.jsx(c,{children:e.jsxs(m,{children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Pruned (stale)"}),e.jsx("div",{className:"text-xl font-semibold",children:s.pruned})]})})]})}function ne(){const[s,i]=d.useState(30),{data:n,error:o,refetch:t}=b(d.useCallback(()=>M(),[]),{pollInterval:2e3}),{data:r,error:a,refetch:l}=b(d.useCallback(()=>A(s),[s]),{pollInterval:2e3}),{events:x}=C(),u=d.useMemo(()=>x.filter(q),[x]),h=d.useMemo(()=>u.length===0?null:u[u.length-1].ts,[u]);if(d.useEffect(()=>{h!=null&&(t(),l())},[h,t,l]),o&&!n)return e.jsxs("div",{className:"tab-stub",children:["Error loading sessions: ",o.message]});if(a&&!r)return e.jsxs("div",{className:"tab-stub",children:["Error loading timeseries: ",a.message]});if(!n)return e.jsx("div",{className:"tab-stub",children:"Loading sessions…"});const j=n.sessions,p=r?.series??[],g=r?.totals??[],$=w.find(f=>f.value===s)?.label??`${s}m`;return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Session Memory Graph"}),e.jsx("div",{className:"flex gap-2",children:w.map(f=>e.jsx(P,{pressed:s===f.value,onClick:()=>i(f.value),children:f.label},f.value))})]}),j.length===0?e.jsx("div",{className:"text-sm text-muted-foreground",children:"No active sessions."}):e.jsxs(e.Fragment,{children:[e.jsx(J,{sessions:n}),e.jsx(G,{series:p,totals:g}),e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Active Sessions"}),e.jsx(Z,{sessions:j,series:p}),e.jsxs("div",{className:"text-xs text-muted-foreground",children:[p.length," series · ",g.length," timestamps ·"," ",$," window · updated"," ",r?.updatedAt??n.updatedAt]})]})]})}export{ne as default};
|
|
2
|
-
//# sourceMappingURL=SessionsTab-B3quvYPZ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsTab-B3quvYPZ.js","sources":["../../src/components/SessionsMemoryChart.tsx","../../src/components/ActiveSessionsTable.tsx","../../src/tabs/SessionsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/SessionsMemoryChart.tsx — Stacked memory graph (S39).\n *\n * Recharts stacked `<Area>` per session + total `<Line>` + `<Tooltip>` +\n * `<Legend>` + `<ResponsiveContainer>`. Data comes from `/api/sessions/timeseries`\n * which returns `SessionSeries[]` (per-session data points + stable hex color)\n * and a `totals[]` array (sum across all sessions per timestamp).\n *\n * PREVENT-PI-004: recharts is bundled into this localhost-served static\n * dashboard bundle; it makes no runtime network calls. Chart data arrives via\n * same-origin fetch to the dashboard server (loopback, ports 9320–9329).\n */\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport {\n\tResponsiveContainer,\n\tAreaChart,\n\tArea,\n\tLine,\n\tXAxis,\n\tYAxis,\n\tCartesianGrid,\n\tTooltip,\n\tLegend,\n} from \"recharts\";\nimport type { SessionSeries } from \"@contracts\";\nimport { Card, CardContent } from \"../components/ui/card\";\n\nexport interface SessionsMemoryChartProps {\n\t/** Per-session series with stable colors (from /api/sessions/timeseries). */\n\tseries: SessionSeries[];\n\t/** Totals (sum across all sessions) per timestamp. */\n\ttotals: ReadonlyArray<{ ts: number; tokens: number }>;\n}\n\n/** A row in the pivoted data table passed to recharts `<AreaChart>`. */\ninterface PivotedRow {\n\t/** Unix timestamp (ms). */\n\tts: number;\n\t/** Total tokens across all sessions at this timestamp. */\n\ttotal: number;\n\t/** Per-session token count keyed by sessionId. */\n\t[sessionId: string]: number;\n}\n\n/**\n * Pivot the per-session series + totals into recharts' expected flat shape:\n * one row per unique timestamp, with each session's token count as a column\n * keyed by sessionId. Missing values for a session at a given timestamp are\n * filled with zero so the stack renders continuously.\n */\nfunction pivot(\n\tseries: SessionSeries[],\n\ttotals: ReadonlyArray<{ ts: number; tokens: number }>,\n): PivotedRow[] {\n\tconst map = new Map<number, PivotedRow>();\n\tconst sessionIds = series.map((s) => s.sessionId);\n\n\tfor (const s of series) {\n\t\tfor (const pt of s.data) {\n\t\t\tlet row = map.get(pt.ts);\n\t\t\tif (!row) {\n\t\t\t\trow = { ts: pt.ts, total: 0 } as PivotedRow;\n\t\t\t\tfor (const sid of sessionIds) row[sid] = 0;\n\t\t\t\tmap.set(pt.ts, row);\n\t\t\t}\n\t\t\trow[s.sessionId] = pt.tokens;\n\t\t}\n\t}\n\tfor (const t of totals) {\n\t\tlet row = map.get(t.ts);\n\t\tif (!row) {\n\t\t\trow = { ts: t.ts, total: 0 } as PivotedRow;\n\t\t\tfor (const sid of sessionIds) row[sid] = 0;\n\t\t\tmap.set(t.ts, row);\n\t\t}\n\t\trow.total = t.tokens;\n\t}\n\n\treturn [...map.values()].sort((a, b) => a.ts - b.ts);\n}\n\nfunction fmtTs(ts: number): string {\n\treturn new Date(ts).toLocaleTimeString([], {\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t});\n}\n\nfunction fmtTokens(value: number | string): string {\n\tconst n = Number(value);\n\treturn Number.isFinite(n) ? n.toLocaleString() : String(value);\n}\n\n/** Sanitize an arbitrary sessionId for use in an SVG gradient id. */\nfunction gradId(sessionId: string): string {\n\tconst safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n\treturn `grad-${safe}`;\n}\n\nexport function SessionsMemoryChart({\n\tseries,\n\ttotals,\n}: SessionsMemoryChartProps): React.ReactElement {\n\tconst data = useMemo(\n\t\t() => pivot(series, totals),\n\t\t[series, totals],\n\t);\n\n\tif (series.length === 0) {\n\t\treturn (\n\t\t\t<Card>\n\t\t\t\t<CardContent>No token samples in this window yet.</CardContent>\n\t\t\t</Card>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardContent>\n\t\t\t<ResponsiveContainer width=\"100%\" height={360}>\n\t\t\t\t<AreaChart\n\t\t\t\t\tdata={data}\n\t\t\t\t\tmargin={{ top: 8, right: 16, left: 0, bottom: 0 }}\n\t\t\t\t>\n\t\t\t\t\t<defs>\n\t\t\t\t\t\t{series.map((s) => (\n\t\t\t\t\t\t\t<linearGradient\n\t\t\t\t\t\t\t\tkey={`${s.sessionId}-grad`}\n\t\t\t\t\t\t\t\tid={gradId(s.sessionId)}\n\t\t\t\t\t\t\t\tx1=\"0\"\n\t\t\t\t\t\t\t\ty1=\"0\"\n\t\t\t\t\t\t\t\tx2=\"0\"\n\t\t\t\t\t\t\t\ty2=\"1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<stop offset=\"5%\" stopColor={s.color} stopOpacity={0.7} />\n\t\t\t\t\t\t\t\t<stop offset=\"95%\" stopColor={s.color} stopOpacity={0.25} />\n\t\t\t\t\t\t\t</linearGradient>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</defs>\n\t\t\t\t\t<CartesianGrid strokeDasharray=\"3 3\" stroke=\"#30363d\" />\n\t\t\t\t\t<XAxis\n\t\t\t\t\t\tdataKey=\"ts\"\n\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\tdomain={[\"dataMin\", \"dataMax\"]}\n\t\t\t\t\t\ttickFormatter={(ts: number) => fmtTs(ts)}\n\t\t\t\t\t\tstroke=\"#8b949e\"\n\t\t\t\t\t\ttick={{ fontSize: 11 }}\n\t\t\t\t\t\tscale=\"time\"\n\t\t\t\t\t/>\n\t\t\t\t\t<YAxis\n\t\t\t\t\t\tstroke=\"#8b949e\"\n\t\t\t\t\t\ttick={{ fontSize: 11 }}\n\t\t\t\t\t\twidth={56}\n\t\t\t\t\t\ttickFormatter={(v: number) => fmtTokens(v)}\n\t\t\t\t\t/>\n\t\t\t\t\t<Tooltip\n\t\t\t\t\t\tlabelFormatter={(ts: number) => fmtTs(ts)}\n\t\t\t\t\t\tformatter={(value) =>\n\t\t\t\t\t\t\t[fmtTokens(value as number), \"tokens\"] as [string, string]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontentStyle={{\n\t\t\t\t\t\t\tbackground: \"#161b22\",\n\t\t\t\t\t\t\tborder: \"1px solid #30363d\",\n\t\t\t\t\t\t\tborderRadius: \"6px\",\n\t\t\t\t\t\t\tcolor: \"#e6edf3\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tlabelStyle={{ color: \"#8b949e\", fontSize: 12 }}\n\t\t\t\t\t/>\n\t\t\t\t\t<Legend wrapperStyle={{ fontSize: 12 }} />\n\t\t\t\t\t{series.map((s) => (\n\t\t\t\t\t\t<Area\n\t\t\t\t\t\t\tkey={s.sessionId}\n\t\t\t\t\t\t\ttype=\"monotone\"\n\t\t\t\t\t\t\tdataKey={s.sessionId}\n\t\t\t\t\t\t\tname={s.label}\n\t\t\t\t\t\t\tstackId=\"1\"\n\t\t\t\t\t\t\tstroke={s.color}\n\t\t\t\t\t\t\tfill={`url(#${gradId(s.sessionId)})`}\n\t\t\t\t\t\t\tfillOpacity={1}\n\t\t\t\t\t\t\tisAnimationActive={false}\n\t\t\t\t\t\t\tdot={false}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t\t<Line\n\t\t\t\t\t\ttype=\"monotone\"\n\t\t\t\t\t\tdataKey=\"total\"\n\t\t\t\t\t\tname=\"Total\"\n\t\t\t\t\t\tstroke=\"#e6edf3\"\n\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\tstrokeDasharray=\"4 2\"\n\t\t\t\t\t\tdot={false}\n\t\t\t\t\t\tisAnimationActive={false}\n\t\t\t\t\t/>\n\t\t\t\t</AreaChart>\n\t\t\t</ResponsiveContainer>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/components/ActiveSessionsTable.tsx — Active sessions table (S39).\n *\n * Per-session rows: PID, Repo, Model, Context %, Tokens/Window,\n * Heartbeat age, State dir, Sparkline.\n *\n * Data from /api/sessions (active sessions list) and /api/sessions/timeseries\n * (per-session data points used to render inline sparklines).\n */\n\nimport type React from \"react\";\nimport type { ActiveSession, SessionSeries } from \"@contracts\";\nimport { Card, CardContent } from \"../components/ui/card\";\n\nexport interface ActiveSessionsTableProps {\n\t/** Active sessions, sorted by lastSeen descending (from /api/sessions). */\n\tsessions: ActiveSession[];\n\t/** Per-session timeseries used to render the inline sparkline column. */\n\tseries: SessionSeries[];\n}\n\nconst HEADERS = [\n\t\"PID\",\n\t\"Repo\",\n\t\"Model\",\n\t\"Context %\",\n\t\"Tokens / Window\",\n\t\"Last Heartbeat\",\n\t\"State Dir\",\n\t\"Trend\",\n] as const;\n\n/** Format a duration in milliseconds → human-readable age string. */\nfunction fmtAge(ms: number): string {\n\tconst s = Math.max(0, Math.round(ms / 1000));\n\tif (s < 60) return `${s}s ago`;\n\tconst m = Math.floor(s / 60);\n\tif (m < 60) return `${m}m ${s % 60}s ago`;\n\tconst h = Math.floor(m / 60);\n\treturn `${h}h ${m % 60}m ago`;\n}\n\nfunction fmtTokens(n: number | null): string {\n\tif (n == null) return \"\\u2014\";\n\tif (n >= 100_000) return `${(n / 1000).toFixed(0)}k`;\n\tif (n >= 1000) return `${(n / 1000).toFixed(1)}k`;\n\treturn n.toLocaleString();\n}\n\nfunction pctClass(pct: number | null): string {\n\tif (pct == null) return \"sessions-pct-ok\";\n\tif (pct >= 85) return \"sessions-pct-err\";\n\tif (pct >= 60) return \"sessions-pct-warn\";\n\treturn \"sessions-pct-ok\";\n}\n\nfunction fmtPct(pct: number | null): string {\n\tif (pct == null) return \"\\u2014\";\n\treturn `${Math.round(pct)}%`;\n}\n\nfunction fmtWindow(tokens: number | null, window: number): string {\n\tif (tokens == null) return \"\\u2014\";\n\treturn `${fmtTokens(tokens)} / ${fmtTokens(window)}`;\n}\n\ninterface SparklineProps {\n\t/** Token counts over time (oldest → newest). */\n\tpoints: number[];\n\t/** Stable hex color for the stroke. */\n\tcolor: string;\n}\n\nfunction Sparkline({ points, color }: SparklineProps): React.ReactElement {\n\tif (points.length < 2) {\n\t\treturn <span className=\"sessions-sparkline-empty\">{\"\\u2014\"}</span>;\n\t}\n\tconst min = Math.min(...points);\n\tconst max = Math.max(...points);\n\tconst range = max - min || 1;\n\tconst w = 64;\n\tconst h = 18;\n\tconst stepX = w / (points.length - 1);\n\tconst segments = points.map((p, i) => {\n\t\tconst x = i * stepX;\n\t\tconst y = h - ((p - min) / range) * h;\n\t\treturn `${i === 0 ? \"M\" : \"L\"} ${x.toFixed(1)} ${y.toFixed(1)}`;\n\t});\n\treturn (\n\t\t<svg\n\t\t\tclassName=\"sessions-sparkline\"\n\t\t\twidth={w}\n\t\t\theight={h}\n\t\t\tviewBox={`0 0 ${w} ${h}`}\n\t\t\trole=\"img\"\n\t\t\taria-label=\"token trend\"\n\t\t>\n\t\t\t<path\n\t\t\t\td={segments.join(\" \")}\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke={color}\n\t\t\t\tstrokeWidth=\"1.5\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\n/** Map sessionId → series for the sparkline column lookup. */\nfunction indexSeries(\n\tseries: SessionSeries[],\n): Map<string, SessionSeries> {\n\tconst m = new Map<string, SessionSeries>();\n\tfor (const s of series) m.set(s.sessionId, s);\n\treturn m;\n}\n\nexport function ActiveSessionsTable({\n\tsessions,\n\tseries,\n}: ActiveSessionsTableProps): React.ReactElement {\n\tconst seriesById = indexSeries(series);\n\tconst now = Date.now();\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardContent>\n\t\t\t<div className=\"overflow-x-auto\">\n\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t{HEADERS.map((h, i) => (\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tkey={h}\n\t\t\t\t\t\t\t\t\tclassName={`border-b border-border px-3 py-2 text-left font-medium text-muted-foreground${\n\t\t\t\t\t\t\t\t\t\ti >= 3 && i !== 6 ? \" text-right\" : \"\"\n\t\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{h}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{sessions.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={HEADERS.length} className=\"px-3 py-4 text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tNo active sessions.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{sessions.map((s) => {\n\t\t\t\t\t\t\tconst srs = seriesById.get(s.sessionId);\n\t\t\t\t\t\t\tconst sparkPts =\n\t\t\t\t\t\t\t\tsrs?.data.map((d) => d.tokens) ?? [];\n\t\t\t\t\t\t\tconst color = srs?.color ?? \"#58a6ff\";\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\t\tkey={`${s.pid}:${s.sessionId}`}\n\t\t\t\t\t\t\t\t\tclassName=\"border-b border-border/50 hover:bg-bg-elevated/40\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2 text-right font-mono\">{s.pid}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" title={s.repoRoot ?? undefined}>\n\t\t\t\t\t\t\t\t\t\t{s.displayName || s.repoRoot || \"\\u2014\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{s.model ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2 text-right\">\n\t\t\t\t\t\t\t\t\t\t<span className={`sessions-pct-pill ${pctClass(s.percent)}`}>\n\t\t\t\t\t\t\t\t\t\t\t{fmtPct(s.percent)}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2 text-right\">\n\t\t\t\t\t\t\t\t\t\t{fmtWindow(s.tokens, s.ctxWindow)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2 text-right\">{fmtAge(now - s.lastSeen)}</td>\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\ttitle={s.stateDir ?? undefined}\n\t\t\t\t\t\t\t\t\t\tclassName=\"px-3 py-2 font-mono text-muted-foreground\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{s.stateDir\n\t\t\t\t\t\t\t\t\t\t\t? s.stateDir.split(\"/\").pop() || s.stateDir\n\t\t\t\t\t\t\t\t\t\t\t: \"\\u2014\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t\t\t\t<Sparkline points={sparkPts} color={color} />\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/SessionsTab.tsx — Sessions tab (S39).\n *\n * Renders: window selector (5/15/30/60 min), sessions summary tiles,\n * the stacked-memory graph (SessionsMemoryChart), and the active sessions\n * table (ActiveSessionsTable).\n *\n * Data sources: fetchSessions() + fetchSessionTimeseries(minutes). Both poll\n * every 2s for near-real-time updates. useSSE() listens for session_sample\n * events emitted by appendTokenSample (Step 5) so the chart refetches\n * instantly between polls when a new token sample is appended to events.log.\n *\n * Empty state: shows \"No active sessions.\" when the /api/sessions response\n * returns an empty array (e.g. no pi processes are running yet).\n */\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { fetchSessions, fetchSessionTimeseries } from \"../api/client\";\nimport type { SseEvent, SseSessionSample, SessionsResponse, SessionTimeseriesResponse } from \"@contracts\";\nimport { SessionsMemoryChart } from \"../components/SessionsMemoryChart\";\nimport { ActiveSessionsTable } from \"../components/ActiveSessionsTable\";\nimport { Toggle } from \"../components/ui/toggle\";\nimport { Card, CardContent } from \"../components/ui/card\";\n\n/**\n * Shape of the {@link SseSessionSample} SSE event appended to events.log by\n * `appendTokenSample` (src/store/sqlite/global-index.ts). The contract now\n * defines this variant as part of the {@link SseEvent} union (Step 5); the\n * local alias keeps the surface terser in this file.\n */\ntype SessionSampleEvent = SseSessionSample;\n\n/** Type guard: narrows an SSE event to a session_sample event. */\nfunction isSessionSample(\n\te: SseEvent,\n): e is SessionSampleEvent {\n\treturn e.type === \"session_sample\";\n}\n\nconst WINDOWS: ReadonlyArray<{ value: number; label: string }> = [\n\t{ value: 5, label: \"5m\" },\n\t{ value: 15, label: \"15m\" },\n\t{ value: 30, label: \"30m\" },\n\t{ value: 60, label: \"60m\" },\n] as const;\n\ninterface SummaryTilesProps {\n\tsessions: SessionsResponse;\n}\n\nfunction SummaryTiles({ sessions }: SummaryTilesProps): React.ReactElement {\n\tconst active = sessions.sessions.length;\n\tconst combinedTokens = sessions.sessions.reduce(\n\t\t(sum, s) => sum + (s.tokens ?? 0),\n\t\t0,\n\t);\n\tconst maxWindow = sessions.sessions.reduce(\n\t\t(max, s) => Math.max(max, s.ctxWindow),\n\t\t0,\n\t);\n\tconst combinedPct = maxWindow > 0 ? (combinedTokens / maxWindow) * 100 : 0;\n\treturn (\n\t\t<div className=\"grid grid-cols-2 gap-4 md:grid-cols-4\">\n\t\t\t<Card>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">Active sessions</span>\n\t\t\t\t\t<div className=\"text-xl font-semibold\">{active}</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t\t<Card>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">Combined tokens</span>\n\t\t\t\t\t<div className=\"text-xl font-semibold\">\n\t\t\t\t\t\t{combinedTokens >= 100_000\n\t\t\t\t\t\t\t? `${(combinedTokens / 1000).toFixed(0)}k`\n\t\t\t\t\t\t\t: combinedTokens.toLocaleString()}\n\t\t\t\t\t</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t\t<Card>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\tCombined % of max window\n\t\t\t\t\t</span>\n\t\t\t\t\t<div className=\"text-xl font-semibold\">{Math.round(combinedPct)}%</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t\t<Card>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">Pruned (stale)</span>\n\t\t\t\t\t<div className=\"text-xl font-semibold\">{sessions.pruned}</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n\nexport default function SessionsTab(): React.ReactElement {\n\tconst [minutes, setMinutes] = useState(30);\n\n\tconst {\n\t\tdata: sessionsData,\n\t\terror: sessionsErr,\n\t\trefetch: refetchSessions,\n\t} = useApi<SessionsResponse>(\n\t\tuseCallback(() => fetchSessions(), []),\n\t\t{ pollInterval: 2_000 },\n\t);\n\n\tconst {\n\t\tdata: timeseriesData,\n\t\terror: timeseriesErr,\n\t\trefetch: refetchTimeseries,\n\t} = useApi<SessionTimeseriesResponse>(\n\t\tuseCallback(() => fetchSessionTimeseries(minutes), [minutes]),\n\t\t{ pollInterval: 2_000 },\n\t);\n\n\tconst { events } = useSSE();\n\n\t// Filter SSE events for session_sample pushes (Step 5) and trigger an\n\t// immediate refetch when a new sample arrives, so the chart updates\n\t// between the 2s polls. The events array grows as new samples arrive; the\n\t// length-based dependency means the effect re-runs when a new sample event\n\t// lands (not on every poll). We debounce via a microtask guard.\n\tconst sampleEvents = useMemo(\n\t\t() => events.filter(isSessionSample),\n\t\t[events],\n\t);\n\n\tconst lastSampleTs = useMemo(() => {\n\t\tif (sampleEvents.length === 0) return null;\n\t\treturn sampleEvents[sampleEvents.length - 1].ts;\n\t}, [sampleEvents]);\n\n\tuseEffect(() => {\n\t\tif (lastSampleTs == null) return;\n\t\trefetchSessions();\n\t\trefetchTimeseries();\n\t}, [lastSampleTs, refetchSessions, refetchTimeseries]);\n\n\tif (sessionsErr && !sessionsData) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\tError loading sessions: {sessionsErr.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (timeseriesErr && !timeseriesData) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\tError loading timeseries: {timeseriesErr.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!sessionsData) {\n\t\treturn <div className=\"tab-stub\">Loading sessions…</div>;\n\t}\n\n\tconst sessions = sessionsData.sessions;\n\tconst series = timeseriesData?.series ?? [];\n\tconst totals = timeseriesData?.totals ?? [];\n\tconst windowLabel =\n\t\tWINDOWS.find((w) => w.value === minutes)?.label ?? `${minutes}m`;\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<h2 className=\"font-heading text-lg font-semibold\">\n\t\t\t\t\tSession Memory Graph\n\t\t\t\t</h2>\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t{WINDOWS.map((w) => (\n\t\t\t\t\t\t<Toggle\n\t\t\t\t\t\t\tkey={w.value}\n\t\t\t\t\t\t\tpressed={minutes === w.value}\n\t\t\t\t\t\t\tonClick={() => setMinutes(w.value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{w.label}\n\t\t\t\t\t\t</Toggle>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{sessions.length === 0 ? (\n\t\t\t\t<div className=\"text-sm text-muted-foreground\">No active sessions.</div>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<SummaryTiles sessions={sessionsData} />\n\t\t\t\t\t<SessionsMemoryChart series={series} totals={totals} />\n\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">\n\t\t\t\t\t\tActive Sessions\n\t\t\t\t\t</h2>\n\t\t\t\t\t<ActiveSessionsTable sessions={sessions} series={series} />\n\t\t\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t{series.length} series · {totals.length} timestamps ·{\" \"}\n\t\t\t\t\t\t{windowLabel} window · updated{\" \"}\n\t\t\t\t\t\t{timeseriesData?.updatedAt ?? sessionsData.updatedAt}\n\t\t\t\t\t</div>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["pivot","series","totals","map","sessionIds","s","pt","row","sid","a","b","fmtTs","ts","fmtTokens","value","n","gradId","sessionId","SessionsMemoryChart","data","useMemo","jsx","Card","CardContent","ResponsiveContainer","jsxs","AreaChart","CartesianGrid","XAxis","YAxis","v","Tooltip","Legend","Area","Line","HEADERS","fmtAge","ms","m","pctClass","pct","fmtPct","fmtWindow","tokens","window","Sparkline","points","color","min","range","w","h","stepX","segments","p","i","x","y","indexSeries","ActiveSessionsTable","sessions","seriesById","now","srs","sparkPts","d","isSessionSample","e","WINDOWS","SummaryTiles","active","combinedTokens","sum","maxWindow","max","combinedPct","SessionsTab","minutes","setMinutes","useState","sessionsData","sessionsErr","refetchSessions","useApi","useCallback","fetchSessions","timeseriesData","timeseriesErr","refetchTimeseries","fetchSessionTimeseries","events","useSSE","sampleEvents","lastSampleTs","useEffect","windowLabel","Toggle","Fragment"],"mappings":"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"}
|