@prettier-ai/dsh-client-ui-tool 0.1.2-alpha.1

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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +111 -0
  4. package/README.zh.md +111 -0
  5. package/lib/client.js +2111 -0
  6. package/lib/index.js +6 -0
  7. package/lib/invariant.js +23 -0
  8. package/lib/types/client/apply.d.ts +9 -0
  9. package/lib/types/client/contract/slots.d.ts +58 -0
  10. package/lib/types/client/index.d.ts +4 -0
  11. package/lib/types/client/locale.d.ts +3 -0
  12. package/lib/types/client/tool/ToolCallTree.d.ts +9 -0
  13. package/lib/types/client/tool/ToolDetails.d.ts +9 -0
  14. package/lib/types/client/tool/components/AskQuestionCard.d.ts +11 -0
  15. package/lib/types/client/tool/components/ToolRow.d.ts +55 -0
  16. package/lib/types/client/tool/models/ask-question-card-model.d.ts +22 -0
  17. package/lib/types/client/tool/models/diff-card-model.d.ts +37 -0
  18. package/lib/types/client/tool/models/primitive-labels.d.ts +36 -0
  19. package/lib/types/client/tool/models/raw-tool-call.d.ts +27 -0
  20. package/lib/types/client/tool/models/read-card-model.d.ts +31 -0
  21. package/lib/types/client/tool/models/search-card-model.d.ts +23 -0
  22. package/lib/types/client/tool/models/terminal-card-model.d.ts +81 -0
  23. package/lib/types/client/tool/models/tool-call-model.d.ts +75 -0
  24. package/lib/types/client/tool/models/web-card-model.d.ts +14 -0
  25. package/lib/types/client/tool/toolviews/GenericToolCard.d.ts +7 -0
  26. package/lib/types/client/tool/toolviews/ask-question-row.d.ts +14 -0
  27. package/lib/types/client/tool/toolviews/bash-sample.d.ts +14 -0
  28. package/lib/types/client/tool/toolviews/file-mutation-row.d.ts +16 -0
  29. package/lib/types/client/tool/toolviews/plan-summary.d.ts +48 -0
  30. package/lib/types/client/tool/toolviews/read-row.d.ts +16 -0
  31. package/lib/types/client/tool/toolviews/search-row.d.ts +14 -0
  32. package/lib/types/client/tool/toolviews/todo-row.d.ts +14 -0
  33. package/lib/types/client/tool/toolviews/web-row.d.ts +14 -0
  34. package/lib/types/index.d.ts +4 -0
  35. package/lib/types/invariant.d.ts +16 -0
  36. package/package.json +90 -0
package/lib/client.js ADDED
@@ -0,0 +1,2111 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@prettier-ai/dsh-client-ui-tool",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
10
+ //#region ../../util/workspace-path/src/index.ts
11
+ /**
12
+ * Browser-safe Workspace path and display helpers.
13
+ * @module @prettier-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
+ }
19
+ /**
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.
24
+ */
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"
56
+ };
57
+ /**
58
+ * Known tool name -> variant.
59
+ *
60
+ * `cordis_define` is deliberately absent: ui-cordis registers a keyed
61
+ * `tool.call.toolview` entry for it, and a keyed hit REPLACES the generic row
62
+ * (this table is only reached through GenericToolCard, the dispatch fallback in
63
+ * ToolCallTree). An entry here would be unreachable, and a second title for the
64
+ * same call would be a second answer to a question the card already owns.
65
+ */
66
+ const TOOL_VARIANTS = {
67
+ bash: "bash",
68
+ pwsh: "bash",
69
+ read: "read",
70
+ web_fetch: "read",
71
+ web_search: "search",
72
+ grep: "search",
73
+ glob: "search",
74
+ write: "write",
75
+ edit: "edit",
76
+ run_code: "code",
77
+ cordis_package_inspect: "read",
78
+ cordis_runtime_inspect: "read",
79
+ cordis_run: "others",
80
+ cordis_stop: "others",
81
+ cordis_undefine: "others"
82
+ };
83
+ /** Tool-owned titles that refine a generic row variant without replacing it. */
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"
91
+ };
92
+ /**
93
+ * Classify a tool name into its row variant.
94
+ * @param toolName - wire tool name.
95
+ * @returns matching variant, others when unknown.
96
+ */
97
+ function classifyTool(toolName) {
98
+ return TOOL_VARIANTS[toolName] ?? "others";
99
+ }
100
+ /**
101
+ * Flatten a settled result's content blocks to display text: text blocks
102
+ * verbatim, other block shapes as pretty JSON. Empty content on a failed call
103
+ * falls back to the structured error's `name: code` line.
104
+ * @param node - the settled result node.
105
+ * @returns the flattened result text (may be empty).
106
+ */
107
+ function resultText(node) {
108
+ const parts = [];
109
+ for (const block of node.content) if (block.type === "text") parts.push(block.text);
110
+ else parts.push(JSON.stringify(block, null, 2));
111
+ if (parts.length === 0 && node.error !== void 0) parts.push(`${node.error.name}: ${node.error.code}`);
112
+ return parts.join("\n");
113
+ }
114
+ function parseArgs(argsRaw) {
115
+ try {
116
+ return JSON.parse(argsRaw);
117
+ } catch {
118
+ return;
119
+ }
120
+ }
121
+ function firstLine(text) {
122
+ const nl = text.indexOf("\n");
123
+ return nl === -1 ? text : text.slice(0, nl);
124
+ }
125
+ function pickString(args, keys) {
126
+ for (const key of keys) {
127
+ const v = args[key];
128
+ if (typeof v === "string" && v !== "") return v;
129
+ }
130
+ }
131
+ /** Summary key preference per variant (args-derived; result-derived summaries are a ledger item). */
132
+ const SUMMARY_KEYS = {
133
+ bash: ["description", "command"],
134
+ read: [
135
+ "path",
136
+ "file_path",
137
+ "url"
138
+ ],
139
+ search: [
140
+ "query",
141
+ "pattern",
142
+ "url"
143
+ ],
144
+ write: ["path", "file_path"],
145
+ edit: ["path", "file_path"],
146
+ code: ["description"],
147
+ others: []
148
+ };
149
+ /**
150
+ * Strip the workspace root from a workspace-rooted absolute path (display only).
151
+ * @param text - the path to shorten.
152
+ * @param cwd - session workspace root; absent or empty leaves the path unchanged.
153
+ * @returns the path relative to the workspace root, or unchanged when it is not rooted there.
154
+ */
155
+ function relativizeToCwd(text, cwd) {
156
+ if (cwd === void 0 || cwd === "") return text;
157
+ const root = cwd.replace(/[/\\]+$/, "");
158
+ if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\`)) return text.slice(root.length + 1);
159
+ return text;
160
+ }
161
+ function deriveSummary(variant, argsRaw) {
162
+ const parsed = parseArgs(argsRaw);
163
+ if (typeof parsed !== "object" || parsed === null) return firstLine(argsRaw);
164
+ const args = parsed;
165
+ if (variant === "search" && Array.isArray(args.queries)) {
166
+ const queries = args.queries.filter((query) => typeof query === "string" && query !== "");
167
+ if (queries.length > 0) return queries.map(firstLine).join(", ");
168
+ }
169
+ const picked = pickString(args, SUMMARY_KEYS[variant]);
170
+ if (picked !== void 0) return firstLine(picked);
171
+ for (const v of Object.values(args)) if (typeof v === "string" && v !== "") return firstLine(v);
172
+ return firstLine(argsRaw);
173
+ }
174
+ /** Path keys only — never `url` (web_fetch lands on the read variant). */
175
+ const FILE_PATH_KEYS = ["path", "file_path"];
176
+ /** File-tool variants whose summary may be an openable workspace path. */
177
+ const FILE_PATH_VARIANTS = new Set([
178
+ "read",
179
+ "write",
180
+ "edit"
181
+ ]);
182
+ function deriveFilePath(variant, argsRaw) {
183
+ if (!FILE_PATH_VARIANTS.has(variant)) return void 0;
184
+ const parsed = parseArgs(argsRaw);
185
+ if (typeof parsed !== "object" || parsed === null) return void 0;
186
+ const picked = pickString(parsed, FILE_PATH_KEYS);
187
+ return picked === void 0 ? void 0 : firstLine(picked);
188
+ }
189
+ function deriveBody(variant, argsRaw) {
190
+ if (argsRaw === "") return null;
191
+ const parsed = parseArgs(argsRaw);
192
+ if (parsed === void 0) return argsRaw;
193
+ if (variant === "code" && typeof parsed === "object" && parsed !== null) {
194
+ const code = parsed.code;
195
+ if (typeof code === "string" && code !== "") return code;
196
+ }
197
+ return JSON.stringify(parsed, null, 2);
198
+ }
199
+ /**
200
+ * Derive the full row model from a frozen call slice.
201
+ * @param toolName - wire tool name (dispatch-supplied; survives windowless results).
202
+ * @param block - RunningToolCall or ToolResultNode off the snapshot caches.
203
+ * @param cwd - session workspace root; workspace-rooted path summaries display relative to it.
204
+ * @param home - host account home; a leftover POSIX home path displays as `~`.
205
+ * @returns the row model.
206
+ */
207
+ function toolRowModel(toolName, block, cwd, home) {
208
+ const variant = classifyTool(toolName);
209
+ const done = "kind" in block;
210
+ const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? "";
211
+ const state = !done ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError ? "error" : "ok";
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;
215
+ const output = done ? resultText(block) || null : null;
216
+ const errorSummary = state === "error" && output !== null ? firstLine(output) : null;
217
+ return {
218
+ variant,
219
+ titleKey: toolTitleKey ?? VARIANT_TITLE_KEYS[variant],
220
+ summary,
221
+ filePath: deriveFilePath(variant, argsRaw),
222
+ body: deriveBody(variant, argsRaw),
223
+ output,
224
+ errorSummary,
225
+ state
226
+ };
227
+ }
228
+ //#endregion
229
+ //#region lib/types/client/tool/models/raw-tool-call.js
230
+ const parsedCalls = /* @__PURE__ */ new WeakMap();
231
+ /**
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.
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
+ }
262
+ /**
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.
327
+ * @param sessionCwd - the session workspace root; a workspace-rooted absolute
328
+ * path label displays relative to it. Absent leaves the path as authored.
329
+ * @param home - host account home; a leftover POSIX home path displays as `~`.
330
+ * @returns the read-card props, or null for the generic path.
331
+ */
332
+ function readCardModel(block, sessionCwd, home) {
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;
340
+ return {
341
+ label: abbreviateHomePath(relativizeToCwd(meta.path, sessionCwd), home),
342
+ lines: meta.lines,
343
+ totalLines: meta.totalLines,
344
+ lang: meta.lang
345
+ };
346
+ }
347
+ //#endregion
348
+ //#region lib/types/client/tool/models/diff-card-model.js
349
+ /**
350
+ * Narrow opaque result metadata's `diffs` to well-formed hunks.
351
+ * @param diffs - the metadata field to validate.
352
+ * @returns the validated hunks, or null when the payload is not usable.
353
+ */
354
+ function narrowDiffs(diffs) {
355
+ if (!Array.isArray(diffs) || diffs.length === 0) return null;
356
+ const out = [];
357
+ for (const hunk of diffs) {
358
+ if (typeof hunk !== "object" || hunk === null) return null;
359
+ const { path, oldText, newText } = hunk;
360
+ if (typeof path !== "string") return null;
361
+ if (oldText !== null && typeof oldText !== "string") return null;
362
+ if (typeof newText !== "string") return null;
363
+ out.push({
364
+ path,
365
+ oldText,
366
+ newText
367
+ });
368
+ }
369
+ return out;
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
+ }
436
+ /**
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.
443
+ * @returns the diff-card props, or null for the generic path.
444
+ */
445
+ function diffCardModel(block) {
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 } };
455
+ }
456
+ //#endregion
457
+ //#region lib/types/client/tool/models/search-card-model.js
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;
505
+ }
506
+ function flattenContent(content) {
507
+ const text = content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
508
+ return text === "" ? void 0 : text;
509
+ }
510
+ /**
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.
514
+ */
515
+ function searchCardModel(block) {
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;
523
+ const common = {
524
+ truncated: meta.truncated,
525
+ total: meta.total
526
+ };
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 : {
532
+ recovery,
533
+ card: {
534
+ kind: "matches",
535
+ files,
536
+ ...common
537
+ }
538
+ };
539
+ }
540
+ if (meta.shape !== "paths" || !Array.isArray(meta.paths)) return null;
541
+ if (!meta.paths.every((path) => typeof path === "string")) return null;
542
+ return {
543
+ recovery,
544
+ card: {
545
+ kind: "paths",
546
+ paths: [...meta.paths],
547
+ ...common
548
+ }
549
+ };
550
+ }
551
+ //#endregion
552
+ //#region lib/types/client/tool/models/terminal-card-model.js
553
+ /**
554
+ * Build the TerminalBlock display copy from the conversation locale seat —
555
+ * the one place the primitive's label surface pairs with this package's
556
+ * dictionary, shared by every terminal render site (chat row, bash row,
557
+ * details panel).
558
+ * @param t - the render site's conversation locale seat.
559
+ * @returns the full label set for {@link TerminalBlockProps}'s `labels`.
560
+ */
561
+ function terminalBlockLabels(t) {
562
+ return {
563
+ signal: (signal) => t("terminal.signal", { signal }),
564
+ exitCode: (code) => t("terminal.exitCode", { code }),
565
+ running: t("terminal.running"),
566
+ failed: t("terminal.failed"),
567
+ done: t("terminal.done"),
568
+ copy: t("copy"),
569
+ copied: t("copied"),
570
+ noOutput: t("terminal.noOutput"),
571
+ collapseAria: t("terminal.collapseAria"),
572
+ collapse: t("collapse"),
573
+ expandAria: (hidden) => t("terminal.expandAria", { n: hidden }),
574
+ expand: (hidden) => t("terminal.expandRest", { n: hidden })
575
+ };
576
+ }
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
+ /**
601
+ * True when a settled terminal card reports a failing exit — a non-zero code
602
+ * or a terminating signal. The bash tool settles a failing command as a
603
+ * completed call (`isError` stays false: the exit status is result data), so
604
+ * this is the collapsed row's only failure signal; without it the red exit
605
+ * pill would be visible only after expanding the card.
606
+ * @param model - a derived terminal card.
607
+ * @returns whether the card's exit status is a failure.
608
+ */
609
+ function terminalFailed(model) {
610
+ const { exitCode, signal, running } = model.card;
611
+ return running !== true && (exitCode !== void 0 && exitCode !== 0 || signal !== void 0);
612
+ }
613
+ /**
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.
619
+ * @param sessionCwd - the session workspace root, if the caller knows it.
620
+ * @returns the working directory for the prompt label, or undefined.
621
+ */
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));
626
+ }
627
+ /**
628
+ * Collapse `.` and `..` segments so the prompt label names the directory the
629
+ * command actually ran in. The bash executor resolves the workdir before
630
+ * running, so a joined `/w/app/..` must display as `w`, not as `..`. Separators
631
+ * are preserved as authored (a Windows path keeps its backslashes) because this
632
+ * value is only ever displayed; a `..` that would climb past the root is
633
+ * dropped, which is what a filesystem does with it. A UNC path's `server` and
634
+ * `share` are part of its root, not poppable segments: Windows cannot climb
635
+ * above a share, so `\\\\server\\share` with a `..` stays there.
636
+ * @param path - a joined or absolute path, possibly carrying `.`/`..` segments.
637
+ * @returns the same path with those segments resolved.
638
+ */
639
+ function normalizeSegments(path) {
640
+ if (!/(?:^|[/\\])\.\.?(?:[/\\]|$)/.test(path)) return path;
641
+ const unc = /^[/\\]{2}([^/\\]+)[/\\]+([^/\\]+)/.exec(path);
642
+ if (unc !== null) {
643
+ const [matched, server, share] = unc;
644
+ const root = `\\\\${String(server)}\\${String(share)}`;
645
+ const rest = collapse(path.slice(matched.length), true);
646
+ return rest === "" ? root : `${root}\\${rest}`;
647
+ }
648
+ const separator = path.includes("\\") && !path.includes("/") ? "\\" : "/";
649
+ const rooted = /^[/\\]/.test(path);
650
+ const drive = /^[A-Za-z]:/.exec(path)?.[0] ?? "";
651
+ const body = collapse(path.slice(drive.length), rooted || drive !== "", separator);
652
+ const leading = rooted ? separator : "";
653
+ return drive === "" ? `${leading}${body}` : `${drive}${rooted ? leading : separator}${body}`;
654
+ }
655
+ /**
656
+ * Collapse the `.`/`..` segments of a path body against a known root state.
657
+ * @param body - the path after any drive letter or UNC root.
658
+ * @param rooted - the body hangs off a root, so a `..` at its top is dropped
659
+ * the way a filesystem drops one; without a root the `..` is kept, since it
660
+ * stays meaningful against a cwd this function cannot see.
661
+ * @param separator - separator to rejoin with (default `/`).
662
+ * @returns the collapsed body, without leading or trailing separators.
663
+ */
664
+ function collapse(body, rooted, separator = "/") {
665
+ const kept = [];
666
+ for (const segment of body.split(/[/\\]/)) {
667
+ if (segment === "" || segment === ".") continue;
668
+ if (segment === "..") {
669
+ if (kept.length > 0 && kept[kept.length - 1] !== "..") kept.pop();
670
+ else if (!rooted) kept.push(segment);
671
+ continue;
672
+ }
673
+ kept.push(segment);
674
+ }
675
+ return kept.join(separator);
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
+ }
703
+ /**
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 `@prettier-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.
760
+ */
761
+ function terminalCardModel(block, sessionCwd) {
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,
779
+ card: {
780
+ cwd,
781
+ output: void 0,
782
+ exitCode: void 0,
783
+ signal: void 0,
784
+ running: true
785
+ }
786
+ };
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);
791
+ return {
792
+ copy,
793
+ card: {
794
+ cwd,
795
+ output: status.output,
796
+ exitCode: status.exitCode,
797
+ signal: status.signal,
798
+ running: false
799
+ }
800
+ };
801
+ }
802
+ //#endregion
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
+ }
837
+ /**
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.
841
+ */
842
+ function webCardModel(block) {
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 {
861
+ kind: "fetch",
862
+ url: meta.url,
863
+ statusCode: meta.statusCode,
864
+ truncated: meta.truncated
865
+ };
866
+ }
867
+ //#endregion
868
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
869
+ function r(e) {
870
+ var t, f, n = "";
871
+ if ("string" == typeof e || "number" == typeof e) n += e;
872
+ else if ("object" == typeof e) if (Array.isArray(e)) {
873
+ var o = e.length;
874
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
875
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
876
+ return n;
877
+ }
878
+ function clsx() {
879
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
880
+ return n;
881
+ }
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/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/components/AskQuestionCard.module.css.mjs
975
+ const css$4 = ".ejgala_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}.ejgala_item{flex-direction:column;gap:2px;min-width:0;display:flex}.ejgala_question,.ejgala_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}.ejgala_question{color:var(--dsw-alias-label-tertiary)}.ejgala_answer{color:var(--dsw-alias-label-primary)}.ejgala_answerLine{display:block}.ejgala_skipped{color:var(--dsw-alias-label-tertiary)}.ejgala_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}.ejgala_questionList{flex-direction:column;gap:8px;margin:0;padding-left:20px;display:flex}.ejgala_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 = "@prettier-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 = "@prettier-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": "ejgala_answer",
986
+ "answerLine": "ejgala_answerLine",
987
+ "card": "ejgala_card",
988
+ "item": "ejgala_item",
989
+ "question": "ejgala_question",
990
+ "questionList": "ejgala_questionList",
991
+ "skipped": "ejgala_skipped",
992
+ "unansweredQuestion": "ejgala_unansweredQuestion",
993
+ "verdict": "ejgala_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
1037
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/components/ToolRow.module.css.mjs
1038
+ const css$3 = ".RAJz1a_root{flex-direction:column;display:flex}.RAJz1a_row{position:relative;overflow:hidden}.RAJz1a_root[data-state=running] .RAJz1a_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 RAJz1a_dsh-tool-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes RAJz1a_dsh-tool-row-sweep{0%{left:-300px}90%,to{left:100%}}.RAJz1a_leading{flex-shrink:0}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_leading,.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{color:var(--dsw-alias-state-business-primary)}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_title{font-weight:500}.RAJz1a_root[data-tool^=cordis_] .RAJz1a_sep{background:var(--dsw-alias-state-business-primary)}.RAJz1a_chevron{color:var(--dsw-alias-label-secondary)}.RAJz1a_title{font-weight:400}.RAJz1a_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.RAJz1a_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}.RAJz1a_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}.RAJz1a_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;text-decoration-color:var(--dsw-alias-label-quaternary);text-underline-offset:3px;cursor:pointer;background:0 0;border:none;flex:auto;margin:0;padding:0;overflow:hidden}.RAJz1a_fileLink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor}.RAJz1a_errorSummary{color:var(--dsw-alias-state-error-primary)}.RAJz1a_bodyWrap{flex-direction:column;display:flex}.RAJz1a_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}.RAJz1a_root:hover .RAJz1a_inspectButton,.RAJz1a_inspectButton:focus-visible{opacity:1}.RAJz1a_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.RAJz1a_bodyScroll{max-height:260px;overflow-y:auto}.RAJz1a_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}.RAJz1a_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.RAJz1a_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.RAJz1a_ioSection::-webkit-scrollbar-track{margin:6px 0}.RAJz1a_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.RAJz1a_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:1px}.RAJz1a_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.RAJz1a_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.RAJz1a_codeBody,.RAJz1a_terminalBody,.RAJz1a_diffBody,.RAJz1a_readBody,.RAJz1a_searchBody,.RAJz1a_webBody{margin:4px 0 4px 4px}.RAJz1a_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}.RAJz1a_codeBody{--dsl-code-block-content-font:var(--dsw-font-markdown-code-block-small)}.RAJz1a_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)}.RAJz1a_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1039
+ const tagId$3 = "@prettier-ai/dsh-client-ui-tool/ToolRow.module.css";
1040
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1041
+ const tag = document.createElement("style");
1042
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-tool";
1043
+ tag.dataset.pluginCss = tagId$3;
1044
+ tag.textContent = css$3;
1045
+ document.head.appendChild(tag);
1046
+ }
1047
+ var ToolRow_module_css_default = {
1048
+ "bodyScroll": "RAJz1a_bodyScroll",
1049
+ "bodyWrap": "RAJz1a_bodyWrap",
1050
+ "chevron": "RAJz1a_chevron",
1051
+ "codeBody": "RAJz1a_codeBody",
1052
+ "diffBody": "RAJz1a_diffBody",
1053
+ "dsh-tool-row-sweep": "RAJz1a_dsh-tool-row-sweep",
1054
+ "errorSummary": "RAJz1a_errorSummary",
1055
+ "fileLink": "RAJz1a_fileLink",
1056
+ "inspectButton": "RAJz1a_inspectButton",
1057
+ "ioCard": "RAJz1a_ioCard",
1058
+ "ioDivider": "RAJz1a_ioDivider",
1059
+ "ioLabel": "RAJz1a_ioLabel",
1060
+ "ioSection": "RAJz1a_ioSection",
1061
+ "ioText": "RAJz1a_ioText",
1062
+ "leading": "RAJz1a_leading",
1063
+ "readBody": "RAJz1a_readBody",
1064
+ "root": "RAJz1a_root",
1065
+ "row": "RAJz1a_row",
1066
+ "searchBody": "RAJz1a_searchBody",
1067
+ "searchRecovery": "RAJz1a_searchRecovery",
1068
+ "sep": "RAJz1a_sep",
1069
+ "summary": "RAJz1a_summary",
1070
+ "summarySuffix": "RAJz1a_summarySuffix",
1071
+ "terminalBody": "RAJz1a_terminalBody",
1072
+ "title": "RAJz1a_title",
1073
+ "visuallyHidden": "RAJz1a_visuallyHidden",
1074
+ "webBody": "RAJz1a_webBody"
1075
+ };
1076
+ //#endregion
1077
+ //#region lib/types/client/tool/components/ToolRow.js
1078
+ function leadingFor$1(state, icon) {
1079
+ switch (state) {
1080
+ case "error": return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "error" });
1081
+ case "stopped": return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "warning" });
1082
+ default: return icon;
1083
+ }
1084
+ }
1085
+ /** Visually hidden run-state label: the StateDot and the CSS sweep are both
1086
+ * aria-hidden / colour-only, so assistive technology needs this text to know a
1087
+ * row is running, failed, or interrupted. null in the ok state (the icon and
1088
+ * summary already describe a settled row). */
1089
+ function stateStatus$1(state, t) {
1090
+ switch (state) {
1091
+ case "running": return t("row.running");
1092
+ case "error": return t("row.failed");
1093
+ case "stopped": return t("row.stopped");
1094
+ default: return null;
1095
+ }
1096
+ }
1097
+ function ToolRow({ t, variant, toolName, icon, title, summary, summarySuffix, body, output, askQuestion, errorSummary, terminal, diff, read, search, web, state, filePath, onOpenFile, inspect }) {
1098
+ const [expanded, setExpanded] = (0, react.useState)(false);
1099
+ const terminalLabels = (0, react.useMemo)(() => terminalBlockLabels(t), [t]);
1100
+ const diffLabels = (0, react.useMemo)(() => diffBlockLabels(t), [t]);
1101
+ const readLabels = (0, react.useMemo)(() => readBlockLabels(t), [t]);
1102
+ const searchLabels = (0, react.useMemo)(() => searchBlockLabels(t), [t]);
1103
+ const webLabels = (0, react.useMemo)(() => webBlockLabels(t), [t]);
1104
+ const terminalBody = terminal === void 0 || terminal === null ? null : localizeTerminalCardModel(terminal, t);
1105
+ const diffBody = diff ?? null;
1106
+ const readBody = read ?? null;
1107
+ const searchBody = search ?? null;
1108
+ const webBody = web ?? null;
1109
+ const askQuestionBody = askQuestion ?? null;
1110
+ const outputText = output ?? null;
1111
+ const expandable = body !== null || outputText !== null || (askQuestionBody ?? terminalBody ?? diffBody ?? readBody ?? searchBody ?? webBody) !== null;
1112
+ const open = expanded && expandable;
1113
+ const status = stateStatus$1(state, t);
1114
+ const failureLine = state === "error" ? errorSummary ?? null : null;
1115
+ const summaryText = failureLine ?? terminalBody?.description ?? summary;
1116
+ const suffix = failureLine === null ? summarySuffix ?? null : null;
1117
+ const fileLink = filePath !== void 0 && onOpenFile !== void 0 && failureLine === null;
1118
+ const toggleExpand = () => {
1119
+ setExpanded((v) => !v);
1120
+ };
1121
+ const openFile = (event) => {
1122
+ event.stopPropagation();
1123
+ if (filePath !== void 0) onOpenFile?.(filePath);
1124
+ };
1125
+ const fileLinkKeyDown = (event) => {
1126
+ if (event.key === "Enter" || event.key === " ") event.stopPropagation();
1127
+ };
1128
+ const cardBody = variant === "code" ? null : body;
1129
+ return (0, react_jsx_runtime.jsxs)("div", {
1130
+ className: ToolRow_module_css_default.root,
1131
+ "data-variant": variant,
1132
+ "data-tool": toolName,
1133
+ "data-state": state,
1134
+ children: [status !== null && (0, react_jsx_runtime.jsx)("span", {
1135
+ className: ToolRow_module_css_default.visuallyHidden,
1136
+ children: status
1137
+ }), (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.DisclosureRow, {
1138
+ rowClassName: ToolRow_module_css_default.row,
1139
+ leadingClassName: ToolRow_module_css_default.leading,
1140
+ titleClassName: ToolRow_module_css_default.title,
1141
+ chevronClassName: ToolRow_module_css_default.chevron,
1142
+ icon: leadingFor$1(state, icon),
1143
+ title,
1144
+ open,
1145
+ expandable,
1146
+ expandOnRowClick: true,
1147
+ keepContentWhenOpen: true,
1148
+ onToggle: toggleExpand,
1149
+ collapsedContent: summaryText !== "" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1150
+ (0, react_jsx_runtime.jsx)("span", {
1151
+ className: ToolRow_module_css_default.sep,
1152
+ "aria-hidden": true
1153
+ }),
1154
+ fileLink ? (0, react_jsx_runtime.jsx)("button", {
1155
+ type: "button",
1156
+ className: ToolRow_module_css_default.fileLink,
1157
+ onClick: openFile,
1158
+ onKeyDown: fileLinkKeyDown,
1159
+ children: summaryText
1160
+ }) : (0, react_jsx_runtime.jsx)("span", {
1161
+ className: clsx(ToolRow_module_css_default.summary, failureLine !== null && ToolRow_module_css_default.errorSummary),
1162
+ children: summaryText
1163
+ }),
1164
+ suffix !== null && (0, react_jsx_runtime.jsx)("span", {
1165
+ className: ToolRow_module_css_default.summarySuffix,
1166
+ children: suffix
1167
+ })
1168
+ ] }),
1169
+ children: (0, react_jsx_runtime.jsxs)("div", {
1170
+ className: ToolRow_module_css_default.bodyWrap,
1171
+ children: [askQuestionBody !== null ? (0, react_jsx_runtime.jsx)(AskQuestionCard, { card: askQuestionBody }) : terminalBody !== null ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.TerminalBlock, {
1172
+ ...terminalBody.card,
1173
+ maxLines: Infinity,
1174
+ labels: terminalLabels,
1175
+ className: ToolRow_module_css_default.terminalBody
1176
+ }) : diffBody !== null ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.DiffBlock, {
1177
+ ...diffBody.card,
1178
+ labels: diffLabels,
1179
+ maxLines: 8,
1180
+ className: ToolRow_module_css_default.diffBody
1181
+ }) : readBody !== null ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.ReadBlock, {
1182
+ ...readBody,
1183
+ labels: readLabels,
1184
+ maxLines: 8,
1185
+ className: ToolRow_module_css_default.readBody
1186
+ }) : searchBody !== null ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.SearchBlock, {
1187
+ ...searchBody.card,
1188
+ labels: searchLabels,
1189
+ maxLines: 8,
1190
+ className: ToolRow_module_css_default.searchBody
1191
+ }), searchBody.recovery !== void 0 && (0, react_jsx_runtime.jsx)("div", {
1192
+ className: ToolRow_module_css_default.searchRecovery,
1193
+ children: searchBody.recovery
1194
+ })] }) : webBody !== null ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.WebBlock, {
1195
+ ...webBody,
1196
+ labels: webLabels,
1197
+ className: ToolRow_module_css_default.webBody
1198
+ }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [variant === "code" && body !== null && (0, react_jsx_runtime.jsx)("div", {
1199
+ className: ToolRow_module_css_default.bodyScroll,
1200
+ children: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.CodeBlock, {
1201
+ code: body,
1202
+ lang: "typescript",
1203
+ copyLabel: t("copy"),
1204
+ copiedLabel: t("copied"),
1205
+ className: ToolRow_module_css_default.codeBody
1206
+ })
1207
+ }), (cardBody !== null || outputText !== null) && (0, react_jsx_runtime.jsxs)("div", {
1208
+ className: ToolRow_module_css_default.ioCard,
1209
+ children: [
1210
+ cardBody !== null && (0, react_jsx_runtime.jsxs)("div", {
1211
+ className: ToolRow_module_css_default.ioSection,
1212
+ children: [(0, react_jsx_runtime.jsx)("span", {
1213
+ className: ToolRow_module_css_default.ioLabel,
1214
+ children: t("row.input")
1215
+ }), (0, react_jsx_runtime.jsx)("span", {
1216
+ className: ToolRow_module_css_default.ioText,
1217
+ children: cardBody
1218
+ })]
1219
+ }),
1220
+ cardBody !== null && outputText !== null && (0, react_jsx_runtime.jsx)("span", {
1221
+ className: ToolRow_module_css_default.ioDivider,
1222
+ "aria-hidden": true
1223
+ }),
1224
+ outputText !== null && (0, react_jsx_runtime.jsxs)("div", {
1225
+ className: ToolRow_module_css_default.ioSection,
1226
+ children: [(0, react_jsx_runtime.jsx)("span", {
1227
+ className: ToolRow_module_css_default.ioLabel,
1228
+ children: t("row.output")
1229
+ }), (0, react_jsx_runtime.jsx)("span", {
1230
+ className: ToolRow_module_css_default.ioText,
1231
+ "data-error": state === "error" || void 0,
1232
+ children: outputText
1233
+ })]
1234
+ })
1235
+ ]
1236
+ })] }), inspect !== void 0 && (0, react_jsx_runtime.jsxs)("button", {
1237
+ type: "button",
1238
+ className: ToolRow_module_css_default.inspectButton,
1239
+ onClick: inspect,
1240
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
1241
+ })]
1242
+ })
1243
+ })]
1244
+ });
1245
+ }
1246
+ //#endregion
1247
+ //#region lib/types/client/tool/toolviews/GenericToolCard.js
1248
+ /** Variant leading icons (figma table); all glyphs render at 14 inside the 16px leading box. */
1249
+ const VARIANT_ICONS = {
1250
+ search: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
1251
+ read: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
1252
+ bash: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconApiOutline14, { size: 14 }),
1253
+ write: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
1254
+ edit: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
1255
+ code: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconCodeOutline16, { size: 14 }),
1256
+ others: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 })
1257
+ };
1258
+ function GenericToolCard({ toolName, block, cwd, home, openFile, inspect, t }) {
1259
+ const model = toolRowModel(toolName, block, cwd, home);
1260
+ const terminal = terminalCardModel(block, cwd);
1261
+ const read = readCardModel(block, cwd, home);
1262
+ const diff = diffCardModel(block);
1263
+ const search = searchCardModel(block);
1264
+ const web = webCardModel(block);
1265
+ const state = model.state === "ok" && terminal !== null && terminalFailed(terminal) ? "error" : model.state;
1266
+ const singleFile = model.filePath !== void 0;
1267
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1268
+ t,
1269
+ variant: model.variant,
1270
+ toolName,
1271
+ icon: VARIANT_ICONS[model.variant],
1272
+ title: t(model.titleKey),
1273
+ summary: model.summary,
1274
+ body: singleFile ? null : model.body,
1275
+ output: model.output,
1276
+ errorSummary: model.errorSummary,
1277
+ terminal,
1278
+ diff,
1279
+ read,
1280
+ search,
1281
+ web,
1282
+ state,
1283
+ filePath: model.filePath,
1284
+ onOpenFile: singleFile ? openFile : void 0,
1285
+ inspect
1286
+ });
1287
+ }
1288
+ //#endregion
1289
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/ToolCallTree.module.css.mjs
1290
+ const css$2 = "._3IQlVW_callRow{border-radius:6px}._3IQlVW_subCalls{border-left:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:4px;margin:4px 0 2px 22px;padding-left:8px;display:flex}";
1291
+ const tagId$2 = "@prettier-ai/dsh-client-ui-tool/ToolCallTree.module.css";
1292
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1293
+ const tag = document.createElement("style");
1294
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-tool";
1295
+ tag.dataset.pluginCss = tagId$2;
1296
+ tag.textContent = css$2;
1297
+ document.head.appendChild(tag);
1298
+ }
1299
+ var ToolCallTree_module_css_default = {
1300
+ "callRow": "_3IQlVW_callRow",
1301
+ "subCalls": "_3IQlVW_subCalls"
1302
+ };
1303
+ //#endregion
1304
+ //#region lib/types/client/tool/ToolCallTree.js
1305
+ /** Root/subcall Tool composition with one keyed atomic dispatch path. */
1306
+ /** Resolve a Tool call's wire name from either lifecycle form. */
1307
+ function callName(node) {
1308
+ return "kind" in node ? node.call?.name ?? "" : node.name;
1309
+ }
1310
+ /** One atomic call dispatched through the Tool-owned keyed slot. */
1311
+ const ToolCall = (0, react.memo)(function ToolCall({ renderSlot, callId, toolName, block, openFile, selected, cwd, home, inspectCall, t, children }) {
1312
+ const owner = (0, react.useMemo)(() => ({
1313
+ callId,
1314
+ toolName,
1315
+ block,
1316
+ openFile,
1317
+ cwd,
1318
+ home,
1319
+ inspect: () => {
1320
+ inspectCall(callId);
1321
+ }
1322
+ }), [
1323
+ callId,
1324
+ toolName,
1325
+ block,
1326
+ openFile,
1327
+ cwd,
1328
+ home,
1329
+ inspectCall
1330
+ ]);
1331
+ return (0, react_jsx_runtime.jsxs)("div", {
1332
+ className: ToolCallTree_module_css_default.callRow,
1333
+ "data-chat-anchor-key": `call:${callId}`,
1334
+ "data-chat-call-id": callId,
1335
+ "data-selected": selected || void 0,
1336
+ children: [renderSlot("tool.call.toolview", owner, {
1337
+ entryKey: toolName,
1338
+ fallback: (0, react_jsx_runtime.jsx)(GenericToolCard, {
1339
+ ...owner,
1340
+ t
1341
+ })
1342
+ }), children]
1343
+ });
1344
+ });
1345
+ const ToolCallBranch = (0, react.memo)(function ToolCallBranch({ renderSlot, block, selectedCallId, cwd, home, openFile, inspectCall, t }) {
1346
+ return (0, react_jsx_runtime.jsx)(ToolCall, {
1347
+ renderSlot,
1348
+ callId: block.callId,
1349
+ toolName: callName(block),
1350
+ block,
1351
+ openFile,
1352
+ selected: block.callId === selectedCallId,
1353
+ cwd,
1354
+ home,
1355
+ inspectCall,
1356
+ t,
1357
+ children: block.subCalls.length > 0 ? (0, react_jsx_runtime.jsx)("div", {
1358
+ className: ToolCallTree_module_css_default.subCalls,
1359
+ "data-subcalls": true,
1360
+ children: block.subCalls.map((child) => (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1361
+ renderSlot,
1362
+ block: child,
1363
+ selectedCallId,
1364
+ cwd,
1365
+ home,
1366
+ openFile,
1367
+ inspectCall,
1368
+ t
1369
+ }, child.callId))
1370
+ }) : null
1371
+ });
1372
+ });
1373
+ /**
1374
+ * Render one root Tool call and its recursive children through the same
1375
+ * atomic keyed dispatch.
1376
+ * @param props - whole-Tool owner data and the Tool-owned child-slot share.
1377
+ * @returns the Tool call tree.
1378
+ */
1379
+ function ToolCallTree({ renderSlot, node, selectedCallId, cwd, openFile, inspectCall, useConnectionGeneration, t }) {
1380
+ const home = useConnectionGeneration((generation) => generation?.host.home);
1381
+ const block = node.data.root;
1382
+ return (0, react_jsx_runtime.jsx)(ToolCallBranch, {
1383
+ renderSlot,
1384
+ block,
1385
+ selectedCallId,
1386
+ cwd,
1387
+ home,
1388
+ openFile,
1389
+ inspectCall,
1390
+ t
1391
+ });
1392
+ }
1393
+ //#endregion
1394
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/ToolDetails.module.css.mjs
1395
+ const css$1 = ".Uhcd_a_description{color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);margin:0 0 6px}.Uhcd_a_cardBody{margin:0}.Uhcd_a_recovery{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font:var(--dsw-font-xs-13);margin:6px 0 0}.Uhcd_a_code{background:var(--dsw-alias-markdown-code-block);font-family:var(--ds-font-family-code);color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;border-radius:12px;margin:0;padding:16px;font-size:13px;line-height:22px}.Uhcd_a_code[data-error]{color:var(--dsw-alias-state-error-primary)}.Uhcd_a_read,.Uhcd_a_web{margin:0}.Uhcd_a_empty{color:var(--dsw-alias-label-tertiary);padding:8px 0;font-size:13px;line-height:20px}";
1396
+ const tagId$1 = "@prettier-ai/dsh-client-ui-tool/ToolDetails.module.css";
1397
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
1398
+ const tag = document.createElement("style");
1399
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-tool";
1400
+ tag.dataset.pluginCss = tagId$1;
1401
+ tag.textContent = css$1;
1402
+ document.head.appendChild(tag);
1403
+ }
1404
+ var ToolDetails_module_css_default = {
1405
+ "cardBody": "Uhcd_a_cardBody",
1406
+ "code": "Uhcd_a_code",
1407
+ "description": "Uhcd_a_description",
1408
+ "empty": "Uhcd_a_empty",
1409
+ "read": "Uhcd_a_read",
1410
+ "recovery": "Uhcd_a_recovery",
1411
+ "web": "Uhcd_a_web"
1412
+ };
1413
+ //#endregion
1414
+ //#region lib/types/client/tool/ToolDetails.js
1415
+ /** Card-aware output body for the selected Tool call in details. */
1416
+ /**
1417
+ * Render the selected Tool call's structured output when its raw fields form a
1418
+ * supported root card, otherwise preserve the flattened result text.
1419
+ * @param props - selected call slice, workspace root, host home, and locale seat.
1420
+ * @returns the details output body.
1421
+ */
1422
+ function ToolDetails({ block, cwd, useConnectionGeneration, t }) {
1423
+ const home = useConnectionGeneration((generation) => generation?.host.home);
1424
+ const terminalModel = terminalCardModel(block, cwd);
1425
+ if (terminalModel !== null) {
1426
+ const terminal = localizeTerminalCardModel(terminalModel, t);
1427
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [terminal.description !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1428
+ className: ToolDetails_module_css_default.description,
1429
+ children: terminal.description
1430
+ }) : null, (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.TerminalBlock, {
1431
+ ...terminal.card,
1432
+ labels: terminalBlockLabels(t),
1433
+ className: ToolDetails_module_css_default.cardBody
1434
+ })] });
1435
+ }
1436
+ const read = readCardModel(block, cwd, home);
1437
+ if (read !== null) return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.ReadBlock, {
1438
+ ...read,
1439
+ labels: readBlockLabels(t),
1440
+ className: ToolDetails_module_css_default.read
1441
+ });
1442
+ const diff = diffCardModel(block);
1443
+ if (diff !== null) return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.DiffBlock, {
1444
+ ...diff.card,
1445
+ labels: diffBlockLabels(t),
1446
+ className: ToolDetails_module_css_default.cardBody
1447
+ });
1448
+ const search = searchCardModel(block);
1449
+ if (search !== null) return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.SearchBlock, {
1450
+ ...search.card,
1451
+ labels: searchBlockLabels(t),
1452
+ className: ToolDetails_module_css_default.cardBody
1453
+ }), search.recovery !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
1454
+ className: ToolDetails_module_css_default.recovery,
1455
+ children: search.recovery
1456
+ }) : null] });
1457
+ const web = webCardModel(block);
1458
+ if (web !== null) {
1459
+ const body = "kind" in block ? resultText(block) : "";
1460
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.WebBlock, {
1461
+ ...web,
1462
+ labels: webBlockLabels(t),
1463
+ className: ToolDetails_module_css_default.web
1464
+ }), body !== "" ? (0, react_jsx_runtime.jsx)("pre", {
1465
+ className: ToolDetails_module_css_default.code,
1466
+ children: body
1467
+ }) : null] });
1468
+ }
1469
+ if (!("kind" in block)) return (0, react_jsx_runtime.jsx)("div", {
1470
+ className: ToolDetails_module_css_default.empty,
1471
+ children: t("details.running")
1472
+ });
1473
+ return (0, react_jsx_runtime.jsx)("pre", {
1474
+ className: ToolDetails_module_css_default.code,
1475
+ "data-error": block.isError || void 0,
1476
+ children: resultText(block)
1477
+ });
1478
+ }
1479
+ //#endregion
1480
+ //#region lib/types/client/locale.js
1481
+ /** Locale namespace supplied by the conversation owner to Tool renderers. */
1482
+ const CONVERSATION_NS = "conversation";
1483
+ //#endregion
1484
+ //#region lib/types/client/tool/toolviews/ask-question-row.js
1485
+ function isRecord(value) {
1486
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1487
+ }
1488
+ function parseJson(text) {
1489
+ try {
1490
+ return JSON.parse(text);
1491
+ } catch {
1492
+ return;
1493
+ }
1494
+ }
1495
+ /** Answer records from the result JSON; null when the result is malformed. */
1496
+ function answerEntries(text) {
1497
+ const parsed = parseJson(text);
1498
+ if (!isRecord(parsed)) return null;
1499
+ const answers = parsed.answers;
1500
+ if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
1501
+ const entries = [];
1502
+ for (const answer of answers) {
1503
+ 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;
1504
+ entries.push({
1505
+ id: answer.id,
1506
+ selected: answer.selected,
1507
+ ...answer.custom === void 0 ? {} : { custom: answer.custom }
1508
+ });
1509
+ }
1510
+ return entries;
1511
+ }
1512
+ /** Questions from call JSON; null when pairing with answers would be ambiguous. */
1513
+ function questionEntries(argsRaw) {
1514
+ const parsed = parseJson(argsRaw);
1515
+ if (!isRecord(parsed) || !Array.isArray(parsed.questions) || parsed.questions.length === 0) return null;
1516
+ const questions = [];
1517
+ const ids = /* @__PURE__ */ new Set();
1518
+ for (const question of parsed.questions) {
1519
+ if (!isRecord(question) || typeof question.id !== "string" || typeof question.question !== "string" || ids.has(question.id)) return null;
1520
+ ids.add(question.id);
1521
+ questions.push({
1522
+ id: question.id,
1523
+ question: question.question
1524
+ });
1525
+ }
1526
+ return questions;
1527
+ }
1528
+ /** Pair questions with result entries by their echoed stable ids. */
1529
+ function pairAnswers(argsRaw, answers) {
1530
+ const questions = questionEntries(argsRaw);
1531
+ if (questions === null || questions.length !== answers.length) return null;
1532
+ const byId = /* @__PURE__ */ new Map();
1533
+ for (const answer of answers) {
1534
+ if (byId.has(answer.id)) return null;
1535
+ byId.set(answer.id, answer);
1536
+ }
1537
+ const paired = [];
1538
+ for (const question of questions) {
1539
+ const answer = byId.get(question.id);
1540
+ if (answer === void 0) return null;
1541
+ paired.push({
1542
+ ...question,
1543
+ answers: [...answer.selected, ...answer.custom === void 0 || answer.custom === "" ? [] : [answer.custom]]
1544
+ });
1545
+ }
1546
+ return paired;
1547
+ }
1548
+ /** Answer summary plus structured transcript content from the two wire JSON documents. */
1549
+ function answeredPresentation(argsRaw, text, t) {
1550
+ const answers = answerEntries(text);
1551
+ if (answers === null) return null;
1552
+ const answered = answers.filter((answer) => answer.selected.length > 0 || (answer.custom ?? "") !== "").length;
1553
+ return {
1554
+ summary: t("ask.answered", {
1555
+ answered,
1556
+ total: answers.length
1557
+ }),
1558
+ questions: pairAnswers(argsRaw, answers)
1559
+ };
1560
+ }
1561
+ /** Best-effort answered-count summary when strict transcript pairing fails. */
1562
+ function answeredSummary(text, t) {
1563
+ const parsed = parseJson(text);
1564
+ if (!isRecord(parsed)) return null;
1565
+ const answers = parsed.answers;
1566
+ if (!Array.isArray(answers) || !answers.every(isRecord)) return null;
1567
+ const answered = answers.filter((a) => Array.isArray(a.selected) && a.selected.length > 0 || typeof a.custom === "string" && a.custom !== "").length;
1568
+ return t("ask.answered", {
1569
+ answered,
1570
+ total: answers.length
1571
+ });
1572
+ }
1573
+ /** Summarizes a pending, answered, cancelled, or interrupted question set. */
1574
+ function AskQuestionRow({ toolName, block, inspect, t }) {
1575
+ const model = toolRowModel(toolName, block);
1576
+ const code = "kind" in block ? block.error?.code : void 0;
1577
+ const argsRaw = ("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "";
1578
+ let summary = model.summary;
1579
+ let state = model.state;
1580
+ let transcript = null;
1581
+ if (code === "ASK_CANCELLED") {
1582
+ summary = t("ask.cancelled");
1583
+ state = "ok";
1584
+ const questions = questionEntries(argsRaw);
1585
+ if (questions !== null) transcript = {
1586
+ kind: "unanswered",
1587
+ questions,
1588
+ verdict: t("ask.cancelledDetail")
1589
+ };
1590
+ } else if (code === "ASK_ABORTED") {
1591
+ summary = t("ask.interrupted");
1592
+ state = "stopped";
1593
+ const questions = questionEntries(argsRaw);
1594
+ if (questions !== null) transcript = {
1595
+ kind: "unanswered",
1596
+ questions,
1597
+ verdict: t("ask.interruptedDetail")
1598
+ };
1599
+ } else if (model.state === "running") summary = t("ask.waiting");
1600
+ else if ("kind" in block && model.state === "ok") {
1601
+ const text = singleResultText(block);
1602
+ if (text !== void 0) {
1603
+ const presentation = answeredPresentation(argsRaw, text, t);
1604
+ summary = presentation?.summary ?? answeredSummary(text, t) ?? model.summary;
1605
+ if (presentation?.questions !== null && presentation?.questions !== void 0) transcript = {
1606
+ kind: "answered",
1607
+ questions: presentation.questions,
1608
+ skippedLabel: t("ask.skipped")
1609
+ };
1610
+ }
1611
+ }
1612
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1613
+ t,
1614
+ variant: model.variant,
1615
+ toolName,
1616
+ icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconQuestionOutline14, {}),
1617
+ title: t("ask.rowTitle"),
1618
+ summary,
1619
+ body: transcript === null ? model.body : null,
1620
+ output: transcript === null ? model.output : null,
1621
+ askQuestion: transcript,
1622
+ state,
1623
+ inspect
1624
+ });
1625
+ }
1626
+ /** Registers the ask-user-question conversation row. */
1627
+ const askQuestionToolview = {
1628
+ name: "ask-question-toolview",
1629
+ inject: ["slots"],
1630
+ apply(ctx) {
1631
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1632
+ name: "tool.call.toolview",
1633
+ key: "ask_user_question",
1634
+ locale: CONVERSATION_NS
1635
+ }, AskQuestionRow));
1636
+ }
1637
+ };
1638
+ //#endregion
1639
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-tool/src/client/tool/toolviews/bash-sample.module.css.mjs
1640
+ const css = ".nLfbSq_card{flex-direction:column;display:flex}.nLfbSq_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}.nLfbSq_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}.nLfbSq_ioSection{grid-template-columns:max-content 1fr;align-items:baseline;column-gap:14px;max-height:150px;padding:12px 16px;display:grid;overflow-y:auto}.nLfbSq_ioSection::-webkit-scrollbar-thumb{background-clip:padding-box;border:2px solid #0000;border-radius:6px}.nLfbSq_ioSection::-webkit-scrollbar-track{margin:6px 0}.nLfbSq_ioLabel{color:var(--dsw-alias-label-caption);align-self:start;position:sticky;top:0}.nLfbSq_ioDivider{background:var(--dsw-alias-border-l2);flex:none;height:1px}.nLfbSq_ioText{white-space:pre-wrap;word-break:break-word;min-width:0;color:var(--dsw-alias-label-secondary)}.nLfbSq_ioText[data-error]{color:var(--dsw-alias-state-error-primary)}.nLfbSq_root[data-expandable]{cursor:pointer}.nLfbSq_root{height:calc(24px + var(--dsh-content-font-delta,0px));align-items:center;min-width:0;display:flex;position:relative;overflow:hidden}.nLfbSq_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 nLfbSq_dsh-bash-row-sweep;position:absolute;top:0;bottom:0;left:0}@keyframes nLfbSq_dsh-bash-row-sweep{0%{left:-300px}90%,to{left:100%}}.nLfbSq_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}.nLfbSq_leading svg:not([data-state]){width:calc(14px + var(--dsh-content-font-delta,0px));height:calc(14px + var(--dsh-content-font-delta,0px))}.nLfbSq_chevron{color:var(--dsw-alias-label-secondary)}.nLfbSq_iconIdle{opacity:1;transition:opacity .1s;display:inline-flex}.nLfbSq_chevronHover{opacity:0;margin:auto;transition:opacity .1s;position:absolute;inset:0}.nLfbSq_root:hover .nLfbSq_iconIdle{opacity:0}.nLfbSq_root:hover .nLfbSq_chevronHover{opacity:1}.nLfbSq_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}.nLfbSq_sep{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}.nLfbSq_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}.nLfbSq_errorSummary{color:var(--dsw-alias-state-error-primary)}.nLfbSq_bodyWrap{flex-direction:column;display:flex}.nLfbSq_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}.nLfbSq_card:hover .nLfbSq_inspectButton,.nLfbSq_inspectButton:focus-visible{opacity:1}.nLfbSq_inspectButton:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}.nLfbSq_visuallyHidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
1641
+ const tagId = "@prettier-ai/dsh-client-ui-tool/bash-sample.module.css";
1642
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1643
+ const tag = document.createElement("style");
1644
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-tool";
1645
+ tag.dataset.pluginCss = tagId;
1646
+ tag.textContent = css;
1647
+ document.head.appendChild(tag);
1648
+ }
1649
+ var bash_sample_module_css_default = {
1650
+ "bodyWrap": "nLfbSq_bodyWrap",
1651
+ "card": "nLfbSq_card",
1652
+ "chevron": "nLfbSq_chevron",
1653
+ "chevronHover": "nLfbSq_chevronHover",
1654
+ "dsh-bash-row-sweep": "nLfbSq_dsh-bash-row-sweep",
1655
+ "errorSummary": "nLfbSq_errorSummary",
1656
+ "iconIdle": "nLfbSq_iconIdle",
1657
+ "inspectButton": "nLfbSq_inspectButton",
1658
+ "ioCard": "nLfbSq_ioCard",
1659
+ "ioDivider": "nLfbSq_ioDivider",
1660
+ "ioLabel": "nLfbSq_ioLabel",
1661
+ "ioSection": "nLfbSq_ioSection",
1662
+ "ioText": "nLfbSq_ioText",
1663
+ "leading": "nLfbSq_leading",
1664
+ "root": "nLfbSq_root",
1665
+ "sep": "nLfbSq_sep",
1666
+ "summary": "nLfbSq_summary",
1667
+ "terminal": "nLfbSq_terminal",
1668
+ "title": "nLfbSq_title",
1669
+ "visuallyHidden": "nLfbSq_visuallyHidden"
1670
+ };
1671
+ //#endregion
1672
+ //#region lib/types/client/tool/toolviews/bash-sample.js
1673
+ function leadingFor(state) {
1674
+ switch (state) {
1675
+ case "error": return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "error" });
1676
+ case "stopped": return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.StateDot, { state: "warning" });
1677
+ default: return (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconApiOutline14, { size: 14 });
1678
+ }
1679
+ }
1680
+ /** Visually hidden status — StateDot is aria-hidden; AT needs a text label. */
1681
+ function stateStatus(state, t) {
1682
+ switch (state) {
1683
+ case "running": return t("bash.running");
1684
+ case "error": return t("bash.failed");
1685
+ case "stopped": return t("bash.stopped");
1686
+ default: return null;
1687
+ }
1688
+ }
1689
+ /** Renders expandable Bash output with an accessible lifecycle label. */
1690
+ function BashRow({ toolName, block, sessionId, useSessions, inspect, t }) {
1691
+ const model = toolRowModel(toolName, block);
1692
+ const terminalModel = terminalCardModel(block, useSessions((list) => list.byId[sessionId]?.cwd));
1693
+ const terminal = terminalModel === null ? null : localizeTerminalCardModel(terminalModel, t);
1694
+ const state = model.state === "ok" && terminalModel !== null && terminalFailed(terminalModel) ? "error" : model.state;
1695
+ const status = stateStatus(state, t);
1696
+ const [expanded, setExpanded] = (0, react.useState)(false);
1697
+ const genericBody = terminal === null && (model.state === "error" || isSettledPersistentShellCall(block)) && (model.body !== null || model.output !== null);
1698
+ const expandable = terminal !== null || genericBody;
1699
+ const open = expanded && expandable;
1700
+ const failureLine = model.state === "error" ? model.errorSummary : null;
1701
+ const toggleExpand = () => {
1702
+ setExpanded((v) => !v);
1703
+ };
1704
+ const toggleFromKeyboard = (event) => {
1705
+ if (!expandable || event.key !== "Enter" && event.key !== " ") return;
1706
+ event.preventDefault();
1707
+ toggleExpand();
1708
+ };
1709
+ const leading = open ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: bash_sample_module_css_default.chevron }) : expandable ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("span", {
1710
+ className: bash_sample_module_css_default.iconIdle,
1711
+ children: leadingFor(state)
1712
+ }), (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(bash_sample_module_css_default.chevron, bash_sample_module_css_default.chevronHover) })] }) : leadingFor(state);
1713
+ return (0, react_jsx_runtime.jsxs)("div", {
1714
+ className: bash_sample_module_css_default.card,
1715
+ children: [(0, react_jsx_runtime.jsxs)("div", {
1716
+ className: bash_sample_module_css_default.root,
1717
+ "data-sample": "bash",
1718
+ "data-variant": "bash",
1719
+ "data-state": state,
1720
+ "data-expandable": expandable || void 0,
1721
+ role: expandable ? "button" : void 0,
1722
+ tabIndex: expandable ? 0 : void 0,
1723
+ "aria-expanded": expandable ? open : void 0,
1724
+ onClick: expandable ? toggleExpand : void 0,
1725
+ onKeyDown: expandable ? toggleFromKeyboard : void 0,
1726
+ children: [
1727
+ (0, react_jsx_runtime.jsx)("span", {
1728
+ className: bash_sample_module_css_default.leading,
1729
+ children: leading
1730
+ }),
1731
+ status !== null && (0, react_jsx_runtime.jsx)("span", {
1732
+ className: bash_sample_module_css_default.visuallyHidden,
1733
+ children: status
1734
+ }),
1735
+ (0, react_jsx_runtime.jsx)("span", {
1736
+ className: bash_sample_module_css_default.title,
1737
+ children: t(model.titleKey)
1738
+ }),
1739
+ (0, react_jsx_runtime.jsx)("span", {
1740
+ className: bash_sample_module_css_default.sep,
1741
+ "aria-hidden": true
1742
+ }),
1743
+ (0, react_jsx_runtime.jsx)("span", {
1744
+ className: clsx(bash_sample_module_css_default.summary, failureLine !== null && bash_sample_module_css_default.errorSummary),
1745
+ children: failureLine ?? terminal?.description ?? model.summary
1746
+ })
1747
+ ]
1748
+ }), open && (0, react_jsx_runtime.jsxs)("div", {
1749
+ className: bash_sample_module_css_default.bodyWrap,
1750
+ children: [terminal !== null ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.TerminalBlock, {
1751
+ ...terminal.card,
1752
+ maxLines: Infinity,
1753
+ labels: terminalBlockLabels(t),
1754
+ className: bash_sample_module_css_default.terminal
1755
+ }) : (0, react_jsx_runtime.jsxs)("div", {
1756
+ className: bash_sample_module_css_default.ioCard,
1757
+ children: [
1758
+ model.body !== null && (0, react_jsx_runtime.jsxs)("div", {
1759
+ className: bash_sample_module_css_default.ioSection,
1760
+ children: [(0, react_jsx_runtime.jsx)("span", {
1761
+ className: bash_sample_module_css_default.ioLabel,
1762
+ children: t("row.input")
1763
+ }), (0, react_jsx_runtime.jsx)("span", {
1764
+ className: bash_sample_module_css_default.ioText,
1765
+ children: model.body
1766
+ })]
1767
+ }),
1768
+ model.body !== null && model.output !== null && (0, react_jsx_runtime.jsx)("span", {
1769
+ className: bash_sample_module_css_default.ioDivider,
1770
+ "aria-hidden": true
1771
+ }),
1772
+ model.output !== null && (0, react_jsx_runtime.jsxs)("div", {
1773
+ className: bash_sample_module_css_default.ioSection,
1774
+ children: [(0, react_jsx_runtime.jsx)("span", {
1775
+ className: bash_sample_module_css_default.ioLabel,
1776
+ children: t("row.output")
1777
+ }), (0, react_jsx_runtime.jsx)("span", {
1778
+ className: bash_sample_module_css_default.ioText,
1779
+ "data-error": state === "error" || void 0,
1780
+ children: model.output
1781
+ })]
1782
+ })
1783
+ ]
1784
+ }), inspect !== void 0 && (0, react_jsx_runtime.jsxs)("button", {
1785
+ type: "button",
1786
+ className: bash_sample_module_css_default.inspectButton,
1787
+ onClick: inspect,
1788
+ children: [(0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconInspectOutline12, {}), t("row.inspect")]
1789
+ })]
1790
+ })]
1791
+ });
1792
+ }
1793
+ /** Registers the standalone Bash conversation-row sample. */
1794
+ const bashToolviewSample = {
1795
+ name: "bash-toolview-sample",
1796
+ inject: ["slots"],
1797
+ apply(ctx) {
1798
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1799
+ name: "tool.call.toolview",
1800
+ key: "bash",
1801
+ locale: CONVERSATION_NS
1802
+ }, BashRow));
1803
+ }
1804
+ };
1805
+ //#endregion
1806
+ //#region lib/types/client/tool/toolviews/file-mutation-row.js
1807
+ /**
1808
+ * Lets users expand an applied file diff and open the reported path.
1809
+ */
1810
+ function FileMutationRow({ toolName, block, cwd, home, openFile, inspect, t }) {
1811
+ const model = toolRowModel(toolName, block, cwd, home);
1812
+ const diff = diffCardModel(block);
1813
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1814
+ t,
1815
+ variant: model.variant,
1816
+ toolName,
1817
+ icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
1818
+ title: t(model.titleKey),
1819
+ summary: model.summary,
1820
+ body: null,
1821
+ output: model.output,
1822
+ errorSummary: model.errorSummary,
1823
+ diff,
1824
+ state: model.state,
1825
+ filePath: model.filePath,
1826
+ onOpenFile: openFile,
1827
+ inspect
1828
+ });
1829
+ }
1830
+ /** Registers the edit and write conversation rows. */
1831
+ const fileMutationToolview = {
1832
+ name: "file-mutation-toolview",
1833
+ inject: ["slots"],
1834
+ apply(ctx) {
1835
+ ctx.slots.inject("tool.call.toolview", function* () {
1836
+ yield ctx.slots.register({
1837
+ name: "tool.call.toolview",
1838
+ key: "edit",
1839
+ locale: CONVERSATION_NS
1840
+ }, FileMutationRow);
1841
+ yield ctx.slots.register({
1842
+ name: "tool.call.toolview",
1843
+ key: "write",
1844
+ locale: CONVERSATION_NS
1845
+ }, FileMutationRow);
1846
+ });
1847
+ }
1848
+ };
1849
+ //#endregion
1850
+ //#region lib/types/client/tool/toolviews/read-row.js
1851
+ /**
1852
+ * Lets users expand a completed read result and open its reported path.
1853
+ */
1854
+ function ReadRow({ toolName, block, cwd, home, openFile, inspect, t }) {
1855
+ const model = toolRowModel(toolName, block, cwd, home);
1856
+ const read = readCardModel(block, cwd, home);
1857
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1858
+ t,
1859
+ variant: model.variant,
1860
+ toolName,
1861
+ icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
1862
+ title: t(model.titleKey),
1863
+ summary: model.summary,
1864
+ body: null,
1865
+ output: model.output,
1866
+ errorSummary: model.errorSummary,
1867
+ read,
1868
+ state: model.state,
1869
+ filePath: model.filePath,
1870
+ onOpenFile: openFile,
1871
+ inspect
1872
+ });
1873
+ }
1874
+ /** Registers the read tool's conversation row. */
1875
+ const readToolview = {
1876
+ name: "read-toolview",
1877
+ inject: ["slots"],
1878
+ apply(ctx) {
1879
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1880
+ name: "tool.call.toolview",
1881
+ key: "read",
1882
+ locale: CONVERSATION_NS
1883
+ }, ReadRow));
1884
+ }
1885
+ };
1886
+ //#endregion
1887
+ //#region lib/types/client/tool/toolviews/search-row.js
1888
+ const SEARCH_TITLE_KEYS = {
1889
+ grep: "tool.title.grep",
1890
+ glob: "tool.title.glob"
1891
+ };
1892
+ /** Lets users expand grep or glob results and recover capped searches. */
1893
+ function SearchRow({ toolName, block, inspect, t }) {
1894
+ const model = toolRowModel(toolName, block);
1895
+ const search = searchCardModel(block);
1896
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1897
+ t,
1898
+ variant: model.variant,
1899
+ toolName,
1900
+ icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
1901
+ title: t(toolName === "grep" ? SEARCH_TITLE_KEYS.grep : toolName === "glob" ? SEARCH_TITLE_KEYS.glob : model.titleKey),
1902
+ summary: model.summary,
1903
+ body: null,
1904
+ output: model.output,
1905
+ errorSummary: model.errorSummary,
1906
+ search,
1907
+ state: model.state,
1908
+ inspect
1909
+ });
1910
+ }
1911
+ /** Registers the grep and glob conversation rows. */
1912
+ const searchToolview = {
1913
+ name: "search-toolview",
1914
+ inject: ["slots"],
1915
+ apply(ctx) {
1916
+ ctx.slots.inject("tool.call.toolview", function* () {
1917
+ yield ctx.slots.register({
1918
+ name: "tool.call.toolview",
1919
+ key: "grep",
1920
+ locale: CONVERSATION_NS
1921
+ }, SearchRow);
1922
+ yield ctx.slots.register({
1923
+ name: "tool.call.toolview",
1924
+ key: "glob",
1925
+ locale: CONVERSATION_NS
1926
+ }, SearchRow);
1927
+ });
1928
+ }
1929
+ };
1930
+ //#endregion
1931
+ //#region lib/types/client/tool/toolviews/plan-summary.js
1932
+ /**
1933
+ * Pure plan derivation for the todo_write row's one-line summary. Several items
1934
+ * may be `in_progress` at once — parallel work runs concurrent tasks, so a
1935
+ * summary built from one active item would silently drop the rest. The plan
1936
+ * strip header derives its own counts inline and shares nothing with this, so
1937
+ * this stays inside the toolviews domain rather than in `contract/` (the
1938
+ * inter-domain face).
1939
+ * @module
1940
+ */
1941
+ /**
1942
+ * Derive the counts and the active summary from a whole-list snapshot. It names
1943
+ * the first `in_progress` item and counts the remaining active ones, so a
1944
+ * parallel plan reports how many tasks are running rather than naming one and
1945
+ * hiding the others. `activeContent` is null when nothing is in progress, or
1946
+ * when the first active item's content is missing, mistyped, or blank once
1947
+ * trimmed — the tool's own rule for usable content, applied here because a
1948
+ * rejected call keeps its args verbatim. The row then renders the counts alone
1949
+ * rather than falling back to the generic tool summary: the counts are already
1950
+ * known to be good, and the active-item clause is the only part an unusable
1951
+ * name costs.
1952
+ * @param todos - the whole list, in model order.
1953
+ * @returns the done/total counts and the two summary halves.
1954
+ */
1955
+ function planSummary(todos) {
1956
+ const active = todos.filter((t) => t.status === "in_progress");
1957
+ const first = active[0]?.content;
1958
+ const named = typeof first === "string" && first.trim() !== "";
1959
+ return {
1960
+ done: todos.filter((t) => t.status === "completed").length,
1961
+ total: todos.length,
1962
+ activeContent: named ? first : null,
1963
+ activeExtra: named ? active.length - 1 : 0
1964
+ };
1965
+ }
1966
+ //#endregion
1967
+ //#region lib/types/client/tool/toolviews/todo-row.js
1968
+ function isItem(value) {
1969
+ return typeof value === "object" && value !== null;
1970
+ }
1971
+ function summarize(argsRaw, t) {
1972
+ let parsed;
1973
+ try {
1974
+ parsed = JSON.parse(argsRaw);
1975
+ } catch {
1976
+ return null;
1977
+ }
1978
+ if (typeof parsed !== "object" || parsed === null) return null;
1979
+ const todos = parsed.todos;
1980
+ if (!Array.isArray(todos) || !todos.every(isItem)) return null;
1981
+ const { done, total, activeContent, activeExtra } = planSummary(todos);
1982
+ const head = t("todo.completed", {
1983
+ done,
1984
+ total
1985
+ });
1986
+ return {
1987
+ text: activeContent === null ? head : `${head} · ${activeContent}`,
1988
+ extra: activeExtra
1989
+ };
1990
+ }
1991
+ /** Summarizes a plan update without presenting a cancelled call as completed. */
1992
+ function TodoRow({ toolName, block, inspect, t }) {
1993
+ const model = toolRowModel(toolName, block);
1994
+ const summary = summarize(("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "", t) ?? {
1995
+ text: model.summary,
1996
+ extra: 0
1997
+ };
1998
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
1999
+ t,
2000
+ variant: model.variant,
2001
+ toolName,
2002
+ icon: (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChecklistOutline14, {}),
2003
+ title: t("todo.rowTitle"),
2004
+ summary: summary.text,
2005
+ summarySuffix: summary.extra > 0 ? `+${summary.extra}` : null,
2006
+ body: model.body,
2007
+ output: model.output,
2008
+ errorSummary: model.errorSummary,
2009
+ state: model.state,
2010
+ inspect
2011
+ });
2012
+ }
2013
+ /** Registers the todo conversation row. */
2014
+ const todoToolview = {
2015
+ name: "todo-toolview",
2016
+ inject: ["slots"],
2017
+ apply(ctx) {
2018
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
2019
+ name: "tool.call.toolview",
2020
+ key: "todo_write",
2021
+ locale: CONVERSATION_NS
2022
+ }, TodoRow));
2023
+ }
2024
+ };
2025
+ //#endregion
2026
+ //#region lib/types/client/tool/toolviews/web-row.js
2027
+ const WEB_TITLE_KEYS = {
2028
+ web_search: "tool.title.webSearch",
2029
+ web_fetch: "tool.title.webFetch"
2030
+ };
2031
+ /** Lets users expand a completed web search or fetch result. */
2032
+ function WebRow({ toolName, block, inspect, t }) {
2033
+ const model = toolRowModel(toolName, block);
2034
+ const web = webCardModel(block);
2035
+ const icon = toolName === "web_fetch" ? (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }) : (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconGlobeOutline14, { size: 14 });
2036
+ return (0, react_jsx_runtime.jsx)(ToolRow, {
2037
+ t,
2038
+ variant: model.variant,
2039
+ toolName,
2040
+ icon,
2041
+ title: t(toolName === "web_search" ? WEB_TITLE_KEYS.web_search : toolName === "web_fetch" ? WEB_TITLE_KEYS.web_fetch : model.titleKey),
2042
+ summary: model.summary,
2043
+ body: null,
2044
+ output: model.output,
2045
+ errorSummary: model.errorSummary,
2046
+ web,
2047
+ state: model.state,
2048
+ inspect
2049
+ });
2050
+ }
2051
+ /** Registers the web search and fetch conversation rows. */
2052
+ const webToolview = {
2053
+ name: "web-toolview",
2054
+ inject: ["slots"],
2055
+ apply(ctx) {
2056
+ ctx.slots.inject("tool.call.toolview", function* () {
2057
+ yield ctx.slots.register({
2058
+ name: "tool.call.toolview",
2059
+ key: "web_search",
2060
+ locale: CONVERSATION_NS
2061
+ }, WebRow);
2062
+ yield ctx.slots.register({
2063
+ name: "tool.call.toolview",
2064
+ key: "web_fetch",
2065
+ locale: CONVERSATION_NS
2066
+ }, WebRow);
2067
+ });
2068
+ }
2069
+ };
2070
+ //#endregion
2071
+ //#region lib/types/client/apply.js
2072
+ /** Required services: the slot registry and the Host description used for POSIX `~`. */
2073
+ const inject = ["slots", "connection"];
2074
+ /**
2075
+ * Mount the whole-Tool renderers and built-in atomic Tool registrations.
2076
+ * @param ctx - Client root context.
2077
+ */
2078
+ function apply(ctx) {
2079
+ const connection = ctx.get("connection");
2080
+ const toolInject = () => ({ hooks: { connectionGeneration: connection.generation } });
2081
+ ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
2082
+ name: "conversation.chat.node",
2083
+ key: "tool-call",
2084
+ locale: CONVERSATION_NS,
2085
+ children: { "tool.call.toolview": {
2086
+ kind: "keyed",
2087
+ scope: "session"
2088
+ } },
2089
+ inject: toolInject
2090
+ }, ToolCallTree));
2091
+ ctx.slots.inject("conversation.details.tool", () => ctx.slots.register({
2092
+ name: "conversation.details.tool",
2093
+ locale: CONVERSATION_NS,
2094
+ inject: toolInject
2095
+ }, ToolDetails));
2096
+ ctx.plugin(bashToolviewSample);
2097
+ ctx.plugin(readToolview);
2098
+ ctx.plugin(fileMutationToolview);
2099
+ ctx.plugin(searchToolview);
2100
+ ctx.plugin(webToolview);
2101
+ ctx.plugin(todoToolview);
2102
+ ctx.plugin(askQuestionToolview);
2103
+ }
2104
+ //#endregion
2105
+ exports.apply = apply;
2106
+ exports.inject = inject;
2107
+ return module.exports;
2108
+ }
2109
+ });
2110
+
2111
+ //# sourceMappingURL=client.js.map