@agent-native/toolkit 0.198.2 → 0.198.3-nightly-20260930124743

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 (110) hide show
  1. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  2. package/dist/app/agentkit/react/components.js +4 -1
  3. package/dist/app/agentkit/react/components.js.map +1 -1
  4. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  5. package/dist/app/chat/AgentKitAssistantChat.js +20 -6
  6. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  7. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  8. package/dist/app/i18n/catalogs/ar-SA.js +211 -0
  9. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  10. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  11. package/dist/app/i18n/catalogs/de-DE.js +147 -0
  12. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  13. package/dist/app/i18n/catalogs/en-US.d.ts +147 -0
  14. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  15. package/dist/app/i18n/catalogs/en-US.js +147 -0
  16. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  17. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  18. package/dist/app/i18n/catalogs/es-ES.js +163 -0
  19. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  20. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  21. package/dist/app/i18n/catalogs/fr-FR.js +163 -0
  22. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  23. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  24. package/dist/app/i18n/catalogs/hi-IN.js +147 -0
  25. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  26. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  27. package/dist/app/i18n/catalogs/ja-JP.js +131 -0
  28. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  29. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  30. package/dist/app/i18n/catalogs/ko-KR.js +131 -0
  31. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  32. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/pt-BR.js +163 -0
  34. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  35. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/zh-CN.js +131 -0
  37. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  38. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  39. package/dist/app/i18n/catalogs/zh-TW.js +131 -0
  40. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  41. package/dist/app/observability/ObservabilityDashboard.d.ts.map +1 -1
  42. package/dist/app/observability/ObservabilityDashboard.js +101 -109
  43. package/dist/app/observability/ObservabilityDashboard.js.map +1 -1
  44. package/dist/app/observability/RunInsights.d.ts +22 -0
  45. package/dist/app/observability/RunInsights.d.ts.map +1 -0
  46. package/dist/app/observability/RunInsights.js +658 -0
  47. package/dist/app/observability/RunInsights.js.map +1 -0
  48. package/package.json +5 -5
  49. package/src/app/agent-page/ConnectionsTab.spec.tsx +0 -3
  50. package/src/app/agentkit/react/components.tsx +4 -1
  51. package/src/app/agentkit/react/composer-context.spec.tsx +4 -1
  52. package/src/app/chat/AgentKitAssistantChat.spec.tsx +57 -0
  53. package/src/app/chat/AgentKitAssistantChat.tsx +44 -8
  54. package/src/app/chat/AgentPanel.header.spec.ts +22 -22
  55. package/src/app/chat/agentkit-chat/action-widget.spec.tsx +17 -2
  56. package/src/app/chat/agentkit-chat/approval-card.spec.tsx +2 -2
  57. package/src/app/chat/agentkit-chat/connections.spec.tsx +1 -1
  58. package/src/app/chat/chat/markdown-renderer.spec.tsx +1 -1
  59. package/src/app/chat/chat/tool-call-display.spec.tsx +25 -2
  60. package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +38 -26
  61. package/src/app/chat/chat/widgets/agent-team-progress-widget.spec.tsx +7 -1
  62. package/src/app/chat/chat/widgets/builtin-connect-required-renderer.spec.tsx +24 -1
  63. package/src/app/chat/chat/widgets/record-change-widget.spec.tsx +16 -1
  64. package/src/app/chat/thinking-display.spec.tsx +1 -1
  65. package/src/app/db-admin/useAgentSync.spec.tsx +1 -1
  66. package/src/app/extensions/ExtensionViewer.spec.tsx +2 -1
  67. package/src/app/i18n/catalogs/ar-SA.ts +282 -0
  68. package/src/app/i18n/catalogs/de-DE.ts +198 -0
  69. package/src/app/i18n/catalogs/en-US.ts +187 -0
  70. package/src/app/i18n/catalogs/es-ES.ts +221 -0
  71. package/src/app/i18n/catalogs/fr-FR.ts +219 -0
  72. package/src/app/i18n/catalogs/hi-IN.ts +182 -0
  73. package/src/app/i18n/catalogs/ja-JP.ts +160 -0
  74. package/src/app/i18n/catalogs/ko-KR.ts +160 -0
  75. package/src/app/i18n/catalogs/pt-BR.ts +220 -0
  76. package/src/app/i18n/catalogs/zh-CN.ts +154 -0
  77. package/src/app/i18n/catalogs/zh-TW.ts +154 -0
  78. package/src/app/integrations/BuilderIntegrationPage.spec.tsx +3 -3
  79. package/src/app/integrations/ChannelsPage.spec.tsx +3 -3
  80. package/src/app/integrations/IntegrationsPage.spec.tsx +3 -3
  81. package/src/app/integrations/IntegrationsPanel.spec.tsx +7 -4
  82. package/src/app/observability/ObservabilityDashboard.review.spec.tsx +211 -52
  83. package/src/app/observability/ObservabilityDashboard.spec.tsx +80 -20
  84. package/src/app/observability/ObservabilityDashboard.tsx +240 -400
  85. package/src/app/observability/RunInsights.tsx +1603 -0
  86. package/src/app/observability/ThumbsFeedback.spec.tsx +29 -12
  87. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +25 -5
  88. package/src/app/onboarding/first-run-startup-gate.spec.tsx +12 -7
  89. package/src/app/resources/McpConnectionSuggestion.render.spec.tsx +43 -5
  90. package/src/app/resources/McpIntegrationDialog.spec.tsx +20 -14
  91. package/src/app/resources/ResourcesPanel.spec.ts +9 -7
  92. package/src/app/review/ReviewThreadPanel.spec.tsx +2 -2
  93. package/src/app/settings/SecretsSection.spec.tsx +12 -3
  94. package/src/app/settings/SettingsTabsPage.spec.tsx +4 -1
  95. package/src/app/settings/VoiceTranscriptionSection.spec.tsx +12 -1
  96. package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -1
  97. package/src/app/settings/account/ProfileSettings.spec.tsx +13 -1
  98. package/src/app/settings/account/SecuritySettings.spec.tsx +12 -1
  99. package/src/app/settings/shell/SettingsShell.spec.tsx +35 -15
  100. package/src/app/settings/shell/pages/app-group-pages.spec.tsx +7 -0
  101. package/src/app/settings/shell/pages/audit.spec.tsx +5 -2
  102. package/src/app/settings/shell/pages/resource-pages.spec.tsx +25 -16
  103. package/src/app/settings/shell/pages/sub-agents.spec.tsx +24 -15
  104. package/src/app/settings/usage/UsagePage.spec.tsx +4 -0
  105. package/src/app/setup-connections/BuilderConnectCard.spec.tsx +27 -8
  106. package/src/app/sharing/ShareButton.spec.tsx +7 -3
  107. package/src/app/sharing/ShareDialog.spec.tsx +4 -4
  108. package/src/app/sharing/useShareButtonController.spec.tsx +1 -1
  109. package/src/app/sharing/useShareDialogController.spec.tsx +2 -2
  110. package/src/app/transcription/BuilderTranscriptionCta.spec.tsx +1 -1
@@ -0,0 +1,658 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { requestAgentChatThreadOpen } from "@agent-native/core/client/agent-chat";
3
+ import { useActionQuery } from "@agent-native/core/client/hooks";
4
+ import { useT } from "@agent-native/core/client/i18n";
5
+ import { Skeleton } from "@agent-native/toolkit/design-system";
6
+ import { Button } from "@agent-native/toolkit/ui/button";
7
+ import { Sheet, SheetContent, SheetDescription, SheetTitle, } from "@agent-native/toolkit/ui/sheet";
8
+ import { IconAlertTriangle, IconChevronDown, IconChevronUp, IconCircleCheck, IconCircleX, IconInfoCircle, IconMessages, IconPigMoney, IconThumbDown, IconThumbUp, IconX, } from "@tabler/icons-react";
9
+ import { useEffect, useMemo, useState } from "react";
10
+ // Apps don't share chart tokens, and this set is checked for colour-blind separation in both themes.
11
+ const LIGHT_PALETTE =
12
+ // guard:allow-raw-color — validated categorical palette, light surface
13
+ "--usage-cache-read: #2a78d6; --usage-cache-write: #eb6834; --usage-fresh-input: #1baf7a; --usage-output: #eda100;";
14
+ const DARK_PALETTE =
15
+ // guard:allow-raw-color — same palette stepped for the dark surface
16
+ "--usage-cache-read: #3987e5; --usage-cache-write: #d95926; --usage-fresh-input: #199e70; --usage-output: #c98500;";
17
+ const PALETTE_CSS = `.run-insights, .run-insights-panel { ${LIGHT_PALETTE} } .dark .run-insights, .dark .run-insights-panel { ${DARK_PALETTE} }`;
18
+ const WRITE_VERBS = new Set([
19
+ "add",
20
+ "apply",
21
+ "create",
22
+ "delete",
23
+ "duplicate",
24
+ "edit",
25
+ "export",
26
+ "generate",
27
+ "insert",
28
+ "move",
29
+ "remove",
30
+ "rename",
31
+ "save",
32
+ "send",
33
+ "set",
34
+ "update",
35
+ "upload",
36
+ "write",
37
+ ]);
38
+ function verbLabel(t, verb, object) {
39
+ const labels = {
40
+ add: () => t("agentChat.observability.insights.toolVerb.add", { object }),
41
+ analyze: () => t("agentChat.observability.insights.toolVerb.analyze", { object }),
42
+ apply: () => t("agentChat.observability.insights.toolVerb.apply", { object }),
43
+ capture: () => t("agentChat.observability.insights.toolVerb.capture", { object }),
44
+ check: () => t("agentChat.observability.insights.toolVerb.check", { object }),
45
+ connect: () => t("agentChat.observability.insights.toolVerb.connect", { object }),
46
+ consume: () => t("agentChat.observability.insights.toolVerb.read", { object }),
47
+ create: () => t("agentChat.observability.insights.toolVerb.create", { object }),
48
+ delete: () => t("agentChat.observability.insights.toolVerb.delete", { object }),
49
+ duplicate: () => t("agentChat.observability.insights.toolVerb.duplicate", { object }),
50
+ edit: () => t("agentChat.observability.insights.toolVerb.edit", { object }),
51
+ export: () => t("agentChat.observability.insights.toolVerb.export", { object }),
52
+ fetch: () => t("agentChat.observability.insights.toolVerb.fetch", { object }),
53
+ find: () => t("agentChat.observability.insights.toolVerb.find", { object }),
54
+ generate: () => t("agentChat.observability.insights.toolVerb.generate", { object }),
55
+ get: () => t("agentChat.observability.insights.toolVerb.read", { object }),
56
+ index: () => t("agentChat.observability.insights.toolVerb.index", { object }),
57
+ insert: () => t("agentChat.observability.insights.toolVerb.insert", { object }),
58
+ list: () => t("agentChat.observability.insights.toolVerb.list", { object }),
59
+ move: () => t("agentChat.observability.insights.toolVerb.move", { object }),
60
+ navigate: () => t("agentChat.observability.insights.toolVerb.navigate", { object }),
61
+ open: () => t("agentChat.observability.insights.toolVerb.open", { object }),
62
+ present: () => t("agentChat.observability.insights.toolVerb.present", { object }),
63
+ propose: () => t("agentChat.observability.insights.toolVerb.propose", { object }),
64
+ query: () => t("agentChat.observability.insights.toolVerb.query", { object }),
65
+ read: () => t("agentChat.observability.insights.toolVerb.read", { object }),
66
+ remove: () => t("agentChat.observability.insights.toolVerb.remove", { object }),
67
+ rename: () => t("agentChat.observability.insights.toolVerb.rename", { object }),
68
+ reply: () => t("agentChat.observability.insights.toolVerb.reply", { object }),
69
+ resolve: () => t("agentChat.observability.insights.toolVerb.resolve", { object }),
70
+ run: () => t("agentChat.observability.insights.toolVerb.run", { object }),
71
+ save: () => t("agentChat.observability.insights.toolVerb.save", { object }),
72
+ search: () => t("agentChat.observability.insights.toolVerb.search", { object }),
73
+ send: () => t("agentChat.observability.insights.toolVerb.send", { object }),
74
+ set: () => t("agentChat.observability.insights.toolVerb.set", { object }),
75
+ take: () => t("agentChat.observability.insights.toolVerb.take", { object }),
76
+ update: () => t("agentChat.observability.insights.toolVerb.update", { object }),
77
+ upload: () => t("agentChat.observability.insights.toolVerb.upload", { object }),
78
+ view: () => t("agentChat.observability.insights.toolVerb.view", { object }),
79
+ write: () => t("agentChat.observability.insights.toolVerb.write", { object }),
80
+ };
81
+ return labels[verb]?.() ?? null;
82
+ }
83
+ function toolWords(name) {
84
+ return name
85
+ .replace(/^mcp__[^_]+__/, "")
86
+ .split(/[-_]/)
87
+ .filter(Boolean);
88
+ }
89
+ function toolVerb(name) {
90
+ const words = toolWords(name);
91
+ if (WRITE_VERBS.has(words[0]) || READ_VERBS.has(words[0]))
92
+ return words[0];
93
+ const last = words.at(-1);
94
+ if (WRITE_VERBS.has(last) || READ_VERBS.has(last))
95
+ return last;
96
+ return null;
97
+ }
98
+ const READ_VERBS = new Set([
99
+ "analyze",
100
+ "capture",
101
+ "check",
102
+ "connect",
103
+ "consume",
104
+ "fetch",
105
+ "find",
106
+ "get",
107
+ "index",
108
+ "list",
109
+ "navigate",
110
+ "open",
111
+ "present",
112
+ "propose",
113
+ "query",
114
+ "read",
115
+ "reply",
116
+ "resolve",
117
+ "run",
118
+ "search",
119
+ "take",
120
+ "view",
121
+ ]);
122
+ /** "edit-design" → "Edited design", "docs-search" → "Searched docs". */
123
+ function humanizeTool(t, name) {
124
+ if (name === "tool-search") {
125
+ return t("agentChat.observability.insights.lookedForTools");
126
+ }
127
+ const words = toolWords(name);
128
+ const verb = toolVerb(name);
129
+ if (!verb)
130
+ return words.join(" ");
131
+ const object = (words[0] === verb ? words.slice(1) : words.slice(0, -1)).join(" ");
132
+ return verbLabel(t, verb, object) ?? words.join(" ");
133
+ }
134
+ function toolNoun(name) {
135
+ return toolWords(name).join(" ");
136
+ }
137
+ function listTools(t, tools) {
138
+ return tools
139
+ .map((tool) => tool.calls > 1
140
+ ? t("agentChat.observability.insights.timesCount", {
141
+ label: humanizeTool(t, tool.name),
142
+ count: tool.calls,
143
+ })
144
+ : humanizeTool(t, tool.name))
145
+ .join(", ");
146
+ }
147
+ function outcomeLine(t, tools) {
148
+ const writes = tools.filter((tool) => WRITE_VERBS.has(toolVerb(tool.name) ?? "") && tool.calls > tool.failed);
149
+ if (writes.length === 0)
150
+ return null;
151
+ return listTools(t, writes.slice(0, 4));
152
+ }
153
+ function turnLabel(t, turn, isLast) {
154
+ if (turn.toolCalls.length === 0) {
155
+ return isLast
156
+ ? t("agentChat.observability.insights.turnReply")
157
+ : t("agentChat.observability.insights.turnThought");
158
+ }
159
+ const counts = new Map();
160
+ for (const call of turn.toolCalls) {
161
+ counts.set(call.name, (counts.get(call.name) ?? 0) + 1);
162
+ }
163
+ return listTools(t, [...counts].map(([name, calls]) => ({ name, calls })));
164
+ }
165
+ function formatUsd(cents) {
166
+ if (cents <= 0)
167
+ return "$0";
168
+ if (cents < 1)
169
+ return "<1¢";
170
+ if (cents < 100)
171
+ return `${Math.round(cents)}¢`;
172
+ return `$${(cents / 100).toLocaleString(undefined, {
173
+ minimumFractionDigits: 2,
174
+ maximumFractionDigits: 2,
175
+ })}`;
176
+ }
177
+ function formatDuration(ms) {
178
+ if (ms < 1000)
179
+ return `${Math.max(1, Math.round(ms))}ms`;
180
+ if (ms < 60_000)
181
+ return `${(ms / 1000).toFixed(1)}s`;
182
+ return `${Math.floor(ms / 60_000)}m ${Math.round((ms % 60_000) / 1000)}s`;
183
+ }
184
+ function formatTime(ms) {
185
+ return new Date(ms).toLocaleString(undefined, {
186
+ month: "short",
187
+ day: "numeric",
188
+ hour: "numeric",
189
+ minute: "2-digit",
190
+ });
191
+ }
192
+ function compactTokens(value) {
193
+ if (value >= 1_000_000)
194
+ return `${(value / 1_000_000).toFixed(1)}M`;
195
+ if (value >= 1_000)
196
+ return `${(value / 1_000).toFixed(1)}K`;
197
+ return Math.round(value).toLocaleString();
198
+ }
199
+ function percent(part, whole) {
200
+ return whole > 0 ? Math.round((part / whole) * 100) : 0;
201
+ }
202
+ /** Restarts worth mentioning: at least 10% of what the prompt cost. */
203
+ function notableRestart(run) {
204
+ return (run.restarts.cents > 0 &&
205
+ run.restarts.cents >= run.cost.estimatedCents * 0.1);
206
+ }
207
+ function failedTools(run) {
208
+ return run.tools.filter((tool) => tool.failed > 0);
209
+ }
210
+ function mainRestartCause(run) {
211
+ const { byCause } = run.restarts;
212
+ return byCause["tool-lookup"].cents >= byCause["prefix-changed"].cents
213
+ ? "tool-lookup"
214
+ : "prefix-changed";
215
+ }
216
+ function restartReason(t, cause) {
217
+ return cause === "tool-lookup"
218
+ ? t("agentChat.observability.insights.reasonToolLookup")
219
+ : t("agentChat.observability.insights.reasonPrefixChanged");
220
+ }
221
+ function restartFix(t, cause) {
222
+ return cause === "tool-lookup"
223
+ ? t("agentChat.observability.insights.fixToolLookup")
224
+ : t("agentChat.observability.insights.fixPrefixChanged");
225
+ }
226
+ function buildInsights(t, runs) {
227
+ const total = runs.reduce((sum, run) => sum + run.cost.estimatedCents, 0);
228
+ const insights = [];
229
+ const errored = runs.filter((run) => run.status === "error");
230
+ if (errored.length > 0) {
231
+ insights.push({
232
+ key: "errored",
233
+ kind: "problem",
234
+ title: t("agentChat.observability.insights.erroredTitle", {
235
+ count: errored.length,
236
+ }),
237
+ body: t("agentChat.observability.insights.erroredBody"),
238
+ fix: t("agentChat.observability.insights.erroredFix"),
239
+ runIds: errored.map((run) => run.runId),
240
+ });
241
+ }
242
+ const toolFailures = new Map();
243
+ for (const run of runs) {
244
+ for (const tool of failedTools(run)) {
245
+ const entry = toolFailures.get(tool.name) ?? {
246
+ error: tool.error,
247
+ runIds: [],
248
+ };
249
+ entry.runIds.push(run.runId);
250
+ toolFailures.set(tool.name, entry);
251
+ }
252
+ }
253
+ const [worstTool] = [...toolFailures].sort((a, b) => b[1].runIds.length - a[1].runIds.length);
254
+ if (worstTool) {
255
+ const [name, { error, runIds }] = worstTool;
256
+ const recovered = runs.filter((run) => runIds.includes(run.runId) && run.status === "success").length;
257
+ const said = error
258
+ ? t("agentChat.observability.insights.toolFailedSaid", {
259
+ error: error.length > 180 ? `${error.slice(0, 179)}…` : error,
260
+ })
261
+ : t("agentChat.observability.insights.toolFailedGeneric");
262
+ const recovery = recovered === 0
263
+ ? ""
264
+ : recovered === runIds.length
265
+ ? t("agentChat.observability.insights.toolRecoveredAll")
266
+ : t("agentChat.observability.insights.toolRecoveredSome", {
267
+ count: recovered,
268
+ });
269
+ insights.push({
270
+ key: `tool-${name}`,
271
+ kind: "problem",
272
+ title: t("agentChat.observability.insights.toolFailedTitle", {
273
+ tool: toolNoun(name),
274
+ count: runIds.length,
275
+ }),
276
+ body: recovery ? `${said} ${recovery}` : said,
277
+ runIds,
278
+ });
279
+ }
280
+ for (const cause of ["tool-lookup", "prefix-changed"]) {
281
+ const affected = runs.filter((run) => run.restarts.byCause[cause].cents > 0);
282
+ const cents = affected.reduce((sum, run) => sum + run.restarts.byCause[cause].cents, 0);
283
+ if (cents >= Math.max(5, total * 0.1)) {
284
+ insights.push({
285
+ key: `restart-${cause}`,
286
+ kind: "saving",
287
+ title: t("agentChat.observability.insights.restartTitle", {
288
+ amount: formatUsd(cents),
289
+ percent: percent(cents, total),
290
+ }),
291
+ body: t("agentChat.observability.insights.restartBody", {
292
+ count: affected.length,
293
+ total: runs.length,
294
+ reason: restartReason(t, cause),
295
+ }),
296
+ fix: restartFix(t, cause),
297
+ runIds: affected.map((run) => run.runId),
298
+ });
299
+ }
300
+ }
301
+ const spend = runs.reduce((sum, run) => sum + run.cost.totalCents, 0);
302
+ const priciest = [...runs].sort((a, b) => b.cost.totalCents - a.cost.totalCents)[0];
303
+ if (runs.length >= 3 &&
304
+ priciest &&
305
+ spend >= 50 &&
306
+ priciest.cost.totalCents > spend * 0.4) {
307
+ insights.push({
308
+ key: "priciest",
309
+ kind: "info",
310
+ title: t("agentChat.observability.insights.priciestTitle", {
311
+ percent: percent(priciest.cost.totalCents, spend),
312
+ }),
313
+ body: t("agentChat.observability.insights.priciestBody", {
314
+ prompt: priciest.prompt ??
315
+ t("agentChat.observability.insights.untitledPrompt"),
316
+ amount: formatUsd(priciest.cost.totalCents),
317
+ count: priciest.modelCalls,
318
+ }),
319
+ runIds: [priciest.runId],
320
+ });
321
+ }
322
+ return insights.slice(0, 3);
323
+ }
324
+ function InsightCard({ insight, onShow, }) {
325
+ const t = useT();
326
+ const style = {
327
+ problem: {
328
+ icon: (_jsx(IconAlertTriangle, { className: "size-4 text-amber-600 dark:text-amber-400" })),
329
+ label: t("agentChat.observability.insights.kindProblem"),
330
+ className: "border-amber-500/30",
331
+ },
332
+ saving: {
333
+ icon: (_jsx(IconPigMoney, { className: "size-4 text-emerald-600 dark:text-emerald-400" })),
334
+ label: t("agentChat.observability.insights.kindSaving"),
335
+ className: "border-emerald-500/30",
336
+ },
337
+ info: {
338
+ icon: _jsx(IconInfoCircle, { className: "size-4 text-muted-foreground" }),
339
+ label: t("agentChat.observability.insights.kindInfo"),
340
+ className: "border-border/70",
341
+ },
342
+ };
343
+ const kind = style[insight.kind];
344
+ return (_jsxs("div", { className: `rounded-lg border bg-card p-4 ${kind.className}`, children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium uppercase tracking-[0.06em] text-muted-foreground", children: [kind.icon, kind.label] }), _jsx("h4", { className: "mt-1.5 text-sm font-semibold leading-5 text-foreground", children: insight.title }), _jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: insight.body }), insight.fix ? (_jsxs("p", { className: "mt-1.5 text-xs leading-5 text-foreground/90", children: [_jsxs("span", { className: "font-medium", children: [t("agentChat.observability.insights.fixLabel"), " "] }), insight.fix] })) : null, _jsx(Button, { type: "button", variant: "link", size: "sm", className: "mt-1 h-auto p-0 text-xs", onClick: onShow, children: insight.runIds.length === 1
345
+ ? t("agentChat.observability.insights.openPrompt")
346
+ : t("agentChat.observability.insights.seePrompts", {
347
+ count: insight.runIds.length,
348
+ }) })] }));
349
+ }
350
+ function RunRow({ run, onOpen }) {
351
+ const t = useT();
352
+ const outcome = outcomeLine(t, run.tools);
353
+ const failures = failedTools(run);
354
+ return (_jsxs("button", { type: "button", onClick: onOpen, className: "flex w-full items-start justify-between gap-4 px-4 py-3 text-left transition-colors hover:bg-accent/40 focus-visible:bg-accent/40 focus-visible:outline-none", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate text-sm text-foreground", children: run.prompt ?? t("agentChat.observability.insights.promptNotSaved") }), run.feedback === "up" ? (_jsx(IconThumbUp, { "aria-label": t("agentChat.observability.insights.ratedHelpful"), className: "size-3.5 shrink-0 text-muted-foreground" })) : run.feedback === "down" ? (_jsx(IconThumbDown, { "aria-label": t("agentChat.observability.insights.ratedUnhelpful"), className: "size-3.5 shrink-0 text-muted-foreground" })) : null] }), _jsxs("div", { className: "mt-0.5 flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground", children: [run.status === "error" ? (_jsxs("span", { className: "flex min-w-0 items-center gap-1 text-destructive", children: [_jsx(IconCircleX, { "aria-hidden": true, className: "size-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: t("agentChat.observability.insights.stoppedWithError") })] })) : run.status === "unknown" ? (_jsx("span", { className: "truncate", children: t("agentChat.observability.insights.detailsUnavailable") })) : (_jsxs("span", { className: "truncate", children: ["\u2192 ", outcome ?? t("agentChat.observability.insights.answered")] })), notableRestart(run) ? (_jsxs("span", { className: "shrink-0 text-amber-700 dark:text-amber-400", children: ["\u00B7", " ", t("agentChat.observability.insights.startedOverShort", {
355
+ count: run.restarts.count,
356
+ })] })) : run.recoveredErrors > 0 ? (_jsxs("span", { className: "shrink-0", children: ["\u00B7", " ", t("agentChat.observability.insights.recoveredShort", {
357
+ count: run.recoveredErrors,
358
+ })] })) : failures.length > 0 ? (_jsxs("span", { className: "shrink-0 text-amber-700 dark:text-amber-400", children: ["\u00B7", " ", t("agentChat.observability.insights.toolsFailedShort", {
359
+ count: failures.length,
360
+ })] })) : null] })] }), _jsxs("div", { className: "shrink-0 text-right", children: [_jsx("div", { className: "text-sm font-medium tabular-nums text-foreground", children: formatUsd(run.cost.totalCents) }), _jsx("div", { className: "text-xs tabular-nums text-muted-foreground", children: run.durationMs
361
+ ? formatDuration(run.durationMs)
362
+ : formatTime(run.createdAt) })] })] }));
363
+ }
364
+ function CostDetails({ run }) {
365
+ const t = useT();
366
+ const parts = [
367
+ {
368
+ label: t("agentChat.observability.insights.partReused"),
369
+ cents: run.cost.cacheReadCents,
370
+ tokens: run.tokens.cacheReadTokens,
371
+ color: "var(--usage-cache-read)",
372
+ },
373
+ {
374
+ label: t("agentChat.observability.insights.partSaved"),
375
+ cents: run.cost.cacheWriteCents,
376
+ tokens: run.tokens.cacheWriteTokens,
377
+ color: "var(--usage-cache-write)",
378
+ },
379
+ {
380
+ label: t("agentChat.observability.insights.partNew"),
381
+ cents: run.cost.uncachedInputCents,
382
+ tokens: Math.max(0, run.tokens.inputTokens -
383
+ run.tokens.cacheReadTokens -
384
+ run.tokens.cacheWriteTokens),
385
+ color: "var(--usage-fresh-input)",
386
+ },
387
+ {
388
+ label: t("agentChat.observability.insights.partOutput"),
389
+ cents: run.cost.outputCents,
390
+ tokens: run.tokens.outputTokens,
391
+ color: "var(--usage-output)",
392
+ },
393
+ ];
394
+ const checks = run.scores.filter((score) => score.source === "heuristic");
395
+ const judged = run.scores.filter((score) => score.source === "judge");
396
+ const estimated = run.cost.estimatedCents;
397
+ return (_jsxs("div", { className: "space-y-4 text-xs", children: [_jsxs("div", { children: [formatUsd(run.cost.noCacheCents) !== formatUsd(estimated) ? (_jsx("p", { className: "text-muted-foreground", children: t("agentChat.observability.insights.noCacheCompare", {
398
+ noCache: formatUsd(run.cost.noCacheCents),
399
+ estimated: formatUsd(estimated),
400
+ }) })) : null, _jsx("div", { className: "mt-2 flex h-2 w-full gap-[2px] overflow-hidden rounded-full", children: parts.map((part) => part.cents > 0 && estimated > 0 ? (_jsx("div", { style: {
401
+ width: `${(part.cents / estimated) * 100}%`,
402
+ minWidth: 3,
403
+ background: part.color,
404
+ } }, part.label)) : null) }), _jsx("div", { className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1.5", children: parts.map((part) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-[2px]", style: { background: part.color } }), _jsx("span", { className: "text-muted-foreground", children: part.label }), _jsx("span", { className: "ml-auto tabular-nums text-foreground", children: formatUsd(part.cents) }), _jsx("span", { className: "w-12 text-right tabular-nums text-muted-foreground", children: compactTokens(part.tokens) })] }, part.label))) })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium text-foreground", children: t("agentChat.observability.insights.checksHeading") }), judged.length === 0 && checks.length === 0 ? (_jsx("p", { className: "mt-1 text-muted-foreground", children: t("agentChat.observability.insights.checksNone") })) : (_jsx("ul", { className: "mt-1 space-y-0.5 text-muted-foreground", children: [...judged, ...checks].map((score) => (_jsxs("li", { className: "flex justify-between gap-4", children: [_jsxs("span", { children: [score.criteria.replaceAll("_", " "), score.source === "judge"
405
+ ? ` ${t("agentChat.observability.insights.checksGraded")}`
406
+ : ""] }), _jsxs("span", { className: "tabular-nums", children: [Math.round(score.score * 100), "%"] })] }, score.criteria))) })), _jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: t("agentChat.observability.insights.checksNote") })] })] }));
407
+ }
408
+ function StepList({ turns }) {
409
+ const t = useT();
410
+ const [openTurn, setOpenTurn] = useState(null);
411
+ return (_jsx("ol", { className: "mt-2 divide-y divide-border/50 rounded-lg border border-border/70", children: turns.map((turn, index) => {
412
+ const open = openTurn === turn.index;
413
+ const failed = turn.toolCalls.some((call) => call.status === "error");
414
+ return (_jsxs("li", { children: [_jsxs("button", { type: "button", className: "flex w-full items-center gap-3 px-3 py-2 text-left text-xs hover:bg-accent/40", onClick: () => setOpenTurn(open ? null : turn.index), "aria-expanded": open, children: [_jsx("span", { className: "w-5 shrink-0 text-right tabular-nums text-muted-foreground", children: turn.index }), _jsx("span", { className: "min-w-0 flex-1 truncate text-foreground", children: turnLabel(t, turn, index === turns.length - 1) }), turn.restart ? (_jsx("span", { className: "shrink-0 text-amber-700 dark:text-amber-400", children: t("agentChat.observability.insights.startedOverTag") })) : null, failed ? (_jsx("span", { className: "shrink-0 text-destructive", children: t("agentChat.observability.insights.toolFailedTag") })) : null, _jsx("span", { className: "w-12 shrink-0 text-right tabular-nums text-muted-foreground", children: formatDuration(turn.durationMs) }), _jsx("span", { className: "w-10 shrink-0 text-right tabular-nums text-foreground", children: formatUsd(turn.cost.estimatedCents) })] }), open ? (_jsxs("div", { className: "space-y-1.5 bg-muted/30 px-3 py-2.5 pl-11 text-xs text-muted-foreground", children: [_jsxs("p", { children: [t("agentChat.observability.insights.turnContext", {
415
+ tokens: compactTokens(turn.tokens.inputTokens),
416
+ percent: percent(turn.tokens.cacheReadTokens, turn.tokens.inputTokens),
417
+ }), " ", turn.cacheExpired
418
+ ? `${t("agentChat.observability.insights.turnExpired")} `
419
+ : "", t("agentChat.observability.insights.turnOutput", {
420
+ tokens: compactTokens(turn.tokens.outputTokens),
421
+ })] }), turn.restart ? (_jsx("p", { className: "text-amber-700 dark:text-amber-400", children: t("agentChat.observability.insights.turnRestart", {
422
+ reason: restartReason(t, turn.restart.cause),
423
+ amount: formatUsd(turn.restart.cents),
424
+ }) })) : null, turn.toolCalls.map((call, callIndex) => (_jsxs("p", { className: call.status === "error" ? "text-destructive" : "", children: [humanizeTool(t, call.name), " \u00B7", " ", formatDuration(call.durationMs), call.status === "error" && call.errorMessage
425
+ ? ` · ${call.errorMessage}`
426
+ : ""] }, callIndex)))] })) : null] }, turn.index));
427
+ }) }));
428
+ }
429
+ function Expandable({ text, lines, limit, }) {
430
+ const t = useT();
431
+ const [full, setFull] = useState(false);
432
+ return (_jsxs(_Fragment, { children: [_jsx("p", { className: `whitespace-pre-wrap ${full ? "" : lines}`, children: text }), text.length > limit ? (_jsx("button", { type: "button", className: "mt-1 text-xs text-muted-foreground hover:text-foreground", onClick: () => setFull((value) => !value), children: full
433
+ ? t("agentChat.observability.insights.showLess")
434
+ : t("agentChat.observability.insights.showAll") })) : null] }));
435
+ }
436
+ function handledText(t, parallel, recovered) {
437
+ const lines = [];
438
+ if (parallel.savedMs >= 1000) {
439
+ lines.push(t("agentChat.observability.insights.handledParallel", {
440
+ count: parallel.calls,
441
+ duration: formatDuration(parallel.savedMs),
442
+ }));
443
+ }
444
+ if (recovered > 0) {
445
+ lines.push(t("agentChat.observability.insights.handledRecovered", {
446
+ count: recovered,
447
+ }));
448
+ }
449
+ return lines;
450
+ }
451
+ function HandledNote({ run }) {
452
+ const t = useT();
453
+ const lines = handledText(t, run.parallel, run.recoveredErrors);
454
+ if (lines.length === 0)
455
+ return null;
456
+ return (_jsxs("div", { className: "rounded-lg border border-emerald-500/30 bg-emerald-500/5 px-3.5 py-3 text-xs leading-5", children: [_jsx("p", { className: "font-medium text-foreground", children: t("agentChat.observability.insights.handledHeading") }), _jsx("ul", { className: "mt-0.5 space-y-0.5 text-muted-foreground", children: lines.map((line) => (_jsx("li", { children: line }, line))) })] }));
457
+ }
458
+ function RunPanel({ run, renderRawTrace, onClose, }) {
459
+ const t = useT();
460
+ const query = useActionQuery("get-usage-run", {
461
+ runId: run.runId,
462
+ scope: "me",
463
+ });
464
+ const [showSteps, setShowSteps] = useState(false);
465
+ const [showDetails, setShowDetails] = useState(false);
466
+ const [showRawTrace, setShowRawTrace] = useState(false);
467
+ const detail = query.data;
468
+ const failures = failedTools(run);
469
+ const outcome = outcomeLine(t, run.tools);
470
+ const cause = mainRestartCause(run);
471
+ const headline = run.status === "error"
472
+ ? t("agentChat.observability.insights.stoppedWithError")
473
+ : run.status === "unknown"
474
+ ? t("agentChat.observability.insights.detailsUnavailable")
475
+ : t("agentChat.observability.insights.finished");
476
+ return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "border-b border-border/70 px-5 pb-4 pt-5", children: [_jsxs("div", { className: "flex items-center gap-2 pr-20", children: [run.status === "error" ? (_jsx(IconCircleX, { className: "size-4 shrink-0 text-destructive" })) : run.status === "unknown" ? (_jsx(IconInfoCircle, { className: "size-4 shrink-0 text-muted-foreground" })) : (_jsx(IconCircleCheck, { className: "size-4 shrink-0 text-emerald-600 dark:text-emerald-400" })), _jsxs(SheetTitle, { className: "text-sm font-medium", children: [headline, run.durationMs
477
+ ? ` ${t("agentChat.observability.insights.headerDuration", {
478
+ duration: formatDuration(run.durationMs),
479
+ })}`
480
+ : "", " ", "\u00B7", " ", t("agentChat.observability.insights.stepsCount", {
481
+ count: run.modelCalls,
482
+ }), " ", "\u00B7 ", formatUsd(run.cost.totalCents)] })] }), _jsxs(SheetDescription, { className: "mt-1 text-xs", children: [formatTime(run.createdAt), " \u00B7 ", run.model, " \u00B7", " ", run.feedback === "up"
483
+ ? t("agentChat.observability.insights.ratedHelpful")
484
+ : run.feedback === "down"
485
+ ? t("agentChat.observability.insights.ratedUnhelpful")
486
+ : t("agentChat.observability.insights.notRated")] }), run.threadId ? (_jsxs("button", { type: "button", onClick: () => {
487
+ requestAgentChatThreadOpen({ threadId: run.threadId });
488
+ onClose();
489
+ }, className: "mt-2 flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground", children: [_jsx(IconMessages, { className: "size-3.5" }), t("observability.openFullConversation")] })) : null] }), _jsxs("div", { className: "flex-1 space-y-5 overflow-y-auto px-5 py-5", children: [_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "ml-auto max-w-[85%] rounded-2xl rounded-br-md bg-muted px-3.5 py-2.5 text-sm leading-6", children: _jsx(Expandable, { text: run.prompt ??
490
+ t("agentChat.observability.insights.promptNotSaved"), lines: "line-clamp-4", limit: 240 }) }), _jsxs("div", { className: "max-w-[92%] space-y-2", children: [outcome ? (_jsxs("p", { className: "text-xs text-muted-foreground", children: [_jsxs("span", { className: "font-medium text-foreground", children: [t("agentChat.observability.insights.whatItDid"), " "] }), outcome] })) : null, query.isLoading ? (_jsx(Skeleton, { className: "h-16 w-full" })) : detail?.reply ? (_jsx("div", { className: "rounded-2xl rounded-bl-md border border-border/70 px-3.5 py-2.5 text-sm leading-6", children: _jsx(Expandable, { text: detail.reply, lines: "line-clamp-6", limit: 400 }) })) : (_jsx("p", { className: "text-xs text-muted-foreground", children: t("agentChat.observability.insights.replyNotSaved") })), run.tools.length > 0 ? (_jsxs("div", { className: "flex flex-wrap gap-1.5 pt-1", children: [run.tools.slice(0, 8).map((tool) => (_jsxs("button", { type: "button", onClick: () => setShowSteps(true), className: `rounded-full border px-2 py-0.5 text-[11px] ${tool.failed ? "border-destructive/40 text-destructive" : "border-border/70 text-muted-foreground hover:text-foreground"}`, children: [tool.calls > 1
491
+ ? t("agentChat.observability.insights.timesCount", {
492
+ label: humanizeTool(t, tool.name),
493
+ count: tool.calls,
494
+ })
495
+ : humanizeTool(t, tool.name), tool.failed
496
+ ? ` · ${t("agentChat.observability.insights.failedSuffix")}`
497
+ : ""] }, tool.name))), run.tools.length > 8 ? (_jsx("span", { className: "px-1 py-0.5 text-[11px] text-muted-foreground", children: t("agentChat.observability.insights.moreTools", {
498
+ count: run.tools.length - 8,
499
+ }) })) : null] })) : null] })] }), _jsx(HandledNote, { run: run }), notableRestart(run) || failures.length > 0 ? (_jsxs("div", { className: "space-y-2", children: [notableRestart(run) ? (_jsxs("div", { className: "rounded-lg border border-amber-500/30 bg-amber-500/5 px-3.5 py-3 text-xs leading-5", children: [_jsx("p", { className: "text-foreground", children: t("agentChat.observability.insights.startedOverNote", {
500
+ count: run.restarts.count,
501
+ reason: restartReason(t, cause),
502
+ amount: formatUsd(run.restarts.cents),
503
+ total: formatUsd(run.cost.estimatedCents),
504
+ }) }), _jsxs("p", { className: "mt-1 text-muted-foreground", children: [_jsxs("span", { className: "font-medium text-foreground/90", children: [t("agentChat.observability.insights.fixLabel"), " "] }), restartFix(t, cause)] })] })) : null, failures.slice(0, 1).map((tool) => (_jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3.5 py-3 text-xs leading-5", children: [_jsx("p", { className: "text-foreground", children: run.status === "success"
505
+ ? t("agentChat.observability.insights.toolFailedRecoveredNote", {
506
+ tool: toolNoun(tool.name),
507
+ count: tool.failed,
508
+ })
509
+ : t("agentChat.observability.insights.toolFailedNote", {
510
+ tool: toolNoun(tool.name),
511
+ count: tool.failed,
512
+ }) }), tool.error ? (_jsx("p", { className: "mt-1 text-muted-foreground", children: tool.error })) : null] }, tool.name)))] })) : null, run.status !== "unknown" ? (_jsxs("section", { children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 text-xs font-medium text-foreground", onClick: () => setShowSteps((value) => !value), "aria-expanded": showSteps, children: [_jsx(IconChevronDown, { className: `size-3.5 transition-transform ${showSteps ? "rotate-180" : ""}` }), showSteps
513
+ ? t("agentChat.observability.insights.hideSteps")
514
+ : t("agentChat.observability.insights.showSteps", {
515
+ count: run.modelCalls,
516
+ })] }), showSteps ? (detail ? (_jsx(StepList, { turns: detail.turns })) : (_jsx(Skeleton, { className: "mt-2 h-24 w-full" }))) : null] })) : null, _jsxs("section", { children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 text-xs font-medium text-foreground", onClick: () => setShowDetails((value) => !value), "aria-expanded": showDetails, children: [_jsx(IconChevronDown, { className: `size-3.5 transition-transform ${showDetails ? "rotate-180" : ""}` }), t("agentChat.observability.insights.costDetails")] }), showDetails && detail ? (_jsx("div", { className: "mt-3", children: _jsx(CostDetails, { run: detail }) })) : null] }), _jsxs("section", { children: [_jsxs("button", { type: "button", className: "flex items-center gap-1.5 text-xs font-medium text-foreground", onClick: () => setShowRawTrace((value) => !value), "aria-expanded": showRawTrace, children: [_jsx(IconChevronDown, { className: `size-3.5 transition-transform ${showRawTrace ? "rotate-180" : ""}` }), t("agentChat.observability.insights.rawTrace")] }), showRawTrace ? (_jsx("div", { className: "mt-3", children: renderRawTrace(run.runId) })) : null] })] })] }));
517
+ }
518
+ function median(values) {
519
+ if (values.length === 0)
520
+ return null;
521
+ const sorted = [...values].sort((a, b) => a - b);
522
+ return sorted[Math.floor(sorted.length / 2)];
523
+ }
524
+ function Stat({ label, value, detail, }) {
525
+ return (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: label }), _jsx("div", { className: "mt-0.5 text-lg font-semibold tabular-nums text-foreground", children: value }), detail ? (_jsx("div", { className: "text-xs text-muted-foreground", children: detail })) : null] }));
526
+ }
527
+ function changeText(t, current, previous) {
528
+ if (previous <= 0)
529
+ return undefined;
530
+ const change = Math.round(((current - previous) / previous) * 100);
531
+ if (change === 0)
532
+ return t("agentChat.observability.insights.changeSame");
533
+ return change > 0
534
+ ? t("agentChat.observability.insights.changeUp", { percent: change })
535
+ : t("agentChat.observability.insights.changeDown", { percent: -change });
536
+ }
537
+ function LoadFailed({ retrying, onRetry, }) {
538
+ const t = useT();
539
+ return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("p", { role: "alert", className: "text-sm text-muted-foreground", children: t("agentChat.common.chunkLoadFailed") }), _jsx(Button, { size: "sm", variant: "outline", disabled: retrying, onClick: onRetry, children: t("agentChat.common.retry") })] }));
540
+ }
541
+ function useRunInsights(days) {
542
+ return useActionQuery("get-usage-insights", {
543
+ sinceDays: days,
544
+ scope: "me",
545
+ });
546
+ }
547
+ export function RunInsightsOverview({ days, extraStats = [], onShowRuns, }) {
548
+ const t = useT();
549
+ const { data, isError, isFetching, refetch } = useRunInsights(days);
550
+ const runs = useMemo(() => data?.runs ?? [], [data]);
551
+ const insights = useMemo(() => buildInsights(t, runs), [t, runs]);
552
+ if (isError) {
553
+ return _jsx(LoadFailed, { retrying: isFetching, onRetry: () => void refetch() });
554
+ }
555
+ if (!data) {
556
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(Skeleton, { className: "h-36 w-full" }), _jsx(Skeleton, { className: "h-32 w-full" })] }));
557
+ }
558
+ const { current, previous } = data;
559
+ const avgCents = current.runs ? current.cost.totalCents / current.runs : 0;
560
+ const previousAvg = previous.runs
561
+ ? previous.cost.totalCents / previous.runs
562
+ : 0;
563
+ const known = runs.filter((run) => run.status !== "unknown");
564
+ const completed = known.filter((run) => run.status === "success").length;
565
+ const recovered = runs.reduce((sum, run) => sum + run.recoveredErrors, 0);
566
+ const handled = handledText(t, {
567
+ calls: runs.reduce((sum, run) => sum + run.parallel.calls, 0),
568
+ savedMs: runs.reduce((sum, run) => sum + run.parallel.savedMs, 0),
569
+ }, recovered);
570
+ const typical = median(runs.map((run) => run.durationMs ?? 0).filter(Boolean));
571
+ const problems = insights.filter((insight) => insight.kind === "problem").length;
572
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("section", { className: "rounded-lg border border-border/70 bg-card p-5", children: [_jsx("div", { className: "flex items-center gap-2 text-sm font-semibold text-foreground", children: insights.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(IconCircleCheck, { className: "size-4 text-emerald-600 dark:text-emerald-400" }), t("agentChat.observability.insights.verdictSmooth")] })) : (_jsxs(_Fragment, { children: [_jsx(IconAlertTriangle, { className: "size-4 text-amber-600 dark:text-amber-400" }), t("agentChat.observability.insights.verdictLook", {
573
+ count: insights.length,
574
+ }), problems
575
+ ? ` · ${t("agentChat.observability.insights.verdictProblems", {
576
+ count: problems,
577
+ })}`
578
+ : ""] })) }), _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: t("agentChat.observability.insights.spentSummary", {
579
+ amount: formatUsd(current.cost.totalCents),
580
+ count: current.runs,
581
+ days,
582
+ }) }), handled.length > 0 ? (_jsxs("p", { className: "mt-2 flex items-start gap-1.5 text-xs leading-5 text-muted-foreground", children: [_jsx(IconCircleCheck, { className: "mt-0.5 size-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" }), _jsxs("span", { children: [_jsxs("span", { className: "font-medium text-foreground", children: [t("agentChat.observability.insights.handledLabel"), " "] }), handled.join(" ")] })] })) : null, current.runs > runs.length ? (_jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: t("agentChat.observability.insights.sampleNote", {
583
+ shown: runs.length,
584
+ total: current.runs,
585
+ }) })) : null, _jsxs("div", { className: "mt-4 grid grid-cols-2 gap-4 border-t border-border/60 pt-4 sm:grid-cols-3 lg:grid-cols-5", children: [_jsx(Stat, { label: t("agentChat.observability.insights.avgPerPrompt"), value: formatUsd(avgCents), detail: changeText(t, avgCents, previousAvg) }), _jsx(Stat, { label: t("agentChat.observability.insights.completed"), value: known.length ? `${percent(completed, known.length)}%` : "—", detail: known.length
586
+ ? recovered
587
+ ? t("agentChat.observability.insights.completedRecovered", {
588
+ done: completed,
589
+ total: known.length,
590
+ count: recovered,
591
+ })
592
+ : t("agentChat.observability.insights.completedDetail", {
593
+ done: completed,
594
+ total: known.length,
595
+ })
596
+ : undefined }), _jsx(Stat, { label: t("agentChat.observability.insights.typicalTime"), value: typical ? formatDuration(typical) : "—", detail: t("agentChat.observability.insights.median") }), extraStats.map((stat) => (_jsx(Stat, { ...stat }, stat.label)))] })] }), insights.length > 0 ? (_jsx("div", { className: `grid gap-3 ${insights.length > 1 ? "md:grid-cols-2" : ""} ${insights.length > 2 ? "xl:grid-cols-3" : ""}`, children: insights.map((insight) => (_jsx(InsightCard, { insight: insight, onShow: () => onShowRuns({
597
+ runIds: insight.runIds,
598
+ openRunId: insight.runIds.length === 1 ? insight.runIds[0] : undefined,
599
+ }) }, insight.key))) })) : null] }));
600
+ }
601
+ export function RunInsightsConversations({ days, filter, onClearFilter, renderRawTrace, }) {
602
+ const t = useT();
603
+ const { data, isError, isFetching, refetch } = useRunInsights(days);
604
+ const runs = useMemo(() => data?.runs ?? [], [data]);
605
+ const [sort, setSort] = useState("newest");
606
+ const [openRunId, setOpenRunId] = useState(filter?.openRunId ?? null);
607
+ useEffect(() => {
608
+ if (filter?.openRunId)
609
+ setOpenRunId(filter.openRunId);
610
+ }, [filter]);
611
+ const visible = useMemo(() => {
612
+ const filtered = filter
613
+ ? runs.filter((run) => filter.runIds.includes(run.runId))
614
+ : runs;
615
+ return sort === "cost"
616
+ ? [...filtered].sort((a, b) => b.cost.totalCents - a.cost.totalCents)
617
+ : filtered;
618
+ }, [runs, filter, sort]);
619
+ const openIndex = visible.findIndex((run) => run.runId === openRunId);
620
+ const openRun = openIndex >= 0 ? visible[openIndex] : null;
621
+ const openAt = (index) => {
622
+ const run = visible[index];
623
+ if (run)
624
+ setOpenRunId(run.runId);
625
+ };
626
+ useEffect(() => {
627
+ if (!openRun)
628
+ return;
629
+ const onKey = (event) => {
630
+ const target = event.target;
631
+ if (target instanceof Element &&
632
+ target.closest("input, textarea, [contenteditable=true]")) {
633
+ return;
634
+ }
635
+ if (event.key === "j")
636
+ openAt(openIndex + 1);
637
+ else if (event.key === "k")
638
+ openAt(openIndex - 1);
639
+ };
640
+ window.addEventListener("keydown", onKey);
641
+ return () => window.removeEventListener("keydown", onKey);
642
+ });
643
+ if (isError) {
644
+ return _jsx(LoadFailed, { retrying: isFetching, onRetry: () => void refetch() });
645
+ }
646
+ if (!data) {
647
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" }), _jsx(Skeleton, { className: "h-10 w-full" })] }));
648
+ }
649
+ return (_jsxs("div", { className: "run-insights", children: [_jsx("style", { children: PALETTE_CSS }), _jsxs("section", { className: "rounded-lg border border-border/70 bg-card", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 px-4 py-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: t("agentChat.observability.insights.promptsHeading") }), filter ? (_jsxs("button", { type: "button", className: "flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-[11px] text-muted-foreground hover:text-foreground", onClick: onClearFilter, children: [t("agentChat.observability.insights.showing", {
650
+ count: filter.runIds.length,
651
+ }), " ", _jsx(IconX, { className: "size-3" })] })) : null] }), _jsx("div", { className: "flex items-center rounded-md border border-border/70 p-0.5", children: ["newest", "cost"].map((value) => (_jsx(Button, { type: "button", size: "sm", variant: sort === value ? "secondary" : "ghost", className: "h-7 px-2.5 text-xs", onClick: () => setSort(value), "aria-pressed": sort === value, children: value === "newest"
652
+ ? t("agentChat.observability.insights.sortNewest")
653
+ : t("agentChat.observability.insights.sortCost") }, value))) })] }), visible.length === 0 ? (_jsx("p", { className: "border-t border-border/70 px-4 py-8 text-center text-sm text-muted-foreground", children: t("agentChat.observability.insights.emptyPrompts") })) : (_jsx("div", { className: "divide-y divide-border/60 border-t border-border/70", children: visible.map((run) => (_jsx(RunRow, { run: run, onOpen: () => setOpenRunId(run.runId) }, run.runId))) }))] }), _jsx(Sheet, { open: Boolean(openRun), onOpenChange: (open) => {
654
+ if (!open)
655
+ setOpenRunId(null);
656
+ }, children: _jsx(SheetContent, { className: "run-insights-panel w-full gap-0 p-0 sm:max-w-[680px]", children: openRun ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "absolute end-12 top-4 flex items-center gap-1", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", disabled: openIndex <= 0, onClick: () => openAt(openIndex - 1), "aria-label": t("agentChat.observability.insights.prevPrompt"), title: t("agentChat.observability.insights.prevPrompt"), children: _jsx(IconChevronUp, { className: "size-4" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", disabled: openIndex >= visible.length - 1, onClick: () => openAt(openIndex + 1), "aria-label": t("agentChat.observability.insights.nextPrompt"), title: t("agentChat.observability.insights.nextPrompt"), children: _jsx(IconChevronDown, { className: "size-4" }) })] }), _jsx(RunPanel, { run: openRun, renderRawTrace: renderRawTrace, onClose: () => setOpenRunId(null) }, openRun.runId)] })) : null }) })] }));
657
+ }
658
+ //# sourceMappingURL=RunInsights.js.map