# Guía de desarrollo [简体中文](./development.md) | [English](./development_en.md) | [日本語](./development_ja.md) | [한국어](./development_ko.md) | [Русский](./development_ru.md) | Español | [Português (Brasil)](./development_pt.md) Esta guía es para quienes quieren ejecutar AnyModelBot desde el código fuente, empaquetarlo o contribuir. Si solo quieres usar la app, descarga un instalador desde [Releases](https://github.com/modelflare-ai/AnyModelBot/releases); consulta el [README](../README_es.md#-descargar-y-ejecutar). - Un bot es una personalidad, un modelo predeterminado y su memoria. Las cuentas son globales (Ajustes › Canales de chat) y las comparten todos los bots, con tantas por servicio como quieras. Está pensado para el uso personal de una sola persona. - Los modelos vienen de una cuenta de Modelflare o de tus propias claves de API oficiales. [Modelflare](https://modelflare.dev/?utm_source=anymodelbot&utm_campaign=amb_readme): en el primer inicio inicias sesión en una cuenta de Modelflare (o te registras) y los modelos aparecen automáticamente. Las solicitudes pasan por el gateway [magpie](https://github.com/yetone/magpie) incluido. - Tres formas de ejecutarlo, una sola base de código: **app de escritorio (Electron)**, **línea de comandos `npm start`**, **Docker**. ## Estructura del repositorio ``` src/ core/ tipos de mensaje compartidos, cola de sesiones y deduplicación, runtime (/model /reset, streaming), el aviso tras un reinicio, admisión (propietario + lista de permitidos), bandeja de salida persistente delivery/ entrega de mensajes proactivos: horario de silencio, límites diarios, resúmenes, la ventana de 24 horas de WeChat y recordatorios de renovación agent/ modo agente: host y puente de DSH, aprobaciones, perfiles de permisos, memoria y gestor de contexto, directorios de sesión de DSH prompts/ todos los prompts que lee el modelo del agente: personalidad, reglas de chat, guía de memoria, el primer mensaje de un segmento, habilidades tools/ capacidades que usa el agente: búsqueda web (search.ts), generación de imágenes / video (media/), uso de la computadora (computer-use/), Windows PowerShell (pwsh/), Conectar apps — conectores de apps integrados y MCP personalizado (apps/), cuentas existentes de Notion / Feishu (accounts/) llm/ cliente compatible con OpenAI (interfaz de acceso a modelos) magpie/ supervisor de magpie: directorios aislados, puerto libre, secreto generado, comprobaciones de estado, reinicio tras fallos, salida limpia channels/ feishu/ (conexión persistente + CardKit) weixin/ (iLink; aviso MIT en LICENSE.openclaw-weixin) messenger/ (Telegram, Slack, Discord, con una ruta común de envío y recepción) tasks/ tareas en segundo plano y reglas: ejecuciones, etapas, costo, retención y limpieza, programaciones botkit/ framework de bots del catálogo: formato de paquete, comprobaciones, empaquetado y firma, catálogo, instalación, rutas de UI estática (ver docs/bot-framework_en.md) skills/ habilidades que instalan los propios usuarios (paquetes ZIP) store/ SQLite (node:sqlite, sin compilación nativa) domain/ Estructuras de datos y reglas puras: ajustes del bot, horario de silencio, programaciones, registros de bots del catálogo, pasos del agente; sin E/S, solo depende de i18n. store solo depende de esto (test/architecture.unit.test.ts comprueba la dirección de las dependencias) account/ cuenta de Modelflare: inicio de sesión PKCE por loopback, rotación de tokens, saldo y recordatorios, claves de presupuesto por bot, capacidades de los modelos y precios de referencia web/ servidor de la consola: server.ts arranca, autentica y despacha; routes/ contiene los endpoints por área (API JSON + chat en streaming por SSE); http.ts tiene las utilidades compartidas cli/ asistente de primer inicio init, weixin-login providers/ de dónde vienen los modelos: modelflare.ts (rutas, validación de claves GET /v1/models, enrutamiento de modelos y atribución), modelflare-domains.ts (prueba de velocidad de rutas), official.ts (tabla de proveedores de API oficiales, direcciones fijas) updates/ actualizaciones: check.ts (comprobación de nuevas versiones), desktop.ts (descarga y reinicio en escritorio) guide.ts progreso de la lista de primeros pasos i18n/ i18n del servidor: errores, textos de estado, menú de la bandeja, mensajes del sistema de los bots (messages/.ts) diagnostics.ts solución de problemas: sondeos, final del registro, informe de diagnóstico con datos sensibles ocultos version.ts versión de la app (package.json) main.ts punto de entrada de la línea de comandos electron/ proceso principal de escritorio (lanzador + bandeja del sistema) ui/ UI de la consola (Preact + htm, sin paso de compilación) design/ sistema de diseño: tokens/, base/, components/, personajes de los bots bots/ locales/ textos de la UI: zh-CN/ y en/, un archivo por namespace css/ lib/ components/ screens/ assets/ main.js dsh-runtime/ manifiesto de dependencias del runtime del agente DSH y plugins de AnyModelBot (plugins/) scripts/ setup / build-magpie / fetch-cua-driver / prepare-dsh / bundle-dsh / electron-smoke / check-readme y más vendor/ submódulos de git: magpie (se compila durante el build), deepseek-harness, kimi-computer-use site/ sitio web (sitio estático con Astro); ver site/README.md docs/ guía de desarrollo, notas de privacidad, recursos del README (docs/assets/) ``` ## Ejecutar desde el código fuente (línea de comandos) Requiere Node.js 22.13+ y Go 1.26.8+ (para compilar magpie desde el código fuente). ```bash npm install npm run setup # obtiene el submódulo vendor/magpie y lo compila en build/bin/ npm run init # opcional: configuración inicial en la terminal (iniciar sesión en Modelflare, credenciales de Feishu) npm start # inicia todo: gateway + bots + consola en http://127.0.0.1:3600/ ``` Los datos se guardan en `./data` de forma predeterminada (cámbialo con `AMB_DATA_DIR`). Las demás variables de entorno están en `.env.example`. ## App de escritorio (Electron) ```bash npm run electron:dev # ejecutar en modo de desarrollo npm run electron:smoke # prueba de humo (usa xvfb-run automáticamente en Linux sin pantalla) npm run dist # empaquetar para la plataforma actual (magpie se incluye en resources/magpie/) npm run dist:linux | dist:mac | dist:win npm run test:dsh-bundle # prueba de integración real de DSH en Electron con el build/dsh empaquetado (ejecuta antes prepare:dsh + bundle:dsh) ``` - Runtime del agente DSH: `scripts/prepare-dsh.mjs` instala `dsh-runtime/` para la plataforma de destino, y `scripts/bundle-dsh.mjs` lo empaqueta en `resources/dsh/dsh.asar` (todo el JavaScript, en un solo archivo) más `resources/dsh/node_modules/` (solo paquetes nativos, unos 55 archivos). El proceso principal de Electron y los procesos hijos con `ELECTRON_RUN_AS_NODE` leen el asar directamente, sin extraerlo en el primer inicio, así que el instalador solo descomprime unos cientos de archivos (en Windows, la extracción y el análisis antivirus de cada archivo son la causa principal de las instalaciones lentas). En desarrollo se sigue usando `dsh-runtime/` directamente. - PowerShell para los comandos del agente en Windows: dentro del sandbox, Windows PowerShell 5.1 tarda entre 20 y 30 segundos por comando y PowerShell 7, alrededor de 1 segundo. La app elige, en este orden: un PowerShell 7 funcional que ya esté en el sistema (se prueba una vez en segundo plano tal como lo ejecuta DSH; no se descarga nada) → el PowerShell 7 privado de la app → Windows PowerShell 5.1. Cuando no hay un 7 funcional y el modo agente está activado, la app descarga en segundo plano el zip oficial de PowerShell 7.6.9 (LTS) en `%LOCALAPPDATA%\AnyModelBot\pwsh\`: sin permisos de administrador, sin escribir en el registro, sin cambiar el PATH y sin tocar tu propio PowerShell. La versión y el SHA256 de los zip x64 / arm64 están fijados en `src/tools/pwsh/private.ts` (actualízalos con `node scripts/pwsh-hashes.mjs `). Tras la descarga se comprueba el hash, tras la extracción se comprueba la firma Authenticode de Microsoft, y solo se usa después de superar una ejecución de prueba al estilo de DSH (versión, leer/escribir un archivo temporal, iniciar un programa externo); las instalaciones fallidas o a medias nunca se usan y se limpian en el siguiente inicio. Durante la instalación, la pantalla de chat y Ajustes → General muestran el progreso, y también `/status`; los comandos siguen usando 5.1 hasta que esté listo, y los procesos del agente inactivos cambian en segundo plano sin reiniciar la app. Si el agente no puede iniciarlo en tiempo de ejecución (porque el antivirus lo eliminó o lo puso en cuarentena, o porque lo bloquea una política), la app vuelve a la opción anterior y explica por qué; Ajustes → Solución de problemas permite instalarlo, reinstalarlo o volver al predeterminado del sistema. En sistemas anteriores a Windows 10 1607 / Server 2016 no se descarga nada. La app de escritorio descarga a través del proxy del sistema (pila de red de Chromium); la versión de línea de comandos usa fetch de Node (las versiones recientes de Node respetan `HTTPS_PROXY` cuando `NODE_USE_ENV_PROXY=1`). `AMB_PWSH_MIRROR` cambia a un prefijo de espejo (el hash y la firma se siguen comprobando), y `AMB_PWSH_AUTO_INSTALL=0` desactiva la instalación automática. - Los comandos del agente tienen acceso a la red (el sandbox solo restringe la escritura) y usan tu proxy: `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` / `NO_PROXY` (y sus formas en minúsculas) y las variables de certificados e índices de paquetes (`SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, `NODE_EXTRA_CA_CERTS`, `PIP_INDEX_URL`, `npm_config_registry`, `GOPROXY`, …) se pasan desde el entorno de la app; cuando el entorno no define ningún proxy, la app de escritorio consulta el proxy del sistema (ajustes del sistema y PAC) cada vez que inicia el proceso del agente y lo pasa como `http://host:port` (ver `src/agent/proxy-env.ts`). Las direcciones de loopback (`127.0.0.1`, `localhost`, `::1`) siempre se agregan a `NO_PROXY` y nunca pasan por el proxy. - Carpeta de datos: `/data` (macOS `~/Library/Application Support/anymodel bot/data`, Windows `%APPDATA%\anymodel bot\data`, Linux `~/.config/anymodel bot/data`). Los registros están en `logs/app.log` dentro de la carpeta de datos. - Instancia única; de forma predeterminada, cerrar la ventana la oculta en la bandeja del sistema (en Ajustes → General puedes hacer que, en su lugar, la app se cierre). Menú de la bandeja: Abrir / Abrir en el navegador / Abrir al iniciar sesión (macOS, Windows) / Abrir carpeta de datos / Salir. - En macOS la ventana no tiene barra de título y los botones de semáforo están en la parte superior de la barra lateral; el ancho mínimo de la ventana es 860. - La consola solo escucha en 127.0.0.1 y genera un token de acceso aleatorio en cada inicio, para que otros programas locales no puedan llamar directamente a la API de administración. - Al salir, primero se detienen los canales y luego magpie. Si una ejecución anterior se cerró a la fuerza y dejó un proceso de magpie, el siguiente inicio lo limpia (Linux/macOS). Publicaciones: escribe la entrada de esta versión en `CHANGELOG.md`, `CHANGELOG_en.md`, `CHANGELOG_ja.md`, `CHANGELOG_ko.md`, `CHANGELOG_ru.md`, `CHANGELOG_es.md` y `CHANGELOG_pt.md` (cambios que notarán los usuarios, no el sitio web ni detalles internos; la página del sitio en inglés omite las líneas que mencionan WeChat o Feishu), actualiza `version` en `package.json`, haz commit y sube una etiqueta `v`. Sin esa entrada, el flujo de publicación falla al inicio. Con ella, se convierte en las notas de la GitHub Release y aparece en la página de registro de cambios del sitio. El diálogo Novedades de la app también la lee: `CHANGELOG*.md` se incluye en los instaladores y en la imagen de Docker (`src/changelog.ts`), y las notas de una versión aún no instalada se leen de la etiqueta de esa versión cuando el usuario las abre. `.github/workflows/release.yml` compila en cada uno de los seis runners de sistema operativo / arquitectura y publica los instaladores junto con `SHA256SUMS` en una GitHub Release. Los secretos del repositorio necesarios para la firma y la notarización se describen en el comentario al principio de ese archivo. La misma etiqueta también inicia `.github/workflows/docker.yml`, que publica la imagen x64 + arm64 en `ghcr.io/modelflare-ai/anymodel-bot` (con las etiquetas `` y `latest`). Con una etiqueta, los instaladores de macOS deben estar firmados con Developer ID y notarizados: después del empaquetado, el .app y el .app dentro del DMG se comprueban con `spctl` y herramientas similares, y si no pasan, la ejecución falla. Las compilaciones de publicación tienen las herramientas de desarrollo desactivadas; para un paquete de depuración usa `npm run dist:debug`, o marca `debug_build` al ejecutar Release manualmente (los nombres de archivo llevan `-debug-`, se rechaza en etiquetas y `scripts/check-build-flavor.mjs` verifica el resultado empaquetado). Cada publicación debe actualizar el sitio web, la documentación relacionada y los README en todos los idiomas. Ejecuta `npm --prefix site run sync` para sincronizar el contenido del sitio. Después de publicar la GitHub Release y la imagen de Docker, sigue las [instrucciones de despliegue del sitio web](../site/README.md) y confirma que las páginas `/download`, `/changelog` y `/bots` en producción muestran la versión, los enlaces de descarga y las descripciones correspondientes. La publicación solo está completa cuando el sitio web en producción también está actualizado. Las actualizaciones desde la app empiezan con la 0.1.29 y usan `electron-updater` solo en las apps empaquetadas de macOS, Windows y Linux AppImage. `ui.autoDownloadUpdates` es `true` de forma predeterminada y se controla en Ajustes → General. Las descargas solo generan una notificación; salir normalmente no instala actualizaciones. La instalación explícita comprueba los chats activos y las tareas en segundo plano antes de reiniciar. Los usuarios de la 0.1.28 o anteriores deben instalar manualmente, una sola vez, una versión con actualizador. GitHub compila los DMG de macOS más los ZIP de actualización y publica los metadatos `latest-${arch}` y los blockmaps de cada arquitectura. Las publicaciones solo se hacen públicas cuando se terminan de subir todos los archivos. La línea de comandos, Docker y las ejecuciones de desarrollo sin empaquetar se siguen actualizando manualmente. ## Docker Los usuarios ejecutan la imagen publicada: `docker run -d --name amb -p 3600:3600 -v amb-data:/data ghcr.io/modelflare-ai/anymodel-bot`. Para compilarla tú mismo desde el código fuente: ```bash git submodule update --init --depth 1 vendor/magpie docker build -t anymodel-bot . docker run -d --name amb -p 3600:3600 -v amb-data:/data anymodel-bot docker exec amb cat /data/ui-token # abre http://:3600/?token= ``` Dentro del contenedor la consola escucha en 0.0.0.0, por lo que el acceso con token es obligatorio. No expongas el puerto 3600 directamente a internet. El inicio de sesión de WeChat con código QR está en Ajustes › Canales de chat. ## Modelos (Modelflare) - El primer inicio muestra un solo botón, “Iniciar sesión o registrarse con Modelflare”: inicia sesión en el navegador del sistema (puedes registrarte en ese momento) y haz clic en Continue para autorizar. De vuelta en la app, se emite una clave de API dedicada para esta computadora y se conecta → elige un modelo predeterminado → empieza a chatear. Docker y las implementaciones remotas no pueden recibir la redirección del navegador: después de iniciar sesión, el navegador llega a una dirección `127.0.0.1` que no carga, y tienes que pegar ese enlace en la pantalla de primer inicio (`POST /amb/api/account/login/complete`, con las mismas comprobaciones de state y PKCE). En las instalaciones antiguas que ingresaron una clave de API sin iniciar sesión, la clave se elimina del dispositivo al actualizar y hay que iniciar sesión. La página de retorno en el navegador muestra primero “Completando el inicio de sesión” y solo dice “Sesión iniciada” después de que el intercambio del token, la lectura de la cuenta, la clave del dispositivo y la conexión con el gateway hayan salido bien (si no, muestra el motivo); la app de escritorio registra un enlace `anymodelbot://`, la página intenta abrir la app una vez al cargar, el botón “Abrir AnyModelBot” permite reintentarlo, y “Ya volviste” solo aparece después de que la app recibió realmente ese enlace. La página de clave de API propia también permite iniciar sesión directamente con una cuenta de Modelflare. En la primera conexión, mientras el modelo predeterminado global siga vacío, se elige una vez un modelo de primer uso con la regla de `src/account/first-model.ts` (admite llamadas a herramientas; tanto la reserva por solicitud como una tarea representativa caben dentro del crédito de prueba; se prefiere DeepSeek); si ninguno cumple, no se preselecciona nada y el usuario elige. - Método de inicio de sesión: código de autorización OIDC + PKCE (RFC 7636), como app nativa según RFC 8252. La app es un cliente público (`client_id` es `anymodel-bot`; no tiene ni necesita secreto de cliente), y el resultado de la autorización se entrega en un puerto temporal de `127.0.0.1`. Puedes revocar el acceso en cualquier momento en “Apps autorizadas”, en tu página de perfil de Modelflare; la app lo detecta y vuelve a la pantalla de inicio de sesión. - La credencial de la cuenta (refresh token) se guarda en `modelflare-account.json` en la carpeta de datos (modo 600) y, al igual que las claves de API del gateway integrado, solo la protegen los permisos del archivo; no se usa el llavero del sistema (un archivo cifrado por la versión de escritorio 0.1.30 o anterior se reescribe sin cifrar la primera vez que se lee). La UI solo recibe el nombre de la cuenta. “Cerrar sesión” revoca la autorización de esta computadora, y Modelflare elimina las claves que emitió para ella y sus bots al iniciar sesión. - Saldo: no está siempre en pantalla. Haz clic en el avatar de la esquina inferior izquierda de la barra lateral para verlo, o abre Ajustes → Modelos. Ambos muestran el saldo de la billetera. “Recargar” abre el sitio web de Modelflare, y el saldo se actualiza cuando vuelves a la ventana. Cuando el saldo baja del umbral de recordatorio (1 dólar estadounidense de forma predeterminada; se puede ajustar o desactivar), la app te avisa una vez, y también puede hacer que un bot te escriba por WeChat / Feishu. - Con la sesión iniciada, Ajustes → Modelos también muestra el uso por día y por modelo, las notificaciones de Modelflare y tu enlace de invitación. - Presupuestos de bots (requiere iniciar sesión): Ajustes del bot → Presupuesto. Si está activado, el bot usa su propia clave (un proveedor separado en el gateway) con el límite en dólares que definas; si está desactivado, vuelve a la clave compartida. - Esfuerzo de razonamiento: elígelo en el panel del selector de modelos o en Ajustes → Modelos (Automático / Bajo / Medio / Alto). Es un único ajuste global y se aplica desde el siguiente turno. Automático no envía ningún parámetro y usa el valor predeterminado del modelo. Solo afecta a los modelos que lo admiten: modelos de razonamiento en la Responses API, y modelos de Chat o Claude que el perfil del modelo marca como de razonamiento (Claude 4.6 y posteriores usan pensamiento adaptativo con `output_config.effort`; los modelos anteriores usan `budget_tokens`). - Generación de imágenes / video: en modo agente, los bots tienen cuatro herramientas —`media_models` (modelos disponibles, valores predeterminados, rangos de parámetros, precios de referencia), `media_docs` (lee en vivo el formato de solicitud del modelo desde docs.modelflare.dev), `generate_image` y `generate_video`— más una habilidad de medios incluida. Los formatos de parámetros varían según el modelo, así que el bot escribe el cuerpo de la solicitud a partir de la documentación; las imágenes de referencia del espacio de trabajo se escriben como `workspace:path`. La clave nunca sale de la app: la app llama a Modelflare con la clave del bot, guarda los resultados en `media/` del espacio de trabajo del bot y los entrega en el chat. - Confirmación antes de generar: la imagen o el video se genera de inmediato. Que el modelo no sea el predeterminado, o que la solicitud sea imprecisa, no es motivo para preguntar: el bot toma la interpretación más razonable, genera y dice en una línea qué usó. El chat pregunta qué modelo usar (con el prompt, los parámetros, el precio de referencia y el motivo de la pregunta) solo cuando el costo estimado supera $0.5 (a partir del precio de referencia, el número de imágenes, la duración y la resolución del video; un video cuyo costo no se puede estimar también cuenta). Puedes confirmar, cambiar de modelo o cancelar: responde con un número o el nombre del modelo en WeChat, o haz clic en Feishu / la consola web. Si no hay respuesta en 10 minutos, no se genera nada. Para confirmar siempre: ajustes del bot → Imágenes y video → Confirmar antes de generar → “Preguntar siempre”. - Búsqueda web: `web_search` del agente no usa la búsqueda integrada de DeepSeek (necesita una clave de DeepSeek); el plugin `amb-web-search` pasa cada búsqueda a la app. De forma predeterminada es gratis y no requiere configuración: prueba por turnos el MCP público de Exa, Bing y DuckDuckGo, empezando por el último que funcionó, y omite durante 10 minutos el que falle (límite de solicitudes, verificación anti-bots, Bing devolviendo resultados no relacionados). Ajustes → Red y acceso → Búsqueda web permite cambiar a Tavily, Exa, Bocha o Zhipu (con clave) o a tu propio SearXNG; si está configurado, se prueba primero y la búsqueda gratuita toma el relevo si falla. Las claves se guardan en la base de datos local, como los tokens de los canales, y nunca llegan al proceso del agente. La app de escritorio busca a través del proxy del sistema; la versión de línea de comandos usa fetch de Node (respeta `HTTPS_PROXY` con `NODE_USE_ENV_PROXY=1`). - Cuentas de canales de chat (`src/core/routes.ts`): las cuentas de WeChat, Feishu, Telegram, Slack y Discord también son globales. Las agregas en Ajustes › Canales de chat y puedes darles alias. Un mensaje va al bot que esa persona eligió con `/bot`, luego al bot predeterminado de la cuenta y luego al primer bot. `/bot` lista los bots; enviar `/bot name` una vez cambia de bot y lo recuerda, así que el chat se queda con ese bot. El prompt del sistema de cada bot lista los demás bots y cómo cambiar, para que pueda responder si se lo preguntan. Los propietarios y las listas de permitidos pertenecen a la cuenta, así que cualquier persona permitida en una cuenta puede hablar con cualquier bot. La clave de una cuenta es un espacio de nombres: los canales de antes de este cambio conservan como clave el ID de bot anterior, para que los inicios de sesión, las cuotas, las listas de permitidos y las colas no se muevan, y el bot predeterminado sigue siendo el mismo. Eliminar un bot no afecta a las cuentas. - Conectar apps (`src/tools/apps/service.ts`): una lista global (menú del avatar › Conectar apps). De forma predeterminada, todos los bots pueden usar todas las apps conectadas; puedes restringirlo por bot y por herramienta (ajustes del bot › Apps). Una app tiene una tarjeta en la página Apps y puede tener varias cuentas, así que puedes decirle a cualquier bot “revisa mi primer Feishu y mi segundo Feishu”; la cuenta se elige con el argumento `account` de la herramienta (número o nombre). - **Apps integradas** (`catalog.ts`): Feishu, Tencent Docs, DingTalk, Amap, Yuque, Notion, GitHub, Linear, Atlassian, Canva. Usan los mismos métodos de autenticación que los conectores personalizados: inicio de sesión (OAuth con registro dinámico, `oauth.ts`), encabezados, parámetros de consulta, URL dedicada. Yuque es un programa local que se incluye con la app (`vendor/yuque-mcp`, generado por `scripts/vendor-yuque-mcp.mjs`); el usuario solo pega un token. - **Conectores personalizados**: Streamable HTTP remoto (con SSE heredado como alternativa) o un comando local, hasta 20. El reconocimiento de lo que se pega y la conversión formulario ⇄ JSON están en `ui/lib/connector-config.js`. Los comandos locales se ejecutan con el PATH del shell de inicio de sesión (`shell-path.ts`); los ejecutores de paquetes (npx, uvx) tienen más tiempo en su primer arranque, cuando descargan el paquete. - **El proceso principal establece la conexión** (`mcp-client.ts`, el `@modelcontextprotocol/client` oficial): las credenciales se quedan en el proceso principal, en `app-secrets.json` (permiso 600, fuera de la base de datos, `secrets.ts`); el plugin `amb-connections` envía cada llamada a herramienta a la app a través del puente. Las herramientas de las conexiones MCP no se registran una por una. En su lugar, el modelo recibe tres herramientas fijas: `app_tools` para encontrar una herramienta, `app_tool_schema` para ver sus parámetros y `app_call` para ejecutarla, además de `app_connect` para pedirle al usuario que conecte una app (aparece una tarjeta en el chat y la tarea continúa cuando se conecta). - **Aprobación** (`gate` en `service.ts`; la pregunta al usuario está en `agent/manager.ts`): las herramientas de lectura se ejecutan de inmediato; las de escritura siguen el nivel de permisos del bot (no se ofrecen a los bots de solo lectura, en el nivel predeterminado se pregunta primero con la opción “permitir siempre esta herramienta” y con acceso total se ejecutan directamente); los borrados y las acciones hacia el exterior siempre preguntan; las reglas que solo observan no pueden escribir. El tipo de cada herramienta viene de `classify.ts`: las apps integradas usan la tabla integrada y las anotaciones oficiales, mientras que las anotaciones de un conector personalizado son solo un valor inicial que el usuario puede cambiar. - **Cuentas existentes de Notion y Feishu** (`src/tools/accounts/`): las cuentas conectadas antes de la actualización siguen funcionando sin volver a autorizarlas; sus tokens pasan de la base de datos a `app-secrets.json` en el primer arranque. Notion (`notion.ts`) usa autorización con un clic o un token: los tokens usan la API REST, y la autorización con un clic usa OAuth + PKCE + registro dinámico con una redirección al `/oauth/notion/callback` local y luego llama al MCP remoto oficial (`mcp.notion.com`); el modelo sigue viendo `notion_search`, `notion_read_page`, `notion_query_database`, `notion_create_page` y `notion_update_page`, sin borrado. Las cuentas existentes de Feishu (`feishu.ts`) leen mensajes como el usuario (`feishu_list_chats`, `feishu_read_chat`, `feishu_search_messages`); las cuentas de Feishu recién conectadas usan el MCP remoto oficial (lectura y escritura de documentos en la nube, y lectura de mensajes), y una cuenta existente cambia a él cuando se vuelve a autorizar. - Modelos predeterminados de imagen / video: se eligen junto con el modelo de texto predeterminado durante la configuración inicial, y se pueden cambiar después en Ajustes → Modelos. “Automático” usa el primer modelo del orden recomendado que no necesite material de referencia. - El selector de modelos muestra la longitud de contexto, las capacidades de visión / herramientas / razonamiento y los precios de referencia mínimos por millón de tokens de entrada / salida (del catálogo público de precios; lo que vale es tu factura). - Costo-beneficio (requiere iniciar sesión): las claves que emite la app (la clave de esta computadora y la clave de presupuesto de cada bot) llevan la estrategia de enrutamiento de Modelflare Smart Key `routing_strategy`: Económico `low_cost` (primero el grupo con el multiplicador más bajo), Equilibrado `balanced` (el predeterminado; empieza por el grupo de precio medio) o Calidad `stable` (primero el multiplicador más alto). Cámbiala en Ajustes → Modelos → Costo-beneficio o con el primer control deslizante del selector de modelos; las claves existentes se actualizan a la vez (`PATCH /api/connected-app/api-keys/`), y la elección se guarda en la base de datos local (`account.routing`). A los usuarios con sesión iniciada que nunca eligieron se les aplica Equilibrado una vez a sus claves existentes, en la primera comprobación de claves (se registra como `applied the default key routing strategy`). Solo se considera compatible cuando Modelflare devuelve `routing_strategy`; si rechaza el campo (400 / 422), la clave se emite de nuevo sin él, de modo que la emisión, las comprobaciones de claves y los presupuestos nunca fallan por su causa. Si ignora el campo (no lo devuelve), también cuenta como no compatible. En ambos casos el ajuste se oculta y se vuelve a intentar un día después. - Inteligencia (el segundo control deslizante del selector de modelos): entre los modelos que esta cuenta puede usar y que tienen datos de Artificial Analysis, toma la frontera de Pareto entre el índice de inteligencia (más alto es mejor) y el costo por tarea (más bajo es mejor), con hasta 5 posiciones, incluyendo siempre el más barato y el más potente. La inteligencia se toma para el esfuerzo que la app usa realmente: los modelos de razonamiento en la Responses API usan medium de forma predeterminada, así que se usa la fila medium de AA; todo lo demás usa la fila predeterminada de AA (`src/account/model-quality.ts`). Costo = costo por tarea de AA (precios oficiales de lista) × el factor de precio del modelo con el ajuste de Costo-beneficio actual (el multiplicador de sus grupos públicos utilizables × su multiplicador de venta: el más bajo para Económico, el más alto para Calidad y el del medio para Equilibrado; se excluyen los grupos exclusivos para empresas; sin sesión iniciada o sin compatibilidad se asume Equilibrado). Mover el control cambia al modelo de esa posición; el nombre del modelo abre la lista completa para elegir manualmente, y un modelo que no es una posición aparece como Personalizado. Con menos de dos posiciones, el selector es solo la lista. Los cálculos están en `ui/lib/frontier.js`. - Los datos de AA se incluyen en el repositorio como `assets/model-quality.json` (`updatedAt`, `source`, solo los campos que usamos, ya asignados a los ID de modelo de Modelflare). Actualízalos con `node scripts/update-model-quality.mjs`, que obtiene la clasificación pública https://artificialanalysis.ai/leaderboards/models y el catálogo público de precios de Modelflare; con `AA_API_KEY` definida usa en su lugar la API oficial (sus campos todavía no se han comprobado con una clave). `--html ` y `--pricing ` usan copias guardadas, y `--dry-run` solo imprime. Si la página cambia de forma (falta una columna, hay filas con una forma incorrecta, números que no se pueden interpretar, muy pocas filas o muy pocos modelos emparejados), sale con código 1 indicando qué falló y deja intacta la instantánea existente. Los modelos cuyos nombres no se asignan con las reglas van en `OVERRIDES` del script. Los datos son de Artificial Analysis, que aparece citado al pie del selector. - Atribución: las solicitudes a través de Modelflare llevan `X-Modelflare-Skill: anymodel-bot/`, y los enlaces llevan `utm_source=anymodelbot&utm_campaign=amb_` (`amb_signup`, `amb_topup`, `amb_console`, etc. dentro de la app, `amb_readme` para el README y la documentación del repositorio, `amb_homepage` para el sitio web; `test/utm.unit.test.ts` fija el formato). - Dominios de la API: elige `https://modelflare.dev/v1`, `https://origin.modelflare.dev/v1` o `https://cf.modelflare.dev/v1` en Ajustes → Modelos → Dominio de la API. Las pruebas manuales se ejecutan desde el dispositivo donde corre AnyModelBot, solicitan `/v1/models` en los tres dominios sin credenciales e informan el tiempo de respuesta mediano y cuántos de los tres sondeos tuvieron éxito. Un HTTP 401 cuenta como respuesta de la API, no como prueba de que la clave sea válida ni de la velocidad de generación del modelo. No hay cambio automático. Los valores de configuración antiguos `auto` / `global` siguen seleccionando el dominio principal, `cn` selecciona origin y `cf` selecciona cf; estos valores de compatibilidad no indican región ni calidad. - Ajustes → Modelos: cuenta, saldo y recarga, recordatorio de saldo, uso, notificaciones, invitaciones; costo-beneficio (estrategia de enrutamiento) y esfuerzo de razonamiento; modelo predeterminado, actualizar la lista de modelos. - El botón de modelo a la derecha del cuadro de mensaje cambia el modelo de la sesión actual (o lo define como predeterminado del bot); `/model`, `/reset`, `/new`, `/status`, `/stop` y `/help` funcionan. - Actividad: cada paso de un turno del agente (solicitudes al modelo con tiempos y tokens, llamadas a herramientas con argumentos y salida, reintentos, subagentes) se agrupa en la línea del tiempo transcurrido, encima de la respuesta; haz clic para desplegarla. Para las sesiones de WeChat / Feishu, consulta panel de información → Conversaciones recientes, donde el turno en curso se muestra en vivo y se puede detener. - Mensajes atascados: `/stop` (en cualquier canal) o “Detener” en el panel de información termina el turno actual de inmediato. Un turno sin progreso durante 10 minutos (sin respuesta del modelo, sin actividad de herramientas, solo reintentos; esperar tu aprobación o tu respuesta no cuenta) se detiene automáticamente, y un solo turno tiene un límite de 30 minutos (`AMB_AGENT_IDLE_TIMEOUT_MS`, `AMB_AGENT_TURN_TIMEOUT_MS`). Detener solo para el turno de esa conversación (lo cancela el plugin amb-control dentro del proceso del agente); las demás conversaciones del bot siguen en marcha. Solo si no se detuvo en 10 segundos (`AMB_STOP_GRACE_MS`) se reinicia el proceso del agente. `/status` muestra a qué paso llegó y cuánto tiempo pasó desde el último progreso. En el chat web, cambiar a otro bot o recargar la ventana no interrumpe un mensaje en curso; al volver, retoma donde estaba (tu mensaje, la respuesta hasta ese momento y la actividad). El botón de detener también funciona mientras el mensaje está en cola, mientras el agente arranca y mientras espera una aprobación, y también en el chat simple. - Intervenir sobre la marcha: un mensaje (texto, imágenes, archivos) enviado mientras el agente trabaja se une al turno actual; el modelo lo ve en su siguiente paso y una sola respuesta cubre ambos (primero recibes un breve “recibido”, enviado por la bandeja de salida y descartado si no puede salir en 10 minutos). Los mensajes agregados así pasan la misma comprobación de admisión que cualquier otro: un mensaje de alguien que no está en la lista de permitidos nunca se une a un turno. Antes de que el agente haya tomado el mensaje anterior, o mientras el turno está terminando, se pone en cola como antes, igual que los comandos (`/model`, `/reset`…). En el chat web puedes seguir escribiendo mientras hay una respuesta en curso. - Los comandos largos pasan a segundo plano: un comando que sigue ejecutándose después de 30 segundos (60 para PowerShell en Windows) se convierte en un trabajo en segundo plano en lugar de bloquear el turno, y el agente responde que sigue en ejecución. Cuando termina, el agente se despierta con el resultado y su respuesta llega como un mensaje nuevo en la misma conversación (en vivo en el chat web; en WeChat y Feishu pasa por la bandeja de salida como cualquier respuesta: su ID se deriva de la sesión y el turno del agente para que se envíe una sola vez, la ventana de 24 horas y la cuota de WeChat se comprueban al enviar, y en Feishu responde a tu último mensaje, cuyo ID se guarda en la base de datos). La actividad muestra los pasos del comando en segundo plano, y `/status`, la detención y el watchdog tratan ese turno como cualquier otro. - Cada conversación usa su propio modelo: dentro de un mismo proceso del agente, cada conversación funciona con su propio modelo y su propia API (un proveedor por API, ventana de contexto y salida máxima por modelo). Cambiar de modelo no reinicia el proceso del agente; un modelo que el parche de arranque no incluía se agrega en tiempo de ejecución. - Reemplazo del proceso del agente: el SDK de DSH no puede cerrar una sesión, así que la sesión de cada tarea en segundo plano y de cada ejecución de regla permanece en el proceso del agente hasta que este se reinicia (medido: unos 0.25–0.45 MB por sesión terminada e inactiva). Cuando un proceso ha abierto más de 100, se reemplaza en segundo plano en cuanto queda completamente inactivo: sin turno de chat, sin tarea ni ejecución de regla en curso o cerrándose, sin aprobación pendiente, sin compactación y sin trabajo en segundo plano en el proceso. Las sesiones continúan como antes, no se interrumpe nada y el usuario no nota nada. El estado del agente en Solución de problemas muestra `taskSessions` y `recycles`; `AMB_DSH_RECYCLE_SESSIONS` cambia el umbral. - Aviso tras un reinicio: un mensaje que aún se estaba procesando cuando la app se cerró o falló (cada conversación guarda en la base de datos un marcador de mensaje en curso, que se elimina cuando el mensaje termina) se notifica una vez en su conversación en el siguiente inicio: “上次的消息「…」因为应用重启没有处理完,请重新发送” (tu último mensaje no se terminó de procesar porque la app se reinició; vuelve a enviarlo). No se reenvía nada automáticamente. El chat web lo recibe en su historial; WeChat usa el context_token guardado y la misma cuota de respuestas, y si la ventana de 24 horas o la cuota ya se agotaron, se dice antes de la respuesta a tu siguiente mensaje en esa conversación; Feishu lo envía al chat original. - API del agente: el agente elige la API según lo que el catálogo público de Modelflare indica que admite el modelo: los modelos con Responses API (GPT-5.4 y posteriores, Grok 4.5 y posteriores, DeepSeek V4, GLM-5, Kimi K3 y otros) usan `/v1/responses` (los modelos de razonamiento usan el esfuerzo medium de forma predeterminada; el razonamiento cifrado se reenvía con el historial); los modelos sin Responses pero con Anthropic Messages (Claude) usan `/v1/messages`; todos los demás modelos, el caso en que el catálogo no está disponible (por ejemplo, sin conexión) y los endpoints externos usan Chat Completions. La API se elige por modelo la primera vez que el proceso del agente lo usa (el estado del agente en Solución de problemas muestra la `api` del último turno y los modelos del proceso); `AMB_AGENT_API=chat` fuerza Chat Completions en todos los casos (para solucionar problemas). El chat simple (sin agente) sigue usando Chat Completions. Red de seguridad: cuando el proveedor rechaza la solicitud Responses / Messages de un modelo (HTTP 400/404/405/415/422 o “parámetro / endpoint no compatible”; los límites de solicitudes, el saldo, los permisos, las caídas del proveedor y los desbordamientos de contexto no cuentan), ese modelo cambia a Chat Completions durante 24 horas (se registra en `dsh/api-fallback.json` en la carpeta de datos, con una advertencia en el registro, y el estado del agente muestra `openai-completions (fallback: reason)`). Si el turno todavía no llamó a ninguna herramienta, esa conversación lo repite automáticamente con Chat (sin reiniciar el proceso del agente) y el usuario recibe igualmente una respuesta; si ya llamó a herramientas, se informa del error y el siguiente turno usa Chat. - Gateway integrado: magpie se ejecuta en modo `--gateway` con su propio HOME / directorio de configuración dentro de la carpeta de datos de la app. **No lee** las credenciales de suscripción de Claude / Codex que ya tengas en tu computadora, **no reescribe** ninguna configuración de agentes y tiene desactivadas la actualización automática y la detección de cuentas. Solo se llama desde dentro del proceso de la app; la consola ya no hace de proxy de sus páginas ni de `/v1`. - La clave de API se guarda solo en la configuración aislada del gateway —nunca en una línea de comandos, nunca en los registros— y el frontend solo recibe sus últimos caracteres. - Desarrollo / pruebas: `AMB_MAGPIE_BASE_URL` apunta la app a cualquier endpoint compatible con OpenAI (el gateway no se inicia, la UI se considera conectada y las funciones de cuenta no están disponibles); `AMB_MODELFLARE_BASE_URL` apunta la URL base de Modelflare a un servidor simulado local, y `AMB_MODELFLARE_ISSUER` apunta el inicio de sesión y las API de cuenta a un servidor simulado local. ## Claves de API oficiales - Las claves se pueden ingresar desde “Usar tu propia clave de API” en la pantalla de bienvenida y en Ajustes → Modelos. Los proveedores admitidos y sus direcciones están fijados en `src/providers/official.ts`, y la API (`GET /amb/api/official`, `PUT`/`DELETE /amb/api/official/`) solo acepta ID de esa tabla, nunca una dirección. - Cada uno es un proveedor en el gateway integrado con el ID `official-`, y los ID de modelo son `official-/`, lo que los separa del modelo de Modelflare con el mismo nombre. Las claves nunca pasan por argumentos de la línea de comandos: llegan a la configuración aislada en el cuerpo de una solicitud a la API local del gateway. - La comprobación usa la acción de lista de modelos del gateway (los proxies y el estilo de autenticación de cada proveedor están en el gateway), y una clave solo se guarda si la supera. Los modelos se comparan cada hora con la lista del proveedor, y los que no sirven para chatear (voz, imagen, embeddings…) no aparecen en el selector. - La API del agente depende del proveedor: Responses para OpenAI, Messages para Anthropic y Chat Completions para el resto. Las longitudes de contexto, los precios y la compatibilidad con razonamiento vienen del catálogo de modelos del gateway (`/v1/models`). Si solo hay claves oficiales, no se leen el catálogo ni la cuenta de Modelflare. - Los modelos de API oficiales no llevan `X-Modelflare-Skill`, no usan la compactación del lado del servidor (su endpoint viene del catálogo de Modelflare) y no están limitados por los presupuestos de bots (un presupuesto es la asignación de una clave de Modelflare). La generación de imágenes y video solo existe mientras Modelflare está conectado. - Pruebas: `AMB_OFFICIAL_BASE_URL` apunta todos los proveedores a un servidor simulado local (`test/official-providers.integration.test.ts`). ## Interfaz - Chats grupales (`src/core/groups.ts`, `ui/screens/group.js`): con dos o más bots, el menú + permite crear un grupo. Cada miembro tiene su propia conversación en el grupo, `:web:group:`, así que el modelo, los permisos, la memoria, las aprobaciones y los costos siguen el mismo camino que en los chats individuales. Lo que dice el usuario se guarda en la conversación `group:`, y la línea de tiempo combina estos mensajes por ID. Responde quien recibe la @-mención (`@everyone` significa todos los miembros); sin @, sigue el bot que respondió por última vez. Un bot que @-menciona a otro miembro en su respuesta le pasa el turno, hasta 6 veces por mensaje del usuario. En un grupo solo responde un bot a la vez. - Barra lateral: lista de bots (avatar + nombre + último mensaje), + Nuevo, clic derecho para cambiar el nombre / eliminar; la lista se divide en secciones que puedes crear, cambiar de nombre, eliminar y arrastrar para reordenar (Míos y Nuevos de forma predeterminada, guardadas en el servidor y compartidas por todas las ventanas); los bots, los chats grupales y los bots del catálogo se pueden arrastrar a otra sección o mover con “Mover a la sección”, y los bots del catálogo recién publicados llegan a Nuevos; en la esquina inferior izquierda, el avatar muestra tu saldo y el engranaje abre Ajustes (`⌘/Ctrl+,`). - Pantalla de chat: la etiqueta flotante con el nombre del bot en la parte superior abre el panel de información (`⌘/Ctrl+⇧+I`: cuentas de canales en las que este bot responde de forma predeterminada, aprobaciones pendientes, tareas y reglas, memoria, conversaciones recientes; ⚙ abre los ajustes del bot). Las respuestas llegan en streaming (Markdown, copia con un clic para los bloques de código) y se pueden detener en cualquier momento (`Esc`). Las aprobaciones y las preguntas aparecen como tarjetas en el chat. Si te desplazas hacia arriba, la vista nunca salta de golpe al final. - Paleta de comandos `⌘/Ctrl+K`; la apariencia (sistema / claro / oscuro) está en Ajustes → General. - Idioma: chino simplificado / inglés / japonés / coreano / ruso / español / portugués (Brasil), en Ajustes → General → Idioma (también en la esquina superior derecha de la pantalla de configuración inicial y en la paleta de comandos). De forma predeterminada sigue al sistema (inglés si el idioma del sistema no es compatible). La preferencia se guarda en `ui.lang` en config.json; el menú de la bandeja, los mensajes de error y de estado del servidor, y los mensajes del sistema que los bots envían a WeChat / Feishu (`/help`, solicitudes de aprobación, …) la siguen. En la línea de comandos / Docker, “Sistema” significa `AMB_LANG`, luego `LC_ALL` / `LC_MESSAGES` / `LANG`, y si no, chino simplificado. Los registros, los prompts enviados al modelo y los nombres de bots que eligieron los usuarios no se traducen. - Los textos de la UI están en `ui/locales//.js` y se usan como `t("namespace.key", { params })` (`ui/lib/i18n.js`); los textos del servidor están en `src/i18n/messages/.ts` con claves comprobadas por tipos. Cada solicitud de la UI lleva `x-amb-lang`, y el servidor responde esa solicitud en el mismo idioma. - Agrega cada texto nuevo en ambos idiomas. `node scripts/i18n-check.mjs [files…]` informa del chino que quede en el código de la UI, de las claves que no existen y de los diccionarios zh / en desincronizados (`test/i18n.unit.test.ts` en `npm test` comprueba lo mismo). El chino que debe quedarse en el código (nombres de fuentes, expresiones regulares que reconocen entradas en chino) lleva un `// i18n-ignore` en esa línea. - Lista de primeros pasos: el chat vacío muestra una lista de seis pasos: saludar, cambiar de modelo, conectar WeChat, pedirle que haga algo, hacer que te recuerde y generar una imagen. El último paso solo reproduce una demostración integrada dentro de la guía, tenga o no la cuenta modelos de imagen / video: igual que el flujo predeterminado real, genera de inmediato y muestra la imagen en el chat, pero no se envía nada al bot, no se llama a ningún modelo y no se cobra nada; la imagen es el archivo incluido `ui/assets/guide-demo.svg`. La idea es enseñar el flujo sin gastar el dinero del usuario; pedirle de verdad que dibuje, o recibir una imagen más adelante, también completa el paso. “Enviar” lo prueba directamente; “Muéstrame” o la entrada de la lista en la esquina inferior izquierda de la barra lateral muestra una tarjeta de guía que señala el control correcto (resaltado con un anillo). Los pasos se marcan solos: una respuesta en el chat, un cambio de modelo, un archivo / imagen recibido, un canal conectado, una escritura en la memoria. El progreso se guarda en `ui.guide` en config.json (API `/amb/api/guide`), y los pasos que los usuarios existentes ya hicieron cuentan como completados. La lista se puede contraer en la esquina inferior izquierda u ocultar para siempre, y Ajustes → General → Lista de primeros pasos la vuelve a empezar. Saludar necesita una respuesta real (los avisos de error no cuentan), y un canal tiene que estar conectado de verdad (solo configurarlo no cuenta); el paso “Conecta tu app de chat habitual” ofrece WeChat, Feishu, Telegram, Slack y Discord, y el idioma de la UI solo decide cuál se sugiere. Cuando el usuario dice explícitamente “Recuerda…”, la revisión de memoria posterior al turno se ejecuta de inmediato en lugar de después de 90 segundos. El progreso se guarda por usuario (por cuenta si se inició sesión en Modelflare, en el KV `guide.owner.*`; si no, sigue en `ui.guide` en config.json); completar todo es un hito tras el cual la lista se oculta para siempre, y las tarjetas de guía que aparecen solas esperan mientras el cuadro de mensaje tiene un borrador o hay una tarea en ejecución. - Aspecto visual: los neutros y los componentes están adaptados de la UI de magpie (MIT); el color de acento es el amarillo verdoso fluorescente de Modelflare `#d7ff00`. El sistema de diseño se describe en `ui/design/README.md`. ### Localización y comprobaciones de publicación Los mantenedores escriben y adaptan cada traducción directamente; no generes textos con API de traducción. Conserva los nombres de productos, los ID de modelo, los comandos, las URL y los parámetros `{placeholder}`. - Mensajes de la app: `ui/locales//.js`; nuevos locales del servidor: `src/i18n/locales//.ts`. - Mensajes del sitio web: `site/src/i18n/locales//`. Por ejemplo, `home/hero.ts` corresponde a `home.hero.*`. Las páginas leen del catálogo. - `ui/locales/manifest.json` lista los idiomas de destino y su estado `ready`. El sitio web mantiene un `site/src/i18n/manifest.json` idéntico; la validación comprueba que coincidan. Los idiomas incompletos se quedan en `ready: false` y no aparecen en los menús públicos ni en las anotaciones de idioma para los buscadores. - `npm run i18n:status` informa del progreso por idioma y por área. `npm run i18n:check` valida todos los idiomas de destino y los documentos complementarios. Las claves faltantes, vacías o sobrantes, los parámetros dañados y las formas de plural faltantes bloquean la publicación. Recurrir al inglés durante el desarrollo no cuenta como traducción. - Los plurales en ruso usan `one / few / many / other`; el español y el portugués usan `one / many / other` (`many` solo cubre los millones redondos y se escribe como `other`); el japonés y el coreano usan `other`. Localiza la presentación sin cambiar la moneda, la zona horaria ni los importes. - `npm run build` del sitio web comprueba todos los locales de destino, los documentos sincronizados y las imágenes para compartir. `build:draft` es para vistas previas locales y no es una comprobación de publicación. ## Personalidad y prompts Todos los prompts que lee el modelo del agente están en `src/agent/prompts/`. `test/prompts.unit.test.ts` comprueba las frases obligatorias y prohibidas de cada parte y un límite de caracteres; `test/agent.integration.test.ts` comprueba, con un proceso hijo real de DSH, el prompt del sistema que recibe el modelo y un límite para el prompt del sistema completo más las definiciones de herramientas. - **Personalidad**: la personalidad propia de DSH es la de un “agente de programación”, precedida de “You are an AI agent powered by DeepSeek Harness.”. El parche (`host.ts` writePatch) reemplaza `personaPrefix` por una personalidad de asistente personal (chatea, y también escribe código, ejecuta comandos, maneja archivos y opera apps de escritorio cuando el uso de la computadora está activado; el texto de páginas web, archivos y mensajes reenviados son datos, no instrucciones) y desactiva `includeHarnessIdentity` (solo controla esa frase). - **Sección del bot**: las reglas de chat, cómo usar la memoria a largo plazo (cuando la memoria está activada), el nombre del bot y la personalidad que escribiste forman la última sección del prompt del sistema (`amb:bot`). Antes de cada turno, el host la escribe en `dsh/.prompts/.md` en la carpeta de datos (las herramientas de archivos del agente no pueden leerla ni cambiarla; una sección sin cambios no se reescribe), y el plugin `dsh-runtime/plugins/amb-prompt.mjs` la lee en cada solicitud al modelo: una personalidad o un nombre editados se aplican desde el siguiente mensaje, sin `/new` y sin reiniciar el proceso del agente. DSH 0.2.1-rc.4 llama al `text` de una sección de forma síncrona (no espera una Promise devuelta), por eso el plugin lee un archivo en lugar de llamar al puente. Cuando la personalidad y las reglas de chat no coinciden, gana la personalidad. Los subagentes no reciben esta sección: no hablan con el usuario. - **Reglas de chat** (en chino, adaptadas del principio de atención de dsh-superagent): responder primero; para una tarea que lleva tiempo, decir en una línea qué va a hacer y luego no narrar los pasos (están en el panel de actividad); al terminar, decir qué se hizo y dónde está el resultado; lo que no está terminado se informa como no terminado y un fallo, como un fallo; decidir por su cuenta y decir qué eligió, sin preguntar nunca por el idioma, el formato, el alcance o el estilo; preguntar solo cuando una suposición equivocada causaría daño, costaría dinero o desperdiciaría la mayor parte del trabajo, cuando necesita algo que solo tú tienes (un inicio de sesión, un archivo, una contraseña) o cuando el paso no se puede deshacer (pagar, borrar tus datos, escribir a otras personas), una pregunta concreta a la vez, con `ask_user_question` para las opciones; sin relleno; respuestas cortas en WeChat y Feishu. Las tareas en segundo plano siguen la misma regla y dicen en el resultado qué eligieron. - **El primer mensaje de un segmento**: el canal (WeChat solo muestra texto sin formato; Feishu y la web admiten Markdown), la instantánea de la memoria a largo plazo de ese momento, la nota de traspaso del segmento anterior y luego tu mensaje. Esto varía según la conversación (memoria del grupo, canal) y cambia con cada dato recordado; en el prompt del sistema invalidaría la caché de prefijo que comparten todas las conversaciones de un bot, así que se queda aquí, sin cambios dentro de un segmento, y los cambios de memoria posteriores se leen con `recall`. - Caché de prefijo: una sección sin cambios mantiene el prompt del sistema idéntico byte a byte; una personalidad editada solo cambia la última sección. Las conversaciones iniciadas antes de la actualización siguen llevando la personalidad anterior en su primer mensaje hasta el siguiente segmento (`/new` o un cambio del gestor de contexto). ## Permisos de archivos En modo agente, cada bot tiene un perfil de permisos de archivos. Cámbialo con el selector de permisos de la esquina inferior izquierda del cuadro de mensaje o en Ajustes del bot → Permisos; `/status` muestra el perfil actual: - **Escritura en el espacio de trabajo** (predeterminado): los comandos se ejecutan en un sandbox y solo pueden modificar el espacio de trabajo de este bot; para cualquier cosa fuera de él, primero te pregunta en el chat. - **Solo lectura**: no modifica archivos; los comandos se ejecutan en un sandbox de solo lectura. - **Acceso total**: para usuarios que entienden el riesgo y consideran que las aprobaciones son demasiado frecuentes. Los comandos se ejecutan **sin sandbox**, directamente con tu usuario, y las acciones fuera de los límites ya no preguntan: puede leer y modificar cualquier archivo de tu computadora, incluida la carpeta de datos de AnyModelBot (base de datos, tokens de canales, claves). Solo los **borrados recursivos** siguen preguntando primero (con el mismo flujo de aprobación: responder con un número en WeChat, tarjetas en Feishu / web): `rm -r` / `-rf` / `--recursive` (incluso dentro de `sudo`, `xargs`, `sh -c`), `find -delete`, `git clean`, `rsync --delete`, PowerShell `Remove-Item` / `rm` / `del` / `rd` / `rmdir` con `-Recurse` (y sus abreviaturas), cmd `rd /s`, `del /s`, `robocopy /MIR`, además de formas comunes como `shutil.rmtree` de Python y `fs.rm(…, { recursive })` de Node; también se revisan los archivos de script nombrados en el comando. Al elegirlo se muestra primero una confirmación, y el selector de permisos y la página de ajustes lo marcan en rojo. - Es una protección contra borrados accidentales, no una barrera de seguridad: inspecciona el texto del comando, así que no ve los comandos construidos en tiempo de ejecución (`eval "$(…)"`, `curl … | sh`) ni scripts de compilación como `make clean` o `npm run clean`; ante la duda, pregunta. Actívalo solo si confías en el modelo y la tarea actuales. ## Quién puede usar un bot El agente se ejecuta en tu propia computadora (posiblemente con acceso total), así que de forma predeterminada **solo el propietario y las personas que permitas** pueden hablar con un bot. El acceso se define por cuenta de canal, y cualquier persona permitida en una cuenta puede hablar con cualquier bot (`src/core/access.ts`): - **Propietario**: en WeChat, la cuenta que escaneó el código QR. Feishu no tiene un inicio de sesión propio, tanto si escaneas para crear la app como si ingresas un App ID y un App Secret, así que **la primera persona que le escriba al bot en un chat individual después de conectarlo** se convierte en el propietario (confianza en el primer uso): una vez conectado, envíale una línea al bot desde tu propio Feishu, y ese mensaje se responde con normalidad. Un inicio de sesión de WeChat que no informó el ID de quien escaneó funciona igual. Ajustes del bot → Quién puede usarlo muestra el propietario de Feishu; “Cambiar” (tras una confirmación) borra el propietario para que la siguiente persona que escriba se convierta en propietario. Cambiar a otra app de Feishu o desconectar Feishu también lo borra. Las personas a las que se les denegó el acceso nunca se convierten en propietario. El chat web local nunca tiene restricciones. - **Cualquier otra persona** recibe una sola línea, “aún no tienes permiso para usar este bot; ya se avisó al propietario” (como máximo una vez por hora por persona); el mensaje no se pone en cola, no se llama a ningún modelo y no se gasta nada. La solicitud aparece en Administrar de esa cuenta en Ajustes › Canales de chat (permitir / denegar) y se envía al propietario con un código de emparejamiento de 4 dígitos (como máximo 3 por cuenta por hora, y solo si el propietario escribió en ese canal en las últimas 24 horas); el propietario responde `同意 1234` / `拒绝 1234` (o `yes 1234` / `no 1234`) en el chat. Los códigos de emparejamiento nunca coinciden con los códigos de aprobación de herramientas. Las personas a las que se les denegó el acceso no reciben ninguna respuesta. - La lista de permitidos está en Administrar de esa cuenta en Ajustes › Canales de chat; se pueden quitar entradas (esas personas pueden volver a pedir acceso enviando un mensaje). `/status` también la muestra. - Al actualizar, todas las personas que ya tenían una conversación directa con un bot se agregan a su lista de permitidos (marcadas como “Ya lo usaba antes de la actualización”), para que nadie se quede fuera; volver a escanear con otra cuenta de WeChat mantiene al propietario anterior en la lista. - Chats grupales: hoy ningún canal los entrega (Feishu solo gestiona mensajes directos y WeChat iLink no tiene grupos), así que los mensajes de grupo se dejan como estaban; las reglas irán en `access.ts` cuando un canal agregue grupos. ## Las respuestas no se pierden Las respuestas finales, los archivos entregados, las solicitudes de aprobación y los avisos se escriben en una bandeja de salida en la base de datos local antes de enviarse (`src/core/outbox.ts`; las actualizaciones de tarjetas en streaming y el indicador de escritura no): - Los mensajes a una misma conversación salen de uno en uno y en orden. Los errores de red o de la plataforma se reintentan (desde 5 s, con esperas crecientes hasta 5 min); si un canal está desconectado, se espera a que vuelva. - Después de que la app se cierre o falle, el envío continúa en el siguiente inicio, y lo que ya se envió no se vuelve a enviar (una respuesta larga se retoma desde la primera parte sin enviar). Los ID se derivan del mensaje entrante, así que un mensaje que la plataforma entrega dos veces sigue recibiendo una sola respuesta final; solo la parte que estaba en tránsito en el momento de un fallo puede salir dos veces, y lleva un `uuid` fijo de Feishu (Feishu elimina duplicados durante una hora) y un `client_id` fijo de WeChat. - WeChat solo permite que un bot responda dentro de las 24 horas siguientes al último mensaje de la persona, y como máximo 10 veces por mensaje. Un mensaje que no puede salir por ese motivo se retiene, no se descarta: la próxima vez que la persona escriba, los mensajes retenidos salen primero, antes de la respuesta, en su orden original y una sola vez, y los de texto se combinan en un solo mensaje “你不在时:…” (“Mientras no estabas: …”) cuando caben en un mensaje de WeChat (los archivos se envían tal cual). Los mensajes retenidos se guardan hasta 7 días; los de corta duración, como el aviso de “recibido” y las solicitudes de aprobación, mantienen su propio plazo. - Los mensajes que ya no se pueden enviar (rechazos repetidos de la plataforma, demasiado tiempo de espera) se marcan con el motivo en el registro y en el diagnóstico. ## Tareas en segundo plano y reglas El trabajo que lleva tiempo se ejecuta en segundo plano y nunca frena el chat (diseño: `docs/design/background-tasks-and-proactive.md`; código: `src/tasks/`): - **Tareas en segundo plano**: el agente del chat inicia una con `task_start` (con su propia sesión de DSH `task-` y una carpeta `tasks/-/` en el espacio de trabajo) y dice en una línea qué va a hacer; el resultado vuelve a la conversación que la pidió (cítalo en WeChat o respóndelo en Feishu para darle seguimiento). Una tarea que te necesita hace una pregunta a la vez por conversación. Tras reiniciar la app, continúa y comprueba antes de repetir acciones externas. `/tasks` lista las tareas y reglas, y `/stop ` detiene una. - **Reglas**: se crean con `rule_create` o en ajustes del bot › Mensajes proactivos › Reglas, y actúan de inmediato: tus propias palabras son el mandato, así que cada ejecución comprueba y solo envía una línea cuando algo importa, y editar el texto mantiene la regla actuando. El modo de observación es opcional (dile al bot “primero observa” o elige Observar en el editor de reglas): solo lectura, no envía nada, registra lo que “habría enviado”, y después de 3 ejecuciones observadas Te necesita pregunta si pasarla a actuar. Cada ejecución tiene una sesión nueva, y el registro de ejecuciones se exporta como Markdown. - **Visible, sin ruido**: el grupo Tareas del panel de información lista las tareas y reglas del bot (las tareas terminadas se agrupan en una fila “terminadas”); abre una para ver de dónde vino, el último progreso, el presupuesto, la carpeta y los pasos de cada ejecución. Todo lo que espera por ti está en Te necesita, en la barra lateral. Solo se envían resultados, decisiones y fallos; el horario de silencio (22:00–08:00 de forma predeterminada), los límites diarios (3 en WeChat, 5 en Feishu) y los resúmenes están en ajustes del bot › Mensajes proactivos. - **Presupuestos**: $1 por tarea, $0.10 por ejecución de regla y $3 por regla al mes de forma predeterminada (estimados con los precios de referencia de Modelflare). Al llegar al límite, el trabajo se detiene y pregunta; cambia el presupuesto de una tarea en sus detalles y el de una regla en el editor de reglas. Cuando a la clave de presupuesto del bot le queda menos del 20 %, el trabajo en segundo plano se detiene para que el chat conserve el resto. - **Pausa**: ajustes del bot › Mensajes proactivos › Pausar el trabajo en segundo plano (o pídeselo al bot) retiene las tareas, las reglas y los mensajes proactivos; el chat sigue funcionando. - **Retención**: las tareas y los registros de ejecución se conservan 30 días; una tarea sin usar durante 30 días se archiva. Una carpeta de tarea sin usar durante 30 días se elimina junto con los archivos de sesión de DSH de la tarea (en `dsh//sessions/`; el SDK no tiene ninguna llamada para eliminar una sesión, así que la app elimina los archivos por su cuenta, y una sesión que siga abierta en el proceso del agente se elimina después de reemplazar el proceso). No se te pregunta ni recibes ninguna notificación: a los 27 días de inactividad se fija la fecha de eliminación y se muestra en los detalles de la tarea, donde Conservar mantiene la carpeta (nunca se elimina antes de que pasen 3 días desde que se fija la fecha). Los detalles de la tarea muestran el tamaño de la carpeta y permiten eliminar la tarea junto con su carpeta. ## Uso de la computadora Cada bot puede activar o desactivar el uso de la computadora, y elegir un backend y un modo de aprobación, en Ajustes del bot (⚙ en el panel de información) → Uso de la computadora; `/status` y la página de ajustes muestran el backend actual, si está disponible y, si no lo está, por qué. - **Backends** - **Cua Driver (predeterminado, en las tres plataformas de escritorio)**: el [cua-driver](https://github.com/trycua/cua/tree/main/libs/cua-driver) 0.37.0 (MIT) oficial precompilado se incluye con el instalador; `scripts/fetch-cua-driver.mjs` lo descarga para cada plataforma / arquitectura durante la compilación y verifica su SHA256. Se ejecuta como el servidor MCP integrado `cua-driver mcp`, siempre con `CUA_DRIVER_PERMISSION_MODE=bounded` más un manifiesto de capacidades, y **nunca** con `--dangerously-bypass-approvals`. El entorno del proceso hijo se limpia (no se pasa ningún secreto), la telemetría está desactivada y `run_script` está deshabilitado. Solo se exponen las 24 herramientas que necesita (leer ventanas / capturas de pantalla, abrir apps, hacer clic, escribir, pulsar teclas, combinaciones de teclas, desplazarse, arrastrar, establecer valores, menús, etc.); todo lo demás (scripts, terminar procesos, portapapeles, inyección en el navegador, grabación, actualizaciones, …) es invisible para el modelo y se rechaza. El manifiesto lista las apps con interfaz gráfica instaladas en la máquina (Linux lee los archivos .desktop, macOS lee /Applications y Windows lee el menú Inicio) y permite la entrada en toda la pantalla; es válido como máximo 24 horas y el driver se reinicia antes de que caduque. - **Puente de Codex Computer Use (opcional, solo macOS)**: kimi-computer-use. Requiere tener OpenAI Codex Computer Use instalado localmente; si no está disponible, se usa Cua Driver. - **Aprobaciones**: de forma predeterminada, todas las acciones de uso de la computadora (leer, hacer capturas de pantalla, hacer clic, escribir, pulsar teclas, desplazarse, arrastrar, establecer valores) **se ejecutan automáticamente sin aprobación**. Opcionalmente, puedes elegir “preguntarme antes de acciones que cambian la pantalla, como clics y escritura” (la lectura de la pantalla sigue siendo automática). Al actualizar, los bots existentes (incluido el bot predeterminado) se migran a automático. Los bots con el perfil de solo lectura solo reciben herramientas de lectura. - **Notas por plataforma** - macOS: concede a la app los permisos de Accesibilidad y Grabación de pantalla en Ajustes del Sistema → Privacidad y seguridad; el driver se ejecuta en modo `--direct` (todavía no verificado en máquinas reales). Sin esos permisos, las llamadas del bot a las herramientas de uso de la computadora se bloquean, el turno termina diciendo que no pudo continuar por falta de permisos, y el chat muestra una tarjeta con exactamente los permisos que faltan (solicitud nativa del sistema, abrir el panel correspondiente de Ajustes del Sistema, volver a comprobar; Grabación de pantalla suele requerir volver a abrir la app, cosa que la tarjeta ofrece, y los borradores sin enviar se recuperan). No se solicita nada en el primer inicio. - Linux: en X11 la interfaz se lee a través de AT-SPI (requiere at-spi2-core y apps con la accesibilidad activada); en Wayland funciona de forma limitada (solo se puede operar la ventana en primer plano de las apps XWayland, y la UI lo indica). No está disponible sin una sesión gráfica (DISPLAY / WAYLAND_DISPLAY). - Windows: debe ejecutarse en la sesión de escritorio interactiva de un usuario con la sesión iniciada (no se admiten las sesiones no interactivas de servicios / tareas programadas). - Las capturas de pantalla solo se envían a modelos (con visión). No se reenvían a los chats de WeChat / Feishu, y solo se envían como archivos cuando el usuario lo pide. - No incluidos: cua-perception, OmniParser (AGPL) ni ningún componente de Cua Spaces (FSL). ## Configurar la app de Feishu La forma más sencilla: Ajustes › Canales de chat → Feishu → Conectar, escanea con Feishu para crear la app del bot y luego envíale un mensaje en Feishu. Para usar una app empresarial personalizada que ya tengas, elige “Usar mi propia app” y configúrala así: 1. Abre la [Feishu Open Platform](https://open.feishu.cn/app) → crea una app empresarial personalizada y agrégale la capacidad Bot. 2. En Permissions, activa: - `im:message.p2p_msg:readonly` (leer los mensajes individuales que los usuarios envían al bot) - `im:message:send_as_bot` (enviar mensajes como la app) - `cardkit:card:write` (crear y actualizar tarjetas; se usa para las respuestas en streaming) - `im:message.history:readonly` (leer el historial de chats: la conexión persistente no vuelve a enviar los mensajes que llegaron mientras la app estaba desconectada, así que al iniciar y tras una reconexión la app se pone al día con el historial de los chats activos en los últimos 7 días, tomando los mensajes de las últimas 24 horas, como máximo 20 por chat. Sin este permiso todo lo demás funciona, pero no se recupera nada, y la app lo avisa una vez) 3. Primero ingresa el App ID / App Secret en esta app, en Ajustes › Canales de chat → Feishu → Conectar → Usar mi propia app, y guarda (la conexión persistente debe estar en línea para poder guardar el siguiente paso). 4. Events & callbacks → modo de suscripción: “**Receive events through persistent connection**” (recibir eventos mediante conexión persistente), y agrega el evento `im.message.receive_v1`. 5. Publica una versión, agrégate al ámbito de disponibilidad y luego envíale una línea al bot desde tu propio Feishu: la primera persona que le escriba después de conectarlo se convierte en su propietario de Feishu (consulta “Quién puede usar un bot”). Nota: por ahora solo se gestionan los chats individuales (p2p); los mensajes de grupo se ignoran. ## WeChat (iLink) - Ajustes › Canales de chat → WeChat → Conectar, escanea el código QR con WeChat y confirma; o ejecuta `npm run weixin:login` (el código QR se muestra en la terminal y se guarda como PNG en la carpeta de datos). - El estado de la sesión, el cursor de sincronización y el context_token se guardan en la carpeta de datos y se reanudan después de un reinicio. - Límites de la plataforma: después de cada mensaje del usuario, se pueden enviar como máximo 10 respuestas en 24 horas; las respuestas se envían de una vez cuando termina la generación y se dividen si superan los 4000 caracteres; mientras se genera se muestra “escribiendo…”. - Imágenes, archivos, videos: lo que el usuario envía (o cita) se descarga del CDN de WeChat y se descifra (hasta 50 MB cada uno). En modo agente se guarda en `inbox/` del espacio de trabajo del bot y el mensaje indica la ruta, para que el agente pueda leerlo, procesarlo o usarlo como imagen de referencia para generar. Los modelos con visión (Claude, GPT-5/6, Gemini, Grok 4, Kimi K3, Muse Spark y otros) también reciben la propia imagen; los demás modelos solo reciben la ruta. En Linux el agente todavía no recibe imágenes directamente (sharp falla en Electron) y solo recibe la ruta. Los archivos que entrega el bot (present) vuelven a WeChat como imágenes o archivos. - Cuando la sesión caduca (código de error -14), el canal se pausa y la UI lo indica; vuelve a escanear para reanudarlo. ## Desarrollo y pruebas ```bash npm run typecheck npm test # pruebas unitarias + de extremo a extremo sin conexión (Feishu / iLink / Modelflare simulados, incluidos el streaming y la detención) + pruebas de integración del gateway (requiere magpie compilado) npm run docs:check # autocomprobación del README: recursos presentes y dentro del tamaño, misma estructura en zh/en, enlaces relativos y anclas, términos prohibidos, texto alt ``` La lista de recursos del README y las especificaciones de grabación están en [docs/assets/README.md](./assets/README.md). Para los bots del catálogo (bots prediseñados con su propia UI), consulta [docs/bot-framework_en.md](./bot-framework_en.md) (en inglés). ## Licencia MIT, ver `LICENSE`. Los componentes de terceros (magpie MIT, incluidos los estilos de UI adaptados, Tencent openclaw-weixin MIT y otros) se enumeran en [THIRD_PARTY_NOTICES_es.md](../THIRD_PARTY_NOTICES_es.md) y en Ajustes → Acerca de. ## Habilidades de usuario y búsqueda en el historial de chats Consulta la [guía de habilidades](./skills_es.md) para la UI y el formato ZIP. `src/skills/archive.ts` valida las rutas del ZIP, los límites de extracción y `SKILL.md`; `service.ts` guarda los paquetes, los estados de activación y las huellas de versión de cada bot. `src/web/skills.ts` proporciona los endpoints de vista previa y administración. Las habilidades activadas se despliegan cuando DSH está inactivo, sin interrumpir turnos activos, subagentes ni comandos en segundo plano. `src/botkit/installer.ts` comprueba los conflictos de nombre de las habilidades importadas al actualizar bots del catálogo. `src/store/chat-search.ts` mantiene el índice de mensajes en SQLite y proporciona filtros, paginación y el contexto del mensaje original. `ui/screens/message-search.js` implementa la búsqueda global; `ui/components/chat-search.js` implementa la búsqueda en el chat actual. La búsqueda lee la base de datos local sin llamar a ningún modelo. Pruebas relacionadas: `test/skills*.test.ts` y `test/chat-search*.test.ts`. Define `AMB_TEST_CHROME` para elegir el Chromium que se usa en las pruebas de aceptación en el navegador. Con el runtime de DSH disponible, las pruebas también verifican el descubrimiento, la carga, la lectura de referencias y la desactivación de una habilidad durante el trabajo activo.