@sentry/junior-dashboard 0.246.0 → 0.248.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.
- package/dist/app.js +69 -15
- package/dist/client.js +50 -50
- package/dist/index.js +69 -15
- package/dist/tailwind.css +1 -1
- package/package.json +3 -3
- package/src/client/components/Metric.tsx +8 -13
- package/src/client/conversations/ConversationChildAgents.tsx +6 -0
- package/src/client/conversations/ConversationMeta.tsx +20 -1
- package/src/client/conversations/ConversationPage.tsx +11 -4
- package/src/client/conversations/ConversationUsageBreakdown.tsx +278 -0
- package/src/client/conversations/TelemetryMetrics.tsx +327 -180
- package/src/client/conversations/UsageCard.tsx +172 -0
- package/src/client/format.ts +1 -0
- package/src/client/types.ts +1 -0
- package/src/mock-reporting/fixtures.ts +73 -14
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { cn } from "../styles";
|
|
2
|
+
import { ShimmerText } from "../components/ShimmerText";
|
|
3
|
+
|
|
4
|
+
export type UsageCardRow = {
|
|
5
|
+
label: string;
|
|
6
|
+
live?: boolean;
|
|
7
|
+
/** Render the label in the code face, such as a model id. */
|
|
8
|
+
code?: boolean;
|
|
9
|
+
value: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type UsageCardSection = {
|
|
13
|
+
/** Rows rolled up into one "N more" line, with their combined value. */
|
|
14
|
+
more?: { count: number; value: string };
|
|
15
|
+
rows: UsageCardRow[];
|
|
16
|
+
title: string;
|
|
17
|
+
/** Section total shown after the title, such as all auxiliary cost. */
|
|
18
|
+
total?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type UsageCardSplit = {
|
|
22
|
+
count: number;
|
|
23
|
+
self: string;
|
|
24
|
+
/** Share of the total that belongs to this agent, from 0 to 1. */
|
|
25
|
+
selfShare: number;
|
|
26
|
+
subagents: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type UsageCardProps = {
|
|
30
|
+
/** Closing hint, such as where to find the full breakdown. */
|
|
31
|
+
footer?: string;
|
|
32
|
+
/** Muted detail after the title, such as a compaction count. */
|
|
33
|
+
note?: string;
|
|
34
|
+
sections: UsageCardSection[];
|
|
35
|
+
split?: UsageCardSplit;
|
|
36
|
+
title: string;
|
|
37
|
+
total: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const SELF_FILL = "bg-dashboard-focus/80";
|
|
41
|
+
const SUBAGENT_FILL = "bg-dashboard-focus/30";
|
|
42
|
+
|
|
43
|
+
function Swatch(props: { className: string }) {
|
|
44
|
+
return (
|
|
45
|
+
<span
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
className={cn("size-2 shrink-0 rounded-xs", props.className)}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Summarize one usage total: the total up top, how it splits between this
|
|
54
|
+
* agent and its subagents, then the largest contributors. Long lists roll
|
|
55
|
+
* their tail into one "N more" row so the card never needs to scroll.
|
|
56
|
+
*/
|
|
57
|
+
export function UsageCard(props: UsageCardProps) {
|
|
58
|
+
const selfPercent = Math.min(
|
|
59
|
+
100,
|
|
60
|
+
Math.max(0, (props.split?.selfShare ?? 1) * 100),
|
|
61
|
+
);
|
|
62
|
+
return (
|
|
63
|
+
<span className="grid gap-3">
|
|
64
|
+
<span
|
|
65
|
+
className={cn(
|
|
66
|
+
"grid gap-2.5",
|
|
67
|
+
(props.sections.length > 0 || props.footer) &&
|
|
68
|
+
"border-b border-dashboard-border pb-3",
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
<span className="flex items-baseline justify-between gap-3">
|
|
72
|
+
<span className="min-w-0 truncate">
|
|
73
|
+
{props.title}
|
|
74
|
+
{props.note ? (
|
|
75
|
+
<span className="text-dashboard-text-faint"> · {props.note}</span>
|
|
76
|
+
) : null}
|
|
77
|
+
</span>
|
|
78
|
+
<span className="whitespace-nowrap text-lg font-semibold tabular-nums text-dashboard-text">
|
|
79
|
+
{props.total}
|
|
80
|
+
</span>
|
|
81
|
+
</span>
|
|
82
|
+
{props.split ? (
|
|
83
|
+
<>
|
|
84
|
+
<span
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
className="flex h-1.5 overflow-hidden rounded-full bg-dashboard-fill-hover"
|
|
87
|
+
>
|
|
88
|
+
<span
|
|
89
|
+
className={SELF_FILL}
|
|
90
|
+
style={{ width: `${selfPercent}%` }}
|
|
91
|
+
/>
|
|
92
|
+
<span
|
|
93
|
+
className={SUBAGENT_FILL}
|
|
94
|
+
style={{ width: `${100 - selfPercent}%` }}
|
|
95
|
+
/>
|
|
96
|
+
</span>
|
|
97
|
+
<span className="flex flex-wrap justify-between gap-x-3 gap-y-1 tabular-nums">
|
|
98
|
+
<span className="inline-flex items-center gap-1.5 whitespace-nowrap">
|
|
99
|
+
<Swatch className={SELF_FILL} />
|
|
100
|
+
this agent
|
|
101
|
+
<span className="font-medium text-dashboard-text">
|
|
102
|
+
{props.split.self}
|
|
103
|
+
</span>
|
|
104
|
+
</span>
|
|
105
|
+
<span className="inline-flex items-center gap-1.5 whitespace-nowrap">
|
|
106
|
+
<Swatch className={SUBAGENT_FILL} />
|
|
107
|
+
{props.split.count} subagent{props.split.count === 1 ? "" : "s"}
|
|
108
|
+
<span className="font-medium text-dashboard-text">
|
|
109
|
+
{props.split.subagents}
|
|
110
|
+
</span>
|
|
111
|
+
</span>
|
|
112
|
+
</span>
|
|
113
|
+
</>
|
|
114
|
+
) : null}
|
|
115
|
+
</span>
|
|
116
|
+
{props.sections.map((section) => (
|
|
117
|
+
<span
|
|
118
|
+
className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-2"
|
|
119
|
+
key={section.title}
|
|
120
|
+
>
|
|
121
|
+
<span className="col-span-2 font-mono uppercase tracking-[0.08em] text-dashboard-text-faint">
|
|
122
|
+
{section.title}
|
|
123
|
+
{section.total ? ` · ${section.total}` : null}
|
|
124
|
+
</span>
|
|
125
|
+
{section.rows.map((row) => (
|
|
126
|
+
<span className="contents" key={row.label}>
|
|
127
|
+
<span
|
|
128
|
+
className={cn(
|
|
129
|
+
"flex min-w-0 items-baseline gap-2",
|
|
130
|
+
row.code && "font-mono text-dashboard-text",
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
<span className="min-w-0 truncate" title={row.label}>
|
|
134
|
+
{row.label}
|
|
135
|
+
</span>
|
|
136
|
+
{row.live ? (
|
|
137
|
+
<span className="shrink-0 font-sans text-2xs text-dashboard-text-muted">
|
|
138
|
+
<ShimmerText active>in progress</ShimmerText>
|
|
139
|
+
</span>
|
|
140
|
+
) : null}
|
|
141
|
+
</span>
|
|
142
|
+
<span className="whitespace-nowrap text-right tabular-nums text-dashboard-text">
|
|
143
|
+
{row.value}
|
|
144
|
+
</span>
|
|
145
|
+
</span>
|
|
146
|
+
))}
|
|
147
|
+
{section.more ? (
|
|
148
|
+
<>
|
|
149
|
+
<span className="text-dashboard-text-faint">
|
|
150
|
+
{section.more.count} more
|
|
151
|
+
</span>
|
|
152
|
+
<span className="whitespace-nowrap text-right tabular-nums">
|
|
153
|
+
{section.more.value}
|
|
154
|
+
</span>
|
|
155
|
+
</>
|
|
156
|
+
) : null}
|
|
157
|
+
</span>
|
|
158
|
+
))}
|
|
159
|
+
{props.footer ? (
|
|
160
|
+
<span
|
|
161
|
+
className={cn(
|
|
162
|
+
"text-dashboard-text-faint",
|
|
163
|
+
props.sections.length > 0 &&
|
|
164
|
+
"border-t border-dashboard-border pt-3",
|
|
165
|
+
)}
|
|
166
|
+
>
|
|
167
|
+
{props.footer}
|
|
168
|
+
</span>
|
|
169
|
+
) : null}
|
|
170
|
+
</span>
|
|
171
|
+
);
|
|
172
|
+
}
|
package/src/client/format.ts
CHANGED
|
@@ -919,6 +919,7 @@ export function buildConversations(
|
|
|
919
919
|
channel: summary.channel,
|
|
920
920
|
channelName: summary.channelName,
|
|
921
921
|
channelNameRedacted: summary.channelNameRedacted,
|
|
922
|
+
childAgentTotals: summary.childAgentTotals,
|
|
922
923
|
cumulativeDurationMs: summary.cumulativeDurationMs,
|
|
923
924
|
cumulativeUsage: summary.cumulativeUsage,
|
|
924
925
|
displayTitle: summary.displayTitle,
|
package/src/client/types.ts
CHANGED
|
@@ -156,6 +156,7 @@ export type Conversation = {
|
|
|
156
156
|
channel?: string;
|
|
157
157
|
channelName?: string;
|
|
158
158
|
channelNameRedacted?: boolean;
|
|
159
|
+
childAgentTotals?: ConversationSummaryReport["childAgentTotals"];
|
|
159
160
|
cumulativeDurationMs: number;
|
|
160
161
|
cumulativeUsage?: ConversationSummaryReport["cumulativeUsage"];
|
|
161
162
|
displayTitle: string;
|
|
@@ -129,6 +129,8 @@ export const DASHBOARD_QA_CONVERSATION_ID = "internal:dashboard-qa";
|
|
|
129
129
|
export const ARCHIVED_CONVERSATION_ID = "internal:archived-restore-qa";
|
|
130
130
|
const DASHBOARD_QA_PLAN_ID = "junior:internal:dashboard-qa:advisor-plan";
|
|
131
131
|
const DASHBOARD_QA_REVIEW_ID = "junior:internal:dashboard-qa:advisor-review";
|
|
132
|
+
const DASHBOARD_QA_PLAN_CHECK_ID =
|
|
133
|
+
"junior:internal:dashboard-qa:advisor-plan:claim-check";
|
|
132
134
|
/** Process-local archive state so mock restore can be exercised in visual QA. */
|
|
133
135
|
const mockArchivedConversationIds = new Set<string>([ARCHIVED_CONVERSATION_ID]);
|
|
134
136
|
|
|
@@ -672,7 +674,24 @@ function dashboardQaConversation(nowMs: number): ConversationDetailReport {
|
|
|
672
674
|
updatedAt: iso(Date.parse(startedAt), 20_000),
|
|
673
675
|
},
|
|
674
676
|
],
|
|
675
|
-
|
|
677
|
+
// Totals include all three subagents, like the API.
|
|
678
|
+
childAgentTotals: {
|
|
679
|
+
count: 3,
|
|
680
|
+
cumulativeDurationMs: 88_400,
|
|
681
|
+
cumulativeUsage: {
|
|
682
|
+
inputTokens: 48_700,
|
|
683
|
+
outputTokens: 2_680,
|
|
684
|
+
cachedInputTokens: 21_600,
|
|
685
|
+
cost: { total: 0.172 },
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
cumulativeDurationMs: 186_400,
|
|
689
|
+
cumulativeUsage: {
|
|
690
|
+
inputTokens: 100_700,
|
|
691
|
+
outputTokens: 6_080,
|
|
692
|
+
cachedInputTokens: 51_600,
|
|
693
|
+
cost: { total: 0.382 },
|
|
694
|
+
},
|
|
676
695
|
events: [
|
|
677
696
|
reportEvent(0, startedAt, {
|
|
678
697
|
type: "message",
|
|
@@ -973,8 +992,9 @@ function advisorConversation(
|
|
|
973
992
|
text: string,
|
|
974
993
|
metrics: Pick<
|
|
975
994
|
ConversationDetailReport,
|
|
976
|
-
"cumulativeDurationMs" | "cumulativeUsage"
|
|
995
|
+
"childAgentTotals" | "cumulativeDurationMs" | "cumulativeUsage"
|
|
977
996
|
>,
|
|
997
|
+
parentConversationId = DASHBOARD_QA_CONVERSATION_ID,
|
|
978
998
|
): MockConversation {
|
|
979
999
|
const startedAt = iso(nowMs, -10 * 60_000);
|
|
980
1000
|
const conversation = detail(nowMs, {
|
|
@@ -999,10 +1019,7 @@ function advisorConversation(
|
|
|
999
1019
|
}),
|
|
1000
1020
|
],
|
|
1001
1021
|
});
|
|
1002
|
-
return {
|
|
1003
|
-
...conversation,
|
|
1004
|
-
parentConversationId: DASHBOARD_QA_CONVERSATION_ID,
|
|
1005
|
-
};
|
|
1022
|
+
return { ...conversation, parentConversationId };
|
|
1006
1023
|
}
|
|
1007
1024
|
|
|
1008
1025
|
function longConversation(nowMs: number): ConversationDetailReport {
|
|
@@ -1404,14 +1421,37 @@ function mockConversations(nowMs: number): MockConversation[] {
|
|
|
1404
1421
|
DASHBOARD_QA_PLAN_ID,
|
|
1405
1422
|
"Review the dashboard plan before editing.",
|
|
1406
1423
|
{
|
|
1407
|
-
|
|
1424
|
+
childAgentTotals: {
|
|
1425
|
+
count: 1,
|
|
1426
|
+
cumulativeDurationMs: 6_200,
|
|
1427
|
+
cumulativeUsage: {
|
|
1428
|
+
inputTokens: 4_100,
|
|
1429
|
+
outputTokens: 220,
|
|
1430
|
+
cost: { total: 0.011 },
|
|
1431
|
+
},
|
|
1432
|
+
},
|
|
1433
|
+
cumulativeDurationMs: 78_600,
|
|
1408
1434
|
cumulativeUsage: {
|
|
1409
|
-
inputTokens:
|
|
1410
|
-
outputTokens:
|
|
1435
|
+
inputTokens: 42_500,
|
|
1436
|
+
outputTokens: 2_370,
|
|
1411
1437
|
cachedInputTokens: 21_600,
|
|
1412
|
-
cost: { input: 0.
|
|
1438
|
+
cost: { input: 0.1, output: 0.053, total: 0.153 },
|
|
1439
|
+
},
|
|
1440
|
+
},
|
|
1441
|
+
),
|
|
1442
|
+
advisorConversation(
|
|
1443
|
+
nowMs,
|
|
1444
|
+
DASHBOARD_QA_PLAN_CHECK_ID,
|
|
1445
|
+
"Check one claim in the dashboard plan.",
|
|
1446
|
+
{
|
|
1447
|
+
cumulativeDurationMs: 6_200,
|
|
1448
|
+
cumulativeUsage: {
|
|
1449
|
+
inputTokens: 4_100,
|
|
1450
|
+
outputTokens: 220,
|
|
1451
|
+
cost: { total: 0.011 },
|
|
1413
1452
|
},
|
|
1414
1453
|
},
|
|
1454
|
+
DASHBOARD_QA_PLAN_ID,
|
|
1415
1455
|
),
|
|
1416
1456
|
advisorConversation(
|
|
1417
1457
|
nowMs,
|
|
@@ -1855,20 +1895,39 @@ export function readMockConversationChildAgents(
|
|
|
1855
1895
|
(candidate) => candidate.conversationId === conversationId,
|
|
1856
1896
|
);
|
|
1857
1897
|
if (!conversation) return undefined;
|
|
1858
|
-
if (conversationId !== DASHBOARD_QA_CONVERSATION_ID) {
|
|
1859
|
-
return { agents: [], conversationId };
|
|
1860
|
-
}
|
|
1861
1898
|
const startedAtMs = NOW_MS - 5 * 60 * 60_000;
|
|
1862
|
-
// Match the API: each row has the usage of its
|
|
1899
|
+
// Match the API: each row has the usage of its child Conversation and of
|
|
1900
|
+
// that child's own subagents.
|
|
1863
1901
|
const childMetrics = (childConversationId: string) => {
|
|
1864
1902
|
const child = conversations.find(
|
|
1865
1903
|
(candidate) => candidate.conversationId === childConversationId,
|
|
1866
1904
|
)!;
|
|
1867
1905
|
return {
|
|
1906
|
+
...(child.childAgentTotals
|
|
1907
|
+
? { childAgentTotals: child.childAgentTotals }
|
|
1908
|
+
: undefined),
|
|
1868
1909
|
cumulativeDurationMs: child.cumulativeDurationMs,
|
|
1869
1910
|
cumulativeUsage: child.cumulativeUsage,
|
|
1870
1911
|
};
|
|
1871
1912
|
};
|
|
1913
|
+
if (conversationId === DASHBOARD_QA_PLAN_ID) {
|
|
1914
|
+
return {
|
|
1915
|
+
agents: [
|
|
1916
|
+
{
|
|
1917
|
+
...childMetrics(DASHBOARD_QA_PLAN_CHECK_ID),
|
|
1918
|
+
conversationId: DASHBOARD_QA_PLAN_CHECK_ID,
|
|
1919
|
+
status: "completed",
|
|
1920
|
+
task: "Check one claim in the dashboard plan.",
|
|
1921
|
+
title: "Check one plan claim",
|
|
1922
|
+
updatedAt: iso(startedAtMs, 16_000),
|
|
1923
|
+
},
|
|
1924
|
+
],
|
|
1925
|
+
conversationId,
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
if (conversationId !== DASHBOARD_QA_CONVERSATION_ID) {
|
|
1929
|
+
return { agents: [], conversationId };
|
|
1930
|
+
}
|
|
1872
1931
|
return {
|
|
1873
1932
|
agents: [
|
|
1874
1933
|
{
|