@sentry/junior-dashboard 0.249.0 → 0.251.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.
@@ -1,17 +1,62 @@
1
+ import type { ReactNode } from "react";
1
2
  import type {
3
+ AutomationExecutionDay,
4
+ AutomationExecutionStatusDay,
5
+ CodeActivityDay,
2
6
  ConversationMetricDay,
7
+ DailyConversationActivity,
8
+ GuardianMetricDay,
3
9
  LocationActivityDayReport,
4
10
  PeopleActivityDayReport,
11
+ PluginOperationalReport,
5
12
  } from "@sentry/junior/api/schema";
6
13
  import { InputCacheChart } from "../../components/charts/InputCacheChart";
7
14
  import { SystemMetricCharts } from "../../components/charts/SystemMetricCharts";
15
+ import { WorkspaceUsageChart } from "../../components/charts/WorkspaceUsageChart";
16
+ import { AutomationCostChart } from "../automations/AutomationCostChart";
17
+ import { AutomationExecutionChart } from "../automations/AutomationExecutionChart";
18
+ import { AutomationExecutionStatusChart } from "../automations/AutomationExecutionStatusChart";
19
+ import { CodeActivityChart } from "../code/CodeActivityChart";
20
+ import { LocationActivityChart } from "../locations/LocationActivityChart";
8
21
  import { LocationDirectoryActivityChart } from "../locations/LocationDirectoryActivityChart";
22
+ import { MemoryCostChart } from "../memory/MemoryCostChart";
23
+ import { MemoryTimeline } from "../memory/MemoryTimeline";
24
+ import type { MemoryCostDay, MemoryDay } from "../memory/memoryDashboard";
9
25
  import { ContributionGrid } from "../people/ContributionGrid";
10
26
  import { PeopleActivityChart } from "../people/PeopleActivityChart";
11
27
  import { ConversationActivityChart } from "../system/ConversationActivityChart";
28
+ import { GuardianActivity } from "../system/GuardianActivity";
29
+ import { PluginBarChart } from "../system/PluginBarChart";
12
30
  import { Card } from "../../components/layout/Card";
13
31
  import { CardHeader } from "../../components/layout/CardHeader";
14
32
 
33
+ type PluginWidget = NonNullable<PluginOperationalReport["widgets"]>[number];
34
+
35
+ // The gallery browser scenario saves one image for each id.
36
+ const CHART_FIXTURE_IDS = [
37
+ "conversation-activity",
38
+ "conversation-activity-hourly",
39
+ "input-cache",
40
+ "system-metrics",
41
+ "people-activity",
42
+ "location-directory-activity",
43
+ "location-activity",
44
+ "code-activity",
45
+ "code-activity-dense",
46
+ "code-activity-empty",
47
+ "workspace-usage",
48
+ "automation-executions",
49
+ "automation-execution-status",
50
+ "automation-cost",
51
+ "memory-timeline",
52
+ "memory-cost",
53
+ "guardian-activity",
54
+ "plugin-bar-chart",
55
+ "contribution-grid",
56
+ ] as const;
57
+
58
+ type ChartFixtureId = (typeof CHART_FIXTURE_IDS)[number];
59
+
15
60
  const METRIC_DAYS: ConversationMetricDay[] = fixtureDates(14).map(
16
61
  (date, index) => ({
17
62
  conversations: 4 + ((index * 5) % 17),
@@ -22,6 +67,16 @@ const METRIC_DAYS: ConversationMetricDay[] = fixtureDates(14).map(
22
67
  }),
23
68
  );
24
69
 
70
+ const HOURLY_METRIC_DAYS: ConversationMetricDay[] = fixtureHours(24).map(
71
+ (date, index) => ({
72
+ conversations: index % 5 === 0 ? 0 : 1 + ((index * 7) % 9),
73
+ costUsd: 0.05 + ((index * 3) % 7) * 0.04,
74
+ date,
75
+ durationMs: 30_000 + ((index * 11) % 7) * 9_000,
76
+ tokens: 2_000 + ((index * 13) % 11) * 400,
77
+ }),
78
+ );
79
+
25
80
  const PEOPLE_DAYS: PeopleActivityDayReport[] = fixtureDates(30).map(
26
81
  (date, index) => ({
27
82
  activePeople: 4 + ((index * 7) % 13),
@@ -38,44 +93,233 @@ const LOCATION_DAYS: LocationActivityDayReport[] = fixtureDates(30).map(
38
93
  }),
39
94
  );
40
95
 
96
+ const LOCATION_DETAIL_DAYS: DailyConversationActivity[] = fixtureDates(90).map(
97
+ (date, index) => {
98
+ const conversations = index % 11 === 0 ? 0 : 2 + ((index * 7) % 15);
99
+ return {
100
+ active: Math.min(conversations, index % 4),
101
+ conversations,
102
+ date,
103
+ durationMs: conversations * 60_000,
104
+ failed: index % 13 === 0 ? 1 : 0,
105
+ };
106
+ },
107
+ );
108
+
109
+ const CODE_DAYS: CodeActivityDay[] = codeDays(30);
110
+
111
+ const DENSE_CODE_DAYS: CodeActivityDay[] = codeDays(90);
112
+
113
+ const EMPTY_CODE_DAYS: CodeActivityDay[] = fixtureDates(30).map((date) => ({
114
+ closed: 0,
115
+ created: 0,
116
+ date,
117
+ merged: 0,
118
+ }));
119
+
120
+ const WORKSPACE_USAGE_DAYS = fixtureDates(30).map((date, index) => ({
121
+ count: index % 6 === 0 ? 0 : 1 + ((index * 5) % 9),
122
+ date,
123
+ }));
124
+
125
+ const AUTOMATION_DAYS: AutomationExecutionDay[] = fixtureDates(30).map(
126
+ (date, index) => ({
127
+ costUsd: index % 7 === 0 ? 0 : 0.04 + ((index * 5) % 9) * 0.11,
128
+ date,
129
+ event: (index * 3) % 5,
130
+ scheduled: index % 7 === 0 ? 0 : 1 + ((index * 7) % 6),
131
+ }),
132
+ );
133
+
134
+ const AUTOMATION_STATUS_DAYS: AutomationExecutionStatusDay[] = fixtureDates(
135
+ 30,
136
+ ).map((date, index) => ({
137
+ blocked: index % 9 === 0 ? 1 : 0,
138
+ completed: index % 7 === 0 ? 0 : 1 + ((index * 5) % 4),
139
+ date,
140
+ failed: index % 5 === 0 ? 1 : 0,
141
+ }));
142
+
143
+ const MEMORY_DAYS: MemoryDay[] = fixtureDates(30).map((date, index) => ({
144
+ date,
145
+ personal: index % 8 === 0 ? 0 : (index * 3) % 7,
146
+ public: (index * 5) % 4,
147
+ }));
148
+
149
+ const MEMORY_EXTRACTION_DAYS: MemoryCostDay[] = fixtureDates(30).map(
150
+ (date, index) => ({
151
+ costUsd: index % 8 === 0 ? 0 : 0.02 + ((index * 7) % 9) * 0.015,
152
+ date,
153
+ events: index % 8 === 0 ? 0 : 1 + ((index * 3) % 6),
154
+ }),
155
+ );
156
+
157
+ const MEMORY_RECALL_DAYS: MemoryCostDay[] = fixtureDates(30).map(
158
+ (date, index) => ({
159
+ costUsd: 0.01 + ((index * 5) % 7) * 0.008,
160
+ date,
161
+ events: 2 + ((index * 5) % 9),
162
+ }),
163
+ );
164
+
165
+ const GUARDIAN_DAYS: GuardianMetricDay[] = fixtureDates(30).map(
166
+ (date, index) => {
167
+ const allow = 6 + ((index * 7) % 19);
168
+ const ask = (index * 3) % 5;
169
+ const deny = index % 6 === 0 ? 2 : 0;
170
+ return {
171
+ allow,
172
+ ask,
173
+ costUsd: (allow + ask + deny) * 0.0021,
174
+ date,
175
+ deny,
176
+ requests: allow + ask + deny,
177
+ };
178
+ },
179
+ );
180
+
181
+ const PLUGIN_WIDGET: PluginWidget = {
182
+ categories: fixtureDates(14).map((date, index) => ({
183
+ id: date,
184
+ label: date,
185
+ values: {
186
+ failed: index % 4 === 0 ? 2 : 0,
187
+ succeeded: 3 + ((index * 5) % 11),
188
+ },
189
+ })),
190
+ description: "Deliveries reported by the plugin.",
191
+ id: "deliveries",
192
+ series: [
193
+ { key: "succeeded", label: "Succeeded", tone: "good" },
194
+ { key: "failed", label: "Failed", tone: "danger" },
195
+ ],
196
+ title: "Deliveries",
197
+ type: "bar_chart",
198
+ };
199
+
41
200
  const CONTRIBUTION_DAYS = fixtureDates(70).map((date, index) => ({
42
201
  conversations: index % 9 === 0 ? 0 : 1 + ((index * 7) % 18),
43
202
  date,
44
203
  durationMs: index % 9 === 0 ? 0 : 45_000 + ((index * 13) % 20) * 18_000,
45
204
  }));
46
205
 
47
- /** Render chart examples with cache drops and missing counters for visual review. */
206
+ /** Render every dashboard chart with fixed data for visual review. */
48
207
  export function ChartFixtures() {
49
208
  return (
50
209
  <>
51
- <ConversationActivityChart days={METRIC_DAYS} />
52
- <InputCacheChart
53
- bucketUnit="day"
54
- days={METRIC_DAYS.map((day, index) => ({
55
- ...day,
56
- inputTokens: 2_000,
57
- cachedInputTokens: index === 9 ? 8_000 : 180_000,
58
- cacheCreationTokens:
59
- index === 5 ? undefined : index === 9 ? 190_000 : 10_000,
60
- }))}
61
- />
62
- <SystemMetricCharts days={METRIC_DAYS} />
63
- <PeopleActivityChart days={PEOPLE_DAYS} />
64
- <LocationDirectoryActivityChart days={LOCATION_DAYS} />
65
- <Card>
66
- <CardHeader
67
- description="Daily conversation intensity over ten weeks."
68
- title="Contribution activity"
210
+ <ChartFixture id="conversation-activity">
211
+ <ConversationActivityChart days={METRIC_DAYS} />
212
+ </ChartFixture>
213
+ <ChartFixture id="conversation-activity-hourly">
214
+ <ConversationActivityChart
215
+ bucketUnit="hour"
216
+ days={HOURLY_METRIC_DAYS}
69
217
  />
70
- <ContributionGrid days={CONTRIBUTION_DAYS} />
71
- </Card>
218
+ </ChartFixture>
219
+ <ChartFixture id="input-cache">
220
+ <InputCacheChart
221
+ bucketUnit="day"
222
+ days={METRIC_DAYS.map((day, index) => ({
223
+ ...day,
224
+ inputTokens: 2_000,
225
+ cachedInputTokens: index === 9 ? 8_000 : 180_000,
226
+ cacheCreationTokens:
227
+ index === 5 ? undefined : index === 9 ? 190_000 : 10_000,
228
+ }))}
229
+ />
230
+ </ChartFixture>
231
+ <ChartFixture id="system-metrics">
232
+ <SystemMetricCharts days={METRIC_DAYS} />
233
+ </ChartFixture>
234
+ <ChartFixture id="people-activity">
235
+ <PeopleActivityChart days={PEOPLE_DAYS} />
236
+ </ChartFixture>
237
+ <ChartFixture id="location-directory-activity">
238
+ <LocationDirectoryActivityChart days={LOCATION_DAYS} />
239
+ </ChartFixture>
240
+ <ChartFixture id="location-activity">
241
+ <LocationActivityChart days={LOCATION_DETAIL_DAYS} />
242
+ </ChartFixture>
243
+ <ChartFixture id="code-activity">
244
+ <CodeActivityChart days={CODE_DAYS} range={30} />
245
+ </ChartFixture>
246
+ <ChartFixture id="code-activity-dense">
247
+ <CodeActivityChart days={DENSE_CODE_DAYS} range={90} />
248
+ </ChartFixture>
249
+ <ChartFixture id="code-activity-empty">
250
+ <CodeActivityChart days={EMPTY_CODE_DAYS} range={30} />
251
+ </ChartFixture>
252
+ <ChartFixture id="workspace-usage">
253
+ <WorkspaceUsageChart
254
+ days={WORKSPACE_USAGE_DAYS}
255
+ range={30}
256
+ workspaceName="junior"
257
+ />
258
+ </ChartFixture>
259
+ <ChartFixture id="automation-executions">
260
+ <AutomationExecutionChart days={AUTOMATION_DAYS} range={30} />
261
+ </ChartFixture>
262
+ <ChartFixture id="automation-execution-status">
263
+ <AutomationExecutionStatusChart
264
+ days={AUTOMATION_STATUS_DAYS}
265
+ range={30}
266
+ />
267
+ </ChartFixture>
268
+ <ChartFixture id="automation-cost">
269
+ <AutomationCostChart days={AUTOMATION_DAYS} range={30} />
270
+ </ChartFixture>
271
+ <ChartFixture id="memory-timeline">
272
+ <MemoryTimeline days={MEMORY_DAYS} range={30} />
273
+ </ChartFixture>
274
+ <ChartFixture id="memory-cost">
275
+ <MemoryCostChart
276
+ extractionDays={MEMORY_EXTRACTION_DAYS}
277
+ range={30}
278
+ recallDays={MEMORY_RECALL_DAYS}
279
+ />
280
+ </ChartFixture>
281
+ <ChartFixture id="guardian-activity">
282
+ <GuardianActivity days={GUARDIAN_DAYS} />
283
+ </ChartFixture>
284
+ <ChartFixture id="plugin-bar-chart">
285
+ <PluginBarChart widget={PLUGIN_WIDGET} />
286
+ </ChartFixture>
287
+ <ChartFixture id="contribution-grid">
288
+ <Card>
289
+ <CardHeader
290
+ description="Daily conversation intensity over ten weeks."
291
+ title="Contribution activity"
292
+ />
293
+ <ContributionGrid days={CONTRIBUTION_DAYS} />
294
+ </Card>
295
+ </ChartFixture>
72
296
  </>
73
297
  );
74
298
  }
75
299
 
300
+ function ChartFixture(props: { children: ReactNode; id: ChartFixtureId }) {
301
+ return <div data-gallery-chart={props.id}>{props.children}</div>;
302
+ }
303
+
304
+ function codeDays(count: number): CodeActivityDay[] {
305
+ return fixtureDates(count).map((date, index) => ({
306
+ closed: index % 9 === 0 ? 1 : 0,
307
+ created: index % 4 === 0 ? 0 : 1 + ((index * 5) % 3),
308
+ date,
309
+ merged: index % 3 === 0 ? 1 + (index % 2) : 0,
310
+ }));
311
+ }
312
+
76
313
  function fixtureDates(count: number): string[] {
77
314
  const start = Date.UTC(2026, 4, 1);
78
315
  return Array.from({ length: count }, (_, index) =>
79
316
  new Date(start + index * 86_400_000).toISOString().slice(0, 10),
80
317
  );
81
318
  }
319
+
320
+ function fixtureHours(count: number): string[] {
321
+ const start = Date.UTC(2026, 4, 1);
322
+ return Array.from({ length: count }, (_, index) =>
323
+ new Date(start + index * 3_600_000).toISOString().slice(0, 13),
324
+ );
325
+ }
@@ -125,6 +125,8 @@ const SANDBOX_CONVERSATION_ID = "slack:CQA999:1770010800.000400";
125
125
  const FAILED_CONVERSATION_ID = "slack:CQA777:1770014400.000500";
126
126
  const LONG_CONVERSATION_ID = "slack:CQA456:1770021600.000600";
127
127
  const SCHEDULER_CONVERSATION_ID = "scheduler:daily-ops-digest";
128
+ /** Event automation run. Its trigger message id equals the conversation id. */
129
+ const EVENT_AUTOMATION_RUN_CONVERSATION_ID = "agent-dispatch:event-1";
128
130
  export const DASHBOARD_QA_CONVERSATION_ID = "internal:dashboard-qa";
129
131
  export const ARCHIVED_CONVERSATION_ID = "internal:archived-restore-qa";
130
132
  const DASHBOARD_QA_PLAN_ID = "junior:internal:dashboard-qa:advisor-plan";
@@ -1395,6 +1397,75 @@ function simpleConversation(
1395
1397
  });
1396
1398
  }
1397
1399
 
1400
+ function eventAutomationRunConversation(
1401
+ nowMs: number,
1402
+ ): ConversationDetailReport {
1403
+ const startedAt = iso(nowMs, -3 * 60 * 60_000);
1404
+ const at = (offsetMs: number) => iso(Date.parse(startedAt), offsetMs);
1405
+ return detail(nowMs, {
1406
+ conversationId: EVENT_AUTOMATION_RUN_CONVERSATION_ID,
1407
+ displayTitle: "Closed issue summary",
1408
+ surface: "internal",
1409
+ startedAt,
1410
+ lastSeenAt: at(14_000),
1411
+ lastProgressAt: at(14_000),
1412
+ cumulativeDurationMs: 14_000,
1413
+ sourceTask: {
1414
+ id: "event-1",
1415
+ kind: "event",
1416
+ label: "Summarize the closed issue",
1417
+ title: "Closed issue summary",
1418
+ triggerMessageId: EVENT_AUTOMATION_RUN_CONVERSATION_ID,
1419
+ triggerLabel: "Linear issue ACME-42 closed",
1420
+ credentialMode: "creator",
1421
+ credentialUser: "Alex Rivera",
1422
+ },
1423
+ events: [
1424
+ reportEvent(0, startedAt, {
1425
+ type: "message",
1426
+ messageId: EVENT_AUTOMATION_RUN_CONVERSATION_ID,
1427
+ role: "user",
1428
+ // Dispatch input is authored by the dispatch owner, not a person.
1429
+ actorIdentity: {
1430
+ fullName: "junior",
1431
+ slackUserId: "junior",
1432
+ slackUserName: "junior",
1433
+ },
1434
+ text: [
1435
+ "[task]",
1436
+ "",
1437
+ "This is a task, not a message from a person.",
1438
+ "",
1439
+ "About: Issue · ACME-42",
1440
+ 'Created by: <@U123>. "Me" and "my" in the instructions mean this person: mention them in the message. If the instructions do not say "me" or "my", do not mention this person.',
1441
+ "Instructions: Summarize the closed issue",
1442
+ "",
1443
+ "Trusted summary: ACME-42 Checkout fails for saved cards was closed by Morgan Lee.",
1444
+ "",
1445
+ "End with `finishAutomationRun`.",
1446
+ ].join("\n"),
1447
+ }),
1448
+ reportEvent(1, at(1_000), {
1449
+ type: "turn_lifecycle",
1450
+ turnId: "dispatch:event-1",
1451
+ state: "started",
1452
+ inputMessageIds: [EVENT_AUTOMATION_RUN_CONVERSATION_ID],
1453
+ }),
1454
+ reportEvent(2, at(13_000), {
1455
+ type: "message",
1456
+ messageId: `${EVENT_AUTOMATION_RUN_CONVERSATION_ID}:reply`,
1457
+ role: "assistant",
1458
+ text: "ACME-42 is closed. The saved-card checkout failure was fixed by the token refresh change and verified in production.",
1459
+ }),
1460
+ reportEvent(3, at(14_000), {
1461
+ type: "turn_lifecycle",
1462
+ turnId: "dispatch:event-1",
1463
+ state: "succeeded",
1464
+ }),
1465
+ ],
1466
+ });
1467
+ }
1468
+
1398
1469
  function actor(
1399
1470
  email: string | undefined,
1400
1471
  fullName: string,
@@ -1488,6 +1559,7 @@ function mockConversations(nowMs: number): MockConversation[] {
1488
1559
  title: "Weekly project summary",
1489
1560
  },
1490
1561
  }),
1562
+ eventAutomationRunConversation(nowMs),
1491
1563
  simpleConversation(nowMs, {
1492
1564
  conversationId: ARCHIVED_CONVERSATION_ID,
1493
1565
  displayTitle: "Archived restore target",