@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
|
@@ -7,18 +7,22 @@ export type ConversationSection = {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
10
|
-
//
|
|
10
|
+
// Pinned sorts first, then Needs attention, then every time section.
|
|
11
|
+
const PINNED_SECTION = { key: "pinned", label: "Pinned", order: -2 };
|
|
11
12
|
const ATTENTION_SECTION = {
|
|
12
13
|
key: "attention",
|
|
13
14
|
label: "Needs attention",
|
|
14
|
-
order:
|
|
15
|
+
order: -1,
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
|
-
* Group conversations into Needs attention, then by last
|
|
19
|
-
* Yesterday, Previous 7 days, Previous 30 days, then one
|
|
19
|
+
* Group conversations into Pinned, then Needs attention, then by last
|
|
20
|
+
* activity: Today, Yesterday, Previous 7 days, Previous 30 days, then one
|
|
21
|
+
* section per month.
|
|
20
22
|
*
|
|
21
|
-
*
|
|
23
|
+
* A pinned conversation shows only in Pinned, even when it needs attention.
|
|
24
|
+
* Conversations in `attentionIds` go to Needs attention. Each section lists
|
|
25
|
+
* the newest first.
|
|
22
26
|
*/
|
|
23
27
|
export function buildConversationSections(
|
|
24
28
|
conversations: Conversation[],
|
|
@@ -35,14 +39,16 @@ export function buildConversationSections(
|
|
|
35
39
|
);
|
|
36
40
|
|
|
37
41
|
for (const conversation of sorted) {
|
|
38
|
-
const section =
|
|
39
|
-
?
|
|
40
|
-
:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
const section = conversation.pinnedAt
|
|
43
|
+
? PINNED_SECTION
|
|
44
|
+
: options.attentionIds?.has(conversation.id)
|
|
45
|
+
? ATTENTION_SECTION
|
|
46
|
+
: conversationSection(
|
|
47
|
+
activityTime(conversation),
|
|
48
|
+
nowDay,
|
|
49
|
+
options.nowMs,
|
|
50
|
+
options.timeZone,
|
|
51
|
+
);
|
|
46
52
|
const existing = sections.get(section.key);
|
|
47
53
|
if (existing) {
|
|
48
54
|
existing.conversations.push(conversation);
|
|
@@ -65,16 +71,16 @@ export function attentionConversationIds(
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
/**
|
|
68
|
-
* Sort attention conversations to the front. The sort is stable,
|
|
69
|
-
* keeps the feed order. Paginated lists call this before paging,
|
|
70
|
-
* holds every attention conversation.
|
|
74
|
+
* Sort pinned, then attention conversations to the front. The sort is stable,
|
|
75
|
+
* so each group keeps the feed order. Paginated lists call this before paging,
|
|
76
|
+
* so page 1 holds every pinned and attention conversation.
|
|
71
77
|
*/
|
|
72
|
-
export function
|
|
78
|
+
export function pinnedAndAttentionFirst(
|
|
73
79
|
conversations: Conversation[],
|
|
74
80
|
attentionIds: ReadonlySet<string>,
|
|
75
81
|
): Conversation[] {
|
|
76
82
|
const rank = (conversation: Conversation) =>
|
|
77
|
-
attentionIds.has(conversation.id) ?
|
|
83
|
+
conversation.pinnedAt ? 0 : attentionIds.has(conversation.id) ? 1 : 2;
|
|
78
84
|
return [...conversations].sort((left, right) => rank(left) - rank(right));
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useState, useSyncExternalStore } from "react";
|
|
2
|
+
|
|
3
|
+
const OPEN_KEY = "junior:conversation:sidebar-open";
|
|
4
|
+
const WIDTH_KEY = "junior:conversation:sidebar-width";
|
|
5
|
+
const WIDE_QUERY = "(min-width: 768px)";
|
|
6
|
+
|
|
7
|
+
/** Pixel limits for the conversation list beside a selected conversation. */
|
|
8
|
+
export const SIDEBAR_WIDTH = { default: 320, max: 480, min: 240 } as const;
|
|
9
|
+
|
|
10
|
+
/** Remember whether the conversation list is open in this browser. */
|
|
11
|
+
export function useConversationSidebarOpen() {
|
|
12
|
+
const [open, setOpen] = useState(() => readStorage(OPEN_KEY) !== "false");
|
|
13
|
+
|
|
14
|
+
function updateOpen(next: boolean) {
|
|
15
|
+
setOpen(next);
|
|
16
|
+
writeStorage(OPEN_KEY, String(next));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return [open, updateOpen] as const;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Read the conversation list width saved in this browser. */
|
|
23
|
+
export function readSidebarWidth(): number {
|
|
24
|
+
const saved = Number(readStorage(WIDTH_KEY));
|
|
25
|
+
if (!Number.isFinite(saved) || saved <= 0) return SIDEBAR_WIDTH.default;
|
|
26
|
+
return Math.min(SIDEBAR_WIDTH.max, Math.max(SIDEBAR_WIDTH.min, saved));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Save the conversation list width without a React render per drag step. */
|
|
30
|
+
export function saveSidebarWidth(width: number) {
|
|
31
|
+
writeStorage(WIDTH_KEY, String(Math.round(width)));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Report whether the viewport has room for the list beside the transcript. */
|
|
35
|
+
export function useSidebarFits(): boolean {
|
|
36
|
+
return useSyncExternalStore(
|
|
37
|
+
(onChange) => {
|
|
38
|
+
const media = window.matchMedia(WIDE_QUERY);
|
|
39
|
+
media.addEventListener("change", onChange);
|
|
40
|
+
return () => media.removeEventListener("change", onChange);
|
|
41
|
+
},
|
|
42
|
+
() => window.matchMedia(WIDE_QUERY).matches,
|
|
43
|
+
() => false,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readStorage(key: string): string | null {
|
|
48
|
+
try {
|
|
49
|
+
return window.localStorage.getItem(key);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function writeStorage(key: string, value: string) {
|
|
56
|
+
try {
|
|
57
|
+
window.localStorage.setItem(key, value);
|
|
58
|
+
} catch {
|
|
59
|
+
// The layout still works for this page when browser storage is unavailable.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -10,31 +10,12 @@ import {
|
|
|
10
10
|
summarizeCost,
|
|
11
11
|
} from "../format";
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
export function eventLogModel(
|
|
15
|
-
event: ConversationReportEvent,
|
|
16
|
-
): ConversationReportEvent["model"] {
|
|
17
|
-
if (event.model) return event.model;
|
|
18
|
-
const data = event.data;
|
|
19
|
-
if (data.type === "turn_routed" || data.type === "handoff") {
|
|
20
|
-
return {
|
|
21
|
-
modelId: data.modelId,
|
|
22
|
-
modelProfile: data.modelProfile,
|
|
23
|
-
reasoningLevel: data.reasoningLevel,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Keep call costs distinct from router cost and never repeat usage on tool results. */
|
|
13
|
+
/** Show call usage once and never repeat it on tool results. */
|
|
30
14
|
export function eventLogUsage(event: ConversationReportEvent): string {
|
|
31
15
|
const usage = event.modelCall?.usage;
|
|
32
16
|
const cost = summarizeCost(usage);
|
|
33
17
|
const fields = [
|
|
34
18
|
cost ? `${formatCostBreakdown(cost)} call` : undefined,
|
|
35
|
-
event.data.type === "turn_routed" && event.data.costUsd !== undefined
|
|
36
|
-
? `${formatCostBreakdown({ total: event.data.costUsd })} router`
|
|
37
|
-
: undefined,
|
|
38
19
|
...(
|
|
39
20
|
[
|
|
40
21
|
["inputTokens", "in"],
|
|
@@ -77,8 +58,6 @@ export function eventLogSummary(data: ConversationReportEventData): string {
|
|
|
77
58
|
return data.state === "failed"
|
|
78
59
|
? `${data.state} · ${data.failureCode}${data.failureReason ? ` · ${data.failureReason}` : ""}`
|
|
79
60
|
: `${data.state} · ${data.turnId}`;
|
|
80
|
-
case "turn_routed":
|
|
81
|
-
return `Route selected · ${data.source}${data.confidence !== undefined ? ` · ${Math.round(data.confidence * 100)}% confidence` : ""}`;
|
|
82
61
|
case "turn_context":
|
|
83
62
|
return `${data.pluginName} · ${data.kind} · v${data.version}`;
|
|
84
63
|
case "guardian_action_reviewed":
|
|
@@ -91,8 +70,6 @@ export function eventLogSummary(data: ConversationReportEventData): string {
|
|
|
91
70
|
.join(", ");
|
|
92
71
|
case "compaction":
|
|
93
72
|
return data.summary ?? "Agent history compacted";
|
|
94
|
-
case "handoff":
|
|
95
|
-
return "Handoff · New configuration applied";
|
|
96
73
|
case "subagent":
|
|
97
74
|
return `${data.agentName ?? data.subagentKind} · ${data.status} · ${data.childConversationId}`;
|
|
98
75
|
}
|
|
@@ -132,16 +109,10 @@ export function eventLogRows(
|
|
|
132
109
|
events: readonly ConversationReportEvent[],
|
|
133
110
|
): EventLogRow[] {
|
|
134
111
|
return events.map((event) => {
|
|
135
|
-
const model = eventLogModel(event);
|
|
136
112
|
return {
|
|
137
113
|
event,
|
|
138
114
|
summary: eventLogSummary(event.data).replace(/\s+/g, " ").slice(0, 300),
|
|
139
|
-
metadata: [
|
|
140
|
-
model?.modelId,
|
|
141
|
-
model?.modelProfile,
|
|
142
|
-
model?.reasoningLevel && `${model.reasoningLevel} reasoning`,
|
|
143
|
-
eventLogUsage(event),
|
|
144
|
-
]
|
|
115
|
+
metadata: [event.model?.modelId, eventLogUsage(event)]
|
|
145
116
|
.filter(Boolean)
|
|
146
117
|
.join(" · "),
|
|
147
118
|
};
|
|
@@ -51,9 +51,6 @@ function specialToolIds(events: ConversationReportEvent[]): Set<string> {
|
|
|
51
51
|
const ids = new Set<string>();
|
|
52
52
|
for (const event of events) {
|
|
53
53
|
const data = event.data;
|
|
54
|
-
if (data.type === "handoff" && data.triggeringToolCallId) {
|
|
55
|
-
ids.add(data.triggeringToolCallId);
|
|
56
|
-
}
|
|
57
54
|
if (data.type === "subagent" && data.parentToolCallId) {
|
|
58
55
|
ids.add(data.parentToolCallId);
|
|
59
56
|
}
|
|
@@ -243,6 +240,7 @@ export function transcriptMessagesFromEvents(
|
|
|
243
240
|
...(data.explicitMention !== undefined
|
|
244
241
|
? { explicitMention: data.explicitMention }
|
|
245
242
|
: undefined),
|
|
243
|
+
...(data.quiet ? { quiet: data.quiet } : undefined),
|
|
246
244
|
...(data.reviewItemChanges
|
|
247
245
|
? { reviewItemChanges: data.reviewItemChanges }
|
|
248
246
|
: undefined),
|
|
@@ -308,22 +306,6 @@ export function transcriptMessagesFromEvents(
|
|
|
308
306
|
continue;
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
if (data.type === "turn_routed") {
|
|
312
|
-
const message = latestUserMessageByTurn.get(data.turnId);
|
|
313
|
-
if (message) {
|
|
314
|
-
message.route = {
|
|
315
|
-
modelProfile: data.modelProfile,
|
|
316
|
-
modelId: data.modelId,
|
|
317
|
-
reasoningLevel: data.reasoningLevel,
|
|
318
|
-
...(data.confidence !== undefined
|
|
319
|
-
? { confidence: data.confidence }
|
|
320
|
-
: undefined),
|
|
321
|
-
source: data.source,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
309
|
if (data.type === "turn_context") {
|
|
328
310
|
const message = latestUserMessageByTurn.get(data.turnId);
|
|
329
311
|
if (message) {
|
|
@@ -437,34 +419,19 @@ export function transcriptMessagesFromEvents(
|
|
|
437
419
|
continue;
|
|
438
420
|
}
|
|
439
421
|
|
|
440
|
-
if (data.type === "compaction"
|
|
422
|
+
if (data.type === "compaction") {
|
|
441
423
|
messages.push(
|
|
442
424
|
eventMessage(event, "system", [
|
|
443
425
|
{
|
|
444
426
|
type: "context_event",
|
|
445
427
|
...(event.mentions ? { mentions: event.mentions } : undefined),
|
|
446
|
-
event:
|
|
447
|
-
data.type
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
...(data.summary ? { summary: data.summary } : undefined),
|
|
454
|
-
...(data.reasoningLevel
|
|
455
|
-
? { reasoningLevel: data.reasoningLevel }
|
|
456
|
-
: undefined),
|
|
457
|
-
}
|
|
458
|
-
: {
|
|
459
|
-
type: data.type,
|
|
460
|
-
createdAt: event.createdAt,
|
|
461
|
-
...(data.modelId ? { modelId: data.modelId } : undefined),
|
|
462
|
-
...(data.modelProfile
|
|
463
|
-
? { modelProfile: data.modelProfile }
|
|
464
|
-
: undefined),
|
|
465
|
-
...(data.summary ? { summary: data.summary } : undefined),
|
|
466
|
-
...(data.details ? { details: data.details } : undefined),
|
|
467
|
-
},
|
|
428
|
+
event: {
|
|
429
|
+
type: data.type,
|
|
430
|
+
createdAt: event.createdAt,
|
|
431
|
+
...(data.modelId ? { modelId: data.modelId } : undefined),
|
|
432
|
+
...(data.summary ? { summary: data.summary } : undefined),
|
|
433
|
+
...(data.details ? { details: data.details } : undefined),
|
|
434
|
+
},
|
|
468
435
|
},
|
|
469
436
|
]),
|
|
470
437
|
);
|