@stigmer/react 0.0.52 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deployment-mode.d.ts +35 -0
- package/deployment-mode.d.ts.map +1 -0
- package/deployment-mode.js +41 -0
- package/deployment-mode.js.map +1 -0
- package/execution/ApprovalCard.d.ts +8 -6
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +34 -96
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/McpToolDetail.d.ts +48 -0
- package/execution/McpToolDetail.d.ts.map +1 -0
- package/execution/McpToolDetail.js +159 -0
- package/execution/McpToolDetail.js.map +1 -0
- package/execution/ToolArgsView.d.ts +41 -0
- package/execution/ToolArgsView.d.ts.map +1 -0
- package/execution/ToolArgsView.js +132 -0
- package/execution/ToolArgsView.js.map +1 -0
- package/execution/ToolCallDetail.d.ts +11 -4
- package/execution/ToolCallDetail.d.ts.map +1 -1
- package/execution/ToolCallDetail.js +30 -101
- package/execution/ToolCallDetail.js.map +1 -1
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +3 -2
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/ToolCallItem.d.ts +2 -0
- package/execution/ToolCallItem.d.ts.map +1 -1
- package/execution/ToolCallItem.js +6 -2
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/index.d.ts +7 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +4 -1
- package/execution/index.js.map +1 -1
- package/execution/tool-categories.d.ts +35 -8
- package/execution/tool-categories.d.ts.map +1 -1
- package/execution/tool-categories.js +76 -10
- package/execution/tool-categories.js.map +1 -1
- package/execution/tool-rendering-primitives.d.ts +61 -0
- package/execution/tool-rendering-primitives.d.ts.map +1 -0
- package/execution/tool-rendering-primitives.js +106 -0
- package/execution/tool-rendering-primitives.js.map +1 -0
- package/index.d.ts +5 -2
- package/index.d.ts.map +1 -1
- package/index.js +5 -1
- package/index.js.map +1 -1
- package/internal/CloudFeatureNotice.d.ts +19 -0
- package/internal/CloudFeatureNotice.d.ts.map +1 -0
- package/internal/CloudFeatureNotice.js +21 -0
- package/internal/CloudFeatureNotice.js.map +1 -0
- package/mcp-server/McpServerDetailView.d.ts +15 -1
- package/mcp-server/McpServerDetailView.d.ts.map +1 -1
- package/mcp-server/McpServerDetailView.js +11 -3
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/package.json +4 -4
- package/provider.d.ts +14 -2
- package/provider.d.ts.map +1 -1
- package/provider.js +3 -2
- package/provider.js.map +1 -1
- package/src/deployment-mode.ts +46 -0
- package/src/execution/ApprovalCard.tsx +130 -283
- package/src/execution/McpToolDetail.tsx +283 -0
- package/src/execution/ToolArgsView.tsx +277 -0
- package/src/execution/ToolCallDetail.tsx +51 -219
- package/src/execution/ToolCallGroup.tsx +3 -2
- package/src/execution/ToolCallItem.tsx +14 -2
- package/src/execution/index.ts +25 -0
- package/src/execution/tool-categories.ts +89 -9
- package/src/execution/tool-rendering-primitives.tsx +253 -0
- package/src/index.ts +13 -0
- package/src/internal/CloudFeatureNotice.tsx +60 -0
- package/src/mcp-server/McpServerDetailView.tsx +24 -2
- package/src/provider.tsx +18 -2
- package/styles.css +1 -1
|
@@ -1,28 +1,38 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
4
3
|
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
5
4
|
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
6
5
|
import { cn } from "@stigmer/theme";
|
|
7
|
-
import { resolveToolCategory
|
|
8
|
-
import {
|
|
6
|
+
import { resolveToolCategory } from "./tool-categories";
|
|
7
|
+
import { McpToolDetail } from "./McpToolDetail";
|
|
8
|
+
import { ToolArgsView } from "./ToolArgsView";
|
|
9
|
+
import {
|
|
10
|
+
CollapsibleCode,
|
|
11
|
+
CollapsiblePre,
|
|
12
|
+
formatResult,
|
|
13
|
+
} from "./tool-rendering-primitives";
|
|
9
14
|
|
|
10
15
|
export interface ToolCallDetailProps {
|
|
11
16
|
readonly toolCall: ToolCall;
|
|
12
17
|
readonly className?: string;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
|
-
const TRUNCATION_LINE_LIMIT = 10;
|
|
16
|
-
|
|
17
20
|
/**
|
|
18
21
|
* Renders the detail panel for a single tool call with
|
|
19
22
|
* category-specific visual treatments.
|
|
20
23
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
+
* Arguments are rendered through the shared {@link ToolArgsView}
|
|
25
|
+
* dispatch (same component used by {@link ApprovalCard}), ensuring
|
|
26
|
+
* visual parity between pre-execution approval previews and
|
|
27
|
+
* post-execution detail views. Result/output sections are layered
|
|
28
|
+
* on top by this component.
|
|
29
|
+
*
|
|
30
|
+
* - **Shell tools**: terminal-style command + output
|
|
31
|
+
* - **File tools (read/write/edit/delete)**: file path + content + result
|
|
32
|
+
* - **Search tools (grep/glob)**: pattern + results
|
|
24
33
|
* - **Think**: muted italic thought block
|
|
25
|
-
* - **
|
|
34
|
+
* - **MCP tools**: structured args + parsed result via {@link McpToolDetail}
|
|
35
|
+
* - **Unknown tools**: generic args + result JSON rendering
|
|
26
36
|
*
|
|
27
37
|
* Used inside {@link ToolCallItem} when expanded, but also
|
|
28
38
|
* independently importable by platform builders who compose
|
|
@@ -34,7 +44,7 @@ const TRUNCATION_LINE_LIMIT = 10;
|
|
|
34
44
|
* ```
|
|
35
45
|
*/
|
|
36
46
|
export function ToolCallDetail({ toolCall, className }: ToolCallDetailProps) {
|
|
37
|
-
const category = resolveToolCategory(toolCall.name);
|
|
47
|
+
const category = resolveToolCategory(toolCall.name, toolCall.mcpServerSlug);
|
|
38
48
|
const isFailed = toolCall.status === ToolCallStatus.TOOL_CALL_FAILED;
|
|
39
49
|
|
|
40
50
|
return (
|
|
@@ -55,6 +65,12 @@ export function ToolCallDetail({ toolCall, className }: ToolCallDetailProps) {
|
|
|
55
65
|
|
|
56
66
|
// ---------------------------------------------------------------------------
|
|
57
67
|
// Category-specific renderers
|
|
68
|
+
//
|
|
69
|
+
// Each renderer composes:
|
|
70
|
+
// MetadataRow (duration, slug) + ToolArgsView (shared args) + result section
|
|
71
|
+
//
|
|
72
|
+
// Think and MCP have fully custom rendering that doesn't fit the
|
|
73
|
+
// MetadataRow + ToolArgsView + Result pattern.
|
|
58
74
|
// ---------------------------------------------------------------------------
|
|
59
75
|
|
|
60
76
|
function CategoryRenderer({
|
|
@@ -80,38 +96,26 @@ function CategoryRenderer({
|
|
|
80
96
|
return <SearchToolDetail toolCall={toolCall} />;
|
|
81
97
|
case "think":
|
|
82
98
|
return <ThinkToolDetail toolCall={toolCall} />;
|
|
99
|
+
case "mcp":
|
|
100
|
+
return <McpToolDetail toolCall={toolCall} />;
|
|
83
101
|
default:
|
|
84
102
|
return <GenericToolDetail toolCall={toolCall} />;
|
|
85
103
|
}
|
|
86
104
|
}
|
|
87
105
|
|
|
88
|
-
/**
|
|
89
|
-
* Terminal-style rendering for shell/execute tools.
|
|
90
|
-
* Shows the command in a dark terminal block and output below.
|
|
91
|
-
*/
|
|
92
106
|
function ShellToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
93
|
-
const command = extractPrimaryArg(toolCall);
|
|
94
107
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
95
108
|
|
|
96
109
|
return (
|
|
97
110
|
<>
|
|
98
|
-
{/* Metadata */}
|
|
99
111
|
<MetadataRow toolCall={toolCall} duration={duration} />
|
|
100
112
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<pre className="whitespace-pre-wrap break-words font-mono text-[var(--stgm-terminal-fg,#e0e0e0)]">
|
|
107
|
-
<span className="select-none text-[var(--stgm-terminal-prompt,#6b7280)]">$ </span>
|
|
108
|
-
{command}
|
|
109
|
-
</pre>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
)}
|
|
113
|
+
<ToolArgsView
|
|
114
|
+
toolName={toolCall.name}
|
|
115
|
+
args={toolCall.args as Record<string, unknown> | null}
|
|
116
|
+
mcpServerSlug={toolCall.mcpServerSlug}
|
|
117
|
+
/>
|
|
113
118
|
|
|
114
|
-
{/* Output */}
|
|
115
119
|
{toolCall.result && (
|
|
116
120
|
<div className="space-y-1">
|
|
117
121
|
<span className="font-medium text-muted-foreground">Output</span>
|
|
@@ -128,17 +132,8 @@ function ShellToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* For **read** mode: shows only the metadata row and a clickable
|
|
134
|
-
* path. Content is intentionally omitted — the Read tool's purpose
|
|
135
|
-
* is for the *agent* to consume the file, and the content is either
|
|
136
|
-
* truncated, omitted, or simply noise for the user. The clickable
|
|
137
|
-
* path provides direct access to the source file.
|
|
138
|
-
*
|
|
139
|
-
* For **write/edit/delete** modes: shows the clickable path followed
|
|
140
|
-
* by the content block (what was written/edited) and any result
|
|
141
|
-
* confirmation.
|
|
135
|
+
* For **read**: metadata + path only (content is noise for the user).
|
|
136
|
+
* For **write/edit/delete**: metadata + path + content (via ToolArgsView) + result.
|
|
142
137
|
*/
|
|
143
138
|
function FileToolDetail({
|
|
144
139
|
toolCall,
|
|
@@ -147,49 +142,19 @@ function FileToolDetail({
|
|
|
147
142
|
toolCall: ToolCall;
|
|
148
143
|
mode: "read" | "write" | "edit" | "delete";
|
|
149
144
|
}) {
|
|
150
|
-
const filePath = extractPrimaryArg(toolCall);
|
|
151
145
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
152
146
|
|
|
153
|
-
if (mode === "read") {
|
|
154
|
-
return (
|
|
155
|
-
<>
|
|
156
|
-
<MetadataRow toolCall={toolCall} duration={duration} />
|
|
157
|
-
{filePath && (
|
|
158
|
-
<div className="flex items-center gap-1.5">
|
|
159
|
-
<FilePathIcon />
|
|
160
|
-
<FilePathLink path={filePath} className="text-xs" />
|
|
161
|
-
</div>
|
|
162
|
-
)}
|
|
163
|
-
</>
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const contentFromArgs =
|
|
168
|
-
mode === "write" || mode === "edit"
|
|
169
|
-
? extractWriteContent(toolCall)
|
|
170
|
-
: null;
|
|
171
|
-
|
|
172
|
-
const displayContent = contentFromArgs || toolCall.result;
|
|
173
|
-
|
|
174
147
|
return (
|
|
175
148
|
<>
|
|
176
149
|
<MetadataRow toolCall={toolCall} duration={duration} />
|
|
177
150
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)}
|
|
184
|
-
|
|
185
|
-
{displayContent && (
|
|
186
|
-
<CollapsibleCode
|
|
187
|
-
label={mode === "delete" ? "Result" : "Content"}
|
|
188
|
-
content={formatResult(displayContent)}
|
|
189
|
-
/>
|
|
190
|
-
)}
|
|
151
|
+
<ToolArgsView
|
|
152
|
+
toolName={toolCall.name}
|
|
153
|
+
args={toolCall.args as Record<string, unknown> | null}
|
|
154
|
+
mcpServerSlug={toolCall.mcpServerSlug}
|
|
155
|
+
/>
|
|
191
156
|
|
|
192
|
-
{
|
|
157
|
+
{mode !== "read" && toolCall.result && (
|
|
193
158
|
<CollapsibleCode
|
|
194
159
|
label="Result"
|
|
195
160
|
content={formatResult(toolCall.result)}
|
|
@@ -199,24 +164,18 @@ function FileToolDetail({
|
|
|
199
164
|
);
|
|
200
165
|
}
|
|
201
166
|
|
|
202
|
-
/**
|
|
203
|
-
* Search/discovery rendering for grep, glob, list tools.
|
|
204
|
-
* Shows search pattern/path and results.
|
|
205
|
-
*/
|
|
206
167
|
function SearchToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
207
|
-
const pattern = extractPrimaryArg(toolCall);
|
|
208
168
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
209
169
|
|
|
210
170
|
return (
|
|
211
171
|
<>
|
|
212
172
|
<MetadataRow toolCall={toolCall} duration={duration} />
|
|
213
173
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)}
|
|
174
|
+
<ToolArgsView
|
|
175
|
+
toolName={toolCall.name}
|
|
176
|
+
args={toolCall.args as Record<string, unknown> | null}
|
|
177
|
+
mcpServerSlug={toolCall.mcpServerSlug}
|
|
178
|
+
/>
|
|
220
179
|
|
|
221
180
|
{toolCall.result && (
|
|
222
181
|
<CollapsibleCode
|
|
@@ -228,10 +187,6 @@ function SearchToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
|
228
187
|
);
|
|
229
188
|
}
|
|
230
189
|
|
|
231
|
-
/**
|
|
232
|
-
* Thought rendering. Muted, italic presentation distinct from
|
|
233
|
-
* regular tool output.
|
|
234
|
-
*/
|
|
235
190
|
function ThinkToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
236
191
|
const thought =
|
|
237
192
|
(toolCall.args?.["thought"] as string | undefined) || toolCall.result;
|
|
@@ -248,10 +203,6 @@ function ThinkToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
|
248
203
|
);
|
|
249
204
|
}
|
|
250
205
|
|
|
251
|
-
/**
|
|
252
|
-
* Fallback rendering for unknown/MCP tools. Preserves the original
|
|
253
|
-
* generic args + result JSON display.
|
|
254
|
-
*/
|
|
255
206
|
function GenericToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
256
207
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
257
208
|
|
|
@@ -259,12 +210,11 @@ function GenericToolDetail({ toolCall }: { toolCall: ToolCall }) {
|
|
|
259
210
|
<>
|
|
260
211
|
<MetadataRow toolCall={toolCall} duration={duration} />
|
|
261
212
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
)}
|
|
213
|
+
<ToolArgsView
|
|
214
|
+
toolName={toolCall.name}
|
|
215
|
+
args={toolCall.args as Record<string, unknown> | null}
|
|
216
|
+
mcpServerSlug={toolCall.mcpServerSlug}
|
|
217
|
+
/>
|
|
268
218
|
|
|
269
219
|
{toolCall.result && (
|
|
270
220
|
<CollapsibleCode
|
|
@@ -302,128 +252,10 @@ function MetadataRow({
|
|
|
302
252
|
);
|
|
303
253
|
}
|
|
304
254
|
|
|
305
|
-
function CollapsibleCode({
|
|
306
|
-
label,
|
|
307
|
-
content,
|
|
308
|
-
}: {
|
|
309
|
-
label: string;
|
|
310
|
-
content: string;
|
|
311
|
-
}) {
|
|
312
|
-
const lines = content.split("\n");
|
|
313
|
-
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
314
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
315
|
-
|
|
316
|
-
const displayContent =
|
|
317
|
-
needsTruncation && !isExpanded
|
|
318
|
-
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
319
|
-
: content;
|
|
320
|
-
|
|
321
|
-
return (
|
|
322
|
-
<div className="space-y-1">
|
|
323
|
-
<span className="font-medium text-muted-foreground">{label}</span>
|
|
324
|
-
<pre className="max-h-80 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 p-2 font-mono text-foreground">
|
|
325
|
-
{displayContent}
|
|
326
|
-
</pre>
|
|
327
|
-
{needsTruncation && (
|
|
328
|
-
<button
|
|
329
|
-
type="button"
|
|
330
|
-
onClick={() => setIsExpanded((v) => !v)}
|
|
331
|
-
className="text-primary hover:text-primary/80 text-xs font-medium transition-colors"
|
|
332
|
-
>
|
|
333
|
-
{isExpanded
|
|
334
|
-
? "Show less"
|
|
335
|
-
: `Show all ${lines.length} lines`}
|
|
336
|
-
</button>
|
|
337
|
-
)}
|
|
338
|
-
</div>
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function CollapsiblePre({
|
|
343
|
-
content,
|
|
344
|
-
className,
|
|
345
|
-
}: {
|
|
346
|
-
content: string;
|
|
347
|
-
className?: string;
|
|
348
|
-
}) {
|
|
349
|
-
const lines = content.split("\n");
|
|
350
|
-
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
351
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
352
|
-
|
|
353
|
-
const displayContent =
|
|
354
|
-
needsTruncation && !isExpanded
|
|
355
|
-
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
356
|
-
: content;
|
|
357
|
-
|
|
358
|
-
return (
|
|
359
|
-
<>
|
|
360
|
-
<pre className={cn("whitespace-pre-wrap break-words font-mono", className)}>
|
|
361
|
-
{displayContent}
|
|
362
|
-
</pre>
|
|
363
|
-
{needsTruncation && (
|
|
364
|
-
<button
|
|
365
|
-
type="button"
|
|
366
|
-
onClick={() => setIsExpanded((v) => !v)}
|
|
367
|
-
className="mt-1 text-primary hover:text-primary/80 text-xs font-medium transition-colors"
|
|
368
|
-
>
|
|
369
|
-
{isExpanded ? "Show less" : `Show all ${lines.length} lines`}
|
|
370
|
-
</button>
|
|
371
|
-
)}
|
|
372
|
-
</>
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
function FilePathIcon() {
|
|
377
|
-
return (
|
|
378
|
-
<svg
|
|
379
|
-
width="10"
|
|
380
|
-
height="10"
|
|
381
|
-
viewBox="0 0 12 12"
|
|
382
|
-
fill="none"
|
|
383
|
-
stroke="currentColor"
|
|
384
|
-
strokeWidth="1.2"
|
|
385
|
-
strokeLinecap="round"
|
|
386
|
-
strokeLinejoin="round"
|
|
387
|
-
className="shrink-0 text-muted-foreground"
|
|
388
|
-
aria-hidden="true"
|
|
389
|
-
>
|
|
390
|
-
<path d="M7 1H3C2.45 1 2 1.45 2 2V10C2 10.55 2.45 11 3 11H9C9.55 11 10 10.55 10 10V4L7 1Z" />
|
|
391
|
-
<path d="M7 1V4H10" />
|
|
392
|
-
</svg>
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
255
|
// ---------------------------------------------------------------------------
|
|
397
256
|
// Utilities
|
|
398
257
|
// ---------------------------------------------------------------------------
|
|
399
258
|
|
|
400
|
-
function extractWriteContent(toolCall: ToolCall): string | null {
|
|
401
|
-
if (!toolCall.args) return null;
|
|
402
|
-
const fields = ["contents", "content", "file_content", "new_text", "new_string", "replacement"];
|
|
403
|
-
for (const field of fields) {
|
|
404
|
-
const val = toolCall.args[field];
|
|
405
|
-
if (typeof val === "string" && val.length > 0) return val;
|
|
406
|
-
}
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function formatJson(obj: object): string {
|
|
411
|
-
try {
|
|
412
|
-
return JSON.stringify(obj, null, 2);
|
|
413
|
-
} catch {
|
|
414
|
-
return String(obj);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function formatResult(result: string): string {
|
|
419
|
-
try {
|
|
420
|
-
const parsed = JSON.parse(result);
|
|
421
|
-
return JSON.stringify(parsed, null, 2);
|
|
422
|
-
} catch {
|
|
423
|
-
return result;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
259
|
/**
|
|
428
260
|
* Returns a human-readable duration string from two ISO 8601
|
|
429
261
|
* timestamps. Returns `null` when either timestamp is empty or
|
|
@@ -86,8 +86,9 @@ function defaultFormatSummary(
|
|
|
86
86
|
status: AggregateStatus,
|
|
87
87
|
): string {
|
|
88
88
|
if (toolCalls.length === 1) {
|
|
89
|
-
const
|
|
90
|
-
const
|
|
89
|
+
const tc = toolCalls[0];
|
|
90
|
+
const cat = resolveToolCategory(tc.name, tc.mcpServerSlug);
|
|
91
|
+
const primary = extractPrimaryArg(tc);
|
|
91
92
|
if (primary) {
|
|
92
93
|
const truncated =
|
|
93
94
|
primary.length > 60 ? primary.slice(0, 57) + "\u2026" : primary;
|
|
@@ -66,7 +66,7 @@ export function ToolCallItem({
|
|
|
66
66
|
const duration = formatDuration(toolCall.startedAt, toolCall.completedAt);
|
|
67
67
|
const isSubAgent = subAgentExecution != null;
|
|
68
68
|
|
|
69
|
-
const categoryInfo = resolveToolCategory(toolCall.name);
|
|
69
|
+
const categoryInfo = resolveToolCategory(toolCall.name, toolCall.mcpServerSlug);
|
|
70
70
|
const CategoryIcon = CATEGORY_ICON[categoryInfo.category];
|
|
71
71
|
const primaryArg = extractPrimaryArg(toolCall);
|
|
72
72
|
|
|
@@ -255,7 +255,7 @@ const STATUS_COLOR: Record<ItemStatus, string> = {
|
|
|
255
255
|
// Category-specific icons (inline SVG, SDK pattern)
|
|
256
256
|
// ---------------------------------------------------------------------------
|
|
257
257
|
|
|
258
|
-
const CATEGORY_ICON: Record<ToolCategory, () => React.JSX.Element> = {
|
|
258
|
+
export const CATEGORY_ICON: Record<ToolCategory, () => React.JSX.Element> = {
|
|
259
259
|
shell: TerminalIcon,
|
|
260
260
|
read: FileIcon,
|
|
261
261
|
write: FilePenIcon,
|
|
@@ -265,6 +265,7 @@ const CATEGORY_ICON: Record<ToolCategory, () => React.JSX.Element> = {
|
|
|
265
265
|
list: FolderIcon,
|
|
266
266
|
think: BrainIcon,
|
|
267
267
|
"sub-agent": BotIcon,
|
|
268
|
+
mcp: McpPlugIcon,
|
|
268
269
|
unknown: WrenchIcon,
|
|
269
270
|
};
|
|
270
271
|
|
|
@@ -355,6 +356,17 @@ function WrenchIcon() {
|
|
|
355
356
|
);
|
|
356
357
|
}
|
|
357
358
|
|
|
359
|
+
function McpPlugIcon() {
|
|
360
|
+
return (
|
|
361
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round">
|
|
362
|
+
<path d="M4 1.5V4" />
|
|
363
|
+
<path d="M8 1.5V4" />
|
|
364
|
+
<path d="M2.5 4H9.5V6.5C9.5 8.43 7.93 10 6 10C4.07 10 2.5 8.43 2.5 6.5V4Z" />
|
|
365
|
+
<path d="M6 10V11" />
|
|
366
|
+
</svg>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
358
370
|
// ---------------------------------------------------------------------------
|
|
359
371
|
// Status icons
|
|
360
372
|
// ---------------------------------------------------------------------------
|
package/src/execution/index.ts
CHANGED
|
@@ -41,6 +41,30 @@ export type { ToolCallGroupProps } from "./ToolCallGroup";
|
|
|
41
41
|
export { ToolCallDetail, formatDuration } from "./ToolCallDetail";
|
|
42
42
|
export type { ToolCallDetailProps } from "./ToolCallDetail";
|
|
43
43
|
|
|
44
|
+
export { McpToolDetail, McpArgsView, McpMetadataRow, parseMcpResult } from "./McpToolDetail";
|
|
45
|
+
export type { McpToolDetailProps } from "./McpToolDetail";
|
|
46
|
+
|
|
47
|
+
export { ToolArgsView } from "./ToolArgsView";
|
|
48
|
+
export type { ToolArgsViewProps } from "./ToolArgsView";
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
CollapsibleCode,
|
|
52
|
+
CollapsiblePre,
|
|
53
|
+
CollapsibleJsonBlock,
|
|
54
|
+
FilePathIcon,
|
|
55
|
+
McpServerIcon,
|
|
56
|
+
TRUNCATION_LINE_LIMIT,
|
|
57
|
+
formatJson,
|
|
58
|
+
formatResult,
|
|
59
|
+
isScalar,
|
|
60
|
+
humanizeArgKey,
|
|
61
|
+
} from "./tool-rendering-primitives";
|
|
62
|
+
export type {
|
|
63
|
+
CollapsibleCodeProps,
|
|
64
|
+
CollapsiblePreProps,
|
|
65
|
+
CollapsibleJsonBlockProps,
|
|
66
|
+
} from "./tool-rendering-primitives";
|
|
67
|
+
|
|
44
68
|
export { ToolCallItem } from "./ToolCallItem";
|
|
45
69
|
export type { ToolCallItemProps } from "./ToolCallItem";
|
|
46
70
|
|
|
@@ -84,6 +108,7 @@ export {
|
|
|
84
108
|
resolveToolCategory,
|
|
85
109
|
extractPrimaryArg,
|
|
86
110
|
extractPrimaryArgFromPreview,
|
|
111
|
+
humanizeToolName,
|
|
87
112
|
} from "./tool-categories";
|
|
88
113
|
export type { ToolCategory, ToolCategoryInfo } from "./tool-categories";
|
|
89
114
|
|
|
@@ -6,6 +6,10 @@ import type { JsonObject } from "@bufbuild/protobuf";
|
|
|
6
6
|
*
|
|
7
7
|
* Mirrors the CLI's `toolDisplayMap` in
|
|
8
8
|
* `client-apps/cli/pkg/toolrender/render.go`.
|
|
9
|
+
*
|
|
10
|
+
* `"mcp"` covers tools originating from an MCP server whose
|
|
11
|
+
* names are dynamic and cannot be statically listed in
|
|
12
|
+
* {@link TOOL_DISPLAY_MAP}.
|
|
9
13
|
*/
|
|
10
14
|
export type ToolCategory =
|
|
11
15
|
| "shell"
|
|
@@ -17,6 +21,7 @@ export type ToolCategory =
|
|
|
17
21
|
| "list"
|
|
18
22
|
| "think"
|
|
19
23
|
| "sub-agent"
|
|
24
|
+
| "mcp"
|
|
20
25
|
| "unknown";
|
|
21
26
|
|
|
22
27
|
export interface ToolCategoryInfo {
|
|
@@ -68,10 +73,19 @@ const TOOL_DISPLAY_MAP: ReadonlyMap<string, ToolDisplayEntry> = new Map([
|
|
|
68
73
|
|
|
69
74
|
/**
|
|
70
75
|
* Resolves a tool name to its category metadata for type-aware
|
|
71
|
-
* rendering.
|
|
72
|
-
*
|
|
76
|
+
* rendering.
|
|
77
|
+
*
|
|
78
|
+
* When `mcpServerSlug` is provided and the tool name is not a
|
|
79
|
+
* known built-in, the tool is categorised as `"mcp"` with a
|
|
80
|
+
* human-readable label derived from the raw tool name.
|
|
81
|
+
*
|
|
82
|
+
* Falls back to `"unknown"` only when the tool is neither
|
|
83
|
+
* built-in nor MCP-originated.
|
|
73
84
|
*/
|
|
74
|
-
export function resolveToolCategory(
|
|
85
|
+
export function resolveToolCategory(
|
|
86
|
+
toolName: string,
|
|
87
|
+
mcpServerSlug?: string,
|
|
88
|
+
): ToolCategoryInfo {
|
|
75
89
|
const entry = TOOL_DISPLAY_MAP.get(toolName);
|
|
76
90
|
if (entry) {
|
|
77
91
|
return {
|
|
@@ -81,6 +95,16 @@ export function resolveToolCategory(toolName: string): ToolCategoryInfo {
|
|
|
81
95
|
fallbackArgFields: entry.fallbackFields ?? [],
|
|
82
96
|
};
|
|
83
97
|
}
|
|
98
|
+
|
|
99
|
+
if (mcpServerSlug) {
|
|
100
|
+
return {
|
|
101
|
+
category: "mcp",
|
|
102
|
+
label: humanizeToolName(toolName),
|
|
103
|
+
primaryArgField: "slug",
|
|
104
|
+
fallbackArgFields: ["name", "org"],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
84
108
|
return {
|
|
85
109
|
category: "unknown",
|
|
86
110
|
label: toolName,
|
|
@@ -89,6 +113,26 @@ export function resolveToolCategory(toolName: string): ToolCategoryInfo {
|
|
|
89
113
|
};
|
|
90
114
|
}
|
|
91
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Converts a snake_case or camelCase tool name into a
|
|
118
|
+
* human-readable title.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* humanizeToolName("apply_mcp_server") // "Apply MCP Server"
|
|
122
|
+
* humanizeToolName("deleteAgent") // "Delete Agent"
|
|
123
|
+
*/
|
|
124
|
+
export function humanizeToolName(name: string): string {
|
|
125
|
+
return name
|
|
126
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
127
|
+
.replace(/[_-]+/g, " ")
|
|
128
|
+
.replace(/\b[a-z]/g, (c) => c.toUpperCase())
|
|
129
|
+
.replace(/\bMcp\b/gi, "MCP")
|
|
130
|
+
.replace(/\bApi\b/gi, "API")
|
|
131
|
+
.replace(/\bId\b/gi, "ID")
|
|
132
|
+
.replace(/\bUrl\b/gi, "URL")
|
|
133
|
+
.replace(/\bIam\b/gi, "IAM");
|
|
134
|
+
}
|
|
135
|
+
|
|
92
136
|
function extractArgValue(
|
|
93
137
|
args: JsonObject | undefined,
|
|
94
138
|
primary: string,
|
|
@@ -118,13 +162,12 @@ function extractArgValue(
|
|
|
118
162
|
/**
|
|
119
163
|
* Extracts the most relevant argument value from a tool call
|
|
120
164
|
* based on its category (command for shell tools, path for file
|
|
121
|
-
* tools,
|
|
165
|
+
* tools, slug for MCP tools, etc.).
|
|
122
166
|
*
|
|
123
|
-
* Returns `null` when the tool
|
|
124
|
-
* or when the expected argument fields are missing.
|
|
167
|
+
* Returns `null` when the tool has no recognised arguments.
|
|
125
168
|
*/
|
|
126
169
|
export function extractPrimaryArg(toolCall: ToolCall): string | null {
|
|
127
|
-
const info = resolveToolCategory(toolCall.name);
|
|
170
|
+
const info = resolveToolCategory(toolCall.name, toolCall.mcpServerSlug);
|
|
128
171
|
const result = extractArgValue(
|
|
129
172
|
toolCall.args,
|
|
130
173
|
info.primaryArgField,
|
|
@@ -133,7 +176,7 @@ export function extractPrimaryArg(toolCall: ToolCall): string | null {
|
|
|
133
176
|
|
|
134
177
|
if (result) return result;
|
|
135
178
|
|
|
136
|
-
if (info.category === "unknown" && toolCall.args) {
|
|
179
|
+
if ((info.category === "unknown" || info.category === "mcp") && toolCall.args) {
|
|
137
180
|
const keys = Object.keys(toolCall.args);
|
|
138
181
|
if (keys.length > 0) {
|
|
139
182
|
const val = toolCall.args[keys[0]];
|
|
@@ -152,6 +195,7 @@ export function extractPrimaryArg(toolCall: ToolCall): string | null {
|
|
|
152
195
|
export function extractPrimaryArgFromPreview(
|
|
153
196
|
toolName: string,
|
|
154
197
|
argsPreview: string,
|
|
198
|
+
mcpServerSlug?: string,
|
|
155
199
|
): string | null {
|
|
156
200
|
if (!argsPreview) return null;
|
|
157
201
|
|
|
@@ -159,7 +203,7 @@ export function extractPrimaryArgFromPreview(
|
|
|
159
203
|
const parsed = JSON.parse(argsPreview);
|
|
160
204
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
161
205
|
|
|
162
|
-
const info = resolveToolCategory(toolName);
|
|
206
|
+
const info = resolveToolCategory(toolName, mcpServerSlug);
|
|
163
207
|
return extractArgValue(
|
|
164
208
|
parsed as JsonObject,
|
|
165
209
|
info.primaryArgField,
|
|
@@ -169,3 +213,39 @@ export function extractPrimaryArgFromPreview(
|
|
|
169
213
|
return null;
|
|
170
214
|
}
|
|
171
215
|
}
|
|
216
|
+
|
|
217
|
+
const WRITE_CONTENT_FIELDS = [
|
|
218
|
+
"contents",
|
|
219
|
+
"content",
|
|
220
|
+
"file_content",
|
|
221
|
+
"new_text",
|
|
222
|
+
"new_string",
|
|
223
|
+
"replacement",
|
|
224
|
+
] as const;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Extracts the file content body from a JSON `argsPreview` string
|
|
228
|
+
* for write/edit tool categories. Scans the same field names used
|
|
229
|
+
* by the post-execution {@link ToolCallDetail} renderer so that
|
|
230
|
+
* the approval preview matches the completed tool call display.
|
|
231
|
+
*
|
|
232
|
+
* Returns `null` if parsing fails or no content field is found.
|
|
233
|
+
*/
|
|
234
|
+
export function extractWriteContentFromPreview(
|
|
235
|
+
argsPreview: string,
|
|
236
|
+
): string | null {
|
|
237
|
+
if (!argsPreview) return null;
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const parsed = JSON.parse(argsPreview);
|
|
241
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
242
|
+
|
|
243
|
+
for (const field of WRITE_CONTENT_FIELDS) {
|
|
244
|
+
const val = (parsed as Record<string, unknown>)[field];
|
|
245
|
+
if (typeof val === "string" && val.length > 0) return val;
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|