Skip to content

Commit 635555a

Browse files
committed
fix(exec): distinguish one-shot from ask=always in approval prompt
When Allow Always is unavailable because a command is one-shot (e.g. shell redirection), show the correct reason instead of falsely claiming the effective policy requires approval every time. Closes #97069
1 parent 4c4396c commit 635555a

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

src/infra/exec-approval-reply.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,20 @@ describe("exec approval reply helpers", () => {
368368
expect(payload.interactive).toBeUndefined();
369369
});
370370

371+
it("shows one-shot reason when allow-always excluded but ask is not always", () => {
372+
const payload = buildExecApprovalPendingReplyPayload({
373+
approvalId: "req-one-shot",
374+
approvalSlug: "slug-one-shot",
375+
ask: "on-miss",
376+
command: "openclaw --version 2>&1",
377+
host: "gateway",
378+
allowedDecisions: ["allow-once", "deny"],
379+
});
380+
381+
expect(payload.text).toContain("cannot be saved for future use");
382+
expect(payload.text).not.toContain("requires approval every time");
383+
});
384+
371385
it("stores agent and session metadata for downstream suppression checks", () => {
372386
const payload = buildExecApprovalPendingReplyPayload({
373387
approvalId: "req-meta",

src/infra/exec-approval-reply.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,11 @@ export function buildExecApprovalPendingReplyPayload(
376376
lines.push(secondaryFence);
377377
}
378378
if (!allowedDecisions.includes("allow-always")) {
379-
lines.push(
380-
"The effective approval policy requires approval every time, so Allow Always is unavailable.",
381-
);
379+
const reason =
380+
params.ask === "always"
381+
? "The effective approval policy requires approval every time, so Allow Always is unavailable."
382+
: "Allow Always is unavailable because this command cannot be saved for future use (e.g., shell redirection or one-shot arguments).";
383+
lines.push(reason);
382384
}
383385
const info: string[] = [];
384386
info.push(`Host: ${params.host}`);

0 commit comments

Comments
 (0)