Create workflow "Morning briefing"?
create-workflow Changes things
Input Name Morning briefing Schedule Every day at 9:00 AM Central time
Approved by you 4:42 PM
One turn's tool calls on a rail: a one-line row per call, the first and last few with the middle folded, failures and calls that need a person always in view, and the run's end state.
Reference usage
One turn's tool calls on a rail. Each call is one line that never wraps: the tool's name as a person reads it (its identifier is a tooltip), the muted key input, → result, and the duration at the end. A long turn keeps its first and last few calls and folds the middle behind one dashed button that says what it hides. A call that failed, was denied, is waiting on a person or is still running never folds, and a failed call carries one danger line with its consequence and quiet links instead of a filled error block.
Use ToolCallRail for the tool calls of a chat turn, where the agent's reply leads and the calls follow. Use RunStepTimeline for the steps of a run with their own timing, retries and branches, and Timeline for dated events. The rail is built on Timeline's rows variant, because run pages and chat read the same kind of sequence: Timeline draws the marker column and the connector, and the rail draws what sits in each row, since a tool call has no timestamp to lead with. A folded group is one entry of its own.
<script lang="ts">
import { ToolCallRail } from '@lostgradient/cinder';
import type { ToolCallRailCall } from '@lostgradient/cinder';
const calls: ToolCallRailCall[] = [
{
id: 'search',
name: 'Search orders',
identifier: 'search-orders',
input: 'carrier Parcelly, damaged',
result: '14 orders',
duration: '0.6s',
status: 'done',
},
{
id: 'lookup',
name: 'Lookup order',
identifier: 'lookup-order',
input: '4471',
result: '$42.00, delivered Mon',
duration: '0.2s',
status: 'done',
},
{
id: 'refund',
name: 'Issue refund',
identifier: 'issue-refund',
input: '4480, $27.25',
status: 'waiting',
},
];
</script>
<ToolCallRail {calls} duration="1.2s" />Used N tools line, no rail connector and no fold.Used 3 tools) and every call still shows. It adds 1 failed and 1 waiting on you after commas, each with its dot, only when such calls exist, and the turn's duration when you pass one.Show 2 more, 2 done: Lookup order ×2 between them. A folded group hides at least two calls, because hiding one saves nothing, so seven calls show all seven.×N when more than once) and counts every other call as 6 others.Show N more opens that group in place, in time order, and a Show fewer button after the last row folds every open group again.open={false}) leaves the summary and the pinned calls, so nothing that needs a person is ever folded away.running, the summary reads Used 12 tools so far… with a spinner and no counts, and the rail keeps the first 3 calls and the ones under way instead of the last 3.status picks the glyph on the rail and the words:
done: a neutral grey dot, the result after an arrow.approved: a green dot, for a call a person approved. decision writes what was decided in the person's words, without an arrow (Approved by you 10:33 AM, $18.50 refunded); without it the row shows result after an arrow like a done call.running: a spinner in place of the dot, Running… in place of a result, and the elapsed time.waiting: an amber shield and Waiting on you, as its own row.denied: a prohibition glyph and neutral words. It is not called failed and carries no error block.interrupted: a red circled cross and the result in red with no arrow, for a call the run ended on before it answered. Word it by the tool's risk: Interrupted, didn't finish for a tool that only reads, Interrupted, may have run for one that changes things. It never folds and is never a green or neutral dot.failed: a red cross and the result in red. failure adds one danger-text line with an error glyph and the quiet links Details (when onSelect is set), Ask the agent to retry (onRetry) and Copy input (onCopyInput).Set onSelect and every row is a button. The row whose id is selectedId is tinted and keeps the quiet focus ring while its panel is open. Call focusCall(id) on the component to hand focus back to a row when the panel closes.
A call with resultHref shows its result (the arrow and the words) as a link to that address, such as the page of a workflow the call created. A link cannot sit inside a button, so with onSelect such a row is a container with a button that covers it and the link above the button: the link is its own click target, and a click anywhere else on the row selects the call.
A call with nested: true draws the children snippet under its row: the approval a waiting call is waiting on, or the decided approval under a call that has been approved. Nothing gates it on the call's status.
<script lang="ts">
import { ApprovalCard, ToolCallRail } from '@lostgradient/cinder';
let selectedId = $state<string | undefined>(undefined);
</script>
<ToolCallRail
calls={[
{
id: 'refund',
name: 'Issue refund',
identifier: 'issue-refund',
input: '4480, $27.25',
status: 'waiting',
nested: true,
},
]}
{selectedId}
onSelect={(id) => (selectedId = id)}
>
{#snippet children(call)}
<ApprovalCard
layout="row"
tool={{ name: call.identifier ?? call.id, label: call.name, risk: 'high' }}
question="Refund order 4480 for $27.25?"
operation={{ kind: 'other' }}
policyVersion="policy-1"
idempotencyKey="approval-4480"
state="pending"
/>
{/snippet}
</ToolCallRail>In a container narrower than 28rem (a phone) each row has two lines: the name and duration on line one, and a muted line of key input and result, cut with an ellipsis, on line two. The summary drops the duration, the dashed button keeps Show 24 more beside the short 24 done, and the failure line gives way to the call's panel.
calls ToolCallRailCall[] required The calls of one turn, in order.
running boolean optional The turn is still running. Defaults to false.
duration string optional How long the turn took, formatted by the caller.
open boolean (bindable) optional Whether the rail is open. Defaults to true.
selectedId string optional The call whose panel is open.
onSelect (id) => void optional Makes rows buttons; fired with the call's id.
onRetry (id) => void optional Shows "Ask the agent to retry" on failed calls.
onCopyInput (id) => void optional Shows "Copy input" on failed calls.
endLabel string optional The run's end state on its own line ("Run finished, no reply").
footnote string optional A muted line under the rail.
label string optional Accessible name. Defaults to "Tool calls".
labels ToolCallRailLabels optional The rail's words, for another language.
class string optional Extra class names.
children Snippet<[call]> optional Content under the rows of calls that set `nested`.
id string required Stable identity.
name string required The tool's name as a person reads it.
identifier string optional The tool's identifier, in a tooltip on the name.
input string optional The key input, muted and truncated.
result string optional What came back, in one line.
decision string optional What a person decided, in their own words and without an arrow.
resultHref string optional Where the result goes; the result on the row becomes a link to it.
duration string optional How long it took, formatted by the caller.
status done | approved | running | waiting | denied | failed | interrupted required
failure string optional A failed call's consequence, in one danger line.
nested boolean optional Draw `children` under this row.
create-workflow, Changes things
Input Name Morning briefing, Schedule Every day at 9:00 AM Central time
Approved by you, 4:42 PM
A tool that changes things is one rail row with its approval nested under it, drawn as decided: "Approved by you, 10:42 AM" shows once, on the nested approval, and the row itself carries the result.
Closed, the rail shows only the summary line "Used 30 tools, 1 failed, 1 waiting on you, 18s" with its chevron pointing right. The call that failed and the call waiting on you stay under it, with the failure line, its links and the approval, so nothing that needs a person is ever folded away.
A call a person refused reads Denied, in neutral text with a prohibition glyph: it is not called Failed and carries no error block. The words are the caller's ("Denied by you 10:40 AM, nothing refunded"), and a denied call never folds away.
A finished turn whose last step is a tool call closes the rail with the run's end state, "Run finished, no reply", on its own line with a check glyph, so the turn does not look hung. A call a person approved has a green dot ("Approved by you 10:33 AM, $18.50 refunded").
A failure in the middle of a run stays on the rail between two folded groups: the first 3 calls, "Show 10 more, 10 done: Lookup order ×10", the failed Send email with its danger line and links, "Show 13 more, 13 done: Check inventory ×4, Lookup customer ×4, Search orders ×3, 2 others", then the last 3 calls. Each group counts only the calls it hides.
When the run ends while a call is under way, nobody knows how the call ended. The row shows a red circled cross and its result in red, with no arrow and never a green or neutral dot. The words follow the tool's risk: "Interrupted, didn't finish" for a tool that only reads, "Interrupted, may have run" for one that changes things. An interrupted call never folds away.
The first 3 and last 3 calls stay on the rail around one dashed "Show 24 more" button that says what it hides, counted from this turn only. The failed Send email keeps its danger line with Details, Ask the agent to retry and Copy input, and the call waiting on you stays its own row with its approval beneath it. Choosing a row selects it; "Show 24 more" opens the hidden calls in place and "Show fewer" folds them again.
In a narrow container each row has two lines: the name and duration on line one, and a muted line of key input and result, cut with an ellipsis, on line two. The summary reads "Used 30 tools, 1 failed, 1 waiting on you" with no duration, the dashed button says "Show 24 more" with the short "24 done", the failed row is red with a red cross and the waiting row is amber. The approval nests under the waiting row, and the failure line gives way to the panel.
While the turn runs the summary reads "Used 12 tools so far…" with a spinner and no failed or waiting counts. The rail keeps the first 3 calls, one dashed "Show 8 more" button and the call under way, which shows a spinner in place of the dot, "Running…" in place of a result and the elapsed time. A second rail shows a short turn whose last call is still running.
The row whose panel is open is tinted and keeps the shared quiet focus ring, 2px and wrapping the whole row, while its panel is open. Choosing it again closes the panel; Esc or Close in the panel hands focus back to that row through focusCall.
One or two calls are plain rows: no "Used N tools" line, no rail and no fold. From 3 calls the summary line appears and every call still shows. From 8 calls the middle folds, with the first 3 and last 3 around "Show 2 more, 2 done: Lookup order ×2". With 7 calls hiding one row would save nothing, so all 7 show.
Name | Type | Default | Description |
|---|---|---|---|
calls required | ToolCallRailCall[] | — | The calls of one turn, in the order they were made. One or two calls draw as plain rows with no summary and no rail; from three the summary line and the rail appear; from eight the middle folds, when at least two calls would hide. |
running | boolean | false | Whether the turn is still running. The summary reads "Used 12 tools so far…" with a spinner and no failed or waiting counts, and the rail keeps the first calls and the ones under way rather than the last three. |
duration | text | — | How long the whole turn took, formatted by the caller ("18s"), at the end of the summary line. Left out in a narrow container. |
open bindable | boolean | true | Whether the rail is open. Closed, the summary line alone shows, with the calls that failed, were denied or need a person still under it. Bindable. |
selectedId | text | — | The call whose panel is open: its row is tinted and keeps the quiet focus ring. |
onSelect | ((id: string) => void)|undefined | — | Fired with a call's id when its row is chosen. Rows are buttons only when this is set. |
onRetry | ((id: string) => void)|undefined | — | Fired when the person asks the agent to retry a failed call. The link shows only when this is set. |
onCopyInput | ((id: string) => void)|undefined | — | Fired when the person copies a failed call's input. The link shows only when this is set. |
endLabel | text | — | The run's end state, on its own line after the last row, in neutral text with a check glyph ("Run finished, no reply"). Set it when the turn ends on a tool call, so it does not look hung. |
footnote | text | — | A muted line under the rail ("Failures and calls that need you stay visible wherever they fall."). |
label | text | 'Tool calls' | An accessible name for the rail. |
labels | {
/** The summary line. Defaults to "Used 30 tools", or "Used 12 tools so far…" while the turn runs. */
used?: ((count: number, running: boolean) => string)|undefined;
/** The summary line's failed count. Defaults to "1 failed". */
failed?: ((count: number) => string) | undefined;
/** The summary line's waiting count. Defaults to "1 waiting on you". */
waiting?: ((count: number) => string)|undefined;
/** The button over a folded group. Defaults to "Show 24 more". */
showMore?: ((count: number) => string)|undefined;
/** The summary beside the group button. Defaults to "24 done: Lookup order ×10, 6 others". */
folded?: ((count: number, names: string) => string)|undefined;
/** The summary beside the group button in a narrow container. Defaults to "24 done". */
foldedShort?: ((count: number) => string)|undefined;
/** The button that folds every open group again. Defaults to "Show fewer". */
showFewer?: string|undefined;
/** The result of a `running` call. Defaults to "Running…". */
running?: string|undefined;
/** The result of a `waiting` call. Defaults to "Waiting on you". */
waitingResult?: string 6 more members|undefined;
/** The result of a `denied` call that has none. Defaults to "Denied". */
denied?: string|undefined;
/** The result of an `interrupted` call that has none. Defaults to "Interrupted". */
interrupted?: string|undefined;
/** The link on a failed call that opens its details. Defaults to "Details". */
details?: string|undefined;
/** The link on a failed call that asks the agent to retry. Defaults to "Ask the agent to retry". */
retry?: string|undefined;
/** The link on a failed call that copies its input. Defaults to "Copy input". */
copyInput?: string|undefined;
} | — | The rail's words. |
children | Snippet<[ToolCallRailCall]>|undefined | — | Content under a call's row: the approval a waiting call is waiting on. Drawn for the calls that set nested. |