@sentry/junior-dashboard 0.276.0 → 0.278.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/dist/app.js +196 -72
- package/dist/client.js +94 -89
- package/dist/index.d.ts +1 -1
- package/dist/index.js +191 -70
- package/dist/tailwind.css +1 -1
- package/package.json +5 -4
- package/src/api/schema.ts +20 -2
- package/src/app.ts +80 -12
- package/src/auth.ts +5 -1
- package/src/client/App.tsx +9 -4
- package/src/client/components/DetailsPanel.tsx +1 -1
- package/src/client/components/FormNotice.tsx +11 -14
- package/src/client/components/ProfileMenu.tsx +5 -6
- package/src/client/components/ui/resizable.tsx +50 -0
- package/src/client/conversations/AutoArchiveToggle.tsx +71 -0
- package/src/client/conversations/ConversationArchiveNotices.tsx +1 -1
- package/src/client/conversations/ConversationChildAgents.tsx +2 -1
- package/src/client/conversations/ConversationDetails.tsx +6 -3
- package/src/client/conversations/ConversationHeader.tsx +15 -2
- package/src/client/conversations/ConversationHeaderActions.tsx +27 -0
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationMemories.tsx +64 -67
- package/src/client/conversations/ConversationMeta.tsx +2 -7
- package/src/client/conversations/ConversationPage.tsx +175 -79
- package/src/client/conversations/ConversationSidebar.tsx +82 -25
- package/src/client/conversations/ConversationTranscript.tsx +11 -8
- package/src/client/conversations/ConversationWatches.tsx +15 -21
- package/src/client/conversations/ConversationWorkspace.tsx +132 -44
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +17 -3
- package/src/client/conversations/DetailsList.tsx +8 -14
- package/src/client/conversations/EventDetails.tsx +2 -62
- package/src/client/conversations/MemoryIcons.tsx +23 -0
- package/src/client/conversations/TranscriptActivityGroup.tsx +36 -14
- package/src/client/conversations/TranscriptContextEventView.tsx +7 -18
- package/src/client/conversations/TranscriptMemoryPopover.tsx +20 -25
- package/src/client/conversations/TranscriptRailEvent.tsx +0 -8
- package/src/client/conversations/TranscriptTurnContextView.tsx +9 -5
- package/src/client/conversations/TranscriptView.tsx +89 -20
- package/src/client/conversations/autoArchiveQueries.ts +53 -0
- package/src/client/conversations/conversationArchive.ts +255 -0
- package/src/client/conversations/conversationMemory.ts +2 -1
- package/src/client/conversations/conversationSections.ts +24 -18
- package/src/client/conversations/conversationSidebarLayout.ts +61 -0
- package/src/client/conversations/eventLog.ts +2 -31
- package/src/client/conversations/eventTranscript.ts +9 -42
- package/src/client/conversations/queries.ts +121 -250
- package/src/client/conversations/transcript.ts +46 -16
- package/src/client/conversations/transcriptBottomPinning.ts +40 -24
- package/src/client/conversations/transcriptReadPosition.ts +31 -7
- package/src/client/conversations/transcriptRenderModel.ts +1 -14
- package/src/client/conversations/transcriptSearch.tsx +3 -11
- package/src/client/conversations/transcriptUnreadHistory.ts +248 -0
- package/src/client/conversations/usePinConversation.ts +88 -0
- package/src/client/display-text/DisplayText.tsx +7 -2
- package/src/client/display-text/codeBlocks.ts +92 -0
- package/src/client/format.ts +3 -67
- package/src/client/http.ts +5 -5
- package/src/client/markdownExport.ts +32 -42
- package/src/client/pages/code/RepositoryTabs.tsx +2 -2
- package/src/client/pages/memory/MemoryDetails.tsx +6 -33
- package/src/client/pages/memory/MemoryPage.tsx +7 -92
- package/src/client/pages/memory/MemoryTimeline.tsx +16 -32
- package/src/client/pages/memory/memoryDashboard.ts +2 -38
- package/src/client/pages/memory/memoryRecord.ts +1 -6
- package/src/client/pages/settings/AutoArchiveSettings.tsx +145 -0
- package/src/client/pages/settings/SettingsPage.tsx +2 -0
- package/src/client/review-desk/ConversationReviewItems.tsx +7 -9
- package/src/client/review-desk/queries.ts +2 -1
- package/src/client/types.ts +19 -33
- package/src/conversation-cache.ts +7 -9
- package/src/mock-reporting/fixtures.ts +126 -60
- package/src/mock-reporting/routes.ts +50 -1
- package/src/tailwind.css +30 -0
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
ArchiveRestore,
|
|
5
5
|
Info,
|
|
6
6
|
MessagesSquare,
|
|
7
|
+
Pin,
|
|
8
|
+
PinOff,
|
|
7
9
|
ScrollText,
|
|
8
10
|
Search,
|
|
9
11
|
X,
|
|
@@ -28,12 +30,20 @@ export type ConversationArchiveAction = {
|
|
|
28
30
|
pending: boolean;
|
|
29
31
|
};
|
|
30
32
|
|
|
33
|
+
export type ConversationPinAction = {
|
|
34
|
+
disabled: boolean;
|
|
35
|
+
error: boolean;
|
|
36
|
+
onClick(): void;
|
|
37
|
+
pinned: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
31
40
|
/**
|
|
32
41
|
* Show conversation actions in the `⋯` menu. On desktop the title bar shows
|
|
33
42
|
* search, share, and details, so the menu leaves them out.
|
|
34
43
|
*/
|
|
35
44
|
export function ConversationHeaderActions(props: {
|
|
36
45
|
archive: ConversationArchiveAction;
|
|
46
|
+
pin: ConversationPinAction;
|
|
37
47
|
conversationId: string;
|
|
38
48
|
copyAction?: ReactNode;
|
|
39
49
|
detailsOpen: boolean;
|
|
@@ -83,6 +93,17 @@ export function ConversationHeaderActions(props: {
|
|
|
83
93
|
/>
|
|
84
94
|
) : null}
|
|
85
95
|
{props.copyAction}
|
|
96
|
+
<MenuActionButton
|
|
97
|
+
disabled={props.pin.disabled}
|
|
98
|
+
label={props.pin.pinned ? "Unpin" : "Pin"}
|
|
99
|
+
onClick={props.pin.onClick}
|
|
100
|
+
>
|
|
101
|
+
{props.pin.pinned ? (
|
|
102
|
+
<PinOff aria-hidden="true" size={16} strokeWidth={2} />
|
|
103
|
+
) : (
|
|
104
|
+
<Pin aria-hidden="true" size={16} strokeWidth={2} />
|
|
105
|
+
)}
|
|
106
|
+
</MenuActionButton>
|
|
86
107
|
<ArchiveConversationButton {...props.archive} />
|
|
87
108
|
{props.sentryConversationUrl ? (
|
|
88
109
|
<a
|
|
@@ -158,6 +179,8 @@ function ViewModeItem(props: {
|
|
|
158
179
|
/** Shared icon control used by conversation and subagent transcript headers. */
|
|
159
180
|
export function HeaderIconButton(props: {
|
|
160
181
|
children: ReactNode;
|
|
182
|
+
controls?: string;
|
|
183
|
+
expanded?: boolean;
|
|
161
184
|
ref?: Ref<HTMLButtonElement>;
|
|
162
185
|
label: string;
|
|
163
186
|
onClick(): void;
|
|
@@ -171,6 +194,8 @@ export function HeaderIconButton(props: {
|
|
|
171
194
|
render={
|
|
172
195
|
<Button
|
|
173
196
|
ref={props.ref}
|
|
197
|
+
aria-controls={props.controls}
|
|
198
|
+
aria-expanded={props.expanded}
|
|
174
199
|
aria-label={props.label}
|
|
175
200
|
aria-pressed={props.pressed}
|
|
176
201
|
className={cn(
|
|
@@ -211,6 +236,7 @@ export function TranscriptSearchToggle(props: {
|
|
|
211
236
|
|
|
212
237
|
function MenuActionButton(props: {
|
|
213
238
|
children: ReactNode;
|
|
239
|
+
disabled?: boolean;
|
|
214
240
|
label: string;
|
|
215
241
|
onClick(): void;
|
|
216
242
|
pressed?: boolean;
|
|
@@ -220,6 +246,7 @@ function MenuActionButton(props: {
|
|
|
220
246
|
aria-label={props.label}
|
|
221
247
|
aria-pressed={props.pressed}
|
|
222
248
|
className={props.pressed ? "bg-dashboard-fill-strong" : undefined}
|
|
249
|
+
disabled={props.disabled}
|
|
223
250
|
onClick={props.onClick}
|
|
224
251
|
size="menu"
|
|
225
252
|
>
|
|
@@ -20,7 +20,7 @@ import { ConversationSidebarAnnotations } from "./ConversationMeta";
|
|
|
20
20
|
import { ConversationArchiveNotices } from "./ConversationArchiveNotices";
|
|
21
21
|
import { ConversationListStatusIcon } from "./ConversationListStatusIcon";
|
|
22
22
|
import { conversationPath } from "./conversationRoutes";
|
|
23
|
-
import { useArchiveConversation } from "./
|
|
23
|
+
import { useArchiveConversation } from "./conversationArchive";
|
|
24
24
|
import { DisplayText } from "../display-text/DisplayText";
|
|
25
25
|
import {
|
|
26
26
|
attentionConversationIds,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ChevronRight } from "lucide-react";
|
|
2
|
-
import type { ConversationDetailReport } from "@sentry/junior/api/schema";
|
|
1
|
+
import { ArrowRight, BookmarkPlus, Brain, ChevronRight } from "lucide-react";
|
|
3
2
|
|
|
4
3
|
import { dashboardDisclosureChevronClass } from "../styles";
|
|
5
4
|
import type {
|
|
@@ -7,6 +6,7 @@ import type {
|
|
|
7
6
|
ConversationMemoryList,
|
|
8
7
|
} from "./conversationMemory";
|
|
9
8
|
import { DetailsCard } from "./DetailsList";
|
|
9
|
+
import { PrivateMemoryIcon } from "./MemoryIcons";
|
|
10
10
|
|
|
11
11
|
/** Count the distinct memories that a Conversation recalled and captured. */
|
|
12
12
|
function conversationMemoryCounts(list: ConversationMemoryList) {
|
|
@@ -48,28 +48,38 @@ export function ConversationMemoryCard(props: {
|
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
51
|
+
/** One memory with its full text, its visibility, and a jump to its Turn. */
|
|
52
|
+
function MemoryCard(props: {
|
|
53
|
+
entry: ConversationMemoryEntry;
|
|
54
|
+
onSelect(entry: ConversationMemoryEntry & { turnId: string }): void;
|
|
55
|
+
}) {
|
|
56
|
+
const { entry } = props;
|
|
57
|
+
const { turnId } = entry;
|
|
58
|
+
return (
|
|
59
|
+
<li className="grid overflow-hidden rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel">
|
|
60
|
+
{/* Break long URLs so the card keeps the panel width. */}
|
|
61
|
+
<p className="m-0 whitespace-pre-wrap px-3 py-2.5 text-sm leading-relaxed text-dashboard-text [overflow-wrap:anywhere]">
|
|
62
|
+
{entry.memory.content}
|
|
63
|
+
</p>
|
|
64
|
+
<div className="flex min-h-8 items-center justify-between gap-2 border-t border-dashboard-border-subtle bg-dashboard-fill-faint px-3 py-1 text-xs text-dashboard-text-muted">
|
|
65
|
+
{entry.memory.visibility === "private" ? (
|
|
66
|
+
<PrivateMemoryIcon />
|
|
67
|
+
) : (
|
|
68
|
+
<span />
|
|
69
|
+
)}
|
|
70
|
+
{turnId ? (
|
|
71
|
+
<button
|
|
72
|
+
className="-mr-1.5 inline-flex cursor-pointer items-center gap-1 rounded-md border-0 bg-transparent px-1.5 py-1 font-sans text-xs font-medium text-dashboard-text-muted transition-colors hover:bg-dashboard-fill-hover hover:text-dashboard-text focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus"
|
|
73
|
+
onClick={() => props.onSelect({ ...entry, turnId })}
|
|
74
|
+
type="button"
|
|
75
|
+
>
|
|
76
|
+
Go to message
|
|
77
|
+
<ArrowRight aria-hidden="true" size={13} />
|
|
78
|
+
</button>
|
|
79
|
+
) : null}
|
|
80
|
+
</div>
|
|
81
|
+
</li>
|
|
82
|
+
);
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
@@ -79,52 +89,39 @@ export function turnPrompts(
|
|
|
79
89
|
export function ConversationMemoryEntries(props: {
|
|
80
90
|
entries: ConversationMemoryEntry[];
|
|
81
91
|
onSelect(entry: ConversationMemoryEntry & { turnId: string }): void;
|
|
82
|
-
prompts: Map<string, string>;
|
|
83
92
|
}) {
|
|
93
|
+
const groups = (["recalled", "captured"] as const)
|
|
94
|
+
.map((source) => ({
|
|
95
|
+
entries: props.entries.filter((entry) => entry.source === source),
|
|
96
|
+
source,
|
|
97
|
+
}))
|
|
98
|
+
.filter((group) => group.entries.length > 0);
|
|
84
99
|
return (
|
|
85
|
-
<
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
? prompt
|
|
92
|
-
? `Recalled for “${prompt}”`
|
|
93
|
-
: "Recalled"
|
|
94
|
-
: prompt
|
|
95
|
-
? `Captured after “${prompt}”`
|
|
96
|
-
: "Captured";
|
|
97
|
-
const body = (
|
|
98
|
-
<>
|
|
99
|
-
<p className="m-0 whitespace-pre-wrap break-words text-sm leading-relaxed text-dashboard-text">
|
|
100
|
-
{entry.memory.content}
|
|
101
|
-
</p>
|
|
102
|
-
<span className="flex min-w-0 items-center gap-2 text-xs text-dashboard-text-muted">
|
|
103
|
-
<span className="min-w-0 truncate">{source}</span>
|
|
104
|
-
{entry.memory.visibility === "private" ? (
|
|
105
|
-
<span className="shrink-0">· Private</span>
|
|
106
|
-
) : null}
|
|
107
|
-
</span>
|
|
108
|
-
</>
|
|
109
|
-
);
|
|
110
|
-
const className =
|
|
111
|
-
"grid w-full gap-1.5 rounded-lg border border-dashboard-border-subtle bg-dashboard-surface-panel px-3 py-2.5 text-left font-sans";
|
|
112
|
-
return (
|
|
113
|
-
<li key={`${entry.source}:${entry.memory.id}`}>
|
|
114
|
-
{turnId ? (
|
|
115
|
-
<button
|
|
116
|
-
className={`${className} cursor-pointer transition-colors hover:bg-dashboard-surface-hover focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-dashboard-focus`}
|
|
117
|
-
onClick={() => props.onSelect({ ...entry, turnId })}
|
|
118
|
-
type="button"
|
|
119
|
-
>
|
|
120
|
-
{body}
|
|
121
|
-
</button>
|
|
100
|
+
<div className="grid gap-5">
|
|
101
|
+
{groups.map((group) => (
|
|
102
|
+
<section className="grid gap-2" key={group.source}>
|
|
103
|
+
<h3 className="m-0 flex items-center gap-1.5 font-sans text-xs font-semibold text-dashboard-text">
|
|
104
|
+
{group.source === "recalled" ? (
|
|
105
|
+
<Brain aria-hidden="true" size={13} strokeWidth={1.8} />
|
|
122
106
|
) : (
|
|
123
|
-
<
|
|
107
|
+
<BookmarkPlus aria-hidden="true" size={13} strokeWidth={1.8} />
|
|
124
108
|
)}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
109
|
+
{group.source === "recalled" ? "Recalled" : "Captured"}
|
|
110
|
+
<span className="font-normal tabular-nums text-dashboard-text-muted">
|
|
111
|
+
{group.entries.length}
|
|
112
|
+
</span>
|
|
113
|
+
</h3>
|
|
114
|
+
<ol className="m-0 grid list-none gap-2 p-0">
|
|
115
|
+
{group.entries.map((entry) => (
|
|
116
|
+
<MemoryCard
|
|
117
|
+
entry={entry}
|
|
118
|
+
key={`${entry.source}:${entry.memory.id}`}
|
|
119
|
+
onSelect={props.onSelect}
|
|
120
|
+
/>
|
|
121
|
+
))}
|
|
122
|
+
</ol>
|
|
123
|
+
</section>
|
|
124
|
+
))}
|
|
125
|
+
</div>
|
|
129
126
|
);
|
|
130
127
|
}
|
|
@@ -469,7 +469,7 @@ function SourceTask(props: {
|
|
|
469
469
|
);
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
-
/** Resolve the model
|
|
472
|
+
/** Resolve the model that an open turn last called. */
|
|
473
473
|
export function liveModelId(
|
|
474
474
|
detail: ConversationDetailReport | undefined,
|
|
475
475
|
): string | undefined {
|
|
@@ -484,12 +484,7 @@ export function liveModelId(
|
|
|
484
484
|
if (openTurns.size === 0) modelId = undefined;
|
|
485
485
|
continue;
|
|
486
486
|
}
|
|
487
|
-
if (openTurns.size
|
|
488
|
-
if (data.type === "turn_routed" && openTurns.has(data.turnId)) {
|
|
489
|
-
modelId = data.modelId;
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
if (data.type === "handoff") modelId = data.modelId;
|
|
487
|
+
if (openTurns.size > 0 && event.model) modelId = event.model.modelId;
|
|
493
488
|
}
|
|
494
489
|
return openTurns.size > 0 ? modelId : undefined;
|
|
495
490
|
}
|