post inicial
This commit is contained in:
@@ -1,3 +1,718 @@
|
||||
# iasysadmin
|
||||
# sysagent
|
||||
|
||||
Agente de IA para administrar sistemas operativos
|
||||
Agente autónomo de administración de sistemas escrito en Go. Usa un modelo de
|
||||
lenguaje accesible mediante una API compatible con OpenAI y trabaja según las
|
||||
reglas del `agent.md` del directorio desde el que se ejecuta.
|
||||
|
||||
> **Estado: completo (fases 1–5 de la especificación).** El agente ejecuta
|
||||
> comandos y herramientas de archivos bajo una política declarada en `agent.md`,
|
||||
> mantiene `contexto.mem` y `memoria.md`, comprime el contexto automáticamente,
|
||||
> investiga en Internet, descarga con verificación de checksum, aplica y genera
|
||||
> parches, pide contraseñas sin eco y documenta sus propias herramientas.
|
||||
> Ver [Limitaciones conocidas](#limitaciones-conocidas).
|
||||
|
||||
La especificación completa del proyecto está en `Agent.md`.
|
||||
|
||||
## Descripción
|
||||
|
||||
`sysagent` administra, diagnostica y mantiene sistemas Linux bajo autorización
|
||||
del propietario. Se ejecuta desde un directorio de proyecto (PROJECT_ROOT) que
|
||||
contiene su configuración (`init.conf`), sus reglas y permisos (`agent.md`) y,
|
||||
opcionalmente, tareas (`01-*.md`, `02-*.md`...). Puede:
|
||||
|
||||
- procesar tareas de forma autónoma (`sysagent`) o conversar (`sysagent --cli`);
|
||||
- leer y modificar el sistema mediante herramientas controladas por la política:
|
||||
comandos, archivos (escritura atómica, backup, validación), parches;
|
||||
- investigar lo que no sabe (documentación, CVE, avisos de seguridad) y
|
||||
descargar con verificación de checksum;
|
||||
- recordar lo aprendido (`memoria.md`) y el histórico de lo hecho
|
||||
(`contexto.mem`), comprimiendo el contexto cuando se acerca al límite;
|
||||
- verificar sus acciones y distinguir PLAN / EXECUTED / VERIFIED.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```text
|
||||
cmd/sysagent/ punto de entrada: flags, señales, código de salida
|
||||
internal/config/ parser de init.conf, enmascarado de secretos
|
||||
internal/policy/ política (bloque ```policy), clasificación de comandos, análisis de shell y Guard
|
||||
internal/project/ PROJECT_ROOT: agent.md, descubrimiento de tareas, aislamiento, memoria.md, .agent/
|
||||
internal/llm/ interfaz Client + cliente OpenAI-compatible (reintentos, descubrimiento de contexto)
|
||||
internal/ctxmgr/ conversación, estimación calibrada de tokens, división segura y compresor de contexto
|
||||
internal/memory/ memoria.md estructurada (remember/forget) y contexto.mem (estado persistente)
|
||||
internal/fsutil/ escritura atómica y backups compartidos
|
||||
internal/term/ lectura sin eco del terminal (ioctl, sin dependencias)
|
||||
internal/patch/ aplicar y generar diffs unificados (Go puro, sin patch/git)
|
||||
internal/tools/ herramientas: exec, shell, archivos, memoria, web, CVE, descargas, parches
|
||||
internal/toolcall/ parser del protocolo de herramientas por texto (```tool, <tool_call>)
|
||||
internal/audit/ logging por niveles y agent.log, con redacción de secretos
|
||||
internal/agent/ arranque, prompt de sistema, ciclo del agente, modo tareas y modo CLI
|
||||
examples/ agent.md de ejemplo, unidades systemd (service + timer)
|
||||
Makefile atajos opcionales: build, check, race, cross, install, live
|
||||
```
|
||||
|
||||
El programa solo depende de la biblioteca estándar de Go (sin dependencias
|
||||
externas; `go.sum` no es necesario).
|
||||
|
||||
Orden de arranque (estricto): PROJECT_ROOT → `init.conf` → `agent.md` →
|
||||
política → `memoria.md` → `contexto.mem` → modo → tareas → contexto inicial →
|
||||
modelo → ciclo del agente.
|
||||
|
||||
## Requisitos
|
||||
|
||||
- Go **1.24** o superior (probado con 1.24.4 de Debian 13).
|
||||
En Debian/Ubuntu: `sudo apt install golang-go`.
|
||||
- Un servidor de inferencia con API estilo OpenAI (`POST /chat/completions`):
|
||||
llama.cpp `llama-server`, vLLM, Ollama, LM Studio, o un servicio remoto.
|
||||
- Linux (amd64 o arm64).
|
||||
- Opcional: `make` para usar el `Makefile`; `gcc` para `go test -race`.
|
||||
|
||||
## Compilación
|
||||
|
||||
```bash
|
||||
git clone <repository>
|
||||
cd <repository>
|
||||
|
||||
go mod download # no descarga nada: no hay dependencias externas
|
||||
go test ./...
|
||||
go build -o sysagent ./cmd/sysagent
|
||||
```
|
||||
|
||||
Pruebas opcionales contra servicios reales (NVD, Debian, buscador, descargas),
|
||||
para comprobar que los formatos externos no han cambiado:
|
||||
|
||||
```bash
|
||||
go test -tags live -run Live -v ./internal/tools
|
||||
```
|
||||
|
||||
Con `make` (opcional): `make check` (gofmt + vet + tests), `make build`,
|
||||
`make race`, `make cross`, `make install`, `make live`. La versión se toma de
|
||||
`git describe` o de `VERSION=...`.
|
||||
|
||||
Con versión inyectada:
|
||||
|
||||
```bash
|
||||
go build -ldflags "-X main.version=0.1.0" -o sysagent ./cmd/sysagent
|
||||
```
|
||||
|
||||
Compilación cruzada:
|
||||
|
||||
```bash
|
||||
GOOS=linux GOARCH=amd64 go build -o sysagent-amd64 ./cmd/sysagent
|
||||
GOOS=linux GOARCH=arm64 go build -o sysagent-arm64 ./cmd/sysagent
|
||||
```
|
||||
|
||||
## Instalación
|
||||
|
||||
```bash
|
||||
sudo install -m 0755 sysagent /usr/local/bin/sysagent
|
||||
```
|
||||
|
||||
Preparar un proyecto:
|
||||
|
||||
```bash
|
||||
mkdir -p /root/admin-agent && cd /root/admin-agent
|
||||
cp /ruta/al/repo/init.conf.example init.conf && chmod 600 init.conf
|
||||
cp /ruta/al/repo/examples/agent.md.example agent.md
|
||||
$EDITOR init.conf agent.md
|
||||
```
|
||||
|
||||
## init.conf
|
||||
|
||||
Una directiva `KEY=VALUE` por línea; se permiten comentarios `#` y líneas vacías.
|
||||
Las claves repetidas son un error. Las claves desconocidas se reportan y se ignoran.
|
||||
|
||||
| Clave | Obligatoria | Descripción |
|
||||
|---|---|---|
|
||||
| `API_URL` | sí | URL base, p.ej. `http://127.0.0.1:8080/v1` |
|
||||
| `API_KEY` | no | Clave; se envía como `Authorization: Bearer`. Se muestra enmascarada (`sk-2f1...8a9`) |
|
||||
| `MODEL` | sí | Nombre exacto del modelo |
|
||||
| `CONTEXT_SIZE` | no | Tokens máximos. `0`/ausente: valor del servidor |
|
||||
| `REQUEST_TIMEOUT` | no | Segundos por petición al modelo (300 por defecto) |
|
||||
| `TOOL_CALLING` | no | `auto` (defecto), `native` o `json`. Ver [Tool calling](#tool-calling) |
|
||||
| `SEARCH_URL` | no | Plantilla del buscador con `{query}` (p.ej. SearXNG JSON). Vacío: DuckDuckGo HTML |
|
||||
|
||||
Con `CONTEXT_SIZE=0` el agente consulta `GET {API_URL}/models`
|
||||
(`max_model_len`, `context_length`, `meta.n_ctx_train`) y `GET /props`
|
||||
(`n_ctx` de llama.cpp). Si no obtiene nada, el tamaño queda como `UNKNOWN`:
|
||||
nunca se inventa.
|
||||
|
||||
## agent.md
|
||||
|
||||
Autoridad primaria del agente: identidad, propósito, límites y política. Si no
|
||||
existe, el programa se niega a arrancar (`ERROR: agent.md not found in PROJECT_ROOT`).
|
||||
Se acepta una única variante que solo difiera en mayúsculas (p.ej. `Agent.md`),
|
||||
con una advertencia.
|
||||
|
||||
### Bloque de política
|
||||
|
||||
Solo se interpretan las directivas dentro de un bloque cercado con info string
|
||||
`policy`. Así, los ejemplos `KEY=VALUE` del texto libre nunca conceden permisos
|
||||
por accidente:
|
||||
|
||||
````markdown
|
||||
```policy
|
||||
EXECUTION_MODE=privileged
|
||||
ALLOW_NETWORK=true
|
||||
ALLOW_DELETE=false
|
||||
```
|
||||
````
|
||||
|
||||
| Directiva | Valores | Por defecto |
|
||||
|---|---|---|
|
||||
| `EXECUTION_MODE` | `guarded`, `privileged`, `unrestricted` | `guarded` |
|
||||
| `ALLOW_NETWORK`, `ALLOW_WEB_RESEARCH`, `ALLOW_DOWNLOADS`, `ALLOW_PACKAGE_INSTALL`, `ALLOW_SERVICE_RESTART`, `ALLOW_OUTSIDE_PROJECT_READ`, `ALLOW_OUTSIDE_PROJECT_WRITE`, `ALLOW_DELETE`, `ALLOW_REBOOT`, `ALLOW_SELF_TOOL_CREATION` | `true`/`false` (también `yes/no`, `1/0`, `on/off`) | `false` |
|
||||
| `BACKUP_BEFORE_SYSTEM_EDIT` | booleano | `true` |
|
||||
| `DEFAULT_COMMAND_TIMEOUT` | segundos (> 0) | `60` |
|
||||
| `MAX_STEPS` | 1–1000 llamadas al modelo por tarea o mensaje | `40` |
|
||||
| `MAX_DOWNLOAD_SIZE` | bytes (sufijos `K`, `M`, `G`) | `1G` |
|
||||
| `TASK_ORDER` | lista separada por comas | orden lexicográfico |
|
||||
|
||||
Reglas: sin bloque se aplican los valores por defecto; varios bloques, una
|
||||
directiva repetida o un valor inválido impiden arrancar; las directivas
|
||||
desconocidas se reportan y se ignoran. `unrestricted` solo se activa si se
|
||||
escribe literalmente, y no implica ninguna capacidad `ALLOW_*`.
|
||||
|
||||
## Herramientas
|
||||
|
||||
| Herramienta | Qué hace |
|
||||
|---|---|
|
||||
| `exec` | Ejecuta un programa con argumentos (sin shell; `exec.CommandContext`) |
|
||||
| `shell` | Ejecuta una expresión con `bash -lc` (tuberías, redirecciones, bucles) |
|
||||
| `read_file` | Lee un archivo de texto con números de línea (`offset`/`limit`) |
|
||||
| `write_file` | Crea o reemplaza un archivo de forma atómica, con backup y validación opcional |
|
||||
| `edit_file` | Reemplaza un fragmento exacto de texto (atómico, backup, validación) |
|
||||
| `append_file` | Añade texto al final (atómico, backup) |
|
||||
| `copy_file` / `move_file` | Copia (atómica) o mueve/renombra |
|
||||
| `remove_file` | Borra archivo o directorio (`recursive`); requiere `ALLOW_DELETE` |
|
||||
| `mkdir`, `list_dir`, `stat`, `chmod`, `chown` | Operaciones de sistema de archivos |
|
||||
| `system_info` | OS, kernel, CPU, RAM, uid/euid, sudo/doas, init, gestores de paquetes, montajes, interfaces |
|
||||
| `remember` / `forget` | Guardar/eliminar conocimiento persistente en `memoria.md` (ver abajo) |
|
||||
| `web_search` | Búsqueda web: títulos, URLs y fragmentos |
|
||||
| `web_fetch` | Descarga una página y la convierte a texto con enlaces numerados (o `raw`) |
|
||||
| `cve_lookup` | CVE en NVD (descripción, CVSS, rangos afectados, referencias) + Debian Security Tracker (versión corregida por release) |
|
||||
| `download` | Descarga HTTP(S) con límite de tamaño y verificación SHA256/SHA512 o `checksum_url` |
|
||||
| `apply_patch` | Aplica un diff unificado: todo o nada, archivado y con copias para rollback |
|
||||
| `diff_files` | Genera un diff unificado entre dos archivos (documentar cambios, crear parches) |
|
||||
| `register_tool` | Documenta en `memoria.md` una herramienta creada por el agente |
|
||||
| `find_program` | Comprueba qué programas existen y cómo encontrar el paquete que falta |
|
||||
| `ask_secret` | Pide una contraseña al operador sin eco (solo `--cli`); el modelo solo ve `{{secret:nombre}}` |
|
||||
|
||||
Comandos (`exec`/`shell`):
|
||||
|
||||
- Timeout obligatorio (`DEFAULT_COMMAND_TIMEOUT` o `timeout_seconds`, máx. 2 h). El
|
||||
proceso corre en su propio grupo y al vencer el timeout (o con Ctrl+C) se mata
|
||||
el grupo completo, incluidos los hijos.
|
||||
- `stdin` es `/dev/null` y se fuerzan `DEBIAN_FRONTEND=noninteractive`,
|
||||
`PAGER=cat`, `SYSTEMD_PAGER=cat`, etc., para que nada espere entrada.
|
||||
- `cwd` configurable sin cambiar el directorio del agente.
|
||||
- stdout y stderr se capturan por separado. Si superan 16 KiB, el modelo recibe
|
||||
los primeros 8 KiB y los últimos 4 KiB, y la salida completa se guarda en
|
||||
`.agent/tmp/` para consultarla con `read_file` o `grep`.
|
||||
|
||||
Archivos:
|
||||
|
||||
- Escritura atómica: temporal en el mismo directorio → fsync → permisos/propietario
|
||||
del original → validación → `rename` → fsync del directorio.
|
||||
- `validate`: comando que se ejecuta sobre el temporal **antes** de aplicarlo
|
||||
(`{file}` se sustituye por su ruta), p. ej. `{"command":"sshd","args":["-t","-f","{file}"]}`.
|
||||
Si falla, el original no se toca.
|
||||
- Con `BACKUP_BEFORE_SYSTEM_EDIT=true`, todo archivo existente **fuera** de
|
||||
PROJECT_ROOT se copia antes de modificarlo o borrarlo a
|
||||
`archivo.sysagent.YYYYMMDD-HHMMSS.bak` (mismos permisos y propietario). El
|
||||
resultado incluye la instrucción de rollback.
|
||||
- Idempotencia: si el contenido ya es el pedido, no se escribe nada.
|
||||
- Argumentos validados estrictamente: un campo desconocido o JSON inválido es un error.
|
||||
- Con `reason`, el motivo del cambio queda en `agent.log`.
|
||||
|
||||
## Aplicación de la política
|
||||
|
||||
Todas las herramientas pasan por `policy.Guard`. Los comandos se clasifican por
|
||||
riesgo (desenvolviendo `sudo`, `env`, `timeout`, `nice`, `xargs`, `find -exec`,
|
||||
`bash -c`...); en `shell` se analiza el script completo, incluidas `$(...)`,
|
||||
`` `...` ``, `<(...)`, heredocs, redirecciones y cambios de directorio con `cd`.
|
||||
|
||||
| Riesgo | Ejemplos | Requiere |
|
||||
|---|---|---|
|
||||
| read-only | `ls`, `cat`, `systemctl status`, `ip addr`, `dpkg -l`, `nft list`, `journalctl` | — |
|
||||
| network | `ping`, `dig`, `curl`, `ssh` | `ALLOW_NETWORK` |
|
||||
| download | `wget`, `curl -o` | `ALLOW_DOWNLOADS` (+ network) |
|
||||
| write | `cp`, `mv`, `mkdir`, `sed -i`, `tar -x`, `make`, `go build` | `privileged`; en `guarded` solo dentro de PROJECT_ROOT |
|
||||
| delete | `rm`, `rmdir`, `find -delete`, `journalctl --vacuum-*` | `ALLOW_DELETE` |
|
||||
| package | `apt install`, `dpkg -i`, `dnf`, `pip install` | `ALLOW_PACKAGE_INSTALL` |
|
||||
| service | `systemctl restart`, `service x reload`, `nginx -s reload` | `ALLOW_SERVICE_RESTART` |
|
||||
| admin | `useradd`, `chmod`, `ip route add`, `nft add`, `mount`, `sysctl -w`, `kill` | `privileged` |
|
||||
| destructive | `dd`, `mkfs`, `wipefs`, `fdisk`, `userdel`, `shred` | `unrestricted` |
|
||||
| reboot | `reboot`, `shutdown`, `systemctl poweroff` | `ALLOW_REBOOT` (en cualquier modo) |
|
||||
| unknown | programas no catalogados, `python`, `awk`, `$CMD` | `privileged` |
|
||||
|
||||
Rutas:
|
||||
|
||||
| Regla | Detalle |
|
||||
|---|---|
|
||||
| Lectura fuera de PROJECT_ROOT | `ALLOW_OUTSIDE_PROJECT_READ` |
|
||||
| Escritura fuera de PROJECT_ROOT | `ALLOW_OUTSIDE_PROJECT_WRITE` |
|
||||
| Escritura en `PROJECT_ROOT/tools/` | `ALLOW_SELF_TOOL_CREATION` |
|
||||
| Symlinks | Se evalúa el destino real: un enlace del proyecto hacia `/etc` cuenta como `/etc` |
|
||||
| Protegidos siempre | `agent.md`, `init.conf` (tampoco se puede **leer**: contiene la API key), `agent.log`, `contexto.mem`, `.context-history` (tampoco se puede leer) y cualquier `*.md` de la raíz salvo `memoria.md`. El agente no puede editar sus instrucciones ni crearse tareas nuevas |
|
||||
| Nunca borrables | `/`, `/etc`, `/usr`, `/var`, `/home`, `/boot`... PROJECT_ROOT, `.agent/`, y comodines sobre ellos (`rm -rf /etc/*`) |
|
||||
|
||||
Cuando algo se deniega, el modelo recibe `denied by policy: <motivo>` y se le
|
||||
indica que no intente rodearlo.
|
||||
|
||||
> **Importante:** el control de política es una barrera contra errores del
|
||||
> modelo, no un sandbox frente a un adversario. El análisis de shell es
|
||||
> aproximado y los programas `unknown` (en `privileged`) pueden hacer cualquier
|
||||
> cosa que permita el usuario del sistema. Para aislamiento real, ejecute el
|
||||
> agente con un usuario sin privilegios, en un contenedor o una VM.
|
||||
|
||||
## Tool calling
|
||||
|
||||
- `TOOL_CALLING=auto` (defecto): se envían las herramientas con el campo `tools`
|
||||
de la API. Si el servidor responde con un error que indica que no lo soporta,
|
||||
el agente cambia al protocolo de texto para el resto de la sesión.
|
||||
- Protocolo de texto (`json`): el prompt de sistema describe las herramientas y el
|
||||
modelo responde con bloques:
|
||||
|
||||
````text
|
||||
```tool
|
||||
{"tool": "exec", "arguments": {"command": "systemctl", "args": ["status", "nginx"]}}
|
||||
```
|
||||
````
|
||||
|
||||
También se aceptan `<tool_call>...</tool_call>` (plantillas Hermes/Qwen), incluso
|
||||
en modo nativo, porque muchos modelos locales escriben así la llamada. Un bloque
|
||||
mal formado se devuelve al modelo como error; nunca se ejecuta texto libre.
|
||||
|
||||
## Ciclo del agente
|
||||
|
||||
Para cada tarea (o mensaje del CLI): el modelo razona y pide herramientas → se
|
||||
validan y ejecutan en orden → los resultados vuelven al modelo → se repite hasta
|
||||
que responde sin pedir herramientas.
|
||||
|
||||
- Límite `MAX_STEPS` por tarea/mensaje.
|
||||
- Detección de bucles: la misma llamada con el mismo resultado 3 veces genera un
|
||||
aviso al modelo; 5 veces detiene la tarea como `BLOCKED`.
|
||||
- En modo tareas, la respuesta final debe terminar en `TASK_STATUS: DONE|BLOCKED|FAILED`
|
||||
(se pide una vez si falta). Una tarea `BLOCKED`/`FAILED` no detiene las
|
||||
siguientes; un error del modelo o Ctrl+C sí.
|
||||
- Contexto: ver [Compresión de contexto](#compresión-de-contexto).
|
||||
|
||||
## Investigación en Internet
|
||||
|
||||
Requiere `ALLOW_NETWORK=true` y `ALLOW_WEB_RESEARCH=true`.
|
||||
|
||||
- `web_search` usa por defecto el HTML de DuckDuckGo. Los buscadores públicos
|
||||
pueden limitar las consultas automáticas; para uso intensivo, configure una
|
||||
instancia SearXNG propia en `init.conf`:
|
||||
`SEARCH_URL=https://searx.example.com/search?q={query}&format=json`.
|
||||
- `web_fetch` elimina scripts, estilos y navegación, conserva párrafos, listas y
|
||||
tablas, y numera los enlaces (`texto [3]` + lista `LINKS`) para poder
|
||||
seguirlos. Máx. 5 MiB por página; el modelo recibe ~12 KB y el texto completo
|
||||
se guarda en `.agent/tmp/`.
|
||||
- `cve_lookup` consulta la API 2.0 de NVD (las entradas con rango de versiones
|
||||
aparecen primero) y la página del CVE en el Debian Security Tracker, que da la
|
||||
versión corregida por cada release de Debian. Siempre recuerda comprobar la
|
||||
versión instalada antes de concluir.
|
||||
- Todo el contenido web llega marcado como `UNTRUSTED WEB CONTENT`: el modelo lo
|
||||
trata como dato, nunca como instrucciones (§59).
|
||||
- El prompt de sistema indica el orden de fuentes (§38): documentación oficial,
|
||||
avisos del fabricante, repositorios oficiales, NVD, tracker de la
|
||||
distribución, código upstream, issues oficiales y, por último, fuentes
|
||||
secundarias.
|
||||
|
||||
Cliente HTTP único (§67): pool de conexiones, TLS ≥ 1.2, `User-Agent:
|
||||
sysagent/<versión>`, timeouts de conexión/TLS/cabeceras de 15–30 s, máx. 10
|
||||
redirecciones solo a http(s), y respeta `HTTPS_PROXY`/`NO_PROXY`.
|
||||
|
||||
## Descargas
|
||||
|
||||
Requiere `ALLOW_NETWORK=true` y `ALLOW_DOWNLOADS=true`.
|
||||
|
||||
- Destino por defecto `.agent/downloads/<nombre>`; otro destino pasa por la
|
||||
política de rutas (fuera del proyecto requiere `ALLOW_OUTSIDE_PROJECT_WRITE`).
|
||||
- Límite `MAX_DOWNLOAD_SIZE` (agent.md, por defecto 1 GiB; admite `512M`, `2G`).
|
||||
- Verificación: `sha256`, `sha512` o `checksum_url` (archivos `SHA256SUMS`,
|
||||
`*.sha256`, formato GNU o BSD). Se descarga a un temporal calculando el hash;
|
||||
**solo** si tamaño y checksum son correctos se mueve al destino. Un
|
||||
`CHECKSUM MISMATCH` descarta el archivo.
|
||||
- Sin checksum, la descarga se permite pero el resultado indica
|
||||
`NOT VERIFIED` y muestra el SHA-256 calculado.
|
||||
|
||||
## Parches
|
||||
|
||||
`internal/patch` aplica y genera diffs unificados en Go, así que no necesita
|
||||
`patch` ni `git` (una instalación mínima de Debian no trae `patch`).
|
||||
|
||||
- `apply_patch` acepta `diff -u` y `git diff` (creación y borrado de archivos,
|
||||
`\ No newline at end of file`, desplazamientos de posición). No hay "fuzz": el
|
||||
contexto debe coincidir exactamente.
|
||||
- **Todo o nada**: todos los hunks de todos los archivos se calculan en memoria;
|
||||
si uno falla, no se escribe nada. `check_only=true` solo comprueba.
|
||||
- Antes de escribir se guarda en `.agent/patches/<fecha>/` el parche aplicado y
|
||||
las copias originales (`orig/…`); los archivos fuera del proyecto también
|
||||
reciben su backup `.sysagent.<fecha>.bak`. El resultado incluye los pasos de
|
||||
rollback.
|
||||
- Cada archivo pasa por la política (no se puede parchear `agent.md`, rutas con
|
||||
`..` se rechazan, borrar requiere `ALLOW_DELETE`).
|
||||
- `diff_files` documenta exactamente un cambio (p.ej. backup frente a actual) y
|
||||
puede guardarlo como `.agent/patches/<nombre>.patch`.
|
||||
|
||||
Flujo de parche propio (§40), indicado al modelo en el prompt: código fuente
|
||||
legítimo → causa → parche mínimo → `apply_patch` (con `check_only` antes) →
|
||||
compilar → pruebas → comparar antes/después → conservar el original →
|
||||
documentar. Siempre se prefiere el parche oficial.
|
||||
|
||||
## Herramientas propias
|
||||
|
||||
Con `ALLOW_SELF_TOOL_CREATION=true` el agente puede escribir en
|
||||
`PROJECT_ROOT/tools/`. Orden de preferencia (§42): herramienta instalada →
|
||||
paquete de la distribución → upstream confiable → script propio → programa
|
||||
propio. `find_program` comprueba qué existe antes de asumirlo; `register_tool`
|
||||
documenta la herramienta en `memoria.md` (sección `Herramientas`: ruta,
|
||||
descripción, uso, lenguaje) para reutilizarla. Las herramientas no se borran al
|
||||
terminar.
|
||||
|
||||
## Contraseñas y secretos
|
||||
|
||||
Si una tarea necesita una contraseña que no está disponible (§80), en `--cli` el
|
||||
modelo llama a `ask_secret`. El operador la escribe **sin eco**; el valor queda
|
||||
solo en memoria durante la sesión y el modelo recibe únicamente el marcador
|
||||
`{{secret:nombre}}`, que usa en sus llamadas:
|
||||
|
||||
- `exec`: el marcador se sustituye en los argumentos al ejecutar.
|
||||
- `shell`: el valor viaja como variable de entorno (`SYSAGENT_SECRET_nombre`) y
|
||||
el marcador se convierte en una referencia a ella adaptada al contexto de
|
||||
comillas (sin comillas, `"..."`, `'...'`, `$(...)`, `` `...` ``). Un valor con
|
||||
comillas o `;` nunca se interpreta como código.
|
||||
- `write_file`/`edit_file`/`append_file`: el valor real se escribe en el archivo.
|
||||
|
||||
El valor se enmascara (vuelve a aparecer como marcador) en todas las salidas,
|
||||
`agent.log`, `contexto.mem`, `memoria.md` y el historial; nunca se envía al
|
||||
modelo. En modo tareas no hay operador: `ask_secret` falla y el agente debe
|
||||
informar del bloqueo. `/secrets` lista los nombres.
|
||||
|
||||
> Los argumentos de un comando son visibles para otros usuarios locales en la
|
||||
> lista de procesos. Para contraseñas, es preferible `shell` (variable de
|
||||
> entorno) o un archivo con permisos restrictivos.
|
||||
|
||||
La lectura sin eco usa `ioctl(TCGETS/TCSETS)` y siempre restaura el terminal,
|
||||
también con Ctrl+C. Si la entrada no es un terminal (tubería), se lee sin más;
|
||||
si es un terminal en el que no se puede desactivar el eco, se rechaza.
|
||||
|
||||
## Tareas
|
||||
|
||||
En modo normal se cargan los `*.md` ubicados **directamente** en PROJECT_ROOT,
|
||||
excepto `agent.md`, `memoria.md` y `README.md` (sin distinguir mayúsculas), en
|
||||
orden lexicográfico (o `TASK_ORDER`). No hay búsqueda recursiva, y los symlinks
|
||||
que apuntan fuera del proyecto se ignoran con una advertencia: ningún archivo
|
||||
externo puede convertirse en instrucción.
|
||||
|
||||
Las tareas completadas (`DONE`) se registran en `contexto.mem` con el hash de
|
||||
su contenido; en la siguiente ejecución **no se repiten** si el archivo no
|
||||
cambió. Para repetirlas: `sysagent --rerun`. Las `BLOCKED`/`FAILED` se
|
||||
reintentan siempre.
|
||||
|
||||
## memoria.md
|
||||
|
||||
Conocimiento persistente descubierto durante las tareas (§21): IPs y roles de
|
||||
hosts, puertos, rutas de configuración, forma correcta de reiniciar un
|
||||
servicio, distribución y kernel, dependencias, herramientas creadas,
|
||||
decisiones. No es un log (eso es `contexto.mem`).
|
||||
|
||||
- Se crea con permisos `0600` si no existe; se reescribe siempre con `0600` de
|
||||
forma atómica. Si ya existía con permisos más abiertos se emite una advertencia.
|
||||
- El modelo la actualiza con la herramienta `remember`
|
||||
(`section`, `entry`, `facts`) y elimina datos obsoletos con `forget`:
|
||||
|
||||
```markdown
|
||||
## Hosts
|
||||
|
||||
### db01
|
||||
|
||||
- IP: 10.10.10.15
|
||||
- Servicio: PostgreSQL
|
||||
- Puerto: 5432
|
||||
```
|
||||
|
||||
- **Sin duplicados** (§82): un hecho `Etiqueta: valor` actualiza la línea con la
|
||||
misma etiqueta (`IP: ...` cambia la IP existente); un hecho idéntico no se
|
||||
añade de nuevo. `replace=true` reescribe la entrada.
|
||||
- El contenido escrito a mano por el operador (texto libre, otras secciones,
|
||||
bloques de código) se conserva literalmente.
|
||||
- Credenciales (§23): un hecho que parece contraseña, token o clave se rechaza
|
||||
salvo `sensitive=true`; el modelo debe preferir referencias
|
||||
(`Password: secret-tool lookup service postgres-admin`). La API key nunca se
|
||||
guarda (se sustituye por su versión enmascarada).
|
||||
- Tras cada cambio, el prompt de sistema se regenera con la memoria nueva.
|
||||
- `/memory` en el CLI la muestra releyéndola del disco.
|
||||
|
||||
## contexto.mem
|
||||
|
||||
Histórico operativo (§17), en JSON, con permisos `0600` y escritura atómica.
|
||||
Se guarda tras cada mensaje/tarea, en cada cambio de estado de tarea, tras cada
|
||||
compactación y al salir (también con Ctrl+C/SIGTERM).
|
||||
|
||||
| Campo | Contenido |
|
||||
|---|---|
|
||||
| `summary` | Contexto histórico comprimido con las 14 secciones de §19 |
|
||||
| `compactions` | Número de compactaciones realizadas |
|
||||
| `journal` | Diario registrado **por el programa** (no por el modelo): inicio/fin de sesión, herramientas ejecutadas con resultado, cambios de archivos con sus backups y motivo, denegaciones, errores, estados de tareas, respuestas finales, compactaciones. Máx. 400 eventos; se descartan primero los ya resumidos |
|
||||
| `recent` | Últimos mensajes aún no resumidos (máx. 60, recortados a 4 KB cada uno) |
|
||||
| `tasks` | Estado, hash y fecha de cada tarea |
|
||||
|
||||
Al arrancar, el resumen se incluye en el prompt como `CONTEXTO HISTÓRICO` y los
|
||||
mensajes recientes como `ÚLTIMOS MENSAJES DE LA SESIÓN ANTERIOR` (en texto, para
|
||||
no depender del protocolo de herramientas de la sesión anterior). Si no caben
|
||||
en ~20 % del contexto, se compactan en la primera petición.
|
||||
|
||||
Si el archivo no es JSON (p.ej. notas escritas a mano) su texto se importa como
|
||||
resumen. Si es JSON corrupto se renombra a `contexto.mem.corrupt-<fecha>` y se
|
||||
empieza de cero con una advertencia; nunca se pierde en silencio.
|
||||
|
||||
## Compresión de contexto
|
||||
|
||||
El uso de contexto se **estima** (~3 bytes/token, más el esquema de
|
||||
herramientas en modo nativo) y se **calibra** con los `prompt_tokens` que
|
||||
reporta el servidor tras cada petición. Nunca se presenta como exacto (§84).
|
||||
|
||||
1. Al **80 %** del contexto (§18) el historial se divide en una parte antigua y
|
||||
una reciente (~25 % del contexto). El corte nunca separa una llamada a
|
||||
herramienta de su resultado.
|
||||
2. La parte antigua, el resumen previo, el diario sin resumir y los mensajes de
|
||||
sesiones anteriores se envían al modelo con un prompt dedicado (§83) que exige
|
||||
conservar rutas, IPs, puertos, versiones, decisiones, errores sin resolver,
|
||||
cambios con sus backups y comandos exactos, y no inventar nada. Si no cabe en
|
||||
una petición, se resume por partes encadenando el resumen parcial.
|
||||
3. La salida se valida (deben estar las secciones `OBJECTIVE`, `CURRENT_STATE`,
|
||||
`COMPLETED_TASKS`, `PENDING_TASKS`, `IMPORTANT_FACTS`, `SYSTEM_STATE`,
|
||||
`FILES_CHANGED`, `COMMANDS_OF_IMPORTANCE`, `ERRORS`, `DECISIONS`,
|
||||
`CREDENTIAL_REFERENCES`, `HOSTS`, `SERVICES`, `LESSONS_LEARNED`; se toleran
|
||||
3 ausentes) y sustituye a la parte antigua. Si la parte reciente no empieza
|
||||
por un mensaje del usuario, se antepone uno que repite la tarea en curso.
|
||||
4. Si la compresión falla o no basta (≥ 95 %), se sustituyen salidas antiguas de
|
||||
herramientas por un marcador.
|
||||
5. Si el servidor rechaza una petición por exceso de contexto (p.ej. con
|
||||
`CONTEXT_SIZE` desconocido), se compacta y se reintenta una vez.
|
||||
|
||||
En el CLI, `/compact` fuerza una compactación y `/context summary` muestra el
|
||||
resumen.
|
||||
|
||||
## Modo --cli
|
||||
|
||||
```text
|
||||
$ cd /root/admin-agent
|
||||
$ sysagent --cli
|
||||
System Agent
|
||||
Model: qwen3.5
|
||||
Mode: privileged
|
||||
Project: /root/admin-agent
|
||||
|
||||
agent> revisa por qué nginx no inicia
|
||||
```
|
||||
|
||||
Carga `init.conf`, `agent.md`, `memoria.md` y `contexto.mem`, e ignora los
|
||||
archivos de tareas.
|
||||
|
||||
| Comando | Descripción |
|
||||
|---|---|
|
||||
| `/help` | Ayuda |
|
||||
| `/status` | Modelo, API (clave enmascarada), modo, uso de contexto, root |
|
||||
| `/context` | Uso de contexto, resumen histórico, diario; `/context summary` imprime el resumen |
|
||||
| `/memory` | Muestra `memoria.md` |
|
||||
| `/policy` | Política efectiva |
|
||||
| `/history` | Conversación de la sesión |
|
||||
| `/journal [n]` | Últimos n eventos del diario de `contexto.mem` (20 por defecto) |
|
||||
| `/compact` | Compacta ya la conversación antigua en el resumen histórico |
|
||||
| `/secrets` | Nombres de los secretos introducidos en la sesión (nunca los valores) |
|
||||
| `/clear` | Vacía la conversación reciente (se conservan resumen, memoria y diario) |
|
||||
| `/quit`, `/exit`, Ctrl+D | Salir |
|
||||
|
||||
Durante un mensaje, la consola muestra cada herramienta (`→ exec: ...`) y su
|
||||
resultado (`← ok`, `← error`, `← denied`). Ctrl+C cancela el mensaje en curso
|
||||
(petición al modelo y procesos hijos); en el prompt, sale.
|
||||
El uso de contexto se muestra como *reported by server* cuando el servidor
|
||||
devuelve `usage`, o *estimated* (estimación conservadora de ~3 bytes/token).
|
||||
No hay edición de línea ni historial con flechas; se puede usar
|
||||
`rlwrap sysagent --cli`.
|
||||
|
||||
## Otras opciones
|
||||
|
||||
```text
|
||||
--version muestra la versión
|
||||
--verbose muestra logs DEBUG/INFO en consola (y DEBUG en agent.log)
|
||||
--rerun repite tareas que contexto.mem registra como DONE
|
||||
--dry-run las herramientas que modifican el sistema solo describen lo que harían;
|
||||
los comandos clasificados como solo lectura/red sí se ejecutan (diagnóstico)
|
||||
```
|
||||
|
||||
## Ejemplos
|
||||
|
||||
```bash
|
||||
# Modo normal: procesa 01-*.md, 02-*.md...
|
||||
cd /root/admin-agent && sysagent
|
||||
|
||||
# Interactivo
|
||||
cd /root/admin-agent && sysagent --cli
|
||||
```
|
||||
|
||||
Con llama.cpp local:
|
||||
|
||||
```bash
|
||||
llama-server -m qwen3-8b.gguf -c 32768 --port 8080
|
||||
# init.conf: API_URL=http://127.0.0.1:8080/v1 MODEL=qwen3 CONTEXT_SIZE=0
|
||||
```
|
||||
|
||||
## Permisos y archivos generados
|
||||
|
||||
| Ruta | Permisos | Contenido |
|
||||
|---|---|---|
|
||||
| `agent.log` | 0600 | Auditoría: timestamps RFC3339, acciones, resultados |
|
||||
| `memoria.md` | 0600 | Conocimiento persistente |
|
||||
| `contexto.mem` | 0600 | Resumen histórico, diario operativo, estado de tareas |
|
||||
| `.agent/{tmp,downloads,patches,logs}` | 0700 | `tmp/`: salidas y páginas extensas; `downloads/`: descargas; `patches/`: parches aplicados, originales y diffs |
|
||||
| `*.sysagent.YYYYMMDD-HHMMSS.bak` | los del original | Backups junto al archivo modificado |
|
||||
| `.context-history` | 0600 | Entradas del operador en el CLI con fecha (§79); se omiten líneas que parecen credenciales y nunca incluye lo tecleado en `ask_secret` |
|
||||
| `.agent/lock` | 0600 | Bloqueo de instancia única (PID) |
|
||||
|
||||
Se recomienda `chmod 600 init.conf`. El programa detecta UID/EUID y lo
|
||||
informa (`Running as root`) pero no asume privilegios.
|
||||
|
||||
## Seguridad
|
||||
|
||||
- **Aislamiento de instrucciones**: solo `agent.md`, las tareas de PROJECT_ROOT y
|
||||
el operador del CLI dan instrucciones. Todo lo demás (archivos externos,
|
||||
salidas de comandos, páginas web) es dato no confiable y se marca como tal.
|
||||
- **Jerarquía de autoridad** incluida en el prompt de sistema: reglas internas >
|
||||
agent.md > operador CLI > tareas > memoria.md > contexto.mem > sistema > Internet.
|
||||
- **Fail closed**: capacidades no declaradas = denegadas; errores de
|
||||
configuración o de política impiden arrancar.
|
||||
- **El agente no puede cambiar sus propias reglas**: `agent.md`, `init.conf`,
|
||||
`agent.log`, `contexto.mem`, `.context-history` y los `*.md` de la raíz están
|
||||
protegidos frente a sus herramientas (ver [Aplicación de la política](#aplicación-de-la-política)).
|
||||
- **Revisión al arrancar**: se niega a arrancar si PROJECT_ROOT, `agent.md`,
|
||||
`init.conf` o un archivo de tarea son escribibles por cualquier usuario, o si
|
||||
PROJECT_ROOT es un directorio compartido tipo `/tmp` (cualquiera podría dictar
|
||||
lo que hace un agente que corre como root). Advierte si son escribibles por el
|
||||
grupo, si pertenecen a otro usuario mientras el agente corre como root, o si
|
||||
`init.conf` con `API_KEY` es legible por otros.
|
||||
- **Instancia única** por PROJECT_ROOT (`flock` en `.agent/lock`): dos agentes
|
||||
a la vez corromperían `contexto.mem` y duplicarían acciones.
|
||||
- **Límites**: `agent.md` y cada tarea ≤ 1 MiB; argumentos de herramienta ≤ 40 MiB;
|
||||
respuestas del modelo ≤ 64 MiB; páginas web ≤ 5 MiB; descargas ≤
|
||||
`MAX_DOWNLOAD_SIZE`; escritura de archivos ≤ 32 MiB.
|
||||
- **Secretos**: la API key solo viaja en la cabecera HTTP; un redactor la
|
||||
sustituye por su versión enmascarada en logs, consola, `contexto.mem`,
|
||||
`memoria.md` y mensajes al modelo. Los secretos de `ask_secret` reciben el
|
||||
mismo trato (ver [Contraseñas y secretos](#contraseñas-y-secretos)).
|
||||
- **Robustez**: un error interno de una herramienta se devuelve como error al
|
||||
modelo en lugar de terminar el proceso; SIGINT/SIGTERM cancelan procesos hijos
|
||||
y guardan el estado (un segundo Ctrl+C fuerza la salida).
|
||||
- **Registro de cambios** (§88): cada modificación de un archivo existente
|
||||
guarda en `agent.log` y en el diario de `contexto.mem` el diff exacto (valor
|
||||
anterior → nuevo), el motivo, la fecha y la ruta del backup.
|
||||
|
||||
> El control de política es una barrera contra errores del modelo, no un
|
||||
> sandbox frente a un adversario. Para aislamiento real, ejecute el agente con
|
||||
> un usuario dedicado sin privilegios (con `sudo` restringido si hace falta), en
|
||||
> un contenedor o en una VM.
|
||||
|
||||
### Reintentos y privilegios
|
||||
|
||||
- Errores transitorios que garantizan que el comando no hizo nada se reintentan
|
||||
con backoff (§54): bloqueo de apt/dpkg (3 reintentos: 5 s, 15 s, 30 s) y fallos
|
||||
temporales de DNS (2 reintentos: 2 s, 5 s). Los errores permanentes no se
|
||||
reintentan. El resultado indica cada reintento.
|
||||
- Si un comando falla por permisos (§46) y el agente no es root, el resultado
|
||||
incluye qué mecanismos de escalada hay (`sudo`/`doas` y si funcionan sin
|
||||
contraseña, comprobado con `-n`; `su`). Si ya es root, se sugieren otras
|
||||
causas (sistema de archivos de solo lectura, SELinux/AppArmor, atributo
|
||||
inmutable, contenedor).
|
||||
|
||||
### Conflictos entre tareas
|
||||
|
||||
Las tareas se ejecutan en el orden de `TASK_ORDER` o lexicográfico (§61). El
|
||||
modelo conoce la lista de tareas de la ejecución y, si la actual contradice a
|
||||
una anterior, no deshace a ciegas: sigue el orden y lo indica con una línea
|
||||
`CONFLICT: ...`, que se registra en `agent.log` y en el diario.
|
||||
|
||||
## Ejecución periódica con systemd
|
||||
|
||||
`examples/sysagent.service` y `examples/sysagent.timer` ejecutan las tareas de
|
||||
un proyecto cada hora. Como las tareas `DONE` sin cambios no se repiten, el
|
||||
agente solo actúa cuando hay tareas nuevas, modificadas o pendientes:
|
||||
|
||||
```bash
|
||||
sudo cp examples/sysagent.service examples/sysagent.timer /etc/systemd/system/
|
||||
sudo systemctl edit sysagent.service # ajustar WorkingDirectory
|
||||
sudo systemctl daemon-reload && sudo systemctl enable --now sysagent.timer
|
||||
journalctl -u sysagent.service
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Síntoma | Causa / solución |
|
||||
|---|---|
|
||||
| `ERROR: agent.md not found in PROJECT_ROOT` | Ejecutar desde el directorio del proyecto; crear `agent.md` |
|
||||
| `init.conf not found in PROJECT_ROOT` | Copiar `init.conf.example` a `init.conf` |
|
||||
| `API_URL must be an http(s) URL` | Incluir el esquema: `http://host:puerto/v1` |
|
||||
| `multiple policy blocks` | Dejar un solo bloque ```` ```policy ```` en agent.md |
|
||||
| `could not query model server` | Aviso: el servidor no expone `/models`; no impide arrancar |
|
||||
| `connection refused` en cada petición | El servidor de inferencia no está en marcha o la URL es incorrecta. Hay 3 reintentos con backoff (1s, 2s, 4s) |
|
||||
| `HTTP 404` en `/chat/completions` | `API_URL` debe terminar en el prefijo de la API (normalmente `/v1`) |
|
||||
| Uso de contexto `UNKNOWN` | Definir `CONTEXT_SIZE` en init.conf |
|
||||
| Respuestas con `<think>` | Se eliminan automáticamente del contenido mostrado |
|
||||
| `denied by policy: ...` | La política de agent.md no lo permite; ajustar el bloque ```` ```policy ```` si procede |
|
||||
| El modelo no usa herramientas o escribe JSON suelto | Probar `TOOL_CALLING=json`; en llama.cpp, iniciar `llama-server` con `--jinja` para tool calling nativo |
|
||||
| `malformed tool call` | El modelo generó JSON inválido; se le devuelve el error y suele corregirse |
|
||||
| `loop detected` | El modelo repitió la misma acción; revisar la tarea o usar un modelo más capaz |
|
||||
| `MAX_STEPS=... reached` | Aumentar `MAX_STEPS` en agent.md o dividir la tarea |
|
||||
| `context compaction failed: ... missing sections` | El modelo no siguió el formato del resumen; se recortan salidas en su lugar. Un modelo más capaz o más contexto lo evita |
|
||||
| Una tarea no se ejecuta (`DONE, skipped`) | Ya se completó y no cambió; usar `--rerun` o editar el archivo |
|
||||
| `contexto.mem could not be parsed` | Se movió a `contexto.mem.corrupt-*`; revisarlo y, si procede, copiar su resumen a mano |
|
||||
| `insecure project: ... writable by any user` | Corregir permisos: `chmod o-w agent.md init.conf *.md` y del directorio |
|
||||
| `another sysagent instance is running` | Otra ejecución usa el proyecto (PID indicado); esperar o detenerla. El bloqueo se libera solo si el proceso muere |
|
||||
| `ask_secret ... no interactive operator` | Las contraseñas solo pueden pedirse en `--cli`; en modo tareas use un almacén (p.ej. `secret-tool`) y guarde en memoria.md dónde está |
|
||||
| `unknown secret(s)` | El modelo usó un marcador que no se pidió en esta sesión (los secretos no se persisten) |
|
||||
| Reiniciar el histórico | Borrar (o mover) `contexto.mem`; `memoria.md` es independiente |
|
||||
| `search provider returned no parseable results` | El buscador limita consultas automáticas; configurar `SEARCH_URL` (SearXNG) |
|
||||
| `NVD: HTTP 403/429` | NVD limita clientes anónimos; reintentar más tarde (Debian tracker sigue funcionando) |
|
||||
| `CHECKSUM MISMATCH` | El archivo no coincide con el checksum: se descartó. Verificar la URL y la fuente del checksum |
|
||||
| `hunk N does not apply: context not found` | El archivo no coincide con el que se usó para crear el parche; revisar `base_dir`/`strip` o regenerar el parche |
|
||||
|
||||
## Estado de implementación
|
||||
|
||||
| Fase | Contenido | Estado |
|
||||
|---|---|---|
|
||||
| 1 | init.conf, agent.md, política, tareas, cliente LLM, `--cli` | hecho |
|
||||
| 2 | ejecución de comandos, herramientas de archivos, tool calling, aplicación de política, `--dry-run` | hecho |
|
||||
| 3 | contexto.mem, memoria.md automática (remember/forget), compresión de contexto | hecho |
|
||||
| 4 | investigación web, CVE, descargas verificadas, parches, herramientas propias | hecho |
|
||||
| 5 | endurecimiento, secretos sin eco, reintentos, registro de cambios, conflictos, systemd, documentación | hecho |
|
||||
|
||||
Pruebas: `go test ./...` cubre, entre otros, el parser de `init.conf`, el de la
|
||||
política, el descubrimiento de tareas, la validación de rutas y comandos, la
|
||||
compresión de contexto, la memoria, el parser de tool calls, las herramientas
|
||||
(con servidores HTTP locales) y el ciclo completo del agente con un modelo
|
||||
simulado. Los comandos de prueba son inofensivos (`printf`, `echo`, `true`,
|
||||
`false`) y todo ocurre en directorios temporales (§73, §74).
|
||||
|
||||
## Limitaciones conocidas
|
||||
|
||||
- **No es un sandbox.** La clasificación de comandos es un catálogo y el análisis
|
||||
de shell es aproximado; los programas no catalogados (`unknown`, incluidos
|
||||
`python`, `awk` y las herramientas de `tools/`) se bloquean en `guarded` pero se
|
||||
permiten en `privileged`.
|
||||
- La calidad del resumen de contexto depende del modelo: se valida su
|
||||
estructura, no su exactitud. El diario del programa (datos exactos) se
|
||||
conserva aparte en `contexto.mem` hasta 400 eventos.
|
||||
- `web_fetch` no ejecuta JavaScript; los buscadores públicos pueden bloquear
|
||||
consultas automáticas (use `SEARCH_URL` con SearXNG); NVD limita clientes
|
||||
anónimos.
|
||||
- `apply_patch` no admite parches binarios ni renombrados de `git`.
|
||||
- Los secretos de `ask_secret` duran solo la sesión y solo existen en `--cli`.
|
||||
- El CLI no tiene edición de línea ni historial con flechas: use
|
||||
`rlwrap sysagent --cli`.
|
||||
- El recuento de tokens es siempre una estimación (calibrada con el servidor
|
||||
cuando este informa `usage`).
|
||||
- Solo Linux (amd64, arm64). Distribuciones priorizadas: Debian y Ubuntu; la
|
||||
detección de gestor de paquetes cubre apt, dnf/yum, pacman, zypper y apk.
|
||||
|
||||
Reference in New Issue
Block a user