Ir al contenido

Gestión de ACP Thread Bound Agents

¿Te ha pasado que lanzas un agente de código en un hilo de chat y, a mitad del proceso, todo se rompe o pierdes el rastro de lo que estaba haciendo? Gestionar sesiones persistentes en entornos como Discord suele ser un dolor de cabeza, especialmente cuando necesitas que el agente mantenga el estado y se recupere de fallos sin enviar respuestas duplicadas o perder el contexto.

OpenClaw soluciona esto mediante una arquitectura híbrida donde el core gestiona el control plane y el runtime de ACP funciona como un componente conectable. Esto permite que tus agentes vivan en hilos específicos con una recuperación ante desastres real.

Para trabajar con ACP Thread Bound Agents, necesitas cumplir con estos requisitos:

  • Una instancia de OpenClaw con el Gateway configurado.
  • Un canal que soporte hilos (Discord es el primero en la lista).
  • El plugin acpx o un runtime de ACP compatible.
  • Permisos para ejecutar comandos de sesión como /focus y /unfocus.

Configura tu primer agente vinculado a un hilo en menos de 5 minutos siguiendo estos pasos:

  1. Vincular la sesión: Usa el comando /focus en un hilo de Discord para spawnear o enfocar una sesión de ACP.
  2. Enrutamiento automático: Envía mensajes en ese hilo; OpenClaw los dirigirá automáticamente a la sesión de ACP vinculada.
  3. Streaming de salida: Observa cómo el agente devuelve los datos en tiempo real al mismo hilo utilizando el sistema de Unified Runtime Streaming.
  4. Control de ciclo de vida: Gestiona la persistencia de la sesión mediante el archivo del hilo o el comando /unfocus.

OpenClaw no intenta reinventar el transporte de ACP en su core. En su lugar, utiliza un AcpSessionManager que actúa como el único orquestador. El sistema garantiza que cada ejecución sea atómica y que el estado se guarde en una base de datos SQLite con modo WAL.

  • creating y idle
  • running y cancelling
  • closed y error
  • queued y running
  • completed y failed

Si encuentras problemas durante la ejecución, estas son las causas y soluciones documentadas:

  • Salidas duplicadas en Discord: OpenClaw implementa claves de idempotencia. Si ves duplicados, verifica que el Gateway no esté reintentando peticiones sin el ID de mensaje correcto.
  • Fallo de enrutamiento en el hilo: Si un mensaje no llega al agente, asegúrate de que el binding del hilo referencia una sesión de ACP válida. El sistema usa un modelo de “fail-closed” para evitar fugas de mensajes.

Para obtener ayuda personalizada con tu configuración, consulta al AI Setup Assistant.

---
title: "Arquitectura de OpenClaw ACP: Cómo gestionamos sesiones y estados"
description: "Una mirada técnica a la estructura de ACP, el modelo de persistencia en SQLite y el sistema de actores para sesiones de agentes."
---
Seguro te ha pasado: intentas mantener una sesión de un agente activa y todo se vuelve un caos de procesos huérfanos y estados inconsistentes. Gestionar el ciclo de vida de un agente, especialmente cuando necesitas que sea persistente y confiable, suele ser un dolor de cabeza que interrumpe el flujo de desarrollo.
En OpenClaw, diseñamos ACP (Agent Control Protocol) para solucionar esto. La idea es separar la lógica pesada de ejecución del control de flujo, asegurando que si algo falla, el sistema sepa exactamente dónde se quedó.
## What You'll Need
Para trabajar con esta arquitectura o implementarla, necesitas:
- OpenClaw core (donde reside el despacho de sesiones).
- Plugin backend (implementación de `acpx`).
- SQLite (configurado en modo WAL para la persistencia).
## Quick Start
La forma más rápida de entender cómo interactuar con ACP es a través de su contrato de runtime. Si estás construyendo un backend, este es el contrato que debes cumplir para que el Gateway pueda orquestar las sesiones:
```ts
Export interface AcpRuntime {
ensureSession(input: {
sessionKey: string;
agent: string;
mode: "persistent" | "oneshot";
cwd?: string;
env?: Record<string, string>;
idempotencyKey: string;
}): Promise<AcpRuntimeHandle>;
submit(input: {
handle: AcpRuntimeHandle;
text: string;
mode: AcpRuntimePromptMode;
idempotencyKey: string;
}): Promise\<{ runtimeRunId: string }>;
stream(input: {
handle: AcpRuntimeHandle;
runtimeRunId: string;
onEvent: (event: AcpRuntimeEvent) => Promise\<void\> | void;
signal?: AbortSignal;
}): Promise\<void\>;
cancel(input: {
handle: AcpRuntimeHandle;
runtimeRunId?: string;
reason?: string;
idempotencyKey: string;
}): Promise\<void\>;
close(input: { handle: AcpRuntimeHandle; reason: string; idempotencyKey: string }): Promise\<void\>;
health?(): Promise\<{ ok: boolean; details?: string }>;
}

Dividimos las responsabilidades para mantener el Core limpio y delegar la ejecución pesada a los plugins.

Core (OpenClaw core):

  • Rama de despacho session-mode en el pipeline de respuesta.
  • Arbitraje de entrega para evitar duplicación de hilos.
  • Persistencia del control-plane (compatible con SessionEntry.acp).
  • Semánticas de unbind y detach vinculadas al reinicio o borrado de sesión.

Plugin backend (implementación acpx):

  • Supervisión de workers del ACP runtime.
  • Invocación de procesos acpx y parseo de eventos.
  • Manejadores de comandos ACP (/acp ...) y UX para operadores.
  • Diagnósticos y valores por defecto específicos del backend.
  • Un proceso Gateway es el dueño del estado de orquestación de ACP.
  • La ejecución de ACP ocurre en procesos hijos supervisados mediante el backend acpx.
  • La estrategia de proceso es de larga duración por cada session key activa, no por cada mensaje.

Esto evita el coste de inicio en cada prompt y hace que las semánticas de cancelación y reconexión sean confiables.

La fuente de verdad es una base de datos SQLite dedicada (modo WAL). Esto garantiza recuperaciones seguras tras un crash. El esquema incluye:

  • acp_sessions: Estado de la sesión (idle, running, error), agente y configuración.
  • acp_runs: Seguimiento de cada ejecución individual y su idempotency_key.
  • acp_bindings: Vinculación entre hilos, canales y sesiones.
  • acp_events: Historial de eventos (seq, payload_json).
  • acp_delivery_checkpoint: Control de qué eventos ya se enviaron a Discord.
  • acp_idempotency: Registro para evitar operaciones duplicadas.

Para la gestión en memoria, usamos este tipo de metadatos:

Export type AcpSessionMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};

Cuando llega un mensaje, el Gateway busca si el hilo está vinculado. Si el destino es una sesión ACP, la ruta cambia: en lugar de usar getReplyFromConfig, se dirige a la rama del ACP runtime.

En la salida, los eventos de ACP se normalizan en fragmentos de respuesta de OpenClaw. Usamos una política de streaming con una ventana de coalescencia para no saturar los límites de ratio de Discord, enviando siempre un mensaje final al completar o fallar.

Para evitar estados inconsistentes, las transacciones deben ser atómicas en estos puntos:

  1. Transacción de spawn: Crea la fila de sesión, actualiza el binding del hilo y encola la ejecución inicial.
  2. Transacción de cierre: Marca la sesión como cerrada, elimina los bindings y escribe el evento final.
  3. Transacción de cancelación: Marca la ejecución como cancelada usando la idempotency_key.

AcpSessionManager ejecuta un actor por cada session key. Este actor:

  • Serializa las acciones (submit, cancel, close) mediante un mailbox.
  • Gestiona el ciclo de vida del proceso adaptador.
  • Escribe los eventos en orden secuencial antes de cualquier entrega a Discord.
  • Actualiza los checkpoints de entrega tras el envío exitoso.

Si el Gateway se reinicia, carga las sesiones no terminales y recrea los actores de forma perezosa (al recibir un evento) o inmediata según la configuración. Si un hilo de Discord tiene un binding pero la sesión ACP desapareció, el sistema falla de forma segura con un mensaje explícito en lugar de intentar una ruta de LLM normal.

Controles de seguridad incluidos:

  • Allowlist de agentes ACP por nombre.
  • Restricción de directorios raíz (workspace roots).
  • Passthrough de variables de entorno controladas.
  • TTL efectivo basado en el mínimo entre la sesión, el hilo de Discord y el dueño del runtime.
  • Bindings huérfanos: Si un binding existe pero la sesión no, el sistema fallará de forma cerrada. Debes validar o limpiar el binding manualmente o esperar a la auto-limpieza.
  • Crashes del runtime: ACP usa un backoff de reinicio acotado para evitar bucles infinitos en procesos fallidos.
  • Mensajes duplicados: Gracias a la tabla acp_delivery_checkpoint, las reintentos resumen desde el último fragmento enviado, garantizando entrega “exactly-once” por ejecución.

AI Setup Assistant

```mdx
---
title: Configuración de la superficie de ACP
description: Guía técnica para configurar los parámetros core, el control plane y los backends de ACP en tu infraestructura.
---
Configurar sistemas de agentes suele ser un dolor de cabeza cuando no tienes claro dónde tocar. Quieres que tus sesiones se gestionen solas y que el ruteo no falle, pero te encuentras saltando entre archivos sin una estructura clara.
Tener el control total de la superficie de configuración es la mejor forma de evitar comportamientos inesperados en producción. Aquí tienes los parámetros necesarios para que tu Gateway y tus agentes funcionen exactamente como esperas.
## What You'll Need
- Un entorno con soporte para `sqlite` (almacenamiento por defecto).
- Acceso a la configuración de `channels` si usas integraciones externas.
## Quick Start
Para poner en marcha el Agent Control Plane, necesitas definir las llaves principales en tu configuración. Te recomiendo empezar con el backend `acpx` y el almacenamiento en `sqlite` para simplificar la persistencia.
1. **Activa el core**: Configura `acp.enabled` en `true`.
2. **Define el backend**: Usa `acp.backend` (por defecto es `acpx`).
3. **Configura el almacenamiento**: Define `acp.controlPlane.store` como `sqlite`.
4. **Establece límites**: Ajusta `acp.maxConcurrentSessions` para controlar la carga.
```typescript
// Ejemplo de configuración core
{
"acp.enabled": true,
"acp.backend": "acpx",
"acp.controlPlane.store": "sqlite",
"acp.maxConcurrentSessions": 100
}

Configuración del Control Plane y Recuperación

Sección titulada «Configuración del Control Plane y Recuperación»

El Control Plane gestiona el estado y la recuperación de tus procesos. Estos son los parámetros clave:

  • acp.controlPlane.storePath: La ruta física para tu base de datos.
  • acp.controlPlane.recovery.eagerActors: Determina la recuperación activa de actores.
  • acp.controlPlane.recovery.reconcileRunningAfterMs: Tiempo de espera antes de reconciliar procesos en ejecución.
  • acp.controlPlane.checkpoint.flushEveryMs: Frecuencia de guardado del estado en milisegundos.

Gestiona el ciclo de vida de las sesiones y el flujo de datos:

  • acp.runtime.ttlMinutes: Tiempo de vida del runtime antes de expirar.
  • acp.idempotency.ttlHours: Ventana de tiempo para evitar ejecuciones duplicadas.
  • acp.stream.coalesceIdleMs: Tiempo de espera para agrupar fragmentos de stream.
  • acp.stream.maxChunkChars: Tamaño máximo de caracteres por fragmento.

Si necesitas personalizar el comportamiento del backend acpx, utiliza estas opciones:

  • backend command/path overrides: Sobrescribe la ruta del binario.
  • backend env allowlist: Lista permitida de variables de entorno.
  • backend per-agent presets: Ajustes predefinidos por cada agente.
  • backend startup/stop timeouts: Tiempos de espera para el inicio y cierre.
  • backend max inflight runs per session: Límite de ejecuciones simultáneas por sesión.

Si experimentas problemas con el ruteo o la persistencia, revisa estos puntos basados en la configuración:

  • Fallo en el ruteo de ACP: Verifica que acp.dispatch.enabled no esté desactivado. Es un kill switch independiente para el ruteo.
  • Sesiones de Discord no inician: Comprueba que channels.discord.threadBindings.spawnAcpSessions esté configurado correctamente.
  • Estado no guardado: Si pierdes datos tras un reinicio, ajusta acp.controlPlane.checkpoint.flushEveryEvents para forzar guardados más frecuentes.
  • Agentes no permitidos: Si un agente es rechazado, asegúrate de incluirlo en la lista acp.allowedAgents[] o define un acp.defaultAgent.

¿Necesitas ayuda para integrar estos parámetros en tu flujo de trabajo? Prueba nuestro AI Setup Assistant.

¿Alguna vez has sentido que gestionar el estado de los agentes y sus sesiones se convierte en un caos de condiciones de carrera y eventos duplicados? Mantener la consistencia entre lo que ocurre en el backend y lo que ve el usuario es un reto constante. Esta especificación define cómo estamos estructurando el ACP (Agent Control Plane) para que las sesiones sean deterministas, seguras y fáciles de recuperar.
## What You'll Need
Para implementar o trabajar con esta especificación, necesitas acceso a los siguientes componentes del core:
- Acceso a `src/core` para añadir los nuevos módulos del control-plane.
- SQLite para la gestión de persistencia mediante `src/acp/control-plane/store.ts`.
- El plugin `acpx` (o un runtime compatible) para la ejecución de comandos.
- Conocimiento de la estructura de `SessionEntry` en el sistema de configuración.
## Quick Start
Si quieres registrar un nuevo backend de ACP y empezar a procesar comandos en menos de 5 minutos, sigue estos pasos:
1. **Registra tu backend**: Utiliza el Registry API en `src/acp/runtime/registry.ts`.
2. **Implementa el contrato de comandos**: Tu backend debe responder a comandos CLI como `sessions ensure` y `prompt`.
3. **Configura el dispatch**: Activa `acp.enabled` y `acp.dispatch.enabled` en tu configuración.
4. **Valida la conexión**: Usa el comando `/acp doctor` para verificar que el backend está registrado y el store está saludable.
## Detalles de la Implementación
### Módulos del control-plane (nuevos)
Hemos añadido módulos dedicados en el core para gestionar el ciclo de vida de ACP:
- `src/acp/control-plane/manager.ts`: Gestiona los actores ACP, transiciones de ciclo de vida y serialización de comandos.
- `src/acp/control-plane/store.ts`: Manejo del esquema SQLite, transacciones y helpers de consulta.
- `src/acp/control-plane/events.ts`: Definiciones de eventos ACP tipados y su serialización.
- `src/acp/control-plane/checkpoint.ts`: Checkpoints de entrega duradera y cursores de repetición.
- `src/acp/control-plane/idempotency.ts`: Reserva de claves de idempotencia y repetición de respuestas.
- `src/acp/control-plane/recovery.ts`: Reconciliación en el arranque y plan de rehidratación de actores.
También incluimos un módulo de compatibilidad temporal:
- `src/acp/runtime/session-meta.ts`: Se mantiene para la proyección en `SessionEntry.acp`, pero dejará de ser la fuente de verdad tras la migración.
### Invariantes obligatorios
El código debe garantizar estos comportamientos:
- La creación de la sesión ACP y el bind del thread son atómicos (una sola transacción).
- Solo hay una ejecución (run) activa por actor de sesión ACP a la vez.
- El `seq` de los eventos aumenta estrictamente por cada ejecución.
- El checkpoint de entrega nunca supera al último evento confirmado.
- La repetición por idempotencia devuelve el payload de éxito anterior para claves duplicadas.
- Los metadatos de ACP obsoletos o faltantes no pueden enviarse por la ruta de respuesta normal.
### API del registro de runtime
Añadimos un módulo de registro central en `src/acp/runtime/registry.ts`:
```ts
export type AcpRuntimeBackend = {
id: string;
runtime: AcpRuntime;
healthy?: () => boolean;
};
export function registerAcpRuntimeBackend(backend: AcpRuntimeBackend): void;
export function unregisterAcpRuntimeBackend(id: string): void;
export function getAcpRuntimeBackend(id?: string): AcpRuntimeBackend | null;
export function requireAcpRuntimeBackend(id?: string): AcpRuntimeBackend;

El comportamiento es directo: requireAcpRuntimeBackend lanza un error tipado si el backend no está, y los plugins registran/eliminan sus backends al iniciar o detenerse.

Para el backend acpx, la conexión con OpenClaw usa este contrato de comandos:

  • ID del backend: acpx
  • ID del servicio: acpx-runtime
  • Encoding del handle: runtimeSessionName = acpx:v1:<base64url(json)>

Campos del payload: name, agent, cwd, mode (persistent | oneshot).

Mapeo de comandos CLI:

  • Asegurar sesión: acpx --format json --json-strict --cwd <cwd> <agent> sessions ensure --name <name>
  • Turno de prompt: acpx --format json --json-strict --cwd <cwd> <agent> prompt --session <name> --file -
  • Cancelar: acpx --format json --json-strict --cwd <cwd> <agent> cancel --session <name>
  • Cerrar: acpx --format json --json-strict --cwd <cwd> <agent> sessions close <name>

Para el streaming, consumimos ndjson con estos mapeos: text -> text_delta/output, thought -> text_delta/thought, tool_call -> tool_call, done -> done.

Parcheamos SessionEntry en src/config/sessions/types.ts:

type SessionAcpMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};

La migración sigue cuatro fases:

  1. Fase A: Escritura dual (proyección acp + SQLite).
  2. Fase B: Lectura primaria desde SQLite con fallback a SessionEntry.acp.
  3. Fase C: Comando de migración para rellenar filas de SQLite desde entradas legacy.
  4. Fase D: Eliminación del fallback de lectura; la proyección queda solo para la UX.

Aplicamos una regla única para turnos vinculados a ACP:

  • Si existe un thread binding activo, entrega solo a ese thread.
  • No envíes el mensaje al canal padre para el mismo turno.
  • Si la selección del destino es ambigua, falla de forma cerrada (fail-closed) con un error explícito.
  • Si no hay binding activo, usa el comportamiento normal de destino de la sesión.

Si encuentras problemas, estos son los códigos de error estables y sus soluciones:

  • ACP_BACKEND_MISSING: El backend no está configurado. Debes instalar y activar el plugin de runtime acpx.
  • ACP_BACKEND_UNAVAILABLE: El backend no responde. Inténtalo de nuevo en unos segundos.
  • ACP_SESSION_INIT_FAILED: No se pudo inicializar el runtime de la sesión. Revisa los permisos del cwd.
  • ACP_TURN_FAILED: El turno falló antes de completar. Revisa los logs de acpx para ver errores de ejecución.

Recuerda que el sistema nunca debe volver silenciosamente a la ruta normal de LLM si se seleccionó explícitamente ACP.

¿Necesitas ayuda con la implementación en tu entorno? Prueba nuestro AI Setup Assistant.

¿Alguna vez has sentido que gestionar agentes con estados complejos es como intentar arrear gatos? Mantener la sincronía entre lo que el usuario ve en Discord y lo que ocurre en el backend suele ser un dolor de cabeza, especialmente cuando las sesiones se quedan colgadas o los comandos no son claros.

Para solucionar esto, hemos diseñado un control-plane para ACP que prioriza la consistencia y la previsibilidad. Aquí tienes el plan de ejecución para que el manejo de agentes sea directo y sin fricciones.

Para implementar o probar estas funcionalidades, asegúrate de contar con lo siguiente:

  • Acceso al Gateway y al sistema de dispatch.
  • Base de datos SQLite configurada para el store de ACP.
  • Adapter de Discord (para la fase 4).
  • Runtime de acpx instalado.

Si quieres empezar a probar los nuevos comandos de ACP, esta es la ruta mínima de 5 minutos. Usa estos comandos directamente en tu terminal o cliente compatible:

Ventana de terminal
# Inicia un agente en un hilo específico
/acp spawn <agent-id> --mode persistent --thread auto
# Envía una instrucción directa al agente activo
/acp steer "Analiza los logs de la última hora"
# Lista todas las sesiones activas
/acp sessions
# Finaliza la sesión actual
/acp close

Si necesitas cancelar una ejecución específica sin cerrar la sesión, usa: /acp cancel [session]

Hemos dividido la implementación en seis fases para garantizar la estabilidad del sistema.

En esta etapa definimos la propiedad del control-plane y los límites del adapter.

  • Congelamos el schema de la DB: acp_sessions, acp_runs, acp_bindings, acp_events, acp_delivery_checkpoint, acp_idempotency.
  • Definimos códigos de error estables y guardias de transición de estado.

Implementamos el AcpSessionManager y el actor runtime por sesión.

  • Creamos el store de SQLite y los helpers para transacciones.
  • Implementamos el store de idempotencia y módulos de checkpoint para entrega de eventos.
  • Conectamos las API de spawn/cancel/close con garantías transaccionales.

Integramos el flujo de mensajes con el manager.

  • Enrutamos los turns de ACP vinculados a hilos (threads) desde el pipeline de dispatch.
  • Aplicamos routing de “fallo cerrado” (fail-closed) si fallan las invariantes de sesión.
  • Integramos acciones de reset/delete/archive con las transacciones de ACP.

Implementamos el adapter contra el contrato del runtime.

  • Funciones principales: ensureSession, submit, stream, cancel, close.
  • Añadimos health checks y supervisión de procesos con políticas de reintento (backoff).
  • Normalizamos eventos ndjson en eventos de runtime de ACP.

Hacemos que los eventos lleguen al usuario final.

  • Implementamos la proyección de hilos basada en eventos con reanudación por checkpoint.
  • Coalescencia de chunks de streaming respetando los rate-limits.
  • Garantizamos exactamente un mensaje final de completado por cada run.
  • Introducimos escritura dual en SessionEntry.acp y en la fuente de verdad de ACP SQLite.
  • Migramos los metadatos antiguos y cambiamos la ruta de lectura a la DB de ACP.
  • Aplicamos límites de concurrencia por cuenta y sesión.
  • Añadimos telemetría completa y dashboards de alerta.
  • Pruebas de caos para recuperación de caídas y supresión de entregas duplicadas.

Si encuentras problemas durante la implementación o el uso, revisa estos escenarios comunes documentados:

El agente entrega mensajes duplicados durante la transición

  • Solución: Verifica el resolver de destino único y el checkpoint de eventos idempotentes. El sistema está diseñado para filtrar duplicados si el checkpoint está activo.

El proceso del runtime se reinicia constantemente bajo carga

  • Solución: Revisa los límites de concurrencia y las políticas de backoff. Asegúrate de que los owners de sesión sean de larga duración.

Error: Plugin ausente o mal configurado

  • Solución: El sistema usa routing fail-closed. No habrá fallback automático a sesiones normales si el ACP falla. Revisa los logs del operador para ver el error explícito.

Confusión entre configuraciones de subagentes y ACP

  • Solución: Usa el feedback de los comandos para ver la política efectiva. Las claves de ACP son explícitas y tienen precedencia clara.

Para mantener el código limpio, hemos completado las siguientes tareas:

  1. Política de dispatch centralizada: Implementada en src/acp/policy.ts.
  2. Comandos divididos por dominio: El router en src/auto-reply/reply/commands-acp.ts ahora es ligero. La lógica reside en /lifecycle.ts, /runtime-options.ts y /diagnostics.ts.
  3. Manager de sesiones modular: Dividido en manager.core.ts, manager.types.ts y manager.utils.ts.
  4. Limpieza de adapter acpx: (Opcional) Separación de ejecución de procesos y parseo de ndjson en extensions/acpx/src/runtime.ts.

¿Necesitas ayuda con la configuración de tu entorno o tienes dudas sobre un comando específico? Consulta al AI Setup Assistant.

OpenClaw

OpenClaw Expert

Sigues atascado?

Si esta pagina no resolvio tu caso, pregunta a OpenClaw Expert para pasos concretos.