Transcript
<cae-transcript> shows a conversation with an agent: chat bubbles for the
prompts, the answers and the model’s reasoning, lines of the agent’s own log,
and a card per tool call that opens on its command and output. A web search
lists the pages it returned as links; a page read shows its URL. Register it
with @contraagent/elements/transcript/define; the types are in
@contraagent/elements/transcript.
Reference: <cae-transcript>
// A conversation with a support bot in two sessions: the first ended with a// failed lookup, the second answered. The host holds the sections and decides// what a reader's pick means: here the picked session is simply shown.import '@contraagent/elements/transcript/define';import { md } from '@contraagent/elements/markdown';import type { TranscriptSection } from '@contraagent/elements/transcript';
const sections: TranscriptSection[] = [ { id: 's1', title: 'Session 1', verdict: { tone: 'failed', text: 'failed', tip: 'The order service did not answer' }, tags: [ { label: 'bot', value: 'helpdesk' }, { label: 'model', value: 'small-2', look: 'mono' }, ], items: [ { type: 'prompt', id: 'p1', text: 'Where is order 1042?' }, { type: 'reasoning', id: 'r1', text: 'Look the order up first, then answer with its **status**.' }, { type: 'tool', id: 't1', name: 'orders.get', icon: 'read', summary: 'order 1042', output: 'timeout after 30s', status: 'error' }, ], footer: { tone: 'failed', title: 'The order service did not answer' }, }, { id: 's2', title: 'Session 2', facts: ['small-2'], verdict: { tone: 'done', text: 'answered' }, items: [ { type: 'event', id: 'e1', icon: '↻', title: 'Request sent again', text: 'after a timeout' }, { type: 'tool', id: 't2', name: 'web_search', icon: 'search', summary: 'carrier tracking 1042', query: 'carrier tracking 1042', links: [ { title: 'Track a parcel', url: 'https://example.com/track' }, { title: 'Delivery times', url: 'https://example.org/delivery' }, ], output: 'Track a parcel https://example.com/track\nDelivery times https://example.org/delivery', status: 'ok', }, { type: 'notice', id: 'n1', text: 'cache hit: orders/1042' }, { type: 'message', id: 'm1', text: 'Order **1042** left the warehouse on Monday and arrives on *Thursday*.' }, ], },];
export function mount(host: HTMLElement): void { const t = document.createElement('cae-transcript'); t.sections = sections; t.markdown = md; t.strings = { sections: 'Sessions', sectionsTip: 'One session per conversation with the bot' }; t.style.height = '420px'; t.addEventListener('cae-section-change', (e) => (t.shown = e.detail.index)); host.replaceChildren(t);}Sections and items
Section titled “Sections and items”The conversation comes in sections, one per session with the agent. Above two
sections, a picker shows one at a time; each section names itself (title),
can add short facts after its name (a model) and a mark of how it ended
(verdict: ✓ with the tone done, ✕ otherwise). A section’s head shows its
tags (who ran it) and its details: folded texts, code (a command line) or
prose (a prompt). A footer after the items says how the section ended, with
a list (errors) or a note.
export interface TranscriptSection { id: string; /** Its name in the section picker. */ title: string; /** Short facts after the title in the picker (a model). */ facts?: readonly string[]; /** How the section ended, in the picker: ✓ with tone `done`, else ✕; `text` for screen readers, `tip` as its tooltip. */ verdict?: { tone: Tone; text: string; tip?: string }; /** The section is still being written: a dot in the picker. */ active?: boolean; /** Chips at the head of the section. */ tags?: readonly TranscriptTag[]; /** Folded texts at the head of the section. */ details?: readonly TranscriptDetail[]; items: readonly TranscriptItem[]; /** A box after the items: how the section ended, and a list (errors) or a note. */ footer?: { tone: Tone; title: string; list?: readonly string[]; note?: string };}An item is a prompt, a message or reasoning (markdown), an event (a glyph, a title and a detail), a notice, or a tool card:
/** One tool call: the card that opens on its start and shows its result. */export interface ToolCard { type: 'tool'; id: string; name: string; /** * The card's glyph: `bash`, `read`, `grep`, `glob`, `edit`, `write`, `task` * or `agent`; any other key shows ⚒. A web search (`query`) and a page read * (`page`) have their own. Set as `data-tool` on the card. */ icon: string; /** One line: the command, pattern, path or URL. */ summary: string; /** Why the call was made, beside the summary. */ intent?: string; /** A web search's query: shown above the pages it returned (`links`). */ query?: string; /** A page the call read: its URL, a link when it is http(s). */ page?: string; /** The command in full, above the output (not for a `query` or a `page`). */ body?: string; /** The result; null while the call runs. */ output: string | null; status: 'running' | 'ok' | 'error'; /** The pages the call returned: listed as links, and counted beside the summary. */ links?: readonly ToolLink[];}
export type TranscriptItem = /** A prompt sent to the agent: its length, the start of it as a tooltip. */ | { type: 'prompt'; id: string; text: string } /** The agent's answer, in markdown. */ | { type: 'message'; id: string; text: string } /** The model's reasoning, in markdown. */ | { type: 'reasoning'; id: string; text: string } /** Something that happened in the session, as a bubble: a glyph, a title and a detail. */ | { type: 'event'; id: string; icon: string; title: string; text?: string } /** A line of the agent's own log. */ | { type: 'notice'; id: string; text: string } | ToolCard;Following and reading
Section titled “Following and reading”shown picks a section (0-based); null follows the latest. While the reader
is at the bottom, new content scrolls into view. Once they scroll up, it stays
where they are, and a button counts what arrived below: count is how much
the transcript holds (by default its sections and items), so a host that
counts events sets it. A section that starts while the reader is scrolled up
in the latest one keeps the one they read on screen. live shows the agent
working, and a tool call with no result as running; without it, such a call
was interrupted.
A host that loads a transcript in pages sets loading until the first page
has arrived, and earlier to the number of lines before it that are not
loaded: a button then asks for them with cae-load-earlier, and the next
sections it gets is the content from further back, which keeps the reader’s
place. For another transcript, give it a new element.
memory keeps what the reader opened and where they were: isOpen and
setOpen for the tool cards, scroll and setScroll for the position, which
is restored once the content has arrived. Without it, both last as long as the
element. reveal(id) opens a tool card and scrolls it into view once its
section is shown; it returns that section’s index for the host to show.
Events
Section titled “Events”cae-section-changewith{index}: the reader picked a section (null: the latest). The host setsshown.cae-section-shownwith{index}: the section on screen changed (-1: none), also when a new section keeps the one being read.cae-load-earlier: the reader asked for the content beforeearlier.
Look and words
Section titled “Look and words”Markdown is turned into HTML by markdown (md of
@contraagent/elements/markdown escapes the text); without it, answers and
reasoning show as plain text. renderDetail draws an open prose detail. The
parts are picker, section, tag, bubble, notice, tool, tool-body
and verdict; tagChips and tagStyles draw tags elsewhere in the same
look. The words are strings (Strings): the picker’s name and
tooltip, the bubbles’ heads, the tool cards’ statuses, the counts.