From 73cb7ff8d6e1d84580aa9e0eb559502ddf199d11 Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:36:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(views):=20transcript=20summary/controls=20?= =?UTF-8?q?=E2=80=94=20JSON=20preview,=20unify=20sort=20button,=20attribut?= =?UTF-8?q?ion=20to=20=E2=93=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tool-result summary took the first non-empty line, so pretty-printed JSON previewed as a lone "[" or "{". Collapse whitespace instead so it reads "[ { "id": ... } ]". - Sort was a segmented tab strip, reading as a different control family. Make it a single two-state toggle button on the shared toolbar chassis (shows current direction, flips on click) — every toolbar control now shares one chassis with a type-appropriate affordance (toggle / menu / action), not one forced shape. - Attribution left the identity row. The accountable human is audit metadata, not read-time context, and "on behalf of" misread the direction; the trigger *mechanism* stays on the identity row, the *person* moves to the ⓘ popover as "Triggered by". - Drop the tool-call count from the toolbar: redundant with the event count and informs no reading decision. Co-Authored-By: Claude Fable 5 --- .../agent-transcript-dialog.tsx | 74 ++++++++----------- .../trace-event-presenter.test.ts | 10 ++- .../task-transcript/trace-event-presenter.ts | 11 ++- packages/views/locales/en/agents.json | 3 +- packages/views/locales/ja/agents.json | 3 +- packages/views/locales/ko/agents.json | 3 +- packages/views/locales/zh-Hans/agents.json | 3 +- 7 files changed, 54 insertions(+), 53 deletions(-) diff --git a/packages/views/common/task-transcript/agent-transcript-dialog.tsx b/packages/views/common/task-transcript/agent-transcript-dialog.tsx index 90116b73c..9f4bf9c08 100644 --- a/packages/views/common/task-transcript/agent-transcript-dialog.tsx +++ b/packages/views/common/task-transcript/agent-transcript-dialog.tsx @@ -436,7 +436,6 @@ export function AgentTranscriptDialog({ ? elapsed : null; - const toolCount = items.filter((i) => i.type === "tool_use").length; const copyTranscriptLabel = copied ? t(($) => $.transcript.copied) : activeFilterKeys.length > 0 @@ -526,7 +525,9 @@ export function AgentTranscriptDialog({ const createdLabel = task.created_at ? formatRunTime(task.created_at) : null; const startedLabel = task.started_at ? formatRunTime(task.started_at) : null; const completedLabel = task.completed_at ? formatRunTime(task.completed_at) : null; + const hasTriggeredBy = !!task.attribution?.initiator; const hasRunDetails = + hasTriggeredBy || !!runtimeInfo || !!task.relative_work_dir || !!createdLabel || @@ -561,13 +562,10 @@ export function AgentTranscriptDialog({ {agentName || agentInfo?.name || ""} + {/* Trigger mechanism (why this run exists). The accountable + human is audit metadata, not read-time context — it lives in + the ⓘ popover as "triggered by", not on this row. */} {triggerLabel} - {/* Accountable member (MUL-4302 §9): whose behalf this run is on. */} -
@@ -585,6 +583,14 @@ export function AgentTranscriptDialog({ {t(($) => $.transcript.run_info)}
+ {hasTriggeredBy && ( +
+ + {t(($) => $.transcript.details_triggered_by)} + + +
+ )} {runtimeInfo && ( $.transcript.details_runtime)} value={runtimeInfo.name} /> )} @@ -645,12 +651,6 @@ export function AgentTranscriptDialog({ ? t(($) => $.transcript.events_filtered, { shown: filteredItems.length, total: items.length }) : t(($) => $.transcript.events, { count: items.length })} - {toolCount > 0 && ( - <> - - {t(($) => $.transcript.tool_calls, { count: toolCount })} - - )}
{items.length > 0 && ( @@ -837,44 +837,28 @@ interface SortDirectionToggleProps { labels: { chronological: string; newestFirst: string; ariaLabel: string }; } +// Sort is a two-state toggle, not a mode picker: one button showing the +// current direction that flips on click. Shares the toolbar button chassis so +// it reads as the same control family as density/filter/copy — no tab strip. function SortDirectionToggle({ value, onChange, labels }: SortDirectionToggleProps) { + const isChronological = value === "chronological"; return ( -
onChange(isChronological ? "newest_first" : "chronological")} aria-label={labels.ariaLabel} - className="inline-flex shrink-0 items-center rounded border bg-muted/40 p-0.5 text-xs" + title={isChronological ? labels.chronological : labels.newestFirst} + className="flex shrink-0 items-center gap-1 rounded px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground" > - - -
+ )} + + {isChronological ? labels.chronological : labels.newestFirst} + + ); } diff --git a/packages/views/common/task-transcript/trace-event-presenter.test.ts b/packages/views/common/task-transcript/trace-event-presenter.test.ts index ecc5ca0b2..21d17266e 100644 --- a/packages/views/common/task-transcript/trace-event-presenter.test.ts +++ b/packages/views/common/task-transcript/trace-event-presenter.test.ts @@ -55,12 +55,16 @@ describe("traceToolArgSummary", () => { }); describe("traceEventSummary", () => { - it("takes the first non-empty line for agent text and tool output", () => { + it("takes the first non-empty line for agent text", () => { expect(traceEventSummary({ type: "text", content: "\n\nFirst line\nrest" })).toBe( "First line", ); - expect(traceEventSummary({ type: "tool_result", output: "line one\nline two" })).toBe( - "line one", + }); + + it("collapses pretty-printed JSON output to a content preview, not a lone bracket", () => { + const output = '[\n {\n "id": "694c",\n "title": "x"\n }\n]'; + expect(traceEventSummary({ type: "tool_result", output })).toBe( + '[ { "id": "694c", "title": "x" } ]', ); }); diff --git a/packages/views/common/task-transcript/trace-event-presenter.ts b/packages/views/common/task-transcript/trace-event-presenter.ts index 99aa9b487..ce511b8e4 100644 --- a/packages/views/common/task-transcript/trace-event-presenter.ts +++ b/packages/views/common/task-transcript/trace-event-presenter.ts @@ -121,6 +121,15 @@ function firstLine(value: string | undefined): string { return value?.split("\n").find((l) => l.trim().length > 0) ?? ""; } +/** + * Collapse all whitespace runs to single spaces. Unlike firstLine this keeps + * content that spans lines, so a pretty-printed JSON result previews as + * `[ { "id": ... } ]` instead of a lone opening bracket. + */ +function collapseWhitespace(value: string | undefined): string { + return (value ?? "").replace(/\s+/g, " ").trim(); +} + /** One-line summary for the collapsed row — never contains a newline. */ export function traceEventSummary(event: TraceEvent): string { switch (traceEventKind(event)) { @@ -129,7 +138,7 @@ export function traceEventSummary(event: TraceEvent): string { case "tool_use": return traceToolArgSummary(event.input); case "tool_result": - return clip(firstLine(event.output), 200); + return clip(collapseWhitespace(event.output), 200); default: return firstLine(event.content ?? event.output); } diff --git a/packages/views/locales/en/agents.json b/packages/views/locales/en/agents.json index 820ed4bfb..2c10e22ba 100644 --- a/packages/views/locales/en/agents.json +++ b/packages/views/locales/en/agents.json @@ -808,7 +808,8 @@ "details_created": "Created", "details_started": "Started", "details_completed": "Completed", - "copy_workdir": "Copy working directory" + "copy_workdir": "Copy working directory", + "details_triggered_by": "Triggered by" }, "task_failure": { "agent_error": "Agent execution error", diff --git a/packages/views/locales/ja/agents.json b/packages/views/locales/ja/agents.json index 74219ce9a..deb1b8a52 100644 --- a/packages/views/locales/ja/agents.json +++ b/packages/views/locales/ja/agents.json @@ -688,7 +688,8 @@ "details_created": "作成", "details_started": "開始", "details_completed": "完了", - "copy_workdir": "作業ディレクトリをコピー" + "copy_workdir": "作業ディレクトリをコピー", + "details_triggered_by": "トリガー" }, "task_failure": { "agent_error": "エージェント実行エラー", diff --git a/packages/views/locales/ko/agents.json b/packages/views/locales/ko/agents.json index c1244527f..2967ab92d 100644 --- a/packages/views/locales/ko/agents.json +++ b/packages/views/locales/ko/agents.json @@ -696,7 +696,8 @@ "details_created": "생성", "details_started": "시작", "details_completed": "완료", - "copy_workdir": "작업 디렉터리 복사" + "copy_workdir": "작업 디렉터리 복사", + "details_triggered_by": "트리거한 사람" }, "task_failure": { "agent_error": "에이전트 실행 오류", diff --git a/packages/views/locales/zh-Hans/agents.json b/packages/views/locales/zh-Hans/agents.json index caaad72e1..8b9b4392a 100644 --- a/packages/views/locales/zh-Hans/agents.json +++ b/packages/views/locales/zh-Hans/agents.json @@ -792,7 +792,8 @@ "details_created": "创建", "details_started": "开始", "details_completed": "完成", - "copy_workdir": "复制工作目录" + "copy_workdir": "复制工作目录", + "details_triggered_by": "触发人" }, "task_failure": { "agent_error": "智能体执行出错",