@oh-my-pi/omp-stats 18.3.4 → 18.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +13 -11
  3. package/build.ts +5 -49
  4. package/dist/client/index.css +1 -1
  5. package/dist/client/index.html +2 -2
  6. package/dist/client/index.js +105 -126
  7. package/dist/types/aggregator.d.ts +25 -22
  8. package/dist/types/client/api.d.ts +11 -7
  9. package/dist/types/client/app/LiveChip.d.ts +2 -0
  10. package/dist/types/client/app/Shell.d.ts +16 -0
  11. package/dist/types/client/app/ThemeToggle.d.ts +1 -0
  12. package/dist/types/client/app/nav.d.ts +18 -0
  13. package/dist/types/client/charts/BarList.d.ts +17 -0
  14. package/dist/types/client/charts/Chart.d.ts +49 -0
  15. package/dist/types/client/charts/Legend.d.ts +16 -0
  16. package/dist/types/client/charts/ShareBar.d.ts +11 -0
  17. package/dist/types/client/charts/Sparkline.d.ts +11 -0
  18. package/dist/types/client/charts/TimeChart.d.ts +9 -0
  19. package/dist/types/client/charts/index.d.ts +7 -0
  20. package/dist/types/client/charts/types.d.ts +25 -0
  21. package/dist/types/client/charts/useWidth.d.ts +3 -0
  22. package/dist/types/client/data/colors.d.ts +25 -0
  23. package/dist/types/client/data/formatters.d.ts +9 -1
  24. package/dist/types/client/data/live.d.ts +29 -0
  25. package/dist/types/client/data/query.d.ts +35 -0
  26. package/dist/types/client/data/range.d.ts +31 -0
  27. package/dist/types/client/data/series.d.ts +28 -0
  28. package/dist/types/client/data/useHashRoute.d.ts +18 -4
  29. package/dist/types/client/data/view-models.d.ts +111 -28
  30. package/dist/types/client/index.d.ts +0 -1
  31. package/dist/types/client/routes/CostsRoute.d.ts +2 -2
  32. package/dist/types/client/routes/ErrorsRoute.d.ts +2 -2
  33. package/dist/types/client/routes/FrustrationRoute.d.ts +7 -0
  34. package/dist/types/client/routes/GainRoute.d.ts +1 -2
  35. package/dist/types/client/routes/ModelsRoute.d.ts +2 -2
  36. package/dist/types/client/routes/OverviewRoute.d.ts +1 -2
  37. package/dist/types/client/routes/ProjectsRoute.d.ts +2 -2
  38. package/dist/types/client/routes/ProvidersRoute.d.ts +2 -2
  39. package/dist/types/client/routes/RequestsRoute.d.ts +1 -2
  40. package/dist/types/client/routes/ToolsRoute.d.ts +2 -2
  41. package/dist/types/client/routes/TracesRoute.d.ts +2 -2
  42. package/dist/types/client/routes/index.d.ts +1 -1
  43. package/dist/types/client/traces/AggregatesPanel.d.ts +4 -3
  44. package/dist/types/client/traces/SummaryStrip.d.ts +2 -2
  45. package/dist/types/client/traces/trace-colors.d.ts +18 -12
  46. package/dist/types/client/ui/Badge.d.ts +19 -0
  47. package/dist/types/client/ui/Card.d.ts +25 -0
  48. package/dist/types/client/ui/Drawer.d.ts +25 -0
  49. package/dist/types/client/ui/JsonBlock.d.ts +2 -2
  50. package/dist/types/client/ui/Modal.d.ts +23 -0
  51. package/dist/types/client/ui/RequestDrawer.d.ts +10 -1
  52. package/dist/types/client/ui/SearchInput.d.ts +8 -0
  53. package/dist/types/client/ui/Segmented.d.ts +15 -0
  54. package/dist/types/client/ui/Stat.d.ts +36 -0
  55. package/dist/types/client/ui/States.d.ts +41 -0
  56. package/dist/types/client/ui/Table.d.ts +48 -0
  57. package/dist/types/client/ui/index.d.ts +9 -10
  58. package/dist/types/db.d.ts +90 -106
  59. package/dist/types/frustration.d.ts +70 -0
  60. package/dist/types/index.d.ts +3 -1
  61. package/dist/types/live.d.ts +30 -0
  62. package/dist/types/rollup.d.ts +115 -0
  63. package/dist/types/server.d.ts +6 -1
  64. package/dist/types/shared-types.d.ts +116 -56
  65. package/dist/types/types.d.ts +4 -0
  66. package/dist/types/user-metrics.d.ts +8 -0
  67. package/package.json +7 -13
  68. package/src/aggregator.ts +276 -211
  69. package/src/client/App.tsx +38 -80
  70. package/src/client/api.ts +65 -7
  71. package/src/client/app/LiveChip.tsx +97 -0
  72. package/src/client/app/Shell.tsx +181 -0
  73. package/src/client/app/ThemeToggle.tsx +5 -2
  74. package/src/client/app/nav.ts +78 -0
  75. package/src/client/charts/BarList.tsx +48 -0
  76. package/src/client/charts/Chart.tsx +388 -0
  77. package/src/client/charts/Legend.tsx +61 -0
  78. package/src/client/charts/ShareBar.tsx +26 -0
  79. package/src/client/charts/Sparkline.tsx +38 -0
  80. package/src/client/charts/TimeChart.tsx +22 -0
  81. package/src/client/charts/index.ts +7 -0
  82. package/src/client/charts/types.ts +27 -0
  83. package/src/client/charts/useWidth.ts +19 -0
  84. package/src/client/data/colors.ts +45 -0
  85. package/src/client/data/formatters.ts +30 -9
  86. package/src/client/data/live.tsx +85 -0
  87. package/src/client/data/query.ts +166 -0
  88. package/src/client/data/range.ts +82 -0
  89. package/src/client/data/series.ts +85 -0
  90. package/src/client/data/useHashRoute.ts +41 -83
  91. package/src/client/data/view-models.ts +347 -140
  92. package/src/client/index.tsx +3 -6
  93. package/src/client/routes/CostsRoute.tsx +394 -222
  94. package/src/client/routes/ErrorsRoute.tsx +401 -98
  95. package/src/client/routes/FrustrationRoute.tsx +924 -0
  96. package/src/client/routes/GainRoute.tsx +209 -206
  97. package/src/client/routes/ModelsRoute.tsx +540 -431
  98. package/src/client/routes/OverviewRoute.tsx +283 -311
  99. package/src/client/routes/ProjectsRoute.tsx +298 -156
  100. package/src/client/routes/ProvidersRoute.tsx +948 -416
  101. package/src/client/routes/RequestsRoute.tsx +295 -107
  102. package/src/client/routes/ToolsRoute.tsx +495 -398
  103. package/src/client/routes/TracesRoute.tsx +136 -117
  104. package/src/client/routes/costs.css +24 -0
  105. package/src/client/routes/errors.css +60 -0
  106. package/src/client/routes/frustration.css +123 -0
  107. package/src/client/routes/index.ts +1 -1
  108. package/src/client/routes/models.css +43 -0
  109. package/src/client/routes/projects.css +6 -0
  110. package/src/client/routes/providers.css +28 -0
  111. package/src/client/routes/tools.css +14 -0
  112. package/src/client/styles.css +1396 -1299
  113. package/src/client/traces/AggregatesPanel.tsx +55 -74
  114. package/src/client/traces/Minimap.tsx +6 -15
  115. package/src/client/traces/SpanDrawer.tsx +128 -194
  116. package/src/client/traces/SummaryStrip.tsx +21 -24
  117. package/src/client/traces/TimelineCanvas.tsx +27 -44
  118. package/src/client/traces/TraceView.tsx +175 -130
  119. package/src/client/traces/TranscriptList.tsx +13 -41
  120. package/src/client/traces/trace-colors.ts +60 -53
  121. package/src/client/traces/traces.css +279 -0
  122. package/src/client/ui/Badge.tsx +29 -0
  123. package/src/client/ui/Card.tsx +61 -0
  124. package/src/client/ui/Drawer.tsx +93 -0
  125. package/src/client/ui/JsonBlock.tsx +26 -44
  126. package/src/client/ui/Modal.tsx +106 -0
  127. package/src/client/ui/RequestDrawer.tsx +163 -188
  128. package/src/client/ui/SearchInput.tsx +25 -0
  129. package/src/client/ui/Segmented.tsx +61 -0
  130. package/src/client/ui/Stat.tsx +104 -0
  131. package/src/client/ui/States.tsx +81 -0
  132. package/src/client/ui/Table.tsx +229 -0
  133. package/src/client/ui/index.ts +9 -10
  134. package/src/client/ui/modal.css +54 -0
  135. package/src/client/ui/request-drawer.css +40 -0
  136. package/src/db.ts +379 -983
  137. package/src/frustration.ts +473 -0
  138. package/src/index.ts +22 -22
  139. package/src/live.ts +259 -0
  140. package/src/parser.ts +55 -66
  141. package/src/rollup.ts +951 -0
  142. package/src/server.ts +123 -40
  143. package/src/shared-types.ts +120 -56
  144. package/src/trace.ts +14 -7
  145. package/src/types.ts +4 -0
  146. package/src/user-metrics.ts +13 -0
  147. package/dist/client/styles.css +0 -1976
  148. package/dist/types/client/app/AppLayout.d.ts +0 -16
  149. package/dist/types/client/app/NavRail.d.ts +0 -7
  150. package/dist/types/client/app/RangeControl.d.ts +0 -7
  151. package/dist/types/client/app/SyncButton.d.ts +0 -14
  152. package/dist/types/client/app/TopBar.d.ts +0 -15
  153. package/dist/types/client/app/routes.d.ts +0 -12
  154. package/dist/types/client/components/AgentTokenShare.d.ts +0 -5
  155. package/dist/types/client/components/chart-shared.d.ts +0 -178
  156. package/dist/types/client/components/models-table-shared.d.ts +0 -175
  157. package/dist/types/client/components/range-meta.d.ts +0 -21
  158. package/dist/types/client/data/charts.d.ts +0 -1
  159. package/dist/types/client/data/useResource.d.ts +0 -13
  160. package/dist/types/client/routes/BehaviorRoute.d.ts +0 -7
  161. package/dist/types/client/ui/AsyncBoundary.d.ts +0 -12
  162. package/dist/types/client/ui/DataTable.d.ts +0 -17
  163. package/dist/types/client/ui/EmptyState.d.ts +0 -7
  164. package/dist/types/client/ui/ErrorState.d.ts +0 -6
  165. package/dist/types/client/ui/MetricCluster.d.ts +0 -5
  166. package/dist/types/client/ui/Panel.d.ts +0 -7
  167. package/dist/types/client/ui/SegmentedControl.d.ts +0 -12
  168. package/dist/types/client/ui/Skeleton.d.ts +0 -8
  169. package/dist/types/client/ui/StatusPill.d.ts +0 -7
  170. package/src/client/app/AppLayout.tsx +0 -93
  171. package/src/client/app/NavRail.tsx +0 -44
  172. package/src/client/app/RangeControl.tsx +0 -39
  173. package/src/client/app/SyncButton.tsx +0 -75
  174. package/src/client/app/TopBar.tsx +0 -73
  175. package/src/client/app/routes.ts +0 -93
  176. package/src/client/components/AgentTokenShare.tsx +0 -68
  177. package/src/client/components/chart-shared.tsx +0 -273
  178. package/src/client/components/models-table-shared.tsx +0 -255
  179. package/src/client/components/range-meta.ts +0 -73
  180. package/src/client/data/charts.ts +0 -14
  181. package/src/client/data/useResource.ts +0 -154
  182. package/src/client/routes/BehaviorRoute.tsx +0 -623
  183. package/src/client/ui/AsyncBoundary.tsx +0 -54
  184. package/src/client/ui/DataTable.tsx +0 -122
  185. package/src/client/ui/EmptyState.tsx +0 -16
  186. package/src/client/ui/ErrorState.tsx +0 -25
  187. package/src/client/ui/MetricCluster.tsx +0 -92
  188. package/src/client/ui/Panel.tsx +0 -24
  189. package/src/client/ui/SegmentedControl.tsx +0 -36
  190. package/src/client/ui/Skeleton.tsx +0 -17
  191. package/src/client/ui/StatusPill.tsx +0 -15
  192. package/tailwind.config.js +0 -40
@@ -1,4 +1,4 @@
1
- import type { AgentType, AgentTypeStats, BehaviorOverallStats, BehaviorTimeSeriesPoint, CostTimeSeriesPoint, FolderStats, ModelPerformancePoint, TimeRange, ToolUsageStats } from "../types.js";
1
+ import type { AgentType, AgentTypeStats, CostTimeSeriesPoint, FolderStats, MessageStats, ModelPerformancePoint, ToolUsageStats } from "../types.js";
2
2
  export interface ConversationTokenStats {
3
3
  totalInputTokens: number;
4
4
  totalOutputTokens: number;
@@ -28,12 +28,37 @@ export interface AgentTokenShareView {
28
28
  * conversation-token buckets as the overview total so the two views reconcile.
29
29
  */
30
30
  export declare function buildAgentTokenShare(stats: AgentTypeStats[]): AgentTokenShareView;
31
- export interface CostSummaryView {
31
+ /** API-equivalent cost split by billing component. */
32
+ export interface CostComponents {
33
+ costInput: number;
34
+ costOutput: number;
35
+ costCacheRead: number;
36
+ costCacheWrite: number;
37
+ }
38
+ /** One model's (model + provider) totals across the cost series. */
39
+ export interface CostModelRow extends CostComponents {
40
+ /** `model::provider` (see `modelKey`). */
41
+ key: string;
42
+ model: string;
43
+ provider: string;
44
+ cost: number;
45
+ requests: number;
46
+ unpricedRequests: number;
47
+ /** Fraction (0-1) of the total estimate. */
48
+ share: number;
49
+ }
50
+ export interface CostSummaryView extends CostComponents {
32
51
  totalCost: number;
52
+ requests: number;
33
53
  unpricedRequests: number;
54
+ /** Day buckets that carry any usage. */
55
+ activeDays: number;
56
+ /** Total estimate ÷ active days. */
34
57
  avgDailyCost: number;
35
- topModelName: string;
36
- topModelCost: number;
58
+ /** Ranked by estimate, then requests, then key. */
59
+ models: CostModelRow[];
60
+ /** Highest-estimate model; `null` when nothing is priced. */
61
+ topModel: CostModelRow | null;
37
62
  }
38
63
  export interface ModelPerformanceDataPoint {
39
64
  timestamp: number;
@@ -41,35 +66,93 @@ export interface ModelPerformanceDataPoint {
41
66
  avgTokensPerSecond: number | null;
42
67
  requests: number;
43
68
  }
44
- export interface ModelPerformanceSeries {
45
- label: string;
46
- data: ModelPerformanceDataPoint[];
47
- }
48
- export interface BehaviorSummaryView {
49
- totalMessages: number;
50
- totalYelling: number;
51
- totalProfanity: number;
52
- totalAnguish: number;
53
- totalFrustration: number;
54
- highestFrictionModel: {
55
- model: string;
56
- provider: string;
57
- score: number;
58
- } | null;
59
- }
60
69
  export interface FolderRowView extends FolderStats {
61
- costPercentage: number;
62
- requestsPercentage: number;
70
+ /** Uncached input + cache reads + cache writes + output. */
71
+ conversationTokens: number;
72
+ /** Share of every folder's requests in range (0-1). */
73
+ requestShare: number;
74
+ /** Share of every folder's API-equivalent cost in range (0-1). */
75
+ costShare: number;
76
+ /** Session directory under a system temp location (`/tmp`, `/var/folders`, …). */
77
+ temporary: boolean;
78
+ }
79
+ export interface FolderTableView {
80
+ rows: FolderRowView[];
81
+ totalRequests: number;
82
+ failedRequests: number;
83
+ totalCost: number;
84
+ unpricedRequests: number;
85
+ conversationTokens: number;
86
+ /** Cache reads ÷ (uncached input + cache reads) across every folder. */
87
+ cacheRate: number;
88
+ /** Largest single-folder request count / cost, for meter scales. */
89
+ maxRequests: number;
90
+ maxCost: number;
91
+ temporaryCount: number;
63
92
  }
64
- export declare function buildCostSummary(costSeries: CostTimeSeriesPoint[]): CostSummaryView;
65
- export declare function buildModelPerformanceLookup(points: ModelPerformancePoint[], range: TimeRange): Map<string, ModelPerformanceSeries>;
66
- export declare function buildBehaviorSummary(overall: BehaviorOverallStats, series: BehaviorTimeSeriesPoint[]): BehaviorSummaryView;
67
- export declare function buildFolderRows(folders: FolderStats[]): FolderRowView[];
93
+ /** Totals, per-model breakdown and component split for the Costs page. */
94
+ export declare function buildCostSummary(costSeries: readonly CostTimeSeriesPoint[]): CostSummaryView;
95
+ /**
96
+ * Per-model (`model::provider`) performance points in ascending time order,
97
+ * with TTFT converted to seconds. Only buckets where the model served
98
+ * requests are present, so sparse history (all time) keeps every point.
99
+ */
100
+ export declare function buildModelPerformanceLookup(points: readonly ModelPerformancePoint[]): Map<string, ModelPerformanceDataPoint[]>;
101
+ /** Table rows plus range totals for the Projects page; linear in the (possibly tens of thousands of) folders. */
102
+ export declare function buildFolderRows(folders: readonly FolderStats[]): FolderTableView;
68
103
  /** Table row for the Tools route: usage stats plus derived rates/shares. */
69
104
  export interface ToolRowView extends ToolUsageStats {
70
105
  /** errors / calls (0 for zero calls). */
71
106
  errorRate: number;
72
- /** Calls relative to the busiest tool, 0-100, for the share bar. */
73
- callsPercentage: number;
107
+ /** This tool's fraction (0..1) of all calls in range. */
108
+ callFraction: number;
109
+ /** This tool's fraction (0..1) of all attributed tokens in range. */
110
+ tokenFraction: number;
111
+ /** This tool's fraction (0..1) of all attributed cost in range. */
112
+ costFraction: number;
113
+ /** Mean result characters fed back into context per call. */
114
+ avgResultChars: number;
74
115
  }
75
116
  export declare function buildToolRows(tools: ToolUsageStats[]): ToolRowView[];
117
+ export type RequestStatus = "ok" | "aborted" | "failed";
118
+ /** Outcome of one request: cancellations abort; provider/transport errors fail. */
119
+ export declare function requestStatus(row: Pick<MessageStats, "stopReason" | "errorMessage">): RequestStatus;
120
+ export interface RequestLogSummary {
121
+ requests: number;
122
+ failed: number;
123
+ aborted: number;
124
+ tokens: number;
125
+ cost: number;
126
+ /** Requests with tokens but no price (see `isUnpricedMessage`). */
127
+ unpriced: number;
128
+ medianDuration: number | null;
129
+ p95Duration: number | null;
130
+ medianTtft: number | null;
131
+ oldest: number | null;
132
+ newest: number | null;
133
+ }
134
+ /** Totals and latency quantiles over a list of requests. */
135
+ export declare function summarizeRequests(rows: readonly MessageStats[]): RequestLogSummary;
136
+ /**
137
+ * Normalized error message used to group failures: whitespace collapsed,
138
+ * request ids / hashes / counters replaced with placeholders (HTTP status
139
+ * codes kept), truncated.
140
+ */
141
+ export declare function errorSignature(message: string | null): string;
142
+ export interface ErrorGroupModel {
143
+ model: string;
144
+ provider: string;
145
+ count: number;
146
+ }
147
+ export interface ErrorGroupView {
148
+ signature: string;
149
+ count: number;
150
+ firstSeen: number;
151
+ lastSeen: number;
152
+ /** Most recent occurrence (full message, drawer target). */
153
+ latest: MessageStats;
154
+ /** Affected models, most failures first. */
155
+ models: ErrorGroupModel[];
156
+ }
157
+ /** Group failures by `errorSignature`; most frequent first, then most recent. */
158
+ export declare function groupErrorsBySignature(rows: readonly MessageStats[]): ErrorGroupView[];
@@ -1,2 +1 @@
1
1
  import "./styles.css";
2
- import "./data/charts.js";
@@ -1,7 +1,7 @@
1
+ import "./costs.css";
1
2
  import type { TimeRange } from "../types.js";
2
3
  export interface CostsRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  }
7
- export declare function CostsRoute({ active, range, refreshTrigger }: CostsRouteProps): import("react").JSX.Element;
7
+ export declare function CostsRoute({ active, range }: CostsRouteProps): import("react").JSX.Element;
@@ -1,8 +1,8 @@
1
1
  import type { TimeRange } from "../types.js";
2
+ import "./errors.css";
2
3
  export interface ErrorsRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  onRequestClick: (id: number) => void;
7
7
  }
8
- export declare function ErrorsRoute({ active, range, refreshTrigger, onRequestClick }: ErrorsRouteProps): import("react").JSX.Element;
8
+ export declare function ErrorsRoute({ active, range, onRequestClick }: ErrorsRouteProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import type { TimeRange } from "../types.js";
2
+ import "./frustration.css";
3
+ export interface FrustrationRouteProps {
4
+ active: boolean;
5
+ range: TimeRange;
6
+ }
7
+ export declare function FrustrationRoute({ active, range }: FrustrationRouteProps): import("react").JSX.Element;
@@ -2,6 +2,5 @@ import type { TimeRange } from "../types.js";
2
2
  export interface GainRouteProps {
3
3
  active: boolean;
4
4
  range: TimeRange;
5
- refreshTrigger: number;
6
5
  }
7
- export declare function GainRoute({ active, range, refreshTrigger }: GainRouteProps): import("react").JSX.Element;
6
+ export declare function GainRoute({ active, range }: GainRouteProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
+ import "./models.css";
1
2
  import type { TimeRange } from "../types.js";
2
3
  export interface ModelsRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  }
7
- export declare function ModelsRoute({ active, range, refreshTrigger }: ModelsRouteProps): import("react").JSX.Element;
7
+ export declare function ModelsRoute({ active, range }: ModelsRouteProps): import("react").JSX.Element;
@@ -2,7 +2,6 @@ import type { TimeRange } from "../types.js";
2
2
  export interface OverviewRouteProps {
3
3
  active: boolean;
4
4
  range: TimeRange;
5
- refreshTrigger: number;
6
5
  onRequestClick: (id: number) => void;
7
6
  }
8
- export declare function OverviewRoute({ active, range, refreshTrigger, onRequestClick }: OverviewRouteProps): import("react").JSX.Element;
7
+ export declare function OverviewRoute({ active, range, onRequestClick }: OverviewRouteProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import type { TimeRange } from "../types.js";
2
+ import "./projects.css";
2
3
  export interface ProjectsRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  }
7
- export declare function ProjectsRoute({ active, range, refreshTrigger }: ProjectsRouteProps): import("react").JSX.Element;
7
+ export declare function ProjectsRoute({ active, range }: ProjectsRouteProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import type { TimeRange } from "../types.js";
2
+ import "./providers.css";
2
3
  export interface ProvidersRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  }
7
- export declare function ProvidersRoute({ active, range, refreshTrigger }: ProvidersRouteProps): import("react").JSX.Element;
7
+ export declare function ProvidersRoute({ active, range }: ProvidersRouteProps): import("react").JSX.Element;
@@ -2,7 +2,6 @@ import type { TimeRange } from "../types.js";
2
2
  export interface RequestsRouteProps {
3
3
  active: boolean;
4
4
  range: TimeRange;
5
- refreshTrigger: number;
6
5
  onRequestClick: (id: number) => void;
7
6
  }
8
- export declare function RequestsRoute({ active, refreshTrigger, onRequestClick }: RequestsRouteProps): import("react").JSX.Element;
7
+ export declare function RequestsRoute({ active, range, onRequestClick }: RequestsRouteProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import type { TimeRange } from "../types.js";
2
+ import "./tools.css";
2
3
  export interface ToolsRouteProps {
3
4
  active: boolean;
4
5
  range: TimeRange;
5
- refreshTrigger: number;
6
6
  }
7
- export declare function ToolsRoute({ active, range, refreshTrigger }: ToolsRouteProps): import("react").JSX.Element;
7
+ export declare function ToolsRoute({ active, range }: ToolsRouteProps): import("react").JSX.Element;
@@ -2,10 +2,10 @@
2
2
  * Traces section: root-session list (subagents folded in) that opens the
3
3
  * flamegraph trace viewer for a selected session.
4
4
  */
5
+ import "../traces/traces.css";
5
6
  export interface TracesRouteProps {
6
7
  active: boolean;
7
8
  session: string | null;
8
9
  onOpenSession: (file: string | null) => void;
9
- refreshTrigger: number;
10
10
  }
11
- export declare function TracesRoute({ active, session, onOpenSession, refreshTrigger }: TracesRouteProps): import("react").JSX.Element;
11
+ export declare function TracesRoute({ active, session, onOpenSession }: TracesRouteProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
- export * from "./BehaviorRoute.js";
2
1
  export * from "./CostsRoute.js";
3
2
  export * from "./ErrorsRoute.js";
3
+ export * from "./FrustrationRoute.js";
4
4
  export * from "./GainRoute.js";
5
5
  export * from "./ModelsRoute.js";
6
6
  export * from "./OverviewRoute.js";
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Collapsible per-tool duration aggregates for one trace, sorted by total
3
- * time descending (as produced by the server).
2
+ * Per-tool duration aggregates for one trace (sortable; server order is total
3
+ * time descending).
4
4
  */
5
5
  import type { TraceToolStat } from "../types.js";
6
6
  export interface AggregatesPanelProps {
7
7
  toolStats: TraceToolStat[];
8
+ index?: number;
8
9
  }
9
- export declare function AggregatesPanel({ toolStats }: AggregatesPanelProps): import("react").JSX.Element | null;
10
+ export declare function AggregatesPanel({ toolStats, index }: AggregatesPanelProps): import("react").JSX.Element | null;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Headline chips for one trace: wall/model/tool/idle time, turns, requests,
3
- * tool calls, agents, tokens, cost.
2
+ * Headline tiles for one trace: wall time split into model/tool/idle, then
3
+ * turns, requests, tool calls, agents, tokens, cost.
4
4
  */
5
5
  import type { TraceSummary } from "../types.js";
6
6
  export interface SummaryStripProps {
@@ -1,31 +1,37 @@
1
1
  /**
2
- * Category palette + canvas chrome tokens for the trace flamegraph, keyed by
3
- * resolved theme. Hues align with the dashboard chip palette; chrome tokens
4
- * mirror `CHART_THEMES` so the canvas matches the chart.js surfaces.
2
+ * Canvas palette for the trace flamegraph, resolved from the dashboard's CSS
3
+ * design tokens so canvas drawing follows the active theme.
5
4
  */
6
5
  import type { TraceSpanKind } from "../types.js";
7
6
  export interface TraceTheme {
8
7
  /** Span fill per category. */
9
8
  category: Record<TraceSpanKind, string>;
10
- /** Error fill tint + left-edge accent. */
9
+ /** Error left-edge accent. */
11
10
  error: string;
11
+ /** Error fill tint. */
12
+ errorSoft: string;
12
13
  /** Marker diamond fill. */
13
14
  marker: string;
14
15
  /** Alternating turn-band fill. */
15
16
  turnBand: string;
16
- /** Canvas background. */
17
- surface: string;
18
17
  /** Ruler/lane hairlines. */
19
18
  grid: string;
20
19
  /** Ruler tick labels. */
21
20
  tick: string;
22
21
  /** Span label text drawn inside blocks. */
23
22
  spanText: string;
24
- /** Selection outline (brand cyan focus ring). */
23
+ /** Selection outline / minimap brush. */
25
24
  selection: string;
26
- tooltipBackground: string;
27
- tooltipTitle: string;
28
- tooltipBody: string;
29
- tooltipBorder: string;
25
+ /** Minimap brush fill. */
26
+ brush: string;
27
+ /** Canvas font stacks. */
28
+ fontSans: string;
29
+ fontMono: string;
30
30
  }
31
- export declare const TRACE_THEMES: Record<"dark" | "light", TraceTheme>;
31
+ /** CSS custom property backing each category (also used by DOM chips). */
32
+ export declare const CATEGORY_VARS: Record<TraceSpanKind, string>;
33
+ /**
34
+ * Current trace palette. Re-reads the tokens whenever the resolved theme
35
+ * changes (store or a direct `data-theme` attribute write).
36
+ */
37
+ export declare function useTraceTheme(): TraceTheme;
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from "react";
2
+ export type Tone = "neutral" | "ok" | "warn" | "bad" | "accent";
3
+ /** Pill label; status badges always carry a word, never color alone. */
4
+ export declare function Badge({ tone, mono, children }: {
5
+ tone?: Tone;
6
+ mono?: boolean;
7
+ children: ReactNode;
8
+ }): import("react").JSX.Element;
9
+ /** Round status dot; `pulse` for things happening right now. */
10
+ export declare function Dot({ tone, pulse }: {
11
+ tone?: Tone | "live";
12
+ pulse?: boolean;
13
+ }): import("react").JSX.Element;
14
+ /** Color square matching a chart series. */
15
+ export declare function Swatch({ color }: {
16
+ color: string;
17
+ }): import("react").JSX.Element;
18
+ /** Badge tone for an error rate (fraction). */
19
+ export declare function errorRateTone(rate: number): Tone;
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from "react";
2
+ export interface CardProps {
3
+ title?: ReactNode;
4
+ description?: ReactNode;
5
+ /** Right-aligned header controls (segmented toggles, selects, buttons). */
6
+ actions?: ReactNode;
7
+ /** Body without padding — for full-bleed tables and lists. */
8
+ flush?: boolean;
9
+ footer?: ReactNode;
10
+ className?: string;
11
+ /** Stagger index for the entrance animation. */
12
+ index?: number;
13
+ /** Dim the body while previous data is on screen (see `useQuery().stale`). */
14
+ stale?: boolean;
15
+ children: ReactNode;
16
+ }
17
+ /** The dashboard's container: top-lit hairline edge, optional header and footer. */
18
+ export declare function Card({ title, description, actions, flush, footer, className, index, stale, children }: CardProps): import("react").JSX.Element;
19
+ export interface PageHeaderProps {
20
+ title: ReactNode;
21
+ description?: ReactNode;
22
+ actions?: ReactNode;
23
+ }
24
+ /** Title band at the top of every page. */
25
+ export declare function PageHeader({ title, description, actions }: PageHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { type ReactNode } from "react";
2
+ export interface DrawerProps {
3
+ open: boolean;
4
+ onClose: () => void;
5
+ title: ReactNode;
6
+ subtitle?: ReactNode;
7
+ /** Header controls left of the close button. */
8
+ actions?: ReactNode;
9
+ /** Sheet width in px. Default 560. */
10
+ width?: number;
11
+ children: ReactNode;
12
+ }
13
+ /**
14
+ * Right-side glass sheet over a scrim, portalled to `document.body`. Esc and
15
+ * scrim clicks close it; focus returns to the opener.
16
+ */
17
+ export declare function Drawer({ open, onClose, title, subtitle, actions, width, children }: DrawerProps): import("react").ReactPortal | null;
18
+ /** Label/value grid for detail panels. */
19
+ export declare function KeyValues({ items }: {
20
+ items: readonly {
21
+ key: string;
22
+ label: ReactNode;
23
+ value: ReactNode;
24
+ }[];
25
+ }): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
- import type React from "react";
2
1
  export interface JsonBlockProps {
3
2
  data: unknown;
4
3
  title?: string;
5
4
  initialCollapsed?: boolean;
6
5
  }
7
- export declare function JsonBlock({ data, title, initialCollapsed }: JsonBlockProps): React.JSX.Element;
6
+ /** Collapsible, copyable pretty-printed JSON. */
7
+ export declare function JsonBlock({ data, title, initialCollapsed }: JsonBlockProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import type React from "react";
2
+ import "./modal.css";
3
+ export interface ModalAction {
4
+ label: string;
5
+ onClick: () => void;
6
+ disabled?: boolean;
7
+ }
8
+ export interface ModalProps {
9
+ open: boolean;
10
+ title: React.ReactNode;
11
+ /** Called on Esc, overlay click, the close icon, and the cancel button. */
12
+ onClose: () => void;
13
+ /** Confirming action; receives focus whenever it is present and enabled. */
14
+ primaryAction?: ModalAction;
15
+ cancelLabel?: string;
16
+ children: React.ReactNode;
17
+ }
18
+ /**
19
+ * Centered dialog over a dimmed overlay, portalled to `document.body` so route
20
+ * containers cannot clip it. Focus lands on the primary action (or the cancel
21
+ * button when there is none) and returns to the opener on close.
22
+ */
23
+ export declare function Modal({ open, title, onClose, primaryAction, cancelLabel, children }: ModalProps): React.ReactPortal | null;
@@ -1,5 +1,14 @@
1
+ import { type RequestStatus } from "../data/view-models.js";
2
+ import { type Tone } from "./Badge.js";
3
+ import "./request-drawer.css";
4
+ /** Label and tone for each request outcome; shared by the request tables. */
5
+ export declare const REQUEST_STATUS: Record<RequestStatus, {
6
+ label: string;
7
+ tone: Tone;
8
+ }>;
1
9
  export interface RequestDrawerProps {
2
10
  id: number | null;
3
11
  onClose: () => void;
4
12
  }
5
- export declare function RequestDrawer({ id, onClose }: RequestDrawerProps): import("react").JSX.Element | null;
13
+ /** Full detail sheet for one model request: timing, tokens, cost, ids, error, and the raw session entry. */
14
+ export declare function RequestDrawer({ id, onClose }: RequestDrawerProps): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ export interface SearchInputProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ placeholder?: string;
5
+ width?: number;
6
+ }
7
+ /** Text filter with a leading search glyph. */
8
+ export declare function SearchInput({ value, onChange, placeholder, width }: SearchInputProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from "react";
2
+ export interface SegmentedOption<T extends string> {
3
+ value: T;
4
+ label: ReactNode;
5
+ title?: string;
6
+ }
7
+ export interface SegmentedProps<T extends string> {
8
+ options: readonly SegmentedOption<T>[];
9
+ value: T;
10
+ onChange: (value: T) => void;
11
+ size?: "md" | "sm";
12
+ "aria-label"?: string;
13
+ }
14
+ /** Pill toggle with a measured sliding thumb. */
15
+ export declare function Segmented<T extends string>({ options, value, onChange, size, "aria-label": ariaLabel, }: SegmentedProps<T>): import("react").JSX.Element;
@@ -0,0 +1,36 @@
1
+ import { type ReactNode } from "react";
2
+ export interface StatDelta {
3
+ /** Relative change vs the previous period (0.12 = +12%). `null` when there is no baseline. */
4
+ change: number | null;
5
+ /** Which direction is good news. Default `"up"`. */
6
+ good?: "up" | "down";
7
+ }
8
+ export interface StatProps {
9
+ label: ReactNode;
10
+ value: ReactNode;
11
+ /** Small print under the value. */
12
+ hint?: ReactNode;
13
+ delta?: StatDelta;
14
+ /** Trend under the value. */
15
+ spark?: readonly number[];
16
+ sparkColor?: string;
17
+ size?: "md" | "sm";
18
+ /** Title tooltip explaining the metric. */
19
+ title?: string;
20
+ }
21
+ /**
22
+ * One metric tile. The value flashes briefly when it changes after mount, so
23
+ * live updates are noticeable without being loud.
24
+ */
25
+ export declare function Stat({ label, value, hint, delta, spark, sparkColor, size, title }: StatProps): import("react").JSX.Element;
26
+ export interface StatGridProps {
27
+ /** Minimum tile width before wrapping. Default 170px. */
28
+ min?: number;
29
+ children: ReactNode;
30
+ }
31
+ /** Tiles sharing one card, separated by hairlines. */
32
+ export declare function StatGrid({ min, children }: StatGridProps): import("react").JSX.Element;
33
+ /** Signed percentage change pill, green when the move is good news. */
34
+ export declare function Delta({ change, good }: StatDelta): import("react").JSX.Element;
35
+ /** Relative change from `previous` to `current`; `null` when there is no baseline. */
36
+ export declare function relativeChange(current: number, previous: number): number | null;
@@ -0,0 +1,41 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ export declare function EmptyState({ title, hint }: {
3
+ title?: ReactNode;
4
+ hint?: ReactNode;
5
+ }): import("react").JSX.Element;
6
+ export declare function ErrorState({ error, onRetry }: {
7
+ error: Error | string;
8
+ onRetry?: () => void;
9
+ }): import("react").JSX.Element;
10
+ export declare function Skeleton({ width, height, style, }: {
11
+ width?: number | string;
12
+ height?: number | string;
13
+ style?: CSSProperties;
14
+ }): import("react").JSX.Element;
15
+ /** Placeholder block matching a chart's footprint. */
16
+ export declare function ChartSkeleton({ height }: {
17
+ height?: number;
18
+ }): import("react").JSX.Element;
19
+ /** Placeholder rows matching a table's footprint. */
20
+ export declare function TableSkeleton({ rows }: {
21
+ rows?: number;
22
+ }): import("react").JSX.Element;
23
+ export interface QueryViewProps<T> {
24
+ query: {
25
+ data: T | null;
26
+ error: Error | null;
27
+ loading: boolean;
28
+ refetch: () => void;
29
+ };
30
+ /** Rendered on first load. */
31
+ skeleton: ReactNode;
32
+ /** Treat loaded data as empty (renders `empty`). */
33
+ isEmpty?: (data: T) => boolean;
34
+ empty?: ReactNode;
35
+ children: (data: T) => ReactNode;
36
+ }
37
+ /**
38
+ * Standard load/error/empty switch for one `useQuery` result. Errors with
39
+ * data already on screen keep the data and show the error above it.
40
+ */
41
+ export declare function QueryView<T>({ query, skeleton, isEmpty, empty, children }: QueryViewProps<T>): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { type ReactNode } from "react";
2
+ export interface Column<T> {
3
+ key: string;
4
+ header: ReactNode;
5
+ render: (row: T) => ReactNode;
6
+ /** Sort key; the column header becomes clickable when present. */
7
+ sort?: (row: T) => number | string;
8
+ align?: "left" | "right" | "center";
9
+ width?: number | string;
10
+ /** Allow the cell to wrap (cells are single-line by default). */
11
+ wrap?: boolean;
12
+ /** Header tooltip. */
13
+ title?: string;
14
+ }
15
+ export type SortDir = "asc" | "desc";
16
+ export interface TableProps<T> {
17
+ columns: readonly Column<T>[];
18
+ rows: readonly T[];
19
+ rowKey: (row: T) => string | number;
20
+ onRowClick?: (row: T) => void;
21
+ selectedKey?: string | number | null;
22
+ /** Initial sort; numeric columns usually start `desc`. */
23
+ initialSort?: {
24
+ key: string;
25
+ dir: SortDir;
26
+ };
27
+ /** Show the first `limit` rows; "Show more" reveals `limit` × 4 more at a time (rendering stays cheap for huge tables). */
28
+ limit?: number;
29
+ dense?: boolean;
30
+ empty?: ReactNode;
31
+ /** Detail row rendered below a row (return `null` for collapsed rows). */
32
+ expanded?: (row: T) => ReactNode | null;
33
+ }
34
+ /** Sortable data table. Clicking a sorted header flips its direction. */
35
+ export declare function Table<T>({ columns, rows, rowKey, onRowClick, selectedKey, initialSort, limit, dense, empty, expanded, }: TableProps<T>): import("react").JSX.Element;
36
+ /** Right-aligned value with a proportional bar, for ranked numeric columns. */
37
+ export declare function MeterCell({ value, max, display, color, }: {
38
+ value: number;
39
+ max: number;
40
+ display: ReactNode;
41
+ color?: string;
42
+ }): import("react").JSX.Element;
43
+ /** Two-line cell: primary label and a dim secondary line. */
44
+ export declare function LabelCell({ primary, secondary, lead, }: {
45
+ primary: ReactNode;
46
+ secondary?: ReactNode;
47
+ lead?: ReactNode;
48
+ }): import("react").JSX.Element;