@deepseek-ai/dsh-client-ui-tool 0.1.1-rc.2 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +79 -17
- package/README.zh.md +80 -18
- package/lib/client.js +868 -418
- 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 +7 -31
- 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 +22 -5
- 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.
|
|
@@ -182,14 +209,14 @@ window.__ModuleLoader__.load({
|
|
|
182
209
|
const done = "kind" in block;
|
|
183
210
|
const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? "";
|
|
184
211
|
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 !== "" &&
|
|
212
|
+
const base = argsRaw === "" ? block.callId : abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home);
|
|
213
|
+
const toolTitleKey = TOOL_TITLE_KEYS[toolName];
|
|
214
|
+
const summary = variant === "others" && toolName !== "" && toolTitleKey === void 0 ? `${toolName} · ${base}` : base;
|
|
188
215
|
const output = done ? resultText(block) || null : null;
|
|
189
216
|
const errorSummary = state === "error" && output !== null ? firstLine(output) : null;
|
|
190
217
|
return {
|
|
191
218
|
variant,
|
|
192
|
-
|
|
219
|
+
titleKey: toolTitleKey ?? VARIANT_TITLE_KEYS[variant],
|
|
193
220
|
summary,
|
|
194
221
|
filePath: deriveFilePath(variant, argsRaw),
|
|
195
222
|
body: deriveBody(variant, argsRaw),
|
|
@@ -199,72 +226,129 @@ window.__ModuleLoader__.load({
|
|
|
199
226
|
};
|
|
200
227
|
}
|
|
201
228
|
//#endregion
|
|
202
|
-
//#region lib/types/client/tool/models/
|
|
229
|
+
//#region lib/types/client/tool/models/raw-tool-call.js
|
|
230
|
+
const parsedCalls = /* @__PURE__ */ new WeakMap();
|
|
203
231
|
/**
|
|
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
|
|
232
|
+
* Parse the call head paired with one immutable Tool block.
|
|
233
|
+
* @param block - running or settled Tool block.
|
|
234
|
+
* @returns the Tool name and object arguments, or null when the call head or valid JSON object is unavailable.
|
|
217
235
|
*/
|
|
236
|
+
function parsedToolCall(block) {
|
|
237
|
+
const cached = parsedCalls.get(block);
|
|
238
|
+
if (cached !== void 0 || parsedCalls.has(block)) return cached ?? null;
|
|
239
|
+
const call = "kind" in block ? block.call : block;
|
|
240
|
+
if (call === null) {
|
|
241
|
+
parsedCalls.set(block, null);
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
let value;
|
|
245
|
+
try {
|
|
246
|
+
value = JSON.parse(call.argsRaw);
|
|
247
|
+
} catch {
|
|
248
|
+
parsedCalls.set(block, null);
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
252
|
+
parsedCalls.set(block, null);
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const parsed = {
|
|
256
|
+
name: call.name,
|
|
257
|
+
args: value
|
|
258
|
+
};
|
|
259
|
+
parsedCalls.set(block, parsed);
|
|
260
|
+
return parsed;
|
|
261
|
+
}
|
|
218
262
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
263
|
+
* Read the exact single text block consumed by first-party card derivations.
|
|
264
|
+
* @param block - settled Tool result.
|
|
265
|
+
* @returns its text, or undefined for any other content layout.
|
|
266
|
+
*/
|
|
267
|
+
function singleResultText(block) {
|
|
268
|
+
if (block.content.length !== 1) return void 0;
|
|
269
|
+
const only = block.content[0];
|
|
270
|
+
return only?.type === "text" ? only.text : void 0;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Validate the optional escalation pair shared by first-party shell and file
|
|
274
|
+
* mutation tools.
|
|
275
|
+
* @param args - parsed open-root Tool arguments.
|
|
276
|
+
* @returns whether the declared escalation fields form a valid pair.
|
|
277
|
+
*/
|
|
278
|
+
function validEscalationFields(args) {
|
|
279
|
+
const permission = args.sandbox_permissions;
|
|
280
|
+
const justification = args.justification;
|
|
281
|
+
if (permission === void 0 && justification === void 0) return true;
|
|
282
|
+
if (permission !== "workspace-write" && permission !== "danger-full-access") return false;
|
|
283
|
+
return typeof justification === "string" && justification.trim() !== "";
|
|
284
|
+
}
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region lib/types/client/tool/models/read-card-model.js
|
|
287
|
+
function validReadCall(block) {
|
|
288
|
+
const call = parsedToolCall(block);
|
|
289
|
+
if (call?.name !== "read") return false;
|
|
290
|
+
const { file_path: path, offset, limit } = call.args;
|
|
291
|
+
if (typeof path !== "string" || path.trim() === "") return false;
|
|
292
|
+
if (offset !== void 0 && (typeof offset !== "number" || !Number.isInteger(offset) || offset < 1)) return false;
|
|
293
|
+
if (limit !== void 0 && (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1)) return false;
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
function readMeta(meta) {
|
|
297
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return null;
|
|
298
|
+
const { path, offset, lines, totalLines, lang } = meta;
|
|
299
|
+
if (typeof path !== "string" || typeof offset !== "number" || !Number.isInteger(offset) || offset < 1) return null;
|
|
300
|
+
if (typeof totalLines !== "number" || !Number.isInteger(totalLines) || totalLines < 0 || !Array.isArray(lines)) return null;
|
|
301
|
+
if (lang !== void 0 && typeof lang !== "string") return null;
|
|
302
|
+
const narrowed = [];
|
|
303
|
+
let previous = offset - 1;
|
|
304
|
+
for (const line of lines) {
|
|
305
|
+
if (typeof line !== "object" || line === null || Array.isArray(line)) return null;
|
|
306
|
+
const { number, text } = line;
|
|
307
|
+
if (typeof number !== "number" || !Number.isInteger(number) || number < 1 || number <= previous) return null;
|
|
308
|
+
if (number > totalLines || typeof text !== "string") return null;
|
|
309
|
+
previous = number;
|
|
310
|
+
narrowed.push({
|
|
311
|
+
number,
|
|
312
|
+
text
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
path,
|
|
317
|
+
offset,
|
|
318
|
+
lines: narrowed,
|
|
319
|
+
totalLines,
|
|
320
|
+
...lang === void 0 ? {} : { lang }
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Derive a settled root read card after validating its persisted metadata and
|
|
325
|
+
* model-facing read envelope.
|
|
326
|
+
* @param block - running or settled Tool block.
|
|
238
327
|
* @param sessionCwd - the session workspace root; a workspace-rooted absolute
|
|
239
328
|
* path label displays relative to it. Absent leaves the path as authored.
|
|
240
329
|
* @param home - host account home; a leftover POSIX home path displays as `~`.
|
|
241
330
|
* @returns the read-card props, or null for the generic path.
|
|
242
331
|
*/
|
|
243
332
|
function readCardModel(block, sessionCwd, home) {
|
|
244
|
-
if (!("kind" in block)) return null;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
333
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
334
|
+
if (!validReadCall(block)) return null;
|
|
335
|
+
const meta = readMeta(block.meta);
|
|
336
|
+
if (meta === null) return null;
|
|
337
|
+
const text = singleResultText(block);
|
|
338
|
+
if (text === void 0) return null;
|
|
339
|
+
if (/^<path>[^\n]*<\/path>\n<type>file<\/type>\n<content>\n([\s\S]*)\n<\/content>$/u.exec(text)?.[1] === void 0) return null;
|
|
251
340
|
return {
|
|
252
|
-
label:
|
|
253
|
-
lines,
|
|
254
|
-
totalLines:
|
|
255
|
-
lang:
|
|
341
|
+
label: abbreviateHomePath(relativizeToCwd(meta.path, sessionCwd), home),
|
|
342
|
+
lines: meta.lines,
|
|
343
|
+
totalLines: meta.totalLines,
|
|
344
|
+
lang: meta.lang
|
|
256
345
|
};
|
|
257
346
|
}
|
|
258
347
|
//#endregion
|
|
259
348
|
//#region lib/types/client/tool/models/diff-card-model.js
|
|
260
349
|
/**
|
|
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.
|
|
350
|
+
* Narrow opaque result metadata's `diffs` to well-formed hunks.
|
|
351
|
+
* @param diffs - the metadata field to validate.
|
|
268
352
|
* @returns the validated hunks, or null when the payload is not usable.
|
|
269
353
|
*/
|
|
270
354
|
function narrowDiffs(diffs) {
|
|
@@ -284,110 +368,182 @@ window.__ModuleLoader__.load({
|
|
|
284
368
|
}
|
|
285
369
|
return out;
|
|
286
370
|
}
|
|
371
|
+
function intendedDiff(block) {
|
|
372
|
+
const parsed = parsedToolCall(block);
|
|
373
|
+
if (parsed === null) return null;
|
|
374
|
+
if (parsed.name === "str_replace_editor") {
|
|
375
|
+
const { command, path, file_text: fileText, old_str: oldText, new_str: newText } = parsed.args;
|
|
376
|
+
if (typeof path !== "string" || path.trim() === "") return null;
|
|
377
|
+
if (command === "create") {
|
|
378
|
+
if (fileText !== void 0 && typeof fileText !== "string") return null;
|
|
379
|
+
return {
|
|
380
|
+
tool: "str_replace_editor",
|
|
381
|
+
diff: {
|
|
382
|
+
path,
|
|
383
|
+
oldText: null,
|
|
384
|
+
newText: fileText ?? ""
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (command === "str_replace") {
|
|
389
|
+
if (oldText !== void 0 && typeof oldText !== "string") return null;
|
|
390
|
+
if (newText !== void 0 && typeof newText !== "string") return null;
|
|
391
|
+
return {
|
|
392
|
+
tool: "str_replace_editor",
|
|
393
|
+
diff: {
|
|
394
|
+
path,
|
|
395
|
+
oldText: oldText ?? null,
|
|
396
|
+
newText: newText ?? ""
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const { file_path: path } = parsed.args;
|
|
403
|
+
if (typeof path !== "string" || path.trim() === "") return null;
|
|
404
|
+
if (!validEscalationFields(parsed.args)) return null;
|
|
405
|
+
if (parsed.name === "write") {
|
|
406
|
+
const { content } = parsed.args;
|
|
407
|
+
return typeof content === "string" ? {
|
|
408
|
+
tool: "write",
|
|
409
|
+
diff: {
|
|
410
|
+
path,
|
|
411
|
+
oldText: null,
|
|
412
|
+
newText: content
|
|
413
|
+
}
|
|
414
|
+
} : null;
|
|
415
|
+
}
|
|
416
|
+
if (parsed.name !== "edit") return null;
|
|
417
|
+
const { old_string: oldText, new_string: newText, replace_all: replaceAll } = parsed.args;
|
|
418
|
+
if (typeof oldText !== "string" || typeof newText !== "string") return null;
|
|
419
|
+
if (replaceAll !== void 0 && typeof replaceAll !== "boolean") return null;
|
|
420
|
+
return {
|
|
421
|
+
tool: "edit",
|
|
422
|
+
diff: {
|
|
423
|
+
path,
|
|
424
|
+
oldText: oldText || null,
|
|
425
|
+
newText
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function appliedDiffs(meta) {
|
|
430
|
+
if (typeof meta !== "object" || meta === null || Array.isArray(meta)) return null;
|
|
431
|
+
const diffs = meta.diffs;
|
|
432
|
+
if (!Array.isArray(diffs)) return null;
|
|
433
|
+
if (diffs.length === 0) return "empty";
|
|
434
|
+
return narrowDiffs(diffs);
|
|
435
|
+
}
|
|
287
436
|
/**
|
|
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.
|
|
437
|
+
* Derive running diffs for root write/edit and `str_replace_editor`
|
|
438
|
+
* create/replace calls, plus applied settled diffs for root write/edit calls.
|
|
439
|
+
* A successful write with valid empty metadata uses its argument-derived
|
|
440
|
+
* whole-file diff, matching create and identical-overwrite presentation;
|
|
441
|
+
* `str_replace_editor` settles through Generic because it has no result view.
|
|
442
|
+
* @param block - running or settled Tool block.
|
|
307
443
|
* @returns the diff-card props, or null for the generic path.
|
|
308
444
|
*/
|
|
309
445
|
function diffCardModel(block) {
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
446
|
+
if (block.parentCallId !== void 0) return null;
|
|
447
|
+
const intended = intendedDiff(block);
|
|
448
|
+
if (intended === null) return null;
|
|
449
|
+
if (!("kind" in block)) return { card: { diffs: [intended.diff] } };
|
|
450
|
+
if (intended.tool === "str_replace_editor") return null;
|
|
451
|
+
if (block.isError) return null;
|
|
452
|
+
const applied = appliedDiffs(block.meta);
|
|
453
|
+
if (applied === null || applied === "empty") return intended.tool === "write" ? { card: { diffs: [intended.diff] } } : null;
|
|
454
|
+
return { card: { diffs: applied } };
|
|
318
455
|
}
|
|
319
456
|
//#endregion
|
|
320
457
|
//#region lib/types/client/tool/models/search-card-model.js
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
458
|
+
function validSearchCall(block) {
|
|
459
|
+
const call = parsedToolCall(block);
|
|
460
|
+
if (call === null) return null;
|
|
461
|
+
const { pattern, path } = call.args;
|
|
462
|
+
if (typeof pattern !== "string") return null;
|
|
463
|
+
if (call.name === "grep" && pattern === "") return null;
|
|
464
|
+
if (call.name === "glob" && pattern.trim() === "") return null;
|
|
465
|
+
if (call.name !== "grep" && call.name !== "glob") return null;
|
|
466
|
+
if (path !== void 0 && (typeof path !== "string" || path.trim() === "")) return null;
|
|
467
|
+
if (call.name === "grep") {
|
|
468
|
+
const { include } = call.args;
|
|
469
|
+
if (include !== void 0 && (typeof include !== "string" || !validInclude(include))) return null;
|
|
470
|
+
}
|
|
471
|
+
return call.name;
|
|
472
|
+
}
|
|
473
|
+
function validInclude(include) {
|
|
474
|
+
if (include.trim() === "" || include.startsWith("!")) return false;
|
|
475
|
+
let braceDepth = 0;
|
|
476
|
+
for (const character of include) if (character === "{") braceDepth += 1;
|
|
477
|
+
else if (character === "}") braceDepth = Math.max(0, braceDepth - 1);
|
|
478
|
+
else if (character === "," && braceDepth === 0) return false;
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
function searchFiles(value) {
|
|
482
|
+
if (!Array.isArray(value)) return null;
|
|
483
|
+
const files = [];
|
|
484
|
+
for (const file of value) {
|
|
485
|
+
if (typeof file !== "object" || file === null || Array.isArray(file)) return null;
|
|
486
|
+
const { path, matches } = file;
|
|
487
|
+
if (typeof path !== "string" || !Array.isArray(matches)) return null;
|
|
488
|
+
const narrowed = [];
|
|
489
|
+
for (const match of matches) {
|
|
490
|
+
if (typeof match !== "object" || match === null || Array.isArray(match)) return null;
|
|
491
|
+
const { lineNumber, line } = match;
|
|
492
|
+
if (typeof lineNumber !== "number" || !Number.isInteger(lineNumber) || lineNumber < 1) return null;
|
|
493
|
+
if (typeof line !== "string") return null;
|
|
494
|
+
narrowed.push({
|
|
495
|
+
lineNumber,
|
|
496
|
+
line
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
files.push({
|
|
500
|
+
path,
|
|
501
|
+
matches: narrowed
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return files;
|
|
333
505
|
}
|
|
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
506
|
function flattenContent(content) {
|
|
344
507
|
const text = content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
345
508
|
return text === "" ? void 0 : text;
|
|
346
509
|
}
|
|
347
510
|
/**
|
|
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.
|
|
511
|
+
* Derive a settled root grep/glob card from persisted metadata.
|
|
512
|
+
* @param block - running or settled Tool block.
|
|
513
|
+
* @returns search-card props, or null for the generic path.
|
|
361
514
|
*/
|
|
362
515
|
function searchCardModel(block) {
|
|
363
|
-
if (!("kind" in block)) return null;
|
|
364
|
-
const
|
|
365
|
-
if (
|
|
516
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
517
|
+
const tool = validSearchCall(block);
|
|
518
|
+
if (tool === null) return null;
|
|
519
|
+
if (typeof block.meta !== "object" || block.meta === null || Array.isArray(block.meta)) return null;
|
|
520
|
+
const meta = block.meta;
|
|
521
|
+
if (typeof meta.truncated !== "boolean") return null;
|
|
522
|
+
if (typeof meta.total !== "number" || !Number.isInteger(meta.total) || meta.total < 0) return null;
|
|
366
523
|
const common = {
|
|
367
|
-
truncated:
|
|
368
|
-
total:
|
|
524
|
+
truncated: meta.truncated,
|
|
525
|
+
total: meta.total
|
|
369
526
|
};
|
|
370
|
-
const recovery =
|
|
371
|
-
if (
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
527
|
+
const recovery = meta.truncated ? flattenContent(block.content) : void 0;
|
|
528
|
+
if (tool === "grep") {
|
|
529
|
+
if (meta.shape !== "matches") return null;
|
|
530
|
+
const files = searchFiles(meta.files);
|
|
531
|
+
return files === null ? null : {
|
|
375
532
|
recovery,
|
|
376
533
|
card: {
|
|
377
534
|
kind: "matches",
|
|
378
|
-
files
|
|
535
|
+
files,
|
|
379
536
|
...common
|
|
380
537
|
}
|
|
381
538
|
};
|
|
382
539
|
}
|
|
383
|
-
if (
|
|
384
|
-
if (!
|
|
540
|
+
if (meta.shape !== "paths" || !Array.isArray(meta.paths)) return null;
|
|
541
|
+
if (!meta.paths.every((path) => typeof path === "string")) return null;
|
|
385
542
|
return {
|
|
386
|
-
title: result.title,
|
|
387
543
|
recovery,
|
|
388
544
|
card: {
|
|
389
545
|
kind: "paths",
|
|
390
|
-
paths:
|
|
546
|
+
paths: [...meta.paths],
|
|
391
547
|
...common
|
|
392
548
|
}
|
|
393
549
|
};
|
|
@@ -395,16 +551,6 @@ window.__ModuleLoader__.load({
|
|
|
395
551
|
//#endregion
|
|
396
552
|
//#region lib/types/client/tool/models/terminal-card-model.js
|
|
397
553
|
/**
|
|
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
554
|
* Build the TerminalBlock display copy from the conversation locale seat —
|
|
409
555
|
* the one place the primitive's label surface pairs with this package's
|
|
410
556
|
* dictionary, shared by every terminal render site (chat row, bash row,
|
|
@@ -429,6 +575,29 @@ window.__ModuleLoader__.load({
|
|
|
429
575
|
};
|
|
430
576
|
}
|
|
431
577
|
/**
|
|
578
|
+
* Resolve locale-owned `terminal_send` copy while preserving Tool-authored
|
|
579
|
+
* shell commands and descriptions verbatim.
|
|
580
|
+
* @param model - locale-neutral terminal card data.
|
|
581
|
+
* @param t - the render site's conversation locale seat.
|
|
582
|
+
* @returns terminal props and description ready for rendering.
|
|
583
|
+
*/
|
|
584
|
+
function localizeTerminalCardModel(model, t) {
|
|
585
|
+
if (model.copy.kind === "shell") return {
|
|
586
|
+
card: {
|
|
587
|
+
command: model.copy.command,
|
|
588
|
+
...model.card
|
|
589
|
+
},
|
|
590
|
+
description: model.copy.description
|
|
591
|
+
};
|
|
592
|
+
return {
|
|
593
|
+
card: {
|
|
594
|
+
command: model.copy.text === "" ? t("terminal.sendInput") : model.copy.text,
|
|
595
|
+
...model.card
|
|
596
|
+
},
|
|
597
|
+
description: t("terminal.session", { sessionId: model.copy.sessionId })
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
432
601
|
* True when a settled terminal card reports a failing exit — a non-zero code
|
|
433
602
|
* or a terminating signal. The bash tool settles a failing command as a
|
|
434
603
|
* completed call (`isError` stays false: the exit status is result data), so
|
|
@@ -442,21 +611,18 @@ window.__ModuleLoader__.load({
|
|
|
442
611
|
return running !== true && (exitCode !== void 0 && exitCode !== 0 || signal !== void 0);
|
|
443
612
|
}
|
|
444
613
|
/**
|
|
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.
|
|
614
|
+
* Resolve a shell call's workdir for display: an absolute path is used as-is,
|
|
615
|
+
* a relative one joins under the session workspace, and an omitted one is the
|
|
616
|
+
* session workspace. Without a session cwd, a relative path stays as authored
|
|
617
|
+
* and an omitted one stays absent.
|
|
618
|
+
* @param workdir - the raw call's workdir, if any.
|
|
453
619
|
* @param sessionCwd - the session workspace root, if the caller knows it.
|
|
454
620
|
* @returns the working directory for the prompt label, or undefined.
|
|
455
621
|
*/
|
|
456
|
-
function resolveTerminalCwd(
|
|
457
|
-
if (
|
|
458
|
-
if (sessionCwd === void 0 || sessionCwd === "") return normalizeSegments(
|
|
459
|
-
return normalizeSegments(
|
|
622
|
+
function resolveTerminalCwd(workdir, sessionCwd) {
|
|
623
|
+
if (workdir === void 0 || workdir === "") return sessionCwd;
|
|
624
|
+
if (sessionCwd === void 0 || sessionCwd === "") return normalizeSegments(workdir);
|
|
625
|
+
return normalizeSegments(resolveWorkspacePath(sessionCwd, workdir));
|
|
460
626
|
}
|
|
461
627
|
/**
|
|
462
628
|
* Collapse `.` and `..` segments so the prompt label names the directory the
|
|
@@ -508,104 +674,195 @@ window.__ModuleLoader__.load({
|
|
|
508
674
|
}
|
|
509
675
|
return kept.join(separator);
|
|
510
676
|
}
|
|
677
|
+
function shellCall(name, args) {
|
|
678
|
+
if (name !== "bash" && name !== "pwsh") return null;
|
|
679
|
+
const { command, description, timeoutMs, workdir, run_in_background: background } = args;
|
|
680
|
+
if (typeof command !== "string" || command.trim() === "") return null;
|
|
681
|
+
if (timeoutMs !== void 0 && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs <= 0)) return null;
|
|
682
|
+
if (workdir !== void 0 && typeof workdir !== "string") return null;
|
|
683
|
+
if (background !== void 0 && typeof background !== "boolean") return null;
|
|
684
|
+
if (!validEscalationFields(args)) return null;
|
|
685
|
+
if (description === void 0) return {
|
|
686
|
+
kind: "shell",
|
|
687
|
+
command,
|
|
688
|
+
description: void 0,
|
|
689
|
+
workdir: void 0,
|
|
690
|
+
persistent: true,
|
|
691
|
+
background: false
|
|
692
|
+
};
|
|
693
|
+
if (typeof description !== "string" || description.trim() === "") return null;
|
|
694
|
+
return {
|
|
695
|
+
kind: "shell",
|
|
696
|
+
command,
|
|
697
|
+
description,
|
|
698
|
+
workdir,
|
|
699
|
+
persistent: false,
|
|
700
|
+
background: background === true
|
|
701
|
+
};
|
|
702
|
+
}
|
|
511
703
|
/**
|
|
512
|
-
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
704
|
+
* Identify a settled root call from the persistent Bash or PowerShell tool.
|
|
705
|
+
* Its result stays on the generic input/output path because the persistent
|
|
706
|
+
* shell can report resets and partial output without one process exit status.
|
|
707
|
+
* @param block - running or settled Tool block.
|
|
708
|
+
* @returns whether the block is a settled persistent-shell call.
|
|
709
|
+
*/
|
|
710
|
+
function isSettledPersistentShellCall(block) {
|
|
711
|
+
if (!("kind" in block) || block.parentCallId !== void 0) return false;
|
|
712
|
+
const parsed = parsedToolCall(block);
|
|
713
|
+
if (parsed === null) return false;
|
|
714
|
+
return shellCall(parsed.name, parsed.args)?.persistent === true;
|
|
715
|
+
}
|
|
716
|
+
function terminalSendCall(name, args) {
|
|
717
|
+
if (name !== "terminal_send") return null;
|
|
718
|
+
const { sessionId, text, submit, run_in_background: background } = args;
|
|
719
|
+
if (typeof sessionId !== "string" || sessionId === "" || typeof text !== "string") return null;
|
|
720
|
+
if (submit !== void 0 && typeof submit !== "boolean") return null;
|
|
721
|
+
if (background !== void 0 && typeof background !== "boolean") return null;
|
|
722
|
+
return {
|
|
723
|
+
kind: "terminal-send",
|
|
724
|
+
text,
|
|
725
|
+
sessionId,
|
|
726
|
+
background: background === true
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Parse the marker literals owned by `@deepseek-ai/dsh-shell/render` without
|
|
731
|
+
* importing that Host-only package into the Client dependency graph.
|
|
732
|
+
* @param text - rendered shell result text.
|
|
733
|
+
* @returns output with a trailing exit-code or signal marker extracted.
|
|
734
|
+
*/
|
|
735
|
+
function parseExitStatus(text) {
|
|
736
|
+
const signal = /\n\[killed by signal: ([^\]\n]+)\]$/.exec(text);
|
|
737
|
+
if (signal?.[1] !== void 0) return {
|
|
738
|
+
output: text.slice(0, signal.index),
|
|
739
|
+
signal: signal[1]
|
|
740
|
+
};
|
|
741
|
+
const exit = /\n\[exit code: (\d+)\]$/.exec(text);
|
|
742
|
+
if (exit?.[1] !== void 0) return {
|
|
743
|
+
output: text.slice(0, exit.index),
|
|
744
|
+
exitCode: Number(exit[1])
|
|
745
|
+
};
|
|
746
|
+
return {
|
|
747
|
+
output: text,
|
|
748
|
+
exitCode: 0
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Derive terminal props for supported root shell and terminal-send calls.
|
|
753
|
+
* Standard shell results parse their final status marker; persistent shell
|
|
754
|
+
* results, background calls, errors, malformed input, or child dispatches use
|
|
755
|
+
* the generic path. {@link isSettledPersistentShellCall} lets that generic
|
|
756
|
+
* persistent result remain expandable without inventing one process status.
|
|
757
|
+
* @param block - running or settled Tool block.
|
|
758
|
+
* @param sessionCwd - session workspace root used to resolve workdir.
|
|
759
|
+
* @returns locale-neutral terminal-card data, or null for the generic path.
|
|
536
760
|
*/
|
|
537
761
|
function terminalCardModel(block, sessionCwd) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
762
|
+
if (block.parentCallId !== void 0) return null;
|
|
763
|
+
const parsed = parsedToolCall(block);
|
|
764
|
+
if (parsed === null) return null;
|
|
765
|
+
const call = shellCall(parsed.name, parsed.args) ?? terminalSendCall(parsed.name, parsed.args);
|
|
766
|
+
if (call === null || call.background) return null;
|
|
767
|
+
const copy = call.kind === "shell" ? {
|
|
768
|
+
kind: "shell",
|
|
769
|
+
command: call.command,
|
|
770
|
+
description: call.description
|
|
771
|
+
} : {
|
|
772
|
+
kind: "terminal-send",
|
|
773
|
+
text: call.text,
|
|
774
|
+
sessionId: call.sessionId
|
|
775
|
+
};
|
|
776
|
+
const cwd = resolveTerminalCwd(call.kind === "shell" ? call.workdir : void 0, sessionCwd);
|
|
777
|
+
if (!("kind" in block)) return {
|
|
778
|
+
copy,
|
|
541
779
|
card: {
|
|
542
|
-
|
|
543
|
-
cwd: resolveTerminalCwd(call.cwd, sessionCwd),
|
|
780
|
+
cwd,
|
|
544
781
|
output: void 0,
|
|
545
782
|
exitCode: void 0,
|
|
546
783
|
signal: void 0,
|
|
547
784
|
running: true
|
|
548
785
|
}
|
|
549
786
|
};
|
|
550
|
-
|
|
551
|
-
|
|
787
|
+
if (block.isError || call.kind === "shell" && call.persistent) return null;
|
|
788
|
+
const output = singleResultText(block);
|
|
789
|
+
if (output === void 0) return null;
|
|
790
|
+
const status = call.kind === "terminal-send" ? { output } : parseExitStatus(output);
|
|
552
791
|
return {
|
|
553
|
-
|
|
792
|
+
copy,
|
|
554
793
|
card: {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
signal: result.signal,
|
|
794
|
+
cwd,
|
|
795
|
+
output: status.output,
|
|
796
|
+
exitCode: status.exitCode,
|
|
797
|
+
signal: status.signal,
|
|
560
798
|
running: false
|
|
561
799
|
}
|
|
562
800
|
};
|
|
563
801
|
}
|
|
564
802
|
//#endregion
|
|
565
803
|
//#region lib/types/client/tool/models/web-card-model.js
|
|
804
|
+
function validWebCall(block) {
|
|
805
|
+
const call = parsedToolCall(block);
|
|
806
|
+
if (call === null) return null;
|
|
807
|
+
if (call.name === "web_search") {
|
|
808
|
+
const { queries } = call.args;
|
|
809
|
+
if (!Array.isArray(queries) || queries.length === 0) return null;
|
|
810
|
+
return queries.every((query) => typeof query === "string" && query.trim() !== "") ? call.name : null;
|
|
811
|
+
}
|
|
812
|
+
if (call.name === "web_fetch") {
|
|
813
|
+
const { url } = call.args;
|
|
814
|
+
return typeof url === "string" && url.trim() !== "" ? call.name : null;
|
|
815
|
+
}
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
function webSources(value) {
|
|
819
|
+
if (!Array.isArray(value)) return null;
|
|
820
|
+
const sources = [];
|
|
821
|
+
for (const source of value) {
|
|
822
|
+
if (typeof source !== "object" || source === null || Array.isArray(source)) return null;
|
|
823
|
+
const { url, title, snippet, publishedAt } = source;
|
|
824
|
+
if (typeof url !== "string") return null;
|
|
825
|
+
if (title !== void 0 && typeof title !== "string") return null;
|
|
826
|
+
if (snippet !== void 0 && typeof snippet !== "string") return null;
|
|
827
|
+
if (publishedAt !== void 0 && typeof publishedAt !== "string") return null;
|
|
828
|
+
sources.push({
|
|
829
|
+
url,
|
|
830
|
+
...title === void 0 ? {} : { title },
|
|
831
|
+
...snippet === void 0 ? {} : { snippet },
|
|
832
|
+
...publishedAt === void 0 ? {} : { publishedAt }
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
return sources;
|
|
836
|
+
}
|
|
566
837
|
/**
|
|
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.
|
|
838
|
+
* Derive a settled root web-search or web-fetch card from persisted metadata.
|
|
839
|
+
* @param block - running or settled Tool block.
|
|
840
|
+
* @returns web-card props, or null for the generic path.
|
|
586
841
|
*/
|
|
587
842
|
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
|
-
|
|
843
|
+
if (block.parentCallId !== void 0 || !("kind" in block) || block.isError) return null;
|
|
844
|
+
const tool = validWebCall(block);
|
|
845
|
+
if (tool === null || typeof block.meta !== "object" || block.meta === null || Array.isArray(block.meta)) return null;
|
|
846
|
+
const meta = block.meta;
|
|
847
|
+
if (typeof meta.truncated !== "boolean") return null;
|
|
848
|
+
if (tool === "web_search") {
|
|
849
|
+
const sources = webSources(meta.sources);
|
|
850
|
+
if (sources === null || meta.answer !== void 0 && typeof meta.answer !== "string") return null;
|
|
851
|
+
return {
|
|
852
|
+
kind: "search",
|
|
853
|
+
answer: meta.answer,
|
|
854
|
+
sources,
|
|
855
|
+
truncated: meta.truncated
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
if (typeof meta.url !== "string") return null;
|
|
859
|
+
if (typeof meta.statusCode !== "number" || !Number.isInteger(meta.statusCode)) return null;
|
|
860
|
+
return {
|
|
603
861
|
kind: "fetch",
|
|
604
|
-
url:
|
|
605
|
-
statusCode:
|
|
606
|
-
truncated:
|
|
862
|
+
url: meta.url,
|
|
863
|
+
statusCode: meta.statusCode,
|
|
864
|
+
truncated: meta.truncated
|
|
607
865
|
};
|
|
608
|
-
return null;
|
|
609
866
|
}
|
|
610
867
|
//#endregion
|
|
611
868
|
//#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
@@ -623,8 +880,162 @@ window.__ModuleLoader__.load({
|
|
|
623
880
|
return n;
|
|
624
881
|
}
|
|
625
882
|
//#endregion
|
|
883
|
+
//#region lib/types/client/tool/models/primitive-labels.js
|
|
884
|
+
/** Localized copy adapters for Cordis-free UI primitives used by Tool cards. */
|
|
885
|
+
/**
|
|
886
|
+
* Build localized Markdown chrome labels.
|
|
887
|
+
* @param t - Conversation locale seat.
|
|
888
|
+
* @returns Markdown chrome labels.
|
|
889
|
+
*/
|
|
890
|
+
function markdownLabels(t) {
|
|
891
|
+
return {
|
|
892
|
+
code: {
|
|
893
|
+
copyLabel: t("copy"),
|
|
894
|
+
copiedLabel: t("copied")
|
|
895
|
+
},
|
|
896
|
+
footnotes: t("markdown.footnotes")
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Build localized diff-card chrome labels.
|
|
901
|
+
* @param t - Conversation locale seat.
|
|
902
|
+
* @returns Diff-card chrome labels.
|
|
903
|
+
*/
|
|
904
|
+
function diffBlockLabels(t) {
|
|
905
|
+
return {
|
|
906
|
+
copy: t("copy"),
|
|
907
|
+
copied: t("copied"),
|
|
908
|
+
collapseAria: t("diff.collapseAria"),
|
|
909
|
+
expandAria: (count) => t("diff.expandAria", { count }),
|
|
910
|
+
collapse: t("collapse"),
|
|
911
|
+
expand: (count) => t("diff.expandRest", { count }),
|
|
912
|
+
files: (count) => t(count === 1 ? "diff.files.one" : "diff.files.other", { count })
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Build localized read-card chrome labels.
|
|
917
|
+
* @param t - Conversation locale seat.
|
|
918
|
+
* @returns Read-card chrome labels.
|
|
919
|
+
*/
|
|
920
|
+
function readBlockLabels(t) {
|
|
921
|
+
return {
|
|
922
|
+
window: (shown, total) => t("read.window", {
|
|
923
|
+
shown,
|
|
924
|
+
total
|
|
925
|
+
}),
|
|
926
|
+
copy: t("copy"),
|
|
927
|
+
copied: t("copied"),
|
|
928
|
+
collapseAria: t("read.collapseAria"),
|
|
929
|
+
expandAria: (count) => t("read.expandAria", { count }),
|
|
930
|
+
collapse: t("collapse"),
|
|
931
|
+
expand: (count) => t("read.expandRest", { count })
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Build localized search-card chrome labels.
|
|
936
|
+
* @param t - Conversation locale seat.
|
|
937
|
+
* @returns Search-card chrome labels.
|
|
938
|
+
*/
|
|
939
|
+
function searchBlockLabels(t) {
|
|
940
|
+
return {
|
|
941
|
+
pathsSummary: (shown, total, truncated) => t(truncated ? "search.paths.truncated" : "search.paths", {
|
|
942
|
+
shown,
|
|
943
|
+
total
|
|
944
|
+
}),
|
|
945
|
+
matchesSummary: (shown, total, files, truncated) => t(truncated ? "search.matches.truncated" : "search.matches", {
|
|
946
|
+
shown,
|
|
947
|
+
total,
|
|
948
|
+
files
|
|
949
|
+
}),
|
|
950
|
+
copy: t("copy"),
|
|
951
|
+
copied: t("copied"),
|
|
952
|
+
noResults: t("search.noResults"),
|
|
953
|
+
collapseAria: t("search.collapseAria"),
|
|
954
|
+
expandAria: (count) => t("search.expandAria", { count }),
|
|
955
|
+
collapse: t("collapse"),
|
|
956
|
+
expand: (count) => t("search.expandRest", { count })
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Build localized web-card chrome labels.
|
|
961
|
+
* @param t - Conversation locale seat.
|
|
962
|
+
* @returns Web-card chrome labels.
|
|
963
|
+
*/
|
|
964
|
+
function webBlockLabels(t) {
|
|
965
|
+
return {
|
|
966
|
+
noResults: t("web.noResults"),
|
|
967
|
+
sourcesTruncated: t("web.sourcesTruncated"),
|
|
968
|
+
http: t("web.http"),
|
|
969
|
+
contentTruncated: t("web.contentTruncated"),
|
|
970
|
+
markdown: markdownLabels(t)
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
|
|
975
|
+
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))}";
|
|
976
|
+
const tagId$4 = "@deepseek-ai/dsh-client-ui-tool/AskQuestionCard.module.css";
|
|
977
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
978
|
+
const tag = document.createElement("style");
|
|
979
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-tool";
|
|
980
|
+
tag.dataset.pluginCss = tagId$4;
|
|
981
|
+
tag.textContent = css$4;
|
|
982
|
+
document.head.appendChild(tag);
|
|
983
|
+
}
|
|
984
|
+
var AskQuestionCard_module_css_default = {
|
|
985
|
+
"answer": "fsXYAq_answer",
|
|
986
|
+
"answerLine": "fsXYAq_answerLine",
|
|
987
|
+
"card": "fsXYAq_card",
|
|
988
|
+
"item": "fsXYAq_item",
|
|
989
|
+
"question": "fsXYAq_question",
|
|
990
|
+
"questionList": "fsXYAq_questionList",
|
|
991
|
+
"skipped": "fsXYAq_skipped",
|
|
992
|
+
"unansweredQuestion": "fsXYAq_unansweredQuestion",
|
|
993
|
+
"verdict": "fsXYAq_verdict"
|
|
994
|
+
};
|
|
995
|
+
//#endregion
|
|
996
|
+
//#region lib/types/client/tool/components/AskQuestionCard.js
|
|
997
|
+
/**
|
|
998
|
+
* Render a validated ask-user transcript from plain card data.
|
|
999
|
+
* @param props - Localized transcript card data.
|
|
1000
|
+
* @returns the readable answered or unanswered question list.
|
|
1001
|
+
*/
|
|
1002
|
+
function AskQuestionCard({ card }) {
|
|
1003
|
+
if (card.kind === "unanswered") return (0, react_jsx_runtime.jsxs)("div", {
|
|
1004
|
+
className: AskQuestionCard_module_css_default.card,
|
|
1005
|
+
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
1006
|
+
className: AskQuestionCard_module_css_default.verdict,
|
|
1007
|
+
children: card.verdict
|
|
1008
|
+
}), (0, react_jsx_runtime.jsx)("ul", {
|
|
1009
|
+
className: AskQuestionCard_module_css_default.questionList,
|
|
1010
|
+
children: card.questions.map((question) => (0, react_jsx_runtime.jsx)("li", {
|
|
1011
|
+
className: AskQuestionCard_module_css_default.unansweredQuestion,
|
|
1012
|
+
children: question.question
|
|
1013
|
+
}, question.id))
|
|
1014
|
+
})]
|
|
1015
|
+
});
|
|
1016
|
+
return (0, react_jsx_runtime.jsx)("dl", {
|
|
1017
|
+
className: AskQuestionCard_module_css_default.card,
|
|
1018
|
+
children: card.questions.map((question) => (0, react_jsx_runtime.jsxs)("div", {
|
|
1019
|
+
className: AskQuestionCard_module_css_default.item,
|
|
1020
|
+
children: [(0, react_jsx_runtime.jsx)("dt", {
|
|
1021
|
+
className: AskQuestionCard_module_css_default.question,
|
|
1022
|
+
children: question.question
|
|
1023
|
+
}), (0, react_jsx_runtime.jsx)("dd", {
|
|
1024
|
+
className: AskQuestionCard_module_css_default.answer,
|
|
1025
|
+
children: question.answers.length === 0 ? (0, react_jsx_runtime.jsx)("span", {
|
|
1026
|
+
className: AskQuestionCard_module_css_default.skipped,
|
|
1027
|
+
children: card.skippedLabel
|
|
1028
|
+
}) : question.answers.map((answer, index) => (0, react_jsx_runtime.jsx)("span", {
|
|
1029
|
+
className: AskQuestionCard_module_css_default.answerLine,
|
|
1030
|
+
children: answer
|
|
1031
|
+
}, `${question.id}-${String(index)}`))
|
|
1032
|
+
})]
|
|
1033
|
+
}, question.id))
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
//#endregion
|
|
626
1037
|
//#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;
|
|
1038
|
+
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
1039
|
const tagId$3 = "@deepseek-ai/dsh-client-ui-tool/ToolRow.module.css";
|
|
629
1040
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
630
1041
|
const tag = document.createElement("style");
|
|
@@ -639,6 +1050,7 @@ window.__ModuleLoader__.load({
|
|
|
639
1050
|
"chevron": "o3BgMG_chevron",
|
|
640
1051
|
"codeBody": "o3BgMG_codeBody",
|
|
641
1052
|
"diffBody": "o3BgMG_diffBody",
|
|
1053
|
+
"diffStat": "o3BgMG_diffStat",
|
|
642
1054
|
"dsh-tool-row-sweep": "o3BgMG_dsh-tool-row-sweep",
|
|
643
1055
|
"errorSummary": "o3BgMG_errorSummary",
|
|
644
1056
|
"fileLink": "o3BgMG_fileLink",
|
|
@@ -664,9 +1076,6 @@ window.__ModuleLoader__.load({
|
|
|
664
1076
|
};
|
|
665
1077
|
//#endregion
|
|
666
1078
|
//#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
1079
|
function leadingFor$1(state, icon) {
|
|
671
1080
|
switch (state) {
|
|
672
1081
|
case "error": return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" });
|
|
@@ -686,20 +1095,31 @@ window.__ModuleLoader__.load({
|
|
|
686
1095
|
default: return null;
|
|
687
1096
|
}
|
|
688
1097
|
}
|
|
689
|
-
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, body, output, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect }) {
|
|
1098
|
+
function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, body, output, askQuestion, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect }) {
|
|
690
1099
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
691
|
-
const
|
|
1100
|
+
const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
|
|
1101
|
+
const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
|
|
1102
|
+
const readLabels = (0, react.useMemo)(() => readBlockLabels(t), [t]);
|
|
1103
|
+
const searchLabels = (0, react.useMemo)(() => searchBlockLabels(t), [t]);
|
|
1104
|
+
const webLabels = (0, react.useMemo)(() => webBlockLabels(t), [t]);
|
|
1105
|
+
const terminalBody = terminal === void 0 || terminal === null ? null : localizeTerminalCardModel(terminal, t);
|
|
692
1106
|
const diffBody = diff ?? null;
|
|
693
1107
|
const readBody = read ?? null;
|
|
694
1108
|
const searchBody = search ?? null;
|
|
695
1109
|
const webBody = web ?? null;
|
|
1110
|
+
const askQuestionBody = askQuestion ?? null;
|
|
696
1111
|
const outputText = output ?? null;
|
|
697
|
-
const expandable = body !== null || outputText !== null || (terminalBody ?? diffBody ?? readBody ?? searchBody ?? webBody) !== null;
|
|
1112
|
+
const expandable = body !== null || outputText !== null || (askQuestionBody ?? terminalBody ?? diffBody ?? readBody ?? searchBody ?? webBody) !== null;
|
|
698
1113
|
const open = expanded && expandable;
|
|
699
1114
|
const status = stateStatus$1(state, t);
|
|
700
1115
|
const failureLine = state === "error" ? errorSummary ?? null : null;
|
|
701
|
-
const summaryText = failureLine ?? summary;
|
|
702
|
-
const
|
|
1116
|
+
const summaryText = failureLine ?? terminalBody?.description ?? summary;
|
|
1117
|
+
const diffStat = (0, react.useMemo)(() => {
|
|
1118
|
+
if (diffBody === null) return null;
|
|
1119
|
+
const { added, removed } = (0, _deepseek_ai_dsh_client_ui_primitives.diffTotals)(diffBody.card.diffs);
|
|
1120
|
+
return `+${added} -${removed}`;
|
|
1121
|
+
}, [diffBody]);
|
|
1122
|
+
const suffix = failureLine === null ? summarySuffix ?? diffStat : null;
|
|
703
1123
|
const fileLink = filePath !== void 0 && onOpenFile !== void 0 && failureLine === null;
|
|
704
1124
|
const toggleExpand = () => {
|
|
705
1125
|
setExpanded((v) => !v);
|
|
@@ -748,27 +1168,30 @@ window.__ModuleLoader__.load({
|
|
|
748
1168
|
children: summaryText
|
|
749
1169
|
}),
|
|
750
1170
|
suffix !== null && (0, react_jsx_runtime.jsx)("span", {
|
|
751
|
-
className: ToolRow_module_css_default.summarySuffix,
|
|
1171
|
+
className: clsx(ToolRow_module_css_default.summarySuffix, suffix === diffStat && ToolRow_module_css_default.diffStat),
|
|
752
1172
|
children: suffix
|
|
753
1173
|
})
|
|
754
1174
|
] }),
|
|
755
1175
|
children: (0, react_jsx_runtime.jsxs)("div", {
|
|
756
1176
|
className: ToolRow_module_css_default.bodyWrap,
|
|
757
|
-
children: [terminalBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
1177
|
+
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
1178
|
...terminalBody.card,
|
|
759
1179
|
maxLines: Infinity,
|
|
760
|
-
labels:
|
|
1180
|
+
labels: terminalLabels,
|
|
761
1181
|
className: ToolRow_module_css_default.terminalBody
|
|
762
1182
|
}) : diffBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
|
|
763
1183
|
...diffBody.card,
|
|
1184
|
+
labels: diffLabels,
|
|
764
1185
|
maxLines: 8,
|
|
765
1186
|
className: ToolRow_module_css_default.diffBody
|
|
766
1187
|
}) : readBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
|
|
767
1188
|
...readBody,
|
|
1189
|
+
labels: readLabels,
|
|
768
1190
|
maxLines: 8,
|
|
769
1191
|
className: ToolRow_module_css_default.readBody
|
|
770
1192
|
}) : 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
1193
|
...searchBody.card,
|
|
1194
|
+
labels: searchLabels,
|
|
772
1195
|
maxLines: 8,
|
|
773
1196
|
className: ToolRow_module_css_default.searchBody
|
|
774
1197
|
}), searchBody.recovery !== void 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -776,6 +1199,7 @@ window.__ModuleLoader__.load({
|
|
|
776
1199
|
children: searchBody.recovery
|
|
777
1200
|
})] }) : webBody !== null ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
|
|
778
1201
|
...webBody,
|
|
1202
|
+
labels: webLabels,
|
|
779
1203
|
className: ToolRow_module_css_default.webBody
|
|
780
1204
|
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [variant === "code" && body !== null && (0, react_jsx_runtime.jsx)("div", {
|
|
781
1205
|
className: ToolRow_module_css_default.bodyScroll,
|
|
@@ -793,7 +1217,7 @@ window.__ModuleLoader__.load({
|
|
|
793
1217
|
className: ToolRow_module_css_default.ioSection,
|
|
794
1218
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
795
1219
|
className: ToolRow_module_css_default.ioLabel,
|
|
796
|
-
children: "
|
|
1220
|
+
children: t("row.input")
|
|
797
1221
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
798
1222
|
className: ToolRow_module_css_default.ioText,
|
|
799
1223
|
children: cardBody
|
|
@@ -807,7 +1231,7 @@ window.__ModuleLoader__.load({
|
|
|
807
1231
|
className: ToolRow_module_css_default.ioSection,
|
|
808
1232
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
809
1233
|
className: ToolRow_module_css_default.ioLabel,
|
|
810
|
-
children: "
|
|
1234
|
+
children: t("row.output")
|
|
811
1235
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
812
1236
|
className: ToolRow_module_css_default.ioText,
|
|
813
1237
|
"data-error": state === "error" || void 0,
|
|
@@ -819,7 +1243,7 @@ window.__ModuleLoader__.load({
|
|
|
819
1243
|
type: "button",
|
|
820
1244
|
className: ToolRow_module_css_default.inspectButton,
|
|
821
1245
|
onClick: inspect,
|
|
822
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), "
|
|
1246
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
|
|
823
1247
|
})]
|
|
824
1248
|
})
|
|
825
1249
|
})]
|
|
@@ -851,8 +1275,8 @@ window.__ModuleLoader__.load({
|
|
|
851
1275
|
variant: model.variant,
|
|
852
1276
|
toolName,
|
|
853
1277
|
icon: VARIANT_ICONS[model.variant],
|
|
854
|
-
title: model.
|
|
855
|
-
summary:
|
|
1278
|
+
title: t(model.titleKey),
|
|
1279
|
+
summary: model.summary,
|
|
856
1280
|
body: singleFile ? null : model.body,
|
|
857
1281
|
output: model.output,
|
|
858
1282
|
errorSummary: model.errorSummary,
|
|
@@ -958,8 +1382,8 @@ window.__ModuleLoader__.load({
|
|
|
958
1382
|
* @param props - whole-Tool owner data and the Tool-owned child-slot share.
|
|
959
1383
|
* @returns the Tool call tree.
|
|
960
1384
|
*/
|
|
961
|
-
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall,
|
|
962
|
-
const home =
|
|
1385
|
+
function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, useHostInfo, t }) {
|
|
1386
|
+
const home = useHostInfo((info) => info.home);
|
|
963
1387
|
const block = node.data.root;
|
|
964
1388
|
return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
|
|
965
1389
|
renderSlot,
|
|
@@ -996,35 +1420,41 @@ window.__ModuleLoader__.load({
|
|
|
996
1420
|
//#region lib/types/client/tool/ToolDetails.js
|
|
997
1421
|
/** Card-aware output body for the selected Tool call in details. */
|
|
998
1422
|
/**
|
|
999
|
-
* Render the selected Tool call's structured output when its
|
|
1000
|
-
*
|
|
1423
|
+
* Render the selected Tool call's structured output when its raw fields form a
|
|
1424
|
+
* supported root card, otherwise preserve the flattened result text.
|
|
1001
1425
|
* @param props - selected call slice, workspace root, host home, and locale seat.
|
|
1002
1426
|
* @returns the details output body.
|
|
1003
1427
|
*/
|
|
1004
|
-
function ToolDetails({ block, cwd,
|
|
1005
|
-
const home =
|
|
1006
|
-
const
|
|
1007
|
-
if (
|
|
1008
|
-
|
|
1009
|
-
children: terminal.description
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1428
|
+
function ToolDetails({ block, cwd, useHostInfo, t }) {
|
|
1429
|
+
const home = useHostInfo((info) => info.home);
|
|
1430
|
+
const terminalModel = terminalCardModel(block, cwd);
|
|
1431
|
+
if (terminalModel !== null) {
|
|
1432
|
+
const terminal = localizeTerminalCardModel(terminalModel, t);
|
|
1433
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [terminal.description !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1434
|
+
className: ToolDetails_module_css_default.description,
|
|
1435
|
+
children: terminal.description
|
|
1436
|
+
}) : null, (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
1437
|
+
...terminal.card,
|
|
1438
|
+
labels: terminalBlockLabels(t),
|
|
1439
|
+
className: ToolDetails_module_css_default.cardBody
|
|
1440
|
+
})] });
|
|
1441
|
+
}
|
|
1015
1442
|
const read = readCardModel(block, cwd, home);
|
|
1016
1443
|
if (read !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ReadBlock, {
|
|
1017
1444
|
...read,
|
|
1445
|
+
labels: readBlockLabels(t),
|
|
1018
1446
|
className: ToolDetails_module_css_default.read
|
|
1019
1447
|
});
|
|
1020
1448
|
const diff = diffCardModel(block);
|
|
1021
1449
|
if (diff !== null) return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DiffBlock, {
|
|
1022
1450
|
...diff.card,
|
|
1451
|
+
labels: diffBlockLabels(t),
|
|
1023
1452
|
className: ToolDetails_module_css_default.cardBody
|
|
1024
1453
|
});
|
|
1025
1454
|
const search = searchCardModel(block);
|
|
1026
1455
|
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
1456
|
...search.card,
|
|
1457
|
+
labels: searchBlockLabels(t),
|
|
1028
1458
|
className: ToolDetails_module_css_default.cardBody
|
|
1029
1459
|
}), search.recovery !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1030
1460
|
className: ToolDetails_module_css_default.recovery,
|
|
@@ -1035,6 +1465,7 @@ window.__ModuleLoader__.load({
|
|
|
1035
1465
|
const body = "kind" in block ? resultText(block) : "";
|
|
1036
1466
|
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.WebBlock, {
|
|
1037
1467
|
...web,
|
|
1468
|
+
labels: webBlockLabels(t),
|
|
1038
1469
|
className: ToolDetails_module_css_default.web
|
|
1039
1470
|
}), body !== "" ? (0, react_jsx_runtime.jsx)("pre", {
|
|
1040
1471
|
className: ToolDetails_module_css_default.code,
|
|
@@ -1057,40 +1488,133 @@ window.__ModuleLoader__.load({
|
|
|
1057
1488
|
const CONVERSATION_NS = "conversation";
|
|
1058
1489
|
//#endregion
|
|
1059
1490
|
//#region lib/types/client/tool/toolviews/ask-question-row.js
|
|
1060
|
-
function
|
|
1061
|
-
return typeof value === "object" && value !== null;
|
|
1491
|
+
function isRecord(value) {
|
|
1492
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1062
1493
|
}
|
|
1063
|
-
|
|
1064
|
-
* empty `selected` and no `custom`); null when answer fields are invalid. */
|
|
1065
|
-
function answeredSummary(text, t) {
|
|
1066
|
-
let parsed;
|
|
1494
|
+
function parseJson(text) {
|
|
1067
1495
|
try {
|
|
1068
|
-
|
|
1496
|
+
return JSON.parse(text);
|
|
1069
1497
|
} catch {
|
|
1070
|
-
return
|
|
1498
|
+
return;
|
|
1071
1499
|
}
|
|
1072
|
-
|
|
1500
|
+
}
|
|
1501
|
+
/** Answer records from the result JSON; null when the result is malformed. */
|
|
1502
|
+
function answerEntries(text) {
|
|
1503
|
+
const parsed = parseJson(text);
|
|
1504
|
+
if (!isRecord(parsed)) return null;
|
|
1505
|
+
const answers = parsed.answers;
|
|
1506
|
+
if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
|
|
1507
|
+
const entries = [];
|
|
1508
|
+
for (const answer of answers) {
|
|
1509
|
+
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;
|
|
1510
|
+
entries.push({
|
|
1511
|
+
id: answer.id,
|
|
1512
|
+
selected: answer.selected,
|
|
1513
|
+
...answer.custom === void 0 ? {} : { custom: answer.custom }
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
return entries;
|
|
1517
|
+
}
|
|
1518
|
+
/** Questions from call JSON; null when pairing with answers would be ambiguous. */
|
|
1519
|
+
function questionEntries(argsRaw) {
|
|
1520
|
+
const parsed = parseJson(argsRaw);
|
|
1521
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.questions) || parsed.questions.length === 0) return null;
|
|
1522
|
+
const questions = [];
|
|
1523
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1524
|
+
for (const question of parsed.questions) {
|
|
1525
|
+
if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string" || ids.has(question.id)) return null;
|
|
1526
|
+
ids.add(question.id);
|
|
1527
|
+
questions.push({
|
|
1528
|
+
id: question.id,
|
|
1529
|
+
question: question.question
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
return questions;
|
|
1533
|
+
}
|
|
1534
|
+
/** Pair questions with result entries by their echoed stable ids. */
|
|
1535
|
+
function pairAnswers(argsRaw, answers) {
|
|
1536
|
+
const questions = questionEntries(argsRaw);
|
|
1537
|
+
if (questions === null || questions.length !== answers.length) return null;
|
|
1538
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1539
|
+
for (const answer of answers) {
|
|
1540
|
+
if (byId.has(answer.id)) return null;
|
|
1541
|
+
byId.set(answer.id, answer);
|
|
1542
|
+
}
|
|
1543
|
+
const paired = [];
|
|
1544
|
+
for (const question of questions) {
|
|
1545
|
+
const answer = byId.get(question.id);
|
|
1546
|
+
if (answer === void 0) return null;
|
|
1547
|
+
paired.push({
|
|
1548
|
+
...question,
|
|
1549
|
+
answers: [...answer.selected, ...answer.custom === void 0 || answer.custom === "" ? [] : [answer.custom]]
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
return paired;
|
|
1553
|
+
}
|
|
1554
|
+
/** Answer summary plus structured transcript content from the two wire JSON documents. */
|
|
1555
|
+
function answeredPresentation(argsRaw, text, t) {
|
|
1556
|
+
const answers = answerEntries(text);
|
|
1557
|
+
if (answers === null) return null;
|
|
1558
|
+
const answered = answers.filter((answer) => answer.selected.length > 0 || (answer.custom ?? "") !== "").length;
|
|
1559
|
+
return {
|
|
1560
|
+
summary: t("ask.answered", {
|
|
1561
|
+
answered,
|
|
1562
|
+
total: answers.length
|
|
1563
|
+
}),
|
|
1564
|
+
questions: pairAnswers(argsRaw, answers)
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
/** Best-effort answered-count summary when strict transcript pairing fails. */
|
|
1568
|
+
function answeredSummary(text, t) {
|
|
1569
|
+
const parsed = parseJson(text);
|
|
1570
|
+
if (!isRecord(parsed)) return null;
|
|
1073
1571
|
const answers = parsed.answers;
|
|
1074
|
-
if (!Array.isArray(answers) || !answers.every(
|
|
1572
|
+
if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
|
|
1075
1573
|
const answered = answers.filter((a) => Array.isArray(a.selected) && a.selected.length > 0 || typeof a.custom === "string" && a.custom !== "").length;
|
|
1076
1574
|
return t("ask.answered", {
|
|
1077
1575
|
answered,
|
|
1078
1576
|
total: answers.length
|
|
1079
1577
|
});
|
|
1080
1578
|
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Input/Output sections, ToolRow's unified expand). */
|
|
1579
|
+
/** Summarizes a pending, answered, cancelled, or interrupted question set. */
|
|
1083
1580
|
function AskQuestionRow({ toolName, block, inspect, t }) {
|
|
1084
1581
|
const model = toolRowModel(toolName, block);
|
|
1085
1582
|
const code = "kind" in block ? block.error?.code : void 0;
|
|
1583
|
+
const argsRaw = ("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "";
|
|
1086
1584
|
let summary = model.summary;
|
|
1087
1585
|
let state = model.state;
|
|
1088
|
-
|
|
1089
|
-
|
|
1586
|
+
let transcript = null;
|
|
1587
|
+
if (code === "ASK_CANCELLED") {
|
|
1588
|
+
summary = t("ask.cancelled");
|
|
1589
|
+
state = "ok";
|
|
1590
|
+
const questions = questionEntries(argsRaw);
|
|
1591
|
+
if (questions !== null) transcript = {
|
|
1592
|
+
kind: "unanswered",
|
|
1593
|
+
questions,
|
|
1594
|
+
verdict: t("ask.cancelledDetail")
|
|
1595
|
+
};
|
|
1596
|
+
} else if (code === "ASK_ABORTED") {
|
|
1090
1597
|
summary = t("ask.interrupted");
|
|
1091
1598
|
state = "stopped";
|
|
1599
|
+
const questions = questionEntries(argsRaw);
|
|
1600
|
+
if (questions !== null) transcript = {
|
|
1601
|
+
kind: "unanswered",
|
|
1602
|
+
questions,
|
|
1603
|
+
verdict: t("ask.interruptedDetail")
|
|
1604
|
+
};
|
|
1092
1605
|
} else if (model.state === "running") summary = t("ask.waiting");
|
|
1093
|
-
else if ("kind" in block && model.state === "ok")
|
|
1606
|
+
else if ("kind" in block && model.state === "ok") {
|
|
1607
|
+
const text = singleResultText(block);
|
|
1608
|
+
if (text !== void 0) {
|
|
1609
|
+
const presentation = answeredPresentation(argsRaw, text, t);
|
|
1610
|
+
summary = presentation?.summary ?? answeredSummary(text, t) ?? model.summary;
|
|
1611
|
+
if (presentation?.questions !== null && presentation?.questions !== void 0) transcript = {
|
|
1612
|
+
kind: "answered",
|
|
1613
|
+
questions: presentation.questions,
|
|
1614
|
+
skippedLabel: t("ask.skipped")
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1094
1618
|
return (0, react_jsx_runtime.jsx)(ToolRow, {
|
|
1095
1619
|
t,
|
|
1096
1620
|
variant: model.variant,
|
|
@@ -1098,23 +1622,17 @@ window.__ModuleLoader__.load({
|
|
|
1098
1622
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQuestionOutline14, {}),
|
|
1099
1623
|
title: t("ask.rowTitle"),
|
|
1100
1624
|
summary,
|
|
1101
|
-
body: model.body,
|
|
1102
|
-
output: model.output,
|
|
1625
|
+
body: transcript === null ? model.body : null,
|
|
1626
|
+
output: transcript === null ? model.output : null,
|
|
1627
|
+
askQuestion: transcript,
|
|
1103
1628
|
state,
|
|
1104
1629
|
inspect
|
|
1105
1630
|
});
|
|
1106
1631
|
}
|
|
1107
|
-
/**
|
|
1108
|
-
* The ask-question row as a plain registrant plugin following the chat
|
|
1109
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1110
|
-
*/
|
|
1632
|
+
/** Registers the ask-user-question conversation row. */
|
|
1111
1633
|
const askQuestionToolview = {
|
|
1112
1634
|
name: "ask-question-toolview",
|
|
1113
1635
|
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
1636
|
apply(ctx) {
|
|
1119
1637
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1120
1638
|
name: "tool.call.toolview",
|
|
@@ -1125,7 +1643,7 @@ window.__ModuleLoader__.load({
|
|
|
1125
1643
|
};
|
|
1126
1644
|
//#endregion
|
|
1127
1645
|
//#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;
|
|
1646
|
+
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
1647
|
const tagId = "@deepseek-ai/dsh-client-ui-tool/bash-sample.module.css";
|
|
1130
1648
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1131
1649
|
const tag = document.createElement("style");
|
|
@@ -1174,19 +1692,16 @@ window.__ModuleLoader__.load({
|
|
|
1174
1692
|
default: return null;
|
|
1175
1693
|
}
|
|
1176
1694
|
}
|
|
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
|
-
*/
|
|
1695
|
+
/** Renders expandable Bash output with an accessible lifecycle label. */
|
|
1182
1696
|
function BashRow({ toolName, block, sessionId, useSessions, inspect, t }) {
|
|
1183
1697
|
const model = toolRowModel(toolName, block);
|
|
1184
|
-
const
|
|
1185
|
-
const
|
|
1698
|
+
const terminalModel = terminalCardModel(block, useSessions((list) => list.byId[sessionId]?.cwd));
|
|
1699
|
+
const terminal = terminalModel === null ? null : localizeTerminalCardModel(terminalModel, t);
|
|
1700
|
+
const state = model.state === "ok" && terminalModel !== null && terminalFailed(terminalModel) ? "error" : model.state;
|
|
1186
1701
|
const status = stateStatus(state, t);
|
|
1187
1702
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
1188
|
-
const
|
|
1189
|
-
const expandable = terminal !== null ||
|
|
1703
|
+
const genericBody = terminal === null && (model.state === "error" || isSettledPersistentShellCall(block)) && (model.body !== null || model.output !== null);
|
|
1704
|
+
const expandable = terminal !== null || genericBody;
|
|
1190
1705
|
const open = expanded && expandable;
|
|
1191
1706
|
const failureLine = model.state === "error" ? model.errorSummary : null;
|
|
1192
1707
|
const toggleExpand = () => {
|
|
@@ -1225,7 +1740,7 @@ window.__ModuleLoader__.load({
|
|
|
1225
1740
|
}),
|
|
1226
1741
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1227
1742
|
className: bash_sample_module_css_default.title,
|
|
1228
|
-
children: model.
|
|
1743
|
+
children: t(model.titleKey)
|
|
1229
1744
|
}),
|
|
1230
1745
|
(0, react_jsx_runtime.jsx)("span", {
|
|
1231
1746
|
className: bash_sample_module_css_default.sep,
|
|
@@ -1250,7 +1765,7 @@ window.__ModuleLoader__.load({
|
|
|
1250
1765
|
className: bash_sample_module_css_default.ioSection,
|
|
1251
1766
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1252
1767
|
className: bash_sample_module_css_default.ioLabel,
|
|
1253
|
-
children: "
|
|
1768
|
+
children: t("row.input")
|
|
1254
1769
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1255
1770
|
className: bash_sample_module_css_default.ioText,
|
|
1256
1771
|
children: model.body
|
|
@@ -1264,10 +1779,10 @@ window.__ModuleLoader__.load({
|
|
|
1264
1779
|
className: bash_sample_module_css_default.ioSection,
|
|
1265
1780
|
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1266
1781
|
className: bash_sample_module_css_default.ioLabel,
|
|
1267
|
-
children: "
|
|
1782
|
+
children: t("row.output")
|
|
1268
1783
|
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1269
1784
|
className: bash_sample_module_css_default.ioText,
|
|
1270
|
-
"data-error":
|
|
1785
|
+
"data-error": state === "error" || void 0,
|
|
1271
1786
|
children: model.output
|
|
1272
1787
|
})]
|
|
1273
1788
|
})
|
|
@@ -1276,22 +1791,15 @@ window.__ModuleLoader__.load({
|
|
|
1276
1791
|
type: "button",
|
|
1277
1792
|
className: bash_sample_module_css_default.inspectButton,
|
|
1278
1793
|
onClick: inspect,
|
|
1279
|
-
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), "
|
|
1794
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
|
|
1280
1795
|
})]
|
|
1281
1796
|
})]
|
|
1282
1797
|
});
|
|
1283
1798
|
}
|
|
1284
|
-
/**
|
|
1285
|
-
* The sample as a plain registrant plugin. Slot injection follows the chat
|
|
1286
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1287
|
-
*/
|
|
1799
|
+
/** Registers the standalone Bash conversation-row sample. */
|
|
1288
1800
|
const bashToolviewSample = {
|
|
1289
1801
|
name: "bash-toolview-sample",
|
|
1290
1802
|
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
1803
|
apply(ctx) {
|
|
1296
1804
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1297
1805
|
name: "tool.call.toolview",
|
|
@@ -1303,13 +1811,7 @@ window.__ModuleLoader__.load({
|
|
|
1303
1811
|
//#endregion
|
|
1304
1812
|
//#region lib/types/client/tool/toolviews/file-mutation-row.js
|
|
1305
1813
|
/**
|
|
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.
|
|
1814
|
+
* Lets users expand an applied file diff and open the reported path.
|
|
1313
1815
|
*/
|
|
1314
1816
|
function FileMutationRow({ toolName, block, cwd, home, openFile, inspect, t }) {
|
|
1315
1817
|
const model = toolRowModel(toolName, block, cwd, home);
|
|
@@ -1319,7 +1821,7 @@ window.__ModuleLoader__.load({
|
|
|
1319
1821
|
variant: model.variant,
|
|
1320
1822
|
toolName,
|
|
1321
1823
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
|
|
1322
|
-
title: model.
|
|
1824
|
+
title: t(model.titleKey),
|
|
1323
1825
|
summary: model.summary,
|
|
1324
1826
|
body: null,
|
|
1325
1827
|
output: model.output,
|
|
@@ -1331,18 +1833,10 @@ window.__ModuleLoader__.load({
|
|
|
1331
1833
|
inspect
|
|
1332
1834
|
});
|
|
1333
1835
|
}
|
|
1334
|
-
/**
|
|
1335
|
-
* The file-mutation rows as a plain registrant plugin following the chat
|
|
1336
|
-
* toolview declaration across independent activation and reload lifetimes.
|
|
1337
|
-
*/
|
|
1836
|
+
/** Registers the edit and write conversation rows. */
|
|
1338
1837
|
const fileMutationToolview = {
|
|
1339
1838
|
name: "file-mutation-toolview",
|
|
1340
1839
|
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
1840
|
apply(ctx) {
|
|
1347
1841
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1348
1842
|
yield ctx.slots.register({
|
|
@@ -1361,9 +1855,7 @@ window.__ModuleLoader__.load({
|
|
|
1361
1855
|
//#endregion
|
|
1362
1856
|
//#region lib/types/client/tool/toolviews/read-row.js
|
|
1363
1857
|
/**
|
|
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.
|
|
1858
|
+
* Lets users expand a completed read result and open its reported path.
|
|
1367
1859
|
*/
|
|
1368
1860
|
function ReadRow({ toolName, block, cwd, home, openFile, inspect, t }) {
|
|
1369
1861
|
const model = toolRowModel(toolName, block, cwd, home);
|
|
@@ -1373,7 +1865,7 @@ window.__ModuleLoader__.load({
|
|
|
1373
1865
|
variant: model.variant,
|
|
1374
1866
|
toolName,
|
|
1375
1867
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
|
|
1376
|
-
title: model.
|
|
1868
|
+
title: t(model.titleKey),
|
|
1377
1869
|
summary: model.summary,
|
|
1378
1870
|
body: null,
|
|
1379
1871
|
output: model.output,
|
|
@@ -1385,17 +1877,10 @@ window.__ModuleLoader__.load({
|
|
|
1385
1877
|
inspect
|
|
1386
1878
|
});
|
|
1387
1879
|
}
|
|
1388
|
-
/**
|
|
1389
|
-
* The read row as a plain registrant plugin following the atomic Tool-view
|
|
1390
|
-
* declaration across independent activation and reload lifetimes.
|
|
1391
|
-
*/
|
|
1880
|
+
/** Registers the read tool's conversation row. */
|
|
1392
1881
|
const readToolview = {
|
|
1393
1882
|
name: "read-toolview",
|
|
1394
1883
|
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
1884
|
apply(ctx) {
|
|
1400
1885
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1401
1886
|
name: "tool.call.toolview",
|
|
@@ -1406,18 +1891,11 @@ window.__ModuleLoader__.load({
|
|
|
1406
1891
|
};
|
|
1407
1892
|
//#endregion
|
|
1408
1893
|
//#region lib/types/client/tool/toolviews/search-row.js
|
|
1409
|
-
const
|
|
1410
|
-
grep: "
|
|
1411
|
-
glob: "
|
|
1894
|
+
const SEARCH_TITLE_KEYS = {
|
|
1895
|
+
grep: "tool.title.grep",
|
|
1896
|
+
glob: "tool.title.glob"
|
|
1412
1897
|
};
|
|
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
|
-
*/
|
|
1898
|
+
/** Lets users expand grep or glob results and recover capped searches. */
|
|
1421
1899
|
function SearchRow({ toolName, block, inspect, t }) {
|
|
1422
1900
|
const model = toolRowModel(toolName, block);
|
|
1423
1901
|
const search = searchCardModel(block);
|
|
@@ -1426,8 +1904,8 @@ window.__ModuleLoader__.load({
|
|
|
1426
1904
|
variant: model.variant,
|
|
1427
1905
|
toolName,
|
|
1428
1906
|
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
|
|
1429
|
-
title:
|
|
1430
|
-
summary:
|
|
1907
|
+
title: t(toolName === "grep" ? SEARCH_TITLE_KEYS.grep : toolName === "glob" ? SEARCH_TITLE_KEYS.glob : model.titleKey),
|
|
1908
|
+
summary: model.summary,
|
|
1431
1909
|
body: null,
|
|
1432
1910
|
output: model.output,
|
|
1433
1911
|
errorSummary: model.errorSummary,
|
|
@@ -1436,19 +1914,10 @@ window.__ModuleLoader__.load({
|
|
|
1436
1914
|
inspect
|
|
1437
1915
|
});
|
|
1438
1916
|
}
|
|
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
|
-
*/
|
|
1917
|
+
/** Registers the grep and glob conversation rows. */
|
|
1444
1918
|
const searchToolview = {
|
|
1445
1919
|
name: "search-toolview",
|
|
1446
1920
|
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
1921
|
apply(ctx) {
|
|
1453
1922
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1454
1923
|
yield ctx.slots.register({
|
|
@@ -1525,10 +1994,7 @@ window.__ModuleLoader__.load({
|
|
|
1525
1994
|
extra: activeExtra
|
|
1526
1995
|
};
|
|
1527
1996
|
}
|
|
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. */
|
|
1997
|
+
/** Summarizes a plan update without presenting a cancelled call as completed. */
|
|
1532
1998
|
function TodoRow({ toolName, block, inspect, t }) {
|
|
1533
1999
|
const model = toolRowModel(toolName, block);
|
|
1534
2000
|
const summary = summarize(("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "", t) ?? {
|
|
@@ -1550,17 +2016,10 @@ window.__ModuleLoader__.load({
|
|
|
1550
2016
|
inspect
|
|
1551
2017
|
});
|
|
1552
2018
|
}
|
|
1553
|
-
/**
|
|
1554
|
-
* The todo row as a plain registrant plugin following the atomic Tool-view
|
|
1555
|
-
* declaration across independent activation and reload lifetimes.
|
|
1556
|
-
*/
|
|
2019
|
+
/** Registers the todo conversation row. */
|
|
1557
2020
|
const todoToolview = {
|
|
1558
2021
|
name: "todo-toolview",
|
|
1559
2022
|
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
2023
|
apply(ctx) {
|
|
1565
2024
|
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1566
2025
|
name: "tool.call.toolview",
|
|
@@ -1571,16 +2030,11 @@ window.__ModuleLoader__.load({
|
|
|
1571
2030
|
};
|
|
1572
2031
|
//#endregion
|
|
1573
2032
|
//#region lib/types/client/tool/toolviews/web-row.js
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
web_fetch: "Fetch"
|
|
2033
|
+
const WEB_TITLE_KEYS = {
|
|
2034
|
+
web_search: "tool.title.webSearch",
|
|
2035
|
+
web_fetch: "tool.title.webFetch"
|
|
1578
2036
|
};
|
|
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
|
-
*/
|
|
2037
|
+
/** Lets users expand a completed web search or fetch result. */
|
|
1584
2038
|
function WebRow({ toolName, block, inspect, t }) {
|
|
1585
2039
|
const model = toolRowModel(toolName, block);
|
|
1586
2040
|
const web = webCardModel(block);
|
|
@@ -1590,7 +2044,7 @@ window.__ModuleLoader__.load({
|
|
|
1590
2044
|
variant: model.variant,
|
|
1591
2045
|
toolName,
|
|
1592
2046
|
icon,
|
|
1593
|
-
title:
|
|
2047
|
+
title: t(toolName === "web_search" ? WEB_TITLE_KEYS.web_search : toolName === "web_fetch" ? WEB_TITLE_KEYS.web_fetch : model.titleKey),
|
|
1594
2048
|
summary: model.summary,
|
|
1595
2049
|
body: null,
|
|
1596
2050
|
output: model.output,
|
|
@@ -1600,17 +2054,10 @@ window.__ModuleLoader__.load({
|
|
|
1600
2054
|
inspect
|
|
1601
2055
|
});
|
|
1602
2056
|
}
|
|
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
|
-
*/
|
|
2057
|
+
/** Registers the web search and fetch conversation rows. */
|
|
1607
2058
|
const webToolview = {
|
|
1608
2059
|
name: "web-toolview",
|
|
1609
2060
|
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
2061
|
apply(ctx) {
|
|
1615
2062
|
ctx.slots.inject("tool.call.toolview", function* () {
|
|
1616
2063
|
yield ctx.slots.register({
|
|
@@ -1628,15 +2075,18 @@ window.__ModuleLoader__.load({
|
|
|
1628
2075
|
};
|
|
1629
2076
|
//#endregion
|
|
1630
2077
|
//#region lib/types/client/apply.js
|
|
1631
|
-
/** Required services: the slot registry and the Host
|
|
1632
|
-
const inject = ["slots", "
|
|
2078
|
+
/** Required services: the slot registry and the Remote face carrying the Host home used for POSIX `~`. */
|
|
2079
|
+
const inject = ["slots", "remote"];
|
|
1633
2080
|
/**
|
|
1634
2081
|
* Mount the whole-Tool renderers and built-in atomic Tool registrations.
|
|
1635
2082
|
* @param ctx - Client root context.
|
|
1636
2083
|
*/
|
|
1637
2084
|
function apply(ctx) {
|
|
1638
|
-
const
|
|
1639
|
-
|
|
2085
|
+
const hostInfo = {
|
|
2086
|
+
getSnapshot: () => ctx.remote.$host,
|
|
2087
|
+
subscribe: (listener) => ctx.on("connection/reset", listener)
|
|
2088
|
+
};
|
|
2089
|
+
const toolInject = () => ({ hooks: { hostInfo } });
|
|
1640
2090
|
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
1641
2091
|
name: "conversation.chat.node",
|
|
1642
2092
|
key: "tool-call",
|