# Policy: Crear un nuevo agente o robot

Guia ejecutable para Claude. Seguir paso a paso sin desviarse.

## Robot vs Agent — decidir primero

| | Agent | Robot |
|---|---|---|
| **Cuando usar** | Necesita LLM, reglas, memoria, tools | Solo responde comandos (!xxx) |
| **Runtime** | `devagents.New()` — completo | `devagents.NewRobot()` — ligero |
| **Config type** | `type: agent` (default) | `type: robot` |
| **LLM** | Si | No |
| **Reglas** | Si (`agent.go` con `Rules()`) | No (sin `agent.go`) |
| **Memoria/Knowledge/Skills** | Si (opcionales) | No |
| **Tools** | Si (opcionales) | No |
| **System prompt** | Si (`prompts/system.md`) | No necesario |
| **Comandos built-in** | help, ping, tools, tool, status, info, clear, prompts, version | help, ping, status, info, version |
| **Comandos custom** | Si (`RegisterCommand`) | Si (`RegisterCommand`) |
| **Template** | `agents/_template/` | `agents/_template_robot/` |
| **Config ejemplo** | ~260 lineas | ~55 lineas |

**Regla**: si el bot necesita entender lenguaje natural, es un Agent. Si solo necesita comandos directos, es un Robot.

## Inputs — preguntar al usuario si no los da

| Input | Requerido | Default | Ejemplo |
|-------|-----------|---------|---------|
| `agent-id` | si | — | `monitor-bot` |
| `display-name` | si | — | `"Monitor Agent"` |
| `description` | si | — | `"Monitorea servicios y reporta estado"` |
| `type` | no | `agent` | `agent` o `robot` |
| `llm.provider` | no (N/A para robots) | `openai` | `openai` o `anthropic` |
| `llm.model` | no (N/A para robots) | `gpt-4o` | `gpt-4o`, `claude-sonnet-4-20250514` |
| `tool_use` | no (N/A para robots) | `false` | `true` si necesita herramientas |
| System prompt | si (N/A para robots) | — | Texto describiendo rol y capacidades |

Si el usuario da todos los inputs, ir directo a la Ruta Rapida. Si faltan, preguntar antes de empezar.

## Ruta rápida — script automatizado

```bash
./dev-scripts/agent/create-full.sh <agent-id> "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-id>/agent.go` — Reglas puras

Template base (generado por el scaffold):

```go
package <pkgname>  // sin guiones: "monitor-bot" → package monitor (strip hyphens, strip _bot)

import (
    "github.com/enmanuel/agents/devagents"
    "github.com/enmanuel/agents/pkg/decision"
)

func init() {
    devdevagents.Register("<agent-id>", Rules)
}

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` y `agents` (para Register), cero I/O, cero side effects
- **Auto-registro**: cada agente se registra via `init()` con `devagents.Register("<agent-id>", Rules)`
- 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/<agent-id>/config.yaml` — Configuración

El scaffold genera un config completo con defaults sensatos. Solo personalizar estas secciones:

**Identidad** (siempre editar):
```yaml
agent:
  description: "<la descripción del agente>"
```

**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/<agent-id>"  # 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/<agent-id>/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:

**Blank import** (en la sección de blank imports de agentes):
```go
_ "github.com/enmanuel/agents/agents/<agent-id>"
```

Las reglas se registran automáticamente via `init()` en el paquete del agente.
No se necesita editar ningún map ni registry manualmente.
**El ID en `devagents.Register()` 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/<id>/agent.go` exporta `Rules()` y es puro (sin I/O)
- [ ] `agents/<id>/config.yaml` tiene `agent.id` = nombre del directorio
- [ ] `cmd/launcher/main.go` tiene blank import del paquete del agente
- [ ] `.env` contiene: `MATRIX_TOKEN_<NORM>`, `MATRIX_PASSWORD_<NORM>`, `PICKLE_KEY_<NORM>`, `SSSS_RECOVERY_KEY_<NORM>`
- [ ] `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 <id>` 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, `devagents.Register()` 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/`
