Plataforma de agente IA personal con arquitectura multiagente, automejora controlada, gateway web y canal Matrix.
- Docker y Docker Compose
# 1. Clonar y configurar
git clone <repo-url> && cd autobot
cp .env.example .env
# 2. Arrancar servicios
docker compose up -d
# 3. Setup inicial interactivo (migraciones, admin, clave de cifrado, OAuth, agentes, Matrix)
docker compose run --rm web flask onboard
# 4. Abrir el dashboard
open http://localhost:5000El comando flask onboard guía paso a paso la configuración inicial:
- Aplica migraciones
- Crea el usuario administrador
- Genera la
TOKEN_ENCRYPTION_KEY(Fernet) para cifrar credenciales y tokens OAuth - Lanza el flujo OAuth de OpenAI Codex (PKCE, callback local en
localhost:1455) - Aprovisiona los dos agentes por defecto: optimus (orquestador) y reviewer
- Configura (opcional) el canal Matrix
Todas las variables se definen en .env. Ver .env.example para referencia.
| Variable | Descripción | Requerida |
|---|---|---|
SECRET_KEY |
Clave secreta de Flask para sesiones | Sí |
DATABASE_URL |
URI de conexión a PostgreSQL | Sí |
REDIS_URL |
URI de conexión a Redis | Sí |
TOKEN_ENCRYPTION_KEY |
Clave Fernet para cifrar credenciales y tokens OAuth en reposo | Sí |
OPENAI_MODEL |
Modelo Codex por defecto (e.g. gpt-5.2) |
No |
MAX_CONTEXT_TOKENS |
Límite del contexto del modelo (default: 128000) |
No |
CONTEXT_RESPONSE_RESERVE_TOKENS |
Tokens reservados para la respuesta del modelo (default: 8000) |
No |
MAX_HISTORY_MESSAGES |
Cap legacy; ya no es el límite real, lo es el budget de tokens | No |
MAX_TOOL_ROUNDS |
Cap de rondas de tool-calls por run (default: 20). Override por agente en agents.max_tool_rounds |
No |
PATCHES_PER_HOUR_PER_AGENT |
Rate-limit de automejora: patches applied + pending_review + approved por hora y agente (default: 30, 0 = desactivado) |
No |
WORKSPACES_BASE_PATH |
Raíz de los workspaces (default: ./workspaces) |
No |
PACKAGE_ALLOWLIST |
PyPI auto-instalables en venvs de workspace (CSV) | No |
VENV_BASE_PACKAGES |
Packages preinstalados en cada venv nuevo (CSV) | No |
PIP_INSTALL_TIMEOUT_SECONDS |
Timeout de pip install (default: 180) |
No |
WORKSPACE_TOOL_TIMEOUT_SECONDS |
Timeout por ejecución de tool de workspace (default: 30) |
No |
AVATAR_UPLOAD_DIR |
Directorio de avatares subidos (default: ./instance/avatars) |
No |
AVATAR_MAX_BYTES |
Tamaño máximo por avatar (default: 2 MB) |
No |
MFA_ISSUER |
Issuer mostrado en apps TOTP (default: Autobot) |
No |
AUTOBOT_CRED_<NAME> |
Credencial preseeded desde entorno — visible a los agentes por get_credential con source=env |
No |
AUTOBOT_GITHUB_REPO |
Repo de GitHub destino de los PRs de promoción (default: https://github.com/lliwi/autobot). Útil si trabajas en un fork |
No |
MATRIX_HOMESERVER |
URL del servidor Matrix (e.g. https://matrix.org) |
Para Matrix |
MATRIX_USER_ID |
User ID del bot Matrix (e.g. @bot:matrix.org) |
Para Matrix |
MATRIX_PASSWORD |
Contraseña del bot Matrix | Para Matrix |
MATRIX_ALLOWED_ROOMS |
IDs de salas permitidas (CSV, vacío = todas) | No |
MATRIX_ALLOWED_USERS |
User IDs permitidos (CSV, vacío = todos) | No |
MATRIX_ALLOWED_DM_USERS |
Allowlist DM (vacío = usa MATRIX_ALLOWED_USERS) |
No |
MATRIX_GROUP_POLICY |
Política de respuesta en grupo: always, mention, allowlist |
No |
SCHEDULER_ENABLED |
Activar scheduler (default: true) |
No |
HEARTBEAT_INTERVAL_MINUTES |
Intervalo por defecto del heartbeat (default: 15) |
No |
El login con Codex ya no usa un flujo web-OAuth redirigido al navegador del usuario. Se hace por PKCE desde la línea de comandos:
docker compose run --rm web flask codex-login # levanta callback en :1455 e imprime la URL
docker compose run --rm web flask codex-status # muestra estado/cuenta
docker compose run --rm web flask codex-logout # borra el tokenflask onboard ejecuta codex-login automáticamente.
Todos los comandos se ejecutan dentro de Docker:
# Servicios
docker compose up -d # Arrancar todo
docker compose down # Parar todo
docker compose logs -f web # Ver logs en tiempo real
docker compose restart web # Reiniciar la app
# Base de datos
docker compose run --rm web flask db migrate -m "descripción" # Crear migración
docker compose run --rm web flask db upgrade # Aplicar migraciones
docker compose run --rm web flask db downgrade # Revertir última migración
# Administración
docker compose run --rm web flask onboard # Setup interactivo completo
docker compose run --rm web flask setup-default-agents # (Re)configurar optimus + reviewer
docker compose run --rm web flask setup-matrix # Configurar sólo el canal Matrix
docker compose run --rm web flask create-admin --email u@m.com --password pw
# Codex
docker compose run --rm web flask codex-login
docker compose run --rm web flask codex-logout
docker compose run --rm web flask codex-status
# Gestión de naming de tools (workspace)
docker compose exec web python scripts/workspace_tools_manager.py --root workspaces/<agente> --no-ref-scan # auditar
docker compose exec web python scripts/workspace_tools_manager.py --root workspaces/<agente> --repair --no-ref-scan # plan
docker compose exec web python scripts/workspace_tools_manager.py --root workspaces/<agente> --repair --apply --no-ref-scan # aplicar
docker compose exec web flask reconcile-tools # ver rows huérfanos en BD (dry-run)
docker compose exec web flask reconcile-tools --apply # eliminar rows huérfanos de BD
docker compose exec web flask audit-tools # auditar todos los agentes
# Backup / portabilidad
scripts/backup.sh # DB + workspaces (sin secretos)
scripts/backup.sh --include-env --include-secrets # backup completo con credenciales
# Restaurar
scripts/restore.sh # restaura el backup más reciente
scripts/restore.sh --list # lista los backups disponibles
scripts/restore.sh backups/autobot_2026-04-20_19-13.tar.gz # restaura uno concreto
# Desarrollo
docker compose build web # Rebuild tras cambios en dependencias
docker compose run --rm web pytest # Ejecutar tests
docker compose run --rm web pytest tests/test_auth.py # Ejecutar un test específicoAutobot usa Alembic (vía Flask-Migrate) para gestionar el esquema de PostgreSQL.
# Aplicar migraciones pendientes (obligatorio tras un git pull o restore)
docker compose run --rm web flask db upgrade
# Revertir la última migración
docker compose run --rm web flask db downgrade
# Crear una nueva migración al cambiar un modelo SQLAlchemy
docker compose run --rm web flask db migrate -m "descripción del cambio"
docker compose run --rm web flask db upgradeCuándo ejecutar flask db upgrade:
- Tras un
git pullque incluya nuevas migraciones enmigrations/versions/ - Tras restaurar un bundle con
scripts/restore.sh(el bundle guarda datos, no el esquema) - En una instalación nueva, después del
flask onboard(lo hace automáticamente)
Las migraciones son idempotentes: ejecutar
flask db upgradecuando ya está al día no hace nada.
Los scripts scripts/backup.sh y scripts/restore.sh son la forma recomendada de hacer copias y restaurar. Escriben en ./backups/ (bind-mounted en el contenedor como /app/backups/), por lo que los ficheros sobreviven a reinicios del contenedor.
# Backup sin secretos (recomendado para backups rutinarios)
scripts/backup.sh
# Backup completo con credenciales y .env en claro
scripts/backup.sh --include-env --include-secrets
# Restaurar el backup más reciente
scripts/restore.sh
# Restaurar uno concreto
scripts/restore.sh backups/autobot_2026-04-20_19-13.tar.gz
# Listar backups disponibles
scripts/restore.sh --list⚠ Aviso: No uses
/tmpdentro del contenedor como destino del export — se borra al reiniciar. Los scripts usan/app/backups/que está bind-mounted en el host.
manifest.json— versión de esquema, timestamp y contadoresagents.json,tools.json,skills.json,packages.json,credentials.json— filas DB serializadas porslugworkspaces/<slug>/— contenido completo del workspace (excluye.venv,runs/,__pycache__/).env— opcional (--include-env)
Las credenciales se exportan descifradas sólo con --include-secrets (el tarball no está cifrado, protégelo fuera). En el import se re-cifran con la TOKEN_ENCRYPTION_KEY del destino, así que las dos instalaciones no necesitan compartir clave.
- Sin
--overwrite: filas y ficheros existentes se respetan (se cuentan comoskipped). - Con
--overwrite: las filas se actualizan in-place (mismo id, FKs intactas) y los ficheros del workspace se reemplazan.
Los packages se importan siempre en pending_review (salvo los que ya estaban rejected) para que el instalador del destino los regenere en su propio venv.
┌─────────────────────────────────────────────────────┐
│ Gateway Web (Flask) │
│ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
│ │ API REST │ │Dashboard │ │ SSE Streaming │ │
│ │ /api/* │ │ HTMX │ │ /api/chat │ │
│ └────┬─────┘ └────┬─────┘ └────────┬──────────┘ │
│ └──────────────┼─────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Services │ │
│ │ auth, agent, │ │
│ │ chat, session, │ │
│ │ run, codex, │ │
│ │ scheduler, │ │
│ │ metrics, matrix│ │
│ │ review, creds, │ │
│ │ packages, │ │
│ │ bundle, patches│ │
│ └───────┬────────┘ │
│ │ │
│ ┌───────────────────▼────────────────────────────┐ │
│ │ Agent Runtime │ │
│ │ context_builder → model_client → tool_exec │ │
│ │ context_budget ─── lazy manifest ──┐ │ │
│ │ ▲ │ │ │
│ │ │ agent_runner │ │ │
│ │ └──────── (loop) ◄─────────────┘ │ │
│ └───────────────────┬────────────────────────────┘ │
│ │ │
│ ┌────────────▼──────────────┐ │
│ │ Workspace Manager │ │
│ │ SOUL.md AGENTS.md │ │
│ │ MEMORY.md TOOLS.md │ │
│ │ HEARTBEAT.md PACKAGES.md │ │
│ │ skills/ tools/ agents/ │ │
│ │ .venv/ (per-workspace) │ │
│ └───────────────────────────┘ │
└─────────────────────────────────────────────────────┘
│ │
┌─────▼─────┐ ┌──────▼──────┐
│PostgreSQL │ │ Redis │
│ │ │ cache/broker│
└───────────┘ └─────────────┘
┌─────────────────────────────────────────────────────┐
│ Worker Service │
│ ┌──────────────────┐ ┌─────────────────────────┐ │
│ │ APScheduler │ │ Matrix Adapter │ │
│ │ heartbeat/cron │ │ matrix-nio async │ │
│ └──────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────┘
app/
├── __init__.py # App factory + CLI commands (onboard, codex-*, setup-*, export/import-bundle…)
├── config.py # Configuración por entorno
├── extensions.py # SQLAlchemy, Flask-Login, Bcrypt, CSRF
├── logging_config.py # JSON logging + Redis ring buffer (compartido web+worker para la vista Logs)
├── models/ # SQLAlchemy — ver sección "Modelo de datos"
├── api/ # Blueprints REST: auth, agents, chat (SSE), runs, scheduler, metrics, skills, tools, subagents, patches, credentials, packages
│ ├── middleware.py # Decoradores auth_required, admin_required
│ └── errors.py # Manejadores de error JSON
├── dashboard/ # Vistas HTMX: overview, agents, chat, scheduler, metrics, logs, skills, tools, topology, subagents, patches, credentials, packages, heartbeat
├── services/ # Lógica de negocio: auth, agent, session, chat, run, codex_auth, scheduler, metrics, matrix, skill, tool, subagent, patch, patch_validator, security_policy, credential, package, venv_manager, review, bundle
├── runtime/ # Motor del agente
│ ├── context_builder.py # Ensambla system prompt + historial con budget de tokens
│ ├── context_budget.py # Token counting (tiktoken cl100k_base) + drop-oldest trimming
│ ├── action_heuristics.py# Detecta promesas sin acción ("voy a…") para re-prompting
│ ├── model_client.py # Wrapper Codex con streaming
│ ├── tool_registry.py # Registro de tools built-in + cache per-run de lecturas
│ ├── tool_executor.py # Ejecuta tools y persiste en tool_executions
│ └── agent_runner.py # Loop de razonamiento (max_tool_rounds configurable, cap de 20K chars por tool result)
├── workspace/ # Gestión de ficheros de workspace por agente
│ ├── manager.py # CRUD de ficheros, scaffolding, refresh TOOLS.md
│ ├── loader.py # Carga SOUL/MEMORY/PACKAGES (AGENTS y TOOLS van al manifest lazy)
│ ├── discovery.py # Descubrimiento de skills/tools, sync con BD, carga dinámica
│ └── manifest.py # Validación de manifiestos JSON
├── templates/ # Jinja2 + HTMX
└── static/ # CSS + JS (chat.js con SSE + markdown + context meter)
└── vendor/ # marked.min.js, purify.min.js, htmx.min.js (servidos en local)
worker.py # Entry point del worker (scheduler + Matrix)
app/worker/
├── scheduler.py # APScheduler con Redis job store
└── matrix_adapter.py # matrix-nio async client en daemon thread
| Tabla | Descripción |
|---|---|
users |
Administradores del dashboard (con MFA TOTP opcional) |
agents |
Agentes con slug, workspace, modelo, parent_agent_id, review_effort, review_token_budget_daily, daily_token_budget y daily_cost_budget |
sessions |
Sesiones de chat por canal (web, matrix) |
messages |
Historial de mensajes por sesión |
runs |
Ejecuciones del agente con métricas (tokens, coste, duración, trigger_type) |
tool_executions |
Registro de cada invocación de tool (incluido output completo) |
scheduled_tasks |
Tareas programadas (cron, heartbeat, one-shot) |
skills |
Skills registradas por agente (manifest, estado, fuente) |
tools |
Tools de workspace por agente (manifest, path, timeout) |
patch_proposals |
Propuestas de automejora con diff, snapshot, nivel de seguridad, estado y cadena de auditoría (content_hash, previous_hash) |
objectives |
Objetivos de trabajo del agente (goal-oriented, multi-run) |
heartbeat_events |
Registro de cada tick del supervisor (decisión, razón, snapshot) |
credentials |
Secretos cifrados con Fernet (API keys, user/password) — globales o por agente |
package_installations |
Packages Python por workspace con estado (pending_review / installing / installed / failed / rejected) |
approval_rules |
Reglas de aprobación automática para patches/packages (por agente, tipo y patrón) |
review_events |
Eventos auditados por el reviewer: qué revisó, qué dijo, tokens consumidos |
codex_quota_snapshots |
Snapshot histórico de cuotas/rate-limits devueltos por la API de Codex |
workspace_file_versions |
Historial de versiones de SOUL.md y AGENTS.md editados desde el dashboard (contenido previo, quién guardó, cuándo) |
El flujo de una interacción por chat:
- El usuario envía un mensaje vía
POST /api/chat. - Se crea/recupera la sesión y se persiste el mensaje.
- Se crea un registro
Runpara métricas. context_builderensambla el contexto:- Baseline de seguridad (política plataforma) +
TOOL_PROTOCOL(acción-first). SOUL.md,MEMORY.md,PACKAGES.md, live roster de sub-agentes y pending review items.- Workspace index (lazy):
TOOLS.md,AGENTS.mdy cadaSKILL.mdNO se inlinean — se listan con su path y descripción. El agente los lee bajo demanda conread_workspace_file. - Historial de mensajes pakado newest-first hasta llenar el token budget (
MAX_CONTEXT_TOKENS−CONTEXT_RESPONSE_RESERVE_TOKENS). El mensaje del usuario y el system prompt siempre se preservan.
- Baseline de seguridad (política plataforma) +
agent_runnerinvoca al modelo con streaming; tokens emitidos como SSE.- Si hay tool calls se ejecutan y se vuelve al paso 5 (máx 10 rondas). Resultados > 20 KB se truncan en el contexto pero se guardan enteros en
tool_executions. Mismo fichero leído dos veces en un run devuelve un stub cacheado. - Detección anti-"voy a hacerlo pero no lo hago": si el agente promete sin llamar tool y la petición era accionable, se re-inyecta una nudge-system y se reintenta una vez.
- Se persiste la respuesta final y el
Runse cierra con métricas.
El budget de tokens (MAX_CONTEXT_TOKENS − CONTEXT_RESPONSE_RESERVE_TOKENS) se calcula con
tiktoken (cl100k_base) y se aplica en cada turno:
- El system prompt y el mensaje del usuario actual son intocables.
- El historial se packea newest-first; los mensajes más antiguos que no caben se descartan.
- Los ficheros grandes del workspace (
TOOLS.md,AGENTS.md, cadaSKILL.md) se sirven por manifest, no inline. - El chat muestra un indicador de uso de contexto (barra verde/ámbar/rojo) debajo del input con
input_tokens / budgetdel último turno. GET /api/chat/context?agent_id=…[&session_id=…]devuelve{total_tokens, budget, pct, message_count}para refrescar el indicador desde UI.
Las respuestas del asistente se renderizan con marked + DOMPurify (vendored bajo
app/static/vendor/). El toggle "Hide tool" persiste en localStorage. Los tokens se acumulan en
un buffer y se re-renderizan en cada chunk SSE sin flicker.
Todos disponibles vía tool calls del modelo. Las descripciones concretas y parámetros están en
app/runtime/tool_registry.py.
| Tool | Descripción |
|---|---|
read_workspace_file |
Lee un fichero del workspace. Lecturas repetidas en el mismo run devuelven stub |
list_workspace_files |
Lista todos los ficheros del workspace |
get_current_time |
Devuelve fecha y hora UTC actual |
fetch_url |
GET/POST HTTP con headers, body y timeout; fallback cuando no hay tool específico |
delegate_task |
Delega a un sub-agente por slug o nombre; devuelve la respuesta |
list_subagents |
Lista los sub-agentes activos del agente actual |
create_skill |
Crea un skill completo (SKILL.md + handler) en una sola llamada |
create_tool |
Crea un tool de workspace (manifest + tool.py) |
propose_change |
Propone un cambio de fichero; L1 auto-aplica, L2 espera aprobación, L3 rechaza |
list_patches |
Lista propuestas recientes del agente |
schedule_task |
Crea una tarea cron para este agente (schedule_expr, message) |
list_scheduled_tasks |
Lista tareas programadas del agente |
cancel_scheduled_task |
Cancela una tarea programada por id |
get_credential |
Resuelve una credencial por nombre: DB agent-scoped → DB global → AUTOBOT_CRED_<NAME> |
list_credentials |
Lista nombres y metadata (sin valores) |
set_credential |
Crea o reemplaza una credencial agent-scoped |
delete_credential |
Borra una credencial agent-scoped |
install_package |
Solicita un package PyPI para el venv del workspace (auto-install si está allowlisted) |
list_packages |
Lista el historial de instalaciones del agente con estado |
Almacén cifrado con Fernet en la tabla credentials, gestionado desde el dashboard y accesible
por los agentes vía get_credential.
- Scope:
agent_id IS NULL= global;agent_id= sólo ese agente (shadowa a la global con el mismo nombre). - Tipos:
token(valor único) yuser_password(username + password). - Fallback
AUTOBOT_CRED_<UPPER(NAME)>: permite preseeded de credenciales por.env; devuelvesource="env"al consultarse. - Los valores sólo aparecen en claro cuando el admin pulsa "Reveal" en la UI o cuando el agente llama a
get_credential. Nunca se serializan en las respuestas de listado.
Cada workspace tiene un venv aislado en <workspace>/.venv. El agente solicita instalaciones con
install_package:
- Si el spec normalizado está en
PACKAGE_ALLOWLIST→ auto-install. - Si no → fila en
pending_review, el admin aprueba/rechaza desde el dashboard. - Estados:
pending_review→approved→installing→installed/failed/rejected. VENV_BASE_PACKAGESse instala en cada venv nuevo para tener un kit base.
SOUL.md y AGENTS.md son editables directamente desde Dashboard → Agents → (agente) → panel de detalle. Cada guardado con cambios crea automáticamente un snapshot de la versión anterior en workspace_file_versions. El botón Historial junto a cada fichero permite ver el diff coloreado entre versiones y restaurar cualquiera de ellas con un clic (la restauración también genera un snapshot, por lo que es reversible). MEMORY.md lo gestiona el agente mediante propose_change; TOOLS.md se regenera automáticamente al registrar tools.
Cada agente tiene un directorio en /workspaces/<slug>/:
| Fichero / dir | Propósito | Frecuencia de cambio |
|---|---|---|
SOUL.md |
Identidad, estilo, principios, límites | Rara vez |
AGENTS.md |
Catálogo de agentes/subagentes (lazy-loaded) | Al crear/modificar agentes |
MEMORY.md |
Memoria persistente resumida | Consolidación periódica |
TOOLS.md |
Inventario de tools disponibles (lazy-loaded) | Regenerado al registrar tools |
HEARTBEAT.md |
Checklist declarativa que lee el supervisor cada tick | Editable por el usuario |
PACKAGES.md |
Packages instalados en el venv del workspace | Automático |
skills/ |
SKILL.md y handler por skill | Creados con create_skill |
tools/ |
Manifest y tool.py por tool | Creados con create_tool |
patches/ |
Snapshots pre/post de patches aplicados | Automático |
runs/ |
Logs de ejecuciones (excluido de exports) | Automático |
.venv/ |
Entorno Python aislado (excluido de exports) | Regenerado al importar |
El heartbeat no es un temporizador — es el bucle de supervisión que mantiene al agente "vivo" entre interacciones. Cada tick hace cinco cosas:
- Snapshot del mundo: lee
HEARTBEAT.md, consulta objetivos activos, detectaRunatascadas (status=runningdurante más de 15 min → marcastuck) e identifica el último canal activo (web / matrix). - Decisión por reglas (sin LLM): si no hay nada accionable →
skip; si hay unRunvivo o acaba de actuar (< 60 s) →defer; si hay tasks vivas u objetivos vencidos →act. - Ejecución contextualizada: cuando decide actuar, construye un prompt
con el snapshot (no un prompt estático) y lanza un
Runcontrigger_type=heartbeat, enrutado al último canal activo. - Telemetría: cada tick (
act/skip/defer) queda registrado enheartbeat_eventscon el snapshot y elrun_idsi hubo ejecución. - Cadencia: el intervalo (minutos) se configura por agente en
agents.heartbeat_interval; APScheduler dispara_execute_heartbeatque delega enapp.services.heartbeat_supervisor.tick(agent_id).
Sintaxis de HEARTBEAT.md: cada entrada es un - item con directivas
opcionales inline:
- Revisar errores recientes. every: 2h priority: high
- [done] Tarea antigua (ignorada por el supervisor)
- Consolidar MEMORY.md si está ruidosa. every: 1d priority: lowObjectives (tabla objectives): unidad de trabajo multi-run que sobrevive
entre interacciones. Estados: active, blocked, waiting, done,
cancelled. El supervisor sólo considera los active cuyo next_check_at
haya vencido (o sea NULL). Se gestionan desde /agents/<id>/heartbeat.
Cada agente puede tener un sub-agente de tipo reviewer (creado por setup-default-agents). Los
puntos de auditoría se activan por review_effort (0 = off, 10 = auditoría total). Cuando create_skill,
create_tool o schedule_task ejecutan, la respuesta puede llevar un campo review con el
feedback del reviewer. review_token_budget_daily limita el gasto diario en auditorías por agente
(cuando se supera, el review_gate se cierra hasta el siguiente día UTC). Todo queda auditado en
review_events.
Cada agente puede tener dos caps independientes configurables desde Dashboard → Edit Agent:
| Campo | Tipo | Efecto |
|---|---|---|
daily_token_budget |
Integer (tokens) | Bloquea nuevos runs cuando la suma de input_tokens + output_tokens del día UTC supera el cap |
daily_cost_budget |
Float (USD) | Bloquea nuevos runs cuando el estimated_cost acumulado del día UTC supera el cap |
El check se realiza al inicio de cada run (agent_runner.run()). Si se supera cualquiera de los caps, el run se aborta inmediatamente con un mensaje explicativo y no se factura ningún token adicional. Los límites se resetean automáticamente a medianoche UTC. Dejar en blanco equivale a sin límite.
Una vez que una tool o skill lleva tiempo funcionando en producción —con sus patches aplicados y validados— el admin puede promoverla a la plantilla por defecto (workspaces/_template/). Los agentes creados a partir de ese momento heredarán la tool/skill automáticamente al ejecutar scaffold_workspace.
El mecanismo tiene tres niveles:
Genera un tar.gz listo para revisar y abrir un PR manualmente:
workspaces/_template/<tools|skills>/<slug>/— ficheros de la tool/skill con su estado actual post-patchesPROMOTION.md— metadatos: agente origen, versión, número de patches aplicados, resultado de validación, instrucciones de testpromote.patch— diff unificado contra lo que ya hay en_template/(vacío si es nueva)
Desde el dashboard (admin): Agents → Tools/Skills del agente → 📦 Bundle
Desde la API:
curl -X POST /api/admin/promote/bundle \
-H "Content-Type: application/json" \
-d '{"type": "tool", "agent_id": 1, "slug": "mi-tool"}'
# Retorna {"ok": true, "bundle_name": "mi-tool-20260427.tar.gz", "diff": "...", "pr_title": "...", "pr_body": "..."}
# Descargar el bundle
curl /api/admin/promote/bundle/mi-tool-20260427.tar.gz -o promotion.tar.gzCon el bundle descargado, aplicar al repo y crear el PR:
tar -xzf promotion.tar.gz # extrae en workspaces/_template/tools/mi-tool/
git checkout -b promote/tool/mi-tool
git add workspaces/_template/
git commit -m "promote(tool): mi-tool v1.2"
gh pr create --title "promote(tool): mi-tool" --body "$(cat PROMOTION.md)"Si hay un token de GitHub configurado, la app crea la rama, hace el commit y abre el PR directamente.
Configurar el token (una de las dos vías):
| Vía | Cómo |
|---|---|
| Credencial cifrada (recomendado) | Dashboard → Credentials → New → nombre gh_token, tipo token, scope global |
| Variable de entorno | GH_TOKEN=ghp_xxx en .env (o AUTOBOT_CRED_GH_TOKEN=ghp_xxx) |
El sistema busca en este orden: credencial gh_token en BD → AUTOBOT_CRED_GH_TOKEN en env → GH_TOKEN en env.
El repo destino del PR se toma de AUTOBOT_GITHUB_REPO en .env (default: https://github.com/lliwi/autobot). Si trabajas en un fork, añade:
AUTOBOT_GITHUB_REPO=https://github.com/tu-usuario/autobotDesde el dashboard (admin): Agents → Tools/Skills del agente → 🔀 PR
Desde la API:
curl -X POST /api/admin/promote/pr \
-H "Content-Type: application/json" \
-d '{"type": "skill", "agent_id": 2, "slug": "mi-skill"}'
# Retorna {"ok": true, "pr_url": "https://github.com/lliwi/autobot/pull/42", "branch": "promote/skill/mi-skill"}La rama creada sigue el patrón promote/<type>/<slug>. Si ya existe, se añade un sufijo de timestamp.
Además de actualizar _template/, copia la tool/skill a todos los agentes que no la tengan:
curl -X POST /api/admin/promote/broadcast \
-H "Content-Type: application/json" \
-d '{"type": "tool", "agent_id": 1, "slug": "mi-tool"}'
# Retorna {"ok": true, "broadcast_copied": 3, "broadcast_errors": ["otro-agente: already has tool"]}Los errores de slug duplicado se acumulan en broadcast_errors sin interrumpir el proceso.
La columna Template en la lista de tools/skills muestra ★ En template cuando el slug ya existe en _template/, o ☆ No promovida si aún no se ha promovido. Los botones de promoción sólo son visibles para admins.
Antes de generar el bundle o el PR, la tool/skill pasa por los mismos checks que los patches:
- JSON parseable y con forma válida de manifiesto
- Sintaxis Python correcta (AST)
- Presencia del
def handler(...)(tools) - Smoke import en subprocess (usando el venv del workspace)
Si alguno falla, la operación se cancela con el error detallado.
Las tools de workspace deben seguir la política: versión en manifest.json, nunca en el nombre del directorio. tools/my-tool-v2/ es inválido; usa tools/my-tool/ con "version": "0.2.0".
El script scripts/workspace_tools_manager.py audita y repara instalaciones existentes. Los comandos Flask flask reconcile-tools y flask audit-tools permiten operar sobre la BD desde Flask context.
# Ver hallazgos sin tocar nada (--no-ref-scan omite escaneo de docs/skills para ir más rápido)
docker compose exec web python scripts/workspace_tools_manager.py \
--root workspaces/<agente> --no-ref-scan
# Salida JSON para automatización o CI
docker compose exec web python scripts/workspace_tools_manager.py \
--root workspaces/<agente> --json --no-ref-scan
# Auditar todos los agentes vía Flask (acceso a BD)
docker compose exec web flask audit-tools
docker compose exec web flask audit-tools --agent-id <id> --no-ref-scanEl script sale con código 2 si hay hallazgos de nivel error — útil como CI gate.
# 1. Ver el plan sin aplicar nada
docker compose exec web python scripts/workspace_tools_manager.py \
--root workspaces/<agente> --repair --no-ref-scan
# 2. Aplicar: consolida los -vN al canonical, bump de versión, limpia referencias
docker compose exec web python scripts/workspace_tools_manager.py \
--root workspaces/<agente> --repair --apply --no-ref-scan
# 3. Desactivar los Tool rows en BD cuyos directorios ya no existen
docker compose exec web flask reconcile-tools --apply
# 4. Opcional: hard-delete de rows disabled
docker compose exec web flask reconcile-tools --apply --purge
# 5. Sincronizar versiones en BD con los manifests actualizados
# Desde el dashboard: Agents → <agente> → Tools → Sync from Workspace
# O desde el dashboard: Tools → <tool> → Reload| Válido | Inválido |
|---|---|
tools/my-tool/ + "version": "0.2.0" |
tools/my-tool-v2/ |
tools/my-tool-token/ (variante funcional) |
tools/my-tool-new/ |
tools/my-tool-agentcred/ (variante de credencial) |
tools/my-tool-final/ |
El backup dir .my-tool.pre-tool-manager-backup/ se crea automáticamente durante la reparación si existe una versión canónica que necesita ser desplazada; puede eliminarse manualmente una vez verificada la migración.
Los agentes disponen de tres built-in tools para gestionar su propio workspace sin necesitar acceso al dashboard:
| Tool | Descripción |
|---|---|
create_tool |
Crea un nuevo tool (manifest.json + tool.py) |
rename_tool |
Renombra un tool: mueve el directorio, actualiza manifest.name y BD, regenera TOOLS.md |
delete_tool |
Elimina un tool de disco, BD y TOOLS.md permanentemente |
Uso típico para limpiar herramientas versionadas:
# El agente detecta que tiene runner2 y runner
rename_tool(old_slug="runner2", new_slug="runner") # ⚠ falla si runner ya existe
delete_tool(slug="runner2", reason="Superseded by runner v0.2.0") # eliminar el obsoleto
delete_tool requiere el parámetro reason para el log de auditoría. Ambas operaciones regeneran TOOLS.md automáticamente.
POST /api/auth/login— Login con email/password (+ TOTP si está activado)POST /api/auth/logout— Cerrar sesiónGET /api/auth/me— Usuario actual
GET /api/agents— Listar agentesPOST /api/agents— Crear agente ({"name": "..."})GET /api/agents/:id— DetallePATCH /api/agents/:id— ActualizarPOST /api/agents/:id/start— ActivarPOST /api/agents/:id/stop— Desactivar
POST /api/chat— Enviar mensaje, respuesta SSE ({"agent_id": 1, "message": "hola"})GET /api/chat/context?agent_id=…[&session_id=…]— Tamaño del contexto del próximo turnoGET /api/chat/history?agent_id=…— Historial de la sesión activaGET /api/sessions— Listar sesionesGET /api/sessions/:id/messages— Mensajes de una sesión
GET /api/runs— Listar ejecuciones (paginado, filtrable poragent_id)GET /api/runs/:id— Detalle de ejecución
GET /api/scheduled-tasks— Listar tareas (filtrable poragent_id)POST /api/scheduled-tasks— Crear tareaGET /api/scheduled-tasks/:id— DetallePUT /api/scheduled-tasks/:id— ActualizarDELETE /api/scheduled-tasks/:id— EliminarPOST /api/scheduled-tasks/:id/toggle— Activar/desactivar
GET /agents/:id/heartbeat— Panel con objetivos, últimos ticks yHEARTBEAT.mdPOST /agents/:id/heartbeat/tick— Disparar un tick manual del supervisorPOST /agents/:id/objectives/create— Crear un objetivo (title,description)POST /objectives/:id/update— Actualizar estado (active/blocked/waiting/done/cancelled)POST /objectives/:id/delete— Eliminar un objetivo
GET /api/patches— Listar patches (filtrable poragent_id,status)GET /api/patches/:id— Detalle con diff completoPOST /api/patches/:id/approve— Aprobar patch L2 pendientePOST /api/patches/:id/reject— Rechazar patchPOST /api/patches/:id/apply— Aplicar patch aprobado al workspacePOST /api/patches/:id/rollback— Revertir patch aplicado usando snapshot
GET /api/agents/:id/subagents— Listar sub-agentes de un agentePOST /api/agents/:id/subagents— Crear sub-agentePOST /api/agents/:id/delegate— Delegar tareaGET /api/agents/topology— Árbol completo de topología de agentesGET /api/agents/:id/topology— Subárbol desde un agente
GET /api/skills— Listar skills (filtrable poragent_id)POST /api/skills— Crear skillPATCH /api/skills/:id— ActualizarPOST /api/skills/:id/reload— Recargar manifest desde filesystemPOST /api/skills/:id/toggle— Activar/desactivarPOST /api/skills/sync— Sincronizar skills del workspace con BD
GET /api/tools— Listar tools (filtrable poragent_id)POST /api/tools— Crear toolPATCH /api/tools/:id— ActualizarPOST /api/tools/:id/toggle— Activar/desactivarPOST /api/tools/:id/test— Ejecutar tool con input de pruebaPOST /api/tools/sync— Sincronizar tools del workspace con BD
GET /api/admin/promote/status?type=tool|skill&slug=...— Estado de promoción (en template, rama activa)POST /api/admin/promote/bundle— Generar bundle descargable ({"type", "agent_id", "slug"})GET /api/admin/promote/bundle/<filename>— Descargar bundle generadoPOST /api/admin/promote/pr— Crear PR en GitHub automáticamente ({"type", "agent_id", "slug"})POST /api/admin/promote/broadcast— Copiar a todos los agentes existentes ({"type", "agent_id", "slug"})
GET /api/credentials— Listar (metadata + preview redactado)POST /api/credentials— Crear credencialPATCH /api/credentials/:id— ActualizarDELETE /api/credentials/:id— BorrarGET /api/credentials/:id/reveal— Ver valor en claro (requiere admin)
GET /api/packages— Listar instalaciones (filtrable poragent_id,status)POST /api/packages/:id/approve— Aprobar y lanzar instalaciónPOST /api/packages/:id/reject— RechazarPOST /api/packages/:id/retry— Re-intentar una instalación fallida
GET /api/metrics/runs-per-day— Ejecuciones por díaGET /api/metrics/response-times— Tiempos de respuesta promedioGET /api/metrics/errors— Errores por díaGET /api/metrics/usage-by-agent— Uso por agenteGET /api/metrics/usage-by-channel— Uso por canalGET /api/metrics/usage-by-tool— Uso por tool
| Nivel | Permitido | Ejemplo |
|---|---|---|
| 1 — Auto | Sin aprobación | Editar MEMORY.md, crear skills/tools |
| 2 — Revisión | Requiere aprobación | Modificar skills existentes, crear subagentes |
| 3 — Prohibido | Bloqueado en MVP | Modificar core Flask, OAuth, BD, políticas |
Todo propose_change pasa por un validador estático antes de tocar disco, y
vuelve a pasar en el momento del apply como red de seguridad. Checks actuales
(ver app/services/patch_validator.py):
| Check | Se ejecuta en | Bloquea si |
|---|---|---|
json_parse |
ficheros .json |
no es JSON válido |
manifest_shape |
manifest.json |
falta name / description; parameters no es objeto |
python_syntax |
ficheros .py |
ast.parse falla |
tool_handler_present |
tools/*/tool.py |
no define def handler(...) top-level |
smoke_import |
ficheros .py |
importar el módulo falla (timeout 10 s, corre en el venv del workspace vía subprocess) |
Un fallo de validación → el patch se queda en rejected con el detalle en
test_result_json.validation.error. En el detalle del patch se pueden re-lanzar
las validaciones manualmente con el botón Re-run validations.
PATCHES_PER_HOUR_PER_AGENT (default 30) limita cuántos patches puede generar
un agente por hora deslizante. Sólo cuentan los estados "con coste"
(applied + pending_review + approved); los rechazados por el validador no
consumen cupo, para no penalizar un primer intento sintácticamente roto.
- El agente invoca
propose_change/create_skill/create_tool. - Clasificación L1/L2/L3 + chequeo de rate-limit + validación estática.
- Si pasa validación: snapshot → L1 auto-apply; L2 busca rule → reviewer sub-agent → queda pendiente si ninguno aprueba; L3 rechazado.
- En
apply(manual o automático) re-validación, escritura al workspace y marcado deapplied_at. rollbackrestaura desde el snapshot guardado enworkspaces/<slug>/patches/.
Cada PatchProposal que supera la validación estática recibe dos campos:
content_hash— SHA-256 de(agent_id, target_path, diff_text, created_at, previous_hash).previous_hash—content_hashdel patch anterior del mismo agente (o"genesis"si es el primero).
Esto forma una cadena enlazada: alterar cualquier campo de un registro invalida su hash y rompe el enlace con todos los patches posteriores. La verificación se hace con:
from app.services.patch_audit_service import verify_chain
result = verify_chain(agent_id=1)
# {"ok": True, "total": 42, "broken_at": None, "first_break": None}
# {"ok": False, "total": 42, "broken_at": 17, "first_break": "patch #17: stored hash 3f2a1b… does not match expected 9c4e7d…"}Los patches rechazados por el validador (antes de tocar disco) no entran en la cadena.
Todos los registros de web y worker se envían a una lista Redis
(autobot:logs, cap 5000) además de a stdout. El dashboard incluye una vista
Observability → Logs con filtros por nivel, proceso (web/worker),
logger, mensaje, y auto-refresh cada 5 s. Útil para ver en un solo timeline
qué hizo el scheduler, el Matrix adapter y el runtime del agente.
- Fase 1 — Núcleo: Flask, PostgreSQL, auth, chat SSE, runtime, workspace, Codex OAuth (PKCE CLI)
- Fase 2 — Canales y Scheduler: Matrix, heartbeat, cron con timezones, métricas, worker service
- Fase 3 — Skills y Tools: registro dinámico, descubrimiento desde workspace, validación, carga dinámica, panel dashboard
- Fase 4 — Multiagente: sub-agentes, herencia de OAuth, delegación síncrona, tools
delegate_task/list_subagents, topología, trazabilidadparent_run_id - Fase 5 — Automejora:
PatchProposal, política L1/L2/L3, diff unificado, snapshots + rollback, review-gating con sub-agente reviewer, validador estático (JSON/AST/handler/smoke-import), rate-limit por agente, UI de patches con checks detallados y botón "Re-run validations" - Credenciales: store cifrado Fernet, scopes global/agente, fallback env, tools
get/set/list/delete_credential - Packages por workspace: venv aislado, allowlist, aprobación en dashboard, tools
install_package/list_packages - Gestión de contexto: token budget con
tiktoken, drop-oldest, workspace index lazy-loaded, indicador de uso en chat - Chat markdown: render seguro con marked + DOMPurify
- Portabilidad:
flask export-bundle/flask import-bundlepara clonar una instalación entera - Promoción a plantilla: tools y skills probadas en producción se promueven a
workspaces/_template/vía bundle descargable o PR automático en GitHub (gh_tokendesde credenciales cifradas o env); broadcast opcional a todos los agentes existentes - Logs centralizados: ring buffer Redis compartido web+worker, vista
Observability → Logscon filtros + auto-refresh - Límites diarios por agente:
daily_token_budget(tokens) ydaily_cost_budget(USD) configurables por agente desde el dashboard; freno automático al inicio de cada run cuando se supera el cap del día UTC (agent_budget_service) - Auditoría firmada de patches: cadena SHA-256 en
patch_proposals— cada patch almacenacontent_hashyprevious_hashenlazando con el anterior;patch_audit_service.verify_chain()detecta cualquier manipulación retrospectiva - Métricas de coste: panel en Metrics con coste estimado (USD) por día y por agente, tarjeta resumen con total del período y coste de hoy; alerta visual configurable via
COST_ALERT_EUR_DAILY - Rate-limit en APIs sensibles:
flask-limitercon backend Redis aplicado a/api/auth/login(10/min, 30/h) y/api/chat(30/min); respuesta 429 JSON en todas las APIs - Historial de ficheros de workspace:
SOUL.mdyAGENTS.mdversionados automáticamente en cada guardado desde el dashboard; diff coloreado entre versiones y restauración reversible con un clic (workspace_file_versions) - Tests del core (bootstrap): suite
tests/con 67 casos en pytest cubriendopatch_validator(JSON/AST/handler/smoke-import),security_policy(clasificación L1/L2/L3),approval_rule_service(patrones + CRUD) ypatch_service(propose/approve/apply/reject/rollback, no-op, dedup, rate-limit). Se ejecutan condocker compose run --rm web pytest. - Fase 6 — Hardening: completada