fix: gate Telegram exec tool warnings behind verbose mode (#20560)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 7ce94931f0
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Ayaan Zaidi
2026-02-19 09:05:49 +05:30
committed by GitHub
parent b228c06bbd
commit 6b05916c14
6 changed files with 98 additions and 2 deletions

View File

@@ -163,7 +163,7 @@ describe("buildEmbeddedRunPayloads", () => {
expect(payloads[0]?.text).toBe("All good");
});
it("adds tool error fallback when the assistant only invoked tools", () => {
it("adds tool error fallback when the assistant only invoked tools and verbose mode is on", () => {
const payloads = buildPayloads({
lastAssistant: makeAssistant({
stopReason: "toolUse",
@@ -178,6 +178,7 @@ describe("buildEmbeddedRunPayloads", () => {
],
}),
lastToolError: { toolName: "exec", error: "Command exited with code 1" },
verboseLevel: "on",
});
expect(payloads).toHaveLength(1);

View File

@@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { buildEmbeddedRunPayloads } from "./payloads.js";
type BuildPayloadParams = Parameters<typeof buildEmbeddedRunPayloads>[0];
function buildPayloads(overrides: Partial<BuildPayloadParams> = {}) {
return buildEmbeddedRunPayloads({
assistantTexts: [],
toolMetas: [],
lastAssistant: undefined,
sessionKey: "session:telegram",
inlineToolResultsAllowed: false,
verboseLevel: "off",
reasoningLevel: "off",
toolResultFormat: "plain",
...overrides,
});
}
describe("buildEmbeddedRunPayloads tool-error warnings", () => {
it("suppresses exec tool errors when verbose mode is off", () => {
const payloads = buildPayloads({
lastToolError: { toolName: "exec", error: "command failed" },
verboseLevel: "off",
});
expect(payloads).toHaveLength(0);
});
it("shows exec tool errors when verbose mode is on", () => {
const payloads = buildPayloads({
lastToolError: { toolName: "exec", error: "command failed" },
verboseLevel: "on",
});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.isError).toBe(true);
expect(payloads[0]?.text).toContain("Exec");
expect(payloads[0]?.text).toContain("command failed");
});
it("keeps non-exec mutating tool failures visible", () => {
const payloads = buildPayloads({
lastToolError: { toolName: "write", error: "permission denied" },
verboseLevel: "off",
});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.isError).toBe(true);
expect(payloads[0]?.text).toContain("Write");
});
});

View File

@@ -49,10 +49,16 @@ function shouldShowToolErrorWarning(params: {
hasUserFacingReply: boolean;
suppressToolErrors: boolean;
suppressToolErrorWarnings?: boolean;
verboseLevel?: VerboseLevel;
}): boolean {
if (params.suppressToolErrorWarnings) {
return false;
}
const normalizedToolName = params.lastToolError.toolName.trim().toLowerCase();
const verboseEnabled = params.verboseLevel === "on" || params.verboseLevel === "full";
if ((normalizedToolName === "exec" || normalizedToolName === "bash") && !verboseEnabled) {
return false;
}
const isMutatingToolError =
params.lastToolError.mutatingAction ?? isLikelyMutatingToolName(params.lastToolError.toolName);
if (isMutatingToolError) {
@@ -255,6 +261,7 @@ export function buildEmbeddedRunPayloads(params: {
hasUserFacingReply: hasUserFacingAssistantReply,
suppressToolErrors: Boolean(params.config?.messages?.suppressToolErrors),
suppressToolErrorWarnings: params.suppressToolErrorWarnings,
verboseLevel: params.verboseLevel,
});
// Always surface mutating tool failures so we do not silently confirm actions that did not happen.