From 84d3309cafa40dd7adbd517c248a6b80411e2547 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2026 04:51:33 +0000 Subject: [PATCH 1/2] fix(dashboard): Show conversation cost in the title bar Co-Authored-By: Thomas Jiang <20501235+thomasjiangcy@users.noreply.github.com> --- .../e2e/conversation-details.spec.ts | 6 +- .../conversations/ConversationHeader.tsx | 12 +++- .../client/conversations/ConversationMeta.tsx | 55 ++++++++++++------- .../client/conversations/ConversationPage.tsx | 2 + .../client/conversations/TelemetryMetrics.tsx | 3 +- 5 files changed, 53 insertions(+), 25 deletions(-) diff --git a/packages/junior-dashboard/e2e/conversation-details.spec.ts b/packages/junior-dashboard/e2e/conversation-details.spec.ts index 8259cb8122..a3d294c999 100644 --- a/packages/junior-dashboard/e2e/conversation-details.spec.ts +++ b/packages/junior-dashboard/e2e/conversation-details.spec.ts @@ -48,11 +48,11 @@ test("opens conversation details beside the chat", async ({ }); // Wide viewports open details beside the chat by default. await expect(details).toBeVisible(); - // Run facts live in details only, not in the title bar. + // Cost stays in the title bar. Desktop details hide their copy of it. const costMetric = page .getByRole("main") .locator('span[tabindex="0"]') - .filter({ hasText: /^\$0\.03$/ }); + .filter({ hasText: /^\$0\.03$/, visible: true }); await expect(costMetric).toHaveCount(1); // Tooltips open on keyboard focus, not on focus after a mouse click. A key // press first makes this a keyboard focus. @@ -72,7 +72,7 @@ test("opens conversation details beside the chat", async ({ ).toHaveAttribute("href", "https://github.com/getsentry/payments/pull/77"); await detailsButton.click(); await expect(details).toBeHidden(); - await expect(costMetric).toHaveCount(0); + await expect(costMetric).toHaveCount(1); await context.grantPermissions(["clipboard-read", "clipboard-write"], { origin: dashboard.baseURL, }); diff --git a/packages/junior-dashboard/src/client/conversations/ConversationHeader.tsx b/packages/junior-dashboard/src/client/conversations/ConversationHeader.tsx index 22acf3c50a..a15b835c84 100644 --- a/packages/junior-dashboard/src/client/conversations/ConversationHeader.tsx +++ b/packages/junior-dashboard/src/client/conversations/ConversationHeader.tsx @@ -20,14 +20,17 @@ import { ShareConversationButton } from "./ShareConversationButton"; import type { TranscriptViewMode } from "./transcriptRenderModel"; /** - * Show the conversation title bar: where it runs, the title, and actions. - * Run facts, linked work, and the Brief live in Conversation details. + * Show the conversation title bar: where it runs, the title, its cost, and + * actions. Other run facts, linked work, and the Brief live in Conversation + * details. */ export function ConversationHeader(props: { /** Parent chain of a child Conversation. Root Conversations omit it. */ agentTree?: ReactNode; /** A `menu` layout copy action for the `⋯` menu. */ copyAction?: ReactNode; + /** Estimated cost. Desktop only; mobile shows it in Conversation details. */ + cost?: ReactNode; archive: ConversationArchiveAction; conversationId: string; detailsOpen: boolean; @@ -179,6 +182,11 @@ export function ConversationHeader(props: { {props.privacy}
+ {props.cost ? ( + + {props.cost} + + ) : null} {props.view === "raw" ? ( // Event log is a debug view reached from the menu. Say which // view this is, and give one click back. diff --git a/packages/junior-dashboard/src/client/conversations/ConversationMeta.tsx b/packages/junior-dashboard/src/client/conversations/ConversationMeta.tsx index 5c10dc37ea..14f53a54f6 100644 --- a/packages/junior-dashboard/src/client/conversations/ConversationMeta.tsx +++ b/packages/junior-dashboard/src/client/conversations/ConversationMeta.tsx @@ -568,6 +568,37 @@ export function conversationLocation( ); } +/** + * Show the estimated cost of a conversation with its breakdown tooltip. + * Returns null until there is a cost or a live run to show. + */ +export function conversationCost( + conversation: Conversation | undefined, + detail: ConversationDetailReport | undefined, + tooltipPlacement?: "above" | "below", +): ReactNode { + if (!conversation) return null; + const costSummary = summarizeCost( + detail?.cumulativeUsage ?? conversation.cumulativeUsage, + ); + const auxiliaryCosts = detail?.auxiliaryCosts ?? conversation.auxiliaryCosts; + const live = (detail?.status ?? conversation.status) === "active"; + if (!costSummary && !auxiliaryCosts && !live) return null; + return ( + + ); +} + function conversationStatItems(props: { conversation: Conversation | undefined; detail?: ConversationDetailReport; @@ -580,7 +611,6 @@ function conversationStatItems(props: { props.detail?.cumulativeUsage ?? props.conversation.cumulativeUsage; const tokenSummary = summarizeModelUsage(props.detail?.modelUsage) ?? summarizeUsage(usage); - const costSummary = summarizeCost(usage); const durationLabel = formatConversationDuration(props.conversation); const live = (props.detail?.status ?? props.conversation.status) === "active"; const activeModelId = liveModelId(props.detail); @@ -589,8 +619,9 @@ function conversationStatItems(props: { props.detail?.childAgentTotals ?? props.conversation.childAgentTotals; const lastActivityAt = props.conversation.lastSeenAt; const location = conversationLocation(props.conversation, props.detail); + const cost = conversationCost(props.conversation, props.detail); const rawStats: Array = [ - // The desktop title bar shows the location. Mobile hides that bar. + // The desktop title bar shows the location and cost. Mobile hides that bar. location ? { content: location, @@ -660,26 +691,12 @@ function conversationStatItems(props: { label: "Tokens", } : undefined, - costSummary || - props.detail?.auxiliaryCosts || - props.conversation.auxiliaryCosts || - live + cost ? { - content: ( - - ), + content: cost, key: "cost", label: "Cost", + mobileOnly: true, } : undefined, childAgentTotals diff --git a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx index 7b20e38bad..4ae43473d8 100644 --- a/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx +++ b/packages/junior-dashboard/src/client/conversations/ConversationPage.tsx @@ -50,6 +50,7 @@ import { ConversationAgentTree } from "./ConversationAgentTree"; import { conversationLinks } from "./ConversationLinkedWork"; import { ConversationAnnotations, + conversationCost, conversationLocation, ConversationPrivacyChip, ConversationStats, @@ -373,6 +374,7 @@ export function ConversationPage(props: { agentTree ? : null } conversationId={conversationId} + cost={conversationCost(conversation, detail.data, "below")} detailsOpen={showDetails} onDetailsClick={() => { if (eventOpen) setSelectedEventSeq(undefined); diff --git a/packages/junior-dashboard/src/client/conversations/TelemetryMetrics.tsx b/packages/junior-dashboard/src/client/conversations/TelemetryMetrics.tsx index 35935b1e9b..3d6b48bd3e 100644 --- a/packages/junior-dashboard/src/client/conversations/TelemetryMetrics.tsx +++ b/packages/junior-dashboard/src/client/conversations/TelemetryMetrics.tsx @@ -350,6 +350,7 @@ export function CostMetric(props: { liveModelId?: string; modelUsage?: ConversationModelUsage[]; summary: CostUsageSummary | undefined; + tooltipPlacement?: "above" | "below"; }) { // Model history can include calls not yet counted in saved Run totals. const summary = summarizeModelCost(props.modelUsage) ?? props.summary; @@ -369,7 +370,7 @@ export function CostMetric(props: { return ( Date: Wed, 4 Nov 2026 05:01:15 +0000 Subject: [PATCH 2/2] fix(dashboard): Tighten the usage card type hierarchy Co-Authored-By: Thomas Jiang <20501235+thomasjiangcy@users.noreply.github.com> --- .../junior-dashboard/src/client/conversations/UsageCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/junior-dashboard/src/client/conversations/UsageCard.tsx b/packages/junior-dashboard/src/client/conversations/UsageCard.tsx index 2d322dbf71..48977b604b 100644 --- a/packages/junior-dashboard/src/client/conversations/UsageCard.tsx +++ b/packages/junior-dashboard/src/client/conversations/UsageCard.tsx @@ -66,13 +66,13 @@ export function UsageCard(props: UsageCardProps) { )} > - + {props.title} {props.note ? ( · {props.note} ) : null} - + {props.total} @@ -124,7 +124,7 @@ export function UsageCard(props: UsageCardProps) {