@sentry/junior-dashboard 0.265.0 → 0.267.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 (104) hide show
  1. package/dist/app.js +49 -12
  2. package/dist/client.js +94 -63
  3. package/dist/index.js +49 -12
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +11 -5
  6. package/src/app.ts +0 -2
  7. package/src/auth.ts +4 -7
  8. package/src/client/App.tsx +35 -53
  9. package/src/client/components/ActorAvatar.tsx +10 -22
  10. package/src/client/components/ConnectionBanner.tsx +4 -3
  11. package/src/client/components/CopyButton.tsx +3 -4
  12. package/src/client/components/DetailsPanel.tsx +12 -13
  13. package/src/client/components/Drawer.tsx +46 -23
  14. package/src/client/components/EmptyTelemetry.tsx +5 -3
  15. package/src/client/components/ImageAttachment.tsx +49 -63
  16. package/src/client/components/InlineError.tsx +8 -4
  17. package/src/client/components/Notice.tsx +19 -26
  18. package/src/client/components/Pagination.tsx +26 -26
  19. package/src/client/components/ParticipantAvatarStack.tsx +13 -10
  20. package/src/client/components/README.md +28 -3
  21. package/src/client/components/SearchInput.tsx +51 -41
  22. package/src/client/components/VersionDriftBanner.tsx +9 -7
  23. package/src/client/components/charts/CacheWrites.tsx +52 -43
  24. package/src/client/components/ui/alert.tsx +64 -0
  25. package/src/client/components/ui/avatar.tsx +53 -0
  26. package/src/client/components/ui/button-group.tsx +59 -0
  27. package/src/client/components/ui/button.tsx +2 -1
  28. package/src/client/components/ui/dialog.tsx +60 -0
  29. package/src/client/components/ui/drawer.tsx +140 -0
  30. package/src/client/components/ui/empty.tsx +61 -0
  31. package/src/client/components/ui/input-group.tsx +96 -0
  32. package/src/client/components/ui/pagination.tsx +60 -0
  33. package/src/client/components/ui/separator.tsx +22 -0
  34. package/src/client/components/ui/spinner.tsx +21 -0
  35. package/src/client/components/ui/table.tsx +22 -1
  36. package/src/client/components/ui/tooltip.tsx +1 -1
  37. package/src/client/components/ui/useSheet.ts +5 -1
  38. package/src/client/conversations/ComposerImages.tsx +66 -14
  39. package/src/client/conversations/ConversationChildAgents.tsx +5 -5
  40. package/src/client/conversations/ConversationComposer.tsx +84 -12
  41. package/src/client/conversations/ConversationDetails.tsx +3 -27
  42. package/src/client/conversations/ConversationEventDetails.tsx +26 -10
  43. package/src/client/conversations/ConversationHeader.tsx +5 -4
  44. package/src/client/conversations/ConversationHeaderActions.tsx +7 -11
  45. package/src/client/conversations/ConversationPage.tsx +156 -2
  46. package/src/client/conversations/ConversationSidebar.tsx +5 -4
  47. package/src/client/conversations/ConversationTranscript.tsx +48 -16
  48. package/src/client/conversations/ConversationUsageBreakdown.tsx +108 -65
  49. package/src/client/conversations/ConversationWatches.tsx +67 -59
  50. package/src/client/conversations/DetailsList.tsx +77 -29
  51. package/src/client/conversations/EventDetails.tsx +46 -19
  52. package/src/client/conversations/EventUsage.tsx +99 -74
  53. package/src/client/conversations/ForkConversationButton.tsx +79 -73
  54. package/src/client/conversations/PendingMailboxStack.tsx +312 -119
  55. package/src/client/conversations/README.md +30 -10
  56. package/src/client/conversations/ShareConversationButton.tsx +3 -4
  57. package/src/client/conversations/SubagentTranscriptDrawer.tsx +19 -22
  58. package/src/client/conversations/TranscriptActivityGroup.tsx +22 -19
  59. package/src/client/conversations/TranscriptAttachment.tsx +39 -4
  60. package/src/client/conversations/TranscriptAutomationTriggerView.tsx +14 -19
  61. package/src/client/conversations/TranscriptEventView.tsx +35 -16
  62. package/src/client/conversations/TranscriptFailureView.tsx +10 -13
  63. package/src/client/conversations/TranscriptMarkdown.tsx +232 -436
  64. package/src/client/conversations/TranscriptMessageContextView.tsx +31 -20
  65. package/src/client/conversations/TranscriptMessageView.tsx +7 -1
  66. package/src/client/conversations/TranscriptStructuredEventView.tsx +25 -15
  67. package/src/client/conversations/TranscriptSummary.tsx +13 -7
  68. package/src/client/conversations/TranscriptTurnContextView.tsx +78 -56
  69. package/src/client/conversations/TranscriptView.tsx +7 -3
  70. package/src/client/conversations/UserInputRequestCard.tsx +244 -0
  71. package/src/client/conversations/conversationOutbox.ts +46 -0
  72. package/src/client/conversations/eventLog.ts +1 -0
  73. package/src/client/conversations/eventTime.ts +13 -1
  74. package/src/client/conversations/eventTranscript.ts +12 -5
  75. package/src/client/conversations/queries.ts +127 -26
  76. package/src/client/conversations/transcriptMarkdownLinks.ts +5 -367
  77. package/src/client/conversations/transcriptMentions.tsx +18 -5
  78. package/src/client/conversations/userInputRequest.ts +56 -0
  79. package/src/client/format.ts +10 -3
  80. package/src/client/pages/automations/{AutomationLifecycle.tsx → AutomationActions.tsx} +60 -69
  81. package/src/client/pages/automations/AutomationDraftChanges.tsx +171 -0
  82. package/src/client/pages/automations/AutomationEditor.tsx +182 -156
  83. package/src/client/pages/automations/AutomationEventFields.tsx +119 -45
  84. package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +1 -1
  85. package/src/client/pages/automations/AutomationFormSection.tsx +20 -2
  86. package/src/client/pages/automations/AutomationOutcomeFields.tsx +1 -1
  87. package/src/client/pages/automations/AutomationOverview.tsx +238 -0
  88. package/src/client/pages/automations/AutomationPage.tsx +343 -0
  89. package/src/client/pages/automations/AutomationRecentRuns.tsx +3 -2
  90. package/src/client/pages/automations/AutomationRow.tsx +11 -13
  91. package/src/client/pages/automations/AutomationRunDetails.tsx +4 -2
  92. package/src/client/pages/automations/{AutomationExecutionsPage.tsx → AutomationRunsTab.tsx} +94 -103
  93. package/src/client/pages/automations/AutomationStatus.tsx +164 -108
  94. package/src/client/pages/automations/AutomationVersionsTab.tsx +518 -0
  95. package/src/client/pages/automations/AutomationsPage.tsx +10 -98
  96. package/src/client/pages/automations/AutomationsPageLayout.tsx +5 -23
  97. package/src/client/pages/automations/README.md +56 -46
  98. package/src/client/types.ts +3 -0
  99. package/src/mock-reporting/fixtures.ts +44 -2
  100. package/src/mock-reporting/watches.ts +1 -1
  101. package/src/client/conversations/transcriptStyles.ts +0 -10
  102. package/src/client/pages/automations/AutomationDetails.tsx +0 -193
  103. package/src/client/pages/automations/AutomationEditPage.tsx +0 -176
  104. package/src/client/pages/automations/AutomationVersionsPage.tsx +0 -390
@@ -1,16 +1,19 @@
1
1
  import { ChevronRight } from "lucide-react";
2
+ import { useState } from "react";
2
3
 
4
+ import { Collapsible, CollapsibleContent } from "../components/ui/collapsible";
5
+ import { dashboardDisclosureChevronClass } from "../styles";
3
6
  import { formatMessageTimestamp, transcriptMessageActorLabel } from "../format";
4
7
  import type { ConversationTranscript, TranscriptViewMessage } from "../types";
5
8
  import {
6
9
  TranscriptHeadingMeta,
7
10
  TranscriptHeadingRow,
8
11
  } from "./TranscriptHeadingRow";
9
- import { HighlightText } from "./transcriptSearch";
12
+ import { HighlightText, useTranscriptSearch } from "./transcriptSearch";
10
13
  import { messageRawText } from "./transcriptRenderModel";
11
14
  import { RedactedMarker } from "./TranscriptRedacted";
12
15
  import { TranscriptTurnContextView } from "./TranscriptTurnContextView";
13
- import { TranscriptSummary } from "./TranscriptSummary";
16
+ import { TranscriptSummaryTrigger } from "./TranscriptSummary";
14
17
 
15
18
  /** Render ambient conversation context that is not a primary chat bubble. */
16
19
  export function TranscriptMessageContextView(props: {
@@ -18,6 +21,8 @@ export function TranscriptMessageContextView(props: {
18
21
  message: TranscriptViewMessage;
19
22
  redacted?: boolean;
20
23
  }) {
24
+ const { active: searchActive } = useTranscriptSearch();
25
+ const [open, setOpen] = useState(false);
21
26
  const actor = transcriptMessageActorLabel(props.conversation, props.message);
22
27
  const timestamp = formatMessageTimestamp(props.message.timestamp);
23
28
  const text = messageRawText(props.message);
@@ -30,33 +35,39 @@ export function TranscriptMessageContextView(props: {
30
35
 
31
36
  return (
32
37
  <>
33
- <details
34
- className="group/message-context min-w-0 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5 md:hidden"
38
+ <Collapsible
39
+ className="min-w-0 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5 md:hidden"
35
40
  data-transcript-message-context
41
+ // Search holds the text open, so the heading button is off until
42
+ // search ends.
43
+ disabled={searchActive}
44
+ onOpenChange={setOpen}
45
+ open={searchActive || open}
36
46
  >
37
- <TranscriptSummary
47
+ <TranscriptSummaryTrigger
38
48
  flush
39
- className="flex items-center justify-between gap-2 font-sans text-xs font-medium text-dashboard-text-muted"
49
+ className="group flex items-center justify-between gap-2 font-sans text-xs font-medium text-dashboard-text-muted"
40
50
  >
41
51
  <span className="min-w-0 truncate">Context from {actor}</span>
42
52
  <ChevronRight
43
53
  aria-hidden="true"
44
- className="shrink-0 transition-transform group-open/message-context:rotate-90"
45
- size={14}
54
+ className={dashboardDisclosureChevronClass}
46
55
  />
47
- </TranscriptSummary>
48
- <div className="mt-2 whitespace-pre-wrap pt-2 text-sm leading-relaxed text-dashboard-text-subtle">
49
- {content}
50
- </div>
51
- {!props.redacted && props.message.contexts?.length ? (
52
- <div className="mt-2">
53
- <TranscriptTurnContextView
54
- contexts={props.message.contexts}
55
- messageId={props.message.messageId}
56
- />
56
+ </TranscriptSummaryTrigger>
57
+ <CollapsibleContent hiddenUntilFound>
58
+ <div className="mt-2 whitespace-pre-wrap pt-2 text-sm leading-relaxed text-dashboard-text-subtle">
59
+ {content}
57
60
  </div>
58
- ) : null}
59
- </details>
61
+ {!props.redacted && props.message.contexts?.length ? (
62
+ <div className="mt-2">
63
+ <TranscriptTurnContextView
64
+ contexts={props.message.contexts}
65
+ messageId={props.message.messageId}
66
+ />
67
+ </div>
68
+ ) : null}
69
+ </CollapsibleContent>
70
+ </Collapsible>
60
71
  <article
61
72
  className="hidden min-w-0 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5 md:block"
62
73
  data-transcript-message-context
@@ -1,5 +1,8 @@
1
1
  import { ForkConversationButton } from "./ForkConversationButton";
2
- import { TranscriptAttachments } from "./TranscriptAttachment";
2
+ import {
3
+ TranscriptAttachments,
4
+ TranscriptLocalImages,
5
+ } from "./TranscriptAttachment";
3
6
  import { ObjectCard } from "./ObjectCard";
4
7
  import { AutomationCard } from "../components/AutomationCard";
5
8
  import { memo, type ReactNode } from "react";
@@ -69,6 +72,9 @@ export const TranscriptMessageView = memo(
69
72
  conversationId={props.conversation.conversationId}
70
73
  />
71
74
  ) : null}
75
+ {props.message.images?.length ? (
76
+ <TranscriptLocalImages images={props.message.images} />
77
+ ) : null}
72
78
  {props.message.cards?.map((card) => {
73
79
  switch (card.kind) {
74
80
  case "object":
@@ -1,8 +1,9 @@
1
+ import { Collapsible, CollapsibleContent } from "../components/ui/collapsible";
1
2
  import { formatMessageTimestamp } from "../format";
2
3
  import type { TranscriptViewStructuredEventPart } from "../types";
3
4
  import { MentionText } from "./transcriptMentions";
4
5
  import { HighlightText, useTranscriptSearch } from "./transcriptSearch";
5
- import { TranscriptSummary } from "./TranscriptSummary";
6
+ import { TranscriptSummaryTrigger } from "./TranscriptSummary";
6
7
 
7
8
  type TranscriptPresentationEventPart = TranscriptViewStructuredEventPart;
8
9
 
@@ -58,27 +59,34 @@ export function TranscriptStructuredEventView(props: {
58
59
  ))}
59
60
  </div>
60
61
  ) : null;
62
+ const collapsible = details.length > 0 && !search.active;
63
+ // The header can sit inside the row button, so it uses only inline elements
64
+ // and no links.
61
65
  const header = (
62
- <div className="grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-2 max-md:grid-cols-[minmax(0,1fr)]">
63
- <div className="min-w-0">
64
- <div className="text-sm font-medium leading-6 text-dashboard-text">
66
+ <span className="grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-2 max-md:grid-cols-[minmax(0,1fr)]">
67
+ <span className="min-w-0">
68
+ <span className="block text-sm font-medium leading-6 text-dashboard-text">
65
69
  <HighlightText text={presentation.title} />
66
- </div>
70
+ </span>
67
71
  {presentation.preview ? (
68
- <div className="mt-0.5 truncate text-xs text-dashboard-text-muted">
69
- <MentionText mentions={mentions} text={presentation.preview} />
70
- </div>
72
+ <span className="mt-0.5 block truncate text-xs text-dashboard-text-muted">
73
+ <MentionText
74
+ links={!collapsible}
75
+ mentions={mentions}
76
+ text={presentation.preview}
77
+ />
78
+ </span>
71
79
  ) : null}
72
- </div>
80
+ </span>
73
81
  {timestamp ? (
74
82
  <span className="text-xs leading-6 tabular-nums text-dashboard-text-muted max-md:hidden">
75
83
  {timestamp}
76
84
  </span>
77
85
  ) : null}
78
- </div>
86
+ </span>
79
87
  );
80
88
 
81
- if (details.length === 0 || search.active) {
89
+ if (!collapsible) {
82
90
  return (
83
91
  <div className={surfaceClass}>
84
92
  {header}
@@ -87,9 +95,11 @@ export function TranscriptStructuredEventView(props: {
87
95
  );
88
96
  }
89
97
  return (
90
- <details className={`group/plugin-event ${surfaceClass}`}>
91
- <TranscriptSummary flush>{header}</TranscriptSummary>
92
- {body}
93
- </details>
98
+ <Collapsible className={surfaceClass}>
99
+ <TranscriptSummaryTrigger flush className="block">
100
+ {header}
101
+ </TranscriptSummaryTrigger>
102
+ <CollapsibleContent hiddenUntilFound>{body}</CollapsibleContent>
103
+ </Collapsible>
94
104
  );
95
105
  }
@@ -1,22 +1,28 @@
1
- import type { ComponentPropsWithoutRef } from "react";
1
+ import type { ComponentProps } from "react";
2
2
 
3
+ import { CollapsibleTrigger } from "../components/ui/collapsible";
3
4
  import { cn } from "../styles";
4
5
 
5
- /** Keep hover and keyboard focus around a padded transcript cell. */
6
- export function TranscriptSummary({
6
+ /**
7
+ * Give a `Collapsible` trigger the padding, hover, and keyboard focus of a
8
+ * transcript disclosure. The caller sets the display. Put only inline
9
+ * elements, such as `span`, inside the trigger.
10
+ */
11
+ export function TranscriptSummaryTrigger({
7
12
  className,
8
13
  flush = false,
9
14
  ...props
10
- }: ComponentPropsWithoutRef<"summary"> & {
15
+ }: Omit<ComponentProps<typeof CollapsibleTrigger>, "className"> & {
16
+ className?: string;
11
17
  /** Extend padding around text that already sits on the transcript edge. */
12
18
  flush?: boolean;
13
19
  }) {
14
20
  return (
15
- <summary
21
+ <CollapsibleTrigger
16
22
  {...props}
17
23
  className={cn(
18
- "cursor-pointer list-none rounded-md px-2 py-1.5 transition-colors hover:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus [&::-webkit-details-marker]:hidden",
19
- flush && "-mx-2 -my-1.5",
24
+ "cursor-pointer rounded-md px-2 py-1.5 text-left transition-colors hover:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus disabled:cursor-default disabled:hover:bg-transparent",
25
+ flush ? "-mx-2 -my-1.5 w-[calc(100%+1rem)]" : "w-full",
20
26
  className,
21
27
  )}
22
28
  />
@@ -9,13 +9,20 @@ import {
9
9
  import { createPortal } from "react-dom";
10
10
 
11
11
  import { DetailsPanel } from "../components/DetailsPanel";
12
+ import { Button } from "../components/ui/button";
13
+ import { Collapsible, CollapsibleContent } from "../components/ui/collapsible";
12
14
  import { Drawer } from "../components/Drawer";
15
+ import {
16
+ Tooltip,
17
+ TooltipContent,
18
+ TooltipTrigger,
19
+ } from "../components/ui/tooltip";
13
20
  import type { TranscriptViewTurnContext } from "../types";
14
21
  import { formatMessageTimestamp, formatTime } from "../format";
15
22
  import { memoryRecallContent, type MemoryRecallContent } from "./turnContext";
16
- import { cn } from "../styles";
23
+ import { cn, dashboardDisclosureChevronClass } from "../styles";
17
24
  import { HighlightText } from "./transcriptSearch";
18
- import { TranscriptSummary } from "./TranscriptSummary";
25
+ import { TranscriptSummaryTrigger } from "./TranscriptSummary";
19
26
 
20
27
  /** The turn context that a page panel shows, keyed by its message. */
21
28
  export type TurnContextSelection = {
@@ -66,24 +73,33 @@ export function TranscriptTurnContextView(props: {
66
73
 
67
74
  return (
68
75
  <>
69
- <button
70
- aria-haspopup={panel ? undefined : "dialog"}
71
- aria-expanded={expanded}
72
- aria-label="View turn context"
73
- className={cn(
74
- "inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:bg-dashboard-fill-hover focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus",
75
- expanded && "bg-dashboard-fill-hover text-dashboard-text",
76
- )}
77
- data-turn-context-id={id}
78
- onClick={() => {
79
- if (!panel) return setOpen(true);
80
- panel.onToggle({ contexts: props.contexts, id });
81
- }}
82
- title="View context"
83
- type="button"
84
- >
85
- <Layers aria-hidden="true" size={14} strokeWidth={1.8} />
86
- </button>
76
+ <Tooltip>
77
+ <TooltipTrigger
78
+ // The open panel keeps focus here. Close the tooltip on click so
79
+ // Escape reaches the panel.
80
+ closeOnClick
81
+ render={
82
+ <Button
83
+ aria-haspopup={panel ? undefined : "dialog"}
84
+ aria-expanded={expanded}
85
+ aria-label="View turn context"
86
+ className={cn(
87
+ "size-6 shrink-0",
88
+ expanded && "bg-dashboard-fill-strong text-dashboard-text",
89
+ )}
90
+ data-turn-context-id={id}
91
+ onClick={() => {
92
+ if (!panel) return setOpen(true);
93
+ panel.onToggle({ contexts: props.contexts, id });
94
+ }}
95
+ size="icon"
96
+ >
97
+ <Layers aria-hidden="true" size={14} strokeWidth={1.8} />
98
+ </Button>
99
+ }
100
+ />
101
+ <TooltipContent size="compact">View turn context</TooltipContent>
102
+ </Tooltip>
87
103
 
88
104
  {!panel && open
89
105
  ? createPortal(
@@ -189,15 +205,17 @@ function MemoryRecall(props: {
189
205
  return (
190
206
  <div className="overflow-hidden rounded-lg border border-dashboard-border-strong">
191
207
  {props.memories.map((memory, index) => (
192
- <details
193
- className="group/memory border-t border-dashboard-border-strong first:border-t-0"
208
+ <Collapsible
209
+ className="border-t border-dashboard-border-strong first:border-t-0"
194
210
  key={memory.id}
195
211
  >
196
- <TranscriptSummary className="flex items-start gap-2.5">
212
+ <TranscriptSummaryTrigger className="group flex items-start gap-2.5">
197
213
  <ChevronRight
198
214
  aria-hidden="true"
199
- className="mt-0.5 shrink-0 text-dashboard-text-muted transition-transform group-open/memory:rotate-90"
200
- size={15}
215
+ className={cn(
216
+ dashboardDisclosureChevronClass,
217
+ "mt-0.5 text-dashboard-text-muted",
218
+ )}
201
219
  />
202
220
  <span className="min-w-0 flex-1">
203
221
  <span className="flex items-center justify-between gap-3">
@@ -212,39 +230,43 @@ function MemoryRecall(props: {
212
230
  <HighlightText text={memory.content} />
213
231
  </span>
214
232
  </span>
215
- </TranscriptSummary>
216
-
217
- <div className="border-t border-dashboard-border bg-dashboard-fill-faint px-4 py-4">
218
- <div className="whitespace-pre-wrap text-sm leading-6 text-dashboard-text">
219
- <HighlightText text={memory.content} />
220
- </div>
233
+ </TranscriptSummaryTrigger>
221
234
 
222
- <dl className="mt-4 grid gap-2 text-xs">
223
- <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
224
- <dt className="text-dashboard-text-muted">Memory ID</dt>
225
- <dd className="m-0 break-all font-mono text-dashboard-text-muted">
226
- <HighlightText text={memory.id} />
227
- </dd>
228
- </div>
229
- <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
230
- <dt className="text-dashboard-text-muted">Observed</dt>
231
- <dd className="m-0 text-dashboard-text-muted">
232
- {formatTime(new Date(memory.observedAtMs).toISOString())}
233
- </dd>
234
- </div>
235
- <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
236
- <dt className="text-dashboard-text-muted">Scope</dt>
237
- <dd className="m-0 text-dashboard-text-muted">
238
- {memory.scope}
239
- </dd>
240
- </div>
241
- <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
242
- <dt className="text-dashboard-text-muted">Kind</dt>
243
- <dd className="m-0 text-dashboard-text-muted">{memory.kind}</dd>
235
+ <CollapsibleContent hiddenUntilFound>
236
+ <div className="border-t border-dashboard-border bg-dashboard-fill-faint px-4 py-4">
237
+ <div className="whitespace-pre-wrap text-sm leading-6 text-dashboard-text">
238
+ <HighlightText text={memory.content} />
244
239
  </div>
245
- </dl>
246
- </div>
247
- </details>
240
+
241
+ <dl className="mt-4 grid gap-2 text-xs">
242
+ <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
243
+ <dt className="text-dashboard-text-muted">Memory ID</dt>
244
+ <dd className="m-0 break-all font-mono text-dashboard-text-muted">
245
+ <HighlightText text={memory.id} />
246
+ </dd>
247
+ </div>
248
+ <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
249
+ <dt className="text-dashboard-text-muted">Observed</dt>
250
+ <dd className="m-0 text-dashboard-text-muted">
251
+ {formatTime(new Date(memory.observedAtMs).toISOString())}
252
+ </dd>
253
+ </div>
254
+ <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
255
+ <dt className="text-dashboard-text-muted">Scope</dt>
256
+ <dd className="m-0 text-dashboard-text-muted">
257
+ {memory.scope}
258
+ </dd>
259
+ </div>
260
+ <div className="grid grid-cols-[5rem_minmax(0,1fr)] items-start gap-3">
261
+ <dt className="text-dashboard-text-muted">Kind</dt>
262
+ <dd className="m-0 text-dashboard-text-muted">
263
+ {memory.kind}
264
+ </dd>
265
+ </div>
266
+ </dl>
267
+ </div>
268
+ </CollapsibleContent>
269
+ </Collapsible>
248
270
  ))}
249
271
  <p className="m-0 border-t border-dashboard-border-strong px-3 py-2 text-xs text-dashboard-text-muted">
250
272
  {props.memories.length}{" "}
@@ -1,10 +1,12 @@
1
1
  import { useState } from "react";
2
+ import type { ConversationMailboxMessage } from "./conversationOutbox";
2
3
  import { ArrowDownToLine } from "lucide-react";
3
4
 
4
5
  import type {
5
6
  ConversationTranscript,
6
7
  TranscriptViewSubagentPart,
7
8
  } from "../types";
9
+ import { EmptyTelemetry } from "../components/EmptyTelemetry";
8
10
  import { Button } from "../components/ui/button";
9
11
  import {
10
12
  Tooltip,
@@ -20,7 +22,6 @@ import {
20
22
  usePinnedTranscriptBottom,
21
23
  } from "./transcriptBottomPinning";
22
24
  import type { TranscriptViewMode } from "./transcriptRenderModel";
23
- import { transcriptEmptyClass } from "./transcriptStyles";
24
25
  import { TranscriptSearchProvider } from "./transcriptSearch";
25
26
  import {
26
27
  TurnContextPanelProvider,
@@ -37,6 +38,8 @@ export function Transcript(props: {
37
38
  pinRequestVersion?: number;
38
39
  onLoadPreviousPage?: () => void;
39
40
  onSelectedEventSeqChange?(seq: number | undefined): void;
41
+ /** The reader's own sends to show at the end until history has them. */
42
+ pendingMessages?: readonly ConversationMailboxMessage[];
40
43
  responding?: boolean;
41
44
  search?: string;
42
45
  onSearchChange?(value: string): void;
@@ -86,9 +89,9 @@ export function Transcript(props: {
86
89
 
87
90
  if (!props.transcript) {
88
91
  return (
89
- <div className={transcriptEmptyClass()}>
92
+ <EmptyTelemetry>
90
93
  No transcript is available for this conversation.
91
- </div>
94
+ </EmptyTelemetry>
92
95
  );
93
96
  }
94
97
 
@@ -139,6 +142,7 @@ export function Transcript(props: {
139
142
  <ConversationTranscriptView
140
143
  onOpenSubagentTranscript={props.onOpenSubagentTranscript}
141
144
  conversation={props.transcript}
145
+ pendingMessages={props.pendingMessages}
142
146
  responding={props.responding ?? props.live ?? false}
143
147
  />
144
148
  </TurnContextPanelProvider>