@stigmer/ink 2.0.1 → 3.0.0
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/components/ApprovalPrompt.d.ts +4 -3
- package/components/ApprovalPrompt.d.ts.map +1 -1
- package/components/ApprovalPrompt.js +10 -3
- package/components/ApprovalPrompt.js.map +1 -1
- package/components/ToolCallItem.d.ts +4 -2
- package/components/ToolCallItem.d.ts.map +1 -1
- package/components/ToolCallItem.js +68 -17
- package/components/ToolCallItem.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/components.test.tsx +51 -4
- package/src/components/ApprovalPrompt.tsx +10 -3
- package/src/components/ToolCallItem.tsx +85 -30
|
@@ -13,9 +13,10 @@ export interface ApprovalPromptProps {
|
|
|
13
13
|
* HITL approval prompt for tool call authorization.
|
|
14
14
|
*
|
|
15
15
|
* Displays the tool name and args preview, then presents
|
|
16
|
-
* Approve/Reject/Skip options navigable via arrow keys
|
|
17
|
-
* shortcut keys (y/n/s). Press Enter to confirm the highlighted
|
|
18
|
-
* selection.
|
|
16
|
+
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
17
|
+
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
18
|
+
* selection. "Approve & don't ask again" (a) maps to APPROVE_ALL, which
|
|
19
|
+
* stops gating the rest of the run.
|
|
19
20
|
*/
|
|
20
21
|
export declare function ApprovalPrompt({ pendingApproval, onSubmit, isSubmitting, }: ApprovalPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
21
22
|
//# sourceMappingURL=ApprovalPrompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;AAE9F,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACpD,gEAAgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kEAAkE,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;AAE9F,wCAAwC;AACxC,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACpD,gEAAgE;IAChE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAoCD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,eAAe,EACf,QAAQ,EACR,YAAoB,GACrB,EAAE,mBAAmB,2CAmFrB"}
|
|
@@ -9,6 +9,12 @@ const OPTIONS = [
|
|
|
9
9
|
color: "green",
|
|
10
10
|
shortcut: "y",
|
|
11
11
|
},
|
|
12
|
+
{
|
|
13
|
+
label: "Approve & don't ask again",
|
|
14
|
+
action: ApprovalAction.APPROVE_ALL,
|
|
15
|
+
color: "green",
|
|
16
|
+
shortcut: "a",
|
|
17
|
+
},
|
|
12
18
|
{
|
|
13
19
|
label: "Reject",
|
|
14
20
|
action: ApprovalAction.REJECT,
|
|
@@ -26,9 +32,10 @@ const OPTIONS = [
|
|
|
26
32
|
* HITL approval prompt for tool call authorization.
|
|
27
33
|
*
|
|
28
34
|
* Displays the tool name and args preview, then presents
|
|
29
|
-
* Approve/Reject/Skip options navigable via arrow keys
|
|
30
|
-
* shortcut keys (y/n/s). Press Enter to confirm the highlighted
|
|
31
|
-
* selection.
|
|
35
|
+
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
36
|
+
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
37
|
+
* selection. "Approve & don't ask again" (a) maps to APPROVE_ALL, which
|
|
38
|
+
* stops gating the rest of the run.
|
|
32
39
|
*/
|
|
33
40
|
export function ApprovalPrompt({ pendingApproval, onSubmit, isSubmitting = false, }) {
|
|
34
41
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApprovalPrompt.js","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;AAmB9F,MAAM,OAAO,GAA4B;IACvC;QACE,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,cAAc,CAAC,OAAO;QAC9B,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,GAAG;KACd;IACD;QACE,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,cAAc,CAAC,MAAM;QAC7B,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,GAAG;KACd;IACD;QACE,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,cAAc,CAAC,IAAI;QAC3B,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,GAAG;KACd;CACF,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"ApprovalPrompt.js","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,10 @@ export interface ToolCallItemProps {
|
|
|
7
7
|
readonly expanded?: boolean;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Renders a single tool call with a status indicator,
|
|
11
|
-
*
|
|
10
|
+
* Renders a single tool call with a status indicator, label, and an optional
|
|
11
|
+
* expanded result. Labels and results come from the shared `@stigmer/sdk` view
|
|
12
|
+
* model, so a terminal session shows the same semantics (diffs, exit codes,
|
|
13
|
+
* match counts) as the web console.
|
|
12
14
|
*/
|
|
13
15
|
export declare function ToolCallItem({ toolCall, expanded }: ToolCallItemProps): import("react/jsx-runtime").JSX.Element;
|
|
14
16
|
//# sourceMappingURL=ToolCallItem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallItem.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iEAAiE,CAAC;
|
|
1
|
+
{"version":3,"file":"ToolCallItem.d.ts","sourceRoot":"","sources":["../../src/components/ToolCallItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iEAAiE,CAAC;AAKhG,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AA0BD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,EAAE,iBAAiB,2CAoC7E"}
|
|
@@ -1,33 +1,84 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
4
|
+
import { ToolKind, resolveToolKind, normalizeToolResult } from "@stigmer/sdk";
|
|
4
5
|
const STATUS_INDICATOR = {
|
|
5
6
|
[ToolCallStatus.TOOL_CALL_RUNNING]: { symbol: "⠋", color: "yellow" },
|
|
6
7
|
[ToolCallStatus.TOOL_CALL_COMPLETED]: { symbol: "✓", color: "green" },
|
|
7
8
|
[ToolCallStatus.TOOL_CALL_FAILED]: { symbol: "✗", color: "red" },
|
|
8
9
|
};
|
|
10
|
+
// Harness-agnostic labels per kind. Classification is shared with the runner,
|
|
11
|
+
// React, and the Go CLI via @stigmer/sdk's resolveToolKind, so Cursor's
|
|
12
|
+
// PascalCase tools render with the same labels as native tools here.
|
|
13
|
+
const KIND_LABEL = {
|
|
14
|
+
[ToolKind.FILE_READ]: "Read",
|
|
15
|
+
[ToolKind.FILE_WRITE]: "Write",
|
|
16
|
+
[ToolKind.FILE_EDIT]: "Edit",
|
|
17
|
+
[ToolKind.FILE_DELETE]: "Delete",
|
|
18
|
+
[ToolKind.SHELL]: "Shell",
|
|
19
|
+
[ToolKind.SEARCH]: "Search",
|
|
20
|
+
[ToolKind.LIST]: "List",
|
|
21
|
+
[ToolKind.FETCH]: "Fetch",
|
|
22
|
+
[ToolKind.WEB_SEARCH]: "Web Search",
|
|
23
|
+
[ToolKind.THINK]: "Thinking",
|
|
24
|
+
[ToolKind.TODO]: "Todos",
|
|
25
|
+
[ToolKind.SUBAGENT]: "Sub-agent",
|
|
26
|
+
};
|
|
9
27
|
/**
|
|
10
|
-
* Renders a single tool call with a status indicator,
|
|
11
|
-
*
|
|
28
|
+
* Renders a single tool call with a status indicator, label, and an optional
|
|
29
|
+
* expanded result. Labels and results come from the shared `@stigmer/sdk` view
|
|
30
|
+
* model, so a terminal session shows the same semantics (diffs, exit codes,
|
|
31
|
+
* match counts) as the web console.
|
|
12
32
|
*/
|
|
13
33
|
export function ToolCallItem({ toolCall, expanded = false }) {
|
|
14
|
-
const indicator = STATUS_INDICATOR[toolCall.status] ?? {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
const indicator = STATUS_INDICATOR[toolCall.status] ?? { symbol: "○" };
|
|
35
|
+
const kind = resolveToolKind(toolCall);
|
|
36
|
+
const label = toolLabel(toolCall, kind);
|
|
37
|
+
const view = normalizeToolResult(toolCall);
|
|
38
|
+
const resultText = expanded ? describeResultView(view) : null;
|
|
39
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: indicator.color, children: indicator.symbol }), _jsx(Text, { children: label }), toolCall.status === ToolCallStatus.TOOL_CALL_RUNNING && (_jsx(Text, { dimColor: true, children: "running" }))] }), view.type === "error" ? (expanded && (_jsx(Box, { paddingLeft: 3, children: _jsx(Text, { color: "red", wrap: "truncate-end", children: view.message }) }))) : (resultText && (_jsx(Box, { paddingLeft: 3, children: _jsx(Text, { dimColor: true, wrap: "truncate-end", children: resultText }) })))] }));
|
|
40
|
+
}
|
|
41
|
+
function toolLabel(toolCall, kind) {
|
|
42
|
+
if (toolCall.mcpServerSlug) {
|
|
43
|
+
return `${toolCall.mcpServerSlug}/${toolCall.name}`;
|
|
44
|
+
}
|
|
45
|
+
return KIND_LABEL[kind] ?? toolCall.name;
|
|
46
|
+
}
|
|
47
|
+
// Renders a concise, terminal-friendly description of a normalized result view.
|
|
48
|
+
function describeResultView(view) {
|
|
49
|
+
switch (view.type) {
|
|
50
|
+
case "diff": {
|
|
51
|
+
const stats = view.linesAdded !== undefined || view.linesRemoved !== undefined
|
|
52
|
+
? ` (+${view.linesAdded ?? 0} -${view.linesRemoved ?? 0})`
|
|
53
|
+
: "";
|
|
54
|
+
return `${view.path}${stats}`;
|
|
55
|
+
}
|
|
56
|
+
case "terminal": {
|
|
57
|
+
const exit = view.exitCode !== undefined && view.exitCode !== 0 ? `[exit ${view.exitCode}] ` : "";
|
|
58
|
+
return exit + truncate(view.stdout || view.stderr);
|
|
59
|
+
}
|
|
60
|
+
case "search":
|
|
61
|
+
return `${view.count} ${view.count === 1 ? "match" : "matches"}`;
|
|
62
|
+
case "list":
|
|
63
|
+
return `${view.count} ${view.count === 1 ? "item" : "items"}`;
|
|
64
|
+
case "file":
|
|
65
|
+
return view.path || null;
|
|
66
|
+
case "contentBlocks":
|
|
67
|
+
return truncate(view.blocks.map((b) => b.text ?? `[${b.type}]`).join(" "));
|
|
68
|
+
case "text":
|
|
69
|
+
return truncate(view.text);
|
|
70
|
+
case "json":
|
|
71
|
+
return truncate(JSON.stringify(view.value));
|
|
72
|
+
case "error":
|
|
73
|
+
return view.message;
|
|
74
|
+
case "empty":
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
26
77
|
}
|
|
27
|
-
function
|
|
28
|
-
const lines =
|
|
78
|
+
function truncate(s, maxLines = 5) {
|
|
79
|
+
const lines = s.split("\n");
|
|
29
80
|
if (lines.length <= maxLines)
|
|
30
|
-
return
|
|
81
|
+
return s;
|
|
31
82
|
return lines.slice(0, maxLines).join("\n") + `\n... (${lines.length - maxLines} more lines)`;
|
|
32
83
|
}
|
|
33
84
|
//# sourceMappingURL=ToolCallItem.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallItem.js","sourceRoot":"","sources":["../../src/components/ToolCallItem.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;
|
|
1
|
+
{"version":3,"file":"ToolCallItem.js","sourceRoot":"","sources":["../../src/components/ToolCallItem.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/ink",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Ink (React for terminals) components for rendering Stigmer agent sessions in the terminal",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@connectrpc/connect-node": "^2.1.1",
|
|
35
|
-
"@stigmer/react": "
|
|
35
|
+
"@stigmer/react": "3.0.0",
|
|
36
36
|
"ink-spinner": "^5.0.0",
|
|
37
37
|
"ink-text-input": "^6.0.0",
|
|
38
38
|
"marked": "^15.0.7",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@bufbuild/protobuf": "^2.0.0",
|
|
43
|
-
"@stigmer/protos": "
|
|
44
|
-
"@stigmer/sdk": "
|
|
43
|
+
"@stigmer/protos": "3.0.0",
|
|
44
|
+
"@stigmer/sdk": "3.0.0",
|
|
45
45
|
"ink": "^7.0.0",
|
|
46
46
|
"react": ">=19.0.0"
|
|
47
47
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { describe, it, expect, vi } from "vitest";
|
|
3
3
|
import { render } from "ink-testing-library";
|
|
4
4
|
import { Text, Box } from "ink";
|
|
5
5
|
import { create } from "@bufbuild/protobuf";
|
|
6
6
|
import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
7
|
-
import { MessageType, ExecutionPhase, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
7
|
+
import { MessageType, ExecutionPhase, ToolCallStatus, ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
8
8
|
import { ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
9
|
+
import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
9
10
|
import { MessageEntry } from "../components/MessageEntry.js";
|
|
10
11
|
import { ExecutionProgress } from "../components/ExecutionProgress.js";
|
|
11
12
|
import { ToolCallItem } from "../components/ToolCallItem.js";
|
|
13
|
+
import { ApprovalPrompt } from "../components/ApprovalPrompt.js";
|
|
12
14
|
|
|
13
15
|
describe("MessageEntry", () => {
|
|
14
16
|
it("renders a human message with 'You' prefix", () => {
|
|
@@ -120,7 +122,7 @@ describe("ToolCallItem", () => {
|
|
|
120
122
|
|
|
121
123
|
const { lastFrame } = render(<ToolCallItem toolCall={tc} />);
|
|
122
124
|
const output = lastFrame() ?? "";
|
|
123
|
-
expect(output).toContain("
|
|
125
|
+
expect(output).toContain("Read");
|
|
124
126
|
expect(output).toContain("✓");
|
|
125
127
|
});
|
|
126
128
|
|
|
@@ -132,7 +134,7 @@ describe("ToolCallItem", () => {
|
|
|
132
134
|
|
|
133
135
|
const { lastFrame } = render(<ToolCallItem toolCall={tc} />);
|
|
134
136
|
const output = lastFrame() ?? "";
|
|
135
|
-
expect(output).toContain("
|
|
137
|
+
expect(output).toContain("Write");
|
|
136
138
|
expect(output).toContain("running");
|
|
137
139
|
});
|
|
138
140
|
|
|
@@ -160,3 +162,48 @@ describe("ToolCallItem", () => {
|
|
|
160
162
|
expect(output).toContain("github/list_resources");
|
|
161
163
|
});
|
|
162
164
|
});
|
|
165
|
+
|
|
166
|
+
describe("ApprovalPrompt", () => {
|
|
167
|
+
it("offers Approve, Approve & don't ask again, Reject, and Skip", () => {
|
|
168
|
+
const pending = create(PendingApprovalSchema);
|
|
169
|
+
pending.toolCallId = "tc-1";
|
|
170
|
+
pending.toolName = "write_file";
|
|
171
|
+
|
|
172
|
+
const { lastFrame } = render(
|
|
173
|
+
<ApprovalPrompt pendingApproval={pending} onSubmit={() => {}} />,
|
|
174
|
+
);
|
|
175
|
+
const output = lastFrame() ?? "";
|
|
176
|
+
expect(output).toContain("[y] Approve");
|
|
177
|
+
expect(output).toContain("[a] Approve & don't ask again");
|
|
178
|
+
expect(output).toContain("[n] Reject");
|
|
179
|
+
expect(output).toContain("[s] Skip");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("submits APPROVE_ALL when the 'a' shortcut is pressed", () => {
|
|
183
|
+
const pending = create(PendingApprovalSchema);
|
|
184
|
+
pending.toolCallId = "tc-1";
|
|
185
|
+
pending.toolName = "write_file";
|
|
186
|
+
const onSubmit = vi.fn();
|
|
187
|
+
|
|
188
|
+
const { stdin } = render(
|
|
189
|
+
<ApprovalPrompt pendingApproval={pending} onSubmit={onSubmit} />,
|
|
190
|
+
);
|
|
191
|
+
stdin.write("a");
|
|
192
|
+
|
|
193
|
+
expect(onSubmit).toHaveBeenCalledWith(ApprovalAction.APPROVE_ALL);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("submits APPROVE when the 'y' shortcut is pressed", () => {
|
|
197
|
+
const pending = create(PendingApprovalSchema);
|
|
198
|
+
pending.toolCallId = "tc-1";
|
|
199
|
+
pending.toolName = "write_file";
|
|
200
|
+
const onSubmit = vi.fn();
|
|
201
|
+
|
|
202
|
+
const { stdin } = render(
|
|
203
|
+
<ApprovalPrompt pendingApproval={pending} onSubmit={onSubmit} />,
|
|
204
|
+
);
|
|
205
|
+
stdin.write("y");
|
|
206
|
+
|
|
207
|
+
expect(onSubmit).toHaveBeenCalledWith(ApprovalAction.APPROVE);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
@@ -27,6 +27,12 @@ const OPTIONS: readonly ActionOption[] = [
|
|
|
27
27
|
color: "green",
|
|
28
28
|
shortcut: "y",
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
label: "Approve & don't ask again",
|
|
32
|
+
action: ApprovalAction.APPROVE_ALL,
|
|
33
|
+
color: "green",
|
|
34
|
+
shortcut: "a",
|
|
35
|
+
},
|
|
30
36
|
{
|
|
31
37
|
label: "Reject",
|
|
32
38
|
action: ApprovalAction.REJECT,
|
|
@@ -45,9 +51,10 @@ const OPTIONS: readonly ActionOption[] = [
|
|
|
45
51
|
* HITL approval prompt for tool call authorization.
|
|
46
52
|
*
|
|
47
53
|
* Displays the tool name and args preview, then presents
|
|
48
|
-
* Approve/Reject/Skip options navigable via arrow keys
|
|
49
|
-
* shortcut keys (y/n/s). Press Enter to confirm the highlighted
|
|
50
|
-
* selection.
|
|
54
|
+
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
55
|
+
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
56
|
+
* selection. "Approve & don't ask again" (a) maps to APPROVE_ALL, which
|
|
57
|
+
* stops gating the rest of the run.
|
|
51
58
|
*/
|
|
52
59
|
export function ApprovalPrompt({
|
|
53
60
|
pendingApproval,
|
|
@@ -2,6 +2,8 @@ import React from "react";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import type { ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
4
4
|
import { ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
|
+
import { ToolKind, resolveToolKind, normalizeToolResult } from "@stigmer/sdk";
|
|
6
|
+
import type { ToolResultView } from "@stigmer/sdk";
|
|
5
7
|
|
|
6
8
|
/** Props for {@link ToolCallItem}. */
|
|
7
9
|
export interface ToolCallItemProps {
|
|
@@ -17,19 +19,37 @@ const STATUS_INDICATOR: Record<number, { symbol: string; color?: string }> = {
|
|
|
17
19
|
[ToolCallStatus.TOOL_CALL_FAILED]: { symbol: "✗", color: "red" },
|
|
18
20
|
};
|
|
19
21
|
|
|
22
|
+
// Harness-agnostic labels per kind. Classification is shared with the runner,
|
|
23
|
+
// React, and the Go CLI via @stigmer/sdk's resolveToolKind, so Cursor's
|
|
24
|
+
// PascalCase tools render with the same labels as native tools here.
|
|
25
|
+
const KIND_LABEL: Partial<Record<ToolKind, string>> = {
|
|
26
|
+
[ToolKind.FILE_READ]: "Read",
|
|
27
|
+
[ToolKind.FILE_WRITE]: "Write",
|
|
28
|
+
[ToolKind.FILE_EDIT]: "Edit",
|
|
29
|
+
[ToolKind.FILE_DELETE]: "Delete",
|
|
30
|
+
[ToolKind.SHELL]: "Shell",
|
|
31
|
+
[ToolKind.SEARCH]: "Search",
|
|
32
|
+
[ToolKind.LIST]: "List",
|
|
33
|
+
[ToolKind.FETCH]: "Fetch",
|
|
34
|
+
[ToolKind.WEB_SEARCH]: "Web Search",
|
|
35
|
+
[ToolKind.THINK]: "Thinking",
|
|
36
|
+
[ToolKind.TODO]: "Todos",
|
|
37
|
+
[ToolKind.SUBAGENT]: "Sub-agent",
|
|
38
|
+
};
|
|
39
|
+
|
|
20
40
|
/**
|
|
21
|
-
* Renders a single tool call with a status indicator,
|
|
22
|
-
*
|
|
41
|
+
* Renders a single tool call with a status indicator, label, and an optional
|
|
42
|
+
* expanded result. Labels and results come from the shared `@stigmer/sdk` view
|
|
43
|
+
* model, so a terminal session shows the same semantics (diffs, exit codes,
|
|
44
|
+
* match counts) as the web console.
|
|
23
45
|
*/
|
|
24
46
|
export function ToolCallItem({ toolCall, expanded = false }: ToolCallItemProps) {
|
|
25
|
-
const indicator = STATUS_INDICATOR[toolCall.status] ?? {
|
|
26
|
-
symbol: "○",
|
|
27
|
-
};
|
|
47
|
+
const indicator = STATUS_INDICATOR[toolCall.status] ?? { symbol: "○" };
|
|
28
48
|
|
|
29
|
-
const
|
|
30
|
-
const label =
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
const kind = resolveToolKind(toolCall);
|
|
50
|
+
const label = toolLabel(toolCall, kind);
|
|
51
|
+
const view = normalizeToolResult(toolCall);
|
|
52
|
+
const resultText = expanded ? describeResultView(view) : null;
|
|
33
53
|
|
|
34
54
|
return (
|
|
35
55
|
<Box flexDirection="column">
|
|
@@ -40,35 +60,70 @@ export function ToolCallItem({ toolCall, expanded = false }: ToolCallItemProps)
|
|
|
40
60
|
<Text dimColor>running</Text>
|
|
41
61
|
)}
|
|
42
62
|
</Box>
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
<Text dimColor wrap="truncate-end">
|
|
46
|
-
{toolCall.argsPreview}
|
|
47
|
-
</Text>
|
|
48
|
-
</Box>
|
|
49
|
-
)}
|
|
50
|
-
{expanded &&
|
|
51
|
-
toolCall.status === ToolCallStatus.TOOL_CALL_COMPLETED &&
|
|
52
|
-
toolCall.result && (
|
|
63
|
+
{view.type === "error" ? (
|
|
64
|
+
expanded && (
|
|
53
65
|
<Box paddingLeft={3}>
|
|
54
|
-
<Text
|
|
55
|
-
{
|
|
66
|
+
<Text color="red" wrap="truncate-end">
|
|
67
|
+
{view.message}
|
|
56
68
|
</Text>
|
|
57
69
|
</Box>
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
toolCall.error && (
|
|
70
|
+
)
|
|
71
|
+
) : (
|
|
72
|
+
resultText && (
|
|
62
73
|
<Box paddingLeft={3}>
|
|
63
|
-
<Text
|
|
74
|
+
<Text dimColor wrap="truncate-end">
|
|
75
|
+
{resultText}
|
|
76
|
+
</Text>
|
|
64
77
|
</Box>
|
|
65
|
-
)
|
|
78
|
+
)
|
|
79
|
+
)}
|
|
66
80
|
</Box>
|
|
67
81
|
);
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
function toolLabel(toolCall: ToolCall, kind: ToolKind): string {
|
|
85
|
+
if (toolCall.mcpServerSlug) {
|
|
86
|
+
return `${toolCall.mcpServerSlug}/${toolCall.name}`;
|
|
87
|
+
}
|
|
88
|
+
return KIND_LABEL[kind] ?? toolCall.name;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Renders a concise, terminal-friendly description of a normalized result view.
|
|
92
|
+
function describeResultView(view: ToolResultView): string | null {
|
|
93
|
+
switch (view.type) {
|
|
94
|
+
case "diff": {
|
|
95
|
+
const stats =
|
|
96
|
+
view.linesAdded !== undefined || view.linesRemoved !== undefined
|
|
97
|
+
? ` (+${view.linesAdded ?? 0} -${view.linesRemoved ?? 0})`
|
|
98
|
+
: "";
|
|
99
|
+
return `${view.path}${stats}`;
|
|
100
|
+
}
|
|
101
|
+
case "terminal": {
|
|
102
|
+
const exit =
|
|
103
|
+
view.exitCode !== undefined && view.exitCode !== 0 ? `[exit ${view.exitCode}] ` : "";
|
|
104
|
+
return exit + truncate(view.stdout || view.stderr);
|
|
105
|
+
}
|
|
106
|
+
case "search":
|
|
107
|
+
return `${view.count} ${view.count === 1 ? "match" : "matches"}`;
|
|
108
|
+
case "list":
|
|
109
|
+
return `${view.count} ${view.count === 1 ? "item" : "items"}`;
|
|
110
|
+
case "file":
|
|
111
|
+
return view.path || null;
|
|
112
|
+
case "contentBlocks":
|
|
113
|
+
return truncate(view.blocks.map((b) => b.text ?? `[${b.type}]`).join(" "));
|
|
114
|
+
case "text":
|
|
115
|
+
return truncate(view.text);
|
|
116
|
+
case "json":
|
|
117
|
+
return truncate(JSON.stringify(view.value));
|
|
118
|
+
case "error":
|
|
119
|
+
return view.message;
|
|
120
|
+
case "empty":
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function truncate(s: string, maxLines = 5): string {
|
|
126
|
+
const lines = s.split("\n");
|
|
127
|
+
if (lines.length <= maxLines) return s;
|
|
73
128
|
return lines.slice(0, maxLines).join("\n") + `\n... (${lines.length - maxLines} more lines)`;
|
|
74
129
|
}
|