Skip to content

Commit 2c8a396

Browse files
committed
Merge branch 'feat/dashboard-agent-ui' into feat/dashboard-agent-flows-watch
2 parents 4d695bb + bf82cce commit 2c8a396

4 files changed

Lines changed: 45 additions & 13 deletions

File tree

apps/webapp/app/components/dashboard-agent/DashboardAgentChat.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import type { AgentPageContext } from "./page-context-types";
2424
import { retryAction } from "./retry-action";
2525
import {
2626
fetchChatTranscript,
27-
hasOpenInvestigation,
2827
pollSettledTranscript,
28+
transcriptLooksUnfinished,
2929
} from "./settled-transcript";
3030
import { takeNavigateIntent } from "./turn-navigation";
3131
import { sendRequestOutcome } from "./send-request";
@@ -373,8 +373,9 @@ export function DashboardAgentChat({
373373

374374
onTurnSettled();
375375
// The terminal card is written to the chat row after the stream closes, so this
376-
// mounted panel would otherwise keep showing the last `in_progress` revision.
377-
if (!hasOpenInvestigation(messagesRef.current)) return;
376+
// mounted panel would otherwise keep showing the last `in_progress` revision — or,
377+
// if the stream died mid-tool, the tool call it never got an output for.
378+
if (!transcriptLooksUnfinished(messagesRef.current)) return;
378379
void pollSettledTranscript<UIMessage>({
379380
fetchTranscript: () => fetchChatTranscript(actionPath, chatId),
380381
apply: (merge) => setMessages((current) => merge(current)),

apps/webapp/app/components/dashboard-agent/report-sparkline.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,10 @@ export function ReportProvenance({ uri }: { uri: string }) {
464464
// --- sparkline --------------------------------------------------------------
465465

466466
/** The fixed sparkline column. Keeps every sparkline aligned. */
467-
const SPARK_WIDTH_CLASS = "w-[6.5rem]";
467+
const SPARK_WIDTH_CLASS = "w-[5.5rem]";
468468

469469
/** The chart's own width; the trailing peak label uses the column's remainder. */
470-
const SPARK_WIDTH = 72;
470+
const SPARK_WIDTH = 56;
471471

472472
type ReportSparkDatum = { count: number; date: Date | null; hot: boolean };
473473

@@ -570,18 +570,18 @@ export function ReportSparkline({
570570
* chart start on the same vertical whatever the value's width.
571571
*/
572572
/**
573-
* Below a 22rem container the fixed tracks no longer fit beside the value, so the
573+
* Below a 19rem container the fixed tracks no longer fit beside the value, so the
574574
* sparkline drops to its own line. The columns never change, so the value, delta
575575
* and note stay on the same verticals at every panel width.
576576
*/
577577
const METRIC_ROW_CLASS =
578-
"grid grid-cols-[7rem_minmax(0,1fr)_2.75rem_6.5rem] items-center gap-x-2 @max-[22rem]:grid-cols-[7rem_minmax(0,1fr)_2.75rem] @max-[22rem]:gap-y-1.5";
578+
"grid grid-cols-[6rem_minmax(0,1fr)_2.75rem_5.5rem] items-center gap-x-2 @max-[19rem]:grid-cols-[6rem_minmax(0,1fr)_2.75rem] @max-[19rem]:gap-y-1.5";
579579

580580
/** The sparkline cell: its own full-width line once the row goes narrow. */
581-
const SPARK_CELL_CLASS = "@max-[22rem]:col-span-3 @max-[22rem]:justify-self-end";
581+
const SPARK_CELL_CLASS = "@max-[19rem]:col-span-3 @max-[19rem]:justify-self-end";
582582

583-
// Labels are never truncated: the column is sized for the longest one and
584-
// anything longer wraps.
583+
// Labels are never truncated: the column fits the common ones and anything
584+
// longer wraps.
585585
const LABEL_CLASS = "text-xs uppercase leading-tight tracking-wide text-text-dimmed";
586586

587587
/** A metric's movement against its baseline. Direction is always an arrow. */
@@ -674,7 +674,7 @@ export function ReportMetricRow({
674674
) : (
675675
// Keeps the column occupied so a series-less metric doesn't pull the
676676
// rows out of alignment.
677-
<span aria-hidden className="@max-[22rem]:hidden" />
677+
<span aria-hidden className="@max-[19rem]:hidden" />
678678
)}
679679
</li>
680680

@@ -683,7 +683,7 @@ export function ReportMetricRow({
683683
// vertical as every other row's value.
684684
<li key={sub.label} className={METRIC_ROW_CLASS}>
685685
{/* Indented under the parent label, shallow enough to stay inside the
686-
7rem label column. */}
686+
6rem label column. */}
687687
<span className={cn(LABEL_CLASS, "pl-6")}>{sub.label}</span>
688688
<span className="whitespace-nowrap text-sm tabular-nums text-text-dimmed">
689689
{sub.value}

apps/webapp/app/components/dashboard-agent/settled-transcript.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
hasOpenInvestigation,
77
mergeSettledMessages,
88
pollSettledTranscript,
9+
transcriptLooksUnfinished,
910
} from "./settled-transcript";
1011

1112
/**
@@ -119,6 +120,27 @@ describe("reading the transcript endpoint", () => {
119120
});
120121
});
121122

123+
describe("deciding whether a settled turn is worth re-reading", () => {
124+
// The stream EOF'd while `get_report` was running: the part never gets an output.
125+
const DANGLING_TOOL = {
126+
id: "msg_dangling",
127+
role: "assistant",
128+
parts: [{ type: "tool-get_report", toolCallId: "call_1", state: "input-available" }],
129+
};
130+
131+
it("re-reads when the stream died mid-tool, not only when a card is open", () => {
132+
expect(transcriptLooksUnfinished([DANGLING_TOOL])).toBe(true);
133+
});
134+
135+
it("re-reads while a card is still open", () => {
136+
expect(transcriptLooksUnfinished([OPEN])).toBe(true);
137+
});
138+
139+
it("leaves a fully settled transcript alone", () => {
140+
expect(transcriptLooksUnfinished([OPEN, SETTLED])).toBe(false);
141+
});
142+
});
143+
122144
describe("an already-open panel when a turn is exhausted", () => {
123145
it("stops showing Working… without a reload or a reopen", async () => {
124146
// What the mounted panel holds when the stream closes: the card the model opened

apps/webapp/app/components/dashboard-agent/settled-transcript.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { liveInvestigation } from "./progress-line";
1+
import { inFlightToolName, liveInvestigation } from "./progress-line";
22

33
/**
44
* Re-reading the stored transcript once a turn settles.
@@ -29,6 +29,15 @@ export function hasOpenInvestigation(messages: ReadonlyArray<unknown>): boolean
2929
return liveInvestigation(messages as never) !== null;
3030
}
3131

32+
/**
33+
* Whether the transcript still reads as mid-turn. A stream that dies without
34+
* `turn-complete` leaves the tool part it was on dangling forever, so an open card is
35+
* not the only shape a re-read has to recover from.
36+
*/
37+
export function transcriptLooksUnfinished(messages: ReadonlyArray<unknown>): boolean {
38+
return hasOpenInvestigation(messages) || inFlightToolName(messages as never) !== null;
39+
}
40+
3241
/**
3342
* The settlement is written in `onTurnComplete`, which runs AFTER the client's stream
3443
* closes, so the first re-read can legitimately land before it. Retry a few times,

0 commit comments

Comments
 (0)