inspector: render tool calls and results with AI Elements - #31
Open
Mark-Life wants to merge 1 commit into
Open
Conversation
The transcript rendered every event as the same generic disclosure row, so a tool call and its result were indistinguishable at a glance and a result never said which tool produced it — the transcript only records `tool_use_id` on the result, never the name. Vendor AI Elements' `Tool` component into `@workspace/ui`, next to the `code-block` already installed there, and use it for `tool-call` and `tool-result`. Upstream is built on the AI SDK's streaming message-parts model; this copy replaces `ToolUIPart["state"]` with a `ToolState` a finished transcript can actually justify, and takes `code` + `language` for the shared highlighter rather than an `unknown` payload. The pairing, unwrapping and outcome logic lives in `lib/tool-event.ts` so it is pure and testable — `@workspace/ui` and `inspector` resolve different copies of React, so the component itself cannot be rendered outside Vite. One row per event either way: pairing a call with its result into one card would reorder the transcript and merge two token figures. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Problem:
Every transcript event in the session history rendered as the same generic disclosure row, so a tool call, its result, and an attachment were indistinguishable until expanded. A result row was labelled
tool-resultand nothing more: the transcript records onlytool_use_idon the result block, never the tool's name, soe.toolName ?? e.kindhad nothing to fall back on. Nothing surfaced whether a call failed, or whether it ever returned at all.Expanding a call showed one of its arguments —
displayBodyunwrappedcodeorcommandand dropped the rest.Solution:
Vendor AI Elements'
Toolinto@workspace/ui/components/ai-elements/, beside thecode-blockalready installed there, and use it fortool-callandtool-result. Upstream is built on the AI SDK's streaming message-parts model, which we have none of, so this copy takescode+languagefor the shared highlighter instead of anunknownpayload, and replacesToolUIPart["state"]with aToolStatea finished transcript can justify:completed,error, orunansweredfor a call whose result the transcript never recorded.ToolHeadergainsicon,leadandchildrenslots so the row keeps its turn number, sidechain badge, summary and token estimate.Calls and results are paired by
toolUseId, so a result names the tool it came from and a call shows the outcome its result recorded. Unwrapping a payload arg now puts the remaining arguments in their own pane rather than hiding them, and the collapsed summary is the command itself instead of the JSON envelope.One row per event either way. Pairing a call with its result into a single card would reorder the transcript and merge two token figures, which a forensics view cannot afford. Search, the type filter, expand-all, the position-keyed collapse state, the dumb-zone divider and the subagent cards are untouched.
assistant-thinkingstays a plain row — the AI Elements reasoning block pulls instreamdown,mermaidandmathto render content our transcripts do not store.Security Impact:
The tool rows render
TimelineEvent.bodyandpreview, whichredactSessionhas already rewritten server-side. No new field is read, so the reveal toggle remains the only path to raw bytes; a test pins that.Testing:
bun test(full workspace),bun run typecheck,bunx ultracite check,bun run --filter=inspector build:apps/inspector/test/tool-event.test.tsdrives the new shaping through the real parser and the shared Claude fixture: name-borrowing across the pair, all three outcomes, payload unwrapping, and that nothing rendered from a redacted session contains the fixture'ssk-ant-key while the raw session still does.Two caveats on what was not run.
bun testneedsCLAUDE_CONFIG_DIRunset in this container or two unrelatedAgentRegistrytests fail.peektrace#build:binaryfails on a missingmsgpackr-extractnative module — verified identical on a stashed clean tree, so it predates this branch.The component is not rendered under test:
@workspace/uiresolves React 19.2.8 whileinspectorresolves 19.2.7, so hooks throw outside Vite, which dedupes via@vitejs/plugin-react. The shaping logic lives inlib/tool-event.tsfor that reason; typecheck and the Vite build cover the JSX. No screenshot — no browser can launch in this container.🤖 Generated with Claude Code