mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-18 20:11:40 +00:00
* fix(tui): show last tool-error summary on aborted runs When a run ends while looping on tool-call validation errors the TUI showed only "run aborted". Carry the last tool failure on the terminal lifecycle metadata and render a sanitized one-line summary on the abort line, e.g. "run aborted: edit tool validation failed: edits: must have required properties edits". Argument dumps are stripped; with no summary it stays "run aborted". Refs #90982 * fix(tui): secure abort validation diagnostics * docs(changelog): move TUI fix to unreleased * docs(changelog): aggregate TUI fixes * fix(tui): scope abort validation diagnostics * docs(changelog): restore unreleased spacing * fix(tui): scope abort validation diagnostics * fix(tui): scope abort validation diagnostics --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
/**
|
|
* Compact tool error summary types.
|
|
*
|
|
* Stores failure metadata used by transcripts, retry behavior, and mutation recovery logic.
|
|
*/
|
|
import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce";
|
|
import type { FileTarget } from "./tool-mutation.js";
|
|
|
|
export type ToolErrorSummary = {
|
|
toolName: string;
|
|
meta?: string;
|
|
errorCode?: string;
|
|
error?: string;
|
|
validationErrorSummary?: string;
|
|
timedOut?: boolean;
|
|
middlewareError?: boolean;
|
|
mutatingAction?: boolean;
|
|
actionFingerprint?: string;
|
|
fileTarget?: FileTarget;
|
|
};
|
|
|
|
const EXEC_LIKE_TOOL_NAMES = new Set(["exec", "bash"]);
|
|
|
|
/** Detects shell-execution tools that share retry and mutation semantics. */
|
|
export function isExecLikeToolName(toolName: string): boolean {
|
|
return EXEC_LIKE_TOOL_NAMES.has(normalizeOptionalLowercaseString(toolName) ?? "");
|
|
}
|
|
|
|
const MAX_ABORT_SUMMARY_LENGTH = 160;
|
|
|
|
function hasUnsafeSummaryCharacter(value: string): boolean {
|
|
for (const char of value) {
|
|
const code = char.charCodeAt(0);
|
|
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Accepts only the compact single-line diagnostic produced below. */
|
|
export function readToolValidationErrorSummary(value: unknown): string | undefined {
|
|
if (typeof value !== "string") {
|
|
return undefined;
|
|
}
|
|
const summary = value.trim();
|
|
if (!summary || summary.length > MAX_ABORT_SUMMARY_LENGTH || hasUnsafeSummaryCharacter(summary)) {
|
|
return undefined;
|
|
}
|
|
return summary;
|
|
}
|
|
|
|
/** Builds a static diagnostic from typed pre-execution validation provenance. */
|
|
export function createToolValidationErrorSummary(toolName: string): string | undefined {
|
|
if (hasUnsafeSummaryCharacter(toolName)) {
|
|
return undefined;
|
|
}
|
|
const normalizedToolName = toolName.replace(/\s+/g, " ").trim();
|
|
if (!normalizedToolName) {
|
|
return undefined;
|
|
}
|
|
return readToolValidationErrorSummary(
|
|
`${normalizedToolName} tool validation failed: invalid arguments`,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns only a boundary-prepared validation summary. Raw validator messages
|
|
* stay private because paths and custom messages can contain model input.
|
|
*/
|
|
export function summarizeToolValidationError(summary: ToolErrorSummary): string | undefined {
|
|
return readToolValidationErrorSummary(summary.validationErrorSummary);
|
|
}
|