@deepseek-ai/dsh-client-ui-tool 0.2.0-rc.1 → 0.2.0-rc.2

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/README.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: 5332d87e76037f07
22
22
  zh: db5d06e7e46b8429
23
23
  /deepseek-ai-dsh-client-ui-tool/use-this-package/built-in-views:
24
- en: 53bd3369a3b868b0
25
- zh: 030f281a5bd66c6f
24
+ en: 5bdd3af9df9b2b7c
25
+ zh: ff3517e71a2be98a
26
26
  /deepseek-ai-dsh-client-ui-tool/understand-the-implementation:
27
27
  en: 00bc7b0c84edc603
28
28
  zh: 1cfcf5b435248a91
@@ -30,11 +30,14 @@
30
30
  en: 2959b10b01e08a7b
31
31
  zh: 7084ca22650cbb5b
32
32
  /deepseek-ai-dsh-client-ui-tool/understand-the-implementation/cards:
33
- en: 401a0118de2dba33
34
- zh: 9efe590dd485c336
33
+ en: 936ae18b0927971f
34
+ zh: 170fb021d250fb58
35
+ /deepseek-ai-dsh-client-ui-tool/understand-the-implementation/declared-optional-capabilities:
36
+ en: c1926deb4e5f3734
37
+ zh: c5845f465b94a459
35
38
  /deepseek-ai-dsh-client-ui-tool/further-exploration:
36
- en: 886059e6bd594ebf
37
- zh: 94cfe8d0a789f413
39
+ en: b37600769951cbca
40
+ zh: 816589549d21d176
38
41
  /deepseek-ai-dsh-client-ui-tool/model-experience:
39
42
  en: 1e5e72dd269855d5
40
43
  zh: 81a4756d460e3af9
package/README.md CHANGED
@@ -49,7 +49,11 @@ The owner payload is `ToolCallOwnerProps`: `callId`, `toolName`, the `phase` dis
49
49
 
50
50
  Every registered view receives the explicit `preparing`, `start`, and `result` props declared in [the Tool slot types](src/client/contract/slots.ts). Shared rows use the same `ToolRow` in all stages. Their row model selects the title and combines any generic tool-name prefix with the available argument summary independently of lifecycle state. Tool-owned titles omit the English name. The shared argument parser returns no call during preparation and does not parse partial JSON. Write/edit use separate preparing and dispatched components, so only the preparing component invokes `useToolCallArgumentsPartial`; start and result share the dispatched component. Custom renderers such as Bash, Skill, and Cordis handle preparation separately; their argument-dependent components accept `StartedToolCallViewProps`.
51
51
 
52
- This package owns the generic fallback and the built-in shell/pwsh, read, read_image, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and PTC dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Running atomic rows share one left-to-right highlight across their titles, separators, summaries, and suffixes, while settled rows stay static. Running and settled foreground standard `bash`/`pwsh` and `terminal_send` calls use terminal cards at the root and in PTC dispatch children, subject to the same argument, result, and error checks. Persistent `bash`/`pwsh` calls use terminal cards only while running. Shell output ending in a recognized spill-policy notice uses expandable generic output in shell rows and generic output in Details; a displaced or omitted exit marker cannot establish success. Settled persistent-shell results stay generic because reset and partial-output diagnostics do not always describe one process exit status; root persistent results are expandable, while background acknowledgements remain collapsed. A native or PTC dispatch failure carrying `AUTO_REVIEW_DENIED` shows the Auto review verdict in its collapsed row and one normalized not-executed reason when expanded; a missing or whitespace-only reason uses localized fallback text. A successful question row pairs call questions with result answers by their stable ids and shows readable question/answer lines when expanded. A cancelled or interrupted row shows its verdict and original questions without inventing answers. Unsupported, malformed, or ambiguous inputs fall back to flattened Tool input/result text. `ui-skill` demonstrates a business-owned registration for `skill`.
52
+ This package owns the generic fallback and the built-in shell/pwsh, read, read_image, write/edit, running `str_replace_editor` `create`/`str_replace`, grep/glob, web, todo, question, and PTC dispatch presentations. Structured cards derive directly from first-party raw event fields; Host `presentCall` and `presentResult` values never enter the Client. Running atomic rows share one left-to-right highlight across their titles, separators, summaries, and suffixes, while settled rows stay static. Running and settled foreground standard `bash`/`pwsh` and `terminal_send` calls use terminal cards at the root and in PTC dispatch children, subject to the same argument, result, and error checks. Persistent `bash`/`pwsh` calls use terminal cards only while running. Shell output ending in a recognized spill-policy notice uses expandable generic output in shell rows and generic output in Details; a displaced or omitted exit marker cannot establish success. Settled persistent-shell results stay generic because reset and partial-output diagnostics do not always describe one process exit status; root persistent results are expandable, while background acknowledgements remain collapsed. A native or PTC dispatch failure carrying `AUTO_REVIEW_DENIED` shows the Auto review verdict in its collapsed row and one normalized not-executed reason when expanded; a missing or whitespace-only reason uses localized fallback text. `ui-skill` demonstrates a business-owned registration for `skill`.
53
+
54
+ Question rows pair answers with questions by id, using the tool result or the `userQuestions` projection for late replies. An answerable call has a button to reopen its panel. Once a late reply is in the agent Inbox, the row shows its submitted answers; “View answers” expands its read-only transcript while admission is pending. A settled call with answers opens the panel read-only. Closed calls without answers and invalid records offer no button. Cancelled and interrupted rows show their verdict without inventing answers. If no panel provider accepts the button action, it expands the row. Unsupported or ambiguous input falls back to flattened Tool text.
55
+
56
+ Answer-panel buttons belong to the question renderer. It composes `DisclosureRow` with its own action and transcript; generic `ToolRow` does not select or dispatch question actions.
53
57
 
54
58
  -----
55
59
 
@@ -75,7 +79,7 @@ The slot-injected `useToolCallArgumentsPartial` Hook lazily subscribes to the ow
75
79
 
76
80
  Every card is read in place in the call tree; there is no second, full-height presentation of a selected call. Row renderers share one pure card model for each terminal, read, diff, search, and web card, and the image card's gallery renders through the tool-owned `tool.call.images` slot. These models validate raw call arguments, result content, failure state, persisted metadata, PTC dispatch `parentCallId`, and Session path facts. Unsupported or malformed inputs use flattened Tool result text. A file-path summary opens the file through the owner's `openFile`, which the chat view routes to the right Sidebar's text preview; `inspect` opens the trajectory view and is absent when that View is unavailable; cards then omit Inspect. Card-specific limits and fallback rules for the terminal, diff, read, search, and web cards remain in [the ui-primitives README](../ui-primitives/README.md); the image card's model in this package carries its own fallback rules.
77
81
 
78
- Chat diff cards keep nine rows before folding, enough for a file header, one removed/added pair, and three context lines on either side. The tool row shows the primitive's exact or coarse-replacement counts; the expanded card contains the diff body without a totals footer.
82
+ Chat diff cards keep nine rows before folding, enough for a file header, one removed/added pair, and three context lines on either side. The tool row shows the primitive's exact or coarse-replacement counts; the expanded card contains the diff body without a totals footer. Hovering the tool header colors its added count green and removed count red; both retain those colors while the card is expanded.
79
83
 
80
84
  An Auto denial takes precedence over keyed specialized views. Its generic row preserves the call identity, omits raw arguments, and normalizes the stored reason only for display: trim surrounding whitespace and collapse line separators to spaces, with localized fallback for an empty result. Session and SDK error details keep the original reason.
81
85
 
@@ -84,6 +88,11 @@ Recorded tool details cover goal and schedule tools, Cordis inspection, workflow
84
88
  Expanded status dots and labels use static semantic colors. Receipt and job-output headers keep neutral text and omit the status while expanded. An interruption receipt confirms only that interruption was requested.
85
89
 
86
90
  The terminal model uses `hasSpillNotice` from the browser-safe `@deepseek-ai/dsh-spill-policy/notice` entry, not an independent UI pattern. The [spill-policy README](../../spill/spill-policy/README.md#shared-notice-ownership) owns notice formatting and recognition. This check conservatively selects generic output; matching text cannot authenticate its source, and replay leaves recorded result bytes untouched.
91
+
92
+ ### Declared optional capabilities
93
+
94
+ This package declares the optional `UserQuestionPanels` capability; `ui-user-questions` provides it. `reveal(sessionId, callId)` opens an answerable panel, and `review(sessionId, callId, record)` opens recorded answers read-only. The row reads the capability through `ctx.get('userQuestionPanels')` and keeps its transcript when no question UI is composed.
95
+
87
96
  </details>
88
97
 
89
98
  -----
@@ -96,6 +105,7 @@ These pages cover the conversation host, the view slots, and the card models.
96
105
  - [ui-conversation](../ui-conversation/README.md) — the chat surface dispatching `tool-call` nodes to this package.
97
106
  - [ui-primitives](../ui-primitives/README.md) — the output card atoms the built-in views compose.
98
107
  - [ui-skill](../ui-skill/README.md) — a business-owned registration for the `skill` tool.
108
+ - [ui-user-questions](../ui-user-questions/README.md) — the question panels the `ask_user_question` row reopens.
99
109
  - [Auto review](../../experimental/auto-review/README.md) — the structured denial identity and user-visible reason owner.
100
110
  - [Conversation subsystem](../../../docs/subsystems/conversation.md) — how a business-owned feature registers a Conversation node.
101
111
  - [Slot system standard](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) — the composition model behind the keyed slot.
package/README.zh.md CHANGED
@@ -49,7 +49,11 @@ owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、`phase` 判别字
49
49
 
50
50
  每个注册视图都接收[工具 slot 类型](src/client/contract/slots.ts)声明的显式 `preparing`、`start` 和 `result` props。通用行在三个阶段使用同一个 `ToolRow`。行模型统一选择标题,并组合通用工具名前缀与已有参数摘要,不按生命周期阶段改变前缀;专用标题不附带英文名。准备阶段的共享参数解析入口直接返回无调用,不解析部分 JSON。write/edit 将准备态和派发后阶段拆成两个组件,只有准备态组件调用 `useToolCallArgumentsPartial`,start 与 result 共用派发后组件。Bash、Skill、Cordis 等自定义 renderer 分别处理准备态,其依赖参数的组件接收 `StartedToolCallViewProps`。
51
51
 
52
- 本包拥有 generic fallback,以及 shell/pwsh、read、read_image、write/edit、运行中的 `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 PTC dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。运行中的原子工具行以同一道高光从左到右扫过标题、分隔符、摘要与后缀;已完成行保持静态。运行中与已完成的前台标准 `bash`/`pwsh` 和 `terminal_send` 调用,无论位于根还是 PTC dispatch 子调用中,都在通过相同的参数、结果和错误检查后使用 terminal 卡片。持久 `bash`/`pwsh` 调用仅在运行中使用 terminal 卡片。以已识别的 spill 策略提示结尾的 shell 输出,在 shell 行中使用可展开的 generic 输出,在 Details 中使用 generic 输出;位置被改变或被省略的退出标记无法证明成功。已完成的持久 shell 结果保持 generic 展示,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;根调用的持久 shell 结果可展开,后台启动回执则保持折叠。带有 `AUTO_REVIEW_DENIED` 的原生或 PTC dispatch 失败会在折叠行显示 Auto review 裁决,展开时显示一行归一化后的“未执行”原因;原因缺失或只有空白时使用本地化 fallback 文案。成功的问题行按稳定 id 配对调用中的问题与结果中的回答,展开后显示可读的问答行。已取消或已中断的问题行显示其裁决与原始问题,不虚构回答。不受支持、格式错误或含糊的输入回退为压平的工具输入/结果文本。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。
52
+ 本包拥有 generic fallback,以及 shell/pwsh、read、read_image、write/edit、运行中的 `str_replace_editor` `create`/`str_replace`、grep/glob、web、todo、question 与 PTC dispatch 的内置展示。结构化卡片直接从第一方原始 event 字段派生;Host `presentCall` 与 `presentResult` 值不会进入 Client。运行中的原子工具行以同一道高光从左到右扫过标题、分隔符、摘要与后缀;已完成行保持静态。运行中与已完成的前台标准 `bash`/`pwsh` 和 `terminal_send` 调用,无论位于根还是 PTC dispatch 子调用中,都在通过相同的参数、结果和错误检查后使用 terminal 卡片。持久 `bash`/`pwsh` 调用仅在运行中使用 terminal 卡片。以已识别的 spill 策略提示结尾的 shell 输出,在 shell 行中使用可展开的 generic 输出,在 Details 中使用 generic 输出;位置被改变或被省略的退出标记无法证明成功。已完成的持久 shell 结果保持 generic 展示,因为 reset 与部分输出诊断不一定描述单个进程的退出状态;根调用的持久 shell 结果可展开,后台启动回执则保持折叠。带有 `AUTO_REVIEW_DENIED` 的原生或 PTC dispatch 失败会在折叠行显示 Auto review 裁决,展开时显示一行归一化后的“未执行”原因;原因缺失或只有空白时使用本地化 fallback 文案。`ui-skill` 展示了业务包自行拥有的 `skill` 注册项。
53
+
54
+ 问题行按 id 配对问题与答案;答案来自工具结果,迟到回答则来自 `userQuestions` 投影。可回答的调用提供按钮以重新打开面板。迟到回复进入 agent Inbox 后,问题行显示已提交的答案;等待准入期间,「查看回答」展开只读的行内记录。带有答案的已结算调用则以只读方式打开面板。没有答案的已关闭调用和无效记录不提供按钮。已取消和已中断的行显示裁决,不虚构答案。若面板提供方不接受按钮操作,点击会展开该行。不受支持或含糊的输入回退为压平的工具文本。
55
+
56
+ 回答面板按钮归问题 renderer 所有。它使用 `DisclosureRow` 组合自己的操作与 transcript;通用 `ToolRow` 不选择或派发问题操作。
53
57
 
54
58
  -----
55
59
 
@@ -75,7 +79,7 @@ slot 注入的 `useToolCallArgumentsPartial` 钩子按需订阅所属 Step 的 `
75
79
 
76
80
  每张卡片都直接在调用树中查看;选中调用后不会再显示第二个全高视图。行 renderer 为 terminal、read、diff、search 和 web 卡片各复用同一个纯 card model,image 卡片的图库经由工具自有 `tool.call.images` slot 渲染。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata、PTC dispatch 的 `parentCallId` 与会话路径信息。不受支持或格式错误的输入使用压平的工具结果文本。文件路径摘要经属主的 `openFile` 打开文件,chat 视图把它路由到右侧 Sidebar 的文本预览;`inspect` 打开轨迹视图;该视图不可用时不提供此回调,卡片随之隐藏 Inspect。terminal、diff、read、search 与 web 卡片的上限与 fallback 规则仍由 [ui-primitives README](../ui-primitives/README.zh.md) 负责;image 卡片的 fallback 规则由本包内的 card model 自行承载。
77
81
 
78
- Chat diff 卡片在折叠前保留九行,足以容纳文件标题、一对删除与新增行及两侧各三行上下文。工具行显示原语提供的精确或粗粒度替换统计;展开卡片包含差异正文,不显示底部统计。
82
+ Chat diff 卡片在折叠前保留九行,足以容纳文件标题、一对删除与新增行及两侧各三行上下文。工具行显示原语提供的精确或粗粒度替换统计;展开卡片包含差异正文,不显示底部统计。悬停工具标题行时,新增数量显示绿色、删除数量显示红色;卡片展开期间保留这两种颜色。
79
83
 
80
84
  Auto 拒绝优先于按工具名选择的专门视图。其通用行保留调用身份、省略原始参数,并且只在显示时归一化存储的理由:去除首尾空白,把行分隔符折叠为空格,结果为空时使用本地化通用理由。Session 与 SDK 错误详情保留原始理由。
81
85
 
@@ -84,6 +88,11 @@ Auto 拒绝优先于按工具名选择的专门视图。其通用行保留调用
84
88
  展开后的状态圆点和文字使用静态语义色。操作回执和任务输出的标题保持中性色,展开时省略标题中的状态。中断回执仅确认已发出中断请求。
85
89
 
86
90
  terminal model 使用浏览器安全入口 `@deepseek-ai/dsh-spill-policy/notice` 的 `hasSpillNotice`,而非独立的 UI 匹配规则。[spill-policy README](../../spill/spill-policy/README.zh.md#shared-notice-ownership) 负责提示文本的格式化与识别。该检查保守地选择通用输出;匹配的文本无法证明其来源,回放也不改变已记录的结果字节。
91
+
92
+ ### 声明的可选能力
93
+
94
+ 本包声明可选的 `UserQuestionPanels` 能力;`ui-user-questions` 提供它。`reveal(sessionId, callId)` 打开可回答的面板,`review(sessionId, callId, record)` 以只读方式打开已记录的答案。行通过 `ctx.get('userQuestionPanels')` 读取此能力;未组合问题 UI 时仍保留文本记录。
95
+
87
96
  </details>
88
97
 
89
98
  -----
@@ -96,6 +105,7 @@ terminal model 使用浏览器安全入口 `@deepseek-ai/dsh-spill-policy/notice
96
105
  - [ui-conversation](../ui-conversation/README.zh.md)——把 `tool-call` 节点分派给本包的聊天界面。
97
106
  - [ui-primitives](../ui-primitives/README.zh.md)——内置视图所拼装的输出卡片原子组件。
98
107
  - [ui-skill](../ui-skill/README.zh.md)——`skill` 工具的业务自有注册。
108
+ - [ui-user-questions](../ui-user-questions/README.zh.md)——`ask_user_question` 行重新打开的提问面板。
99
109
  - [Auto review](../../experimental/auto-review/README.zh.md)——结构化拒绝身份与用户可见原因的 owner。
100
110
  - [Conversation 子系统](../../../docs/subsystems/conversation.zh.md)——业务自有功能如何注册 Conversation node。
101
111
  - [slot 系统标准](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.zh.md)——keyed slot 背后的组合模型。
package/lib/client.js CHANGED
@@ -1194,13 +1194,13 @@ window.__ModuleLoader__.load({
1194
1194
  }
1195
1195
  //#endregion
1196
1196
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
1197
- const css$4 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-base);flex-direction:column;gap:16px;max-height:360px;margin:4px 0 4px 4px;padding:16px 20px;display:flex;overflow-y:auto}.fsXYAq_item{flex-direction:column;gap:2px;min-width:0;display:flex}.fsXYAq_question,.fsXYAq_answer{white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_question{color:var(--dsw-alias-label-tertiary)}.fsXYAq_answer{color:var(--dsw-alias-label-primary)}.fsXYAq_answerLine{display:block}.fsXYAq_skipped{color:var(--dsw-alias-label-tertiary)}.fsXYAq_verdict{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_questionList{flex-direction:column;gap:8px;margin:0;padding-left:20px;display:flex}.fsXYAq_unansweredQuestion{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}";
1198
- const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1199
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
1197
+ const css$5 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-base);flex-direction:column;gap:16px;max-height:360px;margin:4px 0 4px 4px;padding:16px 20px;display:flex;overflow-y:auto}.fsXYAq_item{flex-direction:column;gap:2px;min-width:0;display:flex}.fsXYAq_question,.fsXYAq_answer{white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_question{color:var(--dsw-alias-label-tertiary)}.fsXYAq_answer{color:var(--dsw-alias-label-primary)}.fsXYAq_answerLine{display:block}.fsXYAq_skipped{color:var(--dsw-alias-label-tertiary)}.fsXYAq_verdict{color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));margin:0}.fsXYAq_questionList{flex-direction:column;gap:8px;margin:0;padding-left:20px;display:flex}.fsXYAq_unansweredQuestion{color:var(--dsw-alias-label-tertiary);white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px))}";
1198
+ const tagId$5 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1199
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
1200
1200
  const tag = document.createElement("style");
1201
1201
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1202
- tag.dataset.pluginCss = tagId$4;
1203
- tag.textContent = css$4;
1202
+ tag.dataset.pluginCss = tagId$5;
1203
+ tag.textContent = css$5;
1204
1204
  document.head.appendChild(tag);
1205
1205
  }
1206
1206
  var AskQuestionCard_module_css_default = {
@@ -1257,13 +1257,13 @@ window.__ModuleLoader__.load({
1257
1257
  }
1258
1258
  //#endregion
1259
1259
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/ToolDetails.module.css.mjs
1260
- const css$3 = ".DXqwVW_root{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);margin:4px 0 4px 4px;overflow:hidden}.DXqwVW_list{max-height:320px;margin:0;padding:0;list-style:none;overflow-y:auto}.DXqwVW_item,.DXqwVW_empty{margin:0;padding:10px 14px}.DXqwVW_item+.DXqwVW_item{border-top:.5px solid var(--dsw-alias-border-l2)}.DXqwVW_root[data-inspect]:not([data-caption])>.DXqwVW_list>.DXqwVW_item,.DXqwVW_root[data-inspect]:not([data-caption]) .DXqwVW_empty,.DXqwVW_root[data-inspect] .DXqwVW_caption{padding-inline-end:88px}.DXqwVW_caption{color:var(--dsw-alias-label-caption);padding:10px 14px 6px;font-size:12px}.DXqwVW_heading{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.DXqwVW_text{white-space:pre-wrap;overflow-wrap:anywhere;flex:1;min-width:0}.DXqwVW_status{flex:none;justify-content:center;align-self:flex-start;align-items:center;width:14px;height:18px;font-size:16px;line-height:18px;display:inline-flex}.DXqwVW_pending{box-sizing:border-box;border:1px solid var(--dsw-alias-label-tertiary);border-radius:2px;width:10px;height:10px}.DXqwVW_heading:has(.DXqwVW_statusText) .DXqwVW_text{flex-basis:12em}.DXqwVW_statusText{color:var(--dsw-alias-label-caption);white-space:nowrap;margin-inline-start:auto;font-size:12px}.DXqwVW_previous,.DXqwVW_unchanged{color:var(--dsw-alias-label-tertiary)}.DXqwVW_item[data-change=added] .DXqwVW_status{color:var(--dsw-alias-state-success-primary)}.DXqwVW_item[data-change=removed] .DXqwVW_status{color:var(--dsw-alias-state-error-primary)}.DXqwVW_item[data-change=removed] .DXqwVW_text{color:var(--dsw-alias-label-tertiary);text-decoration:line-through}.DXqwVW_unchanged{border-top:.5px solid var(--dsw-alias-border-l2)}.DXqwVW_unchanged summary{cursor:pointer;align-items:center;gap:6px;padding:9px 14px;list-style:none;display:flex}.DXqwVW_unchanged summary::-webkit-details-marker{display:none}.DXqwVW_unchanged[open] summary svg{transform:rotate(90deg)}.DXqwVW_fields{margin:0}.DXqwVW_heading+.DXqwVW_fields{margin-top:6px}.DXqwVW_field{grid-template-columns:5.5em minmax(0,1fr);gap:12px;display:grid}.DXqwVW_field+.DXqwVW_field{margin-top:4px}.DXqwVW_field dt{color:var(--dsw-alias-label-caption)}.DXqwVW_field dd{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.DXqwVW_badge{color:var(--dsw-alias-label-caption);flex:none;align-items:center;gap:5px;font-size:12px;display:inline-flex}.DXqwVW_badge:before{content:\"\";corner-shape:round;background:currentColor;border-radius:50%;width:5px;height:5px}.DXqwVW_badge[data-tone=info]{color:var(--dsw-alias-state-business-primary)}.DXqwVW_badge[data-tone=success]{color:var(--dsw-alias-state-success-primary)}.DXqwVW_badge[data-tone=warning]{color:var(--dsw-alias-state-warn-primary)}.DXqwVW_badge[data-tone=error]{color:var(--dsw-alias-state-error-primary)}.DXqwVW_subtitle{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;margin-top:3px;font-size:12px}.DXqwVW_description{color:var(--dsw-alias-label-caption);white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0}.DXqwVW_lines{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;padding-inline-start:18px}.DXqwVW_lines li+li{margin-top:4px}.DXqwVW_group{border-top:.5px solid var(--dsw-alias-border-l2);margin-top:8px}.DXqwVW_group>summary{color:var(--dsw-alias-label-caption);cursor:pointer;overflow-wrap:anywhere;align-items:center;gap:6px;padding:8px 0 0;list-style:none;display:flex}.DXqwVW_group>summary::-webkit-details-marker{display:none}.DXqwVW_group[open]>summary svg{transform:rotate(90deg)}.DXqwVW_group>summary svg{flex:none}.DXqwVW_group .DXqwVW_list{max-height:none;overflow:visible}.DXqwVW_group .DXqwVW_item{padding:9px 0 2px 20px}.DXqwVW_prose,.DXqwVW_code{max-width:100%;font-size:13px}.DXqwVW_prose{margin-top:8px}.DXqwVW_root .DXqwVW_item>.DXqwVW_code{max-height:240px;margin:8px 0 0;overflow:auto}.DXqwVW_root .DXqwVW_item:has(>.DXqwVW_code:only-child){padding:0}.DXqwVW_root .DXqwVW_item>.DXqwVW_code:only-child{margin:0}.DXqwVW_root .DXqwVW_group .DXqwVW_item:has(>.DXqwVW_code:only-child){padding:4px 0 0 20px}.DXqwVW_path{min-width:0;color:inherit;font:inherit;text-align:start;overflow-wrap:anywhere;cursor:pointer;background:0 0;border:none;flex:1;padding:0}.DXqwVW_path:hover{text-decoration:underline}.DXqwVW_path:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:3px}";
1261
- const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolDetails.module.css";
1262
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1260
+ const css$4 = ".DXqwVW_root{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);margin:4px 0 4px 4px;overflow:hidden}.DXqwVW_list{max-height:320px;margin:0;padding:0;list-style:none;overflow-y:auto}.DXqwVW_item,.DXqwVW_empty{margin:0;padding:10px 14px}.DXqwVW_item+.DXqwVW_item{border-top:.5px solid var(--dsw-alias-border-l2)}.DXqwVW_root[data-inspect]:not([data-caption])>.DXqwVW_list>.DXqwVW_item,.DXqwVW_root[data-inspect]:not([data-caption]) .DXqwVW_empty,.DXqwVW_root[data-inspect] .DXqwVW_caption{padding-inline-end:88px}.DXqwVW_caption{color:var(--dsw-alias-label-caption);padding:10px 14px 6px;font-size:12px}.DXqwVW_heading{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.DXqwVW_text{white-space:pre-wrap;overflow-wrap:anywhere;flex:1;min-width:0}.DXqwVW_status{flex:none;justify-content:center;align-self:flex-start;align-items:center;width:14px;height:18px;font-size:16px;line-height:18px;display:inline-flex}.DXqwVW_pending{box-sizing:border-box;border:1px solid var(--dsw-alias-label-tertiary);border-radius:2px;width:10px;height:10px}.DXqwVW_heading:has(.DXqwVW_statusText) .DXqwVW_text{flex-basis:12em}.DXqwVW_statusText{color:var(--dsw-alias-label-caption);white-space:nowrap;margin-inline-start:auto;font-size:12px}.DXqwVW_previous,.DXqwVW_unchanged{color:var(--dsw-alias-label-tertiary)}.DXqwVW_item[data-change=added] .DXqwVW_status{color:var(--dsw-alias-state-success-primary)}.DXqwVW_item[data-change=removed] .DXqwVW_status{color:var(--dsw-alias-state-error-primary)}.DXqwVW_item[data-change=removed] .DXqwVW_text{color:var(--dsw-alias-label-tertiary);text-decoration:line-through}.DXqwVW_unchanged{border-top:.5px solid var(--dsw-alias-border-l2)}.DXqwVW_unchanged summary{cursor:pointer;align-items:center;gap:6px;padding:9px 14px;list-style:none;display:flex}.DXqwVW_unchanged summary::-webkit-details-marker{display:none}.DXqwVW_unchanged[open] summary svg{transform:rotate(90deg)}.DXqwVW_fields{margin:0}.DXqwVW_heading+.DXqwVW_fields{margin-top:6px}.DXqwVW_field{grid-template-columns:5.5em minmax(0,1fr);gap:12px;display:grid}.DXqwVW_field+.DXqwVW_field{margin-top:4px}.DXqwVW_field dt{color:var(--dsw-alias-label-caption)}.DXqwVW_field dd{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}.DXqwVW_badge{color:var(--dsw-alias-label-caption);flex:none;align-items:center;gap:5px;font-size:12px;display:inline-flex}.DXqwVW_badge:before{content:\"\";corner-shape:round;background:currentColor;border-radius:50%;width:5px;height:5px}.DXqwVW_badge[data-tone=info]{color:var(--dsw-alias-state-business-primary)}.DXqwVW_badge[data-tone=success]{color:var(--dsw-alias-state-success-primary)}.DXqwVW_badge[data-tone=warning]{color:var(--dsw-alias-state-warn-primary)}.DXqwVW_badge[data-tone=error]{color:var(--dsw-alias-state-error-primary)}.DXqwVW_subtitle{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;margin-top:3px;font-size:12px}.DXqwVW_description{color:var(--dsw-alias-label-caption);white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0}.DXqwVW_lines{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;padding-inline-start:18px}.DXqwVW_lines li+li{margin-top:4px}.DXqwVW_group{border-top:.5px solid var(--dsw-alias-border-l2);margin-top:8px}.DXqwVW_group>summary{color:var(--dsw-alias-label-caption);cursor:pointer;overflow-wrap:anywhere;align-items:center;gap:6px;padding:8px 0 0;list-style:none;display:flex}.DXqwVW_group>summary::-webkit-details-marker{display:none}.DXqwVW_group[open]>summary svg{transform:rotate(90deg)}.DXqwVW_group>summary svg{flex:none}.DXqwVW_group .DXqwVW_list{max-height:none;overflow:visible}.DXqwVW_group .DXqwVW_item{padding:9px 0 2px 20px}.DXqwVW_prose,.DXqwVW_code{max-width:100%;font-size:13px}.DXqwVW_prose{margin-top:8px}.DXqwVW_root .DXqwVW_item>.DXqwVW_code{max-height:240px;margin:8px 0 0;overflow:auto}.DXqwVW_root .DXqwVW_item:has(>.DXqwVW_code:only-child){padding:0}.DXqwVW_root .DXqwVW_item>.DXqwVW_code:only-child{margin:0}.DXqwVW_root .DXqwVW_group .DXqwVW_item:has(>.DXqwVW_code:only-child){padding:4px 0 0 20px}.DXqwVW_path{min-width:0;color:inherit;font:inherit;text-align:start;overflow-wrap:anywhere;cursor:pointer;background:0 0;border:none;flex:1;padding:0}.DXqwVW_path:hover{text-decoration:underline}.DXqwVW_path:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:3px}";
1261
+ const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/ToolDetails.module.css";
1262
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
1263
1263
  const tag = document.createElement("style");
1264
1264
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1265
- tag.dataset.pluginCss = tagId$3;
1266
- tag.textContent = css$3;
1265
+ tag.dataset.pluginCss = tagId$4;
1266
+ tag.textContent = css$4;
1267
1267
  document.head.appendChild(tag);
1268
1268
  }
1269
1269
  var ToolDetails_module_css_default = {
@@ -1431,22 +1431,23 @@ window.__ModuleLoader__.load({
1431
1431
  }
1432
1432
  //#endregion
1433
1433
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
1434
- const css$2 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_leading{flex-shrink:0}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_leading,.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{color:var(--dsw-alias-state-business-primary)}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{font-weight:500}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_sep{background:var(--dsw-alias-state-business-primary)}.o3BgMG_chevron{color:var(--dsw-alias-label-secondary)}.o3BgMG_title{font-weight:400;transition:color .1s}.o3BgMG_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.o3BgMG_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:auto;transition:color .1s;overflow:hidden}.o3BgMG_summarySuffix{white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:none;margin-left:4px;transition:color .1s}.o3BgMG_row:hover .o3BgMG_title,.o3BgMG_row:hover .o3BgMG_summary:not(.o3BgMG_errorSummary):not(.o3BgMG_stoppedSummary),.o3BgMG_row:hover .o3BgMG_summarySuffix{color:var(--dsw-alias-label-primary)}.o3BgMG_diffStat{font-family:var(--ds-font-family-code);font-size:calc(var(--dsh-content-font-size-secondary,13px) - 2px);color:var(--dsw-alias-label-caption);margin-left:10px;transform:translateY(.5px)}.o3BgMG_fileLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:inherit;text-align:left;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);text-decoration:underline dotted;text-decoration-color:var(--dsw-alias-label-tertiary);text-underline-offset:3px;cursor:pointer;background:0 0;border:none;flex:0 auto;margin:0;padding:0;text-decoration-thickness:1px;transition:color .1s;overflow:hidden}.o3BgMG_row:hover .o3BgMG_fileLink,.o3BgMG_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.o3BgMG_stoppedSummary{color:var(--dsw-alias-state-warn-label)}.o3BgMG_bodyWrap{flex-direction:column;display:flex}.o3BgMG_inspectButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.o3BgMG_root:hover .o3BgMG_inspectButton,.o3BgMG_inspectButton:focus-visible{opacity:1}.o3BgMG_detailsBodyWrap{position:relative}.o3BgMG_detailsBodyWrap .o3BgMG_inspectButton{margin:0;position:absolute;top:12px;right:12px}.o3BgMG_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.o3BgMG_bodyScroll{max-height:260px;overflow-y:auto}.o3BgMG_ioCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);flex-direction:column;margin:4px 0 4px 4px;display:flex}.o3BgMG_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.o3BgMG_ioSection::-webkit-scrollbar-thumb{border-radius:var(--dsw-radius-sm);background-clip:padding-box;border:2px solid #0000}.o3BgMG_ioSection::-webkit-scrollbar-track{margin:6px 0}.o3BgMG_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.o3BgMG_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.o3BgMG_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.o3BgMG_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.o3BgMG_codeBody,.o3BgMG_terminalBody,.o3BgMG_diffBody,.o3BgMG_readBody,.o3BgMG_imageBody,.o3BgMG_searchBody,.o3BgMG_webBody{margin:4px 0 4px 4px}.o3BgMG_searchRecovery{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary);margin:4px 0 4px 4px}.o3BgMG_imageLabel{overflow-wrap:anywhere;font:var(--dsw-font-sm-13);color:var(--dsw-alias-label-secondary);margin-bottom:4px}.o3BgMG_imageMeta{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary)}.o3BgMG_terminalBody{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1)}.o3BgMG_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1435
- const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1436
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1434
+ const css$3 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_leading{flex-shrink:0}.o3BgMG_root[data-tool^=cordis_] .o3BgMG_title{font-weight:500}.o3BgMG_title{font-weight:400}.o3BgMG_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.o3BgMG_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));flex:auto;overflow:hidden}.o3BgMG_summarySuffix{white-space:nowrap;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));flex:none;margin-left:4px}.o3BgMG_diffStat{font-family:var(--ds-font-family-code);font-size:calc(var(--dsh-content-font-size-secondary,13px) - 2px);margin-left:10px;transform:translateY(.5px)}.o3BgMG_row:is(:hover,[aria-expanded=true]) .o3BgMG_diffAdded{color:var(--dsw-alias-state-success-primary)}.o3BgMG_row:is(:hover,[aria-expanded=true]) .o3BgMG_diffRemoved{color:var(--dsw-alias-state-error-primary)}.o3BgMG_fileLink{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:inherit;text-align:left;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:inherit;text-underline-offset:3px;cursor:pointer;background:0 0;border:none;flex:0 auto;margin:0;padding:0;text-decoration:underline 1px dotted;overflow:hidden}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.o3BgMG_stoppedSummary{color:var(--dsw-alias-state-warn-label)}.o3BgMG_bodyWrap{flex-direction:column;display:flex}.o3BgMG_inspectButton{border:.5px solid var(--dsw-alias-border-l3);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.o3BgMG_root:hover .o3BgMG_inspectButton,.o3BgMG_inspectButton:focus-visible{opacity:1}.o3BgMG_detailsBodyWrap{position:relative}.o3BgMG_detailsBodyWrap .o3BgMG_inspectButton{margin:0;position:absolute;top:12px;right:12px}.o3BgMG_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.o3BgMG_bodyScroll{max-height:260px;overflow-y:auto}.o3BgMG_ioCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);flex-direction:column;margin:4px 0 4px 4px;display:flex}.o3BgMG_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.o3BgMG_ioSection::-webkit-scrollbar-thumb{border-radius:var(--dsw-radius-sm);background-clip:padding-box;border:2px solid #0000}.o3BgMG_ioSection::-webkit-scrollbar-track{margin:6px 0}.o3BgMG_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.o3BgMG_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.o3BgMG_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.o3BgMG_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.o3BgMG_codeBody,.o3BgMG_terminalBody,.o3BgMG_diffBody,.o3BgMG_readBody,.o3BgMG_imageBody,.o3BgMG_searchBody,.o3BgMG_webBody{margin:4px 0 4px 4px}.o3BgMG_searchRecovery{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary);margin:4px 0 4px 4px}.o3BgMG_imageLabel{overflow-wrap:anywhere;font:var(--dsw-font-sm-13);color:var(--dsw-alias-label-secondary);margin-bottom:4px}.o3BgMG_imageMeta{white-space:pre-wrap;overflow-wrap:anywhere;font:var(--dsw-font-xs-13);color:var(--dsw-alias-label-tertiary)}.o3BgMG_terminalBody{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1)}.o3BgMG_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1435
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1436
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1437
1437
  const tag = document.createElement("style");
1438
1438
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1439
- tag.dataset.pluginCss = tagId$2;
1440
- tag.textContent = css$2;
1439
+ tag.dataset.pluginCss = tagId$3;
1440
+ tag.textContent = css$3;
1441
1441
  document.head.appendChild(tag);
1442
1442
  }
1443
1443
  var ToolRow_module_css_default = {
1444
1444
  "bodyScroll": "o3BgMG_bodyScroll",
1445
1445
  "bodyWrap": "o3BgMG_bodyWrap",
1446
- "chevron": "o3BgMG_chevron",
1447
1446
  "codeBody": "o3BgMG_codeBody",
1448
1447
  "detailsBodyWrap": "o3BgMG_detailsBodyWrap",
1448
+ "diffAdded": "o3BgMG_diffAdded",
1449
1449
  "diffBody": "o3BgMG_diffBody",
1450
+ "diffRemoved": "o3BgMG_diffRemoved",
1450
1451
  "diffStat": "o3BgMG_diffStat",
1451
1452
  "errorSummary": "o3BgMG_errorSummary",
1452
1453
  "fileLink": "o3BgMG_fileLink",
@@ -1526,11 +1527,7 @@ window.__ModuleLoader__.load({
1526
1527
  const running = state === "running" || state === "preparing";
1527
1528
  const normalSummary = terminalBody?.description ?? (open ? detailsBody?.expandedSummary ?? summary : summary);
1528
1529
  const summaryText = (state === "error" ? errorSummary ?? normalSummary : null) ?? normalSummary;
1529
- const diffStat = (0, react.useMemo)(() => {
1530
- if (diffBody === null) return null;
1531
- const { added, removed } = (0, _deepseek_ai_dsh_client_ui_primitives.diffTotals)(diffBody.card.diffs);
1532
- return `+${added} -${removed}`;
1533
- }, [diffBody]);
1530
+ const diffStat = (0, react.useMemo)(() => diffBody === null ? null : (0, _deepseek_ai_dsh_client_ui_primitives.diffTotals)(diffBody.card.diffs), [diffBody]);
1534
1531
  const settledWithCue = state === "error" || state === "stopped";
1535
1532
  const suffix = settledWithCue ? null : summarySuffix ?? diffStat;
1536
1533
  const openFile = (0, react.useMemo)(() => filePath !== void 0 && onOpenFile !== void 0 && !settledWithCue ? (event) => {
@@ -1573,15 +1570,23 @@ window.__ModuleLoader__.load({
1573
1570
  children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, { children: summaryText })
1574
1571
  }),
1575
1572
  suffix !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
1576
- className: clsx(ToolRow_module_css_default.summarySuffix, suffix === diffStat && ToolRow_module_css_default.diffStat),
1577
- children: suffix
1573
+ className: clsx(ToolRow_module_css_default.summarySuffix, typeof suffix !== "string" && ToolRow_module_css_default.diffStat),
1574
+ children: typeof suffix === "string" ? suffix : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1575
+ (0, react_jsx_runtime.jsx)("span", {
1576
+ className: ToolRow_module_css_default.diffAdded,
1577
+ children: `+${suffix.added}`
1578
+ }),
1579
+ " ",
1580
+ (0, react_jsx_runtime.jsx)("span", {
1581
+ className: ToolRow_module_css_default.diffRemoved,
1582
+ children: `-${suffix.removed}`
1583
+ })
1584
+ ] })
1578
1585
  })
1579
1586
  ] }), [
1580
- diffStat,
1581
1587
  summaryLinkKeyDown,
1582
1588
  linkHref,
1583
1589
  openFile,
1584
- running,
1585
1590
  state,
1586
1591
  suffix,
1587
1592
  summaryText
@@ -1720,7 +1725,6 @@ window.__ModuleLoader__.load({
1720
1725
  rowClassName: ToolRow_module_css_default.row,
1721
1726
  leadingClassName: ToolRow_module_css_default.leading,
1722
1727
  titleClassName: ToolRow_module_css_default.title,
1723
- chevronClassName: ToolRow_module_css_default.chevron,
1724
1728
  icon,
1725
1729
  title,
1726
1730
  running,
@@ -1781,13 +1785,13 @@ window.__ModuleLoader__.load({
1781
1785
  }
1782
1786
  //#endregion
1783
1787
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/ToolCallTree.module.css.mjs
1784
- const css$1 = ".ztWv_q_callRow{border-radius:var(--dsw-radius-sm)}.ztWv_q_subCalls{border-left:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:4px;margin:4px 0 2px 22px;padding-left:8px;display:flex}";
1785
- const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1786
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1788
+ const css$2 = ".ztWv_q_callRow{border-radius:var(--dsw-radius-sm)}.ztWv_q_subCalls{border-left:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:4px;margin:4px 0 2px 22px;padding-left:8px;display:flex}";
1789
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1790
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1787
1791
  const tag = document.createElement("style");
1788
1792
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1789
- tag.dataset.pluginCss = tagId$1;
1790
- tag.textContent = css$1;
1793
+ tag.dataset.pluginCss = tagId$2;
1794
+ tag.textContent = css$2;
1791
1795
  document.head.appendChild(tag);
1792
1796
  }
1793
1797
  var ToolCallTree_module_css_default = {
@@ -1947,6 +1951,111 @@ window.__ModuleLoader__.load({
1947
1951
  /** Locale namespace supplied by the conversation owner to Tool renderers. */
1948
1952
  const CONVERSATION_NS = "conversation";
1949
1953
  //#endregion
1954
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/QuestionToolRow.module.css.mjs
1955
+ const css$1 = ".nvmz7W_panelButton{height:calc(22px + var(--dsh-content-font-delta,0px));corner-shape:round;background:var(--dsw-alias-button-floating-fill);color:var(--dsw-alias-label-primary);font-family:inherit;font-size:calc(var(--dsh-content-font-size-secondary,13px) - 1px);white-space:nowrap;cursor:pointer;border:none;border-radius:999px;flex:none;align-items:center;gap:4px;margin-left:8px;padding:0 10px;line-height:1;transition:background .1s;display:inline-flex}.nvmz7W_panelButton svg{width:13px;height:13px}.nvmz7W_panelButton:hover{background:var(--dsw-alias-button-floating-hover)}";
1956
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/QuestionToolRow.module.css";
1957
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1958
+ const tag = document.createElement("style");
1959
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1960
+ tag.dataset.pluginCss = tagId$1;
1961
+ tag.textContent = css$1;
1962
+ document.head.appendChild(tag);
1963
+ }
1964
+ var QuestionToolRow_module_css_default = { "panelButton": "nvmz7W_panelButton" };
1965
+ //#endregion
1966
+ //#region lib/types/client/tool/components/QuestionToolRow.js
1967
+ /** Question-owned summary action and transcript disclosure. */
1968
+ /**
1969
+ * Render a question with its answer-panel entry point.
1970
+ * @param props - Question transcript, panel action, and disclosure state.
1971
+ * @returns The question's summary and optional expanded record.
1972
+ */
1973
+ function QuestionToolRow({ useDisclosure, t, summary, bodyRaw, output, askQuestion, state, inspect, openPanel, panelLabel }) {
1974
+ const { expanded, toggle } = useDisclosure();
1975
+ const expandable = bodyRaw != null || output != null || askQuestion != null;
1976
+ const open = expanded && expandable;
1977
+ return (0, react_jsx_runtime.jsxs)("div", {
1978
+ className: ToolRow_module_css_default.root,
1979
+ "data-variant": "others",
1980
+ "data-tool": "ask_user_question",
1981
+ "data-state": state,
1982
+ children: [state === "running" && (0, react_jsx_runtime.jsx)("span", {
1983
+ className: ToolRow_module_css_default.visuallyHidden,
1984
+ children: t("row.running")
1985
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
1986
+ rowClassName: ToolRow_module_css_default.row,
1987
+ leadingClassName: ToolRow_module_css_default.leading,
1988
+ titleClassName: ToolRow_module_css_default.title,
1989
+ chevronClassName: ToolRow_module_css_default.chevron,
1990
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutlineRegular, {}),
1991
+ title: t("ask.rowTitle"),
1992
+ running: state === "running",
1993
+ open,
1994
+ expandable,
1995
+ keepContentWhenOpen: true,
1996
+ onToggle: toggle,
1997
+ collapsedContent: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [summary !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
1998
+ className: ToolRow_module_css_default.sep,
1999
+ "aria-hidden": true
2000
+ }), (0, react_jsx_runtime.jsx)("span", {
2001
+ className: ToolRow_module_css_default.summary,
2002
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TextShimmer, {
2003
+ active: state === "running",
2004
+ children: summary
2005
+ })
2006
+ })] }), (0, react_jsx_runtime.jsxs)("button", {
2007
+ type: "button",
2008
+ className: QuestionToolRow_module_css_default.panelButton,
2009
+ onClick: (event) => {
2010
+ event.stopPropagation();
2011
+ if (!openPanel()) toggle();
2012
+ },
2013
+ onKeyDown: (event) => {
2014
+ if (event.key === "Enter" || event.key === " ") event.stopPropagation();
2015
+ },
2016
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutlineRegular, {}), panelLabel]
2017
+ })] }),
2018
+ children: open && (0, react_jsx_runtime.jsxs)("div", {
2019
+ className: ToolRow_module_css_default.bodyWrap,
2020
+ children: [askQuestion != null ? (0, react_jsx_runtime.jsx)(AskQuestionCard, { card: askQuestion }) : (0, react_jsx_runtime.jsxs)("div", {
2021
+ className: ToolRow_module_css_default.ioCard,
2022
+ children: [
2023
+ bodyRaw != null && (0, react_jsx_runtime.jsxs)("div", {
2024
+ className: ToolRow_module_css_default.ioSection,
2025
+ children: [(0, react_jsx_runtime.jsx)("span", {
2026
+ className: ToolRow_module_css_default.ioLabel,
2027
+ children: t("row.input")
2028
+ }), (0, react_jsx_runtime.jsx)("span", {
2029
+ className: ToolRow_module_css_default.ioText,
2030
+ children: formatToolBody("others", bodyRaw)
2031
+ })]
2032
+ }),
2033
+ bodyRaw != null && output != null && (0, react_jsx_runtime.jsx)("span", {
2034
+ className: ToolRow_module_css_default.ioDivider,
2035
+ "aria-hidden": true
2036
+ }),
2037
+ output != null && (0, react_jsx_runtime.jsxs)("div", {
2038
+ className: ToolRow_module_css_default.ioSection,
2039
+ children: [(0, react_jsx_runtime.jsx)("span", {
2040
+ className: ToolRow_module_css_default.ioLabel,
2041
+ children: t("row.output")
2042
+ }), (0, react_jsx_runtime.jsx)("span", {
2043
+ className: ToolRow_module_css_default.ioText,
2044
+ children: output
2045
+ })]
2046
+ })
2047
+ ]
2048
+ }), inspect !== void 0 && (0, react_jsx_runtime.jsxs)("button", {
2049
+ type: "button",
2050
+ className: ToolRow_module_css_default.inspectButton,
2051
+ onClick: inspect,
2052
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutlineRegular, {}), t("row.inspect")]
2053
+ })]
2054
+ })
2055
+ })]
2056
+ });
2057
+ }
2058
+ //#endregion
1950
2059
  //#region lib/types/client/tool/toolviews/ask-question-row.js
1951
2060
  function isRecord(value) {
1952
2061
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1975,7 +2084,35 @@ window.__ModuleLoader__.load({
1975
2084
  }
1976
2085
  return entries;
1977
2086
  }
1978
- /** Questions from call JSON; null when pairing with answers would be ambiguous. */
2087
+ /** Answers from a submitted steer that still awaits admission to a step. */
2088
+ function queuedAnswerEntries(message, callId) {
2089
+ if (!isRecord(message) || !Array.isArray(message.content)) return null;
2090
+ const content = message.content[0];
2091
+ if (!isRecord(content) || content.type !== "text" || typeof content.text !== "string") return null;
2092
+ const parsed = parseJson(content.text);
2093
+ if (!isRecord(parsed) || parsed.kind !== "answer_to_pending_question" || parsed.callId !== callId) return null;
2094
+ return answerEntries(content.text);
2095
+ }
2096
+ /** One question's choices from call JSON; absent when any entry is malformed. */
2097
+ function optionEntries(value) {
2098
+ if (!Array.isArray(value)) return void 0;
2099
+ const options = [];
2100
+ for (const option of value) {
2101
+ if (!isRecord(option) || typeof option.label !== "string") return void 0;
2102
+ options.push({
2103
+ label: option.label,
2104
+ ...typeof option.description === "string" ? { description: option.description } : {}
2105
+ });
2106
+ }
2107
+ return options;
2108
+ }
2109
+ /**
2110
+ * Questions from call JSON; null when pairing with answers would be ambiguous.
2111
+ * The optional presentation fields are carried for the read-only answer panel,
2112
+ * which renders the same option lists the user chose from; a malformed one is
2113
+ * dropped rather than failing the whole call, because the transcript card needs
2114
+ * only the id and the question text.
2115
+ */
1979
2116
  function questionEntries(argsRaw) {
1980
2117
  const parsed = parseJson(argsRaw);
1981
2118
  if (!isRecord(parsed) || !Array.isArray(parsed.questions) || parsed.questions.length === 0) return null;
@@ -1984,17 +2121,21 @@ window.__ModuleLoader__.load({
1984
2121
  for (const question of parsed.questions) {
1985
2122
  if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string" || ids.has(question.id)) return null;
1986
2123
  ids.add(question.id);
2124
+ const options = optionEntries(question.options);
1987
2125
  questions.push({
1988
2126
  id: question.id,
1989
- question: question.question
2127
+ question: question.question,
2128
+ ...typeof question.header === "string" ? { header: question.header } : {},
2129
+ ...typeof question.detail === "string" ? { detail: question.detail } : {},
2130
+ ...options === void 0 ? {} : { options },
2131
+ ...typeof question.multi_select === "boolean" ? { multiSelect: question.multi_select } : {}
1990
2132
  });
1991
2133
  }
1992
2134
  return questions;
1993
2135
  }
1994
2136
  /** Pair questions with result entries by their echoed stable ids. */
1995
- function pairAnswers(argsRaw, answers) {
1996
- const questions = questionEntries(argsRaw);
1997
- if (questions === null || questions.length !== answers.length) return null;
2137
+ function pairAnswers(questions, answers) {
2138
+ if (questions.length !== answers.length) return null;
1998
2139
  const byId = /* @__PURE__ */ new Map();
1999
2140
  for (const answer of answers) {
2000
2141
  if (byId.has(answer.id)) return null;
@@ -2005,23 +2146,38 @@ window.__ModuleLoader__.load({
2005
2146
  const answer = byId.get(question.id);
2006
2147
  if (answer === void 0) return null;
2007
2148
  paired.push({
2008
- ...question,
2149
+ id: question.id,
2150
+ question: question.question,
2009
2151
  answers: [...answer.selected, ...answer.custom === void 0 || answer.custom === "" ? [] : [answer.custom]]
2010
2152
  });
2011
2153
  }
2012
2154
  return paired;
2013
2155
  }
2014
- /** Answer summary plus structured transcript content from the two wire JSON documents. */
2015
- function answeredPresentation(argsRaw, text, t) {
2016
- const answers = answerEntries(text);
2017
- if (answers === null) return null;
2156
+ /**
2157
+ * Answer summary, transcript content, and panel material from one recorded
2158
+ * answer batch, whether the result carried it in time or a late reply did.
2159
+ * @param questions - The call's questions, or null when they failed validation.
2160
+ * @param answers - The recorded answer batch.
2161
+ * @param t - Row translator.
2162
+ * @returns What the answered row shows.
2163
+ */
2164
+ function answeredPresentation(questions, answers, t) {
2018
2165
  const answered = answers.filter((answer) => answer.selected.length > 0 || (answer.custom ?? "") !== "").length;
2166
+ const paired = questions === null ? null : pairAnswers(questions, answers);
2019
2167
  return {
2020
2168
  summary: t("ask.answered", {
2021
2169
  answered,
2022
2170
  total: answers.length
2023
2171
  }),
2024
- questions: pairAnswers(argsRaw, answers)
2172
+ transcript: paired === null ? null : {
2173
+ kind: "answered",
2174
+ questions: paired,
2175
+ skippedLabel: t("ask.skipped")
2176
+ },
2177
+ record: paired === null || questions === null ? void 0 : {
2178
+ questions,
2179
+ answers
2180
+ }
2025
2181
  };
2026
2182
  }
2027
2183
  /** Best-effort answered-count summary when strict transcript pairing fails. */
@@ -2036,14 +2192,31 @@ window.__ModuleLoader__.load({
2036
2192
  total: answers.length
2037
2193
  });
2038
2194
  }
2195
+ /** Whether a successful tool result records a still-answerable timed question. */
2196
+ function isPendingResult(text) {
2197
+ const parsed = parseJson(text);
2198
+ return isRecord(parsed) && parsed.pending === true && typeof parsed.callId === "string";
2199
+ }
2039
2200
  /** Summarizes a pending, answered, cancelled, or interrupted question set. */
2040
- function AskQuestionRow({ toolName, block, inspect, useDisclosure, t }) {
2201
+ function AskQuestionRow({ callId, toolName, block, inspect, useDisclosure, useProjection, revealPanel, reviewPanel, t }) {
2041
2202
  const model = toolRowModel(toolName, block);
2203
+ const answerable = useProjection("userQuestions", (view) => view?.active.some((row) => row.callId === callId) ?? false);
2204
+ const settled = useProjection("userQuestions", (view) => view?.settled.find((row) => row.callId === callId));
2205
+ const queuedReply = useProjection("inbox", (view) => {
2206
+ const matches = (message) => {
2207
+ const source = isRecord(message) ? message.source : void 0;
2208
+ return isRecord(source) && source.kind === "user-question-reply" && source.callId === callId;
2209
+ };
2210
+ return view?.["next-step"].find(matches) ?? view?.["next-turn"].find(matches);
2211
+ });
2212
+ const reopen = (0, react.useCallback)(() => revealPanel(callId), [callId, revealPanel]);
2042
2213
  const code = "kind" in block ? block.error?.code : void 0;
2043
2214
  const argsRaw = model.bodyRaw ?? "";
2044
2215
  let summary = model.summary;
2045
2216
  let state = model.state;
2046
2217
  let transcript = null;
2218
+ let rowAction;
2219
+ let rowActionLabel = t("ask.reopen");
2047
2220
  if (code === "ASK_CANCELLED") {
2048
2221
  summary = t("ask.cancelled");
2049
2222
  state = "ok";
@@ -2062,19 +2235,92 @@ window.__ModuleLoader__.load({
2062
2235
  questions,
2063
2236
  verdict: t("ask.interruptedDetail")
2064
2237
  };
2065
- } else if (model.state === "running") summary = t("ask.waiting");
2066
- else if ("kind" in block && model.state === "ok") {
2238
+ } else if (model.state === "running") {
2239
+ summary = t("ask.waiting");
2240
+ if (answerable) rowAction = reopen;
2241
+ } else if (code === "TOOL_OUTCOME_UNKNOWN" && (answerable || settled !== void 0)) {
2242
+ state = "ok";
2243
+ const questions = questionEntries(argsRaw);
2244
+ if (answerable) {
2245
+ summary = t("ask.pending");
2246
+ if (questions !== null) transcript = {
2247
+ kind: "unanswered",
2248
+ questions,
2249
+ verdict: t("ask.pendingDetail")
2250
+ };
2251
+ rowAction = reopen;
2252
+ } else if (settled?.answers.length === 0) {
2253
+ summary = t("ask.closed");
2254
+ if (questions !== null) transcript = {
2255
+ kind: "unanswered",
2256
+ questions,
2257
+ verdict: t("ask.closedDetail")
2258
+ };
2259
+ } else if (settled !== void 0) {
2260
+ const presentation = answeredPresentation(questions, settled.answers, t);
2261
+ summary = presentation.summary;
2262
+ transcript = presentation.transcript;
2263
+ const record = presentation.record;
2264
+ if (record !== void 0) {
2265
+ rowAction = () => reviewPanel(callId, record);
2266
+ rowActionLabel = t("ask.review");
2267
+ }
2268
+ }
2269
+ } else if ("kind" in block && model.state === "ok") {
2067
2270
  const text = singleResultText(block);
2068
2271
  if (text !== void 0) {
2069
- const presentation = answeredPresentation(argsRaw, text, t);
2070
- summary = presentation?.summary ?? answeredSummary(text, t) ?? model.summary;
2071
- if (presentation?.questions !== null && presentation?.questions !== void 0) transcript = {
2072
- kind: "answered",
2073
- questions: presentation.questions,
2074
- skippedLabel: t("ask.skipped")
2075
- };
2272
+ const questions = questionEntries(argsRaw);
2273
+ const pending = isPendingResult(text);
2274
+ if (pending && answerable) {
2275
+ summary = t("ask.pending");
2276
+ if (questions !== null) transcript = {
2277
+ kind: "unanswered",
2278
+ questions,
2279
+ verdict: t("ask.pendingDetail")
2280
+ };
2281
+ rowAction = reopen;
2282
+ } else if (pending && (settled === void 0 || settled.answers.length === 0)) {
2283
+ summary = t("ask.closed");
2284
+ if (questions !== null) transcript = {
2285
+ kind: "unanswered",
2286
+ questions,
2287
+ verdict: t("ask.closedDetail")
2288
+ };
2289
+ } else {
2290
+ const answers = settled?.answers ?? answerEntries(text);
2291
+ const presentation = answers === null ? null : answeredPresentation(questions, answers, t);
2292
+ summary = presentation?.summary ?? answeredSummary(text, t) ?? model.summary;
2293
+ transcript = presentation?.transcript ?? null;
2294
+ const record = settled === void 0 ? void 0 : presentation?.record;
2295
+ if (record !== void 0) {
2296
+ rowAction = () => reviewPanel(callId, record);
2297
+ rowActionLabel = t("ask.review");
2298
+ }
2299
+ }
2300
+ }
2301
+ }
2302
+ if (answerable && queuedReply !== void 0 && rowAction === reopen) {
2303
+ const answers = queuedAnswerEntries(queuedReply, callId);
2304
+ const presentation = answers === null ? null : answeredPresentation(questionEntries(argsRaw), answers, t);
2305
+ if (presentation?.record !== void 0) {
2306
+ summary = presentation.summary;
2307
+ transcript = presentation.transcript;
2308
+ rowAction = () => false;
2309
+ rowActionLabel = t("ask.review");
2076
2310
  }
2077
2311
  }
2312
+ if (rowAction !== void 0) return (0, react_jsx_runtime.jsx)(QuestionToolRow, {
2313
+ useDisclosure,
2314
+ t,
2315
+ summary,
2316
+ bodyRaw: transcript === null ? model.bodyRaw : null,
2317
+ output: transcript === null ? model.output : null,
2318
+ askQuestion: transcript,
2319
+ state,
2320
+ inspect,
2321
+ openPanel: rowAction,
2322
+ panelLabel: rowActionLabel
2323
+ });
2078
2324
  return (0, react_jsx_runtime.jsx)(ToolRow, {
2079
2325
  useDisclosure,
2080
2326
  t,
@@ -2098,7 +2344,11 @@ window.__ModuleLoader__.load({
2098
2344
  ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
2099
2345
  name: "tool.call.toolview",
2100
2346
  key: "ask_user_question",
2101
- locale: CONVERSATION_NS
2347
+ locale: CONVERSATION_NS,
2348
+ inject: (sessionId) => ({
2349
+ revealPanel: (callId) => ctx.get("userQuestionPanels")?.reveal(sessionId, callId) ?? false,
2350
+ reviewPanel: (callId, record) => ctx.get("userQuestionPanels")?.review(sessionId, callId, record) ?? false
2351
+ })
2102
2352
  }, AskQuestionRow));
2103
2353
  }
2104
2354
  };
@@ -2123,7 +2373,7 @@ window.__ModuleLoader__.load({
2123
2373
  }
2124
2374
  //#endregion
2125
2375
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.module.css.mjs
2126
- const css = ".CY-8Ka_card{flex-direction:column;display:flex}.CY-8Ka_terminal{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1);margin:4px 0 4px 4px}.CY-8Ka_ioCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);flex-direction:column;margin:4px 0 4px 4px;display:flex}.CY-8Ka_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.CY-8Ka_ioSection::-webkit-scrollbar-thumb{border-radius:var(--dsw-radius-sm);background-clip:padding-box;border:2px solid #0000}.CY-8Ka_ioSection::-webkit-scrollbar-track{margin:6px 0}.CY-8Ka_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.CY-8Ka_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.CY-8Ka_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.CY-8Ka_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.CY-8Ka_root[data-expandable]{cursor:pointer}.CY-8Ka_root{height:calc(24px + var(--dsh-content-font-delta,0px));align-items:center;min-width:0;display:flex}.CY-8Ka_leading{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex;position:relative}.CY-8Ka_leading svg:not([data-state]){width:calc(14px + var(--dsh-content-font-delta,0px));height:calc(14px + var(--dsh-content-font-delta,0px))}.CY-8Ka_chevron{color:var(--dsw-alias-label-secondary)}.CY-8Ka_iconIdle{opacity:1;transition:opacity .1s;display:inline-flex}.CY-8Ka_chevronHover{opacity:0;margin:auto;transition:opacity .1s;position:absolute;inset:0}.CY-8Ka_root:hover .CY-8Ka_iconIdle{opacity:0}.CY-8Ka_root:hover .CY-8Ka_chevronHover{opacity:1}.CY-8Ka_title{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-secondary);flex:none;transition:color .1s}.CY-8Ka_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.CY-8Ka_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-tertiary);flex:auto;transition:color .1s;overflow:hidden}.CY-8Ka_root:hover .CY-8Ka_title,.CY-8Ka_root:hover .CY-8Ka_summary:not(.CY-8Ka_errorSummary):not(.CY-8Ka_stoppedSummary){color:var(--dsw-alias-label-primary)}.CY-8Ka_errorSummary{color:var(--dsw-alias-state-error-primary)}.CY-8Ka_stoppedSummary{color:var(--dsw-alias-state-warn-label)}.CY-8Ka_bodyWrap{flex-direction:column;display:flex}.CY-8Ka_inspectButton{border:.5px solid var(--dsw-alias-border-l4);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.CY-8Ka_card:hover .CY-8Ka_inspectButton,.CY-8Ka_inspectButton:focus-visible{opacity:1}.CY-8Ka_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.CY-8Ka_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
2376
+ const css = ".CY-8Ka_card{flex-direction:column;display:flex}.CY-8Ka_terminal{--dsl-terminal-font:var(--dsw-font-markdown-code-block-small);--dsl-terminal-line-height:18px;--dsl-terminal-output-max-height:224px;border:.5px solid var(--dsw-alias-border-l1);margin:4px 0 4px 4px}.CY-8Ka_ioCard{border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);flex-direction:column;margin:4px 0 4px 4px;display:flex}.CY-8Ka_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.CY-8Ka_ioSection::-webkit-scrollbar-thumb{border-radius:var(--dsw-radius-sm);background-clip:padding-box;border:2px solid #0000}.CY-8Ka_ioSection::-webkit-scrollbar-track{margin:6px 0}.CY-8Ka_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.CY-8Ka_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:.5px}.CY-8Ka_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.CY-8Ka_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.CY-8Ka_root[data-expandable]{cursor:pointer}.CY-8Ka_root{height:calc(24px + var(--dsh-content-font-delta,0px));min-width:0;color:var(--dsw-alias-label-tertiary);align-items:center;transition:color .1s;display:flex}.CY-8Ka_root:hover{color:var(--dsw-alias-label-secondary)}@media (prefers-reduced-motion:reduce){.CY-8Ka_root{transition:none}}.CY-8Ka_leading{width:calc(16px + var(--dsh-content-font-delta,0px));height:calc(16px + var(--dsh-content-font-delta,0px));color:inherit;flex:none;justify-content:center;align-items:center;margin-right:6px;display:inline-flex;position:relative}.CY-8Ka_leading svg:not([data-state]){width:calc(14px + var(--dsh-content-font-delta,0px));height:calc(14px + var(--dsh-content-font-delta,0px))}.CY-8Ka_chevron{color:inherit}.CY-8Ka_iconIdle{opacity:1;transition:opacity .1s;display:inline-flex}.CY-8Ka_chevronHover{opacity:0;margin:auto;transition:opacity .1s;position:absolute;inset:0}.CY-8Ka_root:hover .CY-8Ka_iconIdle{opacity:0}.CY-8Ka_root:hover .CY-8Ka_chevronHover{opacity:1}.CY-8Ka_title{font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));flex:none}.CY-8Ka_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.CY-8Ka_summary{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(24px + var(--dsh-content-font-delta,0px));flex:auto;overflow:hidden}.CY-8Ka_errorSummary{color:var(--dsw-alias-state-error-primary)}.CY-8Ka_stoppedSummary{color:var(--dsw-alias-state-warn-label)}.CY-8Ka_bodyWrap{flex-direction:column;display:flex}.CY-8Ka_inspectButton{border:.5px solid var(--dsw-alias-border-l4);corner-shape:round;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;border-radius:999px;align-self:flex-start;align-items:center;gap:4px;margin:4px 0 2px 4px;padding:2px 8px;font-size:11px;line-height:16px;transition:opacity .1s;display:inline-flex}.CY-8Ka_card:hover .CY-8Ka_inspectButton,.CY-8Ka_inspectButton:focus-visible{opacity:1}.CY-8Ka_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.CY-8Ka_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
2127
2377
  const tagId = "@deepseek-ai/dsh-client-ui-tool/bash-sample.module.css";
2128
2378
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2129
2379
  const tag = document.createElement("style");
@@ -1,8 +1,9 @@
1
1
  /** Tool UI slot declarations and their composed component props. */
2
- import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime, SlotHookFactory } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime, SessionIdOf, SlotHookFactory } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { RemoteHostFacts } from '@deepseek-ai/dsh-api-remotes/client';
4
4
  import type { AssistantChatData, OpenFileOptions, PreparingToolCall, StartedToolCall, ToolResultNode, UseDisclosure } from '@deepseek-ai/dsh-client-ui-chat/client';
5
5
  import type { MessageImageLoader, MessageImageSource } from '@deepseek-ai/dsh-client-ui-conversation/client';
6
+ import type { AskUserQuestionAnswerItem, AskUserQuestionItem } from '@deepseek-ai/dsh-user-questions/types';
6
7
  declare module '@deepseek-ai/dsh-client-ui-slots' {
7
8
  interface SlotMap {
8
9
  /**
@@ -103,6 +104,50 @@ export type ToolCallViewProps = PropsRuntime<'tool.call.toolview'>;
103
104
  export type StartedToolCallViewProps = Exclude<ToolCallViewProps, {
104
105
  readonly phase: 'preparing';
105
106
  }>;
107
+ /**
108
+ * One settled `ask_user_question` call as its transcript row read it. The row
109
+ * reads the questions from the recorded call JSON, and the answers from
110
+ * whichever recorded them: its own result when the answer arrived in time, or
111
+ * the `userQuestions` projection when a late reply settled the call.
112
+ */
113
+ export interface UserQuestionRecord {
114
+ /** The call's questions, with the option lists the panel renders. */
115
+ readonly questions: readonly AskUserQuestionItem[];
116
+ /** The recorded answer batch; an item with no selection and no custom text was skipped. */
117
+ readonly answers: readonly AskUserQuestionAnswerItem[];
118
+ }
119
+ /**
120
+ * Optional answer-panel provider consumed by the ask-question row. A timed
121
+ * `ask_user_question` call stays answerable after its result is recorded, and
122
+ * its panel can be closed, so the transcript row is the way back to it. A
123
+ * composition without a question UI has no panel to show.
124
+ */
125
+ export interface UserQuestionPanels {
126
+ /**
127
+ * Put one call's answer panel back in the composer, ahead of any other
128
+ * pending question.
129
+ * @param sessionId - Session the call belongs to.
130
+ * @param callId - `ask_user_question` call whose panel to show.
131
+ * @returns whether a panel for that call was there to show.
132
+ */
133
+ reveal(sessionId: SessionIdOf, callId: string): boolean;
134
+ /**
135
+ * Show one settled call's recorded answers as a read-only panel, ahead of
136
+ * any other pending question. Closing that panel drops it for good; the row
137
+ * builds it again from the same record.
138
+ * @param sessionId - Session the call belongs to.
139
+ * @param callId - `ask_user_question` call whose record to show.
140
+ * @param record - the call's questions and recorded answers.
141
+ * @returns whether the panel was shown.
142
+ */
143
+ review(sessionId: SessionIdOf, callId: string, record: UserQuestionRecord): boolean;
144
+ }
145
+ declare module '@deepseek-ai/cordis' {
146
+ interface Context {
147
+ /** Optional user-question answer-panel provider. */
148
+ userQuestionPanels: UserQuestionPanels;
149
+ }
150
+ }
106
151
  /** Injected Host description for POSIX home-path display. */
107
152
  export type ToolHostInfoInjected = {
108
153
  hooks: {
@@ -1,4 +1,4 @@
1
1
  /** Browser Tool plugin: whole-call composition and keyed atomic Tool views. */
2
2
  export { apply, inject } from './apply.ts';
3
- export type { StartedToolCallViewProps, ToolCallCommonProps, ToolCallOwnerProps, ToolCallPhaseProps, ToolCallViewProps, ToolCallHookContext, ToolCallInjected, ToolHostInfoInjected, ToolTreeProps, UseToolCallArgumentsPartial, } from './contract/slots.ts';
3
+ export type { StartedToolCallViewProps, ToolCallCommonProps, ToolCallOwnerProps, ToolCallPhaseProps, ToolCallViewProps, ToolCallHookContext, ToolCallInjected, ToolHostInfoInjected, ToolTreeProps, UseToolCallArgumentsPartial, UserQuestionPanels, UserQuestionRecord, } from './contract/slots.ts';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ import type { ToolRowProps } from './ToolRow.tsx';
2
+ type QuestionToolRowProps = Pick<ToolRowProps, 'useDisclosure' | 't' | 'summary' | 'bodyRaw' | 'output' | 'askQuestion' | 'state' | 'inspect'> & {
3
+ readonly openPanel: () => boolean;
4
+ readonly panelLabel: string;
5
+ };
6
+ /**
7
+ * Render a question with its answer-panel entry point.
8
+ * @param props - Question transcript, panel action, and disclosure state.
9
+ * @returns The question's summary and optional expanded record.
10
+ */
11
+ export declare function QuestionToolRow({ useDisclosure, t, summary, bodyRaw, output, askQuestion, state, inspect, openPanel, panelLabel, }: QuestionToolRowProps): import("react").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=QuestionToolRow.d.ts.map
@@ -39,6 +39,7 @@ export interface ToolRowProps {
39
39
  errorSummary?: string | null | undefined;
40
40
  /** Terminal card; card fields are mutually exclusive and replace text sections. */
41
41
  terminal?: TerminalCardModel | null | undefined;
42
+ /** Diff card with inline totals colored on header hover and while expanded. */
42
43
  diff?: DiffCardModel | null | undefined;
43
44
  read?: ReadCardModel | null | undefined;
44
45
  /**
@@ -1,9 +1,25 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
- import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
- import type { ToolCallViewProps } from '../../contract/slots.ts';
4
- type AskQuestionRowProps = ToolCallViewProps & PropsLocale<'conversation'>;
2
+ import type { InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { ToolCallViewProps, UserQuestionRecord } from '../../contract/slots.ts';
4
+ /** Injected panel verbs for the row's own Session, filled by the optional panel provider. */
5
+ interface AskQuestionPanelInjected {
6
+ /**
7
+ * Show one call's answer panel in the composer.
8
+ * @param callId - the row's own `ask_user_question` call.
9
+ * @returns whether a panel for that call was there to show.
10
+ */
11
+ revealPanel: (callId: string) => boolean;
12
+ /**
13
+ * Show one settled call's recorded answers as a read-only panel.
14
+ * @param callId - the row's own `ask_user_question` call.
15
+ * @param record - the call's questions and recorded answers, read from this row.
16
+ * @returns whether a panel provider was there to show it.
17
+ */
18
+ reviewPanel: (callId: string, record: UserQuestionRecord) => boolean;
19
+ }
20
+ type AskQuestionRowProps = ToolCallViewProps & PropsLocale<'conversation'> & InjectFace<AskQuestionPanelInjected>;
5
21
  /** Summarizes a pending, answered, cancelled, or interrupted question set. */
6
- export declare function AskQuestionRow({ toolName, block, inspect, useDisclosure, t }: AskQuestionRowProps): import("react").JSX.Element;
22
+ export declare function AskQuestionRow({ callId, toolName, block, inspect, useDisclosure, useProjection, revealPanel, reviewPanel, t, }: AskQuestionRowProps): import("react").JSX.Element;
7
23
  /** Registers the ask-user-question conversation row. */
8
24
  export declare const askQuestionToolview: {
9
25
  name: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-tool",
3
3
  "description": "Client Tool call-tree renderer and keyed per-tool presentation slot",
4
- "version": "0.2.0-rc.1",
4
+ "version": "0.2.0-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -46,32 +46,33 @@
46
46
  "react": "^18.2.0",
47
47
  "react-dom": "^18.2.0",
48
48
  "clsx": "^2.0.0",
49
- "@deepseek-ai/dsh-spill-policy": "0.2.0-rc.1",
50
- "@deepseek-ai/dsh-agent": "0.2.0-rc.1",
51
- "@deepseek-ai/dsh-llm": "0.2.0-rc.1",
52
- "@deepseek-ai/dsh-session": "0.2.0-rc.1",
53
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.1",
54
- "@deepseek-ai/dsh-tools": "0.2.0-rc.1",
55
- "@deepseek-ai/dsh-tool-jobs": "0.2.0-rc.1",
56
- "@deepseek-ai/dsh-tool-terminal": "0.2.0-rc.1",
57
- "@deepseek-ai/dsh-spill": "0.2.0-rc.1",
49
+ "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
50
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
51
+ "@deepseek-ai/dsh-session": "0.2.0-rc.2",
52
+ "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
53
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
54
+ "@deepseek-ai/dsh-spill-policy": "0.2.0-rc.2",
55
+ "@deepseek-ai/dsh-tool-jobs": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-tool-session-query": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-tool-terminal": "0.2.0-rc.2",
58
+ "@deepseek-ai/dsh-spill": "0.2.0-rc.2",
58
59
  "@deepseek-ai/cordis": "~4.0.4",
59
- "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.1",
60
- "@deepseek-ai/dsh-tool-session-query": "0.2.0-rc.1",
61
- "@deepseek-ai/dsh-client-locale": "0.2.0-rc.1",
62
- "@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.1",
63
- "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.1",
64
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.1",
65
- "@deepseek-ai/dsh-client-connection": "0.2.0-rc.1",
66
- "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
67
- "@deepseek-ai/dsh-api-workspace-controller": "0.2.0-rc.1",
68
- "@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.1",
69
- "@deepseek-ai/dsh-attachment": "0.2.0-rc.1",
70
- "@deepseek-ai/dsh-ptc-runtime": "0.2.0-rc.1",
71
- "@deepseek-ai/dsh-token-meter": "0.2.0-rc.1",
72
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1",
73
- "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.1",
74
- "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.1"
60
+ "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
61
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
62
+ "@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.2",
63
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
64
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
65
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
66
+ "@deepseek-ai/dsh-api-workspace-controller": "0.2.0-rc.2",
67
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
68
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
69
+ "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
70
+ "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
71
+ "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
72
+ "@deepseek-ai/dsh-ptc-runtime": "0.2.0-rc.2",
73
+ "@deepseek-ai/dsh-token-meter": "0.2.0-rc.2",
74
+ "@deepseek-ai/dsh-user-questions": "0.2.0-rc.2",
75
+ "@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.2"
75
76
  },
76
77
  "files": [
77
78
  "lib/index.js",