@stigmer/ink 3.0.9-dev.20260615153829 → 3.1.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 -2
- package/components/ApprovalPrompt.d.ts.map +1 -1
- package/components/ApprovalPrompt.js +68 -35
- package/components/ApprovalPrompt.js.map +1 -1
- package/components/ToolCallItem.js +15 -5
- package/components/ToolCallItem.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/components.test.tsx +18 -3
- package/src/components/ApprovalPrompt.tsx +93 -35
- package/src/components/ToolCallItem.tsx +16 -5
|
@@ -15,8 +15,10 @@ export interface ApprovalPromptProps {
|
|
|
15
15
|
* Displays the tool name and args preview, then presents
|
|
16
16
|
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
17
17
|
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
18
|
-
* selection.
|
|
19
|
-
*
|
|
18
|
+
* selection. The approve-all option (a) maps to APPROVE_ALL, which grants a
|
|
19
|
+
* run-lifetime lease scoped to the clicked tool's CLASS (its MCP server, or its
|
|
20
|
+
* file edit / delete / shell category) — other classes keep prompting. The
|
|
21
|
+
* option label names that class so the scope is never a surprise.
|
|
20
22
|
*/
|
|
21
23
|
export declare function ApprovalPrompt({ pendingApproval, onSubmit, isSubmitting, }: ApprovalPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
22
24
|
//# 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;
|
|
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;AAO9F,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;AAuCD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC7B,eAAe,EACf,QAAQ,EACR,YAAoB,GACrB,EAAE,mBAAmB,2CAmIrB"}
|
|
@@ -1,60 +1,89 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
4
|
import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
5
|
+
import { ToolKind, resolveToolKindByName, describeApprovalPolicySource, } from "@stigmer/sdk";
|
|
6
|
+
/**
|
|
7
|
+
* Builds the truthful APPROVE_ALL option label for a pending approval's lease
|
|
8
|
+
* class — the terminal mirror of the React card's buildApproveAllLabel.
|
|
9
|
+
*
|
|
10
|
+
* APPROVE_ALL grants a run-lifetime lease scoped to ONE class: the MCP server
|
|
11
|
+
* for an MCP tool, otherwise the approval category (write/delete/shell), where
|
|
12
|
+
* FILE_WRITE and FILE_EDIT collapse to one "file edits" class exactly as the
|
|
13
|
+
* runner's toolApprovalCategory collapses them. The label names that class so it
|
|
14
|
+
* never implies a broader, run-wide bypass.
|
|
15
|
+
*/
|
|
16
|
+
function buildApproveAllLabel(toolName, mcpServerSlug) {
|
|
17
|
+
if (mcpServerSlug) {
|
|
18
|
+
return `Approve all ${mcpServerSlug} tools`;
|
|
19
|
+
}
|
|
20
|
+
switch (resolveToolKindByName(toolName, mcpServerSlug)) {
|
|
21
|
+
case ToolKind.SHELL:
|
|
22
|
+
return "Approve all shell commands";
|
|
23
|
+
case ToolKind.FILE_DELETE:
|
|
24
|
+
return "Approve all file deletions";
|
|
25
|
+
case ToolKind.FILE_WRITE:
|
|
26
|
+
case ToolKind.FILE_EDIT:
|
|
27
|
+
return "Approve all file edits";
|
|
28
|
+
default:
|
|
29
|
+
return "Approve all of this kind";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
31
32
|
/**
|
|
32
33
|
* HITL approval prompt for tool call authorization.
|
|
33
34
|
*
|
|
34
35
|
* Displays the tool name and args preview, then presents
|
|
35
36
|
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
36
37
|
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
37
|
-
* selection.
|
|
38
|
-
*
|
|
38
|
+
* selection. The approve-all option (a) maps to APPROVE_ALL, which grants a
|
|
39
|
+
* run-lifetime lease scoped to the clicked tool's CLASS (its MCP server, or its
|
|
40
|
+
* file edit / delete / shell category) — other classes keep prompting. The
|
|
41
|
+
* option label names that class so the scope is never a surprise.
|
|
39
42
|
*/
|
|
40
43
|
export function ApprovalPrompt({ pendingApproval, onSubmit, isSubmitting = false, }) {
|
|
41
44
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
45
|
+
const options = useMemo(() => [
|
|
46
|
+
{
|
|
47
|
+
label: "Approve",
|
|
48
|
+
action: ApprovalAction.APPROVE,
|
|
49
|
+
color: "green",
|
|
50
|
+
shortcut: "y",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: buildApproveAllLabel(pendingApproval.toolName, pendingApproval.mcpServerSlug),
|
|
54
|
+
action: ApprovalAction.APPROVE_ALL,
|
|
55
|
+
color: "green",
|
|
56
|
+
shortcut: "a",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Reject",
|
|
60
|
+
action: ApprovalAction.REJECT,
|
|
61
|
+
color: "red",
|
|
62
|
+
shortcut: "n",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "Skip",
|
|
66
|
+
action: ApprovalAction.SKIP,
|
|
67
|
+
color: "yellow",
|
|
68
|
+
shortcut: "s",
|
|
69
|
+
},
|
|
70
|
+
], [pendingApproval.toolName, pendingApproval.mcpServerSlug]);
|
|
42
71
|
useInput((input, key) => {
|
|
43
72
|
if (isSubmitting)
|
|
44
73
|
return;
|
|
45
74
|
if (key.upArrow || key.leftArrow) {
|
|
46
|
-
setSelectedIndex((i) => (i > 0 ? i - 1 :
|
|
75
|
+
setSelectedIndex((i) => (i > 0 ? i - 1 : options.length - 1));
|
|
47
76
|
}
|
|
48
77
|
else if (key.downArrow || key.rightArrow) {
|
|
49
|
-
setSelectedIndex((i) => (i <
|
|
78
|
+
setSelectedIndex((i) => (i < options.length - 1 ? i + 1 : 0));
|
|
50
79
|
}
|
|
51
80
|
else if (key.return) {
|
|
52
|
-
onSubmit(
|
|
81
|
+
onSubmit(options[selectedIndex].action);
|
|
53
82
|
}
|
|
54
83
|
else {
|
|
55
|
-
const shortcutMatch =
|
|
84
|
+
const shortcutMatch = options.findIndex((o) => o.shortcut === input.toLowerCase());
|
|
56
85
|
if (shortcutMatch >= 0) {
|
|
57
|
-
onSubmit(
|
|
86
|
+
onSubmit(options[shortcutMatch].action);
|
|
58
87
|
}
|
|
59
88
|
}
|
|
60
89
|
});
|
|
@@ -62,6 +91,10 @@ export function ApprovalPrompt({ pendingApproval, onSubmit, isSubmitting = false
|
|
|
62
91
|
const toolLabel = serverSlug
|
|
63
92
|
? `${serverSlug}/${pendingApproval.toolName}`
|
|
64
93
|
: pendingApproval.toolName;
|
|
65
|
-
|
|
94
|
+
// Why-gated: the authorization provenance the server projected onto the
|
|
95
|
+
// PendingApproval. Mirrors the React card's gate-reason line so the terminal
|
|
96
|
+
// surface explains the gate at parity. Empty for legacy executions.
|
|
97
|
+
const gateReason = describeApprovalPolicySource(pendingApproval.approvalPolicySource);
|
|
98
|
+
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingTop: 1, paddingBottom: 1, borderStyle: "round", borderColor: "yellow", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "yellow", bold: true, children: "\u26A0 Approval required" }), pendingApproval.fromSubAgent && (_jsxs(Text, { dimColor: true, children: ["via ", pendingApproval.subAgentSubject || pendingApproval.subAgentName] }))] }), _jsxs(Box, { paddingLeft: 2, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Tool:" }), _jsx(Text, { bold: true, children: toolLabel })] }), pendingApproval.argsPreview && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Args:" }), _jsx(Text, { wrap: "truncate-end", children: pendingApproval.argsPreview })] })), gateReason && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "Why:" }), _jsx(Text, { dimColor: true, italic: true, children: gateReason })] }))] }), _jsx(Box, { gap: 2, marginTop: 1, paddingLeft: 2, children: options.map((opt, idx) => (_jsxs(Text, { color: idx === selectedIndex ? opt.color : undefined, dimColor: idx !== selectedIndex, bold: idx === selectedIndex, children: [idx === selectedIndex ? "▸ " : " ", "[", opt.shortcut, "] ", opt.label] }, opt.shortcut))) }), isSubmitting && (_jsx(Box, { paddingLeft: 2, marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Submitting..." }) }))] }));
|
|
66
99
|
}
|
|
67
100
|
//# sourceMappingURL=ApprovalPrompt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApprovalPrompt.js","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ApprovalPrompt.js","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"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"}
|
|
@@ -54,11 +54,18 @@ function describeResultView(view) {
|
|
|
54
54
|
return `${view.path}${stats}`;
|
|
55
55
|
}
|
|
56
56
|
case "terminal": {
|
|
57
|
+
// Lead with the command prompt line so the CLI reads as one terminal
|
|
58
|
+
// session, matching the web console.
|
|
59
|
+
const prompt = view.command ? `$ ${view.command}\n` : "";
|
|
57
60
|
const exit = view.exitCode !== undefined && view.exitCode !== 0 ? `[exit ${view.exitCode}] ` : "";
|
|
58
|
-
return exit + truncate(view.stdout || view.stderr);
|
|
61
|
+
return prompt + exit + truncate(view.stdout || view.stderr);
|
|
62
|
+
}
|
|
63
|
+
case "search": {
|
|
64
|
+
const noun = view.kind === "files"
|
|
65
|
+
? view.count === 1 ? "file" : "files"
|
|
66
|
+
: view.count === 1 ? "match" : "matches";
|
|
67
|
+
return `${view.count} ${noun}${view.truncated ? " (truncated)" : ""}`;
|
|
59
68
|
}
|
|
60
|
-
case "search":
|
|
61
|
-
return `${view.count} ${view.count === 1 ? "match" : "matches"}`;
|
|
62
69
|
case "list":
|
|
63
70
|
return `${view.count} ${view.count === 1 ? "item" : "items"}`;
|
|
64
71
|
case "file":
|
|
@@ -70,11 +77,14 @@ function describeResultView(view) {
|
|
|
70
77
|
case "json":
|
|
71
78
|
return truncate(JSON.stringify(view.value));
|
|
72
79
|
case "outputRef": {
|
|
80
|
+
// The full bytes were offloaded to artifact storage. A terminal formatter
|
|
81
|
+
// can't fetch them inline, so we surface the stable storage key rather
|
|
82
|
+
// than a baked URL (which expired after an hour and is no longer present).
|
|
73
83
|
const size = view.sizeBytes ? ` (${formatBytes(view.sizeBytes)})` : "";
|
|
74
84
|
if (view.isImage)
|
|
75
|
-
return `image${size}
|
|
85
|
+
return `[image output${size}] ${view.storageKey}`;
|
|
76
86
|
const preview = view.preview ? `${truncate(view.preview)}\n` : "";
|
|
77
|
-
return `${preview}full output${size}
|
|
87
|
+
return `${preview}[full output offloaded${size}] ${view.storageKey}`;
|
|
78
88
|
}
|
|
79
89
|
case "error":
|
|
80
90
|
return view.message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolCallItem.js","sourceRoot":"","sources":["../../src/components/ToolCallItem.tsx"],"names":[],"mappings":"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
|
|
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.0
|
|
3
|
+
"version": "3.1.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",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@connectrpc/connect-node": "^2.1.1",
|
|
38
|
-
"@stigmer/react": "3.0
|
|
38
|
+
"@stigmer/react": "3.1.0",
|
|
39
39
|
"ink-spinner": "^5.0.0",
|
|
40
40
|
"ink-text-input": "^6.0.0",
|
|
41
41
|
"marked": "^15.0.7",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@bufbuild/protobuf": "^2.0.0",
|
|
46
|
-
"@stigmer/protos": "3.0
|
|
47
|
-
"@stigmer/sdk": "3.0
|
|
46
|
+
"@stigmer/protos": "3.1.0",
|
|
47
|
+
"@stigmer/sdk": "3.1.0",
|
|
48
48
|
"ink": "^7.0.0",
|
|
49
49
|
"react": ">=19.0.0"
|
|
50
50
|
}
|
|
@@ -4,7 +4,7 @@ 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, ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
7
|
+
import { MessageType, ExecutionPhase, ToolCallStatus, ApprovalAction, ApprovalPolicySource } 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
9
|
import { PendingApprovalSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
10
10
|
import { MessageEntry } from "../components/MessageEntry.js";
|
|
@@ -164,7 +164,7 @@ describe("ToolCallItem", () => {
|
|
|
164
164
|
});
|
|
165
165
|
|
|
166
166
|
describe("ApprovalPrompt", () => {
|
|
167
|
-
it("offers Approve,
|
|
167
|
+
it("offers Approve, scope-truthful approve-all, Reject, and Skip", () => {
|
|
168
168
|
const pending = create(PendingApprovalSchema);
|
|
169
169
|
pending.toolCallId = "tc-1";
|
|
170
170
|
pending.toolName = "write_file";
|
|
@@ -174,11 +174,26 @@ describe("ApprovalPrompt", () => {
|
|
|
174
174
|
);
|
|
175
175
|
const output = lastFrame() ?? "";
|
|
176
176
|
expect(output).toContain("[y] Approve");
|
|
177
|
-
|
|
177
|
+
// write_file leases the "file edits" class — the label names that class.
|
|
178
|
+
expect(output).toContain("[a] Approve all file edits");
|
|
178
179
|
expect(output).toContain("[n] Reject");
|
|
179
180
|
expect(output).toContain("[s] Skip");
|
|
180
181
|
});
|
|
181
182
|
|
|
183
|
+
it("renders the why-gated line from the projected approval_policy_source", () => {
|
|
184
|
+
const pending = create(PendingApprovalSchema);
|
|
185
|
+
pending.toolCallId = "tc-1";
|
|
186
|
+
pending.toolName = "write_file";
|
|
187
|
+
pending.approvalPolicySource = ApprovalPolicySource.AGENT_OVERRIDE;
|
|
188
|
+
|
|
189
|
+
const { lastFrame } = render(
|
|
190
|
+
<ApprovalPrompt pendingApproval={pending} onSubmit={() => {}} />,
|
|
191
|
+
);
|
|
192
|
+
const output = lastFrame() ?? "";
|
|
193
|
+
expect(output).toContain("Why:");
|
|
194
|
+
expect(output).toContain("required by agent override");
|
|
195
|
+
});
|
|
196
|
+
|
|
182
197
|
it("submits APPROVE_ALL when the 'a' shortcut is pressed", () => {
|
|
183
198
|
const pending = create(PendingApprovalSchema);
|
|
184
199
|
pending.toolCallId = "tc-1";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useMemo, useState } from "react";
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import type { PendingApproval } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
|
|
4
4
|
import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
|
+
import {
|
|
6
|
+
ToolKind,
|
|
7
|
+
resolveToolKindByName,
|
|
8
|
+
describeApprovalPolicySource,
|
|
9
|
+
} from "@stigmer/sdk";
|
|
5
10
|
|
|
6
11
|
/** Props for {@link ApprovalPrompt}. */
|
|
7
12
|
export interface ApprovalPromptProps {
|
|
@@ -20,32 +25,35 @@ interface ActionOption {
|
|
|
20
25
|
readonly shortcut: string;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Builds the truthful APPROVE_ALL option label for a pending approval's lease
|
|
30
|
+
* class — the terminal mirror of the React card's buildApproveAllLabel.
|
|
31
|
+
*
|
|
32
|
+
* APPROVE_ALL grants a run-lifetime lease scoped to ONE class: the MCP server
|
|
33
|
+
* for an MCP tool, otherwise the approval category (write/delete/shell), where
|
|
34
|
+
* FILE_WRITE and FILE_EDIT collapse to one "file edits" class exactly as the
|
|
35
|
+
* runner's toolApprovalCategory collapses them. The label names that class so it
|
|
36
|
+
* never implies a broader, run-wide bypass.
|
|
37
|
+
*/
|
|
38
|
+
function buildApproveAllLabel(
|
|
39
|
+
toolName: string,
|
|
40
|
+
mcpServerSlug: string,
|
|
41
|
+
): string {
|
|
42
|
+
if (mcpServerSlug) {
|
|
43
|
+
return `Approve all ${mcpServerSlug} tools`;
|
|
44
|
+
}
|
|
45
|
+
switch (resolveToolKindByName(toolName, mcpServerSlug)) {
|
|
46
|
+
case ToolKind.SHELL:
|
|
47
|
+
return "Approve all shell commands";
|
|
48
|
+
case ToolKind.FILE_DELETE:
|
|
49
|
+
return "Approve all file deletions";
|
|
50
|
+
case ToolKind.FILE_WRITE:
|
|
51
|
+
case ToolKind.FILE_EDIT:
|
|
52
|
+
return "Approve all file edits";
|
|
53
|
+
default:
|
|
54
|
+
return "Approve all of this kind";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
49
57
|
|
|
50
58
|
/**
|
|
51
59
|
* HITL approval prompt for tool call authorization.
|
|
@@ -53,8 +61,10 @@ const OPTIONS: readonly ActionOption[] = [
|
|
|
53
61
|
* Displays the tool name and args preview, then presents
|
|
54
62
|
* Approve/Approve-all/Reject/Skip options navigable via arrow keys
|
|
55
63
|
* or shortcut keys (y/a/n/s). Press Enter to confirm the highlighted
|
|
56
|
-
* selection.
|
|
57
|
-
*
|
|
64
|
+
* selection. The approve-all option (a) maps to APPROVE_ALL, which grants a
|
|
65
|
+
* run-lifetime lease scoped to the clicked tool's CLASS (its MCP server, or its
|
|
66
|
+
* file edit / delete / shell category) — other classes keep prompting. The
|
|
67
|
+
* option label names that class so the scope is never a surprise.
|
|
58
68
|
*/
|
|
59
69
|
export function ApprovalPrompt({
|
|
60
70
|
pendingApproval,
|
|
@@ -63,22 +73,55 @@ export function ApprovalPrompt({
|
|
|
63
73
|
}: ApprovalPromptProps) {
|
|
64
74
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
65
75
|
|
|
76
|
+
const options = useMemo<readonly ActionOption[]>(
|
|
77
|
+
() => [
|
|
78
|
+
{
|
|
79
|
+
label: "Approve",
|
|
80
|
+
action: ApprovalAction.APPROVE,
|
|
81
|
+
color: "green",
|
|
82
|
+
shortcut: "y",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
label: buildApproveAllLabel(
|
|
86
|
+
pendingApproval.toolName,
|
|
87
|
+
pendingApproval.mcpServerSlug,
|
|
88
|
+
),
|
|
89
|
+
action: ApprovalAction.APPROVE_ALL,
|
|
90
|
+
color: "green",
|
|
91
|
+
shortcut: "a",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: "Reject",
|
|
95
|
+
action: ApprovalAction.REJECT,
|
|
96
|
+
color: "red",
|
|
97
|
+
shortcut: "n",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: "Skip",
|
|
101
|
+
action: ApprovalAction.SKIP,
|
|
102
|
+
color: "yellow",
|
|
103
|
+
shortcut: "s",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
[pendingApproval.toolName, pendingApproval.mcpServerSlug],
|
|
107
|
+
);
|
|
108
|
+
|
|
66
109
|
useInput(
|
|
67
110
|
(input, key) => {
|
|
68
111
|
if (isSubmitting) return;
|
|
69
112
|
|
|
70
113
|
if (key.upArrow || key.leftArrow) {
|
|
71
|
-
setSelectedIndex((i) => (i > 0 ? i - 1 :
|
|
114
|
+
setSelectedIndex((i) => (i > 0 ? i - 1 : options.length - 1));
|
|
72
115
|
} else if (key.downArrow || key.rightArrow) {
|
|
73
|
-
setSelectedIndex((i) => (i <
|
|
116
|
+
setSelectedIndex((i) => (i < options.length - 1 ? i + 1 : 0));
|
|
74
117
|
} else if (key.return) {
|
|
75
|
-
onSubmit(
|
|
118
|
+
onSubmit(options[selectedIndex].action);
|
|
76
119
|
} else {
|
|
77
|
-
const shortcutMatch =
|
|
120
|
+
const shortcutMatch = options.findIndex(
|
|
78
121
|
(o) => o.shortcut === input.toLowerCase(),
|
|
79
122
|
);
|
|
80
123
|
if (shortcutMatch >= 0) {
|
|
81
|
-
onSubmit(
|
|
124
|
+
onSubmit(options[shortcutMatch].action);
|
|
82
125
|
}
|
|
83
126
|
}
|
|
84
127
|
},
|
|
@@ -89,6 +132,13 @@ export function ApprovalPrompt({
|
|
|
89
132
|
? `${serverSlug}/${pendingApproval.toolName}`
|
|
90
133
|
: pendingApproval.toolName;
|
|
91
134
|
|
|
135
|
+
// Why-gated: the authorization provenance the server projected onto the
|
|
136
|
+
// PendingApproval. Mirrors the React card's gate-reason line so the terminal
|
|
137
|
+
// surface explains the gate at parity. Empty for legacy executions.
|
|
138
|
+
const gateReason = describeApprovalPolicySource(
|
|
139
|
+
pendingApproval.approvalPolicySource,
|
|
140
|
+
);
|
|
141
|
+
|
|
92
142
|
return (
|
|
93
143
|
<Box
|
|
94
144
|
flexDirection="column"
|
|
@@ -120,10 +170,18 @@ export function ApprovalPrompt({
|
|
|
120
170
|
<Text wrap="truncate-end">{pendingApproval.argsPreview}</Text>
|
|
121
171
|
</Box>
|
|
122
172
|
)}
|
|
173
|
+
{gateReason && (
|
|
174
|
+
<Box gap={1}>
|
|
175
|
+
<Text dimColor>Why:</Text>
|
|
176
|
+
<Text dimColor italic>
|
|
177
|
+
{gateReason}
|
|
178
|
+
</Text>
|
|
179
|
+
</Box>
|
|
180
|
+
)}
|
|
123
181
|
</Box>
|
|
124
182
|
|
|
125
183
|
<Box gap={2} marginTop={1} paddingLeft={2}>
|
|
126
|
-
{
|
|
184
|
+
{options.map((opt, idx) => (
|
|
127
185
|
<Text
|
|
128
186
|
key={opt.shortcut}
|
|
129
187
|
color={idx === selectedIndex ? opt.color : undefined}
|
|
@@ -99,12 +99,20 @@ function describeResultView(view: ToolResultView): string | null {
|
|
|
99
99
|
return `${view.path}${stats}`;
|
|
100
100
|
}
|
|
101
101
|
case "terminal": {
|
|
102
|
+
// Lead with the command prompt line so the CLI reads as one terminal
|
|
103
|
+
// session, matching the web console.
|
|
104
|
+
const prompt = view.command ? `$ ${view.command}\n` : "";
|
|
102
105
|
const exit =
|
|
103
106
|
view.exitCode !== undefined && view.exitCode !== 0 ? `[exit ${view.exitCode}] ` : "";
|
|
104
|
-
return exit + truncate(view.stdout || view.stderr);
|
|
107
|
+
return prompt + exit + truncate(view.stdout || view.stderr);
|
|
108
|
+
}
|
|
109
|
+
case "search": {
|
|
110
|
+
const noun =
|
|
111
|
+
view.kind === "files"
|
|
112
|
+
? view.count === 1 ? "file" : "files"
|
|
113
|
+
: view.count === 1 ? "match" : "matches";
|
|
114
|
+
return `${view.count} ${noun}${view.truncated ? " (truncated)" : ""}`;
|
|
105
115
|
}
|
|
106
|
-
case "search":
|
|
107
|
-
return `${view.count} ${view.count === 1 ? "match" : "matches"}`;
|
|
108
116
|
case "list":
|
|
109
117
|
return `${view.count} ${view.count === 1 ? "item" : "items"}`;
|
|
110
118
|
case "file":
|
|
@@ -116,10 +124,13 @@ function describeResultView(view: ToolResultView): string | null {
|
|
|
116
124
|
case "json":
|
|
117
125
|
return truncate(JSON.stringify(view.value));
|
|
118
126
|
case "outputRef": {
|
|
127
|
+
// The full bytes were offloaded to artifact storage. A terminal formatter
|
|
128
|
+
// can't fetch them inline, so we surface the stable storage key rather
|
|
129
|
+
// than a baked URL (which expired after an hour and is no longer present).
|
|
119
130
|
const size = view.sizeBytes ? ` (${formatBytes(view.sizeBytes)})` : "";
|
|
120
|
-
if (view.isImage) return `image${size}
|
|
131
|
+
if (view.isImage) return `[image output${size}] ${view.storageKey}`;
|
|
121
132
|
const preview = view.preview ? `${truncate(view.preview)}\n` : "";
|
|
122
|
-
return `${preview}full output${size}
|
|
133
|
+
return `${preview}[full output offloaded${size}] ${view.storageKey}`;
|
|
123
134
|
}
|
|
124
135
|
case "error":
|
|
125
136
|
return view.message;
|