export type ToolErrorClass = | "network-timeout" | "network-proxy" | "permission-denied" | "path-not-found" | "shell-unavailable" | "schema-error" | "business-gate" | "external-verification" | "empty-result" | "unknown"; export interface ToolErrorClassificationInput { toolName?: string; text?: string; exitCode?: number; } export interface ToolErrorClassification { errorClass: ToolErrorClass; recoveryAction: string; } export function classifyToolError(input: ToolErrorClassificationInput): ToolErrorClassification { const text = [input.toolName ?? "", input.text ?? ""].join("\n"); if (/ETIMEDOUT|timed?\s*out|timeout exceeded|network request/i.test(text)) { return { errorClass: "network-timeout", recoveryAction: "记录网络超时;检查代理或要求用户提供可访问产物,禁止改用猜测结果。" }; } if (/proxy|ECONNREFUSED|ENOTFOUND|registry\.npmjs\.org|127\.0\.0\.1:\d+/i.test(text)) { return { errorClass: "network-proxy", recoveryAction: "记录代理或网络配置问题;要求用户确认代理配置或提供离线包。" }; } if (/permission denied|access is denied|unauthorized|forbidden|EACCES|EPERM/i.test(text)) { return { errorClass: "permission-denied", recoveryAction: "记录权限阻塞;要求用户授权或改用工作区内可写路径。" }; } if (/cannot find path|no such file|path .*does not exist|ENOENT|not found/i.test(text)) { return { errorClass: "path-not-found", recoveryAction: "使用当前环境文件搜索重新定位路径;禁止猜测路径或转给子 agent 代替搜索。" }; } if (/bash.*not.*available|Git Bash|shellPath|spawn.*bash|program not found|PowerShell 启动失败/i.test(text)) { return { errorClass: "shell-unavailable", recoveryAction: "切换到当前环境可用 shell 或 KCode 文件工具;禁止反复自述工具失败。" }; } if (/schema|invalid params|missing.*params|unknown action|未知 factLabel|参数/i.test(text)) { return { errorClass: "schema-error", recoveryAction: "修正工具参数或查询合法枚举;禁止反复试探自造参数。" }; } if (/门禁|禁止|阻塞|open blocking question|工作区外路径|PLAN\.md|execute 阶段/i.test(text)) { return { errorClass: "business-gate", recoveryAction: "按门禁唯一下一动作补齐事实、阶段或证据;禁止绕过门禁。" }; } if (/BOS 注册|人工功能测试|外部系统|生产验证|用户提供.*证据/i.test(text)) { return { errorClass: "external-verification", recoveryAction: "要求用户提供可核验证据并记录来源;LLM 不得声称已完成外部操作。" }; } if (input.exitCode === 0 && /no output|0 results|无结果|empty/i.test(text)) { return { errorClass: "empty-result", recoveryAction: "记录空结果和查询范围;调整真实查询条件或要求用户提供路径。" }; } return { errorClass: "unknown", recoveryAction: "记录原始错误、当前工具和参数;先定位错误类别,再决定恢复动作。" }; }