DSH / Atlas
2026-07-23implementedfeaturearchived 2026-07-26

Trajectory step cell and turn list chrome

Trajectory 步骤单元格与轮次列表 chrome

The trajectory tab needs a reusable step row and turn-list chrome that can show expanded assistant blocks, own-duration times, Message token columns, and in-flight work. Without folding session event times into conversation nodes and expanding blocks into cells, the UI cannot match the product chrome.

English

Problem

The trajectory tab needs a reusable step row and turn-list chrome that can show expanded assistant blocks, own-duration times, Message token columns, and in-flight work. Without folding session event times into conversation nodes and expanding blocks into cells, the UI cannot match the product chrome.

Decision

@deepseek-ai/dsh-client-ui-trajectory owns the presentational trajectory list chrome:

  • TrajectoryCell — 38px step row with kinds User / Message / Tool (no Think, Call, or Result rows). Reasoning blocks are skipped (no block-level clock). Each tool-call + paired tool-result folds into one Tool row (name · truncated args) whose Time is result.time − callTime when both are known. Message rows carry Input/Output/Think token columns from assistant.usage. Own-duration Time uses +Ns / +N.1s, or when absent. Selected state draws a 2px inset --dsw-alias-brand-primary-new-colorprimary-new-color ring (selected prop) and is not wired to chat selection.
  • TrajectoryTurn / header / group header — sticky Turn bar paints full-bleed ghost-active-fill; title/columns and the Message/Step body sit in a centered max-width: 880px lane. Cell trailing columns share the Turn header geometry (320 = 4×71 + 3×12); cells use pad 20/8.
  • deriveTrajectoryLayout expands assistant blocks[] into cells, pairs tool-calls with tool-result by callId into Tool, folds partial and runningCalls (deduped), hangs usage on Message only (including the empty fallback when there is no text block), and builds group descriptions as wall-span + tool histogram (1.5s bash×6). user/message has no wire turn, so each User row is enclosed in the next assistant/steering turn, else the in-flight partial turn, else lastAssistantTurn + 1 (or 1). Context nodes emit no cell but still advance the Message duration cursor.

ConversationNode carries time from SessionEvent.time; ToolResultNode.callTime and RunningToolCall.time come from the paired tool/call. Duration rules: User +0s; Message = assistant.time − previous surface time (including skipped context); Tool = result.time − callTime when both known; in-flight Tool = . Group header duration is earliest→latest absolute time in the group (wall span; Tool contributes start and start+duration).

Alternatives considered

Keep a Think cell for reasoning blocks. Rejected: a single assistant/message.time cannot yield Think own-duration without chunk-level clocks; omit the row rather than show .

Keep separate Call and Result rows. Rejected: Result had no own duration to show; one Tool row carries the call→result interval.

Cumulative elapsed from session/turn start. Rejected; the Time column is each row's own duration.

Hang usage on the first expanded row. Rejected; usage attaches to Message only.

Show in-flight tool durations via Date.now(). Deferred; in-flight Time stays .

Consequences

The Trajectory tab can render expanded finalized and in-flight rows with own-duration times once fold emits time. Behavior-shaped coverage lives in packages/client/ui-trajectory/tests/{cell,layout,views}.spec.tsx. Chat selection deep-links and finer block-level clocks remain deferred.

中文

Problem

trajectory 标签页需要可复用的步骤行与轮次列表 chrome,以展示展开后的 assistant 块、自身耗时、Message token 列,以及进行中的工作。若不将会话事件时间折叠进会话节点,并将块展开为单元格,UI 就无法对齐产品 chrome。

Decision

@deepseek-ai/dsh-client-ui-trajectory 拥有展示型 trajectory 列表 chrome:

  • TrajectoryCell — 高 38px 的步骤行,类型为 User / Message / Tool(无 Think、Call、Result 行)。reasoning 块跳过(无块级时钟)。每对 tool-call + tool-result 折成一行 Tool(name · 加截断参数),Time 在两端皆知时为 result.time − callTime。Message 行携带来自 assistant.usage 的 Input/Output/Think token 列。自身耗时 Time 使用 +Ns / +N.1s,缺失时为 。选中态绘制 2px 内嵌的 --dsw-alias-brand-primary-new-colorprimary-new-color 环(selected prop),且未接线到 chat 选中。
  • TrajectoryTurn / header / group header — 粘性 Turn 条背景通栏铺 ghost-active-fill;标题/列标与 Message/Step 主体落在居中的 max-width: 880px 内容道。单元格右侧列与 Turn 标头共用几何(320 = 4×71 + 3×12);cell pad 20/8。
  • deriveTrajectoryLayout 将 assistant blocks[] 展开为单元格,按 callId 将 tool-call 与 tool-result 配对为 Tool,折叠 partialrunningCalls(去重),仅将用量挂在 Message 上(含无 text 块时的空回退行),并以墙钟跨度 + 工具直方图构建分组描述(1.5s bash×6)。user/message 无线上 turn,故每条 User 行归入下一 assistant/steering 的 turn,否则归入进行中的 partial turn,否则为 lastAssistantTurn + 1(或 1)。context 节点不产出单元格,但仍推进 Message 耗时游标。

ConversationNode 携带来自 SessionEvent.timetimeToolResultNode.callTimeRunningToolCall.time 来自配对的 tool/call。耗时规则:User 为 +0s;Message = assistant.time − 上一表面时间(含跳过的 context);Tool = 在两者皆知时 result.time − callTime;进行中 Tool = 。分组标头耗时为组内最早→最晚绝对时间(墙钟跨度;Tool 贡献起点与起点+自身耗时)。

Alternatives considered

为 reasoning 块保留 Think 单元格。 否决:单条 assistant/message.time 无法给出 Think 自身耗时(除非上 chunk 级时钟);与其显示 ,不如省略该行。

保留分开的 Call 与 Result 行。 否决:Result 没有可展示的自身耗时;一行 Tool 承载 call→result 区间。

自会话/轮次起点累计耗时。 否决;Time 列是每行自身的耗时。

将用量挂在展开后的第一行。 否决;用量仅附着于 Message。

用 Date.now() 显示进行中工具的耗时。 延后;进行中的 Time 保持为

Consequences

一旦 fold 发出 time,Trajectory 标签页即可渲染带自身耗时的已定稿与进行中展开行。行为导向的覆盖位于 packages/client/ui-trajectory/tests/{cell,layout,views}.spec.tsx。chat 选中深链与更细的块级时钟仍延后。