The tool_call segment — a human-in-the-loop tool call UI
Tool call segment - rendered while waiting for a tool handler to resolve
A segment is data, not an element — it has no tag and dispatches nothing. This one is
AparteToolCallSegment, and it is the object you push into a bubble; something else draws it.
Example
Section titled “Example”{ id: 's1', type: 'tool_call', status: 'resolved', toolCall: { id: 'call_1', name: 'get_weather', input: { city: 'Lille' } }, result: '11°C, overcast.',}Discriminated by type: 'tool_call'.
| Field | Type | Required | Description |
|---|---|---|---|
toolCall | import('./tools.js').AparteToolCall | yes | |
status | 'pending' | 'resolved' | 'aborted' | 'awaiting-approval' | 'rejected' | 'failed' | yes | awaiting-approval — paused for a human decision; rejected — the human (or a policy) declined to run it; aborted — stopped, timed out, or nothing could run it; failed — the handler threw, and result carries the crash’s one line. More |
result | string | — | |
structuredResult | unknown | — | The handler’s structuredContent, when it returned one — the value behind result’s prose. |
Shared by every segment
Section titled “Shared by every segment”From AparteSegmentBase, which is also what a segment type of your own extends.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique segment identifier |
type | string | yes | Segment type discriminator |
isStreaming | boolean | — | Whether segment is currently being streamed |
messageId | string | — | Id of the message this segment belongs to. Stamped on insertion. |
index | number | — | Position in the owning message’s segments[], maintained across insertions and removals. |
meta | Record<string, unknown> & { aparte?: AparteSegmentTiming } | — | Extras the producer of the segment knows — token counts, cost, compute device — plus aparte, the one sub-object core writes. More |
Notes on individual fields
Section titled “Notes on individual fields”status
Section titled “status”awaiting-approval — paused for a human decision; rejected — the human (or a
policy) declined to run it; aborted — stopped, timed out, or nothing could run
it; failed — the handler threw, and result carries the crash’s one line.
Extras the producer of the segment knows — token counts, cost, compute
device — plus aparte, the one sub-object core writes.
Everything except meta.aparte is yours: fill it with
updateSegment(segmentId, { meta }), which MERGES rather than replaces.
Mirrors AparteMessage.metadata.
Who emits it
Section titled “Who emits it”An app pushes one with addSegment() on the viewport, or a parser produces it while a
reply streams. Core stamps its identity and timing on insert, so an emitted segment does not
have to carry them.
Who draws it
Section titled “Who draws it”A built-in renderer draws this one, from packages/core/src/renderers/segments/tool-call.ts. It installs itself the first time a
segment of this type needs it, so a chat renders it with no setup.
Replace it with your own for this config only:
import { registerSegmentRenderer } from '@aparte/core';import type { AparteToolCallSegment } from '@aparte/core';
registerSegmentRenderer({ type: 'tool_call', render: (segment: AparteToolCallSegment) => { const el = document.createElement('div'); el.textContent = JSON.stringify(segment); return el; },});See Customization for the whole renderer seam, and bring your own loop for emitting segments yourself.