Tool Call Card
ProInspector for the tool / function calls an agent makes. 4 variants: compact one-line pills, expanded disclosure, streaming (arguments filling in) and error (message, stack trace and retry) — with per-call latency and token attribution, and recursive sub-calls.
import { SvToolCallCardComponent } from '@styloviz-pro/tool-call-card';Demo & usage
Basic usage
ProUsage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Compact pills under a message
ProDense read-only rows that do not steal attention from the answer.
Retry a failed call
ProExpose a Retry button and re-run the tool through your own runtime.
Streaming arguments
ProA running call auto-expands and renders a caret while its arguments arrive.
When to use
- Showing what an assistant actually did while producing an answer.
- Debugging an agent run — which tool was called, with what, and how long it took.
- Surfacing a failed tool call with enough context for the user to retry it.
- Rendering a plan that fans out into sub-calls.
Async states
- loading
- error
- retry
Item identity & owned state
- Restorable state
- openedIds
Bind these to put a saved view back — the open sections, the hidden columns, the selected rows — and read their
Changeevents to store what the user does.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the data is being fetched. |
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| call | ToolCall | null | null | A single call. Ignored when `calls` is non-empty. Set `failure` on an errored call (`transport`, `tool`, `policy`, `timeout` or `cancelled`) to render its cause, meaning and recovery path in place of the generic Failed chip; left unset, the generic state renders. |
| calls | readonly ToolCall[] | [] | Several calls, rendered as a stack. Takes precedence over `call`. |
| variant | ToolCallVariant | 'expanded' | Layout preset. |
| defaultExpanded | boolean | false | Expand every card on first render. |
| showTiming | boolean | true | Show the duration and token chips. |
| retryable | boolean | false | Show a Retry button on failed calls. Never offered on a `policy` refusal. A call with `idempotent: false` gets a duplicate-action warning beside its Retry (louder on a `timeout`), except on a `transport` failure, which never reached the tool. |
| language | CodeLanguage | 'json' | Language used to highlight `args` and `result`. |
| paneMaxHeight | string | '14rem' | Max height of the argument / result panes, as a CSS length. |
| ariaLabel | string | 'Tool calls' | Accessible label for the list of calls. |
| openedIds | ReadonlySet<string> (two-way) | new Set() | Call ids the user opened (two-way). A `model` so a host can restore or persist which calls were open. The variant's own default (`defaultExpanded`, a running streamed call) still applies to calls not listed here. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retryLoad | void | The host should re-run the failed load. Named `retryLoad` because this component already publishes `retry` for re-running a single item — stealing that name would silently change what existing handlers receive. |
| retry | ToolCall | Emits the failed call the user asked to re-run. |
| toggled | ToolCallToggle | Emits whenever a card is expanded or collapsed. |
| openedIdsChange | ReadonlySet<string> | Emits the new `openedIds` whenever it changes — the output half of `[(openedIds)]`. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
ProBound to the names this component actually exposes.
Host class
ProDrive the three async inputs from your own fetch.
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `calls`, `loading` and `error`.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Five failure causes, not one error state: `failure: 'transport' | 'tool' | 'policy' | 'timeout' | 'cancelled'`. Each renders its own chip label, its own meaning and its own recovery line, because three of the five are not the tool's fault and the reader's next move differs in every one. Leave it unset and the card renders the generic **Failed** state it always has — an undeclared cause is never guessed at, since blaming the tool for a network fault sends the reader to the wrong owner.
- A `policy` refusal is offered **no retry at all**, even with `[retryable]` on: it was refused for a reason a second press does not change, and the recovery line says what would actually help.
- `idempotent: false` grows a warning beside the retry — except on a `transport` failure, which never reached the tool: nothing ran, so there is nothing to duplicate. The case it exists for is a non-idempotent tool that *timed out* — 'charge the card' may have charged, may not have, and the button could charge it again. The warning is attached to the control (`aria-describedby`, and folded into the button's label) rather than replacing it: the reader may need to retry, they just have to know what they are risking. It renders directly under the header, so a collapsed card shows it too, and only where a Retry is actually offered — never on a success, a policy refusal or a card with `retryable` off.
- Redaction, from the pack-wide policy (`X-2`): arguments, results, the error message and the stack trace are masked per `provideSvAiPrivacy({ patterns })`, in the expanded panes and in the compact inline summary, with one note per call saying something is hidden rather than one per pane. An error is where a key leaks by accident — a failing client echoes the request it made. Copy is not gated here because there is no copy control to gate — both code blocks render headerless, and the header is where copying lives.
- Four variants: `compact` (one-line pills), `expanded` (disclosure, the default), `stream` (running calls auto-open with a caret) and `error` (message, stack trace and retry).
- Arguments and results render through `sv-code-block`, so JSON is highlighted and scrollable — and nothing is injected as HTML. The blocks render headerless, so the card offers no copy control of its own (see the redaction note).
- Sub-calls in `children` render recursively with depth-capped indentation; the header shows the child count.
- Per-call `durationMs` and `tokens` render as header chips, with a total across a stack of calls; hide them with `showTiming`.
- Emits `retry` (the failed call) and `toggled` (call plus new expanded state) so a host can drive a real runtime.
- Supply one call via `call` or many via `calls` — `calls` wins when both are set.
- Status colours, `language` and `paneMaxHeight` are all inputs; icons come from `@styloviz/icons`.
Best practices
- Do: Use `variant="compact"` beneath a chat message; reserve the expanded card for a dedicated run or debug view.
- Do: Pass `durationMs` and `tokens` when your runtime reports them — cost and latency per call are what make a trace actionable.
- Don't: Do not rely on the card to keep secrets out of `args`, `result`, `error` or `stack` unless you have configured `provideSvAiPrivacy({ patterns })` — with no policy the card renders whatever it is given.
- Don't: Do not pre-format JSON into HTML. Pass the raw string; highlighting is handled by the embedded code block.
- Tip: Set `idempotent: false` on a call whose tool is not safe to repeat. The Retry stays available — the reader may need it — but it carries the duplicate-action warning (except on a `transport` failure, which never reached the tool — nothing ran, so there is nothing to duplicate), and a `policy` refusal never offers one at all.
Accessibility
- Each expandable card is a real `<button>` with `aria-expanded` and `aria-controls` pointing at its panel; a call with nothing to reveal renders no button at all.
- The card stack is a `role="list"` / `role="listitem"` structure with an accessible name from `ariaLabel`.
- An in-flight call exposes its status chip as `role="status"` with `aria-live="polite"`, so a screen reader hears the call complete.
- The Retry button carries an explicit `aria-label` naming the tool, since the visible label is just "Retry".
- Status glyphs are decorative and `aria-hidden`; the status word beside them carries the meaning.
Feedback on Tool Call Card
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.