Skip to content

feat(chat,react): app-declared tool failures and a per-call result slot - #1501

Merged
blove merged 18 commits into
mainfrom
blove/tool-outcome-failure-hook
Nov 10, 2026
Merged

blove merged 18 commits into
mainfrom
blove/tool-outcome-failure-hook

Conversation

@blove

@blove blove commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Summary

A tool can finish at the protocol level and still fail for the application (for example lookup_flight returning {"error": "Flight UA999 not found in dataset"}).
Until now Threadplane showed such calls as Complete, in the expanded row and in the live region ("2 complete"), and an app could only fix that by replacing the whole activity rendering.

This PR adds one narrow, explicit hook plus a per-call slot, in Angular and React with identical semantics:

  • App-declared failure. toolOutcome may return { status: 'failed', text, sources? } after the app decodes its own declared result schema. It only applies to protocol-complete calls: the row becomes Failed, the failure text replaces the generic copy, the raw result stays in Details. Existing { text, sources? } outcomes are unchanged (status defaults to 'succeeded'). The library still never infers failure from payload shape; false, 0, "", [] and null stay literal. Adapter errors, stopped, waiting, pending, running and unknown are never overridden. A throwing toolOutcome leaves the call unchanged (dev-only console.error).
  • Settled mixed label. A settled group with failures reads Used N tools · K failed (live region: Used 2 tools · 1 failed. 1 complete · 1 failed.). While work is active the existing running label and Failed cue are unchanged; an app caption still wins. Group alerts show de-duplicated failure copy (div[role=alert] > p), falling back to the generic sentence.
  • Per-call result slot. React renderToolResult(activity) on MessageList/Chat (and renderResult on ToolActivityGroup); Angular chatToolResultTemplate (per tool name or "*"), forwarded by <chat>. Receives the resolved activity, adds content outside Details, renders below a collapsed group. It runs nothing; renderMessage / chatToolCallTemplate keep full replacement.
  • Primitives. ToolActivity/ToolActivityGroup and chat-tool-activity/chat-tool-activity-group apply a failed outcome passed directly; resolveToolOutcome is exported from both packages. Angular primitive input types are unchanged.
  • AG-UI adapter. A tool message carrying AG-UI 1.0's error field maps to a failed call (a streamed-complete call is upgraded when the snapshot arrives; a late TOOL_CALL_RESULT does not downgrade it). No shipped AG-UI server populates error today, so this is covered by reducer unit tests only.
  • LangGraph. Regression test pinning the existing tool error state mapping.
  • Docs. Both chat-tool-calls pages document declaring a failed result and adding content to one call; API reference and parity dispositions regenerated.

Also fixes an Angular duplicate failure alert when calls are not grouped.

Design decisions: the hook is presentation-only (the model is not told; tools should use their protocol's error flag); partial failure is a group-level fact, not a new per-call phase; retry and save receipts stay application-owned. The application recipe (typed decoding, read-only retry of only the failed call, sources, receipts after an actual save) and its example/fixture work follow in a separate PR.

Test plan

Run on Node 22 at 34a1c6ac1, all passing:

  • npx nx test react --watch=false (907 tests)
  • npx nx test chat --watch=false --maxWorkers=1 (1528 tests)
  • npx nx test ag-ui --watch=false (618 tests)
  • npx nx test langgraph --watch=false
  • npx nx lint react|chat|ag-ui (no errors)
  • npx nx run react:type-tests / chat:type-tests / ag-ui:runtime-quality
  • npx nx run-many -t build -p chat,ag-ui,react
  • node scripts/react-parity/inventory.mjs --check
  • npm run generate-api-docs:check
  • website public-copy spec
  • Browser check of the partial-failure conversation in both frameworks: not run for this PR; it is measured with the recipe example in the follow-up.

🤖 Generated with Claude Code

blove and others added 17 commits November 9, 2026 12:28
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lt slot

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mplate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also keep tool message parts on the upgrade path and drop the redundant
guard and cast in the snapshot-only path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rouped duplicate alert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… types

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
threadplane Ready Ready Preview Nov 9, 2026 10:54pm UTC

Request Review

…entry

The packed-consumer check keeps an exact allowlist of react/chat runtime
exports, so the new resolveToolOutcome export failed "Library — packages".
Move the allowlist to one SUPPORTED_EXPORTS table that the generated
consumer script receives alongside the assertion, and pin the new export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blove
blove merged commit 690c31a into main Nov 10, 2026
132 checks passed
@blove
blove deleted the blove/tool-outcome-failure-hook branch November 10, 2026 00:10

This branch was successfully deployed

1 active deployment
Preview – threadplane — 33f3633c Deployed Nov 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant