Repository navigation
[EPIC-U][LOU-U14] One structured tool-error shape on every tool failure path - #93
Merged
Merged
Conversation
…re path
Add toolErrorResult() (src/execution/toolErrors.ts) producing
{ error, toolName, message, kind } with the 2,000 character message cap.
Thrown errors, validation failures, unknown tool / no registry, rejected
approvals, "not run" results on resume, MCP isError and the sandbox
fail-closed guard all use it; the resume path no longer rejects when an
approved tool is missing from the registry.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
…r-shape # Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
New exported helper
toolErrorResult({ toolName, toolCallId?, error, kind?, details? })(src/execution/toolErrors.ts) builds{ error, toolName, message, kind, ...extras }with the 2,000 character message cap. Every tool failure path now uses it, and the transcript message is flaggedisErroreverywhere. Exported:toolErrorResult,ToolErrorResult,ToolErrorKind,ToolErrorInput.kindis one ofexecution | validation | not-found | rejected | not-run | mcp | sandbox. Thrown errors can declare their kind through atoolErrorKindproperty (McpToolError->mcp, the sandbox guard's newSandboxRequiredError->sandbox,ToolArgumentsValidationError->validation).Audit: shapes that existed before
{ error: <name>, toolName, message }(LOU-U12)kind: 'execution'{ error: 'ToolArgumentsValidationError', toolName, message, issues }kind: 'validation'(same builder, so the cap applies)outcome.result = null; the transcript got{ error: '<message>' }from the fallback inpushToolResult{ error: 'ToolNotFoundError', toolName, message, kind: 'not-found' }isError(mcp/result.ts)McpToolError, so the thrown-error shapekind: 'mcp'viatoolErrorKindError, so the thrown-error shapeSandboxRequiredError(same message),kind: 'sandbox'{ error: 'Tool execution was rejected by the reviewer', note }{ error: 'ToolRejectedError', toolName, message: <same text>, kind: 'rejected', note }{ error: 'Tool call was not run: ...' }{ error: 'ToolNotRunError', toolName, message: <same text>, kind: 'not-run' }{ error: '<message>' }kindresumeAfterApproval()rejected after the approval was already consumednot-founderror result flaggedisErrorWhy
LOU-U12 gave thrown errors a structured result, but each other failure path built its own shape, so a model, hook, or eval check could not rely on one shape. Existing
error/toolName/messagefields are kept andkindis additive.errorstays the error name (not the literaltrue) becausetool.errorevents derive theirnamefrom it.Breaking, with CHANGELOG migration note: not-found, rejected, not-run and post-approval-throw results used to carry the message in
error; the message is now inmessageanderroris a name. Test expectations were updated accordingly.Acceptance criteria
isErrorconsistent (hooks,tool-resultevents andtool.errornames see the same thing)kindadded; changed shapes CHANGELOGged with migration notekindtable); api-overview examples updated; llms files regeneratedLeft alone (outside this ticket's scope), still on their own shapes
src/execution/agentRunState.tspushCancelledToolResult:{ error: 'Tool call was cancelled ...' }, noisError(should bekind: 'not-run')src/execution/subagentRuntime.tssettleSuspensions:{ error: 'Sub-agent ... was not run ...' }withisErrorpushToolResultfallback{ error: outcome.error }is now unreachable for failures built in toolCallExecution.ts.How verified
npx tsc --noEmit: passnpm run lint: 0 errors, 405 warnings; no new warnings in changed filesnpm run test:types: no errorsnpx vitest run: 145 files passed (afternpm run build);npm run test:coverage: 2105 passed, 3 skippednpm run build && npm run build --workspace=packages/create-loushy-agent: passnpm run docs:verify-snippets -- --skip-build: all 107 snippets type-checknpm run docs:llms:check: passnpm run fallow: no issues🤖 Generated with Claude Code
https://claude.ai/code/session_016bY29ibQgv3MDdjAbUT4YR
Generated by Claude Code