fix(agents): suppress exec/bash exit code errors from user display

Exec/bash tool errors with non-zero exit codes (e.g., grep no matches,
command not found) are now treated as internal/recoverable errors and
not displayed to users.

These errors are expected workflow results that the model should handle
internally, not technical failures that need user attention.

Fixes #3765
This commit is contained in:
HirokiKobayashi-R 2026-01-29 17:10:33 +09:00
parent 6372242da7
commit 9a806956a5
2 changed files with 41 additions and 4 deletions

View File

@ -171,10 +171,41 @@ describe("buildEmbeddedRunPayloads", () => {
toolResultFormat: "plain",
});
expect(payloads).toHaveLength(1);
expect(payloads[0]?.isError).toBe(true);
expect(payloads[0]?.text).toContain("Exec");
expect(payloads[0]?.text).toContain("code 1");
// Exec exit code errors are now suppressed as internal/recoverable errors
expect(payloads).toHaveLength(0);
});
it("suppresses exec exit code errors (grep no matches, etc.)", () => {
const payloads = buildEmbeddedRunPayloads({
assistantTexts: [],
toolMetas: [],
lastAssistant: undefined,
lastToolError: { toolName: "exec", error: "Command exited with code 1" },
sessionKey: "session:telegram",
inlineToolResultsAllowed: false,
verboseLevel: "off",
reasoningLevel: "off",
toolResultFormat: "plain",
});
// Exec/bash exit code errors should not be sent to the user
expect(payloads).toHaveLength(0);
});
it("suppresses bash exit code errors", () => {
const payloads = buildEmbeddedRunPayloads({
assistantTexts: [],
toolMetas: [],
lastAssistant: undefined,
lastToolError: { toolName: "bash", error: "Command exited with code 127" },
sessionKey: "session:telegram",
inlineToolResultsAllowed: false,
verboseLevel: "off",
reasoningLevel: "off",
toolResultFormat: "plain",
});
expect(payloads).toHaveLength(0);
});
it("suppresses recoverable tool errors containing 'required'", () => {

View File

@ -182,7 +182,13 @@ export function buildEmbeddedRunPayloads(params: {
// Check if this is a recoverable/internal tool error that shouldn't be shown to users
// when there's already a user-facing reply (the model should have retried).
const errorLower = (params.lastToolError.error ?? "").toLowerCase();
const toolNameLower = (params.lastToolError.toolName ?? "").toLowerCase();
const isExecTool = toolNameLower === "exec" || toolNameLower === "bash";
// Exec/bash non-zero exit codes are internal errors (e.g., grep no matches) that
// the model should handle, not surface to users.
const isExecExitCodeError = isExecTool && errorLower.includes("exited with code");
const isRecoverableError =
isExecExitCodeError ||
errorLower.includes("required") ||
errorLower.includes("missing") ||
errorLower.includes("invalid") ||