Agentes
wu.ai.agent(goal) le da un objetivo al modelo y lo deja trabajar. Es un generador asíncrono: cada paso se emite, así que puedes renderizarlo, registrarlo o parar el bucle saliendo del for.
for await (const step of wu.ai.agent('Find all orders above $100 and summarize them')) { console.log(`Step ${step.step} [${step.type}]:`, step.content?.slice(0, 100));}function agent(goal: string, options?: { maxSteps?: number; // 10 por defecto provider?: string; namespace?: string; // 'agent:…' autogenerado si se omite systemPrompt?: string | ((goal: string) => string | Promise<string>); onStep?: (step: AgentStepResult) => void | Promise<void>; shouldContinue?: (step: AgentStepResult) => boolean | Promise<boolean>; signal?: AbortSignal; temperature?: number; maxTokens?: number;}): AsyncGenerator<AgentStepResult>;interface AgentStepResult { step: number; // empieza en 1 type: 'thinking' | 'tool_call' | 'done' | 'blocked' | 'aborted' | 'interrupted'; content: string | null; toolResults: Array<object> | null; usage: object | null; reason: string | null; elapsed: number; // ms}Cómo funciona un paso
Sección titulada «Cómo funciona un paso»El paso 1 envía el objetivo. Cada paso posterior envía literalmente el prompt “Continue working toward the goal. If you are done, include [DONE] in your response.” Cada paso es un conversation.send() completo, así que el bucle de tool calling corre dentro de un único paso del agente: un paso puede implicar varias rondas de tools.
Cada paso se trata como una llamada de nivel superior a depth: 0 con un trace ID nuevo. maxSteps gobierna el presupuesto de pasos; maxDepth gobierna la recursión causal, y son independientes.
Terminación
Sección titulada «Terminación»Se comprueba en este orden:
| # | Condición | type emitido |
|---|---|---|
| 1 | Se disparó la señal de abort | aborted |
| 2 | La comprobación previa de permisos denegó (límite de peticiones, breaker, guardia de bucles) | blocked |
| 3 | La respuesta contiene la cadena literal [DONE] |
done |
| 4 | El modelo llamó a tools en un paso anterior y no llamó a ninguna en este | done |
| 5 | shouldContinue(step) devolvió false (o lanzó) |
interrupted |
| 6 | Se alcanzó maxSteps |
done, reason: 'Max steps (N) reached' |
Cuando encuentra el marcador, wu lo elimina del content emitido.
Humano en el bucle
Sección titulada «Humano en el bucle»for await (const step of wu.ai.agent('Reorganize the product catalog', { maxSteps: 6, onStep: (s) => renderStep(s), shouldContinue: async (s) => await askUser(`Continue after step ${s.step}?`),})) { if (step.type === 'blocked') showLimitWarning(step.reason);}onStep se ejecuta antes de emitir el paso y sus errores se tragan: un renderizador roto nunca rompe el bucle. shouldContinue se ejecuta después de emitirlo; si lanza, cuenta como “parar”.
El prompt de sistema del agente
Sección titulada «El prompt de sistema del agente»Salvo que lo sobrescribas, wu genera uno que contiene: el protocolo del agente (incluido el contrato [DONE]), las apps montadas, la instantánea actual del store, una firma de una línea por cada tool disponible y, al final, GOAL: <goal>.
Ciclo de vida y limpieza
Sección titulada «Ciclo de vida y limpieza»Los namespaces agent:* autogenerados se eliminan cuando termina la ejecución. Un namespace que aportaste tú se deja intacto: su ciclo de vida es tuyo. Eventos: ai:agent:start, ai:agent:step (uno por paso emitido), ai:agent:done con la razón final, ai:agent:error.
Coordinación entre apps
Sección titulada «Coordinación entre apps»En una arquitectura de microfrontends, conectar la app A con la app B es manual y crece n². El paradigma 4 sustituye ese cableado por un mapa de capacidades: cada app declara lo que sabe hacer, y el modelo resuelve los intents cruzando fronteras.
// En la micro-app de pedidos (React)wu.ai.capability('orders', 'getRecent', { description: 'Get the N most recent orders', parameters: { limit: { type: 'number' } }, handler: async (p) => fetchOrders({ limit: p.limit || 10 }),});
// En la micro-app del dashboard (Svelte)wu.ai.capability('dashboard', 'updateKPIs', { description: 'Refresh the KPI cards with the latest data', handler: async () => { refreshKPIs(); return { updated: true }; },});
// Desde cualquier sitioconst result = await wu.ai.intent('Show me the top customer by order count');// → { content, tool_results, usage, resolved, appsInvolved }Una capacidad es una acción registrada como app:name con la misma forma de configuración: misma API en sandbox, mismas reglas de permisos. Llama a wu.ai.removeApp('orders') en el unmount de la app para que no queden capacidades obsoletas en el mapa.
intent() acepta { plan, provider, temperature, maxTokens, signal, responseFormat }. plan es una pista — una lista ordenada como ['orders:getRecent', 'dashboard:updateKPIs'] — no un contrato.
Workflows
Sección titulada «Workflows»Un workflow es una receta con nombre y parámetros. Hay dos modos, y el modo lo cambia todo en cuanto a coste y fiabilidad.
function workflow(name: string, config: { description?: string; steps: string[] | Array<{ action: string; [k: string]: any }>; mode?: 'ai' | 'deterministic'; // se detecta automáticamente parameters?: Record<string, { type: string; required?: boolean }>; maxSteps?: number; // 15 por defecto (modo ai) provider?: string; temperature?: number; // 0.2 por defecto}): Wu;
function runWorkflow(name: string, params?: object, options?: { onStep?: Function; shouldContinue?: Function; signal?: AbortSignal;}): AsyncGenerator<AgentStepResult>;El modo se detecta automáticamente: si steps[0] es un objeto con una propiedad action, el workflow es deterministic; si no, es ai. Define mode de forma explícita si quieres asegurarte.
Modo ai — pasos en lenguaje natural
Sección titulada «Modo ai — pasos en lenguaje natural»wu.ai.workflow('register-user', { description: 'Register a new user in the system', steps: [ 'Navigate to the Customers section', 'Click the "Add Customer" button', 'Type "{{name}}" into the name field', 'Type "{{email}}" into the email field', 'Click Submit', 'Verify the success message appears', ], parameters: { name: { type: 'string', required: true }, email: { type: 'string', required: true } },});{{param}} se sustituye en cada cadena de paso, los pasos se convierten en un objetivo y el bucle del agente lo ejecuta con las browser actions. Flexible, tolerante a cambios en la interfaz… y cuesta una ida y vuelta al LLM por paso.
Modo deterministic — sin LLM alguno
Sección titulada «Modo deterministic — sin LLM alguno»wu.ai.workflow('register-user-fast', { parameters: { name: { type: 'string', required: true }, email: { type: 'string', required: true } }, steps: [ { action: 'navigate', section: 'customers' }, { action: 'click', selector: '#add-customer' }, { action: 'wait', selector: '#customer-form', timeout: 5000 }, { action: 'type', selector: '#name', value: '{{name}}' }, { action: 'type', selector: '#email', value: '{{email}}' }, { action: 'click', text: 'Submit' }, { action: 'wait', ms: 500 }, ],});
for await (const step of wu.ai.runWorkflow('register-user-fast', { name: 'Ada', email: 'ada@x.io' })) { console.log(step.step, step.type, step.content);}action |
Campos | Hace |
|---|---|---|
click |
selector y/o text |
Hace clic por selector CSS o por texto visible |
type |
selector, value, clear (por defecto true), submit (por defecto false) |
Escribe en un input, compatible con frameworks |
navigate |
section o selector |
Emite nav:section, o hace clic en el selector |
wait |
ms, o selector + timeout (por defecto 5000) |
Espera, o consulta hasta que aparezca un selector |
emit |
event, data |
Emite en el bus de eventos como wu-ai |
setState |
path, value |
Escribe en el store de wu |
La interpolación {{param}} se aplica exactamente a estos campos: value, selector, text, section, event, path. Todo lo demás — data, ms, timeout — se usa literalmente.
Misma interfaz de generador que el agente, así que la misma UI renderiza ambos. Cero tokens, determinista, y se rompe en cuanto cambia un selector. Ese intercambio es justamente el punto: usa deterministic para los caminos que controlas y ai para los que no.
Si faltan parámetros obligatorios, lanza antes del primer paso. Ambos modos emiten ai:workflow:start (con mode) y ai:workflow:done.
