Agent Plan / Task List
Preview: Live
An agent's live plan: ordered steps with status and timing, steering notes, and an optional composer.
Import
import { AgentPlan } from '@marbleui/ui'Props
<AgentPlan>
| Prop | Type | Default | Description |
|---|---|---|---|
| status *required | 'running' | 'done' | 'failed' | 'waiting' | — | |
| steps *required | PlanStep[] | — | |
| title *required | ReactNode | — | |
| className | string | — | |
| elapsed | string | — | |
| footer | ReactNode | — | Composer for steering notes (e.g. a PromptInput). |
| meta | ReactNode | — | e.g. "Claude · 5 steps" |
Usage notes
Live checklist of one agent’s work: ordered steps with status, timing, substeps and steering notes. It only displays what you pass, so drive every status from your run’s events. For several agents use AgentOrchestration.
<AgentPlan
title="Migrate users table"
status="running"
steps={[
{ id: 'read', title: 'Read schema and 14 migrations', status: 'done', durationMs: 1400 },
{ id: 'write', title: 'Write migration 0042', status: 'running' },
{ id: 'pr', title: 'Open a pull request', status: 'pending' },
]}
footer={<PromptInput variant="pill" allowAttachments={false} placeholder="Steer the agent…" onSubmit={queueNote} />}
/>
- Step
statusispending,running,done,failed,waitingorskipped; each glyph has its own shape and a text label, and the step list is a polite live region. - The header shows done/total (top-level steps) while running;
elapsedappears only oncestatus="done". - Steering is yours to wire: on submit, add
{ id, text, state: 'queued' }to a step’snotes, then set it topicked-uporinterrupted.
Example source · show· hide
import { useEffect, useState } from 'react'
import { PromptInput } from '../prompt-input'
import { AgentPlan, type PlanStep } from './agent-plan'
const base: PlanStep[] = [
{ id: '1', title: 'Read schema and 14 migrations', status: 'pending', durationMs: 1400 },
{ id: '2', title: 'Plan the split into accounts and profiles', status: 'pending', durationMs: 4600 },
{ id: '3', title: 'Write migration 0042 for Postgres 16', status: 'pending', durationMs: 5200 },
{ id: '4', title: 'Dry run on staging', status: 'pending', durationMs: 2600 },
{ id: '5', title: 'Open a pull request for review', status: 'pending', durationMs: 1800 },
]
function Live() {
const [i, setI] = useState(0)
const [notes, setNotes] = useState<NonNullable<PlanStep['notes']>>([])
useEffect(() => {
if (i > base.length) return
const t = setTimeout(() => setI(i + 1), 1400)
return () => clearTimeout(t)
}, [i])
const steps = base.map((s, k) => ({
...s,
status: (k < i ? 'done' : k === i ? 'running' : 'pending') as PlanStep['status'],
durationMs: k < i ? s.durationMs : undefined,
notes: k === 2 ? notes.map((n) => ({ ...n, state: i > 2 ? ('picked-up' as const) : n.state })) : undefined,
}))
return (
<AgentPlan
className="w-[32rem]"
title="Migrate users table"
meta="Claude · 5 steps"
status={i >= base.length ? 'done' : 'running'}
elapsed="17.6s"
steps={steps}
footer={i < base.length ? <PromptInput variant="pill" allowAttachments={false} placeholder="Steer the agent…" onSubmit={(t) => setNotes((n) => [...n, { id: String(Date.now()), text: t, state: 'queued' }])} /> : undefined}
/>
)
}
export const styles = {
Live,
Done: () => (
<AgentPlan
className="w-[32rem]"
title="Weekly metrics digest"
meta="Claude · 3 steps"
status="done"
elapsed="4m 12s"
steps={[
{ id: 'a', title: 'Pull numbers from three dashboards', status: 'done', durationMs: 42000 },
{ id: 'b', title: 'Summarize changes', status: 'done', durationMs: 18000, substeps: [{ id: 'b1', title: 'Revenue', status: 'done' }, { id: 'b2', title: 'Activation', status: 'done' }] },
{ id: 'c', title: 'Post to #metrics', status: 'done', durationMs: 900 },
]}
/>
),
}Source: packages/ui/src/agent-plan/agent-plan.tsx
Catalog
- Category
- ai-agents
- Priority
- Pro
- Primitive
- composed
- React Aria
- GridList