@deepseek-ai/dsh-client-ui-tool 0.1.3-alpha.2 → 0.1.5-alpha.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
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-tool/README.md
5
- README.md: e0b49a3b582f5f1e6e9aa19e0b026dedb9ba2a18
6
- README.zh.md: 032f048f2501923c4c9f010b76afe27d98fe7889
5
+ README.md: b8dd2a5e3c6743e496d585a2de238714f00e9f6b
6
+ README.zh.md: 49e82c9fa70953d73171d466f36995ff2870c6b2
package/README.md CHANGED
@@ -57,12 +57,12 @@ The package realizes one dispatch rule: atomic Tool views are keyed by wire Tool
57
57
 
58
58
  ### Rendering contract
59
59
 
60
- `ToolCallTree` receives one root `ToolCallBlock` that already contains recursive `subCalls`, selection state, the session `cwd`, and Host callbacks for opening files and inspecting calls. It recursively walks the standard call blocks and sends the root and children at every depth through the same atomic dispatch path, without subscribing to a separate parent-to-children map. Each root and child wrapper preserves the `data-chat-anchor-key="call:<id>"` and `data-chat-call-id` DOM contract used for paging and selection.
60
+ `ToolCallTree` receives one root `ToolCallBlock` that already contains recursive `subCalls`, the session `cwd`, and the owner's callbacks for opening files and inspecting calls. It recursively walks the standard call blocks and sends the root and children at every depth through the same atomic dispatch path, without subscribing to a separate parent-to-children map. Each root and child wrapper preserves the `data-chat-anchor-key="call:<id>"` and `data-chat-call-id` DOM contract used for paging and selection.
61
61
 
62
- ### Details and cards
62
+ ### Cards
63
63
 
64
64
 
65
- The package fills `conversation.details.tool` with `ToolDetails`. Row and Details renderers share one pure card model for each terminal, read, diff, search, and web card; the image card is row-only because its gallery renders through the tool-owned `tool.call.images` slot the details panel does not declare. These models validate raw call arguments, result content, failure state, persisted metadata, and Session path facts. Diff, read, search, and web models also reject Code Dispatch children; terminal eligibility does not depend on `parentCallId`. Unsupported or malformed inputs use flattened Tool result text. 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.
65
+ 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, Code 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. 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.
66
66
 
67
67
  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.
68
68
  </details>
package/README.zh.md CHANGED
@@ -57,12 +57,12 @@ owner 载荷为 `ToolCallOwnerProps`:`callId`、`toolName`、冻结的 `block`
57
57
 
58
58
  ### 渲染约定
59
59
 
60
- `ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、selection 状态、会话 `cwd`,以及用于打开文件和检查调用的 Host 回调。它递归遍历标准调用块,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。每个 root 和 child 包装层都保留 `data-chat-anchor-key="call:<id>"` 与 `data-chat-call-id` DOM 约定,供分页和 selection 使用。
60
+ `ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、会话 `cwd`,以及属主用于打开文件和检查调用的回调。它递归遍历标准调用块,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。每个 root 和 child 包装层都保留 `data-chat-anchor-key="call:<id>"` 与 `data-chat-call-id` DOM 约定,供分页和 selection 使用。
61
61
 
62
- ### 详情与卡片
62
+ ### 卡片
63
63
 
64
64
 
65
- 本包通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer Details renderer 分别为 terminal、read、diff、search 和 web 卡片复用同一个纯 card modelimage 卡片仅属于行,因为其图库经由工具自有 `tool.call.images` 槽位渲染,而 details 面板不声明该槽位。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata Session 路径事实。diff、read、search web model 还会拒绝 Code Dispatch 子调用;terminal 适用性不依赖 `parentCallId`。不受支持或格式错误的输入使用压平的工具结果文本。terminal、diff、read、search 与 web 卡片的上限与 fallback 规则仍由 [ui-primitives README](../ui-primitives/README.zh.md) 负责;image 卡片的 fallback 规则由本包内的 card model 自行承载。
65
+ 每张卡片都在调用树里就地阅读;不存在选中调用的第二个全高展示面。行 renderer terminal、read、diff、search 和 web 卡片各复用同一个纯 card modelimage 卡片的图库经由工具自有 `tool.call.images` 槽位渲染。这些 model 校验原始调用参数、结果内容、失败状态、持久 metadata、Code Dispatch `parentCallId` Session 路径事实。不受支持或格式错误的输入使用压平的工具结果文本。文件路径摘要经属主的 `openFile` 打开文件,chat 视图把它路由到右侧 Sidebar 的文本预览;`inspect` 打开轨迹视图。terminal、diff、read、search 与 web 卡片的上限与 fallback 规则仍由 [ui-primitives README](../ui-primitives/README.zh.md) 负责;image 卡片的 fallback 规则由本包内的 card model 自行承载。
66
66
 
67
67
  terminal model 使用浏览器安全入口 `@deepseek-ai/dsh-spill-policy/notice` 的 `hasSpillNotice`,而非独立的 UI 匹配规则。[spill-policy README](../../spill/spill-policy/README.zh.md#shared-notice-ownership) 负责通知的格式化与识别。该检查保守地选择通用输出;匹配文本不能认证其来源,回放也不改变已记录的结果字节。
68
68
  </details>
package/lib/client.js CHANGED
@@ -8,22 +8,26 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
10
  //#region ../../util/workspace-path/src/index.ts
11
- /**
12
- * Browser-safe Workspace path and display helpers.
13
- * @module @deepseek-ai/dsh-util-workspace-path
14
- */
15
11
  /** Whether a path uses a Windows drive or UNC prefix. */
16
12
  function isWindowsStylePath(value) {
17
13
  return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
18
14
  }
19
15
  /**
16
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
17
+ * @param path - the path to classify.
18
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
19
+ */
20
+ function isAbsoluteWorkspacePath(path) {
21
+ return path.startsWith("/") || isWindowsStylePath(path);
22
+ }
23
+ /**
20
24
  * Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
21
25
  * @param cwd - Session Workspace root, when known.
22
26
  * @param path - Absolute or Workspace-relative path.
23
27
  * @returns an absolute path when a Workspace root is available, otherwise the original path.
24
28
  */
25
29
  function resolveWorkspacePath(cwd, path) {
26
- if (path.startsWith("/") || isWindowsStylePath(path)) return path;
30
+ if (isAbsoluteWorkspacePath(path)) return path;
27
31
  if (cwd === void 0 || cwd === "") return path;
28
32
  const separator = isWindowsStylePath(cwd) && cwd.includes("\\") ? "\\" : "/";
29
33
  return `${cwd.replace(/[/\\]+$/, "")}${separator}${path.replace(/^[/\\]+/, "")}`;
@@ -43,112 +47,6 @@ window.__ModuleLoader__.load({
43
47
  if (path.startsWith(`${root}/`)) return `~${path.slice(root.length)}`;
44
48
  return path;
45
49
  }
46
- //#endregion
47
- //#region lib/types/client/tool/models/tool-call-model.js
48
- /** Locale key per generic row variant. */
49
- const VARIANT_TITLE_KEYS = {
50
- search: "tool.title.search",
51
- read: "tool.title.read",
52
- bash: "tool.title.bash",
53
- write: "tool.title.write",
54
- edit: "tool.title.edit",
55
- code: "tool.title.code",
56
- others: "tool.title.generic"
57
- };
58
- /**
59
- * Known tool name -> variant.
60
- *
61
- * `cordis_define` is deliberately absent: ui-cordis registers a keyed
62
- * `tool.call.toolview` entry for it, and a keyed hit REPLACES the generic row
63
- * (this table is only reached through GenericToolCard, the dispatch fallback in
64
- * ToolCallTree). An entry here would be unreachable, and a second title for the
65
- * same call would be a second answer to a question the card already owns.
66
- */
67
- const TOOL_VARIANTS = {
68
- bash: "bash",
69
- pwsh: "bash",
70
- read: "read",
71
- read_image: "read",
72
- web_fetch: "read",
73
- web_search: "search",
74
- grep: "search",
75
- glob: "search",
76
- write: "write",
77
- edit: "edit",
78
- run_code: "code",
79
- cordis_package_inspect: "read",
80
- cordis_runtime_inspect: "read",
81
- cordis_run: "others",
82
- cordis_stop: "others",
83
- cordis_undefine: "others"
84
- };
85
- /** Tool-owned titles that refine a generic row variant without replacing it. */
86
- const TOOL_TITLE_KEYS = {
87
- cordis_package_inspect: "tool.title.inspect",
88
- cordis_runtime_inspect: "tool.title.inspect",
89
- cordis_run: "tool.title.runCordis",
90
- cordis_stop: "tool.title.stopCordis",
91
- cordis_undefine: "tool.title.removeCordis",
92
- pwsh: "tool.title.pwsh",
93
- read_image: "tool.title.readImage"
94
- };
95
- /**
96
- * Classify a tool name into its row variant.
97
- * @param toolName - wire tool name.
98
- * @returns matching variant, others when unknown.
99
- */
100
- function classifyTool(toolName) {
101
- return TOOL_VARIANTS[toolName] ?? "others";
102
- }
103
- /**
104
- * Flatten a settled result's content blocks to display text: text blocks
105
- * verbatim, other block shapes as pretty JSON. Empty content on a failed call
106
- * falls back to the structured error's `name: code` line.
107
- * @param node - the settled result node.
108
- * @returns the flattened result text (may be empty).
109
- */
110
- function resultText(node) {
111
- const parts = [];
112
- for (const block of node.content) if (block.type === "text") parts.push(block.text);
113
- else parts.push(JSON.stringify(block, null, 2));
114
- if (parts.length === 0 && node.error !== void 0) parts.push(`${node.error.name}: ${node.error.code}`);
115
- return parts.join("\n");
116
- }
117
- function parseArgs(argsRaw) {
118
- try {
119
- return JSON.parse(argsRaw);
120
- } catch {
121
- return;
122
- }
123
- }
124
- function firstLine(text) {
125
- const nl = text.indexOf("\n");
126
- return nl === -1 ? text : text.slice(0, nl);
127
- }
128
- function pickString(args, keys) {
129
- for (const key of keys) {
130
- const v = args[key];
131
- if (typeof v === "string" && v !== "") return v;
132
- }
133
- }
134
- /** Summary key preference per variant (args-derived; result-derived summaries are a ledger item). */
135
- const SUMMARY_KEYS = {
136
- bash: ["description", "command"],
137
- read: [
138
- "path",
139
- "file_path",
140
- "url"
141
- ],
142
- search: [
143
- "query",
144
- "pattern",
145
- "url"
146
- ],
147
- write: ["path", "file_path"],
148
- edit: ["path", "file_path"],
149
- code: ["description"],
150
- others: []
151
- };
152
50
  /**
153
51
  * Strip the workspace root from a workspace-rooted absolute path (display only).
154
52
  * @param text - the path to shorten.
@@ -161,80 +59,6 @@ window.__ModuleLoader__.load({
161
59
  if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\`)) return text.slice(root.length + 1);
162
60
  return text;
163
61
  }
164
- function deriveSummary(variant, argsRaw) {
165
- const parsed = parseArgs(argsRaw);
166
- if (typeof parsed !== "object" || parsed === null) return firstLine(argsRaw);
167
- const args = parsed;
168
- if (variant === "search" && Array.isArray(args.queries)) {
169
- const queries = args.queries.filter((query) => typeof query === "string" && query !== "");
170
- if (queries.length > 0) return queries.map(firstLine).join(", ");
171
- }
172
- const picked = pickString(args, SUMMARY_KEYS[variant]);
173
- if (picked !== void 0) return firstLine(picked);
174
- for (const v of Object.values(args)) if (typeof v === "string" && v !== "") return firstLine(v);
175
- return firstLine(argsRaw);
176
- }
177
- /** Path keys only — never `url` (web_fetch lands on the read variant). */
178
- const FILE_PATH_KEYS = ["path", "file_path"];
179
- /** File-tool variants whose summary may be an openable workspace path. */
180
- const FILE_PATH_VARIANTS = new Set([
181
- "read",
182
- "write",
183
- "edit"
184
- ]);
185
- function deriveFilePath(variant, argsRaw) {
186
- if (!FILE_PATH_VARIANTS.has(variant)) return void 0;
187
- const parsed = parseArgs(argsRaw);
188
- if (typeof parsed !== "object" || parsed === null) return void 0;
189
- const picked = pickString(parsed, FILE_PATH_KEYS);
190
- return picked === void 0 ? void 0 : firstLine(picked);
191
- }
192
- /**
193
- * Format one argument payload when its generic input body becomes visible.
194
- * @param variant - row presentation selected for the Tool name.
195
- * @param argsRaw - original argument JSON or incomplete raw text.
196
- * @returns display body, or null for empty input.
197
- */
198
- function formatToolBody(variant, argsRaw) {
199
- if (argsRaw === "") return null;
200
- const parsed = parseArgs(argsRaw);
201
- if (parsed === void 0) return argsRaw;
202
- if (variant === "code" && typeof parsed === "object" && parsed !== null) {
203
- const code = parsed.code;
204
- if (typeof code === "string" && code !== "") return code;
205
- }
206
- return JSON.stringify(parsed, null, 2);
207
- }
208
- /**
209
- * Derive the full row model from a frozen call slice.
210
- * @param toolName - wire tool name (dispatch-supplied; survives windowless results).
211
- * @param block - RunningToolCall or ToolResultNode off the snapshot caches.
212
- * @param cwd - session workspace root; workspace-rooted path summaries display relative to it.
213
- * @param home - host account home; a leftover POSIX home path displays as `~`.
214
- * @returns the row model.
215
- */
216
- function toolRowModel(toolName, block, cwd, home) {
217
- const variant = classifyTool(toolName);
218
- const done = "kind" in block;
219
- const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? "";
220
- const state = !done ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError ? "error" : "ok";
221
- const base = argsRaw === "" ? block.callId : abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home);
222
- const toolTitleKey = TOOL_TITLE_KEYS[toolName];
223
- const summary = variant === "others" && toolName !== "" && toolTitleKey === void 0 ? `${toolName} · ${base}` : base;
224
- const output = done ? resultText(block) || null : null;
225
- const errorSummary = state === "error" && output !== null ? firstLine(output) : null;
226
- const bodyRaw = argsRaw === "" ? null : argsRaw;
227
- return {
228
- variant,
229
- titleKey: toolTitleKey ?? VARIANT_TITLE_KEYS[variant],
230
- summary,
231
- filePath: deriveFilePath(variant, argsRaw),
232
- bodyRaw,
233
- output,
234
- errorSummary,
235
- state
236
- };
237
- }
238
62
  //#endregion
239
63
  //#region lib/types/client/tool/models/raw-tool-call.js
240
64
  const parsedCalls = /* @__PURE__ */ new WeakMap();
@@ -294,13 +118,17 @@ window.__ModuleLoader__.load({
294
118
  }
295
119
  //#endregion
296
120
  //#region lib/types/client/tool/models/read-card-model.js
121
+ /** Whether a model-supplied argument is a 1-based line position or count: an integer of at least 1. */
122
+ function positiveInteger$1(value) {
123
+ return typeof value === "number" && Number.isInteger(value) && value >= 1;
124
+ }
297
125
  function validReadCall(block) {
298
126
  const call = parsedToolCall(block);
299
127
  if (call?.name !== "read") return false;
300
128
  const { file_path: path, offset, limit } = call.args;
301
129
  if (typeof path !== "string" || path.trim() === "") return false;
302
- if (offset !== void 0 && (typeof offset !== "number" || !Number.isInteger(offset) || offset < 1)) return false;
303
- if (limit !== void 0 && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1)) return false;
130
+ if (offset !== void 0 && !positiveInteger$1(offset)) return false;
131
+ if (limit !== void 0 && !positiveInteger$1(limit)) return false;
304
132
  return true;
305
133
  }
306
134
  function readMeta(meta) {
@@ -331,6 +159,22 @@ window.__ModuleLoader__.load({
331
159
  };
332
160
  }
333
161
  /**
162
+ * The line one `read` call was about, from its arguments.
163
+ *
164
+ * `offset` is the read tool's own 1-based start line, so opening the path can
165
+ * land where the model looked. Available while the call is still running,
166
+ * unlike the persisted metadata, because the arguments carry it. The arguments
167
+ * are model-produced JSON: only an integer of at least 1 is a line, and a call
168
+ * whose `offset` is anything else names none.
169
+ * @param block - running or settled Tool block.
170
+ * @returns the 1-based line, or undefined when the call named none.
171
+ */
172
+ function readCallLine(block) {
173
+ if (!validReadCall(block)) return void 0;
174
+ const { offset } = parsedToolCall(block)?.args ?? {};
175
+ return positiveInteger$1(offset) ? offset : void 0;
176
+ }
177
+ /**
334
178
  * Derive a settled root read card after validating its persisted metadata and
335
179
  * model-facing read envelope.
336
180
  * @param block - running or settled Tool block.
@@ -947,6 +791,186 @@ window.__ModuleLoader__.load({
947
791
  };
948
792
  }
949
793
  //#endregion
794
+ //#region lib/types/client/tool/models/tool-call-model.js
795
+ /** Locale key per generic row variant. */
796
+ const VARIANT_TITLE_KEYS = {
797
+ search: "tool.title.search",
798
+ read: "tool.title.read",
799
+ bash: "tool.title.bash",
800
+ write: "tool.title.write",
801
+ edit: "tool.title.edit",
802
+ code: "tool.title.code",
803
+ others: "tool.title.generic"
804
+ };
805
+ /**
806
+ * Known tool name -> variant.
807
+ *
808
+ * `cordis_define` is deliberately absent: ui-cordis registers a keyed
809
+ * `tool.call.toolview` entry for it, and a keyed hit REPLACES the generic row
810
+ * (this table is only reached through GenericToolCard, the dispatch fallback in
811
+ * ToolCallTree). An entry here would be unreachable, and a second title for the
812
+ * same call would be a second answer to a question the card already owns.
813
+ */
814
+ const TOOL_VARIANTS = {
815
+ bash: "bash",
816
+ pwsh: "bash",
817
+ read: "read",
818
+ read_image: "read",
819
+ web_fetch: "read",
820
+ web_search: "search",
821
+ grep: "search",
822
+ glob: "search",
823
+ write: "write",
824
+ edit: "edit",
825
+ run_code: "code",
826
+ cordis_package_inspect: "read",
827
+ cordis_runtime_inspect: "read",
828
+ cordis_run: "others",
829
+ cordis_stop: "others",
830
+ cordis_undefine: "others"
831
+ };
832
+ /** Tool-owned titles that refine a generic row variant without replacing it. */
833
+ const TOOL_TITLE_KEYS = {
834
+ cordis_package_inspect: "tool.title.inspect",
835
+ cordis_runtime_inspect: "tool.title.inspect",
836
+ cordis_run: "tool.title.runCordis",
837
+ cordis_stop: "tool.title.stopCordis",
838
+ cordis_undefine: "tool.title.removeCordis",
839
+ pwsh: "tool.title.pwsh",
840
+ read_image: "tool.title.readImage"
841
+ };
842
+ /**
843
+ * Classify a tool name into its row variant.
844
+ * @param toolName - wire tool name.
845
+ * @returns matching variant, others when unknown.
846
+ */
847
+ function classifyTool(toolName) {
848
+ return TOOL_VARIANTS[toolName] ?? "others";
849
+ }
850
+ /**
851
+ * Flatten a settled result's content blocks to display text: text blocks
852
+ * verbatim, other block shapes as pretty JSON. Empty content on a failed call
853
+ * falls back to the structured error's `name: code` line.
854
+ * @param node - the settled result node.
855
+ * @returns the flattened result text (may be empty).
856
+ */
857
+ function resultText(node) {
858
+ const parts = [];
859
+ for (const block of node.content) if (block.type === "text") parts.push(block.text);
860
+ else parts.push(JSON.stringify(block, null, 2));
861
+ if (parts.length === 0 && node.error !== void 0) parts.push(`${node.error.name}: ${node.error.code}`);
862
+ return parts.join("\n");
863
+ }
864
+ function parseArgs(argsRaw) {
865
+ try {
866
+ return JSON.parse(argsRaw);
867
+ } catch {
868
+ return;
869
+ }
870
+ }
871
+ function firstLine(text) {
872
+ const nl = text.indexOf("\n");
873
+ return nl === -1 ? text : text.slice(0, nl);
874
+ }
875
+ function pickString(args, keys) {
876
+ for (const key of keys) {
877
+ const v = args[key];
878
+ if (typeof v === "string" && v !== "") return v;
879
+ }
880
+ }
881
+ /** Summary key preference per variant (args-derived; result-derived summaries are a ledger item). */
882
+ const SUMMARY_KEYS = {
883
+ bash: ["description", "command"],
884
+ read: [
885
+ "path",
886
+ "file_path",
887
+ "url"
888
+ ],
889
+ search: [
890
+ "query",
891
+ "pattern",
892
+ "url"
893
+ ],
894
+ write: ["path", "file_path"],
895
+ edit: ["path", "file_path"],
896
+ code: ["description"],
897
+ others: []
898
+ };
899
+ function deriveSummary(variant, argsRaw) {
900
+ const parsed = parseArgs(argsRaw);
901
+ if (typeof parsed !== "object" || parsed === null) return firstLine(argsRaw);
902
+ const args = parsed;
903
+ if (variant === "search" && Array.isArray(args.queries)) {
904
+ const queries = args.queries.filter((query) => typeof query === "string" && query !== "");
905
+ if (queries.length > 0) return queries.map(firstLine).join(", ");
906
+ }
907
+ const picked = pickString(args, SUMMARY_KEYS[variant]);
908
+ if (picked !== void 0) return firstLine(picked);
909
+ for (const v of Object.values(args)) if (typeof v === "string" && v !== "") return firstLine(v);
910
+ return firstLine(argsRaw);
911
+ }
912
+ /** Path keys only — never `url` (web_fetch lands on the read variant). */
913
+ const FILE_PATH_KEYS = ["path", "file_path"];
914
+ /** File-tool variants whose summary may be an openable workspace path. */
915
+ const FILE_PATH_VARIANTS = new Set([
916
+ "read",
917
+ "write",
918
+ "edit"
919
+ ]);
920
+ function deriveFilePath(variant, argsRaw) {
921
+ if (!FILE_PATH_VARIANTS.has(variant)) return void 0;
922
+ const parsed = parseArgs(argsRaw);
923
+ if (typeof parsed !== "object" || parsed === null) return void 0;
924
+ const picked = pickString(parsed, FILE_PATH_KEYS);
925
+ return picked === void 0 ? void 0 : firstLine(picked);
926
+ }
927
+ /**
928
+ * Format one argument payload when its generic input body becomes visible.
929
+ * @param variant - row presentation selected for the Tool name.
930
+ * @param argsRaw - original argument JSON or incomplete raw text.
931
+ * @returns display body, or null for empty input.
932
+ */
933
+ function formatToolBody(variant, argsRaw) {
934
+ if (argsRaw === "") return null;
935
+ const parsed = parseArgs(argsRaw);
936
+ if (parsed === void 0) return argsRaw;
937
+ if (variant === "code" && typeof parsed === "object" && parsed !== null) {
938
+ const code = parsed.code;
939
+ if (typeof code === "string" && code !== "") return code;
940
+ }
941
+ return JSON.stringify(parsed, null, 2);
942
+ }
943
+ /**
944
+ * Derive the full row model from a frozen call slice.
945
+ * @param toolName - wire tool name (dispatch-supplied; survives windowless results).
946
+ * @param block - RunningToolCall or ToolResultNode off the snapshot caches.
947
+ * @param cwd - session workspace root; workspace-rooted path summaries display relative to it.
948
+ * @param home - host account home; a leftover POSIX home path displays as `~`.
949
+ * @returns the row model.
950
+ */
951
+ function toolRowModel(toolName, block, cwd, home) {
952
+ const variant = classifyTool(toolName);
953
+ const done = "kind" in block;
954
+ const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? "";
955
+ const state = !done ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError ? "error" : "ok";
956
+ const base = argsRaw === "" ? block.callId : abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home);
957
+ const toolTitleKey = TOOL_TITLE_KEYS[toolName];
958
+ const summary = variant === "others" && toolName !== "" && toolTitleKey === void 0 ? `${toolName} · ${base}` : base;
959
+ const output = done ? resultText(block) || null : null;
960
+ const errorSummary = state === "error" && output !== null ? firstLine(output) : null;
961
+ const bodyRaw = argsRaw === "" ? null : argsRaw;
962
+ return {
963
+ variant,
964
+ titleKey: toolTitleKey ?? VARIANT_TITLE_KEYS[variant],
965
+ summary,
966
+ filePath: deriveFilePath(variant, argsRaw),
967
+ bodyRaw,
968
+ output,
969
+ errorSummary,
970
+ state
971
+ };
972
+ }
973
+ //#endregion
950
974
  //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
951
975
  function r(e) {
952
976
  var t, f, n = "";
@@ -1054,13 +1078,13 @@ window.__ModuleLoader__.load({
1054
1078
  }
1055
1079
  //#endregion
1056
1080
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
1057
- const css$4 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:12px;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))}";
1058
- const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1059
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
1081
+ const css$3 = ".fsXYAq_card{border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);border-radius:12px;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))}";
1082
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
1083
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1060
1084
  const tag = document.createElement("style");
1061
1085
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1062
- tag.dataset.pluginCss = tagId$4;
1063
- tag.textContent = css$4;
1086
+ tag.dataset.pluginCss = tagId$3;
1087
+ tag.textContent = css$3;
1064
1088
  document.head.appendChild(tag);
1065
1089
  }
1066
1090
  var AskQuestionCard_module_css_default = {
@@ -1117,13 +1141,13 @@ window.__ModuleLoader__.load({
1117
1141
  }
1118
1142
  //#endregion
1119
1143
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
1120
- const css$3 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_row{position:relative;overflow:hidden}.o3BgMG_root[data-state=running] .o3BgMG_row:after{content:\"\";background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite o3BgMG_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes o3BgMG_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.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}.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;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}.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;overflow:hidden}.o3BgMG_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.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_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);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);border-radius:12px;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{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.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_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.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}";
1121
- const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1122
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1144
+ const css$2 = ".o3BgMG_root{flex-direction:column;display:flex}.o3BgMG_row{position:relative;overflow:hidden}.o3BgMG_root[data-state=running] .o3BgMG_row:after{content:\"\";background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite o3BgMG_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes o3BgMG_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.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}.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;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}.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;overflow:hidden}.o3BgMG_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.o3BgMG_errorSummary{color:var(--dsw-alias-state-error-primary)}.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_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);background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);border-radius:12px;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{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.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_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.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}";
1145
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
1146
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1123
1147
  const tag = document.createElement("style");
1124
1148
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1125
- tag.dataset.pluginCss = tagId$3;
1126
- tag.textContent = css$3;
1149
+ tag.dataset.pluginCss = tagId$2;
1150
+ tag.textContent = css$2;
1127
1151
  document.head.appendChild(tag);
1128
1152
  }
1129
1153
  var ToolRow_module_css_default = {
@@ -1180,7 +1204,7 @@ window.__ModuleLoader__.load({
1180
1204
  default: return null;
1181
1205
  }
1182
1206
  }
1183
- function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, onOpenFile, inspect }) {
1207
+ function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, filePathLine, onOpenFile, inspect }) {
1184
1208
  const [expanded, setExpanded] = (0, react.useState)(false);
1185
1209
  const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
1186
1210
  const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
@@ -1219,7 +1243,9 @@ window.__ModuleLoader__.load({
1219
1243
  };
1220
1244
  const openFile = (event) => {
1221
1245
  event.stopPropagation();
1222
- if (filePath !== void 0) onOpenFile?.(filePath);
1246
+ if (filePath === void 0 || onOpenFile === void 0) return;
1247
+ if (filePathLine === void 0) onOpenFile(filePath);
1248
+ else onOpenFile(filePath, { line: filePathLine });
1223
1249
  };
1224
1250
  const fileLinkKeyDown = (event) => {
1225
1251
  if (event.key === "Enter" || event.key === " ") event.stopPropagation();
@@ -1403,13 +1429,13 @@ window.__ModuleLoader__.load({
1403
1429
  }
1404
1430
  //#endregion
1405
1431
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/ToolCallTree.module.css.mjs
1406
- const css$2 = ".ztWv_q_callRow{border-radius:6px}.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}";
1407
- const tagId$2 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1408
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1432
+ const css$1 = ".ztWv_q_callRow{border-radius:6px}.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}";
1433
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1434
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1409
1435
  const tag = document.createElement("style");
1410
1436
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1411
- tag.dataset.pluginCss = tagId$2;
1412
- tag.textContent = css$2;
1437
+ tag.dataset.pluginCss = tagId$1;
1438
+ tag.textContent = css$1;
1413
1439
  document.head.appendChild(tag);
1414
1440
  }
1415
1441
  var ToolCallTree_module_css_default = {
@@ -1424,7 +1450,7 @@ window.__ModuleLoader__.load({
1424
1450
  return "kind" in node ? node.call?.name ?? "" : node.name;
1425
1451
  }
1426
1452
  /** One atomic call dispatched through the Tool-owned keyed slot. */
1427
- const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, selected, cwd, home, inspectCall, loadImage, t, children }) {
1453
+ const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, cwd, home, inspectCall, loadImage, t, children }) {
1428
1454
  const owner = (0, react.useMemo)(() => ({
1429
1455
  callId,
1430
1456
  toolName,
@@ -1450,7 +1476,6 @@ window.__ModuleLoader__.load({
1450
1476
  className: ToolCallTree_module_css_default.callRow,
1451
1477
  "data-chat-anchor-key": `call:${callId}`,
1452
1478
  "data-chat-call-id": callId,
1453
- "data-selected": selected || void 0,
1454
1479
  children: [renderSlot("tool.call.toolview", owner, {
1455
1480
  entryKey: toolName,
1456
1481
  fallback: (0, react_jsx_runtime.jsx)(GenericToolCard, {
@@ -1460,14 +1485,13 @@ window.__ModuleLoader__.load({
1460
1485
  }), children]
1461
1486
  });
1462
1487
  });
1463
- const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, selectedCallId, cwd, home, openFile, inspectCall, loadImage, t }) {
1488
+ const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, cwd, home, openFile, inspectCall, loadImage, t }) {
1464
1489
  return (0, react_jsx_runtime.jsx)(ToolCall, {
1465
1490
  renderSlot,
1466
1491
  callId: block.callId,
1467
1492
  toolName: callName(block),
1468
1493
  block,
1469
1494
  openFile,
1470
- selected: block.callId === selectedCallId,
1471
1495
  cwd,
1472
1496
  home,
1473
1497
  inspectCall,
@@ -1479,7 +1503,6 @@ window.__ModuleLoader__.load({
1479
1503
  children: block.subCalls.map((child) => (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1480
1504
  renderSlot,
1481
1505
  block: child,
1482
- selectedCallId,
1483
1506
  cwd,
1484
1507
  home,
1485
1508
  openFile,
@@ -1496,13 +1519,12 @@ window.__ModuleLoader__.load({
1496
1519
  * @param props - whole-Tool owner data and the Tool-owned child-slot share.
1497
1520
  * @returns the Tool call tree.
1498
1521
  */
1499
- function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, loadImage, useHostInfo, t }) {
1522
+ function ToolCallTree({ renderSlot, node, cwd, openFile, inspectCall, loadImage, useHostInfo, t }) {
1500
1523
  const home = useHostInfo((info) => info.home);
1501
1524
  const block = node.data.root;
1502
1525
  return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1503
1526
  renderSlot,
1504
1527
  block,
1505
- selectedCallId,
1506
1528
  cwd,
1507
1529
  home,
1508
1530
  openFile,
@@ -1512,92 +1534,6 @@ window.__ModuleLoader__.load({
1512
1534
  });
1513
1535
  }
1514
1536
  //#endregion
1515
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/ToolDetails.module.css.mjs
1516
- const css$1 = ".xDAfVq_description{color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);margin:0 0 6px}.xDAfVq_cardBody{margin:0}.xDAfVq_recovery{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xs-13);margin:6px 0 0}.xDAfVq_code{background:var(--dsw-alias-markdown-code-block);font-family:var(--ds-font-family-code);color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:12px;margin:0;padding:16px;font-size:13px;line-height:22px}.xDAfVq_code[data-error]{color:var(--dsw-alias-state-error-primary)}.xDAfVq_read,.xDAfVq_web{margin:0}.xDAfVq_empty{color:var(--dsw-alias-label-tertiary);padding:8px 0;font-size:13px;line-height:20px}";
1517
- const tagId$1 = "@deepseek-ai/dsh-client-ui-tool/ToolDetails.module.css";
1518
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1519
- const tag = document.createElement("style");
1520
- tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
1521
- tag.dataset.pluginCss = tagId$1;
1522
- tag.textContent = css$1;
1523
- document.head.appendChild(tag);
1524
- }
1525
- var ToolDetails_module_css_default = {
1526
- "cardBody": "xDAfVq_cardBody",
1527
- "code": "xDAfVq_code",
1528
- "description": "xDAfVq_description",
1529
- "empty": "xDAfVq_empty",
1530
- "read": "xDAfVq_read",
1531
- "recovery": "xDAfVq_recovery",
1532
- "web": "xDAfVq_web"
1533
- };
1534
- //#endregion
1535
- //#region lib/types/client/tool/ToolDetails.js
1536
- /** Card-aware output body for the selected Tool call in details. */
1537
- /**
1538
- * Render the selected Tool call's structured output when its raw fields form a
1539
- * supported card, otherwise preserve the flattened result text.
1540
- * @param props - selected call slice, workspace root, host home, and locale seat.
1541
- * @returns the details output body.
1542
- */
1543
- function ToolDetails({ block, cwd, useHostInfo, t }) {
1544
- const home = useHostInfo((info) => info.home);
1545
- const terminalModel = terminalCardModel(block, cwd);
1546
- if (terminalModel !== null) {
1547
- const terminal = localizeTerminalCardModel(terminalModel, t);
1548
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [terminal.description !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1549
- className: ToolDetails_module_css_default.description,
1550
- children: terminal.description
1551
- }) : null, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
1552
- ...terminal.card,
1553
- labels: terminalBlockLabels(t),
1554
- className: ToolDetails_module_css_default.cardBody
1555
- })] });
1556
- }
1557
- const read = readCardModel(block, cwd, home);
1558
- if (read !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
1559
- ...read,
1560
- labels: readBlockLabels(t),
1561
- className: ToolDetails_module_css_default.read
1562
- });
1563
- const diff = diffCardModel(block);
1564
- if (diff !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
1565
- ...diff.card,
1566
- labels: diffBlockLabels(t),
1567
- className: ToolDetails_module_css_default.cardBody
1568
- });
1569
- const search = searchCardModel(block);
1570
- if (search !== null) return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SearchBlock, {
1571
- ...search.card,
1572
- labels: searchBlockLabels(t),
1573
- className: ToolDetails_module_css_default.cardBody
1574
- }), search.recovery !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1575
- className: ToolDetails_module_css_default.recovery,
1576
- children: search.recovery
1577
- }) : null] });
1578
- const web = webCardModel(block);
1579
- if (web !== null) {
1580
- const body = "kind" in block ? resultText(block) : "";
1581
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
1582
- ...web,
1583
- labels: webBlockLabels(t),
1584
- className: ToolDetails_module_css_default.web
1585
- }), body !== "" ? (0, react_jsx_runtime.jsx)("pre", {
1586
- className: ToolDetails_module_css_default.code,
1587
- children: body
1588
- }) : null] });
1589
- }
1590
- if (!("kind" in block)) return (0, react_jsx_runtime.jsx)("div", {
1591
- className: ToolDetails_module_css_default.empty,
1592
- children: t("details.running")
1593
- });
1594
- return (0, react_jsx_runtime.jsx)("pre", {
1595
- className: ToolDetails_module_css_default.code,
1596
- "data-error": block.isError || void 0,
1597
- children: resultText(block)
1598
- });
1599
- }
1600
- //#endregion
1601
1537
  //#region lib/types/client/locale.js
1602
1538
  /** Locale namespace supplied by the conversation owner to Tool renderers. */
1603
1539
  const CONVERSATION_NS = "conversation";
@@ -2003,11 +1939,15 @@ window.__ModuleLoader__.load({
2003
1939
  //#endregion
2004
1940
  //#region lib/types/client/tool/toolviews/read-row.js
2005
1941
  /**
2006
- * Lets users expand a completed read result and open its reported path.
1942
+ * Lets users expand a completed read result and open its reported path at the
1943
+ * line the call started from.
2007
1944
  */
2008
1945
  function ReadRow(props) {
2009
1946
  const { block, cwd, home } = props;
2010
- return readFamilyRow(props, { read: readCardModel(block, cwd, home) });
1947
+ return readFamilyRow(props, {
1948
+ read: readCardModel(block, cwd, home),
1949
+ filePathLine: readCallLine(block)
1950
+ });
2011
1951
  }
2012
1952
  /** Registers the read tool's conversation row. */
2013
1953
  const readToolview = {
@@ -2435,11 +2375,6 @@ window.__ModuleLoader__.load({
2435
2375
  } },
2436
2376
  inject: toolInject
2437
2377
  }, ToolCallTree));
2438
- ctx.slots.inject("conversation.details.tool", () => ctx.slots.register({
2439
- name: "conversation.details.tool",
2440
- locale: CONVERSATION_NS,
2441
- inject: toolInject
2442
- }, ToolDetails));
2443
2378
  ctx.plugin(bashToolviewSample);
2444
2379
  ctx.plugin(readToolview);
2445
2380
  ctx.plugin(readImageToolview);
@@ -1,7 +1,7 @@
1
1
  /** Tool UI slot declarations and their composed component props. */
2
2
  import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { RemoteHostFacts } from '@deepseek-ai/dsh-api-remotes/client';
4
- import type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-chat/client';
4
+ import type { OpenFileOptions, ToolCallBlock } from '@deepseek-ai/dsh-client-ui-chat/client';
5
5
  import type { MessageImageLoader, MessageImageSource } from '@deepseek-ai/dsh-client-ui-conversation/client';
6
6
  declare module '@deepseek-ai/dsh-client-ui-slots' {
7
7
  interface SlotMap {
@@ -65,8 +65,11 @@ export interface ToolCallOwnerProps {
65
65
  cwd?: string | undefined;
66
66
  /** Host account home; POSIX home-rooted summaries display as `~`. */
67
67
  home?: string | undefined;
68
- /** Open a Tool argument path through the Host. */
69
- openFile: (path: string) => void;
68
+ /**
69
+ * Open a Tool argument path. A view that knows which line the call was about
70
+ * passes it, and the opened surface lands there.
71
+ */
72
+ openFile: (path: string, options?: OpenFileOptions) => void;
70
73
  /**
71
74
  * Session-authorized image loader for the `tool.call.images` slot, supplied
72
75
  * by the chat node that owns this call. A composed chat node always
@@ -94,6 +97,4 @@ export type ToolHostInfoInjected = {
94
97
  };
95
98
  /** Full props of the Tool call-tree renderer registered as a `tool-call` Chat Node. */
96
99
  export type ToolTreeProps = PropsRuntime<'conversation.chat.node', 'tool-call'> & PropsRenderSlots<'tool.call.toolview'> & PropsLocale<'conversation'> & InjectFace<ToolHostInfoInjected>;
97
- /** Full props of the selected Tool output renderer in the details panel. */
98
- export type ToolDetailsProps = PropsRuntime<'conversation.details.tool'> & PropsLocale<'conversation'> & InjectFace<ToolHostInfoInjected>;
99
100
  //# sourceMappingURL=slots.d.ts.map
@@ -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 { ToolCallOwnerProps, ToolCallViewProps, ToolDetailsProps, ToolHostInfoInjected, ToolTreeProps, } from './contract/slots.ts';
3
+ export type { ToolCallOwnerProps, ToolCallViewProps, ToolHostInfoInjected, ToolTreeProps, } from './contract/slots.ts';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -5,5 +5,5 @@ import type { ToolTreeProps } from '../contract/slots.ts';
5
5
  * @param props - whole-Tool owner data and the Tool-owned child-slot share.
6
6
  * @returns the Tool call tree.
7
7
  */
8
- export declare function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, loadImage, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
8
+ export declare function ToolCallTree({ renderSlot, node, cwd, openFile, inspectCall, loadImage, useHostInfo, t, }: ToolTreeProps): import("react").JSX.Element;
9
9
  //# sourceMappingURL=ToolCallTree.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { PropsRenderSlots, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { OpenFileOptions } from '@deepseek-ai/dsh-client-ui-chat/client';
3
4
  import type { MessageImageLoader } from '@deepseek-ai/dsh-client-ui-conversation/client';
4
5
  import { type DiffCardModel } from '../models/diff-card-model.ts';
5
6
  import { type ReadCardModel } from '../models/read-card-model.ts';
@@ -60,13 +61,15 @@ export interface ToolRowProps {
60
61
  * renders as a hover-underline link that opens the host default app.
61
62
  */
62
63
  filePath?: string | undefined;
63
- /** Open the path with the host OS default application (already cwd-resolved). */
64
- onOpenFile?: ((path: string) => void) | undefined;
64
+ /** 1-based line the call was about; absent = open the file at its beginning. */
65
+ filePathLine?: number | undefined;
66
+ /** Open the path (already cwd-resolved), landing on `filePathLine` when given. */
67
+ onOpenFile?: ((path: string, options?: OpenFileOptions) => void) | undefined;
65
68
  /**
66
69
  * Jump to this call in the trajectory view: a hover-revealed Inspect pill
67
70
  * over the expanded body. Absent = no affordance.
68
71
  */
69
72
  inspect?: (() => void) | undefined;
70
73
  }
71
- export declare function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, onOpenFile, inspect, }: ToolRowProps): import("react").JSX.Element;
74
+ export declare function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, image, renderSlot, loadImage, search, web, state, filePath, filePathLine, onOpenFile, inspect, }: ToolRowProps): import("react").JSX.Element;
72
75
  //# sourceMappingURL=ToolRow.d.ts.map
@@ -1,6 +1,6 @@
1
1
  /** Pure image-card derivation from raw result content and metadata. @module */
2
2
  import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
3
- import { type ToolCallBlock } from './tool-call-model.ts';
3
+ import type { ToolCallBlock } from './tool-call-model.ts';
4
4
  /**
5
5
  * The image-card material one settled call contributes: the display label plus
6
6
  * the durable references the attachment slot renders as a gallery.
@@ -1,6 +1,6 @@
1
1
  /** Pure read-card derivation from raw result content and metadata. @module */
2
2
  import type { ReadBlockProps } from '@deepseek-ai/dsh-client-ui-primitives';
3
- import { type ToolCallBlock } from './tool-call-model.ts';
3
+ import type { ToolCallBlock } from './tool-call-model.ts';
4
4
  /**
5
5
  * Content lines the chat row's resident read body shows before collapsing the
6
6
  * middle — half the primitive's own default, which the details panel keeps. A
@@ -18,6 +18,18 @@ export declare const CHAT_READ_MAX_LINES = 8;
18
18
  * site.
19
19
  */
20
20
  export type ReadCardModel = Pick<ReadBlockProps, 'label' | 'lines' | 'totalLines' | 'lang'>;
21
+ /**
22
+ * The line one `read` call was about, from its arguments.
23
+ *
24
+ * `offset` is the read tool's own 1-based start line, so opening the path can
25
+ * land where the model looked. Available while the call is still running,
26
+ * unlike the persisted metadata, because the arguments carry it. The arguments
27
+ * are model-produced JSON: only an integer of at least 1 is a line, and a call
28
+ * whose `offset` is anything else names none.
29
+ * @param block - running or settled Tool block.
30
+ * @returns the 1-based line, or undefined when the call named none.
31
+ */
32
+ export declare function readCallLine(block: ToolCallBlock): number | undefined;
21
33
  /**
22
34
  * Derive a settled root read card after validating its persisted metadata and
23
35
  * model-facing read envelope.
@@ -56,13 +56,6 @@ export interface ToolRowModel {
56
56
  * @returns the flattened result text (may be empty).
57
57
  */
58
58
  export declare function resultText(node: ToolResultNode): string;
59
- /**
60
- * Strip the workspace root from a workspace-rooted absolute path (display only).
61
- * @param text - the path to shorten.
62
- * @param cwd - session workspace root; absent or empty leaves the path unchanged.
63
- * @returns the path relative to the workspace root, or unchanged when it is not rooted there.
64
- */
65
- export declare function relativizeToCwd(text: string, cwd: string | undefined): string;
66
59
  /**
67
60
  * Format one argument payload when its generic input body becomes visible.
68
61
  * @param variant - row presentation selected for the Tool name.
@@ -10,10 +10,11 @@ export type ReadFamilyRowProps = ToolCallViewProps & {
10
10
  export type ReadImageRowProps = ReadFamilyRowProps & PropsRenderSlots<'tool.call.images'>;
11
11
  /**
12
12
  * The card material one read-family row contributes: exactly the ToolRow card
13
- * props that row owns. `read` supplies `read`; `read_image` supplies `image`
14
- * together with the slot dispatcher and loader that draw it.
13
+ * props that row owns. `read` supplies `read` and the line its call named;
14
+ * `read_image` supplies `image` together with the slot dispatcher and loader
15
+ * that draw it.
15
16
  */
16
- export type ReadFamilyCard = Pick<ToolRowProps, 'read' | 'image' | 'renderSlot' | 'loadImage'>;
17
+ export type ReadFamilyCard = Pick<ToolRowProps, 'read' | 'image' | 'renderSlot' | 'loadImage' | 'filePathLine'>;
17
18
  /**
18
19
  * Compose a read-family row: the shared chrome and model-derived fields, plus the
19
20
  * caller's card material.
@@ -3,7 +3,8 @@ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
3
3
  import type { ToolCallViewProps } from '../../contract/slots.ts';
4
4
  type ReadRowProps = ToolCallViewProps & PropsLocale<'conversation'>;
5
5
  /**
6
- * Lets users expand a completed read result and open its reported path.
6
+ * Lets users expand a completed read result and open its reported path at the
7
+ * line the call started from.
7
8
  */
8
9
  export declare function ReadRow(props: ReadRowProps): import("react").ReactNode;
9
10
  /** Registers the read tool's conversation row. */
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.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -37,9 +37,6 @@
37
37
  }
38
38
  },
39
39
  "license": "MIT",
40
- "dependencies": {
41
- "clsx": "^2.0.0"
42
- },
43
40
  "peerDependencies": {
44
41
  "@deepseek-ai/cordis": "^4.0.2"
45
42
  },
@@ -48,28 +45,29 @@
48
45
  "@types/react": "~18.3.1",
49
46
  "react": "^18.2.0",
50
47
  "react-dom": "^18.2.0",
51
- "@deepseek-ai/dsh-spill-policy": "^0.1.3-alpha.2",
52
- "@deepseek-ai/dsh-agent": "^0.1.3-alpha.2",
53
- "@deepseek-ai/dsh-llm": "^0.1.3-alpha.2",
54
- "@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
55
- "@deepseek-ai/dsh-system-prompt": "^0.1.3-alpha.2",
56
- "@deepseek-ai/dsh-tools": "^0.1.3-alpha.2",
57
- "@deepseek-ai/dsh-spill": "^0.1.3-alpha.2",
58
- "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.3-alpha.2",
48
+ "clsx": "^2.0.0",
49
+ "@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
50
+ "@deepseek-ai/dsh-spill-policy": "^0.1.5-alpha.2",
51
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
52
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
53
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
54
+ "@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
55
+ "@deepseek-ai/dsh-spill": "^0.1.5-alpha.2",
56
+ "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.5-alpha.2",
59
57
  "@deepseek-ai/cordis": "^4.0.2",
60
- "@deepseek-ai/dsh-api-remotes": "^0.1.3-alpha.2",
61
- "@deepseek-ai/dsh-client-connection": "^0.1.3-alpha.2",
62
- "@deepseek-ai/dsh-client-locale": "^0.1.3-alpha.2",
63
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.3-alpha.2",
64
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.3-alpha.2",
65
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.3-alpha.2",
66
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.3-alpha.2",
67
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.3-alpha.2",
68
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.3-alpha.2",
69
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.3-alpha.2",
70
- "@deepseek-ai/dsh-attachment": "^0.1.3-alpha.2",
71
- "@deepseek-ai/dsh-client-ui-session": "^0.1.3-alpha.2",
72
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.3-alpha.2"
58
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-alpha.2",
59
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.2",
60
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
61
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2",
62
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.2",
63
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-alpha.2",
64
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
65
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.2",
66
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.2",
67
+ "@deepseek-ai/dsh-util-workspace-path": "^0.1.5-alpha.2",
68
+ "@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2",
69
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-alpha.2",
70
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2"
73
71
  },
74
72
  "files": [
75
73
  "lib/index.js",
@@ -1,9 +0,0 @@
1
- import type { ToolDetailsProps } from '../contract/slots.ts';
2
- /**
3
- * Render the selected Tool call's structured output when its raw fields form a
4
- * supported card, otherwise preserve the flattened result text.
5
- * @param props - selected call slice, workspace root, host home, and locale seat.
6
- * @returns the details output body.
7
- */
8
- export declare function ToolDetails({ block, cwd, useHostInfo, t, }: Pick<ToolDetailsProps, 'block' | 'cwd' | 'useHostInfo' | 't'>): import("react").JSX.Element;
9
- //# sourceMappingURL=ToolDetails.d.ts.map