fix(approvals): suppress manual native approval narration

This commit is contained in:
Vincent Koc
2026-04-01 06:18:55 +09:00
parent 211b5a51af
commit b73dd9b326
2 changed files with 39 additions and 1 deletions

View File

@@ -159,6 +159,34 @@ describe("buildAgentSystemPrompt", () => {
);
});
it("keeps manual /approve instructions for non-native approval channels", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/openclaw",
runtimeInfo: { channel: "signal" },
});
expect(prompt).toContain(
"When exec returns approval-pending, include the concrete /approve command from tool output",
);
});
it("tells native approval channels not to duplicate plain chat /approve instructions", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/openclaw",
runtimeInfo: { channel: "telegram" },
});
expect(prompt).toContain(
"When exec returns approval-pending on Discord, Slack, or Telegram, rely on the native approval card/buttons when they appear",
);
expect(prompt).toContain(
"Only include the concrete /approve command if the tool result says chat approvals are unavailable or only manual approval is possible.",
);
expect(prompt).not.toContain(
"When exec returns approval-pending, include the concrete /approve command from tool output",
);
});
it("omits skills in minimal prompt mode when skillsPrompt is absent", () => {
const prompt = buildAgentSystemPrompt({
workspaceDir: "/tmp/openclaw",

View File

@@ -173,6 +173,14 @@ function buildDocsSection(params: { docsPath?: string; isMinimal: boolean; readT
];
}
function buildExecApprovalPromptGuidance(params: { runtimeChannel?: string }) {
const runtimeChannel = params.runtimeChannel?.trim().toLowerCase();
if (runtimeChannel === "discord" || runtimeChannel === "slack" || runtimeChannel === "telegram") {
return "When exec returns approval-pending on Discord, Slack, or Telegram, rely on the native approval card/buttons when they appear and do not also send plain chat /approve instructions. Only include the concrete /approve command if the tool result says chat approvals are unavailable or only manual approval is possible.";
}
return "When exec returns approval-pending, include the concrete /approve command from tool output (with allow-once|allow-always|deny) as plain chat text for the user, and do not ask for a different or rotated code.";
}
export function buildAgentSystemPrompt(params: {
workspaceDir: string;
defaultThinkLevel?: ThinkLevel;
@@ -450,7 +458,9 @@ export function buildAgentSystemPrompt(params: {
"Keep narration brief and value-dense; avoid repeating obvious steps.",
"Use plain human language for narration unless in a technical context.",
"When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI or slash commands.",
"When exec returns approval-pending, include the concrete /approve command from tool output (with allow-once|allow-always|deny) as plain chat text for the user, and do not ask for a different or rotated code.",
buildExecApprovalPromptGuidance({
runtimeChannel: params.runtimeInfo?.channel,
}),
"Never execute /approve through exec or any other shell/tool path; /approve is a user-facing approval command, not a shell command.",
"Treat allow-once as single-command only: if another elevated command needs approval, request a fresh /approve and do not claim prior approval covered it.",
"When approvals are required, preserve and show the full command/script exactly as provided (including chained operators like &&, ||, |, ;, or multiline shells) so the user can approve what will actually run.",