chore: initialize recovered claude workspace
This commit is contained in:
132
src/utils/toolErrors.ts
Normal file
132
src/utils/toolErrors.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import type { ZodError } from 'zod/v4'
|
||||
import { AbortError, ShellError } from './errors.js'
|
||||
import { INTERRUPT_MESSAGE_FOR_TOOL_USE } from './messages.js'
|
||||
|
||||
export function formatError(error: unknown): string {
|
||||
if (error instanceof AbortError) {
|
||||
return error.message || INTERRUPT_MESSAGE_FOR_TOOL_USE
|
||||
}
|
||||
if (!(error instanceof Error)) {
|
||||
return String(error)
|
||||
}
|
||||
const parts = getErrorParts(error)
|
||||
const fullMessage =
|
||||
parts.filter(Boolean).join('\n').trim() || 'Command failed with no output'
|
||||
if (fullMessage.length <= 10000) {
|
||||
return fullMessage
|
||||
}
|
||||
const halfLength = 5000
|
||||
const start = fullMessage.slice(0, halfLength)
|
||||
const end = fullMessage.slice(-halfLength)
|
||||
return `${start}\n\n... [${fullMessage.length - 10000} characters truncated] ...\n\n${end}`
|
||||
}
|
||||
|
||||
export function getErrorParts(error: Error): string[] {
|
||||
if (error instanceof ShellError) {
|
||||
return [
|
||||
`Exit code ${error.code}`,
|
||||
error.interrupted ? INTERRUPT_MESSAGE_FOR_TOOL_USE : '',
|
||||
error.stderr,
|
||||
error.stdout,
|
||||
]
|
||||
}
|
||||
const parts = [error.message]
|
||||
if ('stderr' in error && typeof error.stderr === 'string') {
|
||||
parts.push(error.stderr)
|
||||
}
|
||||
if ('stdout' in error && typeof error.stdout === 'string') {
|
||||
parts.push(error.stdout)
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a Zod validation path into a readable string
|
||||
* e.g., ['todos', 0, 'activeForm'] => 'todos[0].activeForm'
|
||||
*/
|
||||
function formatValidationPath(path: PropertyKey[]): string {
|
||||
if (path.length === 0) return ''
|
||||
|
||||
return path.reduce((acc, segment, index) => {
|
||||
const segmentStr = String(segment)
|
||||
if (typeof segment === 'number') {
|
||||
return `${String(acc)}[${segmentStr}]`
|
||||
}
|
||||
return index === 0 ? segmentStr : `${String(acc)}.${segmentStr}`
|
||||
}, '') as string
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Zod validation errors into a human-readable and LLM friendly error message
|
||||
*
|
||||
* @param toolName The name of the tool that failed validation
|
||||
* @param error The Zod error object
|
||||
* @returns A formatted error message string
|
||||
*/
|
||||
export function formatZodValidationError(
|
||||
toolName: string,
|
||||
error: ZodError,
|
||||
): string {
|
||||
const missingParams = error.issues
|
||||
.filter(
|
||||
err =>
|
||||
err.code === 'invalid_type' &&
|
||||
err.message.includes('received undefined'),
|
||||
)
|
||||
.map(err => formatValidationPath(err.path))
|
||||
|
||||
const unexpectedParams = error.issues
|
||||
.filter(err => err.code === 'unrecognized_keys')
|
||||
.flatMap(err => err.keys)
|
||||
|
||||
const typeMismatchParams = error.issues
|
||||
.filter(
|
||||
err =>
|
||||
err.code === 'invalid_type' &&
|
||||
!err.message.includes('received undefined'),
|
||||
)
|
||||
.map(err => {
|
||||
const typeErr = err as { expected: string }
|
||||
const receivedMatch = err.message.match(/received (\w+)/)
|
||||
const received = receivedMatch ? receivedMatch[1] : 'unknown'
|
||||
return {
|
||||
param: formatValidationPath(err.path),
|
||||
expected: typeErr.expected,
|
||||
received,
|
||||
}
|
||||
})
|
||||
|
||||
// Default to original error message if we can't create a better one
|
||||
let errorContent = error.message
|
||||
|
||||
// Build a human-readable error message
|
||||
const errorParts = []
|
||||
|
||||
if (missingParams.length > 0) {
|
||||
const missingParamErrors = missingParams.map(
|
||||
param => `The required parameter \`${param}\` is missing`,
|
||||
)
|
||||
errorParts.push(...missingParamErrors)
|
||||
}
|
||||
|
||||
if (unexpectedParams.length > 0) {
|
||||
const unexpectedParamErrors = unexpectedParams.map(
|
||||
param => `An unexpected parameter \`${param}\` was provided`,
|
||||
)
|
||||
errorParts.push(...unexpectedParamErrors)
|
||||
}
|
||||
|
||||
if (typeMismatchParams.length > 0) {
|
||||
const typeErrors = typeMismatchParams.map(
|
||||
({ param, expected, received }) =>
|
||||
`The parameter \`${param}\` type is expected as \`${expected}\` but provided as \`${received}\``,
|
||||
)
|
||||
errorParts.push(...typeErrors)
|
||||
}
|
||||
|
||||
if (errorParts.length > 0) {
|
||||
errorContent = `${toolName} failed due to the following ${errorParts.length > 1 ? 'issues' : 'issue'}:\n${errorParts.join('\n')}`
|
||||
}
|
||||
|
||||
return errorContent
|
||||
}
|
||||
Reference in New Issue
Block a user