@relatalabs/debatidor-agent
v0.6.2
Published
Debatidor Agent CLI: puente entre salas Debatidor y tu sistema de archivos local.
Downloads
991
Readme
Debatidor Agent
Terminal multifacética de Debatidor/RelataLabs: tu consola
conectada a salas multi-modelo donde el "cerebro" puede ser un modelo web vía la
extensión o el PAL por API. El bridge web soporta hoy Qwen Web y ChatGPT Web;
otros hosts (Claude Web, DeepSeek, Kimi, …) se incorporan mediante sus adapters.
Desde 0.5.6, el mismo runner local también sirve como puente para que clientes
MCP autorizados operen directamente el proyecto mediante debatidor-back, sin DOM.
El comando principal es debatidor. debatidor-agent se conserva como alias de
compatibilidad para instalaciones y scripts existentes.
Requisitos
- Node.js 22+
Inicio rápido
# 1) Autenticarse (una sola vez; device flow por navegador)
debatidor auth login
# 2) Entrar al REPL desde tu proyecto
cd mi-proyecto
debatidorProducción es el default (wss://api.debatidor.com/agent). Para desarrollo local:
debatidor --local.
La cabecera del REPL muestra el logo pixelado de Debatidor, la versión efectiva del
paquete, host local, versión de Node y cwd. debatidor --version imprime únicamente
la versión del CLI.
Dentro del REPL
| Comando | Qué hace |
|---|---|
| <texto> | Habla en la sala (persistido como intervención humana). Un paste con varias líneas se envía como una sola intervención, preservando los saltos de línea internos |
| /mode | Muestra el cerebro actual |
| /mode web | El cerebro es el modelo de la pestaña conectada (Qwen web, ChatGPT web, …). Tu mensaje viaja con el contrato de herramientas inyectado automáticamente |
| /mode api | El cerebro es el PAL (participantes API de la sala); sin DOM |
| /mode both | Ambos a la vez (default) |
| /host qwen | Dirige los mensajes web a Qwen (conn_dom_qwen) |
| /host chatgpt | Dirige los mensajes web a ChatGPT (conn_dom_openai) |
| /host all | Broadcast a todos los hosts web habilitados |
| /send tools | (Re)inyecta el contrato de herramientas al modelo web, sin persistir nada |
| /permissions manual\|auto\|plan | Controla la ejecución de shell.run |
| /debate <id> | Vincula la sesión a una sala (vacío = desvincular) |
| /status | Conexión, sala, cerebro, host, cwd, streams |
| /quit | Salir (o Ctrl+C dos veces) |
Al escribir /, el REPL muestra sugerencias filtradas. El ranking favorece
coincidencias exactas y de prefijo; también considera aliases y descripción. Por
ejemplo, /per prioriza /permissions, /hos prioriza /host y /send t
prioriza /send tools. Tab completa usando el mismo registro de comandos que
alimenta /help, por lo que ayuda, autocomplete y ejecución no mantienen listas
duplicadas.
La salida del agente vive en el transcript por encima del compositor. Si llega
un evento mientras estás escribiendo, el CLI conserva el borrador y la posición
del cursor, imprime el evento y restaura la entrada. Los snapshots DOM parciales
no se imprimen por defecto; para depurar streaming crudo se puede arrancar con
DEBATIDOR_STREAM_DEBUG=1. Durante una confirmación manual de shell.run (Y/n),
la salida asíncrona se difiere hasta que respondas para no partir el prompt.
El host seleccionado persiste durante la sesión del REPL. Si cierras Qwen y abres ChatGPT, cambia explícitamente con
/host chatgptantes de enviar la siguiente tarea.
Herramientas (tool calling vía DOM→CLI)
En modo web, el modelo web puede operar tu proyecto emitiendo bloques:
{"tool":"fs.list","path":"src"}
{"tool":"fs.read","path":"src/app.module.ts"}
{"tool":"fs.write","path":"docs/nota.md","content":"texto"}
{"tool":"shell.run","command":"git status"}El backend las enruta a ESTE CLI, que las ejecuta confinado al cwd actual
(sin rutas absolutas, sin .., con .debatidor/protected-files.json para
exclusiones) y devuelve el resultado al modelo para que continúe.
Guardas: watchdog por operación (DOM_TOOL_TIMEOUT_MS, 15 s) y tope de
cadena autónoma configurable mediante DOM_TOOL_MAX_HOPS.
Este camino sigue existiendo para conversaciones web controladas por la extensión.
Modo puente headless (sin REPL)
debatidor connect --remote # producción, filesystem habilitado
debatidor connect --remote --shell-auto # además habilita shell.run
debatidor connect --local # backend local :3001El runner atiende operaciones lead.* del backend para el mismo usuario/workspace.
Además de su uso como worker desatendido, desde 0.5.6 este es el extremo local del
flujo MCP nativo:
ChatGPT / Claude + Debatidor MCP
|
tool MCP
|
debatidor-back
|
/agent WS
|
debatidor
|
repo / filesystem
|
resultado al mismo chatCuando el trabajo se inicia desde un cliente MCP no se necesita la extensión DOM.
Shell headless
Por seguridad, shell.run está deshabilitada por defecto en connect. Con:
debatidor connect --remotefs.list, fs.read y fs.write siguen disponibles, pero lead.execute_command
responde denied_headless_shell_disabled.
Para permitir comandos no interactivos de forma consciente:
debatidor connect --remote --shell-autoSolo entonces el agent anuncia la capability shell.run. El backend/MCP sigue
tratando shell como una acción potencialmente destructiva y no idempotente.
El cwd opcional de un comando shell también debe permanecer dentro del proyecto.
Confinamiento de filesystem
Las operaciones de archivo se resuelven respecto del cwd con el que arrancó el
agent:
- no se aceptan rutas absolutas Unix ni Windows;
- no se permite traversal con
.., usando/o\\; .debatidor/protected-files.jsonpuede definir exclusiones adicionales;- el backend valida paths antes de enviarlos al runner y
fs-guardvuelve a validarlos localmente.
Memoria y proyectos de contexto
La memoria guardada en Debatidor es la fuente autoritativa. El CLI puede generar
una copia local explícita para un proyecto, usando la sesión de debatidor auth
login; no necesita claves BYOK. login, connect y el REPL no generan ni importan
MEMORY.md automáticamente.
# Leer la memoria privada propia, sin crear archivos locales
debatidor memory preview --scope user
# Guardar o actualizar una proyección administrada en este proyecto
debatidor memory sync --scope workspace --root ./mi-proyecto --output MEMORY.md
# Seleccionar fuentes y tipos concretos; ambas opciones son repetibles
debatidor memory export --scope user --source <source-id> --kind FACT --kind DECISION
# Retirar solamente la proyección local, conservando la memoria en Debatidor
debatidor memory remove --root ./mi-proyecto --output MEMORY.mdEl ámbito predeterminado es user: únicamente fuentes privadas propias.
workspace selecciona fuentes compartidas. Un proyecto de contexto es una
colección privada de fuentes que ya puedes leer; agruparlas no concede acceso a
fuentes privadas de otra persona ni a otro workspace.
debatidor memory projects create --name "Mi proyecto"
debatidor memory projects list
debatidor memory projects sources <project-id> --source <source-id> --source <otro-id>
debatidor memory projects show <project-id>
debatidor memory sync --scope project --project <project-id> --root ./mi-proyecto
debatidor memory projects sources <project-id> --clear
debatidor memory projects remove <project-id>projects sources reemplaza la selección completa. Vaciarla requiere --clear.
projects remove elimina la colección y sus snapshots administrados, conservando
las fuentes y el historial. Los identificadores de fuentes aparecen en la
procedencia exportada y en el catálogo de Memoria del Hub.
preview imprime el Markdown completo seleccionado. export y sync guardan ese
mismo contenido junto a MEMORY.md.debatidor.json, que contiene el hash, el ámbito
y la identidad del usuario/workspace, sin credenciales. La raíz local es el
directorio actual salvo --root (--cwd es alias); --output siempre es relativo
a esa raíz. Una regeneración sin cambios conserva los bytes y la fecha del
archivo, aunque el snapshot remoto sea nuevo.
Cada sync usa la selección de esa invocación. Repite --scope, --project,
--source y --kind cuando quieras conservar los mismos filtros; el CLI no los
importa del archivo local ni de su sidecar. Si omites --source o --kind, se
seleccionan todas las fuentes o tipos permitidos dentro del ámbito indicado.
Sin la opción de notas de usuario descrita abajo, no se sobrescriben ni eliminan archivos desconocidos o modificados localmente. Ante un conflicto, conserva tus notas y elige otra ruta de salida. Las rutas protegidas, enlaces simbólicos, junctions y archivos con hardlinks se rechazan. Cada archivo se publica mediante un temporal y rename; una interrupción entre el Markdown y su sidecar deja un conflicto que no se repara silenciosamente. Estas comprobaciones no constituyen un sandbox frente a un proceso hostil que cambie rutas concurrentemente.
Cada exportación descarga todas las páginas admitidas (hasta 10.000 entradas y
32 MiB de Markdown) y retira su snapshot remoto al terminar, también ante fallos.
Un error de página o limpieza impide publicar una copia parcial. Si se pierde la
respuesta al crear el snapshot, no se reintenta automáticamente: el snapshot cuya
identidad no se recibió caduca según el TTL del servidor. Borrar o revocar fuentes
en Debatidor no puede retirar copias que ya descargaste; cada sync vuelve a
consultar los permisos actuales.
El servidor procede de la sesión guardada. --api <origen HTTPS> o --local
permiten seleccionar otro, pero una credencial guardada para otro origen se
rechaza; ejecuta debatidor auth login --api <origen> para autenticarte allí. Se
admite HTTP únicamente en localhost. No se siguen redirecciones con credenciales
ni se ejecuta un login implícito desde estos comandos.
Notas locales editables (opcional, desde 0.5.10)
Para agregar una sección editable al final de la proyección, elige expresamente
--user-notes en memory export o memory sync:
debatidor memory sync --scope project --project <project-id> --root ./mi-proyecto --user-notesEl flag crea el formato nuevo o convierte una proyección anterior intacta.
No adopta archivos ajenos ni convierte ediciones de la parte exportada en notas.
Repite --user-notes y los filtros de selección en cada sincronización posterior;
omitir el flag en un archivo ya convertido provoca un conflicto. Los archivos
previos siguen con su formato y sidecar v1 mientras no optes por la conversión.
Edita únicamente entre los dos delimitadores finales del archivo:
<!-- debatidor:user-notes:v1:start -->
Estas notas son mías y se conservan al sincronizar.
<!-- debatidor:user-notes:v1:end -->La DB es autoritativa para la parte exportada. sync conserva los bytes exactos
de tus notas, incluidos Unicode, espacios y saltos de línea. Editar sólo las notas
no obliga a reescribir el archivo ni el sidecar si la DB no cambió. Las notas no
se envían al Hub, no se importan y no aparecen en los resúmenes del comando.
memory preview sigue mostrando únicamente el export remoto. El CLI tampoco
ejecuta acciones Git sobre ninguna de las dos secciones.
El sidecar v2 guarda el hash y longitud en bytes UTF-8 de la parte generada, su scope/usuario/workspace/revisión y el origen API sin credenciales. La sección se localiza por ese límite verificado y por el cierre al final del archivo; marcadores que aparezcan dentro del contenido de la DB se tratan como datos. Los dos textos exactos de delimitador están reservados y no se admiten dentro de las notas. Cambios fuera de la sección, marcadores alterados, metadata editada, UTF-8 inválido y cambios de API o identidad/scope se rechazan. Se mantienen todos los controles de rutas, enlaces, locks y publicación descritos arriba. Evita que el editor reformatee el archivo completo o cambie sus saltos de línea: también cambiaría los bytes de la parte autoritativa y produciría un conflicto.
La parte generada admite hasta 32 MiB y las notas hasta 1 MiB, más los delimitadores. Estos límites cuentan bytes, no caracteres ni índices UTF-16. Para retirar una proyección con notas no vacías, cópialas antes si quieres conservarlas y autoriza explícitamente su eliminación local:
debatidor memory remove --root ./mi-proyecto --discard-user-notesSin --discard-user-notes, remove preserva las notas no vacías y devuelve un
conflicto. Una sección vacía y las proyecciones v1 mantienen el borrado local
habitual. Este comando no requiere credenciales ni borra memoria remota.
Sesiones y conocimiento explícito (desde 0.5.11)
Estos comandos consumen la misma API de Contexto que el Hub y MCP. Requieren un
backend con las rutas de sesiones y conocimiento P11; la versión del CLI no
demuestra que ese backend ya esté desplegado. Usan la sesión guardada por
debatidor auth login; no aceptan --token, no abren otro login ni usan BYOK.
No capturan conversaciones, archivos, el REPL o notas de MEMORY.md automáticamente.
debatidor memory sessions create --root ./mi-proyecto --input session.json
debatidor memory sessions list --project <project-id> --limit 50
debatidor memory sessions show <session-id>
debatidor memory sessions append <session-id> --root ./mi-proyecto --input event.json
debatidor memory sessions events <session-id> --limit 50
debatidor memory sessions events <session-id> --limit 50 --cursor <nextCursor>
debatidor memory sessions close <session-id>
debatidor memory declarations create --root ./mi-proyecto --input declaration.json
debatidor memory declarations show <declaration-id>
debatidor memory origin show SESSION_EVENT <event-id> --revision 1
debatidor memory statussessions show devuelve metadatos. sessions list y sessions events consultan
una página por invocación, de 50 registros por defecto y hasta 100 con
--limit. Continúa con nextCursor si no es null, conservando el proyecto
seleccionado o el ID de sesión. events conserva el throughSequence fijado al
abrir la primera página: los eventos posteriores quedan para una consulta nueva.
Una respuesta inválida, un cambio de sesión/fuente/límite o un error de acceso
impide imprimir esa página. El CLI no convierte páginas incompletas en éxitos.
--project en list es opcional; sin él lista las sesiones privadas propias.
Para create y append, prepara un JSON UTF-8 y selecciona su ruta relativa
con --input dentro de un --root explícito (--cwd es alias). La lectura usa
los controles del proyector: rechaza escapes de raíz, rutas protegidas, symlinks,
junctions, hardlinks, cambios de identidad de archivo y UTF-8 inválido. No crea
archivos ni directorios. El JSON completo admite hasta 256 KiB, incluidos los
escapes y la procedencia; los límites de contenido siguientes son adicionales.
No se aceptan campos desconocidos ni credenciales dentro del objeto JSON.
session.json:
{
"label": "Revisión del proyecto",
"projectId": "<project-id>",
"clientSessionId": "revision-proyecto-001"
}label es obligatorio (hasta 120 unidades UTF-16). projectId y
clientSessionId son opcionales. El servidor crea una fuente privada propia y,
si seleccionaste proyecto, la vincula a esa colección. La respuesta contiene
id de sesión y sourceId; usa esos IDs en las siguientes operaciones.
event.json:
{
"clientEventId": "revision-evento-001",
"role": "ASSISTANT",
"content": "Se propone revisar la decisión antes de aprobarla."
}Los tres campos son obligatorios. role admite HUMAN, ASSISTANT, TOOL o
SYSTEM; declarar un rol no activa herramientas ni cambia permisos. El texto
se conserva exactamente, con hasta 32768 bytes UTF-8. append devuelve el
evento aceptado, su secuencia y materialization: "queued"; esto acredita el
guardado raw y la admisión de trabajo, no que ya terminó su materialización.
close impide añadir nuevos eventos y conserva el historial.
declaration.json:
{
"clientDeclarationId": "decision-001",
"sourceId": "<source-id-de-la-sesion>",
"kind": "DECISION",
"content": "La revisión es necesaria antes de aprobar.",
"origins": [
{
"rawType": "SESSION_EVENT",
"rawId": "<event-id>",
"revision": 1,
"startUtf16": 0,
"endUtf16": 10
}
]
}Todos estos campos son obligatorios. kind admite FACT, DECISION y
CONCLUSION; la declaración sigue siendo una afirmación explícita del usuario,
sin inferencia automática de ese significado. content admite 24000 bytes
UTF-8. Hay entre 1 y 32 orígenes distintos (MESSAGE o SESSION_EVENT), de la
misma fuente seleccionada. Cada cita usa una revisión de 1 a 2147483647 y un intervalo
no vacío [startUtf16, endUtf16) del texto raw completo. Los offsets cuentan
unidades UTF-16, no bytes ni caracteres visibles; el servidor comprueba que
no corten pares sustitutos y que la revisión siga vigente y sea accesible.
Usa origin show para consultar el texto completo y elegir los límites exactos.
Los IDs de cliente de eventos y declaraciones son obligatorios para reconocer una repetición explícita. Reutilizar el mismo ID con otro contenido genera conflicto. Cada comando envía la mutación una sola vez, incluso si pierde la respuesta: el error no prueba que el servidor haya descartado la operación. Puedes comprobar el estado o repetir deliberadamente la misma solicitud y su ID; nunca se reintenta automáticamente.
events, declarations show y origin show imprimen contenido únicamente por
esas invocaciones. origin show verifica tipo/ID/revisión y SHA-256 del contenido
completo (hasta 1 MiB), también para revisiones históricas disponibles. El estado
declarado puede ser current, stale o forgotten: olvidar memoria derivada no
borra el historial raw original. memory status imprime métricas agregadas del
índice y del pipeline de conocimiento, incluidas colas, resúmenes obsoletos y
pendingMessageHydration de mensajes de escritores anteriores pendientes de
hidratar. Esas métricas no implican que una cola vacía contenga conocimiento
completo ni que todas las declaraciones sean correctas.
Las respuestas JSON sólo incluyen los campos reconocidos; errores remotos y
credenciales no se imprimen. Cada página/respuesta tiene un máximo de 24 MiB de
transferencia JSON. El export conserva la procedencia canónica y valida
origins y derivation cuando existen (verbatim, extractive, declared,
pipeline 1), manteniendo compatibilidad con entradas anteriores sin esos campos.
Los spans vacíos de un resumen representan entradas consideradas sin cita.
La proyección y las notas locales v1/v2 conservan su comportamiento anterior.
Desarrollo
npm test # node --test
node src/cli.js # desde el repoMedia Rail (fs.get / asset.stream)
lead.get_file: lectura binaria inline (base64, topeDEBATIDOR_ASSET_INLINE_MAX_BYTES, default 8 MiB, máximo 32 MiB) con MIME detectado por magic bytes y dimensiones de imagen (png/jpeg/gif/webp/bmp).metadataOnlydevuelve tamaño, sha256 y tipo sin transferir bytes;streamemiteagent.file_chunk(DEBATIDOR_ASSET_STREAM_CHUNK_BYTES, default 256 KiB) con back-pressure sobre el WebSocket.asset.begin/chunk/commit/abortse encola poruploadId: el Hub puede mantener varios chunks en vuelo (antes cualquier solapamiento respondíaasset_upload_busy). El tamaño de chunk es configurable enbegin(chunkSize, 4096–65536 bytes, default 16384, envDEBATIDOR_ASSET_CHUNK_BYTES) para hosts cuyo canal de I/O impone un límite de mensaje pequeño; el agente recorta el valor solicitado a ese rango y lo devuelve. Cada chunk se decodifica desdebase64(default) ohex.- Ambos modos (
connectheadless ychatinteractivo) compartensrc/lead-file-ops.js, por lo quefs.put,asset.*yfs.getse comportan igual en los dos. - Los bytes nunca se re-encodean ni se recomprimen en ninguna dirección.
