From 2c8b9210548da43a5045ffc1b938cfa2663b21ef Mon Sep 17 00:00:00 2001 From: Vai Date: Sat, 14 Feb 2026 22:28:58 -0500 Subject: [PATCH] feat: add messages.suppressToolErrors config option (#16620) Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: 9ae4394b81bf0a68a5dfbcfe39fb60a21689ac49 Co-authored-by: vai-oro <258511217+vai-oro@users.noreply.github.com> Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com> Reviewed-by: @sebslight --- CHANGELOG.md | 1 + .../run/payloads.e2e.test.ts | 36 +++++++++++++++++++ src/agents/pi-embedded-runner/run/payloads.ts | 4 ++- src/config/schema.help.ts | 2 ++ src/config/schema.labels.ts | 1 + src/config/types.messages.ts | 2 ++ src/config/zod-schema.session.ts | 1 + 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88188f91250..1b38013ffce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai - Discord: allow exec approval prompts to target channels or both DM+channel via `channels.discord.execApprovals.target`. (#16051) Thanks @leonnardo. - Sandbox: add `sandbox.browser.binds` to configure browser-container bind mounts separately from exec containers. (#16230) Thanks @seheepeak. - Discord: add debug logging for message routing decisions to improve `--debug` tracing. (#16202) Thanks @jayleekr. +- Agents: add optional `messages.suppressToolErrors` config to hide non-mutating tool-failure warnings from user-facing chat while still surfacing mutating failures. (#16620) Thanks @vai-oro. ### Fixes diff --git a/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts b/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts index f9fd438e056..b169ddbf774 100644 --- a/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts +++ b/src/agents/pi-embedded-runner/run/payloads.e2e.test.ts @@ -278,6 +278,42 @@ describe("buildEmbeddedRunPayloads", () => { expect(payloads).toHaveLength(0); }); + it("suppresses non-mutating non-recoverable tool errors when messages.suppressToolErrors is enabled", () => { + const payloads = buildEmbeddedRunPayloads({ + assistantTexts: [], + toolMetas: [], + lastAssistant: undefined, + lastToolError: { toolName: "browser", error: "connection timeout" }, + config: { messages: { suppressToolErrors: true } }, + sessionKey: "session:telegram", + inlineToolResultsAllowed: false, + verboseLevel: "off", + reasoningLevel: "off", + toolResultFormat: "plain", + }); + + expect(payloads).toHaveLength(0); + }); + + it("still shows mutating tool errors when messages.suppressToolErrors is enabled", () => { + const payloads = buildEmbeddedRunPayloads({ + assistantTexts: [], + toolMetas: [], + lastAssistant: undefined, + lastToolError: { toolName: "write", error: "connection timeout" }, + config: { messages: { suppressToolErrors: true } }, + sessionKey: "session:telegram", + inlineToolResultsAllowed: false, + verboseLevel: "off", + reasoningLevel: "off", + toolResultFormat: "plain", + }); + + expect(payloads).toHaveLength(1); + expect(payloads[0]?.isError).toBe(true); + expect(payloads[0]?.text).toContain("connection timeout"); + }); + it("shows recoverable tool errors for mutating tools", () => { const payloads = buildEmbeddedRunPayloads({ assistantTexts: [], diff --git a/src/agents/pi-embedded-runner/run/payloads.ts b/src/agents/pi-embedded-runner/run/payloads.ts index ad5d99d0a8b..28dc9613c9a 100644 --- a/src/agents/pi-embedded-runner/run/payloads.ts +++ b/src/agents/pi-embedded-runner/run/payloads.ts @@ -233,7 +233,9 @@ export function buildEmbeddedRunPayloads(params: { const isMutatingToolError = params.lastToolError.mutatingAction ?? isLikelyMutatingToolName(params.lastToolError.toolName); - const shouldShowToolError = isMutatingToolError || (!hasUserFacingReply && !isRecoverableError); + const shouldShowToolError = + isMutatingToolError || + (!hasUserFacingReply && !isRecoverableError && !params.config?.messages?.suppressToolErrors); // Always surface mutating tool failures so we do not silently confirm actions that did not happen. // Otherwise, keep the previous behavior and only surface non-recoverable failures when no reply exists. diff --git a/src/config/schema.help.ts b/src/config/schema.help.ts index 6bab5950a32..288711004dc 100644 --- a/src/config/schema.help.ts +++ b/src/config/schema.help.ts @@ -325,6 +325,8 @@ export const FIELD_HELP: Record = { "Max reply-back turns between requester and target (0–5).", "channels.telegram.customCommands": "Additional Telegram bot menu commands (merged with native; conflicts ignored).", + "messages.suppressToolErrors": + "When true, suppress ⚠️ tool-error warnings from being shown to the user. The agent already sees errors in context and can retry. Default: false.", "messages.ackReaction": "Emoji reaction used to acknowledge inbound messages (empty disables).", "messages.ackReactionScope": 'When to send ack reactions ("group-mentions", "group-all", "direct", "all").', diff --git a/src/config/schema.labels.ts b/src/config/schema.labels.ts index 60a467fd02c..dda03df3127 100644 --- a/src/config/schema.labels.ts +++ b/src/config/schema.labels.ts @@ -219,6 +219,7 @@ export const FIELD_LABELS: Record = { "browser.remoteCdpHandshakeTimeoutMs": "Remote CDP Handshake Timeout (ms)", "session.dmScope": "DM Session Scope", "session.agentToAgent.maxPingPongTurns": "Agent-to-Agent Ping-Pong Turns", + "messages.suppressToolErrors": "Suppress Tool Error Warnings", "messages.ackReaction": "Ack Reaction Emoji", "messages.ackReactionScope": "Ack Reaction Scope", "messages.inbound.debounceMs": "Inbound Message Debounce (ms)", diff --git a/src/config/types.messages.ts b/src/config/types.messages.ts index 0f197c98e6d..d63eee32d29 100644 --- a/src/config/types.messages.ts +++ b/src/config/types.messages.ts @@ -82,6 +82,8 @@ export type MessagesConfig = { ackReactionScope?: "group-mentions" | "group-all" | "direct" | "all"; /** Remove ack reaction after reply is sent (default: false). */ removeAckAfterReply?: boolean; + /** When true, suppress ⚠️ tool-error warnings from being shown to the user. Default: false. */ + suppressToolErrors?: boolean; /** Text-to-speech settings for outbound replies. */ tts?: TtsConfig; }; diff --git a/src/config/zod-schema.session.ts b/src/config/zod-schema.session.ts index f2b2f3152a2..224632defc9 100644 --- a/src/config/zod-schema.session.ts +++ b/src/config/zod-schema.session.ts @@ -114,6 +114,7 @@ export const MessagesSchema = z ackReaction: z.string().optional(), ackReactionScope: z.enum(["group-mentions", "group-all", "direct", "all"]).optional(), removeAckAfterReply: z.boolean().optional(), + suppressToolErrors: z.boolean().optional(), tts: TtsConfigSchema, }) .strict()