






















@@ -4,21 +4,53 @@ import {
44DEFAULT_APPROVAL_TIMEOUT_MS,
55} from "./bash-tools.exec-runtime.js";
667+const commandExplainerMock = vi.hoisted(() => ({
8+importCount: 0,
9+explainShellCommand: vi.fn(async (command: string): Promise<string> => command),
10+formatCommandSpans: vi.fn((command: string) => {
11+if (command.startsWith("node ")) {
12+return [{ startIndex: 0, endIndex: 4 }];
13+}
14+return [
15+{ startIndex: 0, endIndex: 2 },
16+{ startIndex: 0, endIndex: 4 },
17+{ startIndex: 5, endIndex: 9 },
18+{ startIndex: 20, endIndex: 26 },
19+];
20+}),
21+}));
22+23+vi.mock("../infra/command-explainer/index.js", () => {
24+commandExplainerMock.importCount += 1;
25+return {
26+explainShellCommand: commandExplainerMock.explainShellCommand,
27+formatCommandSpans: commandExplainerMock.formatCommandSpans,
28+};
29+});
30+731vi.mock("./tools/gateway.js", () => ({
832callGatewayTool: vi.fn(),
933}));
10341135let callGatewayTool: typeof import("./tools/gateway.js").callGatewayTool;
1236let requestExecApprovalDecision: typeof import("./bash-tools.exec-approval-request.js").requestExecApprovalDecision;
37+let registerExecApprovalRequestForHost: typeof import("./bash-tools.exec-approval-request.js").registerExecApprovalRequestForHost;
13381439describe("requestExecApprovalDecision", () => {
1540beforeAll(async () => {
1641({ callGatewayTool } = await import("./tools/gateway.js"));
17-({ requestExecApprovalDecision } = await import("./bash-tools.exec-approval-request.js"));
42+({ requestExecApprovalDecision, registerExecApprovalRequestForHost } =
43+await import("./bash-tools.exec-approval-request.js"));
1844});
19452046beforeEach(() => {
2147vi.mocked(callGatewayTool).mockClear();
48+commandExplainerMock.explainShellCommand.mockClear();
49+commandExplainerMock.formatCommandSpans.mockClear();
50+});
51+52+it("does not load the command explainer when importing approval requests", () => {
53+expect(commandExplainerMock.importCount).toBe(0);
2254});
23552456it("returns string decisions", async () => {
@@ -174,4 +206,79 @@ describe("requestExecApprovalDecision", () => {
174206expect(result).toBe("deny");
175207expect(vi.mocked(callGatewayTool).mock.calls).toHaveLength(1);
176208});
209+210+it("adds command spans to host approval registration payloads", async () => {
211+vi.mocked(callGatewayTool).mockResolvedValue({ id: "approval-id", expiresAtMs: 1234 });
212+213+await registerExecApprovalRequestForHost({
214+approvalId: "approval-id",
215+command: 'ls | grep "stuff" | python -c \'print("hi")\'',
216+workdir: "/tmp/project",
217+host: "node",
218+security: "allowlist",
219+ask: "always",
220+});
221+222+expect(callGatewayTool).toHaveBeenCalledWith(
223+"exec.approval.request",
224+expect.anything(),
225+expect.objectContaining({
226+commandSpans: expect.arrayContaining([
227+{ startIndex: 0, endIndex: 2 },
228+{ startIndex: 5, endIndex: 9 },
229+{ startIndex: 20, endIndex: 26 },
230+]),
231+}),
232+expect.anything(),
233+);
234+});
235+236+it("uses system run plan command text for host approval explanations", async () => {
237+vi.mocked(callGatewayTool).mockResolvedValue({ id: "approval-id", expiresAtMs: 1234 });
238+239+await registerExecApprovalRequestForHost({
240+approvalId: "approval-id",
241+systemRunPlan: {
242+argv: ["node", "-e", "console.log(1)"],
243+cwd: "/tmp/project",
244+commandText: 'node -e "console.log(1)"',
245+agentId: null,
246+sessionKey: null,
247+},
248+workdir: "/tmp/project",
249+host: "node",
250+security: "allowlist",
251+ask: "always",
252+});
253+254+expect(callGatewayTool).toHaveBeenCalledWith(
255+"exec.approval.request",
256+expect.anything(),
257+expect.objectContaining({
258+commandSpans: expect.arrayContaining([{ startIndex: 0, endIndex: 4 }]),
259+}),
260+expect.anything(),
261+);
262+});
263+264+it("keeps explicit command spans", async () => {
265+vi.mocked(callGatewayTool).mockResolvedValue({ id: "approval-id", expiresAtMs: 1234 });
266+267+await registerExecApprovalRequestForHost({
268+approvalId: "approval-id",
269+command: "echo hi",
270+commandSpans: [{ startIndex: 0, endIndex: 4 }],
271+workdir: "/tmp/project",
272+host: "node",
273+security: "allowlist",
274+ask: "always",
275+});
276+277+expect(callGatewayTool).toHaveBeenCalledWith(
278+"exec.approval.request",
279+expect.anything(),
280+expect.objectContaining({ commandSpans: [{ startIndex: 0, endIndex: 4 }] }),
281+expect.anything(),
282+);
283+});
177284});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。