@sentry/junior-dashboard 0.264.0 → 0.266.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +2 -5
  3. package/dist/app.js +119 -35
  4. package/dist/client.js +145 -136
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.js +119 -35
  7. package/dist/tailwind.css +1 -1
  8. package/package.json +5 -4
  9. package/src/api/schema.ts +0 -1
  10. package/src/app.ts +5 -20
  11. package/src/client/App.tsx +29 -31
  12. package/src/client/api.ts +7 -0
  13. package/src/client/components/ActorAvatar.tsx +10 -22
  14. package/src/client/components/BottomSheet.tsx +12 -2
  15. package/src/client/components/ConnectionBanner.tsx +4 -3
  16. package/src/client/components/CopyButton.tsx +82 -0
  17. package/src/client/components/DetailsPanel.tsx +133 -0
  18. package/src/client/components/Drawer.tsx +15 -24
  19. package/src/client/components/EmptyTelemetry.tsx +5 -3
  20. package/src/client/components/FilterBar.tsx +16 -31
  21. package/src/client/components/ImageAttachment.tsx +49 -63
  22. package/src/client/components/InlineError.tsx +8 -4
  23. package/src/client/components/Metric.tsx +33 -23
  24. package/src/client/components/Notice.tsx +19 -26
  25. package/src/client/components/PageContentSkeleton.tsx +1 -1
  26. package/src/client/components/Pagination.tsx +27 -27
  27. package/src/client/components/ParticipantAvatarStack.tsx +43 -39
  28. package/src/client/components/ProfileMenu.tsx +27 -93
  29. package/src/client/components/README.md +103 -78
  30. package/src/client/components/SearchInput.tsx +51 -41
  31. package/src/client/components/SegmentedNav.tsx +1 -1
  32. package/src/client/components/ShimmerText.tsx +2 -6
  33. package/src/client/components/ThemeSwitcher.tsx +19 -15
  34. package/src/client/components/VersionDriftBanner.tsx +9 -7
  35. package/src/client/components/charts/ActivityChart.tsx +173 -288
  36. package/src/client/components/charts/CacheWrites.tsx +59 -37
  37. package/src/client/components/charts/InputCacheChart.tsx +137 -131
  38. package/src/client/components/charts/README.md +4 -4
  39. package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
  40. package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
  41. package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
  42. package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
  43. package/src/client/components/layout/DashboardHeader.tsx +44 -90
  44. package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
  45. package/src/client/components/layout/VisualViewportShell.tsx +2 -2
  46. package/src/client/components/ui/accordion.tsx +77 -0
  47. package/src/client/components/ui/alert-dialog.tsx +89 -0
  48. package/src/client/components/ui/alert.tsx +64 -0
  49. package/src/client/components/ui/avatar.tsx +53 -0
  50. package/src/client/components/ui/button.tsx +86 -0
  51. package/src/client/components/ui/chart.tsx +74 -0
  52. package/src/client/components/ui/checkbox.tsx +26 -0
  53. package/src/client/components/ui/collapsible.tsx +22 -0
  54. package/src/client/components/ui/dialog.tsx +66 -0
  55. package/src/client/components/ui/empty.tsx +32 -0
  56. package/src/client/components/ui/field.tsx +49 -0
  57. package/src/client/components/ui/input-group.tsx +96 -0
  58. package/src/client/components/ui/input.tsx +35 -0
  59. package/src/client/components/ui/native-select.tsx +57 -0
  60. package/src/client/components/ui/pagination.tsx +60 -0
  61. package/src/client/components/ui/popover.tsx +67 -0
  62. package/src/client/components/ui/radio-group.tsx +32 -0
  63. package/src/client/components/ui/sheet.tsx +13 -3
  64. package/src/client/components/ui/skeleton.tsx +19 -0
  65. package/src/client/components/ui/spinner.tsx +21 -0
  66. package/src/client/components/ui/table.tsx +101 -0
  67. package/src/client/components/ui/tabs.tsx +75 -0
  68. package/src/client/components/ui/textarea.tsx +17 -0
  69. package/src/client/components/ui/toast.tsx +215 -0
  70. package/src/client/components/ui/toggle-group.tsx +36 -0
  71. package/src/client/components/ui/toggle.tsx +37 -0
  72. package/src/client/components/ui/tooltip.tsx +168 -0
  73. package/src/client/components/ui/trace.tsx +96 -0
  74. package/src/client/conversations/ChatLayout.tsx +7 -3
  75. package/src/client/conversations/ConversationBrief.tsx +17 -6
  76. package/src/client/conversations/ConversationChildAgents.tsx +5 -5
  77. package/src/client/conversations/ConversationComposer.tsx +26 -12
  78. package/src/client/conversations/ConversationDetails.tsx +147 -62
  79. package/src/client/conversations/ConversationEventDetails.tsx +141 -0
  80. package/src/client/conversations/ConversationEventLog.tsx +150 -129
  81. package/src/client/conversations/ConversationHeader.tsx +13 -7
  82. package/src/client/conversations/ConversationHeaderActions.tsx +97 -93
  83. package/src/client/conversations/ConversationHomeList.tsx +1 -1
  84. package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
  85. package/src/client/conversations/ConversationMeta.tsx +55 -88
  86. package/src/client/conversations/ConversationPage.tsx +220 -101
  87. package/src/client/conversations/ConversationSidebar.tsx +49 -29
  88. package/src/client/conversations/ConversationUsageBreakdown.tsx +108 -65
  89. package/src/client/conversations/ConversationWatches.tsx +173 -0
  90. package/src/client/conversations/ConversationWorkspace.tsx +12 -22
  91. package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
  92. package/src/client/conversations/DetailsList.tsx +139 -0
  93. package/src/client/conversations/EventDetails.tsx +42 -8
  94. package/src/client/conversations/EventUsage.tsx +99 -74
  95. package/src/client/conversations/ForkConversationButton.tsx +27 -16
  96. package/src/client/conversations/ObjectCard.tsx +20 -8
  97. package/src/client/conversations/PendingMailboxStack.tsx +52 -38
  98. package/src/client/conversations/README.md +89 -13
  99. package/src/client/conversations/ShareConversationButton.tsx +28 -17
  100. package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
  101. package/src/client/conversations/ToolFrame.tsx +36 -52
  102. package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
  103. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
  104. package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
  105. package/src/client/conversations/TranscriptEventView.tsx +35 -16
  106. package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
  107. package/src/client/conversations/TranscriptLoading.tsx +1 -1
  108. package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
  109. package/src/client/conversations/TranscriptMessageContextView.tsx +35 -18
  110. package/src/client/conversations/TranscriptMessageView.tsx +6 -1
  111. package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
  112. package/src/client/conversations/TranscriptStructuredEventView.tsx +25 -15
  113. package/src/client/conversations/TranscriptSummary.tsx +34 -2
  114. package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
  115. package/src/client/conversations/TranscriptToolView.tsx +1 -1
  116. package/src/client/conversations/TranscriptTurnContextView.tsx +144 -65
  117. package/src/client/conversations/TranscriptView.tsx +63 -27
  118. package/src/client/conversations/eventLog.ts +58 -0
  119. package/src/client/conversations/eventTime.ts +60 -0
  120. package/src/client/conversations/queries.ts +29 -0
  121. package/src/client/conversations/transcriptBottomPinning.ts +34 -0
  122. package/src/client/conversations/transcriptMentions.tsx +18 -5
  123. package/src/client/format.ts +5 -0
  124. package/src/client/pages/ConnectionsPage.tsx +2 -2
  125. package/src/client/pages/PersonalTokensPage.tsx +4 -4
  126. package/src/client/pages/SettingsPage.tsx +9 -10
  127. package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
  128. package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
  129. package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
  130. package/src/client/pages/automations/AutomationDetails.tsx +193 -0
  131. package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
  132. package/src/client/pages/automations/AutomationEditor.tsx +41 -29
  133. package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
  134. package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
  135. package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
  136. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
  137. package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
  138. package/src/client/pages/automations/AutomationFilters.tsx +189 -74
  139. package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
  140. package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
  141. package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
  142. package/src/client/pages/automations/AutomationRow.tsx +12 -138
  143. package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
  144. package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
  145. package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
  146. package/src/client/pages/automations/AutomationStatus.tsx +201 -0
  147. package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
  148. package/src/client/pages/automations/AutomationsPage.tsx +80 -68
  149. package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
  150. package/src/client/pages/automations/README.md +57 -0
  151. package/src/client/pages/automations/TriggerFilters.tsx +22 -0
  152. package/src/client/pages/automations/automationFormat.ts +37 -23
  153. package/src/client/pages/code/CodeActivityChart.tsx +59 -101
  154. package/src/client/pages/code/CodePage.tsx +52 -55
  155. package/src/client/pages/code/RepositoryPage.tsx +6 -8
  156. package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
  157. package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
  158. package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
  159. package/src/client/pages/memory/MemoryDetails.tsx +218 -0
  160. package/src/client/pages/memory/MemoryPage.tsx +168 -112
  161. package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
  162. package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
  163. package/src/client/pages/memory/memoryRecord.ts +1 -1
  164. package/src/client/pages/people/ContributionGrid.tsx +23 -16
  165. package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
  166. package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
  167. package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
  168. package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
  169. package/src/client/pages/system/GuardianActivity.tsx +1 -2
  170. package/src/client/pages/system/PluginBarChart.tsx +106 -87
  171. package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
  172. package/src/client/pages/system/README.md +23 -0
  173. package/src/client/pages/system/SystemNavigation.tsx +5 -1
  174. package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
  175. package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
  176. package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
  177. package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
  178. package/src/client/pages/system/WorkspaceList.tsx +20 -30
  179. package/src/client/pages/system/WorkspacesPage.tsx +117 -119
  180. package/src/client/pages/system/sandboxSize.ts +8 -0
  181. package/src/client/pages/system/workspaceQueries.ts +53 -0
  182. package/src/client/pages/user/PluginUserPage.tsx +51 -2
  183. package/src/client/pages/user/pluginUserPageData.ts +1 -6
  184. package/src/client/styles.ts +4 -0
  185. package/src/client.tsx +7 -1
  186. package/src/mock-reporting/automation-collection.ts +4 -2
  187. package/src/mock-reporting/fixtures.ts +23 -7
  188. package/src/mock-reporting/routes.ts +12 -0
  189. package/src/mock-reporting/watches.ts +94 -0
  190. package/src/shell.ts +4 -9
  191. package/src/tailwind.css +0 -10
  192. package/src/client/components/Button.tsx +0 -118
  193. package/src/client/components/Field.tsx +0 -53
  194. package/src/client/components/SegmentedTabs.tsx +0 -119
  195. package/src/client/components/Select.tsx +0 -52
  196. package/src/client/components/Skeleton.tsx +0 -17
  197. package/src/client/components/TextInput.tsx +0 -58
  198. package/src/client/components/Tooltip.tsx +0 -216
  199. package/src/client/components/charts/useChartLayout.ts +0 -27
  200. package/src/client/components/ui/sheet.LICENSE +0 -21
  201. package/src/client/conversations/ConversationDetailsFrame.tsx +0 -119
  202. package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
  203. package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
  204. package/src/client/pages/dev/ChartFixtures.tsx +0 -325
  205. package/src/client/pages/dev/ComponentsPage.tsx +0 -899
  206. package/src/client/pages/dev/ConversationFixture.tsx +0 -231
  207. package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
  208. package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
  209. package/src/client/pages/dev/SelectFixtures.tsx +0 -62
  210. package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
  211. package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
  212. package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
  213. package/src/client/pages/system/SnapshotSummary.tsx +0 -78
@@ -1,4 +1,12 @@
1
- import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
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 { CopyMarkdownButton } from "./CopyMarkdownButton";
29
+ import { CopyButton } from "../components/CopyButton";
22
30
  import { ConversationBrief } from "./ConversationBrief";
23
31
  import {
24
32
  CONVERSATION_EVENT_PARAM,
@@ -27,11 +35,24 @@ import {
27
35
  import { ConversationComposer } from "./ConversationComposer";
28
36
  import { ConversationHeader } from "./ConversationHeader";
29
37
  import { ConversationDetails } from "./ConversationDetails";
30
- import { useConversationDetailsDefaultOpen } from "./ConversationDetailsFrame";
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";
31
51
  import { cn } from "../styles";
32
52
  import { ConversationForkLinks } from "./ConversationForkLinks";
33
53
  import { ConversationAgentTree } from "./ConversationAgentTree";
34
54
  import { ConversationHeaderMeta } from "./ConversationHeaderMeta";
55
+ import { conversationLinks } from "./ConversationLinkedWork";
35
56
  import {
36
57
  ConversationAnnotations,
37
58
  ConversationPrivacyChip,
@@ -75,6 +96,14 @@ import type {
75
96
 
76
97
  export { liveModelId } from "./ConversationMeta";
77
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
+
78
107
  /** Render the selected conversation transcript inside the workspace. */
79
108
  export function ConversationPage(props: {
80
109
  conversationId: string;
@@ -82,11 +111,12 @@ export function ConversationPage(props: {
82
111
  onRead?(conversationId: string, lastReadAt: string): void;
83
112
  pendingArchiveUpdate?: PendingArchiveConversationUpdate;
84
113
  }) {
85
- // Large viewports show details beside the chat until the reader closes
86
- // them. Smaller viewports open details only on request.
87
- const detailsDefaultOpen = useConversationDetailsDefaultOpen();
88
- const [detailsChoice, setDetailsChoice] = useState<boolean>();
89
- const detailsOpen = detailsChoice ?? detailsDefaultOpen;
114
+ const detailsDefaultOpen = useSyncExternalStore(
115
+ subscribeToDetailsLayout,
116
+ () => window.matchMedia(detailsDefaultOpenQuery).matches,
117
+ () => false,
118
+ );
119
+ const pageRef = useRef<HTMLDivElement>(null);
90
120
  const [subagentTarget, setSubagentTarget] =
91
121
  useState<SubagentTranscriptTarget>();
92
122
  const [searchParams, setSearchParams] = useSearchParams();
@@ -94,6 +124,16 @@ export function ConversationPage(props: {
94
124
  const selectedEventSeq = parseConversationEventSeq(
95
125
  searchParams.get(CONVERSATION_EVENT_PARAM),
96
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;
97
137
  const [viewState, setView] = useState<TranscriptViewMode>(() =>
98
138
  selectedEventSeq === undefined ? "rich" : "raw",
99
139
  );
@@ -129,6 +169,10 @@ export function ConversationPage(props: {
129
169
  onRead?.(conversation.id, conversation.lastSeenAt);
130
170
  }, [conversation, onRead]);
131
171
  const transcript = detail.data;
172
+ const selectedEvent =
173
+ transcript?.eventHistory.status === "expired"
174
+ ? undefined
175
+ : transcript?.events.find((event) => event.seq === selectedEventSeq);
132
176
  const visualStatus = conversation
133
177
  ? visualStatusForConversation(conversation)
134
178
  : undefined;
@@ -165,6 +209,8 @@ export function ConversationPage(props: {
165
209
  if (value !== "raw" && selectedEventSeq !== undefined) {
166
210
  setSelectedEventSeq(undefined);
167
211
  }
212
+ // Turn context belongs to a message in the Conversation view.
213
+ setTurnContext(undefined);
168
214
  setView(value);
169
215
  },
170
216
  [selectedEventSeq, setSelectedEventSeq],
@@ -186,6 +232,35 @@ export function ConversationPage(props: {
186
232
  useEffect(() => {
187
233
  if (loadLinkedEvent) loadPreviousPageRef.current();
188
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
+ );
189
264
  const requestPin = useCallback(() => {
190
265
  setPinRequestVersion((version) => version + 1);
191
266
  }, []);
@@ -206,111 +281,139 @@ export function ConversationPage(props: {
206
281
  [],
207
282
  );
208
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
+
209
302
  return (
210
303
  <div
304
+ ref={pageRef}
211
305
  className={cn(
212
306
  "grid h-full min-h-0 min-w-0",
213
- detailsOpen && "xl:grid-cols-[minmax(0,1fr)_25rem]",
307
+ (showDetails || showEvent || turnContext) &&
308
+ "xl:grid-cols-[minmax(0,1fr)_25rem]",
214
309
  )}
215
310
  >
216
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
+ }
217
397
  scrollMinTall
218
398
  scrollAriaLabel="Conversation transcript"
219
- scrollClassName="px-4 pb-4 md:px-7 md:pb-6"
399
+ scrollClassName={
400
+ view === "raw" ? undefined : "px-4 pb-4 md:px-7 md:pb-6"
401
+ }
220
402
  scroll={
221
403
  <section className="min-w-0">
222
- <ConversationHeader
223
- agentTree={
224
- agentTree ? <ConversationAgentTree nodes={agentTree} /> : null
225
- }
226
- conversationId={conversationId}
227
- detailsOpen={detailsOpen}
228
- onDetailsClick={() => setDetailsChoice(!detailsOpen)}
229
- forks={
230
- detail.data?.forkedFromConversationId ||
231
- detail.data?.forks?.length ? (
232
- <ConversationForkLinks
233
- forkedFromConversationId={
234
- detail.data.forkedFromConversationId
235
- }
236
- forkedFromTitle={detail.data.forkedFromTitle}
237
- forks={detail.data.forks}
238
- />
239
- ) : null
240
- }
241
- copyAction={
242
- <CopyMarkdownButton
243
- key={conversationDetail?.conversationId ?? "loading"}
244
- getMarkdown={
245
- conversationDetail
246
- ? async () =>
247
- buildConversationMarkdown(
248
- await detail.loadCompleteTranscript(),
249
- conversation,
250
- )
251
- : undefined
252
- }
253
- />
254
- }
255
- linkedWork={
256
- hasConversationAnnotations(detail.data?.annotations) ? (
257
- <ConversationAnnotations
258
- detail={detail.data}
259
- layout="strip"
260
- />
261
- ) : null
262
- }
263
- archive={{
264
- archived: Boolean(conversation?.archivedAt),
265
- disabled: !conversation || archive.isPending,
266
- error: Boolean(archive.error),
267
- onClick: () =>
268
- archive.mutate({
269
- archived: !conversation?.archivedAt,
270
- lastSeenAt: conversation!.lastSeenAt,
271
- }),
272
- pending: archive.isPending,
273
- }}
274
- live={live}
275
- meta={
276
- <ConversationHeaderMeta
277
- identity={identity}
278
- stats={
279
- hasConversationStats({
280
- conversation,
281
- detail: detail.data,
282
- variant: "compact",
283
- }) ? (
284
- <ConversationStats
285
- conversation={conversation}
286
- detail={detail.data}
287
- variant="compact"
288
- />
289
- ) : null
290
- }
291
- />
292
- }
293
- onSearchChange={setSearch}
294
- onViewChange={onViewChange}
295
- privacy={
296
- <ConversationPrivacyChip
297
- visibility={conversation?.visibility}
298
- />
299
- }
300
- search={search}
301
- title={title}
302
- view={view}
303
- />
304
-
305
404
  <div
306
405
  className={
307
406
  view === "raw"
308
- ? "w-full pt-5 md:pt-7"
407
+ ? "w-full"
309
408
  : "mx-auto w-full max-w-[52.5rem] pt-5 md:pt-7"
310
409
  }
311
410
  >
312
411
  {detail.isPending ? (
313
- <TranscriptLoading />
412
+ view === "raw" ? (
413
+ <ConversationEventLogLoading />
414
+ ) : (
415
+ <TranscriptLoading />
416
+ )
314
417
  ) : detail.error && !detail.data ? (
315
418
  <Card className="border-dashboard-border bg-dashboard-fill-soft p-4 font-sans text-xs leading-relaxed text-dashboard-text-muted">
316
419
  {detail.error.message}
@@ -337,8 +440,10 @@ export function ConversationPage(props: {
337
440
  }
338
441
  onOpenSubagentTranscript={onOpenSubagentTranscript}
339
442
  search={search}
443
+ onSearchChange={setSearch}
340
444
  selectedEventSeq={selectedEventSeq}
341
445
  transcript={transcript}
446
+ turnContextPanel={turnContextPanel}
342
447
  view={view}
343
448
  />
344
449
  </>
@@ -366,18 +471,14 @@ export function ConversationPage(props: {
366
471
  ) : undefined
367
472
  }
368
473
  />
369
- {detailsOpen ? (
474
+ {showDetails ? (
370
475
  <ConversationDetails
371
476
  conversationId={conversationId}
372
477
  identity={identity}
373
478
  sentryConversationUrl={detail.data?.sentryConversationUrl}
374
479
  onClose={() => setDetailsChoice(false)}
375
480
  onOpenSubagent={setSubagentTarget}
376
- annotations={
377
- hasConversationAnnotations(detail.data?.annotations) ? (
378
- <ConversationAnnotations detail={detail.data} />
379
- ) : null
380
- }
481
+ links={conversationLinks(detail.data)}
381
482
  brief={
382
483
  detail.data?.brief ? (
383
484
  <ConversationBrief brief={detail.data.brief} variant="summary" />
@@ -407,6 +508,24 @@ export function ConversationPage(props: {
407
508
  }
408
509
  />
409
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}
410
529
  <SubagentTranscriptDrawer
411
530
  onClose={() => setSubagentTarget(undefined)}
412
531
  target={subagentTarget}
@@ -19,7 +19,13 @@ import {
19
19
  } from "./conversationSections";
20
20
  import { EmptyTelemetry } from "../components/EmptyTelemetry";
21
21
  import { SearchInput } from "../components/SearchInput";
22
- import { Skeleton } from "../components/Skeleton";
22
+ import { Button } from "../components/ui/button";
23
+ import { Skeleton } from "../components/ui/skeleton";
24
+ import {
25
+ Tooltip,
26
+ TooltipContent,
27
+ TooltipTrigger,
28
+ } from "../components/ui/tooltip";
23
29
  import { ConversationSidebarAnnotations } from "./ConversationMeta";
24
30
  import { ConversationListStatusIcon } from "./ConversationListStatusIcon";
25
31
 
@@ -104,15 +110,21 @@ export function ConversationSidebar(props: {
104
110
  </h2>
105
111
  <div className="flex items-center gap-0.5">
106
112
  {isLanding ? null : (
107
- <button
108
- aria-label="New conversation"
109
- 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"
110
- onClick={props.onNewConversation}
111
- title="New conversation"
112
- type="button"
113
- >
114
- <SquarePen aria-hidden="true" size={15} />
115
- </button>
113
+ <Tooltip>
114
+ <TooltipTrigger
115
+ render={
116
+ <Button
117
+ aria-label="New conversation"
118
+ className="size-7"
119
+ onClick={props.onNewConversation}
120
+ size="icon"
121
+ >
122
+ <SquarePen aria-hidden="true" size={15} />
123
+ </Button>
124
+ }
125
+ />
126
+ <TooltipContent size="compact">New conversation</TooltipContent>
127
+ </Tooltip>
116
128
  )}
117
129
  </div>
118
130
  </div>
@@ -321,25 +333,33 @@ const ConversationSidebarRow = memo(function ConversationSidebarRow(props: {
321
333
  ) : null}
322
334
  </div>
323
335
  </Link>
324
- <button
325
- aria-label={`${props.conversation.archivedAt ? "Restore" : "Archive"} ${title}`}
326
- 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"
327
- disabled={archive.isPending}
328
- onClick={() =>
329
- archive.mutate({
330
- archived: !props.conversation.archivedAt,
331
- lastSeenAt: props.conversation.lastSeenAt,
332
- })
333
- }
334
- title={`${props.conversation.archivedAt ? "Restore" : "Archive"} ${title}`}
335
- type="button"
336
- >
337
- {props.conversation.archivedAt ? (
338
- <ArchiveRestore aria-hidden="true" size={14} />
339
- ) : (
340
- <Archive aria-hidden="true" size={14} />
341
- )}
342
- </button>
336
+ <Tooltip>
337
+ <TooltipTrigger
338
+ render={
339
+ <button
340
+ aria-label={`${props.conversation.archivedAt ? "Restore" : "Archive"} ${title}`}
341
+ 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"
342
+ disabled={archive.isPending}
343
+ onClick={() =>
344
+ archive.mutate({
345
+ archived: !props.conversation.archivedAt,
346
+ lastSeenAt: props.conversation.lastSeenAt,
347
+ })
348
+ }
349
+ type="button"
350
+ >
351
+ {props.conversation.archivedAt ? (
352
+ <ArchiveRestore aria-hidden="true" size={14} />
353
+ ) : (
354
+ <Archive aria-hidden="true" size={14} />
355
+ )}
356
+ </button>
357
+ }
358
+ />
359
+ <TooltipContent size="compact">
360
+ {props.conversation.archivedAt ? "Restore" : "Archive"}
361
+ </TooltipContent>
362
+ </Tooltip>
343
363
  </div>
344
364
  );
345
365
  });