mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 11:51:23 +00:00
refactor: centralize tool-error visibility policy
This commit is contained in:
@@ -28,6 +28,10 @@ type LastToolError = {
|
||||
mutatingAction?: boolean;
|
||||
actionFingerprint?: string;
|
||||
};
|
||||
type ToolErrorWarningPolicy = {
|
||||
showWarning: boolean;
|
||||
includeDetails: boolean;
|
||||
};
|
||||
|
||||
const RECOVERABLE_TOOL_ERROR_KEYWORDS = [
|
||||
"required",
|
||||
@@ -44,30 +48,37 @@ function isRecoverableToolError(error: string | undefined): boolean {
|
||||
return RECOVERABLE_TOOL_ERROR_KEYWORDS.some((keyword) => errorLower.includes(keyword));
|
||||
}
|
||||
|
||||
function shouldShowToolErrorWarning(params: {
|
||||
function isVerboseToolDetailEnabled(level?: VerboseLevel): boolean {
|
||||
return level === "on" || level === "full";
|
||||
}
|
||||
|
||||
function resolveToolErrorWarningPolicy(params: {
|
||||
lastToolError: LastToolError;
|
||||
hasUserFacingReply: boolean;
|
||||
suppressToolErrors: boolean;
|
||||
suppressToolErrorWarnings?: boolean;
|
||||
verboseLevel?: VerboseLevel;
|
||||
}): boolean {
|
||||
}): ToolErrorWarningPolicy {
|
||||
const includeDetails = isVerboseToolDetailEnabled(params.verboseLevel);
|
||||
if (params.suppressToolErrorWarnings) {
|
||||
return false;
|
||||
return { showWarning: false, includeDetails };
|
||||
}
|
||||
const normalizedToolName = params.lastToolError.toolName.trim().toLowerCase();
|
||||
const verboseEnabled = params.verboseLevel === "on" || params.verboseLevel === "full";
|
||||
if ((normalizedToolName === "exec" || normalizedToolName === "bash") && !verboseEnabled) {
|
||||
return false;
|
||||
if ((normalizedToolName === "exec" || normalizedToolName === "bash") && !includeDetails) {
|
||||
return { showWarning: false, includeDetails };
|
||||
}
|
||||
const isMutatingToolError =
|
||||
params.lastToolError.mutatingAction ?? isLikelyMutatingToolName(params.lastToolError.toolName);
|
||||
if (isMutatingToolError) {
|
||||
return true;
|
||||
return { showWarning: true, includeDetails };
|
||||
}
|
||||
if (params.suppressToolErrors) {
|
||||
return false;
|
||||
return { showWarning: false, includeDetails };
|
||||
}
|
||||
return !params.hasUserFacingReply && !isRecoverableToolError(params.lastToolError.error);
|
||||
return {
|
||||
showWarning: !params.hasUserFacingReply && !isRecoverableToolError(params.lastToolError.error),
|
||||
includeDetails,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildEmbeddedRunPayloads(params: {
|
||||
@@ -256,7 +267,7 @@ export function buildEmbeddedRunPayloads(params: {
|
||||
}
|
||||
|
||||
if (params.lastToolError) {
|
||||
const shouldShowToolError = shouldShowToolErrorWarning({
|
||||
const warningPolicy = resolveToolErrorWarningPolicy({
|
||||
lastToolError: params.lastToolError,
|
||||
hasUserFacingReply: hasUserFacingAssistantReply,
|
||||
suppressToolErrors: Boolean(params.config?.messages?.suppressToolErrors),
|
||||
@@ -266,16 +277,14 @@ export function buildEmbeddedRunPayloads(params: {
|
||||
|
||||
// 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.
|
||||
if (shouldShowToolError) {
|
||||
if (warningPolicy.showWarning) {
|
||||
const toolSummary = formatToolAggregate(
|
||||
params.lastToolError.toolName,
|
||||
params.lastToolError.meta ? [params.lastToolError.meta] : undefined,
|
||||
{ markdown: useMarkdown },
|
||||
);
|
||||
const verboseErrorDetailsEnabled =
|
||||
params.verboseLevel === "on" || params.verboseLevel === "full";
|
||||
const errorSuffix =
|
||||
verboseErrorDetailsEnabled && params.lastToolError.error
|
||||
warningPolicy.includeDetails && params.lastToolError.error
|
||||
? `: ${params.lastToolError.error}`
|
||||
: "";
|
||||
const warningText = `⚠️ ${toolSummary} failed${errorSuffix}`;
|
||||
|
||||
Reference in New Issue
Block a user