--- name: unibots lang: go domain: infra version: 0.1.0 description: "Plataforma de bots que consumen el bus unibus; primer bot = eco (bot sin LLM que demuestra los dos patrones de conversación del bus)." tags: [bots, messaging, unibus-client] uses_functions: [] uses_types: [] framework: "" entry_point: "cmd/echobot" dir_path: "projects/message_bus/apps/unibots" repo_url: "" e2e_checks: - id: build cmd: "CGO_ENABLED=0 go build ./..." timeout_s: 180 - id: vet cmd: "CGO_ENABLED=0 go vet ./..." timeout_s: 120 - id: unit cmd: "CGO_ENABLED=0 go test ./..." timeout_s: 180 --- ## Qué es `unibots` es la plataforma de **bots** que consumen el bus de mensajería [`unibus`](../unibus/). Un **bot** es todo peer automatizado del bus, con o sin LLM. Un **bot agente** es un bot que contiene un LLM (no aplica todavía aquí). El término único en código, nombres y docs es **bot**. El primer bot es el **bot eco** (`cmd/echobot`): un bot **sin LLM** que se une al bus y devuelve cada mensaje prefijado con `"echo: "`. Existe para demostrar de forma autónoma los **dos patrones de conversación** que el bus expone, sin depender de ningún modelo: | Patrón | Subject | Cómo | Pareja | |---|---|---|---| | **Chat** (bot↔humano) | `room.echo` (cleartext, `room.ModeNATS`) | `Subscribe` a la room + `Publish` la respuesta | cualquier peer en el mismo subject | | **RPC** (bot↔proceso) | `rpc.echo` | request/reply de NATS (`Client.Reply` registra el responder) | cualquier proceso que haga `Client.Request` | `unibots` es **código de aplicación**, no funciones del registry: orquesta la librería cliente de `unibus` (`pkg/client`) y no reimplementa nada. Por eso `uses_functions` está vacío — el crypto/transporte lo aporta `unibus`, que a su vez importa las primitivas del registry. Referencia: [[convencion-bot-vs-agente]]. Consumidor de [[unibus]]. ## Ejemplo Lanzar el echobot contra un `membershipd` corriendo (NATS embebido en `:4250`, HTTP en `:8470`, los defaults productivos de unibus): ```bash cd projects/message_bus/apps/unibus # 1. Bus de membresía/claves (NATS embebido + control plane HTTP) go run ./cmd/membershipd # 2. En otra terminal: el bot eco (defaults: nats://127.0.0.1:4250, http://127.0.0.1:8470) cd ../unibots go run ./cmd/echobot # Loguea al arrancar: endpoint id, subjects de chat y rpc, y a qué bus apunta. ``` Probarlo desde otra terminal sin escribir más Go: ```bash # Modo RPC (bot<->proceso) con la CLI de NATS contra el mismo NATS embebido: # nats --server nats://127.0.0.1:4250 request rpc.echo "ping" # -> recibe "echo: ping" # Modo chat (bot<->humano): cualquier peer que publique en el subject room.echo # (p.ej. el `chat` de unibus apuntando a ese subject, o un cliente propio) recibe # de vuelta "echo: ". ``` Apuntar a un bus distinto: ```bash go run ./cmd/echobot \ --nats-url nats://mi-host:4222 \ --ctrl-url http://mi-host:8470 \ --room-subject room.demo \ --rpc-subject rpc.demo ``` ## Cuando usarla - Cuando quieras **validar que un bus unibus está vivo** end-to-end (chat y RPC) sin montar un bot agente con LLM. - Como **plantilla mínima** para escribir un bot nuevo: copia `cmd/echobot`, cambia la lógica del handler (en vez de `"echo: " + body`, llama a tu servicio, base de datos o, más adelante, a un LLM para convertirlo en bot agente). - Para **demostrar los dos patrones** del bus (chat por room cleartext vs RPC request/reply) a alguien que aprende la arquitectura. ## Gotchas - **Guard anti-bucle (crítico).** El handler de chat ignora los mensajes cuyo `frame.Frame.Sender == c.Endpoint().ID`. Sin este guard, el bot se haría eco de su propio `"echo: ..."` indefinidamente (y dos echobots en el mismo subject entrarían en un bucle infinito). El test `TestChatEcho` verifica que nunca aparece `"echo: echo: hola"`. - **Cleartext comparte subject, no room id.** El bot usa `room.ModeNATS` (cleartext, efímero, sin firma). NATS enruta por **subject**, así que el bot conversa con cualquier peer en el mismo subject aunque cada uno tenga su propio `room_id` (mismo patrón que el worker/chat de unibus). No hay "unirse a una room por nombre": cada `CreateRoom` produce un ULID nuevo mapeado al subject. - **Modo RPC sí está soportado.** La librería de unibus expone request/reply: `Client.Request(subject, body, timeout)` y `Client.Reply(subject, handler)` (cleartext v1, sobre `rpc.*`). El echobot registra un responder con `Reply`. No hubo que omitir ni inventar nada en unibus. - **Identidad = secreto crítico.** `local_files/echobot.id` contiene las claves privadas (Ed25519 + X25519), se escribe 0600. Perderlo no rompe el eco (es cleartext) pero cambia la identidad del bot. Está gitignorado. - **Build sin CGO.** Igual que unibus: `CGO_ENABLED=0`, sin `fts5` ni `gcc`. El crypto del registry (`cybersecurity`) y el driver SQLite pure-Go compilan limpio. - **Los tests usan puertos propios aislados.** El test de integración levanta un `membershipd` con NATS embebido en puertos libres (`:0`) bajo `t.TempDir()`, nunca en `8470/4250` ni en los del playground del usuario; todo se limpia por handle vía `t.Cleanup`. ## Convención de subjects (heredada de unibus) ``` proc.. telemetría/coordinación de procesos rpc. request/reply (rpc.echo) room. chat humano/grupo (room.echo) agent..{in,out} inbox/outbox de bot agente (futuro) ```