@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.
Files changed (71) hide show
  1. package/deployment-mode.d.ts +35 -0
  2. package/deployment-mode.d.ts.map +1 -0
  3. package/deployment-mode.js +41 -0
  4. package/deployment-mode.js.map +1 -0
  5. package/execution/ApprovalCard.d.ts +8 -6
  6. package/execution/ApprovalCard.d.ts.map +1 -1
  7. package/execution/ApprovalCard.js +34 -96
  8. package/execution/ApprovalCard.js.map +1 -1
  9. package/execution/McpToolDetail.d.ts +48 -0
  10. package/execution/McpToolDetail.d.ts.map +1 -0
  11. package/execution/McpToolDetail.js +159 -0
  12. package/execution/McpToolDetail.js.map +1 -0
  13. package/execution/ToolArgsView.d.ts +41 -0
  14. package/execution/ToolArgsView.d.ts.map +1 -0
  15. package/execution/ToolArgsView.js +132 -0
  16. package/execution/ToolArgsView.js.map +1 -0
  17. package/execution/ToolCallDetail.d.ts +11 -4
  18. package/execution/ToolCallDetail.d.ts.map +1 -1
  19. package/execution/ToolCallDetail.js +30 -101
  20. package/execution/ToolCallDetail.js.map +1 -1
  21. package/execution/ToolCallGroup.d.ts.map +1 -1
  22. package/execution/ToolCallGroup.js +3 -2
  23. package/execution/ToolCallGroup.js.map +1 -1
  24. package/execution/ToolCallItem.d.ts +2 -0
  25. package/execution/ToolCallItem.d.ts.map +1 -1
  26. package/execution/ToolCallItem.js +6 -2
  27. package/execution/ToolCallItem.js.map +1 -1
  28. package/execution/index.d.ts +7 -1
  29. package/execution/index.d.ts.map +1 -1
  30. package/execution/index.js +4 -1
  31. package/execution/index.js.map +1 -1
  32. package/execution/tool-categories.d.ts +35 -8
  33. package/execution/tool-categories.d.ts.map +1 -1
  34. package/execution/tool-categories.js +76 -10
  35. package/execution/tool-categories.js.map +1 -1
  36. package/execution/tool-rendering-primitives.d.ts +61 -0
  37. package/execution/tool-rendering-primitives.d.ts.map +1 -0
  38. package/execution/tool-rendering-primitives.js +106 -0
  39. package/execution/tool-rendering-primitives.js.map +1 -0
  40. package/index.d.ts +5 -2
  41. package/index.d.ts.map +1 -1
  42. package/index.js +5 -1
  43. package/index.js.map +1 -1
  44. package/internal/CloudFeatureNotice.d.ts +19 -0
  45. package/internal/CloudFeatureNotice.d.ts.map +1 -0
  46. package/internal/CloudFeatureNotice.js +21 -0
  47. package/internal/CloudFeatureNotice.js.map +1 -0
  48. package/mcp-server/McpServerDetailView.d.ts +15 -1
  49. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  50. package/mcp-server/McpServerDetailView.js +11 -3
  51. package/mcp-server/McpServerDetailView.js.map +1 -1
  52. package/package.json +4 -4
  53. package/provider.d.ts +14 -2
  54. package/provider.d.ts.map +1 -1
  55. package/provider.js +3 -2
  56. package/provider.js.map +1 -1
  57. package/src/deployment-mode.ts +46 -0
  58. package/src/execution/ApprovalCard.tsx +130 -283
  59. package/src/execution/McpToolDetail.tsx +283 -0
  60. package/src/execution/ToolArgsView.tsx +277 -0
  61. package/src/execution/ToolCallDetail.tsx +51 -219
  62. package/src/execution/ToolCallGroup.tsx +3 -2
  63. package/src/execution/ToolCallItem.tsx +14 -2
  64. package/src/execution/index.ts +25 -0
  65. package/src/execution/tool-categories.ts +89 -9
  66. package/src/execution/tool-rendering-primitives.tsx +253 -0
  67. package/src/index.ts +13 -0
  68. package/src/internal/CloudFeatureNotice.tsx +60 -0
  69. package/src/mcp-server/McpServerDetailView.tsx +24 -2
  70. package/src/provider.tsx +18 -2
  71. package/styles.css +1 -1
@@ -8,7 +8,8 @@ import {
8
8
  resolveToolCategory,
9
9
  extractPrimaryArgFromPreview,
10
10
  } from "./tool-categories";
11
- import { FilePathLink } from "./FilePathLink";
11
+ import { CATEGORY_ICON } from "./ToolCallItem";
12
+ import { ToolArgsView } from "./ToolArgsView";
12
13
 
13
14
  export interface ApprovalCardProps {
14
15
  readonly pendingApproval: PendingApproval;
@@ -24,16 +25,16 @@ export interface ApprovalCardProps {
24
25
  readonly className?: string;
25
26
  }
26
27
 
27
- const TRUNCATION_LINE_LIMIT = 8;
28
-
29
28
  /**
30
- * Renders a pending tool-call approval request as a prominent card
31
- * with Approve, Skip, and Reject action buttons.
29
+ * Renders a pending tool-call approval request with the same visual
30
+ * structure as an expanded {@link ToolCallItem} in the history list.
31
+ *
32
+ * The compact header row matches the ToolCallItem layout:
33
+ * [CategoryIcon] Label primaryArg [⏳ waiting Xm]
32
34
  *
33
- * Tool-type-aware: shell tools render the command in a terminal-style
34
- * block, file tools show the path prominently, and destructive tools
35
- * (delete) use warning styling. Falls back to generic JSON args
36
- * display for unrecognized tools.
35
+ * Arguments are rendered through the shared {@link ToolArgsView}
36
+ * dispatch, ensuring pixel-level parity between the approval
37
+ * preview and the post-execution detail view.
37
38
  *
38
39
  * @example
39
40
  * ```tsx
@@ -66,250 +67,141 @@ export function ApprovalCard({
66
67
  }
67
68
  }, [isSubmitting]);
68
69
 
69
- const categoryInfo = resolveToolCategory(pendingApproval.toolName);
70
- const primaryArg = extractPrimaryArgFromPreview(
70
+ const categoryInfo = resolveToolCategory(
71
71
  pendingApproval.toolName,
72
- pendingApproval.argsPreview,
72
+ pendingApproval.mcpServerSlug,
73
73
  );
74
74
 
75
- const { header, headerIcon, borderClass } = getApprovalHeader(
76
- categoryInfo.category,
77
- pendingApproval.fromSubAgent,
75
+ const CategoryIcon = CATEGORY_ICON[categoryInfo.category];
76
+
77
+ const primaryArg = useMemo(
78
+ () =>
79
+ extractPrimaryArgFromPreview(
80
+ pendingApproval.toolName,
81
+ pendingApproval.argsPreview,
82
+ pendingApproval.mcpServerSlug,
83
+ ),
84
+ [
85
+ pendingApproval.toolName,
86
+ pendingApproval.argsPreview,
87
+ pendingApproval.mcpServerSlug,
88
+ ],
78
89
  );
79
90
 
91
+ const parsedArgs = useMemo<Record<string, unknown> | null>(() => {
92
+ if (!pendingApproval.argsPreview) return null;
93
+ try {
94
+ const parsed = JSON.parse(pendingApproval.argsPreview);
95
+ if (typeof parsed === "object" && parsed !== null) {
96
+ return parsed as Record<string, unknown>;
97
+ }
98
+ return null;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }, [pendingApproval.argsPreview]);
103
+
104
+ const borderClass =
105
+ categoryInfo.category === "delete"
106
+ ? "border-destructive/30 bg-destructive/5"
107
+ : "border-warning/30 bg-warning/5";
108
+
80
109
  return (
81
110
  <div
82
111
  role="alert"
83
112
  aria-label={`Approval required for ${pendingApproval.toolName}`}
84
- className={cn(
85
- "rounded-lg border px-4 py-3",
86
- borderClass,
87
- className,
88
- )}
113
+ className={cn("rounded-lg border", borderClass, className)}
89
114
  >
90
- {/* Header */}
91
- <div className="flex items-center gap-2 text-sm font-medium text-warning">
92
- {headerIcon}
93
- <span>{header}</span>
94
- </div>
95
-
96
- {/* Sub-agent attribution */}
97
- {pendingApproval.fromSubAgent && pendingApproval.subAgentName && (
98
- <p className="mt-1.5 text-xs text-muted-foreground">
99
- Sub-agent{" "}
100
- <span className="font-medium text-foreground">
101
- {pendingApproval.subAgentName}
102
- </span>{" "}
103
- wants to execute a tool
104
- </p>
105
- )}
115
+ {/* Compact header row — matches ToolCallItem layout */}
116
+ <div
117
+ className={cn(
118
+ "flex items-center gap-2 px-2.5 py-1.5 text-xs",
119
+ "border-b border-border/30",
120
+ )}
121
+ >
122
+ <span className="shrink-0 text-warning" aria-hidden="true">
123
+ <CategoryIcon />
124
+ </span>
106
125
 
107
- {/* Tool name badge */}
108
- <div className="mt-2 flex items-center gap-2">
109
- <span className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground">
110
- {categoryInfo.label}
126
+ <span className="min-w-0 flex-1 flex items-baseline gap-1.5 overflow-hidden">
127
+ <span className="shrink-0 font-medium text-foreground">
128
+ {categoryInfo.label}
129
+ </span>
130
+ {primaryArg && (
131
+ <span className="min-w-0 truncate font-mono text-muted-foreground">
132
+ {primaryArg}
133
+ </span>
134
+ )}
111
135
  </span>
112
- <WaitingDuration requestedAt={pendingApproval.requestedAt} />
113
- </div>
114
136
 
115
- {/* Approval message */}
116
- {pendingApproval.message && (
117
- <p className="mt-2 text-sm text-foreground">
118
- {pendingApproval.message}
119
- </p>
120
- )}
137
+ <WaitingDuration requestedAt={pendingApproval.requestedAt} />
121
138
 
122
- {/* Category-specific args preview */}
123
- <div className="mt-2">
124
- <CategoryArgsPreview
125
- category={categoryInfo.category}
126
- primaryArg={primaryArg}
127
- argsPreview={pendingApproval.argsPreview}
128
- />
139
+ <span className="shrink-0 text-warning" aria-hidden="true">
140
+ <ClockIcon />
141
+ </span>
129
142
  </div>
130
143
 
131
- {/* Action buttons */}
132
- <div className="mt-3 flex items-center gap-2">
133
- <ActionButton
134
- label="Approve"
135
- action={ApprovalAction.APPROVE}
136
- activeAction={activeAction}
137
- isSubmitting={isSubmitting}
138
- onClick={handleAction}
139
- variant="approve"
140
- />
141
- <ActionButton
142
- label="Skip"
143
- action={ApprovalAction.SKIP}
144
- activeAction={activeAction}
145
- isSubmitting={isSubmitting}
146
- onClick={handleAction}
147
- variant="skip"
148
- />
149
- <ActionButton
150
- label="Reject"
151
- action={ApprovalAction.REJECT}
152
- activeAction={activeAction}
153
- isSubmitting={isSubmitting}
154
- onClick={handleAction}
155
- variant="reject"
156
- />
144
+ {/* Body */}
145
+ <div className="px-3 py-2.5 space-y-2">
146
+ {/* Sub-agent attribution */}
147
+ {pendingApproval.fromSubAgent && pendingApproval.subAgentName && (
148
+ <p className="text-xs text-muted-foreground">
149
+ Sub-agent{" "}
150
+ <span className="font-medium text-foreground">
151
+ {pendingApproval.subAgentName}
152
+ </span>{" "}
153
+ wants to execute this tool
154
+ </p>
155
+ )}
156
+
157
+ {/* Approval message */}
158
+ {pendingApproval.message && (
159
+ <p className="text-xs text-foreground">
160
+ {pendingApproval.message}
161
+ </p>
162
+ )}
163
+
164
+ {/* Category-specific args preview — shared with ToolCallDetail */}
165
+ {parsedArgs && (
166
+ <ToolArgsView
167
+ toolName={pendingApproval.toolName}
168
+ args={parsedArgs}
169
+ mcpServerSlug={pendingApproval.mcpServerSlug}
170
+ />
171
+ )}
172
+
173
+ {/* Action buttons */}
174
+ <div className="flex items-center gap-2 pt-1">
175
+ <ActionButton
176
+ label="Approve"
177
+ action={ApprovalAction.APPROVE}
178
+ activeAction={activeAction}
179
+ isSubmitting={isSubmitting}
180
+ onClick={handleAction}
181
+ variant="approve"
182
+ />
183
+ <ActionButton
184
+ label="Skip"
185
+ action={ApprovalAction.SKIP}
186
+ activeAction={activeAction}
187
+ isSubmitting={isSubmitting}
188
+ onClick={handleAction}
189
+ variant="skip"
190
+ />
191
+ <ActionButton
192
+ label="Reject"
193
+ action={ApprovalAction.REJECT}
194
+ activeAction={activeAction}
195
+ isSubmitting={isSubmitting}
196
+ onClick={handleAction}
197
+ variant="reject"
198
+ />
199
+ </div>
157
200
  </div>
158
201
  </div>
159
202
  );
160
203
  }
161
204
 
162
- // ---------------------------------------------------------------------------
163
- // Category-aware header
164
- // ---------------------------------------------------------------------------
165
-
166
- function getApprovalHeader(
167
- category: string,
168
- fromSubAgent: boolean,
169
- ): {
170
- header: string;
171
- headerIcon: React.JSX.Element;
172
- borderClass: string;
173
- } {
174
- if (fromSubAgent) {
175
- return {
176
- header: "Sub-agent approval required",
177
- headerIcon: <ShieldIcon />,
178
- borderClass: "border-warning/30 bg-warning/5",
179
- };
180
- }
181
-
182
- switch (category) {
183
- case "shell":
184
- return {
185
- header: "Execute command",
186
- headerIcon: <TerminalApprovalIcon />,
187
- borderClass: "border-warning/30 bg-warning/5",
188
- };
189
- case "delete":
190
- return {
191
- header: "Delete file",
192
- headerIcon: <ShieldIcon />,
193
- borderClass: "border-destructive/30 bg-destructive/5",
194
- };
195
- default:
196
- return {
197
- header: "Approval required",
198
- headerIcon: <ShieldIcon />,
199
- borderClass: "border-warning/30 bg-warning/5",
200
- };
201
- }
202
- }
203
-
204
- // ---------------------------------------------------------------------------
205
- // Category-specific args preview
206
- // ---------------------------------------------------------------------------
207
-
208
- function CategoryArgsPreview({
209
- category,
210
- primaryArg,
211
- argsPreview,
212
- }: {
213
- category: string;
214
- primaryArg: string | null;
215
- argsPreview: string;
216
- }) {
217
- if (category === "shell" && primaryArg) {
218
- return <ShellArgsPreview command={primaryArg} />;
219
- }
220
-
221
- if ((category === "read" || category === "write" || category === "edit" || category === "delete") && primaryArg) {
222
- return <FileArgsPreview path={primaryArg} argsPreview={argsPreview} />;
223
- }
224
-
225
- if ((category === "search" || category === "list") && primaryArg) {
226
- return <SearchArgsPreview pattern={primaryArg} />;
227
- }
228
-
229
- if (!argsPreview) return null;
230
- return <GenericArgsPreview content={argsPreview} />;
231
- }
232
-
233
- function ShellArgsPreview({ command }: { command: string }) {
234
- return (
235
- <div className="rounded-md border border-border bg-[var(--stgm-terminal-bg,#1a1a2e)] p-2.5">
236
- <pre className="whitespace-pre-wrap break-words font-mono text-xs text-[var(--stgm-terminal-fg,#e0e0e0)]">
237
- <span className="select-none text-[var(--stgm-terminal-prompt,#6b7280)]">$ </span>
238
- {command}
239
- </pre>
240
- </div>
241
- );
242
- }
243
-
244
- function FileArgsPreview({
245
- path,
246
- argsPreview,
247
- }: {
248
- path: string;
249
- argsPreview: string;
250
- }) {
251
- return (
252
- <div className="space-y-1.5">
253
- <div className="flex items-center gap-1.5 text-xs">
254
- <FilePathIcon />
255
- <FilePathLink path={path} className="text-xs" />
256
- </div>
257
- {argsPreview && <GenericArgsPreview content={argsPreview} />}
258
- </div>
259
- );
260
- }
261
-
262
- function SearchArgsPreview({ pattern }: { pattern: string }) {
263
- return (
264
- <div className="flex items-center gap-1.5 text-xs">
265
- <span className="text-muted-foreground">Pattern:</span>
266
- <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-foreground">
267
- {pattern}
268
- </code>
269
- </div>
270
- );
271
- }
272
-
273
- function GenericArgsPreview({ content }: { content: string }) {
274
- const formatted = useMemo(() => {
275
- try {
276
- const parsed = JSON.parse(content);
277
- return JSON.stringify(parsed, null, 2);
278
- } catch {
279
- return content;
280
- }
281
- }, [content]);
282
-
283
- const lines = formatted.split("\n");
284
- const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
285
- const [isExpanded, setIsExpanded] = useState(false);
286
-
287
- const displayContent =
288
- needsTruncation && !isExpanded
289
- ? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
290
- : formatted;
291
-
292
- return (
293
- <div className="space-y-1">
294
- <span className="text-xs font-medium text-muted-foreground">
295
- Arguments
296
- </span>
297
- <pre className="max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted/40 p-2 font-mono text-xs text-foreground">
298
- {displayContent}
299
- </pre>
300
- {needsTruncation && (
301
- <button
302
- type="button"
303
- onClick={() => setIsExpanded((v) => !v)}
304
- className="text-xs font-medium text-primary transition-colors hover:text-primary/80"
305
- >
306
- {isExpanded ? "Show less" : `Show all ${lines.length} lines`}
307
- </button>
308
- )}
309
- </div>
310
- );
311
- }
312
-
313
205
  // ---------------------------------------------------------------------------
314
206
  // Internal sub-components
315
207
  // ---------------------------------------------------------------------------
@@ -366,9 +258,6 @@ function ActionButton({
366
258
  );
367
259
  }
368
260
 
369
- /**
370
- * Live-ticking elapsed time since the approval was requested.
371
- */
372
261
  function WaitingDuration({ requestedAt }: { requestedAt: string }) {
373
262
  const startMs = useMemo(() => {
374
263
  if (!requestedAt) return null;
@@ -393,8 +282,8 @@ function WaitingDuration({ requestedAt }: { requestedAt: string }) {
393
282
  if (elapsed === null) return null;
394
283
 
395
284
  return (
396
- <span className="text-xs text-muted-foreground">
397
- waiting {formatElapsed(elapsed)}
285
+ <span className="shrink-0 text-xs text-muted-foreground">
286
+ {formatElapsed(elapsed)}
398
287
  </span>
399
288
  );
400
289
  }
@@ -421,47 +310,7 @@ function formatElapsed(ms: number): string {
421
310
  // Inline SVG icons
422
311
  // ---------------------------------------------------------------------------
423
312
 
424
- function ShieldIcon() {
425
- return (
426
- <svg
427
- width="14"
428
- height="14"
429
- viewBox="0 0 14 14"
430
- fill="none"
431
- stroke="currentColor"
432
- strokeWidth="1.5"
433
- strokeLinecap="round"
434
- strokeLinejoin="round"
435
- aria-hidden="true"
436
- >
437
- <path d="M7 1L2 3.5V6.5C2 9.75 4.1 12.35 7 13C9.9 12.35 12 9.75 12 6.5V3.5L7 1Z" />
438
- <path d="M7 5V8" />
439
- <circle cx="7" cy="10" r="0.5" fill="currentColor" stroke="none" />
440
- </svg>
441
- );
442
- }
443
-
444
- function TerminalApprovalIcon() {
445
- return (
446
- <svg
447
- width="14"
448
- height="14"
449
- viewBox="0 0 14 14"
450
- fill="none"
451
- stroke="currentColor"
452
- strokeWidth="1.5"
453
- strokeLinecap="round"
454
- strokeLinejoin="round"
455
- aria-hidden="true"
456
- >
457
- <rect x="1" y="2" width="12" height="10" rx="2" />
458
- <path d="M4 5.5L6 7.5L4 9.5" />
459
- <path d="M7.5 9.5H10" />
460
- </svg>
461
- );
462
- }
463
-
464
- function FilePathIcon() {
313
+ function ClockIcon() {
465
314
  return (
466
315
  <svg
467
316
  width="10"
@@ -469,14 +318,12 @@ function FilePathIcon() {
469
318
  viewBox="0 0 12 12"
470
319
  fill="none"
471
320
  stroke="currentColor"
472
- strokeWidth="1.2"
321
+ strokeWidth="1.5"
473
322
  strokeLinecap="round"
474
323
  strokeLinejoin="round"
475
- className="shrink-0 text-muted-foreground"
476
- aria-hidden="true"
477
324
  >
478
- <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" />
479
- <path d="M7 1V4H10" />
325
+ <circle cx="6" cy="6" r="4.5" />
326
+ <path d="M6 3.5V6L7.5 7.5" />
480
327
  </svg>
481
328
  );
482
329
  }