@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
|
@@ -50,7 +50,10 @@ export function TranscriptMessageContextView(props: {
|
|
|
50
50
|
</div>
|
|
51
51
|
{!props.redacted && props.message.contexts?.length ? (
|
|
52
52
|
<div className="mt-2">
|
|
53
|
-
<TranscriptTurnContextView
|
|
53
|
+
<TranscriptTurnContextView
|
|
54
|
+
contexts={props.message.contexts}
|
|
55
|
+
messageId={props.message.messageId}
|
|
56
|
+
/>
|
|
54
57
|
</div>
|
|
55
58
|
) : null}
|
|
56
59
|
</details>
|
|
@@ -78,7 +81,10 @@ export function TranscriptMessageContextView(props: {
|
|
|
78
81
|
</div>
|
|
79
82
|
{!props.redacted && props.message.contexts?.length ? (
|
|
80
83
|
<div className="mt-2">
|
|
81
|
-
<TranscriptTurnContextView
|
|
84
|
+
<TranscriptTurnContextView
|
|
85
|
+
contexts={props.message.contexts}
|
|
86
|
+
messageId={props.message.messageId}
|
|
87
|
+
/>
|
|
82
88
|
</div>
|
|
83
89
|
) : null}
|
|
84
90
|
</article>
|
|
@@ -122,7 +122,12 @@ function messageAction(
|
|
|
122
122
|
conversation: ConversationTranscript,
|
|
123
123
|
): ReactNode {
|
|
124
124
|
if (message.role === "user" && message.contexts?.length) {
|
|
125
|
-
return
|
|
125
|
+
return (
|
|
126
|
+
<TranscriptTurnContextView
|
|
127
|
+
contexts={message.contexts}
|
|
128
|
+
messageId={message.messageId}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
126
131
|
}
|
|
127
132
|
if (
|
|
128
133
|
conversation.canFork &&
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
1
2
|
import { Lightbulb } from "lucide-react";
|
|
2
3
|
|
|
4
|
+
import {
|
|
5
|
+
TraceCollapsible,
|
|
6
|
+
TraceCollapsibleContent,
|
|
7
|
+
TraceCollapsibleTrigger,
|
|
8
|
+
} from "../components/ui/trace";
|
|
3
9
|
import { formatMessageTimestamp } from "../format";
|
|
4
10
|
import type { TranscriptViewReasoningPart } from "../types";
|
|
5
|
-
import { cn } from "../styles";
|
|
6
11
|
import {
|
|
7
12
|
TranscriptHeadingMeta,
|
|
8
13
|
TranscriptHeadingRow,
|
|
9
14
|
} from "./TranscriptHeadingRow";
|
|
10
15
|
import { HighlightText, useTranscriptSearch } from "./transcriptSearch";
|
|
11
|
-
import { TranscriptSummary } from "./TranscriptSummary";
|
|
12
|
-
import { transcriptActivityBodyClass } from "./ToolFrame";
|
|
13
16
|
|
|
14
17
|
/** Render reasoning collapsed with a short preview until expanded or searched. */
|
|
15
18
|
export function TranscriptReasoningView(props: {
|
|
@@ -17,74 +20,57 @@ export function TranscriptReasoningView(props: {
|
|
|
17
20
|
timestamp?: number;
|
|
18
21
|
}) {
|
|
19
22
|
const { active: searchActive } = useTranscriptSearch();
|
|
23
|
+
const [open, setOpen] = useState(false);
|
|
24
|
+
const expanded = searchActive || open;
|
|
20
25
|
const rendered = props.part.redacted ? "Reasoning redacted" : props.part.text;
|
|
21
26
|
const timestamp = formatMessageTimestamp(props.timestamp);
|
|
22
|
-
const summary = (
|
|
23
|
-
<>
|
|
24
|
-
<span
|
|
25
|
-
aria-label="Reasoning"
|
|
26
|
-
className="inline-flex size-4 shrink-0 items-center justify-center text-violet-200/80"
|
|
27
|
-
title="Reasoning"
|
|
28
|
-
>
|
|
29
|
-
<Lightbulb aria-hidden="true" size={14} strokeWidth={2} />
|
|
30
|
-
</span>
|
|
31
|
-
<TranscriptHeadingRow
|
|
32
|
-
left={
|
|
33
|
-
searchActive ? (
|
|
34
|
-
<span className="block min-w-0 text-dashboard-text-muted">
|
|
35
|
-
Reasoning
|
|
36
|
-
</span>
|
|
37
|
-
) : (
|
|
38
|
-
<>
|
|
39
|
-
<span className="block min-w-0 truncate italic text-dashboard-text-muted group-open/reasoning:hidden">
|
|
40
|
-
{rendered}
|
|
41
|
-
</span>
|
|
42
|
-
<span className="hidden min-w-0 text-dashboard-text-muted group-open/reasoning:inline">
|
|
43
|
-
Reasoning
|
|
44
|
-
</span>
|
|
45
|
-
</>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
right={
|
|
49
|
-
timestamp ? (
|
|
50
|
-
<TranscriptHeadingMeta className="min-w-0 break-words text-xs tabular-nums text-dashboard-text-muted max-md:hidden">
|
|
51
|
-
{timestamp}
|
|
52
|
-
</TranscriptHeadingMeta>
|
|
53
|
-
) : undefined
|
|
54
|
-
}
|
|
55
|
-
rightClassName="min-w-0 max-md:hidden"
|
|
56
|
-
/>
|
|
57
|
-
</>
|
|
58
|
-
);
|
|
59
|
-
const content = (
|
|
60
|
-
<div className={cn(transcriptActivityBodyClass, "px-3 py-2.5")}>
|
|
61
|
-
<div className="min-w-0 whitespace-pre-wrap break-words text-sm leading-relaxed text-dashboard-text-subtle">
|
|
62
|
-
<HighlightText text={rendered} />
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if (searchActive) {
|
|
68
|
-
return (
|
|
69
|
-
<div className={reasoningFrameClass()}>
|
|
70
|
-
<div className="grid list-none grid-cols-[1rem_minmax(0,1fr)] items-center gap-2 py-1.5">
|
|
71
|
-
{summary}
|
|
72
|
-
</div>
|
|
73
|
-
{content}
|
|
74
|
-
</div>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
27
|
|
|
78
28
|
return (
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
|
|
29
|
+
<TraceCollapsible
|
|
30
|
+
className="text-xs leading-tight"
|
|
31
|
+
// Search holds rows open, so the row button is off until search ends.
|
|
32
|
+
disabled={searchActive}
|
|
33
|
+
open={expanded}
|
|
34
|
+
onOpenChange={setOpen}
|
|
35
|
+
>
|
|
36
|
+
<TraceCollapsibleTrigger className="-mx-2 w-auto">
|
|
37
|
+
<span className="grid min-w-0 flex-1 grid-cols-[1rem_minmax(0,1fr)] items-center gap-2">
|
|
38
|
+
<span
|
|
39
|
+
aria-label="Reasoning"
|
|
40
|
+
className="inline-flex size-4 shrink-0 items-center justify-center text-violet-200/80"
|
|
41
|
+
title="Reasoning"
|
|
42
|
+
>
|
|
43
|
+
<Lightbulb aria-hidden="true" size={14} strokeWidth={2} />
|
|
44
|
+
</span>
|
|
45
|
+
<TranscriptHeadingRow
|
|
46
|
+
left={
|
|
47
|
+
expanded ? (
|
|
48
|
+
<span className="block min-w-0 text-dashboard-text-muted">
|
|
49
|
+
Reasoning
|
|
50
|
+
</span>
|
|
51
|
+
) : (
|
|
52
|
+
<span className="block min-w-0 truncate italic text-dashboard-text-muted">
|
|
53
|
+
{rendered}
|
|
54
|
+
</span>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
right={
|
|
58
|
+
timestamp ? (
|
|
59
|
+
<TranscriptHeadingMeta className="min-w-0 break-words text-xs tabular-nums text-dashboard-text-muted max-md:hidden">
|
|
60
|
+
{timestamp}
|
|
61
|
+
</TranscriptHeadingMeta>
|
|
62
|
+
) : undefined
|
|
63
|
+
}
|
|
64
|
+
rightClassName="min-w-0 max-md:hidden"
|
|
65
|
+
/>
|
|
66
|
+
</span>
|
|
67
|
+
</TraceCollapsibleTrigger>
|
|
68
|
+
{/* Keep closed reasoning in the page, so browser find can reach it. */}
|
|
69
|
+
<TraceCollapsibleContent className="px-3 py-2.5" hiddenUntilFound>
|
|
70
|
+
<div className="min-w-0 whitespace-pre-wrap break-words text-sm leading-relaxed text-dashboard-text-subtle">
|
|
71
|
+
<HighlightText text={rendered} />
|
|
72
|
+
</div>
|
|
73
|
+
</TraceCollapsibleContent>
|
|
74
|
+
</TraceCollapsible>
|
|
85
75
|
);
|
|
86
76
|
}
|
|
87
|
-
|
|
88
|
-
function reasoningFrameClass(): string {
|
|
89
|
-
return "min-w-0 text-xs leading-tight";
|
|
90
|
-
}
|
|
@@ -8,9 +8,9 @@ import { subagentStatusTone } from "./ConversationChildAgents";
|
|
|
8
8
|
import { HighlightText } from "./transcriptSearch";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Render a child agent as a compact card in the transcript stream
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Render a child agent as a compact card in the transcript stream: name,
|
|
12
|
+
* status, and run time first, then the task title. It opens the child
|
|
13
|
+
* transcript.
|
|
14
14
|
*/
|
|
15
15
|
export function TranscriptSubagentView(props: {
|
|
16
16
|
onOpenTranscript?: (part: TranscriptViewSubagentPart) => void;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createContext, useContext, useState, type ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Tooltip,
|
|
5
|
+
TooltipContent,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
} from "../components/ui/tooltip";
|
|
4
8
|
import {
|
|
5
9
|
formatMessageTimestamp,
|
|
6
10
|
formatRelativeMessageTimestamp,
|
|
@@ -51,18 +55,16 @@ export function TranscriptTimestamp(props: { value: number | undefined }) {
|
|
|
51
55
|
|
|
52
56
|
const details = formatTranscriptTimestampDetails(props.value!);
|
|
53
57
|
return (
|
|
54
|
-
<Tooltip
|
|
55
|
-
|
|
58
|
+
<Tooltip>
|
|
59
|
+
<TooltipTrigger render={button} />
|
|
60
|
+
<TooltipContent>
|
|
56
61
|
<span className="grid grid-cols-[auto_auto] gap-x-3">
|
|
57
62
|
<span>Local</span>
|
|
58
63
|
<span>{details.local}</span>
|
|
59
64
|
<span>UTC</span>
|
|
60
65
|
<span>{details.utc}</span>
|
|
61
66
|
</span>
|
|
62
|
-
|
|
63
|
-
placement="above"
|
|
64
|
-
>
|
|
65
|
-
{button}
|
|
67
|
+
</TooltipContent>
|
|
66
68
|
</Tooltip>
|
|
67
69
|
);
|
|
68
70
|
}
|
|
@@ -96,7 +96,7 @@ function ToolSignature(props: {
|
|
|
96
96
|
<HighlightText text={props.name} />
|
|
97
97
|
</ShimmerText>
|
|
98
98
|
{props.preview && !searchActive ? (
|
|
99
|
-
<code className="min-w-0 truncate font-[inherit] text-dashboard-text-muted group-
|
|
99
|
+
<code className="min-w-0 truncate font-[inherit] text-dashboard-text-muted group-aria-expanded/trace-collapsible-trigger:hidden">
|
|
100
100
|
(<HighlightText text={props.preview} />)
|
|
101
101
|
</code>
|
|
102
102
|
) : null}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { Brain, Braces, ChevronRight, Layers } from "lucide-react";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
useContext,
|
|
5
|
+
useId,
|
|
6
|
+
useState,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from "react";
|
|
3
9
|
import { createPortal } from "react-dom";
|
|
4
10
|
|
|
11
|
+
import { DetailsPanel } from "../components/DetailsPanel";
|
|
5
12
|
import { Drawer } from "../components/Drawer";
|
|
6
13
|
import type { TranscriptViewTurnContext } from "../types";
|
|
7
14
|
import { formatMessageTimestamp, formatTime } from "../format";
|
|
@@ -10,57 +17,91 @@ import { cn } from "../styles";
|
|
|
10
17
|
import { HighlightText } from "./transcriptSearch";
|
|
11
18
|
import { TranscriptSummary } from "./TranscriptSummary";
|
|
12
19
|
|
|
13
|
-
/**
|
|
20
|
+
/** The turn context that a page panel shows, keyed by its message. */
|
|
21
|
+
export type TurnContextSelection = {
|
|
22
|
+
contexts: TranscriptViewTurnContext[];
|
|
23
|
+
id: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Let the page that owns a panel show turn context there. */
|
|
27
|
+
export type TurnContextPanel = {
|
|
28
|
+
openId?: string;
|
|
29
|
+
onToggle(selection: TurnContextSelection): void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const TurnContextPanelContext = createContext<TurnContextPanel | undefined>(
|
|
33
|
+
undefined,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Route turn context in one transcript to its owner's panel. Without a
|
|
38
|
+
* `panel`, turn context opens in a modal drawer. Each transcript sets this, so
|
|
39
|
+
* a nested transcript does not use the panel of the page behind it.
|
|
40
|
+
*/
|
|
41
|
+
export function TurnContextPanelProvider(props: {
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
panel?: TurnContextPanel;
|
|
44
|
+
}) {
|
|
45
|
+
return (
|
|
46
|
+
<TurnContextPanelContext value={props.panel}>
|
|
47
|
+
{props.children}
|
|
48
|
+
</TurnContextPanelContext>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Show structured context attached to one transcript user message. The panel
|
|
54
|
+
* keys context by `messageId`, so every control for one message, including a
|
|
55
|
+
* remounted one, shows the same open state.
|
|
56
|
+
*/
|
|
14
57
|
export function TranscriptTurnContextView(props: {
|
|
15
58
|
contexts: TranscriptViewTurnContext[];
|
|
59
|
+
messageId?: string;
|
|
16
60
|
}) {
|
|
61
|
+
const panel = useContext(TurnContextPanelContext);
|
|
17
62
|
const [open, setOpen] = useState(false);
|
|
18
|
-
const
|
|
63
|
+
const instanceId = useId();
|
|
64
|
+
const id = props.messageId ?? instanceId;
|
|
65
|
+
const expanded = panel ? panel.openId === id : open;
|
|
19
66
|
|
|
20
67
|
return (
|
|
21
68
|
<>
|
|
22
69
|
<button
|
|
23
|
-
aria-haspopup="dialog"
|
|
24
|
-
aria-expanded={
|
|
70
|
+
aria-haspopup={panel ? undefined : "dialog"}
|
|
71
|
+
aria-expanded={expanded}
|
|
25
72
|
aria-label="View turn context"
|
|
26
73
|
className={cn(
|
|
27
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",
|
|
28
|
-
|
|
75
|
+
expanded && "bg-dashboard-fill-hover text-dashboard-text",
|
|
29
76
|
)}
|
|
30
|
-
|
|
77
|
+
data-turn-context-id={id}
|
|
78
|
+
onClick={() => {
|
|
79
|
+
if (!panel) return setOpen(true);
|
|
80
|
+
panel.onToggle({ contexts: props.contexts, id });
|
|
81
|
+
}}
|
|
31
82
|
title="View context"
|
|
32
83
|
type="button"
|
|
33
84
|
>
|
|
34
85
|
<Layers aria-hidden="true" size={14} strokeWidth={1.8} />
|
|
35
86
|
</button>
|
|
36
87
|
|
|
37
|
-
{open
|
|
88
|
+
{!panel && open
|
|
38
89
|
? createPortal(
|
|
39
90
|
<Drawer
|
|
40
91
|
closeLabel="Close turn context"
|
|
41
92
|
header={
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</h2>
|
|
49
|
-
<p className="m-0 mt-1 text-xs text-dashboard-text-muted">
|
|
50
|
-
Context supplied with this message
|
|
51
|
-
</p>
|
|
52
|
-
</>
|
|
93
|
+
<h2
|
|
94
|
+
className="m-0 text-sm font-semibold text-dashboard-text"
|
|
95
|
+
id={instanceId}
|
|
96
|
+
>
|
|
97
|
+
Turn context
|
|
98
|
+
</h2>
|
|
53
99
|
}
|
|
54
100
|
onClose={() => setOpen(false)}
|
|
55
|
-
openKey={
|
|
56
|
-
titleId={
|
|
101
|
+
openKey={instanceId}
|
|
102
|
+
titleId={instanceId}
|
|
57
103
|
>
|
|
58
|
-
{props.contexts
|
|
59
|
-
<TurnContext
|
|
60
|
-
context={context}
|
|
61
|
-
key={`${context.pluginName}:${context.kind}:${context.version}:${index}`}
|
|
62
|
-
/>
|
|
63
|
-
))}
|
|
104
|
+
<TurnContextList contexts={props.contexts} />
|
|
64
105
|
</Drawer>,
|
|
65
106
|
document.body,
|
|
66
107
|
)
|
|
@@ -69,6 +110,37 @@ export function TranscriptTurnContextView(props: {
|
|
|
69
110
|
);
|
|
70
111
|
}
|
|
71
112
|
|
|
113
|
+
/** Show turn context in the page panel: an aside, or a sheet on small screens. */
|
|
114
|
+
export function TurnContextDetails(props: {
|
|
115
|
+
contexts: TranscriptViewTurnContext[];
|
|
116
|
+
onClose(): void;
|
|
117
|
+
}) {
|
|
118
|
+
return (
|
|
119
|
+
<DetailsPanel
|
|
120
|
+
label="Turn context"
|
|
121
|
+
onClose={props.onClose}
|
|
122
|
+
title={
|
|
123
|
+
<h2 className="m-0 text-base font-semibold text-dashboard-text">
|
|
124
|
+
Turn context
|
|
125
|
+
</h2>
|
|
126
|
+
}
|
|
127
|
+
>
|
|
128
|
+
<div className="px-4 pb-[max(1rem,env(safe-area-inset-bottom))]">
|
|
129
|
+
<TurnContextList contexts={props.contexts} />
|
|
130
|
+
</div>
|
|
131
|
+
</DetailsPanel>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function TurnContextList(props: { contexts: TranscriptViewTurnContext[] }) {
|
|
136
|
+
return props.contexts.map((context, index) => (
|
|
137
|
+
<TurnContext
|
|
138
|
+
context={context}
|
|
139
|
+
key={`${context.pluginName}:${context.kind}:${context.version}:${index}`}
|
|
140
|
+
/>
|
|
141
|
+
));
|
|
142
|
+
}
|
|
143
|
+
|
|
72
144
|
function TurnContext(props: { context: TranscriptViewTurnContext }) {
|
|
73
145
|
const memory = memoryRecallContent(props.context);
|
|
74
146
|
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
1
2
|
import { ArrowDownToLine } from "lucide-react";
|
|
2
3
|
|
|
3
4
|
import type {
|
|
4
5
|
ConversationTranscript,
|
|
5
6
|
TranscriptViewSubagentPart,
|
|
6
7
|
} from "../types";
|
|
7
|
-
import { Button } from "../components/
|
|
8
|
+
import { Button } from "../components/ui/button";
|
|
9
|
+
import {
|
|
10
|
+
Tooltip,
|
|
11
|
+
TooltipContent,
|
|
12
|
+
TooltipTrigger,
|
|
13
|
+
} from "../components/ui/tooltip";
|
|
8
14
|
import { ConversationTranscriptView } from "./ConversationTranscript";
|
|
9
15
|
import { ConversationEventLog } from "./ConversationEventLog";
|
|
10
16
|
import {
|
|
11
17
|
transcriptBottomVersion,
|
|
12
18
|
transcriptJuniorMessageVersion,
|
|
19
|
+
useAutoLoadPreviousPage,
|
|
13
20
|
usePinnedTranscriptBottom,
|
|
14
21
|
} from "./transcriptBottomPinning";
|
|
15
22
|
import type { TranscriptViewMode } from "./transcriptRenderModel";
|
|
16
23
|
import { transcriptEmptyClass } from "./transcriptStyles";
|
|
17
24
|
import { TranscriptSearchProvider } from "./transcriptSearch";
|
|
25
|
+
import {
|
|
26
|
+
TurnContextPanelProvider,
|
|
27
|
+
type TurnContextPanel,
|
|
28
|
+
} from "./TranscriptTurnContextView";
|
|
18
29
|
|
|
19
30
|
/** Render one conversation transcript as ordered message and tool events. */
|
|
20
31
|
export function Transcript(props: {
|
|
@@ -28,16 +39,20 @@ export function Transcript(props: {
|
|
|
28
39
|
onSelectedEventSeqChange?(seq: number | undefined): void;
|
|
29
40
|
responding?: boolean;
|
|
30
41
|
search?: string;
|
|
42
|
+
onSearchChange?(value: string): void;
|
|
31
43
|
selectedEventSeq?: number;
|
|
32
44
|
onOpenSubagentTranscript?: (args: {
|
|
33
45
|
part: TranscriptViewSubagentPart;
|
|
34
46
|
conversation: ConversationTranscript;
|
|
35
47
|
}) => void;
|
|
36
48
|
transcript?: ConversationTranscript;
|
|
49
|
+
/** Show turn context in the owner's panel. Without it, a drawer opens. */
|
|
50
|
+
turnContextPanel?: TurnContextPanel;
|
|
37
51
|
view?: TranscriptViewMode;
|
|
38
52
|
}) {
|
|
39
53
|
const view = props.view ?? "rich";
|
|
40
|
-
const
|
|
54
|
+
const [localSearch, setLocalSearch] = useState("");
|
|
55
|
+
const search = props.search ?? localSearch;
|
|
41
56
|
const historyLabel = view === "raw" ? "events" : "messages";
|
|
42
57
|
const redacted = props.transcript?.eventHistory.status === "redacted";
|
|
43
58
|
const bottomPinning = usePinnedTranscriptBottom({
|
|
@@ -54,6 +69,21 @@ export function Transcript(props: {
|
|
|
54
69
|
},
|
|
55
70
|
});
|
|
56
71
|
|
|
72
|
+
const loadPreviousPage = () => {
|
|
73
|
+
bottomPinning.preserveViewportForPrepend();
|
|
74
|
+
props.onLoadPreviousPage?.();
|
|
75
|
+
};
|
|
76
|
+
const previousPageRef = useAutoLoadPreviousPage({
|
|
77
|
+
enabled: Boolean(
|
|
78
|
+
props.onLoadPreviousPage &&
|
|
79
|
+
props.hasPreviousPage &&
|
|
80
|
+
!props.loadingPreviousPage &&
|
|
81
|
+
!props.historyError,
|
|
82
|
+
),
|
|
83
|
+
historyVersion: props.historyVersion ?? "empty",
|
|
84
|
+
onLoad: loadPreviousPage,
|
|
85
|
+
});
|
|
86
|
+
|
|
57
87
|
if (!props.transcript) {
|
|
58
88
|
return (
|
|
59
89
|
<div className={transcriptEmptyClass()}>
|
|
@@ -71,16 +101,13 @@ export function Transcript(props: {
|
|
|
71
101
|
</div>
|
|
72
102
|
) : null}
|
|
73
103
|
{props.hasPreviousPage || props.loadingPreviousPage ? (
|
|
74
|
-
<div className="mb-3 flex items-center gap-3">
|
|
104
|
+
<div className="mb-3 flex items-center gap-3" ref={previousPageRef}>
|
|
75
105
|
<span className="h-px min-w-4 flex-1 bg-dashboard-fill-strong" />
|
|
76
106
|
<button
|
|
77
107
|
aria-label="Load earlier events"
|
|
78
108
|
className="shrink-0 cursor-pointer border-0 bg-transparent px-1 py-0.5 font-sans text-xs font-medium text-dashboard-text-muted transition-colors hover:text-dashboard-text focus-visible:outline focus-visible:outline-1 focus-visible:outline-dashboard-focus/55 disabled:cursor-not-allowed disabled:opacity-50"
|
|
79
109
|
disabled={props.loadingPreviousPage}
|
|
80
|
-
onClick={
|
|
81
|
-
bottomPinning.preserveViewportForPrepend();
|
|
82
|
-
props.onLoadPreviousPage?.();
|
|
83
|
-
}}
|
|
110
|
+
onClick={loadPreviousPage}
|
|
84
111
|
title="Load earlier events"
|
|
85
112
|
type="button"
|
|
86
113
|
>
|
|
@@ -103,15 +130,18 @@ export function Transcript(props: {
|
|
|
103
130
|
<ConversationEventLog
|
|
104
131
|
key={props.transcript.conversationId}
|
|
105
132
|
conversation={props.transcript}
|
|
133
|
+
onSearchChange={props.onSearchChange ?? setLocalSearch}
|
|
106
134
|
onSelectedSeqChange={props.onSelectedEventSeqChange}
|
|
107
135
|
selectedSeq={props.selectedEventSeq}
|
|
108
136
|
/>
|
|
109
137
|
) : (
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
<TurnContextPanelProvider panel={props.turnContextPanel}>
|
|
139
|
+
<ConversationTranscriptView
|
|
140
|
+
onOpenSubagentTranscript={props.onOpenSubagentTranscript}
|
|
141
|
+
conversation={props.transcript}
|
|
142
|
+
responding={props.responding ?? props.live ?? false}
|
|
143
|
+
/>
|
|
144
|
+
</TurnContextPanelProvider>
|
|
115
145
|
)}
|
|
116
146
|
<div
|
|
117
147
|
aria-hidden="true"
|
|
@@ -141,21 +171,27 @@ function JumpToLatestButton(props: {
|
|
|
141
171
|
|
|
142
172
|
return (
|
|
143
173
|
<div className="pointer-events-none sticky bottom-3 z-20 flex h-0 justify-center px-3 md:fixed md:inset-x-auto md:bottom-6 md:right-8 md:h-auto md:justify-end md:px-0">
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
<Tooltip>
|
|
175
|
+
<TooltipTrigger
|
|
176
|
+
render={
|
|
177
|
+
<Button
|
|
178
|
+
aria-label={label}
|
|
179
|
+
className="pointer-events-auto relative -translate-y-full rounded-lg !border !border-dashboard-border-emphasis !bg-dashboard-surface-active text-dashboard-text shadow-[0_6px_24px_rgba(0,0,0,0.36)] hover:!border-dashboard-border-interactive hover:!bg-dashboard-surface-hover hover:!text-dashboard-text md:translate-y-0"
|
|
180
|
+
onClick={props.onClick}
|
|
181
|
+
size="icon"
|
|
182
|
+
>
|
|
183
|
+
<ArrowDownToLine aria-hidden="true" size={16} strokeWidth={2} />
|
|
184
|
+
{props.hasPendingUpdate ? (
|
|
185
|
+
<span
|
|
186
|
+
aria-hidden="true"
|
|
187
|
+
className="absolute right-1.5 top-1.5 size-2 bg-emerald-300"
|
|
188
|
+
/>
|
|
189
|
+
) : null}
|
|
190
|
+
</Button>
|
|
191
|
+
}
|
|
192
|
+
/>
|
|
193
|
+
<TooltipContent size="compact">{label}</TooltipContent>
|
|
194
|
+
</Tooltip>
|
|
159
195
|
</div>
|
|
160
196
|
);
|
|
161
197
|
}
|
|
@@ -118,3 +118,60 @@ export function eventLogTone(data: ConversationReportEventData): string {
|
|
|
118
118
|
}
|
|
119
119
|
return "text-dashboard-text-subtle";
|
|
120
120
|
}
|
|
121
|
+
|
|
122
|
+
/** One event log entry with its display text. */
|
|
123
|
+
export type EventLogRow = {
|
|
124
|
+
event: ConversationReportEvent;
|
|
125
|
+
summary: string;
|
|
126
|
+
metadata: string;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/** Build the display text for each event log entry. */
|
|
130
|
+
export function eventLogRows(
|
|
131
|
+
events: readonly ConversationReportEvent[],
|
|
132
|
+
): EventLogRow[] {
|
|
133
|
+
return events.map((event) => {
|
|
134
|
+
const model = eventLogModel(event);
|
|
135
|
+
return {
|
|
136
|
+
event,
|
|
137
|
+
summary: eventLogSummary(event.data).replace(/\s+/g, " ").slice(0, 300),
|
|
138
|
+
metadata: [
|
|
139
|
+
model?.modelId,
|
|
140
|
+
model?.modelProfile,
|
|
141
|
+
model?.reasoningLevel && `${model.reasoningLevel} reasoning`,
|
|
142
|
+
eventLogUsage(event),
|
|
143
|
+
]
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.join(" · "),
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Keep the rows that match a trimmed, lowercase search query. The match
|
|
152
|
+
* includes the full event payload, so large payloads are serialized here.
|
|
153
|
+
*/
|
|
154
|
+
export function filterEventLogRows(
|
|
155
|
+
rows: EventLogRow[],
|
|
156
|
+
normalizedQuery: string,
|
|
157
|
+
): EventLogRow[] {
|
|
158
|
+
if (!normalizedQuery) return rows;
|
|
159
|
+
return rows.filter(({ event, summary, metadata }) =>
|
|
160
|
+
`${summary} ${metadata} ${JSON.stringify(event)}`
|
|
161
|
+
.toLowerCase()
|
|
162
|
+
.includes(normalizedQuery),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Find the visible rows before and after the selected event. */
|
|
167
|
+
export function adjacentEventLogSeqs(
|
|
168
|
+
rows: readonly EventLogRow[],
|
|
169
|
+
seq: number | undefined,
|
|
170
|
+
): { previous?: number; next?: number } {
|
|
171
|
+
const index = rows.findIndex((row) => row.event.seq === seq);
|
|
172
|
+
if (index === -1) return {};
|
|
173
|
+
return {
|
|
174
|
+
previous: rows[index - 1]?.event.seq,
|
|
175
|
+
next: rows[index + 1]?.event.seq,
|
|
176
|
+
};
|
|
177
|
+
}
|