@deepseek-ai/dsh-client-ui-tool 0.1.1-rc.2 → 0.1.2-alpha.3
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 +2 -2
- package/README.md +79 -17
- package/README.zh.md +80 -18
- package/lib/client.js +898 -432
- package/lib/types/client/apply.d.ts +3 -2
- package/lib/types/client/contract/slots.d.ts +13 -8
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/tool/ToolCallTree.d.ts +1 -1
- package/lib/types/client/tool/ToolDetails.d.ts +3 -3
- package/lib/types/client/tool/components/AskQuestionCard.d.ts +11 -0
- package/lib/types/client/tool/components/ToolRow.d.ts +10 -34
- package/lib/types/client/tool/models/ask-question-card-model.d.ts +22 -0
- package/lib/types/client/tool/models/diff-card-model.d.ts +7 -28
- package/lib/types/client/tool/models/primitive-labels.d.ts +36 -0
- package/lib/types/client/tool/models/raw-tool-call.d.ts +27 -0
- package/lib/types/client/tool/models/read-card-model.d.ts +4 -19
- package/lib/types/client/tool/models/search-card-model.d.ts +9 -75
- package/lib/types/client/tool/models/terminal-card-model.d.ts +46 -36
- package/lib/types/client/tool/models/tool-call-model.d.ts +31 -7
- package/lib/types/client/tool/models/web-card-model.d.ts +9 -34
- package/lib/types/client/tool/toolviews/ask-question-row.d.ts +2 -11
- package/lib/types/client/tool/toolviews/bash-sample.d.ts +2 -14
- package/lib/types/client/tool/toolviews/file-mutation-row.d.ts +2 -17
- package/lib/types/client/tool/toolviews/read-row.d.ts +2 -12
- package/lib/types/client/tool/toolviews/search-row.d.ts +2 -19
- package/lib/types/client/tool/toolviews/todo-row.d.ts +2 -13
- package/lib/types/client/tool/toolviews/web-row.d.ts +2 -14
- package/package.json +17 -19
package/lib/client.js
CHANGED
|
@@ -7,25 +7,52 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
-
|
|
11
|
-
|
|
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
|
+
/** Whether a path uses a Windows drive or UNC prefix. */
|
|
16
|
+
function isWindowsStylePath(value) {
|
|
17
|
+
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
|
|
18
|
+
}
|
|
12
19
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* a terminal card gets its expanded body from the views instead, through
|
|
18
|
-
* `terminalCardModel` in terminal-card-model.ts.
|
|
20
|
+
* Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
|
|
21
|
+
* @param cwd - Session Workspace root, when known.
|
|
22
|
+
* @param path - Absolute or Workspace-relative path.
|
|
23
|
+
* @returns an absolute path when a Workspace root is available, otherwise the original path.
|
|
19
24
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
function resolveWorkspacePath(cwd, path) {
|
|
26
|
+
if (path.startsWith("/") || isWindowsStylePath(path)) return path;
|
|
27
|
+
if (cwd === void 0 || cwd === "") return path;
|
|
28
|
+
return `${cwd.replace(/[/\\]+$/, "")}/${path.replace(/^[/\\]+/, "")}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Abbreviate a POSIX home directory for display.
|
|
32
|
+
* @param path - Absolute or already-short display path.
|
|
33
|
+
* @param home - Host account home; absent skips abbreviation.
|
|
34
|
+
* @returns `~` or `~/…` for the POSIX home and its descendants, otherwise `path`.
|
|
35
|
+
*/
|
|
36
|
+
function abbreviateHomePath(path, home) {
|
|
37
|
+
if (home === void 0 || home === "") return path;
|
|
38
|
+
if (isWindowsStylePath(path) || isWindowsStylePath(home)) return path;
|
|
39
|
+
const root = home.replace(/\/+$/, "");
|
|
40
|
+
if (root === "" || root === "/") return path;
|
|
41
|
+
if (path.replace(/\/+$/, "") === root) return "~";
|
|
42
|
+
if (path.startsWith(`${root}/`)) return `~${path.slice(root.length)}`;
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region lib/types/client/tool/models/tool-call-model.js
|
|
47
|
+
/** Locale key per generic row variant. */
|
|
48
|
+
const VARIANT_TITLE_KEYS = {
|
|
49
|
+
search: "tool.title.search",
|
|
50
|
+
read: "tool.title.read",
|
|
51
|
+
bash: "tool.title.bash",
|
|
52
|
+
write: "tool.title.write",
|
|
53
|
+
edit: "tool.title.edit",
|
|
54
|
+
code: "tool.title.code",
|
|
55
|
+
others: "tool.title.generic"
|
|
29
56
|
};
|
|
30
57
|
/**
|
|
31
58
|
* Known tool name -> variant.
|
|
@@ -54,13 +81,13 @@ window.__ModuleLoader__.load({
|
|
|
54
81
|
cordis_undefine: "others"
|
|
55
82
|
};
|
|
56
83
|
/** Tool-owned titles that refine a generic row variant without replacing it. */
|
|
57
|
-
const
|
|
58
|
-
cordis_package_inspect: "
|
|
59
|
-
cordis_runtime_inspect: "
|
|
60
|
-
cordis_run: "
|
|
61
|
-
cordis_stop: "
|
|
62
|
-
cordis_undefine: "
|
|
63
|
-
pwsh: "
|
|
84
|
+
const TOOL_TITLE_KEYS = {
|
|
85
|
+
cordis_package_inspect: "tool.title.inspect",
|
|
86
|
+
cordis_runtime_inspect: "tool.title.inspect",
|
|
87
|
+
cordis_run: "tool.title.runCordis",
|
|
88
|
+
cordis_stop: "tool.title.stopCordis",
|
|
89
|
+
cordis_undefine: "tool.title.removeCordis",
|
|
90
|
+
pwsh: "tool.title.pwsh"
|
|
64
91
|
};
|
|
65
92
|
/**
|
|
66
93
|
* Classify a tool name into its row variant.
|
|
@@ -159,7 +186,13 @@ window.__ModuleLoader__.load({
|
|
|
159
186
|
const picked = pickString(parsed, FILE_PATH_KEYS);
|
|
160
187
|
return picked === void 0 ? void 0 : firstLine(picked);
|
|
161
188
|
}
|
|
162
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Format one argument payload when its generic input body becomes visible.
|
|
191
|
+
* @param variant - row presentation selected for the Tool name.
|
|
192
|
+
* @param argsRaw - original argument JSON or incomplete raw text.
|
|
193
|
+
* @returns display body, or null for empty input.
|
|
194
|
+
*/
|
|
195
|
+
function formatToolBody(variant, argsRaw) {
|
|
163
196
|
if (argsRaw === "") return null;
|
|
164
197
|
const parsed = parseArgs(argsRaw);
|
|
165
198
|
if (parsed === void 0) return argsRaw;
|
|
@@ -182,89 +215,147 @@ window.__ModuleLoader__.load({
|
|
|
182
215
|
const done = "kind" in block;
|
|
183
216
|
const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? "";
|
|
184
217
|
const state = !done ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError ? "error" : "ok";
|
|
185
|
-
const base = argsRaw === "" ? block.callId :
|
|
186
|
-
const
|
|
187
|
-
const summary = variant === "others" && toolName !== "" &&
|
|
218
|
+
const base = argsRaw === "" ? block.callId : abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home);
|
|
219
|
+
const toolTitleKey = TOOL_TITLE_KEYS[toolName];
|
|
220
|
+
const summary = variant === "others" && toolName !== "" && toolTitleKey === void 0 ? `${toolName} · ${base}` : base;
|
|
188
221
|
const output = done ? resultText(block) || null : null;
|
|
189
222
|
const errorSummary = state === "error" && output !== null ? firstLine(output) : null;
|
|
223
|
+
const bodyRaw = argsRaw === "" ? null : argsRaw;
|
|
190
224
|
return {
|
|
191
225
|
variant,
|
|
192
|
-
|
|
226
|
+
titleKey: toolTitleKey ?? VARIANT_TITLE_KEYS[variant],
|
|
193
227
|
summary,
|
|
194
228
|
filePath: deriveFilePath(variant, argsRaw),
|
|
195
|
-
|
|
229
|
+
bodyRaw,
|
|
196
230
|
output,
|
|
197
231
|
errorSummary,
|
|
198
232
|
state
|
|
199
233
|
};
|
|
200
234
|
}
|
|
201
235
|
//#endregion
|
|
202
|
-
//#region lib/types/client/tool/models/
|
|
236
|
+
//#region lib/types/client/tool/models/raw-tool-call.js
|
|
237
|
+
const parsedCalls = /* @__PURE__ */ new WeakMap();
|
|
203
238
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* the
|
|
207
|
-
* it into what {@link ReadBlock} draws. Both conversation render sites (the chat
|
|
208
|
-
* tool row's resident body and the details panel's Output section) call this, so
|
|
209
|
-
* the path, lines, total, and language they show are derived once.
|
|
210
|
-
*
|
|
211
|
-
* The read card is result-side only ([read card note](../../../../../../.agents/notes/implemented/feature/2026-07-30-web-read-card.md)):
|
|
212
|
-
* a call carries no file content until `execute` returns, so the pending call
|
|
213
|
-
* stays a generic card (`kind: 'read'`). A running read therefore has no read
|
|
214
|
-
* card, and this returns null for it — the row keeps its args-derived summary
|
|
215
|
-
* until the result arrives.
|
|
216
|
-
* @module
|
|
239
|
+
* Parse the call head paired with one immutable Tool block.
|
|
240
|
+
* @param block - running or settled Tool block.
|
|
241
|
+
* @returns the Tool name and object arguments, or null when the call head or valid JSON object is unavailable.
|
|
217
242
|
*/
|
|
243
|
+
function parsedToolCall(block) {
|
|
244
|
+
const cached = parsedCalls.get(block);
|
|
245
|
+
if (cached !== void 0 || parsedCalls.has(block)) return cached ?? null;
|
|
246
|
+
const call = "kind" in block ? block.call : block;
|
|
247
|
+
if (call === null) {
|
|
248
|
+
parsedCalls.set(block, null);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
let value;
|
|
252
|
+
try {
|
|
253
|
+
value = JSON.parse(call.argsRaw);
|
|
254
|
+
} catch {
|
|
255
|
+
parsedCalls.set(block, null);
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
259
|
+
parsedCalls.set(block, null);
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const parsed = {
|
|
263
|
+
name: call.name,
|
|
264
|
+
args: value
|
|
265
|
+
};
|
|
266
|
+
parsedCalls.set(block, parsed);
|
|
267
|
+
return parsed;
|
|
268
|
+
}
|
|
218
269
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
270
|
+
* Read the exact single text block consumed by first-party card derivations.
|
|
271
|
+
* @param block - settled Tool result.
|
|
272
|
+
* @returns its text, or undefined for any other content layout.
|
|
273
|
+
*/
|
|
274
|
+
function singleResultText(block) {
|
|
275
|
+
if (block.content.length !== 1) return void 0;
|
|
276
|
+
const only = block.content[0];
|
|
277
|
+
return only?.type === "text" ? only.text : void 0;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Validate the optional escalation pair shared by first-party shell and file
|
|
281
|
+
* mutation tools.
|
|
282
|
+
* @param args - parsed open-root Tool arguments.
|
|
283
|
+
* @returns whether the declared escalation fields form a valid pair.
|
|
284
|
+
*/
|
|
285
|
+
function validEscalationFields(args) {
|
|
286
|
+
const permission = args.sandbox_permissions;
|
|
287
|
+
const justification = args.justification;
|
|
288
|
+
if (permission === void 0 && justification === void 0) return true;
|
|
289
|
+
if (permission !== "workspace-write" && permission !== "danger-full-access") return false;
|
|
290
|
+
return typeof justification === "string" && justification.trim() !== "";
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region lib/types/client/tool/models/read-card-model.js
|
|
294
|
+
function validReadCall(block) {
|
|
295
|
+
const call = parsedToolCall(block);
|
|
296
|
+
if (call?.name !== "read") return false;
|
|
297
|
+
const { file_path: path, offset, limit } = call.args;
|
|
298
|
+
if (typeof path !== "string" || path.trim() === "") return false;
|
|
299
|
+
if (offset !== void 0 && (typeof offset !== "number" || !Number.isInteger(offset) || offset < 1)) return false;
|
|
300
|
+
if (limit !== void 0 && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1)) return false;
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
function readMeta(meta) {
|
|
304
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return null;
|
|
305
|
+
const { path, offset, lines, totalLines, lang } = meta;
|
|
306
|
+
if (typeof path !== "string" || typeof offset !== "number" || !Number.isInteger(offset) || offset < 1) return null;
|
|
307
|
+
if (typeof totalLines !== "number" || !Number.isInteger(totalLines) || totalLines < 0 || !Array.isArray(lines)) return null;
|
|
308
|
+
if (lang !== void 0 && typeof lang !== "string") return null;
|
|
309
|
+
const narrowed = [];
|
|
310
|
+
let previous = offset - 1;
|
|
311
|
+
for (const line of lines) {
|
|
312
|
+
if (typeof line !== "object" || line === null || Array.isArray(line)) return null;
|
|
313
|
+
const { number, text } = line;
|
|
314
|
+
if (typeof number !== "number" || !Number.isInteger(number) || number < 1 || number <= previous) return null;
|
|
315
|
+
if (number > totalLines || typeof text !== "string") return null;
|
|
316
|
+
previous = number;
|
|
317
|
+
narrowed.push({
|
|
318
|
+
number,
|
|
319
|
+
text
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
path,
|
|
324
|
+
offset,
|
|
325
|
+
lines: narrowed,
|
|
326
|
+
totalLines,
|
|
327
|
+
...lang === void 0 ? {} : { lang }
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Derive a settled root read card after validating its persisted metadata and
|
|
332
|
+
* model-facing read envelope.
|
|
333
|
+
* @param block - running or settled Tool block.
|
|
238
334
|
* @param sessionCwd - the session workspace root; a workspace-rooted absolute
|
|
239
335
|
* path label displays relative to it. Absent leaves the path as authored.
|
|
240
336
|
* @param home - host account home; a leftover POSIX home path displays as `~`.
|
|
241
337
|
* @returns the read-card props, or null for the generic path.
|
|
242
338
|
*/
|
|
243
339
|
function readCardModel(block, sessionCwd, home) {
|
|
244
|
-
if (!("kind" in block)) return null;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
340
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
341
|
+
if (!validReadCall(block)) return null;
|
|
342
|
+
const meta = readMeta(block.meta);
|
|
343
|
+
if (meta === null) return null;
|
|
344
|
+
const text = singleResultText(block);
|
|
345
|
+
if (text === void 0) return null;
|
|
346
|
+
if (/^<path>[^\n]*<\/path>\n<type>file<\/type>\n<content>\n([\s\S]*)\n<\/content>$/u.exec(text)?.[1] === void 0) return null;
|
|
251
347
|
return {
|
|
252
|
-
label:
|
|
253
|
-
lines,
|
|
254
|
-
totalLines:
|
|
255
|
-
lang:
|
|
348
|
+
label: abbreviateHomePath(relativizeToCwd(meta.path, sessionCwd), home),
|
|
349
|
+
lines: meta.lines,
|
|
350
|
+
totalLines: meta.totalLines,
|
|
351
|
+
lang: meta.lang
|
|
256
352
|
};
|
|
257
353
|
}
|
|
258
354
|
//#endregion
|
|
259
355
|
//#region lib/types/client/tool/models/diff-card-model.js
|
|
260
356
|
/**
|
|
261
|
-
* Narrow
|
|
262
|
-
*
|
|
263
|
-
* string, so a version mismatch or an anomalous plugin can deliver a `diff` card
|
|
264
|
-
* whose `diffs` is absent, not an array, or carries malformed hunks. Returning
|
|
265
|
-
* null for any of those routes the block to the generic path instead of letting
|
|
266
|
-
* DiffBlock's `for...of`/`split` throw and crash the row or the details panel.
|
|
267
|
-
* @param diffs - the view's `diffs` field, unverified.
|
|
357
|
+
* Narrow opaque result metadata's `diffs` to well-formed hunks.
|
|
358
|
+
* @param diffs - the metadata field to validate.
|
|
268
359
|
* @returns the validated hunks, or null when the payload is not usable.
|
|
269
360
|
*/
|
|
270
361
|
function narrowDiffs(diffs) {
|
|
@@ -284,110 +375,182 @@ window.__ModuleLoader__.load({
|
|
|
284
375
|
}
|
|
285
376
|
return out;
|
|
286
377
|
}
|
|
378
|
+
function intendedDiff(block) {
|
|
379
|
+
const parsed = parsedToolCall(block);
|
|
380
|
+
if (parsed === null) return null;
|
|
381
|
+
if (parsed.name === "str_replace_editor") {
|
|
382
|
+
const { command, path, file_text: fileText, old_str: oldText, new_str: newText } = parsed.args;
|
|
383
|
+
if (typeof path !== "string" || path.trim() === "") return null;
|
|
384
|
+
if (command === "create") {
|
|
385
|
+
if (fileText !== void 0 && typeof fileText !== "string") return null;
|
|
386
|
+
return {
|
|
387
|
+
tool: "str_replace_editor",
|
|
388
|
+
diff: {
|
|
389
|
+
path,
|
|
390
|
+
oldText: null,
|
|
391
|
+
newText: fileText ?? ""
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (command === "str_replace") {
|
|
396
|
+
if (oldText !== void 0 && typeof oldText !== "string") return null;
|
|
397
|
+
if (newText !== void 0 && typeof newText !== "string") return null;
|
|
398
|
+
return {
|
|
399
|
+
tool: "str_replace_editor",
|
|
400
|
+
diff: {
|
|
401
|
+
path,
|
|
402
|
+
oldText: oldText ?? null,
|
|
403
|
+
newText: newText ?? ""
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const { file_path: path } = parsed.args;
|
|
410
|
+
if (typeof path !== "string" || path.trim() === "") return null;
|
|
411
|
+
if (!validEscalationFields(parsed.args)) return null;
|
|
412
|
+
if (parsed.name === "write") {
|
|
413
|
+
const { content } = parsed.args;
|
|
414
|
+
return typeof content === "string" ? {
|
|
415
|
+
tool: "write",
|
|
416
|
+
diff: {
|
|
417
|
+
path,
|
|
418
|
+
oldText: null,
|
|
419
|
+
newText: content
|
|
420
|
+
}
|
|
421
|
+
} : null;
|
|
422
|
+
}
|
|
423
|
+
if (parsed.name !== "edit") return null;
|
|
424
|
+
const { old_string: oldText, new_string: newText, replace_all: replaceAll } = parsed.args;
|
|
425
|
+
if (typeof oldText !== "string" || typeof newText !== "string") return null;
|
|
426
|
+
if (replaceAll !== void 0 && typeof replaceAll !== "boolean") return null;
|
|
427
|
+
return {
|
|
428
|
+
tool: "edit",
|
|
429
|
+
diff: {
|
|
430
|
+
path,
|
|
431
|
+
oldText: oldText || null,
|
|
432
|
+
newText
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function appliedDiffs(meta) {
|
|
437
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return null;
|
|
438
|
+
const diffs = meta.diffs;
|
|
439
|
+
if (!Array.isArray(diffs)) return null;
|
|
440
|
+
if (diffs.length === 0) return "empty";
|
|
441
|
+
return narrowDiffs(diffs);
|
|
442
|
+
}
|
|
287
443
|
/**
|
|
288
|
-
* Derive
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* arguments alone. While the call is still running only the call side exists,
|
|
295
|
-
* so a running write/edit shows its intended change. Null is the documented
|
|
296
|
-
* generic-card default and covers every non-diff card — including a `card`
|
|
297
|
-
* value this UI version does not know, which arrives over the wire and cannot
|
|
298
|
-
* be trusted to be one of the compiled variants — and a settled call whose
|
|
299
|
-
* result view is generic (how write/edit keep their execution errors on the
|
|
300
|
-
* generic path).
|
|
301
|
-
*
|
|
302
|
-
* This derivation consumes only `diffs`; the render intent's `title` field is
|
|
303
|
-
* deliberately dropped. The row supplies its own title (`Edit`/`Write · path`
|
|
304
|
-
* from the args), which outranks the view's `title`. A tool that names its own
|
|
305
|
-
* diff header therefore does not surface that text on the Web row.
|
|
306
|
-
* @param block - RunningToolCall or ToolResultNode off the snapshot caches.
|
|
444
|
+
* Derive running diffs for root write/edit and `str_replace_editor`
|
|
445
|
+
* create/replace calls, plus applied settled diffs for root write/edit calls.
|
|
446
|
+
* A successful write with valid empty metadata uses its argument-derived
|
|
447
|
+
* whole-file diff, matching create and identical-overwrite presentation;
|
|
448
|
+
* `str_replace_editor` settles through Generic because it has no result view.
|
|
449
|
+
* @param block - running or settled Tool block.
|
|
307
450
|
* @returns the diff-card props, or null for the generic path.
|
|
308
451
|
*/
|
|
309
452
|
function diffCardModel(block) {
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
453
|
+
if (block.parentCallId !== void 0) return null;
|
|
454
|
+
const intended = intendedDiff(block);
|
|
455
|
+
if (intended === null) return null;
|
|
456
|
+
if (!("kind" in block)) return { card: { diffs: [intended.diff] } };
|
|
457
|
+
if (intended.tool === "str_replace_editor") return null;
|
|
458
|
+
if (block.isError) return null;
|
|
459
|
+
const applied = appliedDiffs(block.meta);
|
|
460
|
+
if (applied === null || applied === "empty") return intended.tool === "write" ? { card: { diffs: [intended.diff] } } : null;
|
|
461
|
+
return { card: { diffs: applied } };
|
|
318
462
|
}
|
|
319
463
|
//#endregion
|
|
320
464
|
//#region lib/types/client/tool/models/search-card-model.js
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
465
|
+
function validSearchCall(block) {
|
|
466
|
+
const call = parsedToolCall(block);
|
|
467
|
+
if (call === null) return null;
|
|
468
|
+
const { pattern, path } = call.args;
|
|
469
|
+
if (typeof pattern !== "string") return null;
|
|
470
|
+
if (call.name === "grep" && pattern === "") return null;
|
|
471
|
+
if (call.name === "glob" && pattern.trim() === "") return null;
|
|
472
|
+
if (call.name !== "grep" && call.name !== "glob") return null;
|
|
473
|
+
if (path !== void 0 && (typeof path !== "string" || path.trim() === "")) return null;
|
|
474
|
+
if (call.name === "grep") {
|
|
475
|
+
const { include } = call.args;
|
|
476
|
+
if (include !== void 0 && (typeof include !== "string" || !validInclude(include))) return null;
|
|
477
|
+
}
|
|
478
|
+
return call.name;
|
|
479
|
+
}
|
|
480
|
+
function validInclude(include) {
|
|
481
|
+
if (include.trim() === "" || include.startsWith("!")) return false;
|
|
482
|
+
let braceDepth = 0;
|
|
483
|
+
for (const character of include) if (character === "{") braceDepth += 1;
|
|
484
|
+
else if (character === "}") braceDepth = Math.max(0, braceDepth - 1);
|
|
485
|
+
else if (character === "," && braceDepth === 0) return false;
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
function searchFiles(value) {
|
|
489
|
+
if (!Array.isArray(value)) return null;
|
|
490
|
+
const files = [];
|
|
491
|
+
for (const file of value) {
|
|
492
|
+
if (typeof file !== "object" || file === null || Array.isArray(file)) return null;
|
|
493
|
+
const { path, matches } = file;
|
|
494
|
+
if (typeof path !== "string" || !Array.isArray(matches)) return null;
|
|
495
|
+
const narrowed = [];
|
|
496
|
+
for (const match of matches) {
|
|
497
|
+
if (typeof match !== "object" || match === null || Array.isArray(match)) return null;
|
|
498
|
+
const { lineNumber, line } = match;
|
|
499
|
+
if (typeof lineNumber !== "number" || !Number.isInteger(lineNumber) || lineNumber < 1) return null;
|
|
500
|
+
if (typeof line !== "string") return null;
|
|
501
|
+
narrowed.push({
|
|
502
|
+
lineNumber,
|
|
503
|
+
line
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
files.push({
|
|
507
|
+
path,
|
|
508
|
+
matches: narrowed
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return files;
|
|
333
512
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Flatten a settled tool result's content blocks to their text, joined by
|
|
336
|
-
* newlines. The search view carries no result text — a UI without a card falls
|
|
337
|
-
* back to the raw `tool/result` content — so the truncation recovery footer is
|
|
338
|
-
* read from the block's own content here. Non-text blocks (a search result
|
|
339
|
-
* carries none) are skipped.
|
|
340
|
-
* @param content - the result node's content blocks.
|
|
341
|
-
* @returns the joined text, or undefined when empty.
|
|
342
|
-
*/
|
|
343
513
|
function flattenContent(content) {
|
|
344
514
|
const text = content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
345
515
|
return text === "" ? void 0 : text;
|
|
346
516
|
}
|
|
347
517
|
/**
|
|
348
|
-
* Derive
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
* Only the result side matters: the search card carries no call-time state, so
|
|
352
|
-
* a still-running call (no result view) is null, as is a settled call whose
|
|
353
|
-
* result view is not a search card — including a `card` value this UI version
|
|
354
|
-
* does not know, which arrives over the wire and cannot be trusted to be one of
|
|
355
|
-
* the compiled variants, a `card: 'search'` view whose `shape` is neither
|
|
356
|
-
* `matches` nor `paths` (equally untrusted wire data), and a generic result a
|
|
357
|
-
* `grep`/`glob` failure or nested `run_code` dispatch produces (its text keeps
|
|
358
|
-
* the generic path).
|
|
359
|
-
* @param block - RunningToolCall or ToolResultNode off the snapshot caches.
|
|
360
|
-
* @returns the search-card props, or null for the generic path.
|
|
518
|
+
* Derive a settled root grep/glob card from persisted metadata.
|
|
519
|
+
* @param block - running or settled Tool block.
|
|
520
|
+
* @returns search-card props, or null for the generic path.
|
|
361
521
|
*/
|
|
362
522
|
function searchCardModel(block) {
|
|
363
|
-
if (!("kind" in block)) return null;
|
|
364
|
-
const
|
|
365
|
-
if (
|
|
523
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
524
|
+
const tool = validSearchCall(block);
|
|
525
|
+
if (tool === null) return null;
|
|
526
|
+
if (typeof block.meta !== "object" || block.meta === null || Array.isArray(block.meta)) return null;
|
|
527
|
+
const meta = block.meta;
|
|
528
|
+
if (typeof meta.truncated !== "boolean") return null;
|
|
529
|
+
if (typeof meta.total !== "number" || !Number.isInteger(meta.total) || meta.total < 0) return null;
|
|
366
530
|
const common = {
|
|
367
|
-
truncated:
|
|
368
|
-
total:
|
|
531
|
+
truncated: meta.truncated,
|
|
532
|
+
total: meta.total
|
|
369
533
|
};
|
|
370
|
-
const recovery =
|
|
371
|
-
if (
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
534
|
+
const recovery = meta.truncated ? flattenContent(block.content) : void 0;
|
|
535
|
+
if (tool === "grep") {
|
|
536
|
+
if (meta.shape !== "matches") return null;
|
|
537
|
+
const files = searchFiles(meta.files);
|
|
538
|
+
return files === null ? null : {
|
|
375
539
|
recovery,
|
|
376
540
|
card: {
|
|
377
541
|
kind: "matches",
|
|
378
|
-
files
|
|
542
|
+
files,
|
|
379
543
|
...common
|
|
380
544
|
}
|
|
381
545
|
};
|
|
382
546
|
}
|
|
383
|
-
if (
|
|
384
|
-
if (!
|
|
547
|
+
if (meta.shape !== "paths" || !Array.isArray(meta.paths)) return null;
|
|
548
|
+
if (!meta.paths.every((path) => typeof path === "string")) return null;
|
|
385
549
|
return {
|
|
386
|
-
title: result.title,
|
|
387
550
|
recovery,
|
|
388
551
|
card: {
|
|
389
552
|
kind: "paths",
|
|
390
|
-
paths:
|
|
553
|
+
paths: [...meta.paths],
|
|
391
554
|
...common
|
|
392
555
|
}
|
|
393
556
|
};
|
|
@@ -395,16 +558,6 @@ window.__ModuleLoader__.load({
|
|
|
395
558
|
//#endregion
|
|
396
559
|
//#region lib/types/client/tool/models/terminal-card-model.js
|
|
397
560
|
/**
|
|
398
|
-
* Pure derivation of the terminal-card props from a frozen call slice: the
|
|
399
|
-
* `card:'terminal'` render intent the shell tools declare arrives on the
|
|
400
|
-
* snapshot as `callView`/`resultView`, and this is the one place that turns
|
|
401
|
-
* that pair into what {@link TerminalBlock} draws. Both conversation render
|
|
402
|
-
* sites (the chat tool row's expanded body and the details panel's Output
|
|
403
|
-
* section) call this, so the command, cwd, output and exit status they show
|
|
404
|
-
* are derived once.
|
|
405
|
-
* @module
|
|
406
|
-
*/
|
|
407
|
-
/**
|
|
408
561
|
* Build the TerminalBlock display copy from the conversation locale seat —
|
|
409
562
|
* the one place the primitive's label surface pairs with this package's
|
|
410
563
|
* dictionary, shared by every terminal render site (chat row, bash row,
|
|
@@ -429,6 +582,29 @@ window.__ModuleLoader__.load({
|
|
|
429
582
|
};
|
|
430
583
|
}
|
|
431
584
|
/**
|
|
585
|
+
* Resolve locale-owned `terminal_send` copy while preserving Tool-authored
|
|
586
|
+
* shell commands and descriptions verbatim.
|
|
587
|
+
* @param model - locale-neutral terminal card data.
|
|
588
|
+
* @param t - the render site's conversation locale seat.
|
|
589
|
+
* @returns terminal props and description ready for rendering.
|
|
590
|
+
*/
|
|
591
|
+
function localizeTerminalCardModel(model, t) {
|
|
592
|
+
if (model.copy.kind === "shell") return {
|
|
593
|
+
card: {
|
|
594
|
+
command: model.copy.command,
|
|
595
|
+
...model.card
|
|
596
|
+
},
|
|
597
|
+
description: model.copy.description
|
|
598
|
+
};
|
|
599
|
+
return {
|
|
600
|
+
card: {
|
|
601
|
+
command: model.copy.text === "" ? t("terminal.sendInput") : model.copy.text,
|
|
602
|
+
...model.card
|
|
603
|
+
},
|
|
604
|
+
description: t("terminal.session", { sessionId: model.copy.sessionId })
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
432
608
|
* True when a settled terminal card reports a failing exit — a non-zero code
|
|
433
609
|
* or a terminating signal. The bash tool settles a failing command as a
|
|
434
610
|
* completed call (`isError` stays false: the exit status is result data), so
|
|
@@ -442,21 +618,18 @@ window.__ModuleLoader__.load({
|
|
|
442
618
|
return running !== true && (exitCode !== void 0 && exitCode !== 0 || signal !== void 0);
|
|
443
619
|
}
|
|
444
620
|
/**
|
|
445
|
-
* Resolve a
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
* is nothing to resolve against, so a relative path stays as authored and an
|
|
451
|
-
* omitted one stays absent (the prompt row then draws a bare `$`).
|
|
452
|
-
* @param viewCwd - the cwd the terminal call view carries, if any.
|
|
621
|
+
* Resolve a shell call's workdir for display: an absolute path is used as-is,
|
|
622
|
+
* a relative one joins under the session workspace, and an omitted one is the
|
|
623
|
+
* session workspace. Without a session cwd, a relative path stays as authored
|
|
624
|
+
* and an omitted one stays absent.
|
|
625
|
+
* @param workdir - the raw call's workdir, if any.
|
|
453
626
|
* @param sessionCwd - the session workspace root, if the caller knows it.
|
|
454
627
|
* @returns the working directory for the prompt label, or undefined.
|
|
455
628
|
*/
|
|
456
|
-
function resolveTerminalCwd(
|
|
457
|
-
if (
|
|
458
|
-
if (sessionCwd === void 0 || sessionCwd === "") return normalizeSegments(
|
|
459
|
-
return normalizeSegments(
|
|
629
|
+
function resolveTerminalCwd(workdir, sessionCwd) {
|
|
630
|
+
if (workdir === void 0 || workdir === "") return sessionCwd;
|
|
631
|
+
if (sessionCwd === void 0 || sessionCwd === "") return normalizeSegments(workdir);
|
|
632
|
+
return normalizeSegments(resolveWorkspacePath(sessionCwd, workdir));
|
|
460
633
|
}
|
|
461
634
|
/**
|
|
462
635
|
* Collapse `.` and `..` segments so the prompt label names the directory the
|
|
@@ -508,104 +681,195 @@ window.__ModuleLoader__.load({
|
|
|
508
681
|
}
|
|
509
682
|
return kept.join(separator);
|
|
510
683
|
}
|
|
684
|
+
function shellCall(name, args) {
|
|
685
|
+
if (name !== "bash" && name !== "pwsh") return null;
|
|
686
|
+
const { command, description, timeoutMs, workdir, run_in_background: background } = args;
|
|
687
|
+
if (typeof command !== "string" || command.trim() === "") return null;
|
|
688
|
+
if (timeoutMs !== void 0 && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0)) return null;
|
|
689
|
+
if (workdir !== void 0 && typeof workdir !== "string") return null;
|
|
690
|
+
if (background !== void 0 && typeof background !== "boolean") return null;
|
|
691
|
+
if (!validEscalationFields(args)) return null;
|
|
692
|
+
if (description === void 0) return {
|
|
693
|
+
kind: "shell",
|
|
694
|
+
command,
|
|
695
|
+
description: void 0,
|
|
696
|
+
workdir: void 0,
|
|
697
|
+
persistent: true,
|
|
698
|
+
background: false
|
|
699
|
+
};
|
|
700
|
+
if (typeof description !== "string" || description.trim() === "") return null;
|
|
701
|
+
return {
|
|
702
|
+
kind: "shell",
|
|
703
|
+
command,
|
|
704
|
+
description,
|
|
705
|
+
workdir,
|
|
706
|
+
persistent: false,
|
|
707
|
+
background: background === true
|
|
708
|
+
};
|
|
709
|
+
}
|
|
511
710
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
711
|
+
* Identify a settled root call from the persistent Bash or PowerShell tool.
|
|
712
|
+
* Its result stays on the generic input/output path because the persistent
|
|
713
|
+
* shell can report resets and partial output without one process exit status.
|
|
714
|
+
* @param block - running or settled Tool block.
|
|
715
|
+
* @returns whether the block is a settled persistent-shell call.
|
|
716
|
+
*/
|
|
717
|
+
function isSettledPersistentShellCall(block) {
|
|
718
|
+
if (!("kind" in block) || block.parentCallId !== void 0) return false;
|
|
719
|
+
const parsed = parsedToolCall(block);
|
|
720
|
+
if (parsed === null) return false;
|
|
721
|
+
return shellCall(parsed.name, parsed.args)?.persistent === true;
|
|
722
|
+
}
|
|
723
|
+
function terminalSendCall(name, args) {
|
|
724
|
+
if (name !== "terminal_send") return null;
|
|
725
|
+
const { sessionId, text, submit, run_in_background: background } = args;
|
|
726
|
+
if (typeof sessionId !== "string" || sessionId === "" || typeof text !== "string") return null;
|
|
727
|
+
if (submit !== void 0 && typeof submit !== "boolean") return null;
|
|
728
|
+
if (background !== void 0 && typeof background !== "boolean") return null;
|
|
729
|
+
return {
|
|
730
|
+
kind: "terminal-send",
|
|
731
|
+
text,
|
|
732
|
+
sessionId,
|
|
733
|
+
background: background === true
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Parse the marker literals owned by `@deepseek-ai/dsh-shell/render` without
|
|
738
|
+
* importing that Host-only package into the Client dependency graph.
|
|
739
|
+
* @param text - rendered shell result text.
|
|
740
|
+
* @returns output with a trailing exit-code or signal marker extracted.
|
|
741
|
+
*/
|
|
742
|
+
function parseExitStatus(text) {
|
|
743
|
+
const signal = /\n\[killed by signal: ([^\]\n]+)\]$/.exec(text);
|
|
744
|
+
if (signal?.[1] !== void 0) return {
|
|
745
|
+
output: text.slice(0, signal.index),
|
|
746
|
+
signal: signal[1]
|
|
747
|
+
};
|
|
748
|
+
const exit = /\n\[exit code: (\d+)\]$/.exec(text);
|
|
749
|
+
if (exit?.[1] !== void 0) return {
|
|
750
|
+
output: text.slice(0, exit.index),
|
|
751
|
+
exitCode: Number(exit[1])
|
|
752
|
+
};
|
|
753
|
+
return {
|
|
754
|
+
output: text,
|
|
755
|
+
exitCode: 0
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Derive terminal props for supported root shell and terminal-send calls.
|
|
760
|
+
* Standard shell results parse their final status marker; persistent shell
|
|
761
|
+
* results, background calls, errors, malformed input, or child dispatches use
|
|
762
|
+
* the generic path. {@link isSettledPersistentShellCall} lets that generic
|
|
763
|
+
* persistent result remain expandable without inventing one process status.
|
|
764
|
+
* @param block - running or settled Tool block.
|
|
765
|
+
* @param sessionCwd - session workspace root used to resolve workdir.
|
|
766
|
+
* @returns locale-neutral terminal-card data, or null for the generic path.
|
|
536
767
|
*/
|
|
537
768
|
function terminalCardModel(block, sessionCwd) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
769
|
+
if (block.parentCallId !== void 0) return null;
|
|
770
|
+
const parsed = parsedToolCall(block);
|
|
771
|
+
if (parsed === null) return null;
|
|
772
|
+
const call = shellCall(parsed.name, parsed.args) ?? terminalSendCall(parsed.name, parsed.args);
|
|
773
|
+
if (call === null || call.background) return null;
|
|
774
|
+
const copy = call.kind === "shell" ? {
|
|
775
|
+
kind: "shell",
|
|
776
|
+
command: call.command,
|
|
777
|
+
description: call.description
|
|
778
|
+
} : {
|
|
779
|
+
kind: "terminal-send",
|
|
780
|
+
text: call.text,
|
|
781
|
+
sessionId: call.sessionId
|
|
782
|
+
};
|
|
783
|
+
const cwd = resolveTerminalCwd(call.kind === "shell" ? call.workdir : void 0, sessionCwd);
|
|
784
|
+
if (!("kind" in block)) return {
|
|
785
|
+
copy,
|
|
541
786
|
card: {
|
|
542
|
-
|
|
543
|
-
cwd: resolveTerminalCwd(call.cwd, sessionCwd),
|
|
787
|
+
cwd,
|
|
544
788
|
output: void 0,
|
|
545
789
|
exitCode: void 0,
|
|
546
790
|
signal: void 0,
|
|
547
791
|
running: true
|
|
548
792
|
}
|
|
549
793
|
};
|
|
550
|
-
|
|
551
|
-
|
|
794
|
+
if (block.isError || call.kind === "shell" && call.persistent) return null;
|
|
795
|
+
const output = singleResultText(block);
|
|
796
|
+
if (output === void 0) return null;
|
|
797
|
+
const status = call.kind === "terminal-send" ? { output } : parseExitStatus(output);
|
|
552
798
|
return {
|
|
553
|
-
|
|
799
|
+
copy,
|
|
554
800
|
card: {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
signal: result.signal,
|
|
801
|
+
cwd,
|
|
802
|
+
output: status.output,
|
|
803
|
+
exitCode: status.exitCode,
|
|
804
|
+
signal: status.signal,
|
|
560
805
|
running: false
|
|
561
806
|
}
|
|
562
807
|
};
|
|
563
808
|
}
|
|
564
809
|
//#endregion
|
|
565
810
|
//#region lib/types/client/tool/models/web-card-model.js
|
|
811
|
+
function validWebCall(block) {
|
|
812
|
+
const call = parsedToolCall(block);
|
|
813
|
+
if (call === null) return null;
|
|
814
|
+
if (call.name === "web_search") {
|
|
815
|
+
const { queries } = call.args;
|
|
816
|
+
if (!Array.isArray(queries) || queries.length === 0) return null;
|
|
817
|
+
return queries.every((query) => typeof query === "string" && query.trim() !== "") ? call.name : null;
|
|
818
|
+
}
|
|
819
|
+
if (call.name === "web_fetch") {
|
|
820
|
+
const { url } = call.args;
|
|
821
|
+
return typeof url === "string" && url.trim() !== "" ? call.name : null;
|
|
822
|
+
}
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
function webSources(value) {
|
|
826
|
+
if (!Array.isArray(value)) return null;
|
|
827
|
+
const sources = [];
|
|
828
|
+
for (const source of value) {
|
|
829
|
+
if (typeof source !== "object" || source === null || Array.isArray(source)) return null;
|
|
830
|
+
const { url, title, snippet, publishedAt } = source;
|
|
831
|
+
if (typeof url !== "string") return null;
|
|
832
|
+
if (title !== void 0 && typeof title !== "string") return null;
|
|
833
|
+
if (snippet !== void 0 && typeof snippet !== "string") return null;
|
|
834
|
+
if (publishedAt !== void 0 && typeof publishedAt !== "string") return null;
|
|
835
|
+
sources.push({
|
|
836
|
+
url,
|
|
837
|
+
...title === void 0 ? {} : { title },
|
|
838
|
+
...snippet === void 0 ? {} : { snippet },
|
|
839
|
+
...publishedAt === void 0 ? {} : { publishedAt }
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
return sources;
|
|
843
|
+
}
|
|
566
844
|
/**
|
|
567
|
-
* Derive
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
* The result side supplies the whole card: the sources and answer for a
|
|
571
|
-
* `search`, the URL and status for a `fetch`. Cases producing null, all of
|
|
572
|
-
* them the documented generic-card default:
|
|
573
|
-
*
|
|
574
|
-
* - A running call (no `resultView` yet): the web tools keep a generic pending
|
|
575
|
-
* card, so nothing web-shaped exists until the call settles.
|
|
576
|
-
* - A settled call whose result view is not a web card — including a `card`
|
|
577
|
-
* value this UI version does not know, which arrives over the wire and so
|
|
578
|
-
* cannot be trusted to be one of the compiled variants, and a generic result
|
|
579
|
-
* view (a web tool's error path returns the generic card, whose text the
|
|
580
|
-
* generic path preserves).
|
|
581
|
-
* - A web card whose `kind` this UI version does not know (a newer host's
|
|
582
|
-
* value): the wire cannot be trusted to be `search` or `fetch`, so it takes
|
|
583
|
-
* the generic path rather than rendering as a malformed fetch.
|
|
584
|
-
* @param block - RunningToolCall or ToolResultNode off the snapshot caches.
|
|
585
|
-
* @returns the web-card props, or null for the generic path.
|
|
845
|
+
* Derive a settled root web-search or web-fetch card from persisted metadata.
|
|
846
|
+
* @param block - running or settled Tool block.
|
|
847
|
+
* @returns web-card props, or null for the generic path.
|
|
586
848
|
*/
|
|
587
849
|
function webCardModel(block) {
|
|
588
|
-
if (!("kind" in block)) return null;
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
sources
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
850
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
851
|
+
const tool = validWebCall(block);
|
|
852
|
+
if (tool === null || typeof block.meta !== "object" || block.meta === null || Array.isArray(block.meta)) return null;
|
|
853
|
+
const meta = block.meta;
|
|
854
|
+
if (typeof meta.truncated !== "boolean") return null;
|
|
855
|
+
if (tool === "web_search") {
|
|
856
|
+
const sources = webSources(meta.sources);
|
|
857
|
+
if (sources === null || meta.answer !== void 0 && typeof meta.answer !== "string") return null;
|
|
858
|
+
return {
|
|
859
|
+
kind: "search",
|
|
860
|
+
answer: meta.answer,
|
|
861
|
+
sources,
|
|
862
|
+
truncated: meta.truncated
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
if (typeof meta.url !== "string") return null;
|
|
866
|
+
if (typeof meta.statusCode !== "number" || !Number.isInteger(meta.statusCode)) return null;
|
|
867
|
+
return {
|
|
603
868
|
kind: "fetch",
|
|
604
|
-
url:
|
|
605
|
-
statusCode:
|
|
606
|
-
truncated:
|
|
869
|
+
url: meta.url,
|
|
870
|
+
statusCode: meta.statusCode,
|
|
871
|
+
truncated: meta.truncated
|
|
607
872
|
};
|
|
608
|
-
return null;
|
|
609
873
|
}
|
|
610
874
|
//#endregion
|
|
611
875
|
//#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
@@ -623,8 +887,162 @@ window.__ModuleLoader__.load({
|
|
|
623
887
|
return n;
|
|
624
888
|
}
|
|
625
889
|
//#endregion
|
|
890
|
+
//#region lib/types/client/tool/models/primitive-labels.js
|
|
891
|
+
/** Localized copy adapters for Cordis-free UI primitives used by Tool cards. */
|
|
892
|
+
/**
|
|
893
|
+
* Build localized Markdown chrome labels.
|
|
894
|
+
* @param t - Conversation locale seat.
|
|
895
|
+
* @returns Markdown chrome labels.
|
|
896
|
+
*/
|
|
897
|
+
function markdownLabels(t) {
|
|
898
|
+
return {
|
|
899
|
+
code: {
|
|
900
|
+
copyLabel: t("copy"),
|
|
901
|
+
copiedLabel: t("copied")
|
|
902
|
+
},
|
|
903
|
+
footnotes: t("markdown.footnotes")
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Build localized diff-card chrome labels.
|
|
908
|
+
* @param t - Conversation locale seat.
|
|
909
|
+
* @returns Diff-card chrome labels.
|
|
910
|
+
*/
|
|
911
|
+
function diffBlockLabels(t) {
|
|
912
|
+
return {
|
|
913
|
+
copy: t("copy"),
|
|
914
|
+
copied: t("copied"),
|
|
915
|
+
collapseAria: t("diff.collapseAria"),
|
|
916
|
+
expandAria: (count) => t("diff.expandAria", { count }),
|
|
917
|
+
collapse: t("collapse"),
|
|
918
|
+
expand: (count) => t("diff.expandRest", { count }),
|
|
919
|
+
files: (count) => t(count === 1 ? "diff.files.one" : "diff.files.other", { count })
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Build localized read-card chrome labels.
|
|
924
|
+
* @param t - Conversation locale seat.
|
|
925
|
+
* @returns Read-card chrome labels.
|
|
926
|
+
*/
|
|
927
|
+
function readBlockLabels(t) {
|
|
928
|
+
return {
|
|
929
|
+
window: (shown, total) => t("read.window", {
|
|
930
|
+
shown,
|
|
931
|
+
total
|
|
932
|
+
}),
|
|
933
|
+
copy: t("copy"),
|
|
934
|
+
copied: t("copied"),
|
|
935
|
+
collapseAria: t("read.collapseAria"),
|
|
936
|
+
expandAria: (count) => t("read.expandAria", { count }),
|
|
937
|
+
collapse: t("collapse"),
|
|
938
|
+
expand: (count) => t("read.expandRest", { count })
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Build localized search-card chrome labels.
|
|
943
|
+
* @param t - Conversation locale seat.
|
|
944
|
+
* @returns Search-card chrome labels.
|
|
945
|
+
*/
|
|
946
|
+
function searchBlockLabels(t) {
|
|
947
|
+
return {
|
|
948
|
+
pathsSummary: (shown, total, truncated) => t(truncated ? "search.paths.truncated" : "search.paths", {
|
|
949
|
+
shown,
|
|
950
|
+
total
|
|
951
|
+
}),
|
|
952
|
+
matchesSummary: (shown, total, files, truncated) => t(truncated ? "search.matches.truncated" : "search.matches", {
|
|
953
|
+
shown,
|
|
954
|
+
total,
|
|
955
|
+
files
|
|
956
|
+
}),
|
|
957
|
+
copy: t("copy"),
|
|
958
|
+
copied: t("copied"),
|
|
959
|
+
noResults: t("search.noResults"),
|
|
960
|
+
collapseAria: t("search.collapseAria"),
|
|
961
|
+
expandAria: (count) => t("search.expandAria", { count }),
|
|
962
|
+
collapse: t("collapse"),
|
|
963
|
+
expand: (count) => t("search.expandRest", { count })
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Build localized web-card chrome labels.
|
|
968
|
+
* @param t - Conversation locale seat.
|
|
969
|
+
* @returns Web-card chrome labels.
|
|
970
|
+
*/
|
|
971
|
+
function webBlockLabels(t) {
|
|
972
|
+
return {
|
|
973
|
+
noResults: t("web.noResults"),
|
|
974
|
+
sourcesTruncated: t("web.sourcesTruncated"),
|
|
975
|
+
http: t("web.http"),
|
|
976
|
+
contentTruncated: t("web.contentTruncated"),
|
|
977
|
+
markdown: markdownLabels(t)
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
//#endregion
|
|
981
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
|
|
982
|
+
const css$4 = ".fsXYAq_card{border:1px 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))}";
|
|
983
|
+
const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
|
|
984
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
985
|
+
const tag = document.createElement("style");
|
|
986
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
|
|
987
|
+
tag.dataset.pluginCss = tagId$4;
|
|
988
|
+
tag.textContent = css$4;
|
|
989
|
+
document.head.appendChild(tag);
|
|
990
|
+
}
|
|
991
|
+
var AskQuestionCard_module_css_default = {
|
|
992
|
+
"answer": "fsXYAq_answer",
|
|
993
|
+
"answerLine": "fsXYAq_answerLine",
|
|
994
|
+
"card": "fsXYAq_card",
|
|
995
|
+
"item": "fsXYAq_item",
|
|
996
|
+
"question": "fsXYAq_question",
|
|
997
|
+
"questionList": "fsXYAq_questionList",
|
|
998
|
+
"skipped": "fsXYAq_skipped",
|
|
999
|
+
"unansweredQuestion": "fsXYAq_unansweredQuestion",
|
|
1000
|
+
"verdict": "fsXYAq_verdict"
|
|
1001
|
+
};
|
|
1002
|
+
//#endregion
|
|
1003
|
+
//#region lib/types/client/tool/components/AskQuestionCard.js
|
|
1004
|
+
/**
|
|
1005
|
+
* Render a validated ask-user transcript from plain card data.
|
|
1006
|
+
* @param props - Localized transcript card data.
|
|
1007
|
+
* @returns the readable answered or unanswered question list.
|
|
1008
|
+
*/
|
|
1009
|
+
function AskQuestionCard({ card }) {
|
|
1010
|
+
if (card.kind === "unanswered") return (0, react_jsx_runtime.jsxs)("div", {
|
|
1011
|
+
className: AskQuestionCard_module_css_default.card,
|
|
1012
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1013
|
+
className: AskQuestionCard_module_css_default.verdict,
|
|
1014
|
+
children: card.verdict
|
|
1015
|
+
}), (0, react_jsx_runtime.jsx)("ul", {
|
|
1016
|
+
className: AskQuestionCard_module_css_default.questionList,
|
|
1017
|
+
children: card.questions.map((question) => (0, react_jsx_runtime.jsx)("li", {
|
|
1018
|
+
className: AskQuestionCard_module_css_default.unansweredQuestion,
|
|
1019
|
+
children: question.question
|
|
1020
|
+
}, question.id))
|
|
1021
|
+
})]
|
|
1022
|
+
});
|
|
1023
|
+
return (0, react_jsx_runtime.jsx)("dl", {
|
|
1024
|
+
className: AskQuestionCard_module_css_default.card,
|
|
1025
|
+
children: card.questions.map((question) => (0, react_jsx_runtime.jsxs)("div", {
|
|
1026
|
+
className: AskQuestionCard_module_css_default.item,
|
|
1027
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
1028
|
+
className: AskQuestionCard_module_css_default.question,
|
|
1029
|
+
children: question.question
|
|
1030
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
1031
|
+
className: AskQuestionCard_module_css_default.answer,
|
|
1032
|
+
children: question.answers.length === 0 ? (0, react_jsx_runtime.jsx)("span", {
|
|
1033
|
+
className: AskQuestionCard_module_css_default.skipped,
|
|
1034
|
+
children: card.skippedLabel
|
|
1035
|
+
}) : question.answers.map((answer, index) => (0, react_jsx_runtime.jsx)("span", {
|
|
1036
|
+
className: AskQuestionCard_module_css_default.answerLine,
|
|
1037
|
+
children: answer
|
|
1038
|
+
}, `${question.id}-${String(index)}`))
|
|
1039
|
+
})]
|
|
1040
|
+
}, question.id))
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
//#endregion
|
|
626
1044
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
|
|
627
|
-
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;color:var(--dsw-alias-label-tertiary);flex:auto;
|
|
1045
|
+
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:1px solid var(--dsw-alias-border-l2);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:1px 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:1px}.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_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_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:1px 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}";
|
|
628
1046
|
const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
|
|
629
1047
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
630
1048
|
const tag = document.createElement("style");
|
|
@@ -639,6 +1057,7 @@ window.__ModuleLoader__.load({
|
|
|
639
1057
|
"chevron": "o3BgMG_chevron",
|
|
640
1058
|
"codeBody": "o3BgMG_codeBody",
|
|
641
1059
|
"diffBody": "o3BgMG_diffBody",
|
|
1060
|
+
"diffStat": "o3BgMG_diffStat",
|
|
642
1061
|
"dsh-tool-row-sweep": "o3BgMG_dsh-tool-row-sweep",
|
|
643
1062
|
"errorSummary": "o3BgMG_errorSummary",
|
|
644
1063
|
"fileLink": "o3BgMG_fileLink",
|
|
@@ -664,9 +1083,6 @@ window.__ModuleLoader__.load({
|
|
|
664
1083
|
};
|
|
665
1084
|
//#endregion
|
|
666
1085
|
//#region lib/types/client/tool/components/ToolRow.js
|
|
667
|
-
/** Leading-slot state substitution: the tool icon yields to the terminal state
|
|
668
|
-
* semantic (error = red, interrupted = amber halo). Running keeps the icon —
|
|
669
|
-
* the row sweep (CSS on data-state) carries the in-flight signal. */
|
|
670
1086
|
function leadingFor$1(state, icon) {
|
|
671
1087
|
switch (state) {
|
|
672
1088
|
case "error": return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" });
|
|
@@ -686,20 +1102,38 @@ window.__ModuleLoader__.load({
|
|
|
686
1102
|
default: return null;
|
|
687
1103
|
}
|
|
688
1104
|
}
|
|
689
|
-
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix,
|
|
1105
|
+
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, bodyRaw, output, askQuestion, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect }) {
|
|
690
1106
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
691
|
-
const
|
|
1107
|
+
const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
|
|
1108
|
+
const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
|
|
1109
|
+
const readLabels = (0, react.useMemo)(() => readBlockLabels(t), [t]);
|
|
1110
|
+
const searchLabels = (0, react.useMemo)(() => searchBlockLabels(t), [t]);
|
|
1111
|
+
const webLabels = (0, react.useMemo)(() => webBlockLabels(t), [t]);
|
|
1112
|
+
const terminalBody = terminal === void 0 || terminal === null ? null : localizeTerminalCardModel(terminal, t);
|
|
692
1113
|
const diffBody = diff ?? null;
|
|
693
1114
|
const readBody = read ?? null;
|
|
694
1115
|
const searchBody = search ?? null;
|
|
695
1116
|
const webBody = web ?? null;
|
|
1117
|
+
const askQuestionBody = askQuestion ?? null;
|
|
696
1118
|
const outputText = output ?? null;
|
|
697
|
-
const
|
|
1119
|
+
const card = askQuestionBody ?? terminalBody ?? diffBody ?? readBody ?? searchBody ?? webBody;
|
|
1120
|
+
const expandable = bodyRaw != null || outputText !== null || card !== null;
|
|
698
1121
|
const open = expanded && expandable;
|
|
1122
|
+
const bodyText = (0, react.useMemo)(() => open && card === null && bodyRaw != null ? formatToolBody(variant, bodyRaw) : null, [
|
|
1123
|
+
bodyRaw,
|
|
1124
|
+
card,
|
|
1125
|
+
open,
|
|
1126
|
+
variant
|
|
1127
|
+
]);
|
|
699
1128
|
const status = stateStatus$1(state, t);
|
|
700
1129
|
const failureLine = state === "error" ? errorSummary ?? null : null;
|
|
701
|
-
const summaryText = failureLine ?? summary;
|
|
702
|
-
const
|
|
1130
|
+
const summaryText = failureLine ?? terminalBody?.description ?? summary;
|
|
1131
|
+
const diffStat = (0, react.useMemo)(() => {
|
|
1132
|
+
if (diffBody === null) return null;
|
|
1133
|
+
const { added, removed } = (0, _deepseek_ai_dsh_client_ui_primitives.diffTotals)(diffBody.card.diffs);
|
|
1134
|
+
return `+${added} -${removed}`;
|
|
1135
|
+
}, [diffBody]);
|
|
1136
|
+
const suffix = failureLine === null ? summarySuffix ?? diffStat : null;
|
|
703
1137
|
const fileLink = filePath !== void 0 && onOpenFile !== void 0 && failureLine === null;
|
|
704
1138
|
const toggleExpand = () => {
|
|
705
1139
|
setExpanded((v) => !v);
|
|
@@ -711,7 +1145,7 @@ window.__ModuleLoader__.load({
|
|
|
711
1145
|
const fileLinkKeyDown = (event) => {
|
|
712
1146
|
if (event.key === "Enter" || event.key === " ") event.stopPropagation();
|
|
713
1147
|
};
|
|
714
|
-
const cardBody = variant === "code" ? null :
|
|
1148
|
+
const cardBody = variant === "code" ? null : bodyText;
|
|
715
1149
|
return (0, react_jsx_runtime.jsxs)("div", {
|
|
716
1150
|
className: ToolRow_module_css_default.root,
|
|
717
1151
|
"data-variant": variant,
|
|
@@ -748,27 +1182,30 @@ window.__ModuleLoader__.load({
|
|
|
748
1182
|
children: summaryText
|
|
749
1183
|
}),
|
|
750
1184
|
suffix !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
751
|
-
className: ToolRow_module_css_default.summarySuffix,
|
|
1185
|
+
className: clsx(ToolRow_module_css_default.summarySuffix, suffix === diffStat && ToolRow_module_css_default.diffStat),
|
|
752
1186
|
children: suffix
|
|
753
1187
|
})
|
|
754
1188
|
] }),
|
|
755
1189
|
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
756
1190
|
className: ToolRow_module_css_default.bodyWrap,
|
|
757
|
-
children: [terminalBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
1191
|
+
children: [askQuestionBody !== null ? (0, react_jsx_runtime.jsx)(AskQuestionCard, { card: askQuestionBody }) : terminalBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
758
1192
|
...terminalBody.card,
|
|
759
1193
|
maxLines: Infinity,
|
|
760
|
-
labels:
|
|
1194
|
+
labels: terminalLabels,
|
|
761
1195
|
className: ToolRow_module_css_default.terminalBody
|
|
762
1196
|
}) : diffBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
|
|
763
1197
|
...diffBody.card,
|
|
1198
|
+
labels: diffLabels,
|
|
764
1199
|
maxLines: 8,
|
|
765
1200
|
className: ToolRow_module_css_default.diffBody
|
|
766
1201
|
}) : readBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
|
|
767
1202
|
...readBody,
|
|
1203
|
+
labels: readLabels,
|
|
768
1204
|
maxLines: 8,
|
|
769
1205
|
className: ToolRow_module_css_default.readBody
|
|
770
1206
|
}) : searchBody !== null ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SearchBlock, {
|
|
771
1207
|
...searchBody.card,
|
|
1208
|
+
labels: searchLabels,
|
|
772
1209
|
maxLines: 8,
|
|
773
1210
|
className: ToolRow_module_css_default.searchBody
|
|
774
1211
|
}), searchBody.recovery !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -776,11 +1213,12 @@ window.__ModuleLoader__.load({
|
|
|
776
1213
|
children: searchBody.recovery
|
|
777
1214
|
})] }) : webBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
|
|
778
1215
|
...webBody,
|
|
1216
|
+
labels: webLabels,
|
|
779
1217
|
className: ToolRow_module_css_default.webBody
|
|
780
|
-
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [variant === "code" &&
|
|
1218
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [variant === "code" && bodyText !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
781
1219
|
className: ToolRow_module_css_default.bodyScroll,
|
|
782
1220
|
children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.CodeBlock, {
|
|
783
|
-
code:
|
|
1221
|
+
code: bodyText,
|
|
784
1222
|
lang: "typescript",
|
|
785
1223
|
copyLabel: t("copy"),
|
|
786
1224
|
copiedLabel: t("copied"),
|
|
@@ -793,7 +1231,7 @@ window.__ModuleLoader__.load({
|
|
|
793
1231
|
className: ToolRow_module_css_default.ioSection,
|
|
794
1232
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
795
1233
|
className: ToolRow_module_css_default.ioLabel,
|
|
796
|
-
children: "
|
|
1234
|
+
children: t("row.input")
|
|
797
1235
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
798
1236
|
className: ToolRow_module_css_default.ioText,
|
|
799
1237
|
children: cardBody
|
|
@@ -807,7 +1245,7 @@ window.__ModuleLoader__.load({
|
|
|
807
1245
|
className: ToolRow_module_css_default.ioSection,
|
|
808
1246
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
809
1247
|
className: ToolRow_module_css_default.ioLabel,
|
|
810
|
-
children: "
|
|
1248
|
+
children: t("row.output")
|
|
811
1249
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
812
1250
|
className: ToolRow_module_css_default.ioText,
|
|
813
1251
|
"data-error": state === "error" || void 0,
|
|
@@ -819,7 +1257,7 @@ window.__ModuleLoader__.load({
|
|
|
819
1257
|
type: "button",
|
|
820
1258
|
className: ToolRow_module_css_default.inspectButton,
|
|
821
1259
|
onClick: inspect,
|
|
822
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), "
|
|
1260
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
|
|
823
1261
|
})]
|
|
824
1262
|
})
|
|
825
1263
|
})]
|
|
@@ -851,9 +1289,9 @@ window.__ModuleLoader__.load({
|
|
|
851
1289
|
variant: model.variant,
|
|
852
1290
|
toolName,
|
|
853
1291
|
icon: VARIANT_ICONS[model.variant],
|
|
854
|
-
title: model.
|
|
855
|
-
summary:
|
|
856
|
-
|
|
1292
|
+
title: t(model.titleKey),
|
|
1293
|
+
summary: model.summary,
|
|
1294
|
+
bodyRaw: singleFile ? null : model.bodyRaw,
|
|
857
1295
|
output: model.output,
|
|
858
1296
|
errorSummary: model.errorSummary,
|
|
859
1297
|
terminal,
|
|
@@ -958,8 +1396,8 @@ window.__ModuleLoader__.load({
|
|
|
958
1396
|
* @param props - whole-Tool owner data and the Tool-owned child-slot share.
|
|
959
1397
|
* @returns the Tool call tree.
|
|
960
1398
|
*/
|
|
961
|
-
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall,
|
|
962
|
-
const home =
|
|
1399
|
+
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, useHostInfo, t }) {
|
|
1400
|
+
const home = useHostInfo((info) => info.home);
|
|
963
1401
|
const block = node.data.root;
|
|
964
1402
|
return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
|
|
965
1403
|
renderSlot,
|
|
@@ -996,35 +1434,41 @@ window.__ModuleLoader__.load({
|
|
|
996
1434
|
//#region lib/types/client/tool/ToolDetails.js
|
|
997
1435
|
/** Card-aware output body for the selected Tool call in details. */
|
|
998
1436
|
/**
|
|
999
|
-
* Render the selected Tool call's structured output when its
|
|
1000
|
-
*
|
|
1437
|
+
* Render the selected Tool call's structured output when its raw fields form a
|
|
1438
|
+
* supported root card, otherwise preserve the flattened result text.
|
|
1001
1439
|
* @param props - selected call slice, workspace root, host home, and locale seat.
|
|
1002
1440
|
* @returns the details output body.
|
|
1003
1441
|
*/
|
|
1004
|
-
function ToolDetails({ block, cwd,
|
|
1005
|
-
const home =
|
|
1006
|
-
const
|
|
1007
|
-
if (
|
|
1008
|
-
|
|
1009
|
-
children: terminal.description
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1442
|
+
function ToolDetails({ block, cwd, useHostInfo, t }) {
|
|
1443
|
+
const home = useHostInfo((info) => info.home);
|
|
1444
|
+
const terminalModel = terminalCardModel(block, cwd);
|
|
1445
|
+
if (terminalModel !== null) {
|
|
1446
|
+
const terminal = localizeTerminalCardModel(terminalModel, t);
|
|
1447
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [terminal.description !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1448
|
+
className: ToolDetails_module_css_default.description,
|
|
1449
|
+
children: terminal.description
|
|
1450
|
+
}) : null, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
1451
|
+
...terminal.card,
|
|
1452
|
+
labels: terminalBlockLabels(t),
|
|
1453
|
+
className: ToolDetails_module_css_default.cardBody
|
|
1454
|
+
})] });
|
|
1455
|
+
}
|
|
1015
1456
|
const read = readCardModel(block, cwd, home);
|
|
1016
1457
|
if (read !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
|
|
1017
1458
|
...read,
|
|
1459
|
+
labels: readBlockLabels(t),
|
|
1018
1460
|
className: ToolDetails_module_css_default.read
|
|
1019
1461
|
});
|
|
1020
1462
|
const diff = diffCardModel(block);
|
|
1021
1463
|
if (diff !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
|
|
1022
1464
|
...diff.card,
|
|
1465
|
+
labels: diffBlockLabels(t),
|
|
1023
1466
|
className: ToolDetails_module_css_default.cardBody
|
|
1024
1467
|
});
|
|
1025
1468
|
const search = searchCardModel(block);
|
|
1026
1469
|
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, {
|
|
1027
1470
|
...search.card,
|
|
1471
|
+
labels: searchBlockLabels(t),
|
|
1028
1472
|
className: ToolDetails_module_css_default.cardBody
|
|
1029
1473
|
}), search.recovery !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1030
1474
|
className: ToolDetails_module_css_default.recovery,
|
|
@@ -1035,6 +1479,7 @@ window.__ModuleLoader__.load({
|
|
|
1035
1479
|
const body = "kind" in block ? resultText(block) : "";
|
|
1036
1480
|
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
|
|
1037
1481
|
...web,
|
|
1482
|
+
labels: webBlockLabels(t),
|
|
1038
1483
|
className: ToolDetails_module_css_default.web
|
|
1039
1484
|
}), body !== "" ? (0, react_jsx_runtime.jsx)("pre", {
|
|
1040
1485
|
className: ToolDetails_module_css_default.code,
|
|
@@ -1057,40 +1502,133 @@ window.__ModuleLoader__.load({
|
|
|
1057
1502
|
const CONVERSATION_NS = "conversation";
|
|
1058
1503
|
//#endregion
|
|
1059
1504
|
//#region lib/types/client/tool/toolviews/ask-question-row.js
|
|
1060
|
-
function
|
|
1061
|
-
return typeof value === "object" && value !== null;
|
|
1505
|
+
function isRecord(value) {
|
|
1506
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1062
1507
|
}
|
|
1063
|
-
|
|
1064
|
-
* empty `selected` and no `custom`); null when answer fields are invalid. */
|
|
1065
|
-
function answeredSummary(text, t) {
|
|
1066
|
-
let parsed;
|
|
1508
|
+
function parseJson(text) {
|
|
1067
1509
|
try {
|
|
1068
|
-
|
|
1510
|
+
return JSON.parse(text);
|
|
1069
1511
|
} catch {
|
|
1070
|
-
return
|
|
1512
|
+
return;
|
|
1071
1513
|
}
|
|
1072
|
-
|
|
1514
|
+
}
|
|
1515
|
+
/** Answer records from the result JSON; null when the result is malformed. */
|
|
1516
|
+
function answerEntries(text) {
|
|
1517
|
+
const parsed = parseJson(text);
|
|
1518
|
+
if (!isRecord(parsed)) return null;
|
|
1073
1519
|
const answers = parsed.answers;
|
|
1074
|
-
if (!Array.isArray(answers) || !answers.every(
|
|
1520
|
+
if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
|
|
1521
|
+
const entries = [];
|
|
1522
|
+
for (const answer of answers) {
|
|
1523
|
+
if (typeof answer.id !== "string" || !Array.isArray(answer.selected) || !answer.selected.every((item) => typeof item === "string") || answer.custom !== void 0 && typeof answer.custom !== "string") return null;
|
|
1524
|
+
entries.push({
|
|
1525
|
+
id: answer.id,
|
|
1526
|
+
selected: answer.selected,
|
|
1527
|
+
...answer.custom === void 0 ? {} : { custom: answer.custom }
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
return entries;
|
|
1531
|
+
}
|
|
1532
|
+
/** Questions from call JSON; null when pairing with answers would be ambiguous. */
|
|
1533
|
+
function questionEntries(argsRaw) {
|
|
1534
|
+
const parsed = parseJson(argsRaw);
|
|
1535
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.questions) || parsed.questions.length === 0) return null;
|
|
1536
|
+
const questions = [];
|
|
1537
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1538
|
+
for (const question of parsed.questions) {
|
|
1539
|
+
if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string" || ids.has(question.id)) return null;
|
|
1540
|
+
ids.add(question.id);
|
|
1541
|
+
questions.push({
|
|
1542
|
+
id: question.id,
|
|
1543
|
+
question: question.question
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
return questions;
|
|
1547
|
+
}
|
|
1548
|
+
/** Pair questions with result entries by their echoed stable ids. */
|
|
1549
|
+
function pairAnswers(argsRaw, answers) {
|
|
1550
|
+
const questions = questionEntries(argsRaw);
|
|
1551
|
+
if (questions === null || questions.length !== answers.length) return null;
|
|
1552
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1553
|
+
for (const answer of answers) {
|
|
1554
|
+
if (byId.has(answer.id)) return null;
|
|
1555
|
+
byId.set(answer.id, answer);
|
|
1556
|
+
}
|
|
1557
|
+
const paired = [];
|
|
1558
|
+
for (const question of questions) {
|
|
1559
|
+
const answer = byId.get(question.id);
|
|
1560
|
+
if (answer === void 0) return null;
|
|
1561
|
+
paired.push({
|
|
1562
|
+
...question,
|
|
1563
|
+
answers: [...answer.selected, ...answer.custom === void 0 || answer.custom === "" ? [] : [answer.custom]]
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
return paired;
|
|
1567
|
+
}
|
|
1568
|
+
/** Answer summary plus structured transcript content from the two wire JSON documents. */
|
|
1569
|
+
function answeredPresentation(argsRaw, text, t) {
|
|
1570
|
+
const answers = answerEntries(text);
|
|
1571
|
+
if (answers === null) return null;
|
|
1572
|
+
const answered = answers.filter((answer) => answer.selected.length > 0 || (answer.custom ?? "") !== "").length;
|
|
1573
|
+
return {
|
|
1574
|
+
summary: t("ask.answered", {
|
|
1575
|
+
answered,
|
|
1576
|
+
total: answers.length
|
|
1577
|
+
}),
|
|
1578
|
+
questions: pairAnswers(argsRaw, answers)
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
/** Best-effort answered-count summary when strict transcript pairing fails. */
|
|
1582
|
+
function answeredSummary(text, t) {
|
|
1583
|
+
const parsed = parseJson(text);
|
|
1584
|
+
if (!isRecord(parsed)) return null;
|
|
1585
|
+
const answers = parsed.answers;
|
|
1586
|
+
if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
|
|
1075
1587
|
const answered = answers.filter((a) => Array.isArray(a.selected) && a.selected.length > 0 || typeof a.custom === "string" && a.custom !== "").length;
|
|
1076
1588
|
return t("ask.answered", {
|
|
1077
1589
|
answered,
|
|
1078
1590
|
total: answers.length
|
|
1079
1591
|
});
|
|
1080
1592
|
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Input/Output sections, ToolRow's unified expand). */
|
|
1593
|
+
/** Summarizes a pending, answered, cancelled, or interrupted question set. */
|
|
1083
1594
|
function AskQuestionRow({ toolName, block, inspect, t }) {
|
|
1084
1595
|
const model = toolRowModel(toolName, block);
|
|
1085
1596
|
const code = "kind" in block ? block.error?.code : void 0;
|
|
1597
|
+
const argsRaw = ("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "";
|
|
1086
1598
|
let summary = model.summary;
|
|
1087
1599
|
let state = model.state;
|
|
1088
|
-
|
|
1089
|
-
|
|
1600
|
+
let transcript = null;
|
|
1601
|
+
if (code === "ASK_CANCELLED") {
|
|
1602
|
+
summary = t("ask.cancelled");
|
|
1603
|
+
state = "ok";
|
|
1604
|
+
const questions = questionEntries(argsRaw);
|
|
1605
|
+
if (questions !== null) transcript = {
|
|
1606
|
+
kind: "unanswered",
|
|
1607
|
+
questions,
|
|
1608
|
+
verdict: t("ask.cancelledDetail")
|
|
1609
|
+
};
|
|
1610
|
+
} else if (code === "ASK_ABORTED") {
|
|
1090
1611
|
summary = t("ask.interrupted");
|
|
1091
1612
|
state = "stopped";
|
|
1613
|
+
const questions = questionEntries(argsRaw);
|
|
1614
|
+
if (questions !== null) transcript = {
|
|
1615
|
+
kind: "unanswered",
|
|
1616
|
+
questions,
|
|
1617
|
+
verdict: t("ask.interruptedDetail")
|
|
1618
|
+
};
|
|
1092
1619
|
} else if (model.state === "running") summary = t("ask.waiting");
|
|
1093
|
-
else if ("kind" in block && model.state === "ok")
|
|
1620
|
+
else if ("kind" in block && model.state === "ok") {
|
|
1621
|
+
const text = singleResultText(block);
|
|
1622
|
+
if (text !== void 0) {
|
|
1623
|
+
const presentation = answeredPresentation(argsRaw, text, t);
|
|
1624
|
+
summary = presentation?.summary ?? answeredSummary(text, t) ?? model.summary;
|
|
1625
|
+
if (presentation?.questions !== null && presentation?.questions !== void 0) transcript = {
|
|
1626
|
+
kind: "answered",
|
|
1627
|
+
questions: presentation.questions,
|
|
1628
|
+
skippedLabel: t("ask.skipped")
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1094
1632
|
return (0, react_jsx_runtime.jsx)(ToolRow, {
|
|
1095
1633
|
t,
|
|
1096
1634
|
variant: model.variant,
|
|
@@ -1098,23 +1636,17 @@ window.__ModuleLoader__.load({
|
|
|
1098
1636
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, {}),
|
|
1099
1637
|
title: t("ask.rowTitle"),
|
|
1100
1638
|
summary,
|
|
1101
|
-
|
|
1102
|
-
output: model.output,
|
|
1639
|
+
bodyRaw: transcript === null ? model.bodyRaw : null,
|
|
1640
|
+
output: transcript === null ? model.output : null,
|
|
1641
|
+
askQuestion: transcript,
|
|
1103
1642
|
state,
|
|
1104
1643
|
inspect
|
|
1105
1644
|
});
|
|
1106
1645
|
}
|
|
1107
|
-
/**
|
|
1108
|
-
* The ask-question row as a plain registrant plugin following the chat
|
|
1109
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1110
|
-
*/
|
|
1646
|
+
/** Registers the ask-user-question conversation row. */
|
|
1111
1647
|
const askQuestionToolview = {
|
|
1112
1648
|
name: "ask-question-toolview",
|
|
1113
1649
|
inject: ["slots"],
|
|
1114
|
-
/**
|
|
1115
|
-
* Register the ask-question row into the Tool-owned keyed view slot.
|
|
1116
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1117
|
-
*/
|
|
1118
1650
|
apply(ctx) {
|
|
1119
1651
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1120
1652
|
name: "tool.call.toolview",
|
|
@@ -1125,7 +1657,7 @@ window.__ModuleLoader__.load({
|
|
|
1125
1657
|
};
|
|
1126
1658
|
//#endregion
|
|
1127
1659
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.module.css.mjs
|
|
1128
|
-
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:1px solid var(--dsw-alias-border-l1);margin:4px 0 4px 4px}.CY-8Ka_ioCard{border:1px 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}.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{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.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:1px}.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{align-items:center;min-width:0;
|
|
1660
|
+
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:1px solid var(--dsw-alias-border-l1);margin:4px 0 4px 4px}.CY-8Ka_ioCard{border:1px 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}.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{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.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:1px}.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;position:relative;overflow:hidden}.CY-8Ka_root[data-state=running]: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 CY-8Ka_dsh-bash-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes CY-8Ka_dsh-bash-row-sweep{0%{left:-300px}90%,to{left:100%}}.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}.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;overflow:hidden}.CY-8Ka_errorSummary{color:var(--dsw-alias-state-error-primary)}.CY-8Ka_bodyWrap{flex-direction:column;display:flex}.CY-8Ka_inspectButton{border:1px solid var(--dsw-alias-border-l2);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}";
|
|
1129
1661
|
const tagId = "@deepseek-ai/dsh-client-ui-tool/bash-sample.module.css";
|
|
1130
1662
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1131
1663
|
const tag = document.createElement("style");
|
|
@@ -1174,20 +1706,23 @@ window.__ModuleLoader__.load({
|
|
|
1174
1706
|
default: return null;
|
|
1175
1707
|
}
|
|
1176
1708
|
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Bash row: icon + Bash · {description} in the shared ToolRow chrome, the
|
|
1179
|
-
* whole row toggling the command's terminal or generic error card (ToolRow's unified
|
|
1180
|
-
* expand interaction, replicated locally per the registrant posture).
|
|
1181
|
-
*/
|
|
1709
|
+
/** Renders expandable Bash output with an accessible lifecycle label. */
|
|
1182
1710
|
function BashRow({ toolName, block, sessionId, useSessions, inspect, t }) {
|
|
1183
1711
|
const model = toolRowModel(toolName, block);
|
|
1184
|
-
const
|
|
1185
|
-
const
|
|
1712
|
+
const terminalModel = terminalCardModel(block, useSessions((list) => list.byId[sessionId]?.cwd));
|
|
1713
|
+
const terminal = terminalModel === null ? null : localizeTerminalCardModel(terminalModel, t);
|
|
1714
|
+
const state = model.state === "ok" && terminalModel !== null && terminalFailed(terminalModel) ? "error" : model.state;
|
|
1186
1715
|
const status = stateStatus(state, t);
|
|
1187
1716
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
1188
|
-
const
|
|
1189
|
-
const expandable = terminal !== null ||
|
|
1717
|
+
const genericBody = terminal === null && (model.state === "error" || isSettledPersistentShellCall(block)) && (model.bodyRaw !== null || model.output !== null);
|
|
1718
|
+
const expandable = terminal !== null || genericBody;
|
|
1190
1719
|
const open = expanded && expandable;
|
|
1720
|
+
const body = (0, react.useMemo)(() => open && genericBody && model.bodyRaw !== null ? formatToolBody(model.variant, model.bodyRaw) : null, [
|
|
1721
|
+
genericBody,
|
|
1722
|
+
model.bodyRaw,
|
|
1723
|
+
model.variant,
|
|
1724
|
+
open
|
|
1725
|
+
]);
|
|
1191
1726
|
const failureLine = model.state === "error" ? model.errorSummary : null;
|
|
1192
1727
|
const toggleExpand = () => {
|
|
1193
1728
|
setExpanded((v) => !v);
|
|
@@ -1225,7 +1760,7 @@ window.__ModuleLoader__.load({
|
|
|
1225
1760
|
}),
|
|
1226
1761
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1227
1762
|
className: bash_sample_module_css_default.title,
|
|
1228
|
-
children: model.
|
|
1763
|
+
children: t(model.titleKey)
|
|
1229
1764
|
}),
|
|
1230
1765
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1231
1766
|
className: bash_sample_module_css_default.sep,
|
|
@@ -1246,17 +1781,17 @@ window.__ModuleLoader__.load({
|
|
|
1246
1781
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
1247
1782
|
className: bash_sample_module_css_default.ioCard,
|
|
1248
1783
|
children: [
|
|
1249
|
-
|
|
1784
|
+
body !== null && (0, react_jsx_runtime.jsxs)("div", {
|
|
1250
1785
|
className: bash_sample_module_css_default.ioSection,
|
|
1251
1786
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1252
1787
|
className: bash_sample_module_css_default.ioLabel,
|
|
1253
|
-
children: "
|
|
1788
|
+
children: t("row.input")
|
|
1254
1789
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1255
1790
|
className: bash_sample_module_css_default.ioText,
|
|
1256
|
-
children:
|
|
1791
|
+
children: body
|
|
1257
1792
|
})]
|
|
1258
1793
|
}),
|
|
1259
|
-
|
|
1794
|
+
body !== null && model.output !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
1260
1795
|
className: bash_sample_module_css_default.ioDivider,
|
|
1261
1796
|
"aria-hidden": true
|
|
1262
1797
|
}),
|
|
@@ -1264,10 +1799,10 @@ window.__ModuleLoader__.load({
|
|
|
1264
1799
|
className: bash_sample_module_css_default.ioSection,
|
|
1265
1800
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1266
1801
|
className: bash_sample_module_css_default.ioLabel,
|
|
1267
|
-
children: "
|
|
1802
|
+
children: t("row.output")
|
|
1268
1803
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1269
1804
|
className: bash_sample_module_css_default.ioText,
|
|
1270
|
-
"data-error":
|
|
1805
|
+
"data-error": state === "error" || void 0,
|
|
1271
1806
|
children: model.output
|
|
1272
1807
|
})]
|
|
1273
1808
|
})
|
|
@@ -1276,22 +1811,15 @@ window.__ModuleLoader__.load({
|
|
|
1276
1811
|
type: "button",
|
|
1277
1812
|
className: bash_sample_module_css_default.inspectButton,
|
|
1278
1813
|
onClick: inspect,
|
|
1279
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), "
|
|
1814
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
|
|
1280
1815
|
})]
|
|
1281
1816
|
})]
|
|
1282
1817
|
});
|
|
1283
1818
|
}
|
|
1284
|
-
/**
|
|
1285
|
-
* The sample as a plain registrant plugin. Slot injection follows the chat
|
|
1286
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1287
|
-
*/
|
|
1819
|
+
/** Registers the standalone Bash conversation-row sample. */
|
|
1288
1820
|
const bashToolviewSample = {
|
|
1289
1821
|
name: "bash-toolview-sample",
|
|
1290
1822
|
inject: ["slots"],
|
|
1291
|
-
/**
|
|
1292
|
-
* Register the bash row into the Tool-owned keyed view slot.
|
|
1293
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1294
|
-
*/
|
|
1295
1823
|
apply(ctx) {
|
|
1296
1824
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1297
1825
|
name: "tool.call.toolview",
|
|
@@ -1303,13 +1831,7 @@ window.__ModuleLoader__.load({
|
|
|
1303
1831
|
//#endregion
|
|
1304
1832
|
//#region lib/types/client/tool/toolviews/file-mutation-row.js
|
|
1305
1833
|
/**
|
|
1306
|
-
*
|
|
1307
|
-
* with the applied diff as the row's collapsed-by-default card body. The
|
|
1308
|
-
* summary is a path link (a file tool's interaction); the host's `openFile`
|
|
1309
|
-
* resolves it against the session cwd, so this passes the tool's own path
|
|
1310
|
-
* verbatim. An errored mutation has no diff card, so ToolRow surfaces the
|
|
1311
|
-
* model-facing error text through its Output section and its first line in the
|
|
1312
|
-
* collapsed summary instead.
|
|
1834
|
+
* Lets users expand an applied file diff and open the reported path.
|
|
1313
1835
|
*/
|
|
1314
1836
|
function FileMutationRow({ toolName, block, cwd, home, openFile, inspect, t }) {
|
|
1315
1837
|
const model = toolRowModel(toolName, block, cwd, home);
|
|
@@ -1319,9 +1841,8 @@ window.__ModuleLoader__.load({
|
|
|
1319
1841
|
variant: model.variant,
|
|
1320
1842
|
toolName,
|
|
1321
1843
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
|
|
1322
|
-
title: model.
|
|
1844
|
+
title: t(model.titleKey),
|
|
1323
1845
|
summary: model.summary,
|
|
1324
|
-
body: null,
|
|
1325
1846
|
output: model.output,
|
|
1326
1847
|
errorSummary: model.errorSummary,
|
|
1327
1848
|
diff,
|
|
@@ -1331,18 +1852,10 @@ window.__ModuleLoader__.load({
|
|
|
1331
1852
|
inspect
|
|
1332
1853
|
});
|
|
1333
1854
|
}
|
|
1334
|
-
/**
|
|
1335
|
-
* The file-mutation rows as a plain registrant plugin following the chat
|
|
1336
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1337
|
-
*/
|
|
1855
|
+
/** Registers the edit and write conversation rows. */
|
|
1338
1856
|
const fileMutationToolview = {
|
|
1339
1857
|
name: "file-mutation-toolview",
|
|
1340
1858
|
inject: ["slots"],
|
|
1341
|
-
/**
|
|
1342
|
-
* Register the file-mutation row into the Tool-owned keyed view slot
|
|
1343
|
-
* under both mutation tool names.
|
|
1344
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1345
|
-
*/
|
|
1346
1859
|
apply(ctx) {
|
|
1347
1860
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1348
1861
|
yield ctx.slots.register({
|
|
@@ -1361,9 +1874,7 @@ window.__ModuleLoader__.load({
|
|
|
1361
1874
|
//#endregion
|
|
1362
1875
|
//#region lib/types/client/tool/toolviews/read-row.js
|
|
1363
1876
|
/**
|
|
1364
|
-
*
|
|
1365
|
-
* read card as the row's collapsed-by-default card body. The summary path is an
|
|
1366
|
-
* openable host link when the row names a single file.
|
|
1877
|
+
* Lets users expand a completed read result and open its reported path.
|
|
1367
1878
|
*/
|
|
1368
1879
|
function ReadRow({ toolName, block, cwd, home, openFile, inspect, t }) {
|
|
1369
1880
|
const model = toolRowModel(toolName, block, cwd, home);
|
|
@@ -1373,9 +1884,8 @@ window.__ModuleLoader__.load({
|
|
|
1373
1884
|
variant: model.variant,
|
|
1374
1885
|
toolName,
|
|
1375
1886
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
|
|
1376
|
-
title: model.
|
|
1887
|
+
title: t(model.titleKey),
|
|
1377
1888
|
summary: model.summary,
|
|
1378
|
-
body: null,
|
|
1379
1889
|
output: model.output,
|
|
1380
1890
|
errorSummary: model.errorSummary,
|
|
1381
1891
|
read,
|
|
@@ -1385,17 +1895,10 @@ window.__ModuleLoader__.load({
|
|
|
1385
1895
|
inspect
|
|
1386
1896
|
});
|
|
1387
1897
|
}
|
|
1388
|
-
/**
|
|
1389
|
-
* The read row as a plain registrant plugin following the atomic Tool-view
|
|
1390
|
-
* declaration across independent activation and reload lifetimes.
|
|
1391
|
-
*/
|
|
1898
|
+
/** Registers the read tool's conversation row. */
|
|
1392
1899
|
const readToolview = {
|
|
1393
1900
|
name: "read-toolview",
|
|
1394
1901
|
inject: ["slots"],
|
|
1395
|
-
/**
|
|
1396
|
-
* Register the read row into the Tool-owned keyed view slot.
|
|
1397
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1398
|
-
*/
|
|
1399
1902
|
apply(ctx) {
|
|
1400
1903
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1401
1904
|
name: "tool.call.toolview",
|
|
@@ -1406,18 +1909,11 @@ window.__ModuleLoader__.load({
|
|
|
1406
1909
|
};
|
|
1407
1910
|
//#endregion
|
|
1408
1911
|
//#region lib/types/client/tool/toolviews/search-row.js
|
|
1409
|
-
const
|
|
1410
|
-
grep: "
|
|
1411
|
-
glob: "
|
|
1912
|
+
const SEARCH_TITLE_KEYS = {
|
|
1913
|
+
grep: "tool.title.grep",
|
|
1914
|
+
glob: "tool.title.glob"
|
|
1412
1915
|
};
|
|
1413
|
-
/**
|
|
1414
|
-
* Search row: icon + Grep/Glob · {summary} in the shared ToolRow chrome, with the
|
|
1415
|
-
* completed search's card as the row's collapsed-by-default card body (a capped
|
|
1416
|
-
* search's recovery footer rides below it, inside ToolRow). Registered under
|
|
1417
|
-
* both `grep` and `glob`; the derived model's `kind` decides the card shape. A
|
|
1418
|
-
* settled call with no search card surfaces its model-facing text through
|
|
1419
|
-
* ToolRow's Output section, since the keyed SearchRow owns this render slot.
|
|
1420
|
-
*/
|
|
1916
|
+
/** Lets users expand grep or glob results and recover capped searches. */
|
|
1421
1917
|
function SearchRow({ toolName, block, inspect, t }) {
|
|
1422
1918
|
const model = toolRowModel(toolName, block);
|
|
1423
1919
|
const search = searchCardModel(block);
|
|
@@ -1426,9 +1922,8 @@ window.__ModuleLoader__.load({
|
|
|
1426
1922
|
variant: model.variant,
|
|
1427
1923
|
toolName,
|
|
1428
1924
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
|
|
1429
|
-
title:
|
|
1430
|
-
summary:
|
|
1431
|
-
body: null,
|
|
1925
|
+
title: t(toolName === "grep" ? SEARCH_TITLE_KEYS.grep : toolName === "glob" ? SEARCH_TITLE_KEYS.glob : model.titleKey),
|
|
1926
|
+
summary: model.summary,
|
|
1432
1927
|
output: model.output,
|
|
1433
1928
|
errorSummary: model.errorSummary,
|
|
1434
1929
|
search,
|
|
@@ -1436,19 +1931,10 @@ window.__ModuleLoader__.load({
|
|
|
1436
1931
|
inspect
|
|
1437
1932
|
});
|
|
1438
1933
|
}
|
|
1439
|
-
/**
|
|
1440
|
-
* The search view follows the atomic Tool-view declaration across activation
|
|
1441
|
-
* and reload. One component registers under both keys because `grep` and
|
|
1442
|
-
* `glob` are the same visual object discriminated by the result view's `kind`.
|
|
1443
|
-
*/
|
|
1934
|
+
/** Registers the grep and glob conversation rows. */
|
|
1444
1935
|
const searchToolview = {
|
|
1445
1936
|
name: "search-toolview",
|
|
1446
1937
|
inject: ["slots"],
|
|
1447
|
-
/**
|
|
1448
|
-
* Register the search row into the Tool-owned keyed view slot under both
|
|
1449
|
-
* the `grep` and `glob` tool names.
|
|
1450
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1451
|
-
*/
|
|
1452
1938
|
apply(ctx) {
|
|
1453
1939
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1454
1940
|
yield ctx.slots.register({
|
|
@@ -1525,10 +2011,7 @@ window.__ModuleLoader__.load({
|
|
|
1525
2011
|
extra: activeExtra
|
|
1526
2012
|
};
|
|
1527
2013
|
}
|
|
1528
|
-
/**
|
|
1529
|
-
* sections, ToolRow's unified expand). Non-ok execution states keep the
|
|
1530
|
-
* shared row's dot semantics — a cancelled call wrote no todo/write, so it
|
|
1531
|
-
* must not read as a completed update. */
|
|
2014
|
+
/** Summarizes a plan update without presenting a cancelled call as completed. */
|
|
1532
2015
|
function TodoRow({ toolName, block, inspect, t }) {
|
|
1533
2016
|
const model = toolRowModel(toolName, block);
|
|
1534
2017
|
const summary = summarize(("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "", t) ?? {
|
|
@@ -1543,24 +2026,17 @@ window.__ModuleLoader__.load({
|
|
|
1543
2026
|
title: t("todo.rowTitle"),
|
|
1544
2027
|
summary: summary.text,
|
|
1545
2028
|
summarySuffix: summary.extra > 0 ? `+${summary.extra}` : null,
|
|
1546
|
-
|
|
2029
|
+
bodyRaw: model.bodyRaw,
|
|
1547
2030
|
output: model.output,
|
|
1548
2031
|
errorSummary: model.errorSummary,
|
|
1549
2032
|
state: model.state,
|
|
1550
2033
|
inspect
|
|
1551
2034
|
});
|
|
1552
2035
|
}
|
|
1553
|
-
/**
|
|
1554
|
-
* The todo row as a plain registrant plugin following the atomic Tool-view
|
|
1555
|
-
* declaration across independent activation and reload lifetimes.
|
|
1556
|
-
*/
|
|
2036
|
+
/** Registers the todo conversation row. */
|
|
1557
2037
|
const todoToolview = {
|
|
1558
2038
|
name: "todo-toolview",
|
|
1559
2039
|
inject: ["slots"],
|
|
1560
|
-
/**
|
|
1561
|
-
* Register the todo row into the Tool-owned keyed view slot.
|
|
1562
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1563
|
-
*/
|
|
1564
2040
|
apply(ctx) {
|
|
1565
2041
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1566
2042
|
name: "tool.call.toolview",
|
|
@@ -1571,16 +2047,11 @@ window.__ModuleLoader__.load({
|
|
|
1571
2047
|
};
|
|
1572
2048
|
//#endregion
|
|
1573
2049
|
//#region lib/types/client/tool/toolviews/web-row.js
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
web_fetch: "Fetch"
|
|
2050
|
+
const WEB_TITLE_KEYS = {
|
|
2051
|
+
web_search: "tool.title.webSearch",
|
|
2052
|
+
web_fetch: "tool.title.webFetch"
|
|
1578
2053
|
};
|
|
1579
|
-
/**
|
|
1580
|
-
* Web row: icon + Search/Fetch · {summary} in the shared ToolRow chrome, with
|
|
1581
|
-
* the completed retrieval's web card as the row's collapsed-by-default card
|
|
1582
|
-
* body. The row discriminates on `toolName` only to pick its icon and title.
|
|
1583
|
-
*/
|
|
2054
|
+
/** Lets users expand a completed web search or fetch result. */
|
|
1584
2055
|
function WebRow({ toolName, block, inspect, t }) {
|
|
1585
2056
|
const model = toolRowModel(toolName, block);
|
|
1586
2057
|
const web = webCardModel(block);
|
|
@@ -1590,9 +2061,8 @@ window.__ModuleLoader__.load({
|
|
|
1590
2061
|
variant: model.variant,
|
|
1591
2062
|
toolName,
|
|
1592
2063
|
icon,
|
|
1593
|
-
title:
|
|
2064
|
+
title: t(toolName === "web_search" ? WEB_TITLE_KEYS.web_search : toolName === "web_fetch" ? WEB_TITLE_KEYS.web_fetch : model.titleKey),
|
|
1594
2065
|
summary: model.summary,
|
|
1595
|
-
body: null,
|
|
1596
2066
|
output: model.output,
|
|
1597
2067
|
errorSummary: model.errorSummary,
|
|
1598
2068
|
web,
|
|
@@ -1600,17 +2070,10 @@ window.__ModuleLoader__.load({
|
|
|
1600
2070
|
inspect
|
|
1601
2071
|
});
|
|
1602
2072
|
}
|
|
1603
|
-
/**
|
|
1604
|
-
* The web rows follow the atomic Tool-view declaration across activation and
|
|
1605
|
-
* reload. One WebRow component registers under both web tool names.
|
|
1606
|
-
*/
|
|
2073
|
+
/** Registers the web search and fetch conversation rows. */
|
|
1607
2074
|
const webToolview = {
|
|
1608
2075
|
name: "web-toolview",
|
|
1609
2076
|
inject: ["slots"],
|
|
1610
|
-
/**
|
|
1611
|
-
* Register the web row under both web tool names' keyed toolview holes.
|
|
1612
|
-
* @param ctx - registrant context (disposal rides ctx.effect inside slots.register).
|
|
1613
|
-
*/
|
|
1614
2077
|
apply(ctx) {
|
|
1615
2078
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1616
2079
|
yield ctx.slots.register({
|
|
@@ -1628,15 +2091,18 @@ window.__ModuleLoader__.load({
|
|
|
1628
2091
|
};
|
|
1629
2092
|
//#endregion
|
|
1630
2093
|
//#region lib/types/client/apply.js
|
|
1631
|
-
/** Required services: the slot registry and the Host
|
|
1632
|
-
const inject = ["slots", "
|
|
2094
|
+
/** Required services: the slot registry and the Remote face carrying the Host home used for POSIX `~`. */
|
|
2095
|
+
const inject = ["slots", "remote"];
|
|
1633
2096
|
/**
|
|
1634
2097
|
* Mount the whole-Tool renderers and built-in atomic Tool registrations.
|
|
1635
2098
|
* @param ctx - Client root context.
|
|
1636
2099
|
*/
|
|
1637
2100
|
function apply(ctx) {
|
|
1638
|
-
const
|
|
1639
|
-
|
|
2101
|
+
const hostInfo = {
|
|
2102
|
+
getSnapshot: () => ctx.remote.$host,
|
|
2103
|
+
subscribe: (listener) => ctx.on("connection/reset", listener)
|
|
2104
|
+
};
|
|
2105
|
+
const toolInject = () => ({ hooks: { hostInfo } });
|
|
1640
2106
|
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
1641
2107
|
name: "conversation.chat.node",
|
|
1642
2108
|
key: "tool-call",
|