| 52 | } |
| 53 | |
| 54 | export function transformError(err: any, toolCall?: ToolCall): string { |
| 55 | const code = err.code ?? ''; |
| 56 | const msg = err.message ?? String(err); |
| 57 | |
| 58 | if (err.name === 'AbortError' || err.name === 'CancelledError') { |
| 59 | return `[CANCELLED] Operation was cancelled. Stop and ask the user how to proceed.`; |
| 60 | } |
| 61 | |
| 62 | if (code === 'ENOENT') { |
| 63 | return `[FILE_NOT_FOUND] ${msg}. Verify the path with ls or glob before retrying.`; |
| 64 | } |
| 65 | if (code === 'EACCES' || code === 'EPERM') { |
| 66 | return `[PERMISSION_DENIED] ${msg}. The OS refused access. The user may need to adjust file permissions.`; |
| 67 | } |
| 68 | if (code === 'EISDIR') { |
| 69 | return `[IS_A_DIRECTORY] ${msg}. Use ls instead of read_file on directories.`; |
| 70 | } |
| 71 | if (code === 'ENOTDIR') { |
| 72 | return `[NOT_A_DIRECTORY] ${msg}. Check the path.`; |
| 73 | } |
| 74 | if (code === 'EBUSY' || code === 'EAGAIN') { |
| 75 | return `[FILE_BUSY] ${msg}. Another process may be using this file. Retry once or move on.`; |
| 76 | } |
| 77 | if (code === 'ENOSPC') { |
| 78 | return `[NO_SPACE] ${msg}. Disk full. Tell the user.`; |
| 79 | } |
| 80 | if (code === 'BUDGET_EXCEEDED') { |
| 81 | return `[BUDGET_EXCEEDED] ${msg}. Stop and summarize what you did so far.`; |
| 82 | } |
| 83 | if (code === 'PERMISSION_DENIED') { |
| 84 | return `[USER_DENIED] ${msg}. The user explicitly rejected. Do not retry the same action — try a different approach or ask the user.`; |
| 85 | } |
| 86 | |
| 87 | // JSON parse error |
| 88 | if (err instanceof SyntaxError && msg.includes('JSON')) { |
| 89 | return `[INVALID_JSON] Tool arguments were not valid JSON: ${msg}. Re-output the arguments as strict JSON (double-quoted keys, no trailing commas).`; |
| 90 | } |
| 91 | |
| 92 | // Provider HTTP errors |
| 93 | if (err.httpStatus === 429) { |
| 94 | return `[RATE_LIMITED] ${msg}. Wait briefly and retry, or switch model.`; |
| 95 | } |
| 96 | if (err.httpStatus && err.httpStatus >= 500) { |
| 97 | return `[SERVER_ERROR] ${err.provider ?? 'Provider'} returned ${err.httpStatus}. Retry once. If it persists, switch model.`; |
| 98 | } |
| 99 | |
| 100 | // Tool name hint for debugging |
| 101 | const toolHint = toolCall ? ` (during ${toolCall.function.name})` : ''; |
| 102 | return `[ERROR]${toolHint} ${msg}\nRead the error and adjust your approach.`; |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Decide what to do when the model keeps re-reading the SAME file (identical read-only |