Pro

Agent task

Pro block

The live view of an agent doing a multi-step task: a phase whose color runs through the card, a checklist, a panel that shows what it's reading, searching, weighing, writing or revising, the tool in use, the model, and tokens, cost and time that never shift. Pause, stop, and run again.

An agent fixing a bug in Acme

Fix rounding in split bills

0/5
  1. To do: Read split-bill.ts
  2. To do: Look up how others split cents
  3. To do: Choose an approach
  4. To do: Write the fix
  5. To do: Run the tests and review

Getting started…

  • GitHub
  • Files
  • Figma
  • Linear
  • npm

0tokensCost: $0.000Time: 0:00

Five steps: reading the file, searching, weighing the options, writing the fix and reviewing it. Pause holds the clock; Stop ends the run where it is.

Every state

Fix rounding in split bills

4/5
  1. Done: Read split-bill.ts
  2. Done: Look up how others split cents
  3. Done: Choose an approach
  4. Done: Write the fix
  5. In progress: Run the tests and review

split-bill.tsrevised

Each share was Removed: rounded on its own Added: counted in whole cents, so $100 for three Removed: came to $99.99 Added: splits $33.34, $33.33, $33.33. 14 tests pass.

  • GitHub
  • Files
  • Figma
  • Linear
  • npm, in use

20.0ktokensCost: $0.060Time: 0:00

Installation

This is a Pro block. UI Polish Pro costs $149 one time and includes every Pro component, block and template, now and later. Add your license key to your MCP connection or components.json, then install it either way below.

With UI Polish connected, ask your agent:

Add the UI Polish agent task to this project.

Or with the shadcn CLI:

npx shadcn@latest add @uipolish/agent-task
Package@base-ui/react
Brings inBrand marks, Button, Icons, Model picker, Text, Text morph

The live view of an agent working through a task of several steps. One color, the accent while it works, follows its state through the phases (reading, searching, thinking, writing or reviewing), and everything that means "now" takes it: the agent's tile, the phase's name, the bar under the title, the checks on the steps, and the tool in use. The tile shows the phase's own glyph (a file, a magnifier, an ellipsis, a pencil, an eye), so you can tell what the agent is doing at a glance. Under the steps, a panel of fixed height shows what the agent is looking at: the file it's reading, its search and the results, the notes it's weighing, the text it's writing with a caret, or a revision with what it struck out and what it put in. At the foot: its tools, the model, and tokens, cost and time that count up without moving anything. Pause, Stop, and Run again when it's over.

TSX
<AgentTask  agent="Coding agent"  task="Fix rounding in split bills"  phase={run.phase} // "reading" | "searching" | "thinking" | "writing" | "reviewing"  status={run.status} // "running" | "paused" | "done" | "failed" | "stopped"  steps={run.steps} // [{ id, title, status }]  context={run.context} // what the panel shows; see Context  error={run.error}  tools={[    { id: "github", name: "GitHub", brand: "github" },    { id: "files", name: "Files", icon: <Icon name="file" /> },    { id: "linear", name: "Linear", brand: "linear" },    { id: "npm", name: "npm", brand: "npm" },  ]}  activeTool={run.tool}  usage={{ tokens: run.tokens, cost: run.cost, startedAt: run.startedAt, endedAt: run.endedAt, pausedMs: run.pausedMs }}  model={model}  models={models}  onModelChange={setModel}  onPause={() => agent.pause(run.id)}  onResume={() => agent.resume(run.id)}  onStop={() => agent.stop(run.id)}  onRunAgain={() => agent.start(run.task)}/>

It's controlled: your agent reports its phase, steps, what it's looking at and what it has spent, and the card draws it. Nothing in it fakes progress; the only timer is the clock.

Anatomy

  • Head. A 40px tile with the phase's glyph on its tint; the agent's name and the phase in words; the task as the title (the hero, at the heading step); then a 4px bar with "2/5" at its end.
  • Steps. One 32px row each: a ring for to do, a small arc turning for the one in progress (the only loop in the card), a filled check for done, a red "!" for failed. The step in progress is in the text color; the rest step back.
  • Context. A panel of fixed height that shows one thing at a time. When the kind changes, the old one leaves, then the new one rises in; the two are never seen at once.
  • Foot. A 40px row of tools, the one in use lit, and the model picker; then a 48px row with tokens, cost and time on the left and the controls on the right.

When to use it

  • An agent running a task of 3 to 8 steps while the person watches: fixing a bug, drafting a document, planning an event, triaging an inbox.
  • When the person needs to see what it's on, step in (pause, stop), and know what it cost.

When not to

  • One tool call, or a list of calls with their raw input and output. That's tool-steps, which sits in a thread and folds away.
  • Research across many web pages. research-progress shows the pages.
  • The same action on many items: batch-progress.
  • Work that runs while the person is elsewhere: put it in a background-run, and show this on the run's own page.

States

statusHeadBarStepsContextControls
runningThe phase's glyph and name, in the accentAccentChecks in the accent; the arc turnscontextStop, Pause
pausedA pause glyph, "Paused", greyHoldsThe arc holds where it wasHolds; the caret stopsStop, Resume
doneA check, "Complete", greenFull, greenAll greenA summary context, if you pass oneRun again
failed"!", "Failed", redRed, where it stoppedDone steps grey, the failed one red with "!""Step 5 of 5 failed: …" and errorTry again
stoppedA stop square, "Stopped", greyGreyDone steps grey; put the running one back to pendingWhat it was on, fadedRun again

A control shows only when you pass its callback. Pause and Resume, then Run again, are one button that changes its name, so focus stays on it; Pause and Resume reserve each other's width, so trading them moves nothing. When Stop goes, focus that was on it moves to that button.

Phases. Every phase is the accent: its name and glyph say which one, so the card adds no hues of its own.

phaseDefault labelGlyph
readingReading filefile
searchingSearching websearch
thinkingThinkingmore (an ellipsis)
writingWritingpencil
reviewingReviewingeye

Green and red stay for done and failed. Each phase is still its own variable on the root (--at-reading, --at-reading-text and so on), so a product can give one a color. Pass phaseLabel for your own words ("Reading the issue"); keep it to two or three.

Progress. The bar defaults to the steps: each done or skipped step counts one and the running one a half. Pass progress (0 to 1) if you know better.

Context

context is one of six kinds. When the kind changes (or the file's path does), the panel trades layers; within one, the content updates in place and only what's new arrives. A search keeps its layer while the query is typed and the results come in.

kindFieldsWhat it shows
filepath, lines?, startLine?The path in mono, then the lines with their numbers. Without lines, placeholder lines with one band of light across them.
searchquery, results, running?The query in a chip, a caret after it while running; results arrive one by one, each with its site's mark from brand-marks.
notesnotesWhat it's weighing, a few words each. The newest is in the phase's tint; the rest step back.
writingpath?, text, streaming?, code?The text so far with a caret. The panel follows the end while the reader hasn't scrolled up. code sets it in mono.
reviewpath?, note?, parts, code?Text where { removed } is struck by a line drawn across it and { added } arrives in the phase's color once the strike lands.
summarytitle, detail?A check and one line: "Split bills add up to the cent". detail defaults to "5 steps · 10.5k tokens · 0:15".

The panel is 160px tall in every phase, so the card never changes height as the phases go by. Set --at-context-height on the card for more. Anything taller than the panel scrolls inside it, fading at the edge that has more, and the panel takes focus so a keyboard can scroll it too.

Props

PropTypeDefault
agentstring"Agent"The agent's name in the head and in what screen readers hear.
taskstringThe task, as its title. Names the card.
phaseAgentPhaseWhat it's doing now. Sets the color.
phaseLabelstringper phaseThe phase in words, after the agent's name, while running.
statusAgentStatus"running"
stepsAgentStep[]{ id, title, status: "pending" | "running" | "done" | "failed" | "skipped" }.
contextAgentContextSee Context. Without one, "Getting started…".
errorstringfailed: what happened, and what it means.
progressnumberfrom steps0 to 1.
toolsAgentTool[][]{ id, name, brand?, icon? }. A tool without brand or icon shows its first letter.
activeToolstringThe tool in use. Nothing is lit once the run is over.
usageAgentUsage{ tokens, cost, startedAt?, endedAt?, pausedMs? }.
formatCost(dollars) => string"$0.032"
model, models, onModelChangeThe model picker shows when all three are passed.
onPause, onResume, onStop, onRunAgain() => void
classNamestring

formatTokens(n) is exported too: 842 gives "842", 10480 gives "10.5k".

Counters

Tokens, cost and time are set in mono, and each sits in a box sized by the widest value of its shape ("88.8k", "$8.888", "88:88"), so a 1 turning into an 8 never nudges what's beside it. The "2/5" by the bar is in the text face, which has no tabular figures, so it reserves every digit in turn. The clock counts while running, from startedAt, less pausedMs; it holds while paused and stops at endedAt. When you resume, add the time spent paused to pausedMs. It starts after the card mounts, so the server and the browser render the same HTML.

Motion

WhatDurationEasing
The phase color moves through the card (tile, name, bar, checks, tool)--ui-dur-base--ui-ease-color
The tile's glyph: the old one's strokes retract, the new one's draw inas iconsas icons
The phase's name morphs into the next; nothing sits after it, so nothing moves--ui-dur-move--ui-ease-move, via text morph
The bar grows to its new length--ui-dur-glide (320ms)--ui-ease-glide: lands without passing its end
The arc on the step in progress turns (the one loop)a third of --ui-dur-sweep per turnlinear
A step's icon changes--ui-dur-fast in, --ui-dur-instant out, scale on --ui-ease-snap
A check's tick draws on--ui-dur-base--ui-ease-draw
The context trades layers in turn: the old fades out, then the new fades in and rises 4px--ui-dur-instant out, then --ui-dur-base in--ui-ease-exit, --ui-ease-enter
A result or note arrives: fade and 3px rise--ui-dur-fast--ui-ease-enter
A strike draws across what's removed, then what's added fades in--ui-dur-base, then --ui-dur-fast--ui-ease-draw, --ui-ease-enter

What's there when it mounts doesn't animate. Pause holds every loop where it is. Under prefers-reduced-motion: reduce, nothing loops or travels: the caret stops, the arc breathes slowly in place instead of turning, the bar changes at once, layers and items fade in without rising, and the strike is there from the start.

Layout

The card is one column as wide as its container, so on a 320px phone its panels narrow with it instead of widening it.

Accessibility

  • The card is a section named by the task. The steps are an <ol>, each with its status in words ("Done: Read split-bill.ts").
  • The bar is a progressbar with "3 of 5 steps done".
  • A polite role="status" region speaks milestones only: each phase ("Coding agent: Writing"), a pause, and how it ended, with the error. Never the counters or the streaming text.
  • The context panel is a region. Removed and added text are <del> and <ins> with "Removed:" and "Added:" for screen readers. The tool in use is named in words ("npm, in use").
  • Phase and status are always in words; color only repeats them. Their text reads the status text roles, so it holds 4.5:1.
  • Under 460px wide, every tool folds to its mark in a capsule of one width; names stay for screen readers, and nothing folds or opens as the tool in use changes. Under 400px, Stop and Pause fold to icon capsules named by aria-label; "Run again" keeps its words. Under 330px the tools and model, and the counters and controls, take a row each, and the controls get their names back. The card keeps one height in every state at every width. Nothing is cut: titles, paths, results and notes wrap.

Before / after

Before: a spinner and "Working…", then a wall of text.

TSX
{working ? <Spinner label="Working…" /> : <Markdown>{result}</Markdown>}

After: the person sees the plan, what's being read, searched, weighed and written, which tool and model it's using, and what it costs, and can pause or stop it.

TSX
<AgentTask  agent="Coding agent"  task={run.title}  phase={phaseOf(run.lastEvent)} // map your agent's events onto the five phases  status={run.status}  steps={run.plan.map((s) => ({ id: s.id, title: s.title, status: s.state }))}  context={contextOf(run.lastEvent)} // { kind: "file", path, lines } and so on  error={run.error?.message}  tools={tools}  activeTool={run.lastEvent?.tool}  usage={run.usage}  onPause={() => agent.pause(run.id)}  onResume={() => agent.resume(run.id)}  onStop={() => agent.stop(run.id)}  onRunAgain={() => agent.start(run.title)}/>