Tool call

Shows one tool call an agent made as a step of a checklist that opens onto what came back.

Examples

An agent turn at work

support-agentRunning
Running evalrefund-policyRunning

Asking before a refund

Refund $48.00?

Returns $48.00 to Maya Chen's original payment method. It cannot be undone.

Refund $48.00 on ord_4812

Every state, in one list

Preparing replyPreparing
Running evalrefund-policyRunning
Looked upord_4812OK840ms

The orders service timed out after 3s.

Refund $48.00?

Returns $48.00 to Maya Chen's original payment method. It cannot be undone.

Refund $48.00 on ord_4812

Search policyrefund windowStopped

Earlier calls folded

Running evalrefund-policyRunning

Output in its well

PASS  case 01  refund-policy  (12ms)
PASS  case 02  refund-policy  (15ms)
PASS  case 03  refund-policy  (18ms)
PASS  case 04  refund-policy  (21ms)
PASS  case 05  refund-policy  (24ms)
PASS  case 06  refund-policy  (27ms)
PASS  case 07  refund-policy  (30ms)
PASS  case 08  refund-policy  (33ms)

Requests as they happen

Sizes

  • defaultdefault

  • sm

Tool

  • eval

  • Looked upord_4812OK90ms

    lookup

  • Fetchedhelp.example.com/policies/refundsOK640ms

    web fetch

  • ticketId
    tkt_4812
    priority
    high

    any other tool

States

  • rest

  • hover

  • focus-visible

  • pressed

  • Hi Maya, sorry the headphones arrived damaged. I have refunded $48.00 to your original payment method.

    open

Usage

When to use it
Use
An agent calls a tool and the person should see what it asked for and what came back.
Use
A turn lists the calls of a step, in order, with how each ended.
Use
A call needs the person: it is paused for permission or was refused.
Not for
The output is a long shell session the person may type into. Use Code block.

Rules

11 rules
Must
Give every event a per-call seq and dispatch all of them through toolCallsReducer. Because a plain setState applies a duplicate or late running event over completed, so a finished call spins again.
Must
Send status: 'completed' or 'error' explicitly when a tool ends. Because the reducer reads a result with no status as an error, so a call that worked shows Failed.
Must
Dispatch {type: 'stop', gen} before the network cancel returns, and open the next turn with a newer gen. Because events that arrive while the cancel is awaited keep rows running, and reusing the stopped gen drops the next turn.
Must
Hide finished calls with ToolCallList hideCompleted, not a filter of your own. Because a hand filter for running drops failed and awaiting-permission rows, so the run stalls on an approval no one sees.
Should
Call registerTool(name, renderer, aliases) once at module scope for a product tool; never pass label per call site.
Should
Write active as an -ing verb, done in past tense and ask in the imperative, one or two words, with the object in detail.
Must
Use status: 'denied' with the person's feedback as reason for a refused call; never status: 'error'. Because a refusal is the person's choice; as an error it opens on a failure message and counts as a failed call in the turn's line.
Must
Keep the state in the glyph cell and the end of the line to the duration; add a word only for Failed, Stopped and Denied. Because a status word on every row is a column of noise, and the word is what survives grayscale and forced colors when the red is gone.
Must
Pass onReply to a waiting call, and put what the call does in permission.message. Because the ask card shows the detail in full under the question and the cost, and the person decides on those; a call with no message asks the question with nothing to weigh, and with no onReply the card has no way to answer.
Never
Stack ToolCalls in a div. Put them in a ToolCallList. Because the list folds finished calls into one line and keeps the newest visible; a hand-made stack shows every row of a long turn.
Never
Dump the input JSON as the body of a tool call. Because the registry renders the command, patch or capped output, and raw JSON shows escaped quotes with no cap.

Accessibility

7 checks
A row that opens is a button with aria-expanded; its name is label, detail, the status word and the time.
Every status has a word, read aloud when it is not shown: Done, Running, Failed, Denied, Stopped. The glyph is aria-hidden. The root sets aria-busy while pending or running.
Enter or Space opens the body; Tab moves into the body controls; plain rows are not tab stops.
The arc is aria-hidden beside its read-aloud word; a failure message is the first thing in the opened body.
Deny comes before Allow in the tab order, so the first stop in the ask card never allows by accident.
A result before its call shows after 2s as "Orphaned result", never Done.
The ask card is a group named by its question. When it goes, the row takes focus, so a keyboard user is not dropped to the top of the page.

Edge cases

11 edge cases
120-character path
Label and detail truncate to one line; the body wraps anywhere.
No input or output
One plain row, not a button, with no hover wash and no chevron. The glyph cell stays, so its verb lines up with the rows that open.
Narrow screen
The target truncates first, down to 6 characters; on a phone it wraps to a second line instead. The end of the line never wraps.
Command in an approval
Shown in full and wrapped in a code block, never truncated.
Right to left
Spacing and caret mirror; code stays left to right.
Duplicate, out-of-order or replayed events
Dropped and counted; a late stop echo leaves the next turn running.
Finished turn
ToolCallList folds every call into "Worked for 14s" (or "Ran 5 tools"), with a red Failed at its end when a call failed.
Live turn
Completed calls before the newest fold into one line like "Read 2 files, searched once"; the newest stays a row.
Output longer than the cap
Shell shows 24 lines, other tools 8, and 20,000 characters in all, with the cut said.
Forced colors
The words carry every state; the arc keeps its edge.
Reduced motion
The arc holds still; the sr-only word still says running.

API

21 props
call
ToolCallData default required
call.tool and the input it reads
built-in renderers
call.startedAt, call.endedAt
number
call.permission
{patterns: string[]; message?: string}
size
'default' | 'sm' default 'default'
onReply
(reply: 'once' | 'reject') => void
actions
ReactNode
detailMono
boolean default the renderer's
label
string default from the registry
doneLabel
string default past tense of label
detail
string default from the registry
duration
number
children
ReactNode
defaultOpen
boolean default true when the call failed
ToolCallList calls
readonly ToolCallData[] default required
ToolCallList live
boolean default any call not ended
ToolCallList hideCompleted
boolean default false
ToolCallList orphanTtl
number default 2000
ToolCallList onReply
(call, reply) => void
useToolCalls(initial)
[{calls, gen, dropped}, dispatch] default initial: []
labels
ToolCallLabels default English words

Tokens

10 tokens
--line-frame
oklch(var(--ink-c) / var(--line-frame-alpha))
--surface-hover
oklch(var(--ink-c) / 0.05)
--surface-card
oklch(var(--grey-c))
--fail-text
oklch(51% 0.180 26)
--text-2
oklch(var(--ink-c) / 0.80)
--type-tag
var(--type-code-sm)
--type-body-sm
380 13px/18px var(--font-sans)
--tool-detail-min
6ch
--code-prompt
oklch(var(--ink-c) / 0.40)
--ease-step
cubic-bezier(0.2, 0, 0, 1)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.