@sentry/junior-dashboard 0.263.0 → 0.265.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/LICENSE +201 -0
- package/README.md +2 -5
- package/dist/app.js +119 -35
- package/dist/client.js +148 -135
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +9 -4
- package/src/api/schema.ts +0 -1
- package/src/app.ts +5 -20
- package/src/client/App.tsx +29 -31
- package/src/client/api.ts +7 -0
- package/src/client/components/BottomSheet.tsx +26 -18
- package/src/client/components/CopyButton.tsx +83 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +34 -33
- package/src/client/components/FilterBar.tsx +16 -31
- package/src/client/components/Metric.tsx +33 -23
- package/src/client/components/PageContentSkeleton.tsx +1 -1
- package/src/client/components/Pagination.tsx +1 -1
- package/src/client/components/ParticipantAvatarStack.tsx +40 -36
- package/src/client/components/ProfileMenu.tsx +27 -93
- package/src/client/components/README.md +101 -64
- package/src/client/components/SegmentedNav.tsx +1 -1
- package/src/client/components/ShimmerText.tsx +2 -6
- package/src/client/components/ThemeSwitcher.tsx +19 -15
- package/src/client/components/charts/ActivityChart.tsx +173 -288
- package/src/client/components/charts/CacheWrites.tsx +63 -50
- package/src/client/components/charts/InputCacheChart.tsx +137 -131
- package/src/client/components/charts/README.md +4 -4
- package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
- package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
- package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
- package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
- package/src/client/components/layout/DashboardHeader.tsx +44 -90
- package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
- package/src/client/components/layout/VisualViewportShell.tsx +2 -2
- package/src/client/components/ui/accordion.tsx +77 -0
- package/src/client/components/ui/alert-dialog.tsx +89 -0
- package/src/client/components/ui/button.tsx +85 -0
- package/src/client/components/ui/chart.tsx +74 -0
- package/src/client/components/ui/checkbox.tsx +26 -0
- package/src/client/components/ui/collapsible.tsx +22 -0
- package/src/client/components/ui/field.tsx +49 -0
- package/src/client/components/ui/input.tsx +35 -0
- package/src/client/components/ui/native-select.tsx +57 -0
- package/src/client/components/ui/popover.tsx +67 -0
- package/src/client/components/ui/radio-group.tsx +32 -0
- package/src/client/components/ui/sheet.tsx +151 -0
- package/src/client/components/ui/skeleton.tsx +19 -0
- package/src/client/components/ui/table.tsx +80 -0
- package/src/client/components/ui/tabs.tsx +75 -0
- package/src/client/components/ui/textarea.tsx +17 -0
- package/src/client/components/ui/toast.tsx +215 -0
- package/src/client/components/ui/toggle-group.tsx +36 -0
- package/src/client/components/ui/toggle.tsx +37 -0
- package/src/client/components/ui/tooltip.tsx +168 -0
- package/src/client/components/ui/trace.tsx +96 -0
- package/src/client/components/ui/useSheet.ts +74 -0
- package/src/client/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationChildAgents.tsx +87 -58
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +198 -95
- package/src/client/conversations/ConversationEventDetails.tsx +141 -0
- package/src/client/conversations/ConversationEventLog.tsx +150 -129
- package/src/client/conversations/ConversationHeader.tsx +8 -3
- package/src/client/conversations/ConversationHeaderActions.tsx +90 -82
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
- package/src/client/conversations/ConversationMeta.tsx +111 -92
- package/src/client/conversations/ConversationPage.tsx +241 -110
- package/src/client/conversations/ConversationSidebar.tsx +48 -29
- package/src/client/conversations/ConversationUsageBreakdown.tsx +16 -3
- package/src/client/conversations/ConversationWatches.tsx +165 -0
- package/src/client/conversations/ConversationWorkspace.tsx +12 -22
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
- package/src/client/conversations/DetailsList.tsx +124 -0
- package/src/client/conversations/EventDetails.tsx +42 -8
- package/src/client/conversations/ForkConversationButton.tsx +27 -16
- package/src/client/conversations/ObjectCard.tsx +20 -8
- package/src/client/conversations/PendingMailboxStack.tsx +52 -33
- package/src/client/conversations/README.md +93 -19
- package/src/client/conversations/ShareConversationButton.tsx +28 -3
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
- package/src/client/conversations/ToolFrame.tsx +36 -52
- package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
- package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
- package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
- package/src/client/conversations/TranscriptLoading.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
- package/src/client/conversations/TranscriptMessageContextView.tsx +8 -2
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
- package/src/client/conversations/TranscriptSubagentView.tsx +3 -3
- package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
- package/src/client/conversations/TranscriptToolView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +99 -27
- package/src/client/conversations/TranscriptView.tsx +63 -27
- package/src/client/conversations/eventLog.ts +57 -0
- package/src/client/conversations/eventTime.ts +60 -0
- package/src/client/conversations/queries.ts +29 -0
- package/src/client/conversations/transcriptBottomPinning.ts +34 -0
- package/src/client/format.ts +5 -0
- package/src/client/lib/utils.ts +1 -0
- package/src/client/pages/ConnectionsPage.tsx +2 -2
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/SettingsPage.tsx +9 -10
- package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
- package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
- package/src/client/pages/automations/AutomationDetails.tsx +193 -0
- package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
- package/src/client/pages/automations/AutomationEditor.tsx +41 -29
- package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
- package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
- package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
- package/src/client/pages/automations/AutomationFilters.tsx +189 -74
- package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
- package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
- package/src/client/pages/automations/AutomationRow.tsx +12 -138
- package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
- package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
- package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
- package/src/client/pages/automations/AutomationStatus.tsx +201 -0
- package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
- package/src/client/pages/automations/AutomationsPage.tsx +80 -68
- package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
- package/src/client/pages/automations/README.md +57 -0
- package/src/client/pages/automations/TriggerFilters.tsx +22 -0
- package/src/client/pages/automations/automationFormat.ts +37 -23
- package/src/client/pages/code/CodeActivityChart.tsx +59 -101
- package/src/client/pages/code/CodePage.tsx +52 -55
- package/src/client/pages/code/RepositoryPage.tsx +6 -8
- package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
- package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
- package/src/client/pages/memory/MemoryDetails.tsx +218 -0
- package/src/client/pages/memory/MemoryPage.tsx +168 -112
- package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
- package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/people/ContributionGrid.tsx +23 -16
- package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
- package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
- package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
- package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
- package/src/client/pages/system/GuardianActivity.tsx +1 -2
- package/src/client/pages/system/PluginBarChart.tsx +106 -87
- package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
- package/src/client/pages/system/README.md +23 -0
- package/src/client/pages/system/SystemNavigation.tsx +5 -1
- package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
- package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
- package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
- package/src/client/pages/system/WorkspaceList.tsx +20 -30
- package/src/client/pages/system/WorkspacesPage.tsx +117 -119
- package/src/client/pages/system/sandboxSize.ts +8 -0
- package/src/client/pages/system/workspaceQueries.ts +53 -0
- package/src/client/pages/user/PluginUserPage.tsx +51 -2
- package/src/client/pages/user/pluginUserPageData.ts +1 -6
- package/src/client/styles.ts +4 -0
- package/src/client.tsx +7 -1
- package/src/mock-reporting/automation-collection.ts +4 -2
- package/src/mock-reporting/fixtures.ts +23 -7
- package/src/mock-reporting/routes.ts +12 -0
- package/src/mock-reporting/watches.ts +94 -0
- package/src/shell.ts +4 -9
- package/src/tailwind.css +77 -10
- package/src/client/components/Button.tsx +0 -118
- package/src/client/components/Field.tsx +0 -53
- package/src/client/components/SegmentedTabs.tsx +0 -119
- package/src/client/components/Select.tsx +0 -52
- package/src/client/components/Sheet.LICENSE +0 -21
- package/src/client/components/Sheet.tsx +0 -141
- package/src/client/components/Skeleton.tsx +0 -17
- package/src/client/components/TextInput.tsx +0 -58
- package/src/client/components/Tooltip.tsx +0 -216
- package/src/client/components/charts/useChartLayout.ts +0 -27
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -91
- package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
- package/src/client/pages/dev/ChartFixtures.tsx +0 -325
- package/src/client/pages/dev/ComponentsPage.tsx +0 -899
- package/src/client/pages/dev/ConversationFixture.tsx +0 -231
- package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
- package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
- package/src/client/pages/dev/SelectFixtures.tsx +0 -62
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
- package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
- package/src/client/pages/system/SnapshotSummary.tsx +0 -78
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
memo,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
useSyncExternalStore,
|
|
9
|
+
} from "react";
|
|
2
10
|
import { useSearchParams } from "react-router";
|
|
3
11
|
import type {
|
|
4
12
|
ConversationDetailReport,
|
|
@@ -18,7 +26,7 @@ import {
|
|
|
18
26
|
import { conversationIsResponding } from "./transcript";
|
|
19
27
|
import type { ConversationMailboxMessage } from "./conversationOutbox";
|
|
20
28
|
import { buildConversationMarkdown } from "../markdownExport";
|
|
21
|
-
import {
|
|
29
|
+
import { CopyButton } from "../components/CopyButton";
|
|
22
30
|
import { ConversationBrief } from "./ConversationBrief";
|
|
23
31
|
import {
|
|
24
32
|
CONVERSATION_EVENT_PARAM,
|
|
@@ -27,10 +35,24 @@ import {
|
|
|
27
35
|
import { ConversationComposer } from "./ConversationComposer";
|
|
28
36
|
import { ConversationHeader } from "./ConversationHeader";
|
|
29
37
|
import { ConversationDetails } from "./ConversationDetails";
|
|
38
|
+
import { ConversationEventDetails } from "./ConversationEventDetails";
|
|
39
|
+
import { ConversationEventLogLoading } from "./ConversationEventLog";
|
|
40
|
+
import {
|
|
41
|
+
adjacentEventLogSeqs,
|
|
42
|
+
eventLogRows,
|
|
43
|
+
filterEventLogRows,
|
|
44
|
+
} from "./eventLog";
|
|
45
|
+
import { TranscriptSearchProvider } from "./transcriptSearch";
|
|
46
|
+
import {
|
|
47
|
+
TurnContextDetails,
|
|
48
|
+
type TurnContextPanel,
|
|
49
|
+
type TurnContextSelection,
|
|
50
|
+
} from "./TranscriptTurnContextView";
|
|
30
51
|
import { cn } from "../styles";
|
|
31
52
|
import { ConversationForkLinks } from "./ConversationForkLinks";
|
|
32
53
|
import { ConversationAgentTree } from "./ConversationAgentTree";
|
|
33
54
|
import { ConversationHeaderMeta } from "./ConversationHeaderMeta";
|
|
55
|
+
import { conversationLinks } from "./ConversationLinkedWork";
|
|
34
56
|
import {
|
|
35
57
|
ConversationAnnotations,
|
|
36
58
|
ConversationPrivacyChip,
|
|
@@ -39,7 +61,10 @@ import {
|
|
|
39
61
|
hasConversationStats,
|
|
40
62
|
PendingAuthorization,
|
|
41
63
|
} from "./ConversationMeta";
|
|
42
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
ConversationUsageBreakdown,
|
|
66
|
+
hasConversationUsageBreakdown,
|
|
67
|
+
} from "./ConversationUsageBreakdown";
|
|
43
68
|
import { PendingMailboxStack } from "./PendingMailboxStack";
|
|
44
69
|
import {
|
|
45
70
|
buildConversations,
|
|
@@ -71,6 +96,14 @@ import type {
|
|
|
71
96
|
|
|
72
97
|
export { liveModelId } from "./ConversationMeta";
|
|
73
98
|
|
|
99
|
+
const detailsDefaultOpenQuery = "(min-width: 1440px)";
|
|
100
|
+
|
|
101
|
+
function subscribeToDetailsLayout(onChange: () => void) {
|
|
102
|
+
const media = window.matchMedia(detailsDefaultOpenQuery);
|
|
103
|
+
media.addEventListener("change", onChange);
|
|
104
|
+
return () => media.removeEventListener("change", onChange);
|
|
105
|
+
}
|
|
106
|
+
|
|
74
107
|
/** Render the selected conversation transcript inside the workspace. */
|
|
75
108
|
export function ConversationPage(props: {
|
|
76
109
|
conversationId: string;
|
|
@@ -78,7 +111,12 @@ export function ConversationPage(props: {
|
|
|
78
111
|
onRead?(conversationId: string, lastReadAt: string): void;
|
|
79
112
|
pendingArchiveUpdate?: PendingArchiveConversationUpdate;
|
|
80
113
|
}) {
|
|
81
|
-
const
|
|
114
|
+
const detailsDefaultOpen = useSyncExternalStore(
|
|
115
|
+
subscribeToDetailsLayout,
|
|
116
|
+
() => window.matchMedia(detailsDefaultOpenQuery).matches,
|
|
117
|
+
() => false,
|
|
118
|
+
);
|
|
119
|
+
const pageRef = useRef<HTMLDivElement>(null);
|
|
82
120
|
const [subagentTarget, setSubagentTarget] =
|
|
83
121
|
useState<SubagentTranscriptTarget>();
|
|
84
122
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
@@ -86,6 +124,16 @@ export function ConversationPage(props: {
|
|
|
86
124
|
const selectedEventSeq = parseConversationEventSeq(
|
|
87
125
|
searchParams.get(CONVERSATION_EVENT_PARAM),
|
|
88
126
|
);
|
|
127
|
+
// Event details and turn context show over Conversation details. Closing
|
|
128
|
+
// them shows details again when details are open. A linked event starts
|
|
129
|
+
// with details closed, so closing it does not open a panel nobody chose.
|
|
130
|
+
const [detailsChoice, setDetailsChoice] = useState<boolean | undefined>(() =>
|
|
131
|
+
selectedEventSeq === undefined ? undefined : false,
|
|
132
|
+
);
|
|
133
|
+
const detailsOpen = detailsChoice ?? detailsDefaultOpen;
|
|
134
|
+
const [turnContext, setTurnContext] = useState<TurnContextSelection>();
|
|
135
|
+
const eventOpen = selectedEventSeq !== undefined;
|
|
136
|
+
const showDetails = detailsOpen && !eventOpen && !turnContext;
|
|
89
137
|
const [viewState, setView] = useState<TranscriptViewMode>(() =>
|
|
90
138
|
selectedEventSeq === undefined ? "rich" : "raw",
|
|
91
139
|
);
|
|
@@ -121,6 +169,10 @@ export function ConversationPage(props: {
|
|
|
121
169
|
onRead?.(conversation.id, conversation.lastSeenAt);
|
|
122
170
|
}, [conversation, onRead]);
|
|
123
171
|
const transcript = detail.data;
|
|
172
|
+
const selectedEvent =
|
|
173
|
+
transcript?.eventHistory.status === "expired"
|
|
174
|
+
? undefined
|
|
175
|
+
: transcript?.events.find((event) => event.seq === selectedEventSeq);
|
|
124
176
|
const visualStatus = conversation
|
|
125
177
|
? visualStatusForConversation(conversation)
|
|
126
178
|
: undefined;
|
|
@@ -157,6 +209,8 @@ export function ConversationPage(props: {
|
|
|
157
209
|
if (value !== "raw" && selectedEventSeq !== undefined) {
|
|
158
210
|
setSelectedEventSeq(undefined);
|
|
159
211
|
}
|
|
212
|
+
// Turn context belongs to a message in the Conversation view.
|
|
213
|
+
setTurnContext(undefined);
|
|
160
214
|
setView(value);
|
|
161
215
|
},
|
|
162
216
|
[selectedEventSeq, setSelectedEventSeq],
|
|
@@ -178,6 +232,35 @@ export function ConversationPage(props: {
|
|
|
178
232
|
useEffect(() => {
|
|
179
233
|
if (loadLinkedEvent) loadPreviousPageRef.current();
|
|
180
234
|
}, [loadLinkedEvent, oldestLoadedSeq]);
|
|
235
|
+
// Keep the panel while the transcript or a linked event loads.
|
|
236
|
+
const eventLoading =
|
|
237
|
+
eventOpen &&
|
|
238
|
+
!selectedEvent &&
|
|
239
|
+
(detail.isPending ||
|
|
240
|
+
(linkedEventMissing &&
|
|
241
|
+
(detail.hasPreviousPage || detail.isLoadingPreviousPage) &&
|
|
242
|
+
!detail.historyError));
|
|
243
|
+
const showEvent = Boolean(selectedEvent) || eventLoading;
|
|
244
|
+
// Step through the same filtered rows that the event log shows.
|
|
245
|
+
const events = transcript?.events;
|
|
246
|
+
const normalizedSearch = search.trim().toLowerCase();
|
|
247
|
+
const eventRows = useMemo(
|
|
248
|
+
() =>
|
|
249
|
+
eventOpen && events
|
|
250
|
+
? filterEventLogRows(eventLogRows(events), normalizedSearch)
|
|
251
|
+
: [],
|
|
252
|
+
[eventOpen, events, normalizedSearch],
|
|
253
|
+
);
|
|
254
|
+
const turnContextPanel = useMemo<TurnContextPanel>(
|
|
255
|
+
() => ({
|
|
256
|
+
openId: turnContext?.id,
|
|
257
|
+
onToggle: (selection) =>
|
|
258
|
+
setTurnContext((current) =>
|
|
259
|
+
current?.id === selection.id ? undefined : selection,
|
|
260
|
+
),
|
|
261
|
+
}),
|
|
262
|
+
[turnContext?.id],
|
|
263
|
+
);
|
|
181
264
|
const requestPin = useCallback(() => {
|
|
182
265
|
setPinRequestVersion((version) => version + 1);
|
|
183
266
|
}, []);
|
|
@@ -198,111 +281,139 @@ export function ConversationPage(props: {
|
|
|
198
281
|
[],
|
|
199
282
|
);
|
|
200
283
|
|
|
284
|
+
const closeEventDetails = () => {
|
|
285
|
+
const trigger = pageRef.current?.querySelector<HTMLButtonElement>(
|
|
286
|
+
`[data-event-seq="${selectedEventSeq}"] button`,
|
|
287
|
+
);
|
|
288
|
+
setSelectedEventSeq(undefined);
|
|
289
|
+
trigger?.focus({ preventScroll: true });
|
|
290
|
+
};
|
|
291
|
+
const closeTurnContext = () => {
|
|
292
|
+
// A message can have one context control per layout. Focus the visible one.
|
|
293
|
+
const trigger = [
|
|
294
|
+
...(pageRef.current?.querySelectorAll<HTMLButtonElement>(
|
|
295
|
+
`[data-turn-context-id="${CSS.escape(turnContext?.id ?? "")}"]`,
|
|
296
|
+
) ?? []),
|
|
297
|
+
].find((button) => button.getClientRects().length > 0);
|
|
298
|
+
setTurnContext(undefined);
|
|
299
|
+
trigger?.focus({ preventScroll: true });
|
|
300
|
+
};
|
|
301
|
+
|
|
201
302
|
return (
|
|
202
303
|
<div
|
|
304
|
+
ref={pageRef}
|
|
203
305
|
className={cn(
|
|
204
306
|
"grid h-full min-h-0 min-w-0",
|
|
205
|
-
|
|
307
|
+
(showDetails || showEvent || turnContext) &&
|
|
308
|
+
"xl:grid-cols-[minmax(0,1fr)_25rem]",
|
|
206
309
|
)}
|
|
207
310
|
>
|
|
208
311
|
<ChatLayout
|
|
312
|
+
header={
|
|
313
|
+
<ConversationHeader
|
|
314
|
+
agentTree={
|
|
315
|
+
agentTree ? <ConversationAgentTree nodes={agentTree} /> : null
|
|
316
|
+
}
|
|
317
|
+
conversationId={conversationId}
|
|
318
|
+
detailsOpen={showDetails}
|
|
319
|
+
onDetailsClick={() => {
|
|
320
|
+
if (eventOpen) setSelectedEventSeq(undefined);
|
|
321
|
+
setTurnContext(undefined);
|
|
322
|
+
setDetailsChoice(!showDetails);
|
|
323
|
+
}}
|
|
324
|
+
forks={
|
|
325
|
+
detail.data?.forkedFromConversationId ||
|
|
326
|
+
detail.data?.forks?.length ? (
|
|
327
|
+
<ConversationForkLinks
|
|
328
|
+
forkedFromConversationId={
|
|
329
|
+
detail.data.forkedFromConversationId
|
|
330
|
+
}
|
|
331
|
+
forkedFromTitle={detail.data.forkedFromTitle}
|
|
332
|
+
forks={detail.data.forks}
|
|
333
|
+
/>
|
|
334
|
+
) : null
|
|
335
|
+
}
|
|
336
|
+
copyAction={
|
|
337
|
+
<CopyButton
|
|
338
|
+
label="Copy as Markdown"
|
|
339
|
+
copyingLabel="Preparing Markdown"
|
|
340
|
+
key={conversationDetail?.conversationId ?? "loading"}
|
|
341
|
+
getText={
|
|
342
|
+
conversationDetail
|
|
343
|
+
? async () =>
|
|
344
|
+
buildConversationMarkdown(
|
|
345
|
+
await detail.loadCompleteTranscript(),
|
|
346
|
+
conversation,
|
|
347
|
+
)
|
|
348
|
+
: undefined
|
|
349
|
+
}
|
|
350
|
+
/>
|
|
351
|
+
}
|
|
352
|
+
linkedWork={
|
|
353
|
+
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
354
|
+
<ConversationAnnotations detail={detail.data} />
|
|
355
|
+
) : null
|
|
356
|
+
}
|
|
357
|
+
archive={{
|
|
358
|
+
archived: Boolean(conversation?.archivedAt),
|
|
359
|
+
disabled: !conversation || archive.isPending,
|
|
360
|
+
error: Boolean(archive.error),
|
|
361
|
+
onClick: () =>
|
|
362
|
+
archive.mutate({
|
|
363
|
+
archived: !conversation?.archivedAt,
|
|
364
|
+
lastSeenAt: conversation!.lastSeenAt,
|
|
365
|
+
}),
|
|
366
|
+
pending: archive.isPending,
|
|
367
|
+
}}
|
|
368
|
+
live={live}
|
|
369
|
+
meta={
|
|
370
|
+
<ConversationHeaderMeta
|
|
371
|
+
identity={identity}
|
|
372
|
+
stats={
|
|
373
|
+
hasConversationStats({
|
|
374
|
+
conversation,
|
|
375
|
+
detail: detail.data,
|
|
376
|
+
variant: "compact",
|
|
377
|
+
}) ? (
|
|
378
|
+
<ConversationStats
|
|
379
|
+
conversation={conversation}
|
|
380
|
+
detail={detail.data}
|
|
381
|
+
variant="compact"
|
|
382
|
+
/>
|
|
383
|
+
) : null
|
|
384
|
+
}
|
|
385
|
+
/>
|
|
386
|
+
}
|
|
387
|
+
onSearchChange={setSearch}
|
|
388
|
+
onViewChange={onViewChange}
|
|
389
|
+
privacy={
|
|
390
|
+
<ConversationPrivacyChip visibility={conversation?.visibility} />
|
|
391
|
+
}
|
|
392
|
+
search={search}
|
|
393
|
+
title={title}
|
|
394
|
+
view={view}
|
|
395
|
+
/>
|
|
396
|
+
}
|
|
209
397
|
scrollMinTall
|
|
210
398
|
scrollAriaLabel="Conversation transcript"
|
|
211
|
-
scrollClassName=
|
|
399
|
+
scrollClassName={
|
|
400
|
+
view === "raw" ? undefined : "px-4 pb-4 md:px-7 md:pb-6"
|
|
401
|
+
}
|
|
212
402
|
scroll={
|
|
213
403
|
<section className="min-w-0">
|
|
214
|
-
<ConversationHeader
|
|
215
|
-
agentTree={
|
|
216
|
-
agentTree ? <ConversationAgentTree nodes={agentTree} /> : null
|
|
217
|
-
}
|
|
218
|
-
conversationId={conversationId}
|
|
219
|
-
detailsOpen={detailsOpen}
|
|
220
|
-
onDetailsClick={() => setDetailsOpen((open) => !open)}
|
|
221
|
-
forks={
|
|
222
|
-
detail.data?.forkedFromConversationId ||
|
|
223
|
-
detail.data?.forks?.length ? (
|
|
224
|
-
<ConversationForkLinks
|
|
225
|
-
forkedFromConversationId={
|
|
226
|
-
detail.data.forkedFromConversationId
|
|
227
|
-
}
|
|
228
|
-
forkedFromTitle={detail.data.forkedFromTitle}
|
|
229
|
-
forks={detail.data.forks}
|
|
230
|
-
/>
|
|
231
|
-
) : null
|
|
232
|
-
}
|
|
233
|
-
copyAction={
|
|
234
|
-
<CopyMarkdownButton
|
|
235
|
-
key={conversationDetail?.conversationId ?? "loading"}
|
|
236
|
-
getMarkdown={
|
|
237
|
-
conversationDetail
|
|
238
|
-
? async () =>
|
|
239
|
-
buildConversationMarkdown(
|
|
240
|
-
await detail.loadCompleteTranscript(),
|
|
241
|
-
conversation,
|
|
242
|
-
)
|
|
243
|
-
: undefined
|
|
244
|
-
}
|
|
245
|
-
/>
|
|
246
|
-
}
|
|
247
|
-
linkedWork={
|
|
248
|
-
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
249
|
-
<ConversationAnnotations
|
|
250
|
-
detail={detail.data}
|
|
251
|
-
layout="strip"
|
|
252
|
-
/>
|
|
253
|
-
) : null
|
|
254
|
-
}
|
|
255
|
-
archive={{
|
|
256
|
-
archived: Boolean(conversation?.archivedAt),
|
|
257
|
-
disabled: !conversation || archive.isPending,
|
|
258
|
-
error: Boolean(archive.error),
|
|
259
|
-
onClick: () =>
|
|
260
|
-
archive.mutate({
|
|
261
|
-
archived: !conversation?.archivedAt,
|
|
262
|
-
lastSeenAt: conversation!.lastSeenAt,
|
|
263
|
-
}),
|
|
264
|
-
pending: archive.isPending,
|
|
265
|
-
}}
|
|
266
|
-
live={live}
|
|
267
|
-
meta={
|
|
268
|
-
<ConversationHeaderMeta
|
|
269
|
-
identity={identity}
|
|
270
|
-
stats={
|
|
271
|
-
hasConversationStats({
|
|
272
|
-
conversation,
|
|
273
|
-
detail: detail.data,
|
|
274
|
-
variant: "compact",
|
|
275
|
-
}) ? (
|
|
276
|
-
<ConversationStats
|
|
277
|
-
conversation={conversation}
|
|
278
|
-
detail={detail.data}
|
|
279
|
-
variant="compact"
|
|
280
|
-
/>
|
|
281
|
-
) : null
|
|
282
|
-
}
|
|
283
|
-
/>
|
|
284
|
-
}
|
|
285
|
-
onSearchChange={setSearch}
|
|
286
|
-
onViewChange={onViewChange}
|
|
287
|
-
privacy={
|
|
288
|
-
<ConversationPrivacyChip
|
|
289
|
-
visibility={conversation?.visibility}
|
|
290
|
-
/>
|
|
291
|
-
}
|
|
292
|
-
search={search}
|
|
293
|
-
title={title}
|
|
294
|
-
view={view}
|
|
295
|
-
/>
|
|
296
|
-
|
|
297
404
|
<div
|
|
298
405
|
className={
|
|
299
406
|
view === "raw"
|
|
300
|
-
? "w-full
|
|
407
|
+
? "w-full"
|
|
301
408
|
: "mx-auto w-full max-w-[52.5rem] pt-5 md:pt-7"
|
|
302
409
|
}
|
|
303
410
|
>
|
|
304
411
|
{detail.isPending ? (
|
|
305
|
-
|
|
412
|
+
view === "raw" ? (
|
|
413
|
+
<ConversationEventLogLoading />
|
|
414
|
+
) : (
|
|
415
|
+
<TranscriptLoading />
|
|
416
|
+
)
|
|
306
417
|
) : detail.error && !detail.data ? (
|
|
307
418
|
<Card className="border-dashboard-border bg-dashboard-fill-soft p-4 font-sans text-xs leading-relaxed text-dashboard-text-muted">
|
|
308
419
|
{detail.error.message}
|
|
@@ -329,8 +440,10 @@ export function ConversationPage(props: {
|
|
|
329
440
|
}
|
|
330
441
|
onOpenSubagentTranscript={onOpenSubagentTranscript}
|
|
331
442
|
search={search}
|
|
443
|
+
onSearchChange={setSearch}
|
|
332
444
|
selectedEventSeq={selectedEventSeq}
|
|
333
445
|
transcript={transcript}
|
|
446
|
+
turnContextPanel={turnContextPanel}
|
|
334
447
|
view={view}
|
|
335
448
|
/>
|
|
336
449
|
</>
|
|
@@ -358,43 +471,61 @@ export function ConversationPage(props: {
|
|
|
358
471
|
) : undefined
|
|
359
472
|
}
|
|
360
473
|
/>
|
|
361
|
-
{
|
|
474
|
+
{showDetails ? (
|
|
362
475
|
<ConversationDetails
|
|
363
476
|
conversationId={conversationId}
|
|
364
477
|
identity={identity}
|
|
365
|
-
lastActivityAt={conversation?.lastSeenAt}
|
|
366
478
|
sentryConversationUrl={detail.data?.sentryConversationUrl}
|
|
367
|
-
onClose={() =>
|
|
479
|
+
onClose={() => setDetailsChoice(false)}
|
|
368
480
|
onOpenSubagent={setSubagentTarget}
|
|
369
|
-
|
|
370
|
-
hasConversationAnnotations(detail.data?.annotations) ? (
|
|
371
|
-
<ConversationAnnotations detail={detail.data} />
|
|
372
|
-
) : null
|
|
373
|
-
}
|
|
481
|
+
links={conversationLinks(detail.data)}
|
|
374
482
|
brief={
|
|
375
483
|
detail.data?.brief ? (
|
|
376
484
|
<ConversationBrief brief={detail.data.brief} variant="summary" />
|
|
377
485
|
) : null
|
|
378
486
|
}
|
|
379
|
-
|
|
487
|
+
overview={
|
|
380
488
|
hasConversationStats({
|
|
381
489
|
conversation,
|
|
382
490
|
detail: detail.data,
|
|
383
491
|
}) ? (
|
|
384
|
-
<
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
492
|
+
<ConversationStats
|
|
493
|
+
conversation={conversation}
|
|
494
|
+
detail={detail.data}
|
|
495
|
+
/>
|
|
496
|
+
) : null
|
|
497
|
+
}
|
|
498
|
+
usage={
|
|
499
|
+
hasConversationUsageBreakdown({
|
|
500
|
+
conversation,
|
|
501
|
+
detail: detail.data,
|
|
502
|
+
}) ? (
|
|
503
|
+
<ConversationUsageBreakdown
|
|
504
|
+
conversation={conversation}
|
|
505
|
+
detail={detail.data}
|
|
506
|
+
/>
|
|
394
507
|
) : null
|
|
395
508
|
}
|
|
396
509
|
/>
|
|
397
510
|
) : null}
|
|
511
|
+
{showEvent ? (
|
|
512
|
+
<ConversationEventDetails
|
|
513
|
+
conversation={transcript}
|
|
514
|
+
event={selectedEvent}
|
|
515
|
+
onClose={closeEventDetails}
|
|
516
|
+
onSelect={setSelectedEventSeq}
|
|
517
|
+
{...adjacentEventLogSeqs(eventRows, selectedEventSeq)}
|
|
518
|
+
/>
|
|
519
|
+
) : null}
|
|
520
|
+
{turnContext ? (
|
|
521
|
+
<TranscriptSearchProvider query={search}>
|
|
522
|
+
<TurnContextDetails
|
|
523
|
+
contexts={turnContext.contexts}
|
|
524
|
+
key={turnContext.id}
|
|
525
|
+
onClose={closeTurnContext}
|
|
526
|
+
/>
|
|
527
|
+
</TranscriptSearchProvider>
|
|
528
|
+
) : null}
|
|
398
529
|
<SubagentTranscriptDrawer
|
|
399
530
|
onClose={() => setSubagentTarget(undefined)}
|
|
400
531
|
target={subagentTarget}
|
|
@@ -19,7 +19,12 @@ import {
|
|
|
19
19
|
} from "./conversationSections";
|
|
20
20
|
import { EmptyTelemetry } from "../components/EmptyTelemetry";
|
|
21
21
|
import { SearchInput } from "../components/SearchInput";
|
|
22
|
-
import { Skeleton } from "../components/
|
|
22
|
+
import { Skeleton } from "../components/ui/skeleton";
|
|
23
|
+
import {
|
|
24
|
+
Tooltip,
|
|
25
|
+
TooltipContent,
|
|
26
|
+
TooltipTrigger,
|
|
27
|
+
} from "../components/ui/tooltip";
|
|
23
28
|
import { ConversationSidebarAnnotations } from "./ConversationMeta";
|
|
24
29
|
import { ConversationListStatusIcon } from "./ConversationListStatusIcon";
|
|
25
30
|
|
|
@@ -104,15 +109,21 @@ export function ConversationSidebar(props: {
|
|
|
104
109
|
</h2>
|
|
105
110
|
<div className="flex items-center gap-0.5">
|
|
106
111
|
{isLanding ? null : (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
<Tooltip>
|
|
113
|
+
<TooltipTrigger
|
|
114
|
+
render={
|
|
115
|
+
<button
|
|
116
|
+
aria-label="New conversation"
|
|
117
|
+
className="grid size-7 cursor-pointer place-items-center rounded-md text-dashboard-text-muted transition hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashboard-focus"
|
|
118
|
+
onClick={props.onNewConversation}
|
|
119
|
+
type="button"
|
|
120
|
+
>
|
|
121
|
+
<SquarePen aria-hidden="true" size={15} />
|
|
122
|
+
</button>
|
|
123
|
+
}
|
|
124
|
+
/>
|
|
125
|
+
<TooltipContent size="compact">New conversation</TooltipContent>
|
|
126
|
+
</Tooltip>
|
|
116
127
|
)}
|
|
117
128
|
</div>
|
|
118
129
|
</div>
|
|
@@ -321,25 +332,33 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(props: {
|
|
|
321
332
|
) : null}
|
|
322
333
|
</div>
|
|
323
334
|
</Link>
|
|
324
|
-
<
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
335
|
+
<Tooltip>
|
|
336
|
+
<TooltipTrigger
|
|
337
|
+
render={
|
|
338
|
+
<button
|
|
339
|
+
aria-label={`${props.conversation.archivedAt ? "Restore" : "Archive"} ${title}`}
|
|
340
|
+
className="absolute right-1.5 top-1/2 z-10 grid size-7 -translate-y-1/2 cursor-pointer place-items-center rounded-md bg-dashboard-surface-active text-dashboard-text-muted transition hover:text-dashboard-text focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-dashboard-focus sm:pointer-events-none sm:opacity-0 sm:focus:pointer-events-auto sm:focus:opacity-100 sm:group-focus-within:pointer-events-auto sm:group-focus-within:opacity-100 sm:group-hover:pointer-events-auto sm:group-hover:opacity-100 disabled:cursor-not-allowed"
|
|
341
|
+
disabled={archive.isPending}
|
|
342
|
+
onClick={() =>
|
|
343
|
+
archive.mutate({
|
|
344
|
+
archived: !props.conversation.archivedAt,
|
|
345
|
+
lastSeenAt: props.conversation.lastSeenAt,
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
type="button"
|
|
349
|
+
>
|
|
350
|
+
{props.conversation.archivedAt ? (
|
|
351
|
+
<ArchiveRestore aria-hidden="true" size={14} />
|
|
352
|
+
) : (
|
|
353
|
+
<Archive aria-hidden="true" size={14} />
|
|
354
|
+
)}
|
|
355
|
+
</button>
|
|
356
|
+
}
|
|
357
|
+
/>
|
|
358
|
+
<TooltipContent size="compact">
|
|
359
|
+
{props.conversation.archivedAt ? "Restore" : "Archive"}
|
|
360
|
+
</TooltipContent>
|
|
361
|
+
</Tooltip>
|
|
343
362
|
</div>
|
|
344
363
|
);
|
|
345
364
|
});
|
|
@@ -167,6 +167,19 @@ function BreakdownTable(props: {
|
|
|
167
167
|
);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/** True when the Conversation has model or auxiliary usage to break down. */
|
|
171
|
+
export function hasConversationUsageBreakdown(props: {
|
|
172
|
+
conversation: Conversation | undefined;
|
|
173
|
+
detail?: ConversationDetailReport;
|
|
174
|
+
}): boolean {
|
|
175
|
+
if (!props.conversation) return false;
|
|
176
|
+
const auxiliaryCosts =
|
|
177
|
+
props.detail?.auxiliaryCosts ?? props.conversation.auxiliaryCosts;
|
|
178
|
+
return Boolean(
|
|
179
|
+
props.detail?.modelUsage?.length || auxiliaryCosts?.operations?.length,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
170
183
|
/**
|
|
171
184
|
* Break a Conversation's usage down by agent, model, and auxiliary
|
|
172
185
|
* operation. The header tooltips show only the largest items; this lists
|
|
@@ -177,7 +190,9 @@ export function ConversationUsageBreakdown(props: {
|
|
|
177
190
|
conversation: Conversation | undefined;
|
|
178
191
|
detail?: ConversationDetailReport;
|
|
179
192
|
}) {
|
|
180
|
-
if (!props.conversation)
|
|
193
|
+
if (!hasConversationUsageBreakdown(props) || !props.conversation) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
181
196
|
const modelUsage = props.detail?.modelUsage ?? [];
|
|
182
197
|
const auxiliaryCosts =
|
|
183
198
|
props.detail?.auxiliaryCosts ?? props.conversation.auxiliaryCosts;
|
|
@@ -193,8 +208,6 @@ export function ConversationUsageBreakdown(props: {
|
|
|
193
208
|
const operations = [...(auxiliaryCosts?.operations ?? [])].sort(
|
|
194
209
|
(a, b) => b.costUsd - a.costUsd,
|
|
195
210
|
);
|
|
196
|
-
if (!modelUsage.length && !operations.length) return null;
|
|
197
|
-
|
|
198
211
|
const formatCost = costFormatter(cost ?? 0);
|
|
199
212
|
const formatTokens = (value: number | undefined) =>
|
|
200
213
|
value === undefined ? NOT_RECORDED : formatCompactNumber(value);
|