Saltar al contenido principal

Agent Run

Un agente corriendo en una sola tarjeta. Sus pasos, las herramientas que llama cada uno, el razonamiento detrás y la respuesta a la que llega.

Refactoring the auth module1m 42sDone

The session and token stores now share one CookieStore interface. Both adapters live next to it, the parsing happens once, and every call site compiles unchanged.

Tests pass. Nothing else in the module needed touching.

4 of 4 steps
1'use client';
2
3import { AgentRun, type AgentStep } from '@/components/ui/agent-run';
4
5const STEPS: AgentStep[] = [
6 {
7 id: 'read',
8 title: 'Read the auth module',
9 status: 'complete',
10 detail: 'Scanned 12 files and the three tests that cover them',
11 files: ['auth.service.ts', 'session-store.ts', 'token-store.ts'],
12 },
13 {
14 id: 'plan',
15 title: 'Plan the refactor',
16 status: 'complete',
17 reasoning:
18 'The session store and the token store read the same cookie and expose the same three methods. One interface with two adapters removes the duplicated parsing and keeps both call sites untouched.',
19 },
20 {
21 id: 'write',
22 title: 'Rewrite the session store',
23 status: 'complete',
24 tools: [
25 {
26 id: 'read-file',
27 name: 'read_file',
28 status: 'complete',
29 description: 'Reads a file from the workspace',
30 input: { path: 'src/auth/session-store.ts' },
31 output: { lines: 84, exports: ['SessionStore', 'createSessionStore'] },
32 },
33 {
34 id: 'write-file',
35 name: 'write_file',
36 status: 'complete',
37 description: 'Writes a file to the workspace',
38 input: { path: 'src/auth/session-store.ts', bytes: 2210 },
39 output: 'ok',
40 },
41 ],
42 files: ['session-store.ts'],
43 },
44 {
45 id: 'test',
46 title: 'Run the tests',
47 status: 'complete',
48 detail: '3 files, 14 tests, all green',
49 },
50];
51
52const STARTED = new Date('2026-09-22T10:00:00Z');
53const FINISHED = new Date('2026-09-22T10:01:42Z');
54
55export function Default() {
56 return (
57 <div className="w-full max-w-xl">
58 <AgentRun
59 title="Refactoring the auth module"
60 status="complete"
61 steps={STEPS}
62 startedAt={STARTED}
63 finishedAt={FINISHED}
64 usage={{ used: 38400, max: 200000 }}
65 answer={
66 <>
67 <p>
68 The session and token stores now share one <code>CookieStore</code> interface. Both
69 adapters live next to it, the parsing happens once, and every call site compiles
70 unchanged.
71 </p>
72 <p>Tests pass. Nothing else in the module needed touching.</p>
73 </>
74 }
75 />
76 </div>
77 );
78}

Instalación

pnpm dlx nachui add agent-run

Anatomía

1import { AgentRun, type AgentStep } from '@/components/ui/agent-run';
1<AgentRun
2 title="Refactorizando el módulo de auth"
3 status="running"
4 steps={steps}
5 startedAt={startedAt}
6 usage={{ used: 38400, max: 200000 }}
7 answer={<p>Los stores de sesión y de token ahora comparten una interfaz.</p>}
8/>
Le pasás la corrida como datos y arma todo: un encabezado con el estado y el tiempo transcurrido, una barra de progreso, un Task por paso, la respuesta como Response y el uso de tokens como un medidor Context en el pie. Si le pasás children, elegís y ordenás las partes vos.

Composición

AgentRun es un hybrid: está hecho de elements que ya tenés, cableados en orden. Abrís el archivo y los encontrás con esta forma:
AgentRun
├── AgentRun.Header
├── AgentRun.Progress
├── AgentRun.Steps
│ └── Task (uno por paso)
│ ├── Task.Trigger
│ └── Task.Content
│ ├── Task.Item
│ ├── Task.File
│ ├── Reasoning
│ └── Tool
│ ├── Tool.Header
│ ├── Tool.Input
│ └── Tool.Output
├── AgentRun.Answer
│ └── Response
└── AgentRun.Footer
└── Context

Forma de un paso

Cada entrada de steps es un AgentStep. Solo id, title y status son obligatorios; el resto aparece cuando está.
CampoTipoDescripción
idstringKey estable
titlestringEtiqueta del paso
status'pending' | 'active' | 'complete' | 'error'Define el ícono del Task y qué paso se abre
detailstringUna línea debajo del título
filesstring[]Se muestran como chips Task.File
reasoningstringVa en un panel Reasoning, cerrado
toolsAgentTool[]Se muestran como tarjetas Tool con entrada y salida
Un AgentTool es { id, name, status, description?, input?, output?, error? }, donde status es el ToolStatus del element Tool. Una herramienta que falló arranca abierta, así el error no queda escondido detrás de un click.

En vivo

El paso activo se abre solo y los terminados se pliegan, la barra avanza con cada paso completado, la respuesta llega con un cursor cuando empieza, y el tiempo se detiene cuando termina la corrida.
Adding a composer to the docsWaiting
1'use client';
2
3import { useEffect, useRef, useState } from 'react';
4import { AgentRun, type AgentRunStatus, type AgentStep } from '@/components/ui/agent-run';
5
6const PLAN: AgentStep[] = [
7 { id: 'read', title: 'Read the docs page', status: 'pending', detail: 'Fetching llms.txt' },
8 {
9 id: 'find',
10 title: 'Find the component',
11 status: 'pending',
12 reasoning: 'The request mentions a composer with attachments, which matches prompt-input.',
13 tools: [
14 {
15 id: 'search',
16 name: 'search_registry',
17 status: 'complete',
18 input: { query: 'composer attachments' },
19 output: { slug: 'ai/prompt-input', score: 0.93 },
20 },
21 ],
22 },
23 {
24 id: 'install',
25 title: 'Install it',
26 status: 'pending',
27 files: ['prompt-input.tsx', 'attachments.tsx'],
28 },
29 { id: 'verify', title: 'Type check', status: 'pending', detail: 'tsc --noEmit' },
30];
31
32const ANSWER =
33 'Installed prompt-input and attachments into components/ui. The type check passes and the demo is on the page.';
34
35const STEP_MS = 1400;
36const CHAR_MS = 18;
37
38export function Live() {
39 const [status, setStatus] = useState<AgentRunStatus>('idle');
40 const [steps, setSteps] = useState<AgentStep[]>(PLAN);
41 const [answer, setAnswer] = useState('');
42 const [startedAt, setStartedAt] = useState<number | undefined>();
43 const [finishedAt, setFinishedAt] = useState<number | undefined>();
44 const timers = useRef<number[]>([]);
45
46 const clear = () => {
47 timers.current.forEach((timer) => window.clearTimeout(timer));
48 timers.current = [];
49 };
50
51 useEffect(() => clear, []);
52
53 const start = () => {
54 clear();
55 setStatus('running');
56 setSteps(PLAN.map((step, index) => ({ ...step, status: index === 0 ? 'active' : 'pending' })));
57 setAnswer('');
58 setStartedAt(Date.now());
59 setFinishedAt(undefined);
60
61 PLAN.forEach((_, index) => {
62 timers.current.push(
63 window.setTimeout(
64 () => {
65 setSteps((current) =>
66 current.map((step, position) => {
67 if (position <= index) return { ...step, status: 'complete' };
68 if (position === index + 1) return { ...step, status: 'active' };
69 return step;
70 }),
71 );
72 },
73 STEP_MS * (index + 1),
74 ),
75 );
76 });
77
78 const answerStart = STEP_MS * (PLAN.length + 1);
79 for (let index = 1; index <= ANSWER.length; index += 1) {
80 timers.current.push(
81 window.setTimeout(() => setAnswer(ANSWER.slice(0, index)), answerStart + CHAR_MS * index),
82 );
83 }
84 timers.current.push(
85 window.setTimeout(
86 () => {
87 setStatus('complete');
88 setFinishedAt(Date.now());
89 },
90 answerStart + CHAR_MS * ANSWER.length + 200,
91 ),
92 );
93 };
94
95 return (
96 <div className="flex w-full max-w-xl flex-col gap-4">
97 <button
98 type="button"
99 onClick={start}
100 disabled={status === 'running'}
101 className="border-border hover:bg-muted w-fit rounded-full border px-3 py-1.5 text-xs transition-colors disabled:opacity-50"
102 >
103 {status === 'running' ? 'Running' : status === 'complete' ? 'Run again' : 'Start the run'}
104 </button>
105 <AgentRun
106 title="Adding a composer to the docs"
107 status={status}
108 steps={steps}
109 startedAt={startedAt}
110 finishedAt={finishedAt}
111 answer={answer ? <p>{answer}</p> : undefined}
112 />
113 </div>
114 );
115}

Referencia de API

AgentRun

PropTipoDefaultDescripción
stepsAgentStep[]-La corrida, una entrada por paso
status'idle' | 'running' | 'complete' | 'error''idle'Estado de toda la corrida
titlestring-Se imprime en el encabezado
answerReactNode-La respuesta final, dentro de Response
usage{ used: number; max: number }-Uso de tokens para el medidor del pie
startedAtDate | number-Inicio del reloj
finishedAtDate | number-Detiene el reloj
labelsPartial<AgentRunLabels>InglésNombres de estado, etiquetas de entrada y salida, progreso
defaultOpenboolean-Fuerza todos los pasos abiertos o cerrados
childrenReactNode-Reemplaza el layout por defecto con tus propias partes
classNamestring-Clases CSS adicionales

AgentRun.Header

PropTipoDefaultDescripción
childrenReactNode-Reemplaza el título
classNamestring-Clases CSS adicionales

AgentRun.Progress

PropTipoDefaultDescripción
classNamestring-Clases CSS adicionales

AgentRun.Steps

PropTipoDefaultDescripción
classNamestring-Clases CSS adicionales

AgentRun.Answer

PropTipoDefaultDescripción
childrenReactNode-Reemplaza la prop answer
classNamestring-Clases CSS adicionales

AgentRun.Footer

PropTipoDefaultDescripción
childrenReactNode-Reemplaza el medidor de uso de la derecha
classNamestring-Clases CSS adicionales

Helpers

agentRunProgress(steps) devuelve { done, total, ratio }, los mismos números que imprimen la barra y el pie. useAgentRun() lee la corrida desde el contexto dentro de partes propias.
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue