# Policy: Crear un nuevo agente Guía ejecutable para Claude. Seguir paso a paso sin desviarse. ## Inputs — preguntar al usuario si no los da | Input | Requerido | Default | Ejemplo | |-------|-----------|---------|---------| | `agent-id` | sí | — | `monitor-bot` | | `display-name` | sí | — | `"Monitor Agent"` | | `description` | sí | — | `"Monitorea servicios y reporta estado"` | | `llm.provider` | no | `openai` | `openai` o `anthropic` | | `llm.model` | no | `gpt-4o` | `gpt-4o`, `claude-sonnet-4-20250514` | | `tool_use` | no | `false` | `true` si necesita herramientas | | System prompt | sí | — | Texto describiendo rol y capacidades | Si el usuario da todos los inputs, ir directo a la Ruta Rápida. Si faltan, preguntar antes de empezar. ## Ruta rápida — script automatizado ```bash ./dev-scripts/agent/create-full.sh "Display Name" ``` Este script ejecuta en orden: scaffold → build → register Matrix → verify E2EE. Crea todos los archivos, registra en el launcher, genera todas las env vars en `.env`. Después del script, personalizar los 3 archivos del agente (ver sección siguiente). ## Archivos a personalizar después del scaffold ### 1. `agents//agent.go` — Reglas puras Template base (generado por el scaffold): ```go package // sin guiones: "monitor-bot" → package monitor (strip hyphens, strip _bot) import "github.com/enmanuel/agents/pkg/decision" func Rules() []decision.Rule { return []decision.Rule{ // Any DM or mention → LLM { Name: "llm-all", Match: func(ctx decision.MessageContext) bool { return ctx.IsDirectMsg || ctx.IsMention }, Actions: []decision.Action{{ Kind: decision.ActionKindLLM, LLM: &decision.LLMAction{}, }}, }, } } ``` **Reglas estrictas:** - **PURO**: solo imports de `pkg/decision`, cero I/O, cero side effects - Package name = ID sin guiones ni `_bot` (e.g. `monitor-bot` → `package monitor`) - **No usar reglas para comandos** (`!help`, `!ping`, etc.) — los comandos se gestionan via `RegisterCommand` (ver policy `create_command.md`) - Las reglas solo aplican a mensajes normales (sin prefijo `!`) Tipos de acción disponibles: - `ActionKindReply` — respuesta estática (con `ReplyAction{Content: "..."}`) - `ActionKindLLM` — pasa al LLM (con `LLMAction{}`) ### 2. `agents//config.yaml` — Configuración El scaffold genera un config completo con defaults sensatos. Solo personalizar estas secciones: **Identidad** (siempre editar): ```yaml agent: description: "" ``` **LLM** (si quieres cambiar provider/model): ```yaml llm: primary: provider: anthropic # o openai (default) model: claude-sonnet-4-20250514 # o gpt-4o (default) api_key_env: ANTHROPIC_API_KEY # o OPENAI_API_KEY (default) ``` **Claude-code provider** (si usa `claude-code` como provider): ```yaml llm: primary: provider: claude-code claude_code: working_dir: "/tmp/claude-agents/" # SIEMPRE configurar, nunca dejar vacío permission_mode: "bypassPermissions" ``` **Importante**: `working_dir` debe apuntar fuera del repositorio para evitar que el subproceso `claude -p` acceda al código fuente. Si se deja vacío, se usará un directorio temporal (con WARN en logs). **Tool use** (si el agente necesita herramientas): ```yaml llm: tool_use: enabled: true # cambiar de false a true max_iterations: 5 ``` **Personalidad** (ajustar tono): ```yaml personality: tone: friendly # friendly | professional | casual | technical language: es # es | en prefix: "🤖" # emoji del bot ``` **Threads** (habilitado por defecto en el scaffold): ```yaml matrix: threads: enabled: true # responder en threads cuando el mensaje viene de un thread auto_thread: false # true para crear thread automático por cada conversación nueva ``` Referencia completa del schema: `internal/config/schema.go` ### 3. `agents//prompts/system.md` — System prompt Escribir el system prompt completo. Debe incluir: - **Identidad**: quién es, cómo se llama - **Rol**: qué hace, para qué sirve - **Capacidades**: qué puede hacer (incluir tools si `tool_use.enabled: true`) - **Estilo**: idioma, tono, formato de respuestas - **Restricciones**: qué NO debe hacer - **Seguridad** (obligatorio): copiar la seccion de `.claude/templates/security-prompt.md` al final del prompt. Esta seccion protege contra prompt injection. Ejemplo de referencia: `agents/asistente-2/prompts/system.md` ## Registro en el launcher — `cmd/launcher/main.go` El script `new-agent.sh` (ejecutado por `create-full.sh`) hace esto automáticamente. Si falla, hacer manualmente: **Import** (después de los imports de agentes existentes): ```go agent "github.com/enmanuel/agents/agents/" ``` **rulesRegistry** (dentro del map): ```go "": agent.Rules, ``` El `` es el package name del agent.go (sin guiones, sin `_bot`). **El ID en rulesRegistry DEBE coincidir exactamente con `agent.id` en config.yaml.** ## Convención de env vars — REGLA CRÍTICA Normalización: `normalize_id()` → mayúsculas, guiones → underscores. **Sin eliminar sufijos.** | Agent ID | Normalizado | Env vars | |---|---|---| | `assistant-bot` | `ASSISTANT_BOT` | `MATRIX_TOKEN_ASSISTANT_BOT`, `MATRIX_PASSWORD_ASSISTANT_BOT`, `PICKLE_KEY_ASSISTANT_BOT`, `SSSS_RECOVERY_KEY_ASSISTANT_BOT` | | `mi-bot` | `MI_BOT` | `MATRIX_TOKEN_MI_BOT`, ... | **NUNCA** aplicar transformaciones que eliminen partes del ID (no `sed 's/_BOT$//'`). ## Verificación post-creación Checklist a verificar antes de considerar el agente listo: - [ ] `go build -tags goolm ./...` compila sin errores - [ ] `agents//agent.go` exporta `Rules()` y es puro (sin I/O) - [ ] `agents//config.yaml` tiene `agent.id` = nombre del directorio - [ ] `cmd/launcher/main.go` tiene import + entry en rulesRegistry con el mismo ID - [ ] `.env` contiene: `MATRIX_TOKEN_`, `MATRIX_PASSWORD_`, `PICKLE_KEY_`, `SSSS_RECOVERY_KEY_` - [ ] `prompts/system.md` tiene contenido real (no el stub) - [ ] `prompts/system.md` incluye la seccion de seguridad anti-injection (de `.claude/templates/security-prompt.md`) - [ ] Si `tool_use.enabled: true`, el prompt menciona las tools disponibles ## Arranque y verificación ```bash # Arrancar (reconstruye y lanza todos los agentes habilitados) ./dev-scripts/server/start.sh # Verificar logs tail -f run/launcher.log # Logs esperados al arrancar correctamente: # {"level":"INFO","msg":"e2ee ready"} # {"level":"INFO","msg":"agent running"} # {"level":"INFO","msg":"starting matrix sync"} ``` ## Troubleshooting E2EE | Problema | Solución | |----------|----------| | "device not verified by its owner" | `./dev-scripts/agent/verify.sh ` y reiniciar | | "self-signing private key not in cache" | Recovery key incorrecta → re-ejecutar verify.sh | | "received update for device with different signing key" | Recompilar launcher: `go build -tags goolm -o bin/launcher ./cmd/launcher` | | Recovery key sin comillas en .env | Añadir comillas: `SSSS_RECOVERY_KEY_*="EsXX YYYY ..."` | ## Reglas generales - **Nunca** side effects en `agent.go` - **Siempre** compilar con `-tags goolm` - **Siempre** que `agent.id` coincida entre config.yaml, rulesRegistry y directorio - **No** crear `data/` manualmente — se auto-genera - **No** commitear tokens ni passwords - **No** compartir crypto stores entre agentes - Referencia de agente con tools: `agents/asistente-2/` - Referencia de agente simple: `agents/assistant-bot/`