@stigmer/react 0.0.52 → 0.0.54
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/ArtifactCard.d.ts +11 -1
- package/execution/ArtifactCard.d.ts.map +1 -1
- package/execution/ArtifactCard.js +22 -3
- package/execution/ArtifactCard.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +1 -1
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/ArtifactsWidget.d.ts +26 -19
- package/execution/ArtifactsWidget.d.ts.map +1 -1
- package/execution/ArtifactsWidget.js +24 -26
- package/execution/ArtifactsWidget.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/MessageThread.d.ts +10 -1
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +19 -17
- package/execution/MessageThread.js.map +1 -1
- package/execution/SandboxContext.d.ts +32 -0
- package/execution/SandboxContext.d.ts.map +1 -0
- package/execution/SandboxContext.js +26 -0
- package/execution/SandboxContext.js.map +1 -0
- package/execution/ToolArgsView.d.ts +41 -0
- package/execution/ToolArgsView.d.ts.map +1 -0
- package/execution/ToolArgsView.js +134 -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 +32 -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 +13 -3
- package/execution/ToolCallItem.js.map +1 -1
- package/execution/WriteBackCard.d.ts +34 -0
- package/execution/WriteBackCard.d.ts.map +1 -0
- package/execution/WriteBackCard.js +75 -0
- package/execution/WriteBackCard.js.map +1 -0
- package/execution/WriteBacksWidget.d.ts +49 -0
- package/execution/WriteBacksWidget.d.ts.map +1 -0
- package/execution/WriteBacksWidget.js +44 -0
- package/execution/WriteBacksWidget.js.map +1 -0
- package/execution/__tests__/file-path-resolver.test.d.ts +2 -0
- package/execution/__tests__/file-path-resolver.test.d.ts.map +1 -0
- package/execution/__tests__/file-path-resolver.test.js +180 -0
- package/execution/__tests__/file-path-resolver.test.js.map +1 -0
- package/execution/file-path-resolver.d.ts +3 -3
- package/execution/file-path-resolver.d.ts.map +1 -1
- package/execution/file-path-resolver.js +23 -12
- package/execution/file-path-resolver.js.map +1 -1
- package/execution/index.d.ts +16 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +9 -1
- package/execution/index.js.map +1 -1
- package/execution/sandbox-path-normalizer.d.ts +46 -0
- package/execution/sandbox-path-normalizer.d.ts.map +1 -0
- package/execution/sandbox-path-normalizer.js +73 -0
- package/execution/sandbox-path-normalizer.js.map +1 -0
- 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/execution/useArtifactContent.d.ts +5 -1
- package/execution/useArtifactContent.d.ts.map +1 -1
- package/execution/useArtifactContent.js +6 -2
- package/execution/useArtifactContent.js.map +1 -1
- package/execution/useWorkspaceWriteBacks.d.ts +40 -0
- package/execution/useWorkspaceWriteBacks.d.ts.map +1 -0
- package/execution/useWorkspaceWriteBacks.js +41 -0
- package/execution/useWorkspaceWriteBacks.js.map +1 -0
- package/github/GitHubRepoPicker.d.ts +5 -2
- package/github/GitHubRepoPicker.d.ts.map +1 -1
- package/github/GitHubRepoPicker.js +133 -36
- package/github/GitHubRepoPicker.js.map +1 -1
- package/github/index.d.ts +1 -0
- package/github/index.d.ts.map +1 -1
- package/github/index.js +1 -0
- package/github/index.js.map +1 -1
- package/github/useGitHubSearch.d.ts +20 -0
- package/github/useGitHubSearch.d.ts.map +1 -0
- package/github/useGitHubSearch.js +127 -0
- package/github/useGitHubSearch.js.map +1 -0
- package/index.d.ts +9 -6
- package/index.d.ts.map +1 -1
- package/index.js +7 -3
- 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/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +2 -0
- package/session/index.js.map +1 -1
- package/session/useSessionArtifacts.d.ts +73 -0
- package/session/useSessionArtifacts.d.ts.map +1 -0
- package/session/useSessionArtifacts.js +95 -0
- package/session/useSessionArtifacts.js.map +1 -0
- package/session/useSessionWriteBacks.d.ts +56 -0
- package/session/useSessionWriteBacks.d.ts.map +1 -0
- package/session/useSessionWriteBacks.js +56 -0
- package/session/useSessionWriteBacks.js.map +1 -0
- package/src/deployment-mode.ts +46 -0
- package/src/execution/ApprovalCard.tsx +130 -283
- package/src/execution/ArtifactCard.tsx +40 -0
- package/src/execution/ArtifactPreviewModal.tsx +2 -0
- package/src/execution/ArtifactsWidget.tsx +51 -43
- package/src/execution/McpToolDetail.tsx +283 -0
- package/src/execution/MessageThread.tsx +18 -0
- package/src/execution/SandboxContext.ts +47 -0
- package/src/execution/ToolArgsView.tsx +279 -0
- package/src/execution/ToolCallDetail.tsx +54 -220
- package/src/execution/ToolCallGroup.tsx +3 -2
- package/src/execution/ToolCallItem.tsx +21 -3
- package/src/execution/WriteBackCard.tsx +210 -0
- package/src/execution/WriteBacksWidget.tsx +82 -0
- package/src/execution/__tests__/file-path-resolver.test.ts +295 -0
- package/src/execution/file-path-resolver.ts +24 -12
- package/src/execution/index.ts +38 -0
- package/src/execution/sandbox-path-normalizer.ts +80 -0
- package/src/execution/tool-categories.ts +89 -9
- package/src/execution/tool-rendering-primitives.tsx +253 -0
- package/src/execution/useArtifactContent.ts +6 -1
- package/src/execution/useWorkspaceWriteBacks.ts +56 -0
- package/src/github/GitHubRepoPicker.tsx +413 -108
- package/src/github/index.ts +5 -0
- package/src/github/useGitHubSearch.ts +162 -0
- package/src/index.ts +27 -0
- package/src/internal/CloudFeatureNotice.tsx +60 -0
- package/src/mcp-server/McpServerDetailView.tsx +24 -2
- package/src/provider.tsx +18 -2
- package/src/session/index.ts +12 -0
- package/src/session/useSessionArtifacts.ts +143 -0
- package/src/session/useSessionWriteBacks.ts +94 -0
- package/styles.css +1 -1
|
@@ -62,7 +62,15 @@ export function resolveGitBrowseUrl(
|
|
|
62
62
|
if (!repoPath) return null;
|
|
63
63
|
|
|
64
64
|
const ref = commit || branch || "HEAD";
|
|
65
|
-
|
|
65
|
+
let cleanRelPath = relPath.replace(/^\/+/, "");
|
|
66
|
+
|
|
67
|
+
// Guard against relPath that accidentally duplicates the org/repo
|
|
68
|
+
// already encoded in gitUrl (e.g. relPath = "acme/app/src/main.go"
|
|
69
|
+
// when repoPath is "acme/app"). Strip the redundant prefix so the
|
|
70
|
+
// final URL doesn't contain the repo path twice.
|
|
71
|
+
if (cleanRelPath.startsWith(repoPath + "/")) {
|
|
72
|
+
cleanRelPath = cleanRelPath.slice(repoPath.length + 1);
|
|
73
|
+
}
|
|
66
74
|
|
|
67
75
|
return `https://github.com/${repoPath}/blob/${ref}/${cleanRelPath}`;
|
|
68
76
|
}
|
|
@@ -83,9 +91,9 @@ export type ResolvedPathAction =
|
|
|
83
91
|
* 1. **Platform paths** (`.stigmer/` prefix) → copy raw path.
|
|
84
92
|
* 2. **Workspace paths** → match against workspace entries:
|
|
85
93
|
* - Single entry: treat path as relative to that entry.
|
|
86
|
-
* - Multiple entries:
|
|
87
|
-
*
|
|
88
|
-
*
|
|
94
|
+
* - Multiple entries: scan path segments for an entry name match
|
|
95
|
+
* (handles org-prefixed paths like `plantonhq/agent-fleet/...`).
|
|
96
|
+
* Unmatched paths fall back to the first entry.
|
|
89
97
|
* 3. **Git source** → construct GitHub blob URL.
|
|
90
98
|
* 4. **Local source** → join with absolute local path for copy.
|
|
91
99
|
* 5. **Fallback** → copy the raw path.
|
|
@@ -139,8 +147,11 @@ export function resolvePathAction(
|
|
|
139
147
|
/**
|
|
140
148
|
* Matches a workspace-relative path against entries. With a single
|
|
141
149
|
* entry the path is used as-is. With multiple entries the first path
|
|
142
|
-
* segment is tested against entry names
|
|
143
|
-
*
|
|
150
|
+
* segment is tested against entry names. When the first segment
|
|
151
|
+
* doesn't match, deeper segments are scanned — this handles tool-call
|
|
152
|
+
* paths that embed an org prefix (e.g. `plantonhq/agent-fleet/...`
|
|
153
|
+
* where the entry name is `agent-fleet`). Unmatched paths fall back
|
|
154
|
+
* to the first entry.
|
|
144
155
|
*/
|
|
145
156
|
function matchWorkspaceEntry(
|
|
146
157
|
relPath: string,
|
|
@@ -150,13 +161,14 @@ function matchWorkspaceEntry(
|
|
|
150
161
|
return { entry: entries[0], relPath };
|
|
151
162
|
}
|
|
152
163
|
|
|
153
|
-
const
|
|
154
|
-
const firstSegment = slashIdx >= 0 ? relPath.slice(0, slashIdx) : relPath;
|
|
164
|
+
const segments = relPath.split("/");
|
|
155
165
|
|
|
156
|
-
for (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
for (let i = 0; i < segments.length; i++) {
|
|
167
|
+
for (const entry of entries) {
|
|
168
|
+
if (entry.name === segments[i]) {
|
|
169
|
+
const rest = segments.slice(i + 1).join("/");
|
|
170
|
+
return { entry, relPath: rest };
|
|
171
|
+
}
|
|
160
172
|
}
|
|
161
173
|
}
|
|
162
174
|
|
package/src/execution/index.ts
CHANGED
|
@@ -19,6 +19,12 @@ export type { UseExecutionArtifactsReturn } from "./useExecutionArtifacts";
|
|
|
19
19
|
export { useArtifactContent } from "./useArtifactContent";
|
|
20
20
|
export type { UseArtifactContentReturn } from "./useArtifactContent";
|
|
21
21
|
|
|
22
|
+
export { useWorkspaceWriteBacks } from "./useWorkspaceWriteBacks";
|
|
23
|
+
export type { UseWorkspaceWriteBacksReturn } from "./useWorkspaceWriteBacks";
|
|
24
|
+
|
|
25
|
+
export { WriteBackCard } from "./WriteBackCard";
|
|
26
|
+
export type { WriteBackCardProps } from "./WriteBackCard";
|
|
27
|
+
|
|
22
28
|
export {
|
|
23
29
|
isTextArtifact,
|
|
24
30
|
isArtifactExpired,
|
|
@@ -41,6 +47,30 @@ export type { ToolCallGroupProps } from "./ToolCallGroup";
|
|
|
41
47
|
export { ToolCallDetail, formatDuration } from "./ToolCallDetail";
|
|
42
48
|
export type { ToolCallDetailProps } from "./ToolCallDetail";
|
|
43
49
|
|
|
50
|
+
export { McpToolDetail, McpArgsView, McpMetadataRow, parseMcpResult } from "./McpToolDetail";
|
|
51
|
+
export type { McpToolDetailProps } from "./McpToolDetail";
|
|
52
|
+
|
|
53
|
+
export { ToolArgsView } from "./ToolArgsView";
|
|
54
|
+
export type { ToolArgsViewProps } from "./ToolArgsView";
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
CollapsibleCode,
|
|
58
|
+
CollapsiblePre,
|
|
59
|
+
CollapsibleJsonBlock,
|
|
60
|
+
FilePathIcon,
|
|
61
|
+
McpServerIcon,
|
|
62
|
+
TRUNCATION_LINE_LIMIT,
|
|
63
|
+
formatJson,
|
|
64
|
+
formatResult,
|
|
65
|
+
isScalar,
|
|
66
|
+
humanizeArgKey,
|
|
67
|
+
} from "./tool-rendering-primitives";
|
|
68
|
+
export type {
|
|
69
|
+
CollapsibleCodeProps,
|
|
70
|
+
CollapsiblePreProps,
|
|
71
|
+
CollapsibleJsonBlockProps,
|
|
72
|
+
} from "./tool-rendering-primitives";
|
|
73
|
+
|
|
44
74
|
export { ToolCallItem } from "./ToolCallItem";
|
|
45
75
|
export type { ToolCallItemProps } from "./ToolCallItem";
|
|
46
76
|
|
|
@@ -80,10 +110,14 @@ export type { ArtifactPreviewModalProps } from "./ArtifactPreviewModal";
|
|
|
80
110
|
export { ArtifactsWidget } from "./ArtifactsWidget";
|
|
81
111
|
export type { ArtifactsWidgetProps } from "./ArtifactsWidget";
|
|
82
112
|
|
|
113
|
+
export { WriteBacksWidget } from "./WriteBacksWidget";
|
|
114
|
+
export type { WriteBacksWidgetProps } from "./WriteBacksWidget";
|
|
115
|
+
|
|
83
116
|
export {
|
|
84
117
|
resolveToolCategory,
|
|
85
118
|
extractPrimaryArg,
|
|
86
119
|
extractPrimaryArgFromPreview,
|
|
120
|
+
humanizeToolName,
|
|
87
121
|
} from "./tool-categories";
|
|
88
122
|
export type { ToolCategory, ToolCategoryInfo } from "./tool-categories";
|
|
89
123
|
|
|
@@ -93,6 +127,10 @@ export type { FilePathLinkProps } from "./FilePathLink";
|
|
|
93
127
|
export { FilePathContext } from "./FilePathContext";
|
|
94
128
|
export type { FilePathContextValue } from "./FilePathContext";
|
|
95
129
|
|
|
130
|
+
export { normalizeSandboxPaths } from "./sandbox-path-normalizer";
|
|
131
|
+
export { SandboxContext, useSandboxNormalize } from "./SandboxContext";
|
|
132
|
+
export type { SandboxContextValue } from "./SandboxContext";
|
|
133
|
+
|
|
96
134
|
export { classifyPath, resolveGitBrowseUrl, resolvePathAction } from "./file-path-resolver";
|
|
97
135
|
export type { PathClassification, ResolvedPathAction } from "./file-path-resolver";
|
|
98
136
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-time normalization for sandbox workspace paths.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Python `humanize_sandbox_paths` in
|
|
5
|
+
* `graphton.core.backends.platform_mount` — same semantics, same
|
|
6
|
+
* replacement order. The SDK version acts as a safety net for
|
|
7
|
+
* historical data (persisted before backend humanization was added)
|
|
8
|
+
* and streaming edge cases.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Replace absolute sandbox workspace paths with workspace-relative
|
|
15
|
+
* display paths.
|
|
16
|
+
*
|
|
17
|
+
* Performs three ordered replacements:
|
|
18
|
+
*
|
|
19
|
+
* 1. `workspaceRoot + "/"` → empty string (makes paths workspace-relative)
|
|
20
|
+
* 2. `workspaceRoot` (exact) → `"."` (the workspace root itself)
|
|
21
|
+
* 3. Parent of `workspaceRoot` (sandbox home) → `"~"` (Unix convention)
|
|
22
|
+
*
|
|
23
|
+
* Returns `text` unchanged when `workspaceRoot` is empty.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* normalizeSandboxPaths(
|
|
28
|
+
* "ls /home/daytona/workspace/plantonhq/",
|
|
29
|
+
* "/home/daytona/workspace",
|
|
30
|
+
* );
|
|
31
|
+
* // => "ls plantonhq/"
|
|
32
|
+
*
|
|
33
|
+
* normalizeSandboxPaths(
|
|
34
|
+
* "cd /home/daytona/workspace && ls",
|
|
35
|
+
* "/home/daytona/workspace",
|
|
36
|
+
* );
|
|
37
|
+
* // => "cd . && ls"
|
|
38
|
+
*
|
|
39
|
+
* normalizeSandboxPaths(
|
|
40
|
+
* "cat /home/daytona/.bashrc",
|
|
41
|
+
* "/home/daytona/workspace",
|
|
42
|
+
* );
|
|
43
|
+
* // => "cat ~/.bashrc"
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function normalizeSandboxPaths(
|
|
47
|
+
text: string,
|
|
48
|
+
workspaceRoot: string,
|
|
49
|
+
): string {
|
|
50
|
+
if (!text || !workspaceRoot) return text;
|
|
51
|
+
|
|
52
|
+
const wsRoot = workspaceRoot.replace(/\/+$/, "");
|
|
53
|
+
|
|
54
|
+
// 1) Strip workspace root prefix (with trailing slash) → workspace-relative
|
|
55
|
+
text = replaceAll(text, wsRoot + "/", "");
|
|
56
|
+
|
|
57
|
+
// 2) Replace bare workspace root → "."
|
|
58
|
+
text = replaceAll(text, wsRoot, ".");
|
|
59
|
+
|
|
60
|
+
// 3) Replace sandbox home prefix → "~"
|
|
61
|
+
const lastSlash = wsRoot.lastIndexOf("/");
|
|
62
|
+
if (lastSlash > 0) {
|
|
63
|
+
const sandboxHome = wsRoot.slice(0, lastSlash);
|
|
64
|
+
text = replaceAll(text, sandboxHome + "/", "~/");
|
|
65
|
+
text = replaceAll(text, sandboxHome, "~");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function replaceAll(text: string, search: string, replacement: string): string {
|
|
72
|
+
if (!search) return text;
|
|
73
|
+
let result = text;
|
|
74
|
+
let idx = result.indexOf(search);
|
|
75
|
+
while (idx !== -1) {
|
|
76
|
+
result = result.slice(0, idx) + replacement + result.slice(idx + search.length);
|
|
77
|
+
idx = result.indexOf(search, idx + replacement.length);
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared truncation threshold for all collapsible tool rendering
|
|
8
|
+
* primitives. Applied consistently across detail views and approval
|
|
9
|
+
* card previews.
|
|
10
|
+
*/
|
|
11
|
+
export const TRUNCATION_LINE_LIMIT = 10;
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// CollapsibleCode — labeled code block with line-based truncation
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export interface CollapsibleCodeProps {
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly content: string;
|
|
20
|
+
readonly className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A labeled `<pre>` block with automatic line-based truncation and
|
|
25
|
+
* an expand/collapse toggle.
|
|
26
|
+
*
|
|
27
|
+
* Used for tool arguments, file content previews, and result blocks
|
|
28
|
+
* across both the detail view and the approval card.
|
|
29
|
+
*/
|
|
30
|
+
export function CollapsibleCode({ label, content, className }: CollapsibleCodeProps) {
|
|
31
|
+
const lines = content.split("\n");
|
|
32
|
+
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
33
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
34
|
+
|
|
35
|
+
const displayContent =
|
|
36
|
+
needsTruncation && !isExpanded
|
|
37
|
+
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
38
|
+
: content;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className={cn("space-y-1", className)}>
|
|
42
|
+
<span className="font-medium text-muted-foreground">{label}</span>
|
|
43
|
+
<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">
|
|
44
|
+
{displayContent}
|
|
45
|
+
</pre>
|
|
46
|
+
{needsTruncation && (
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={() => setIsExpanded((v) => !v)}
|
|
50
|
+
className="text-xs font-medium text-primary transition-colors hover:text-primary/80"
|
|
51
|
+
>
|
|
52
|
+
{isExpanded ? "Show less" : `Show all ${lines.length} lines`}
|
|
53
|
+
</button>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// CollapsiblePre — raw pre with line-based truncation (no label/border)
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export interface CollapsiblePreProps {
|
|
64
|
+
readonly content: string;
|
|
65
|
+
readonly className?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A bare `<pre>` element with line-based truncation. Unlike
|
|
70
|
+
* {@link CollapsibleCode}, this has no label, border, or background
|
|
71
|
+
* — the caller controls container styling via `className`.
|
|
72
|
+
*
|
|
73
|
+
* Pass container styles (border, background, max-height) through
|
|
74
|
+
* `className` when rendering standalone; omit when the parent
|
|
75
|
+
* already provides a styled container (e.g. terminal blocks).
|
|
76
|
+
*/
|
|
77
|
+
export function CollapsiblePre({ content, className }: CollapsiblePreProps) {
|
|
78
|
+
const lines = content.split("\n");
|
|
79
|
+
const needsTruncation = lines.length > TRUNCATION_LINE_LIMIT;
|
|
80
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
81
|
+
|
|
82
|
+
const displayContent =
|
|
83
|
+
needsTruncation && !isExpanded
|
|
84
|
+
? lines.slice(0, TRUNCATION_LINE_LIMIT).join("\n") + "\n\u2026"
|
|
85
|
+
: content;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<pre className={cn("whitespace-pre-wrap break-words font-mono", className)}>
|
|
90
|
+
{displayContent}
|
|
91
|
+
</pre>
|
|
92
|
+
{needsTruncation && (
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
onClick={() => setIsExpanded((v) => !v)}
|
|
96
|
+
className="mt-1 text-xs font-medium text-primary transition-colors hover:text-primary/80"
|
|
97
|
+
>
|
|
98
|
+
{isExpanded ? "Show less" : `Show all ${lines.length} lines`}
|
|
99
|
+
</button>
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// CollapsibleJsonBlock — chevron-toggled JSON section
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
export interface CollapsibleJsonBlockProps {
|
|
110
|
+
readonly label: string;
|
|
111
|
+
readonly content: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A collapsible JSON section with a chevron toggle. Initially
|
|
116
|
+
* collapsed, showing the label and line count. Useful for complex
|
|
117
|
+
* (non-scalar) tool arguments.
|
|
118
|
+
*/
|
|
119
|
+
export function CollapsibleJsonBlock({ label, content }: CollapsibleJsonBlockProps) {
|
|
120
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
121
|
+
const lines = content.split("\n");
|
|
122
|
+
const isLong = lines.length > 3;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<div className="space-y-1">
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
onClick={() => setIsExpanded((v) => !v)}
|
|
129
|
+
className="flex items-center gap-1 font-medium text-muted-foreground transition-colors hover:text-foreground"
|
|
130
|
+
>
|
|
131
|
+
<svg
|
|
132
|
+
width="8"
|
|
133
|
+
height="8"
|
|
134
|
+
viewBox="0 0 8 8"
|
|
135
|
+
fill="none"
|
|
136
|
+
stroke="currentColor"
|
|
137
|
+
strokeWidth="1.5"
|
|
138
|
+
strokeLinecap="round"
|
|
139
|
+
strokeLinejoin="round"
|
|
140
|
+
className={cn(
|
|
141
|
+
"shrink-0 transition-transform duration-150",
|
|
142
|
+
isExpanded && "rotate-90",
|
|
143
|
+
)}
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
>
|
|
146
|
+
<path d="M2 1L6 4L2 7" />
|
|
147
|
+
</svg>
|
|
148
|
+
{label}
|
|
149
|
+
{!isExpanded && isLong && (
|
|
150
|
+
<span className="font-normal text-muted-foreground/60">
|
|
151
|
+
({lines.length} lines)
|
|
152
|
+
</span>
|
|
153
|
+
)}
|
|
154
|
+
</button>
|
|
155
|
+
{isExpanded && (
|
|
156
|
+
<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">
|
|
157
|
+
{content}
|
|
158
|
+
</pre>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Inline SVG icons
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
/** Small document icon for file path displays (10x10). */
|
|
169
|
+
export function FilePathIcon() {
|
|
170
|
+
return (
|
|
171
|
+
<svg
|
|
172
|
+
width="10"
|
|
173
|
+
height="10"
|
|
174
|
+
viewBox="0 0 12 12"
|
|
175
|
+
fill="none"
|
|
176
|
+
stroke="currentColor"
|
|
177
|
+
strokeWidth="1.2"
|
|
178
|
+
strokeLinecap="round"
|
|
179
|
+
strokeLinejoin="round"
|
|
180
|
+
className="shrink-0 text-muted-foreground"
|
|
181
|
+
aria-hidden="true"
|
|
182
|
+
>
|
|
183
|
+
<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" />
|
|
184
|
+
<path d="M7 1V4H10" />
|
|
185
|
+
</svg>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** MCP server node/link icon (10x10). */
|
|
190
|
+
export function McpServerIcon() {
|
|
191
|
+
return (
|
|
192
|
+
<svg
|
|
193
|
+
width="10"
|
|
194
|
+
height="10"
|
|
195
|
+
viewBox="0 0 12 12"
|
|
196
|
+
fill="none"
|
|
197
|
+
stroke="currentColor"
|
|
198
|
+
strokeWidth="1.2"
|
|
199
|
+
strokeLinecap="round"
|
|
200
|
+
strokeLinejoin="round"
|
|
201
|
+
className="shrink-0"
|
|
202
|
+
aria-hidden="true"
|
|
203
|
+
>
|
|
204
|
+
<circle cx="6" cy="3" r="1.5" />
|
|
205
|
+
<circle cx="6" cy="9" r="1.5" />
|
|
206
|
+
<path d="M6 4.5V7.5" />
|
|
207
|
+
<path d="M3 6H4.5" />
|
|
208
|
+
<path d="M7.5 6H9" />
|
|
209
|
+
</svg>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Shared utilities
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
/** Safely serialise an object to pretty JSON. */
|
|
218
|
+
export function formatJson(obj: unknown): string {
|
|
219
|
+
try {
|
|
220
|
+
return JSON.stringify(obj, null, 2);
|
|
221
|
+
} catch {
|
|
222
|
+
return String(obj);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Pretty-print a result string if it's valid JSON, otherwise return as-is. */
|
|
227
|
+
export function formatResult(result: string): string {
|
|
228
|
+
try {
|
|
229
|
+
const parsed = JSON.parse(result);
|
|
230
|
+
return JSON.stringify(parsed, null, 2);
|
|
231
|
+
} catch {
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Detect scalar values (string, number, boolean). */
|
|
237
|
+
export function isScalar(value: unknown): value is string | number | boolean {
|
|
238
|
+
const t = typeof value;
|
|
239
|
+
return t === "string" || t === "number" || t === "boolean";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Title-case a snake_case or camelCase argument key for display.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* humanizeArgKey("mcp_server_slug") // "Mcp Server Slug"
|
|
247
|
+
*/
|
|
248
|
+
export function humanizeArgKey(key: string): string {
|
|
249
|
+
return key
|
|
250
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
251
|
+
.replace(/[_-]+/g, " ")
|
|
252
|
+
.replace(/\b[a-z]/g, (c) => c.toUpperCase());
|
|
253
|
+
}
|
|
@@ -80,6 +80,10 @@ export interface UseArtifactContentReturn {
|
|
|
80
80
|
* @param storageKey - Storage key from `ExecutionArtifact.storageKey`, or `null` to skip.
|
|
81
81
|
* @param entryPath - For directory artifacts: relative path of a file within
|
|
82
82
|
* the archive to extract. `null` returns the full artifact (existing behavior).
|
|
83
|
+
* @param contentHash - SHA-256 hex digest from `ExecutionArtifact.contentHash`.
|
|
84
|
+
* When the same file is overwritten during execution, the `storageKey` stays
|
|
85
|
+
* stable but `contentHash` changes, triggering a re-fetch so the UI never
|
|
86
|
+
* shows stale content. Pass `undefined` or omit for backwards compatibility.
|
|
83
87
|
*
|
|
84
88
|
* @see useExecutionArtifacts — extracts artifact metadata from an execution
|
|
85
89
|
* @see isTextArtifact — heuristic for whether content is fetchable as text
|
|
@@ -88,6 +92,7 @@ export function useArtifactContent(
|
|
|
88
92
|
executionId: string | null,
|
|
89
93
|
storageKey: string | null,
|
|
90
94
|
entryPath?: string | null,
|
|
95
|
+
contentHash?: string,
|
|
91
96
|
): UseArtifactContentReturn {
|
|
92
97
|
const stigmer = useStigmer();
|
|
93
98
|
|
|
@@ -150,7 +155,7 @@ export function useArtifactContent(
|
|
|
150
155
|
return () => {
|
|
151
156
|
cancelled.current = true;
|
|
152
157
|
};
|
|
153
|
-
}, [executionId, storageKey, entryPath, stigmer, fetchKey]);
|
|
158
|
+
}, [executionId, storageKey, entryPath, contentHash, stigmer, fetchKey]);
|
|
154
159
|
|
|
155
160
|
return { content, contentType, isTruncated, isLoading, error, refetch };
|
|
156
161
|
}
|