Fuente: docs/implementation-status.md · English version
Estado de implementación — 0.4.4
La especificación original es la dirección del producto, no una declaración de que todos sus criterios de release estén superados. 0.4.4 es una versión estable (semver 0.x: las versiones menores pueden incluir cambios incompatibles hasta 1.0.0), funcional y no solo interfaces o stubs.
Contenido
El índice es de texto plano a propósito: este archivo se lee tanto en GitHub como dentro del sitio, y los dos generan anclas distintas para títulos con acentos (GitHub las conserva, VitePress las elimina), así que unos enlaces internos aquí se romperían en uno de los dos. Use el esquema de la derecha en el sitio o la búsqueda de su navegador en GitHub.
- Implementado: proveedores y
/connect; núcleo, límites y almacenamiento; herramientas, AGENTS.md y skills; subagentes; plugins, extensiones y MCP; CLI, runtime y empaquetado; plantillas, pantalla de inicio y TUI; compactación, plugins y memoria; modelo y enrutamiento por sesión; preguntar al usuario; herramientas de red y CLI; confianza de proyecto y diagnóstico; agente activo y effort de razonamiento; agentes del usuario (ventana Agentes y/agents); servidor web (alisio serve); modos de permisos,/reloady/changelog(fase 1 de la especificación de modos, goal y tareas en segundo plano); pestaña Memory de la web y vistas de datos de plugins (specs/archive/alisio-web-memory-tab-v1.md); tareas en segundo plano (fase 3) y objetivos de sesión/goal(fase 4) de la especificación de modos, goal y tareas en segundo plano; Decision Intelligence (fases 1 a 5 despecs/alisio-decision-intelligence-v1.md); Smart Dashboard (fases 0 a 7 despecs/alisio-smart-dashboard-v1.md; pendientes la medición E7, el benchmark B/C y el release). - Validación.
- Pendiente para llegar a 1.0.0.
- Alcance de la verificación: una sección por área (runtime y empaquetado; subagentes, AGENTS.md y skills; plantillas y
/init; pantalla de inicio y extensiones; TUI y compactación; presupuesto de tokens de salida del agente; límite de contexto frente al catálogo; memoria y plugins; pegado y adjuntos de imagen; preguntar al usuario; herramientas de red; confianza de proyecto y permisos; agente activo y effort; contratos de eventos y bloques UI; persistencia v4, blobs y catálogo de comandos; agentes del usuario; servidor web; modos,/reloady/changelog; pestaña Memory y vistas de plugins; tareas en segundo plano; objetivos de sesión; Decision Intelligence; Smart Dashboard). - Límites conocidos: runtime y empaquetado; subagentes; proveedores, plantillas y licencia; memoria; plugins e instalación; portapapeles, pegado y TUI; skills y contexto; compactación y truncamiento; permisos, aprobaciones y confianza; preguntas y herramientas de red; persistencia, estadísticas y Herdr; agente activo y effort; agentes del usuario; servidor web; modos, recarga y novedades; pestaña Memory y vistas de plugins; tareas en segundo plano; objetivos de sesión; Decision Intelligence; Smart Dashboard.
Implementado
Proveedores y /connect
- Proveedores de primera clase: contrato SDK aditivo
providers.register, registro múltiple en el núcleo y activación transaccional. El adaptador OpenAI-compatible salió del núcleo al plugin integrado@alisio/plugin-openai-compatible(Chat/Responses, catálogo y mismo ID heredadoopenai-compatible:<modo>:<baseURL normalizada>)./connectpermite elegir proveedor, configuración y modelo; persiste globalmente el perfil sin secretos enproviders.jsony las credenciales encredentials.json(escritura atómica,0600, directorio0700; no cifrado). Cuando el catálogo no informa la ventana de contexto del modelo seleccionado (servidores locales como llama.cpp),/connectpregunta uncontextWindowopcional en tokens que se guarda en losvaluesdel perfil y alimenta la barra de contexto. El perfil también recuerda el nombre de la variable de entorno de la clave (values.apiKeyEnv): los registros de los plugins exponen el campo,/connectlo persiste, y al crear el proveedor se usa el nombre recordado (con respaldo al nombre por defecto del plugin) para leerprocess.env[...]cuando no hay credencial guardada; la credencial guardada siempre tiene prioridad. El arranque sin proveedor permite onboarding; headless nunca pregunta. Cada cambio inicia una sesión nueva para no mezclar continuación opaca. Se mantienen config/env/flags heredados y la máxima prioridad deAppOptions.provider. Un perfil activo de/connectse restaura entre proyectos aunque la capa de confianza solo defina MCP/plugins/skills; solo unproviderraíz real en la capa de proyecto/explícita o una anulación de endpoint selecciona el adaptador heredado para esa ejecución. La cobertura usa hogares y proyectos temporales con credenciales ficticias e incluye reinicio tras/model; no consulta configuración ni credenciales reales del usuario. El formulario de/connectacepta pegado normal y bracketed paste por fragmentos, permite editar URL con cursor y mantiene los secretos enmascarados fuera del historial y del transcript. Hay tres plugins dedicados adicionales, publicados como paquetes independientes desde el monorepo alisio-plugins e instalables conalisio install npm:@alisio/plugin-...(ya no son integrados de Alisio):@alisio/plugin-deepseekusa el endpoint oficial por defecto, descubre metadatos de contexto/salida/modalidades/capacidades y admite Chat Completions y Responses;@alisio/plugin-opencodeintegra Console/Zen con referenciasopencode/<id-del-modelo>; y@alisio/plugin-opencode-gousaopencode-go/<id-del-modelo>. Ambos OpenCode consultan catálogos sin autenticación y envían Bearer,user-agent: alisio/<versión>y unx-opencode-sessionopaco y estable en inferencia. Cada producto conserva su propio mapa exacto documentado para Responses, Chat Completions o Anthropic Messages; los IDs futuros desconocidos se ocultan y fallan cerrados. Zen filtra explícitamente Gemini nativo y System One porque no satisfacen el contrato actual del agente de texto. El selector muestra el proveedor propietario, y cada cambio persistido inicia una sesión nueva para aislar datos de continuación por producto, endpoint y protocolo. El proveedor genérico sigue disponible.
Núcleo, límites y almacenamiento
- Núcleo propio: streaming, tool loop, validación de entradas, límites de turnos/tiempo/contexto, presupuesto de tokens reportados, cancelación y eventos versionados.
- Límite de turnos SUAVE (
limits.maxTurns, por defecto100): al alcanzar el tope, la ejecución termina constatus: "turns-exceeded"y eventorun_turns_exceeded(datos con el tope) en lugar de fallar: el transcript hasta ese punto se conserva íntegro, la TUI muestra un aviso amable (no un error), la siguiente ejecución en la misma sesión continúa donde quedó, y un subagente que agota su tope entrega su informe parcial comocompletedcon el marcadorturnsExceeded(nuncafailed). Los topes duros reales siguen siendo el presupuesto de tokens (maxTokens) y el tiempo de espera (timeoutMs), sin cambios de semántica. - API compatible con OpenAI configurable: Chat Completions y Responses, modelo/URL/clave, ausencia de autenticación y diferencias de parámetros de tokens.
- SQLite: conversación autoritativa, eventos, journal de herramientas, estado de plugins, bloqueo de sesión y recuperación conservadora de efectos inciertos.
- Contratos de la fase 0 de
alisio serve(aditivos,schemaVersionsigue en1): el SDK tipa cada evento que emite el runner (RunEventType,RunEventDataMap,KnownRunEvent,EphemeralRunEventType/isEphemeralRunEventType);RunEventganaeventIdopcional (elevents.seqglobal persistido, ausente entext_delta/reasoning_delta/tool_progress) ycorrelationIdopcional;SessionStore.eventdevuelvenumber | void;RunOptionsaceptarunIdycorrelationId;turn_completedañadedurationMsyttftMs.UiBlockgana los kindsdiff,terminal,mermaid,math,json,test-resultsyprogress(lista enUI_BLOCK_KINDS) con fallback de texto en la TUI y en la proyección de texto del núcleo; un kind desconocido o mal formado se muestra como JSON etiquetado en lugar de fallar. El SDK añade los tipos del protocolo web v1 (ServerFrame,PendingApproval,PendingInteraction,CommandDescriptor,SessionUiStatus,BlobRef,ApiError), como borrador sin servidor aún.Attachment.datasigue siendo obligatorio: las subidas por hash viajan comoBlobRefy el host las resuelve adata(fase 1). - Persistencia v4 y catálogo de comandos (fase 1 de
alisio serve): migración aditiva v4 deSQLiteStore(tablasruns,workspacesyblobs; columnas nulables ensessions,eventsytool_calls; índice único parcial(session, request_id)); métodos opcionales deSessionStore(beginRun,endRun,runByRequest,runs,messagesPage,eventsPage,interruptRuns); el runner registra cada ejecución (también TUI y headless) y marca las llamadas a herramientas conrun_id, nombre, efecto y tiempos;createApplicationmarca comointerruptedlas ejecuciones de procesos muertos al arrancar (junto ainterruptStale(), que se invoca en el constructor deChildSessions).BlobStore(app.blobs) guarda adjuntos por SHA-256 en<state home>/blobs/sha256/<aa>/<hash>y resuelve unBlobRefa unAttachmenten base64 verificado.CommandCatalogyBUILTIN_COMMANDSen@alisio/coredescriben los comandos de barra (integrados, plugins, plantillas, skills) con superficies y modo de ejecución; la TUI toma de ahí su lista y resolución y delega/toolsy/sessions. La lógica pura del agente activo vive en@alisio/core(agents/active.ts); la CLI la reexporta.
Herramientas, AGENTS.md y skills
- Herramientas locales: lectura, escritura/edición con hash, ripgrep (con mensaje de instalación por plataforma si
rgfalta, en las herramientas y enalisio doctor), procesos, shell y Git. - AGENTS.md según la convención agents.md: global
<config>/AGENTS.md(conAGENTS.override.mdque lo reemplaza); recorrido desde la raíz hasta el cwd con un archivo por directorio (AGENTS.override.md>AGENTS.md>AGENT.mdcomo alias heredado >CLAUDE.mdsolo concontext.claudeMdFallback); orden raíz → cercano con cabecera "el más cercano gana; las instrucciones explícitas del usuario prevalecen"; archivos anidados adjuntados de forma perezosa una vez por sesión y archivo; límite total de 32 KiB conservando los más cercanos.Agente.mddeja de leerse. - Agent Skills:
.agents/skills,.alisio/skillsy.claude/skillsdesde el cwd hasta la raíz (solo proyectos de confianza), rutas configuradas,~/.agents/skillsy<config>/skills, y plugins; el proyecto prevalece con aviso y ganador determinista; validación de nombres según la especificación (el desajuste con el directorio solo avisa); profundidad ≤ 5 y ≤ 2000 directorios; catálogo progresivo, activación y recursos. Gestor TUI/skillscon búsqueda, orden por nombre/origen/tokens aproximados, viewport acotado y navegación completa; metadatos seguros de origen/alcance/propietario, anulaciones de proyecto atómicas con efecto inmediato y skills de plugins bloqueadas por su ciclo de vida. - Presupuesto de tokens proporcional:
limits.maxTokenses opcional; por defecto 8 × ventana de contexto (entre 400k y 8M) o 1M si la ventana es desconocida.
Subagentes
- Plugin integrado
subagents(@alisio/plugin-subagents, desactivable): definiciones de agentes en Markdown + YAML con precedencia CLI > proyecto >.agents/agents(convención especulativa) > compatibilidad.claude/agentsy.opencode/agent(s)> usuario > plugins > integrados (general,explore,plan); herramientastask,task_status,task_waitysend_message; sesiones hijas persistidas con padre, profundidad, estado y uso (migración 3); contexto nuevo por hijo; límites de profundidad (se retiratask), concurrencia, cola acotada, turnos, tiempo y tokens; permisos solo restrictivos con aprobaciones que suben a la TUI con la ruta del agente; cancelación en cascada con SIGTERM y SIGKILL tras 5 s; hijos interrumpidos al reiniciar y reanudables contask_id; notificaciones de tareas en segundo plano; escrituras en paralelo en git con worktree por subagente (merge/discard), escritura serial o directorio compartido; panel de árbol de agentes con navegación por flechas y vistas de solo lectura.
Plugins, extensiones y MCP
- Categorías de plugins: la unión
PluginCategorydel SDK se amplió amemory,subagents,search,tools,security,analytics,mcp,storageyui(másmodel-providerymethodology-harness), validadas por esquema en el host. Los integradosmemoryysubagentsdeclaran su categoría:/pluginslos agrupa bajo encabezados propios en lugar de "General" y la vista de detalle los lista, sin cambios en la pantalla de inicio ni en el conteo de proveedores. La documentación de plugins (EN/ES) lista las categorías aceptadas con su significado. - Puntos de extensión genéricos:
api.sessions,api.ui.panel/select/open/interactive,api.resources.agents/list,ToolDefinition.concurrent, contexto de sesión en comandos. - Plugins locales y manifiestos de directorio: herramientas, comandos, eventos, contexto, skills, estado y desregistro/cleanup.
- MCP oficial v2: stdio, Streamable HTTP, herramientas, recursos, prompts y cierre. Configuración global + proyecto de confianza (o global +
--configexplícito), combinación por nombre, forma canónicamcp.serversy alias compatiblemcpServers; conexión diferida bajo--allow-mcpen usos no TUI o mediante consentimiento explícito válido solo para la sesión TUI actual; bloqueo absoluto con--read-only. Consentimiento global persistentemcp.allow(booleano en la configuración de usuario, solo capa global): al iniciar con permiso concedido (--allow-mcpomcp.allow:true), los servidoresenabledse auto-conectan como si se pulsara Conectar en cada uno, con fallos por servidor, saneados y no fatales al arranque (expuestos comomcpStartupFailuresy avisos de la TUI/headless); el proyecto nunca puede concederse consentimiento (mcp.allowde capa seleccionada se ignora);--read-onlyprevalece y bloquea también concesión, recuerdo y revocación. En/mcps, el diálogo de consentimiento ofrece "Conceder solo para esta sesión" o "Conceder y recordar (global)"; la escritura global es atómica y conserva campos no relacionados; una acción "Revocar consentimiento MCP global" limpiamcp.allowy elimina el permiso de ejecución desconectando los servidores. Gestor TUI/mcpsagrupado por origen real, con estados, detalles saneados, catálogo y anotaciones de herramientas; distingue activación configurada, permiso de ejecución, conexión y herramientas cargadas. La conexión/reconexión registra nombres semánticos seguros comomcp_devforge_time_diffpara el siguiente turno, limpia al desconectar y persiste la activación de forma atómica en la forma y archivo que definieron el servidor. El modelo conserva la decisión de llamar una herramienta; el nombre semántico mejora, pero no garantiza, la selección automática. - Herdr custom: reportes de lifecycle, sesión y herramientas de comunicación entre agentes.
CLI, runtime y empaquetado
- CLI interactiva/headless, JSONL, reanudación, configuración y diagnósticos.
- Runtime Node-first: Node.js >=22.16 (mínimo verificado: 22.13–22.15 incluyen
node:sqlitesin FTS5; 22.16.0 funciona) y compatible con Bun. Sin APIsBun.*:node:sqlite(en ambos runtimes),node:fsynode:child_processdetrás de la capa de runtime; elExperimentalWarningde SQLite se filtra de forma específica sin ocultar otros avisos. - Monorepo publicable:
@alisio/sdk(contrato, sin dependencias),@alisio/core(núcleo embebible),@alisio/plugin-memory,@alisio/plugin-openai-compatible,@alisio/plugin-subagentsyalisio(CLI/TUI, registro de plugins integrados). Los plugins dedicados de proveedores (DeepSeek, OpenCode Console/Go) se publican desde el monorepo alisio-plugins. Build contscadist/(JS +.d.ts),publishConfig.exportssin fuentes, changesets para versionado y publicación con provenance. Nuevoscripts/publish.ts(pnpm publish): empaqueta y publica en orden dependiente seguro (sdk → core → plugins → cli), con--all/--package <nombre>obligatorios,--version(bump atómico),--dry-run(sin efectos),--build/--no-build, el mismo chequeo de fugas quepack:checksobre el manifiesto empaquetado,==> nombre@versiónpor paquete y detención clara ante fallos sin publicar en silencio el resto; documentado en Publishing. - Plugins como paquetes npm (
--plugin nombreoplugins: ["nombre"]), resueltos desde el proyecto y luego las raíces globales; exigen la keywordalisio-plugin. - Instalador de plugins npm (
alisio install npm:<paquete>[@<versión>]y herramienta del hostplugin_installpara el agente): validación de la especificación antes de cualquier red (charset seguro, sin../rutas absolutas, prefijos desconocidos rechazados), instalación GLOBAL connpm install --prefix <config home>/pluginsy persistencia del NOMBRE npm en el arraypluginsde<config home>/config.json(escritura atómica, campos no relacionados conservados, sin duplicados); idempotencia con--updatepara refrescar a@latest; confirmación previa de scripts de ciclo de vida en terminal interactiva, rechazo accionable en headless/--jsonsin--yes(o--trust-plugin) y rechazo absoluto con--read-only; salida de npm fallida saneada (sin tokens/secretos) con el comando de reintento exacto; la misma rutina compartida para CLI y herramienta, con runner inyectable para pruebas sin red (npm falso por shim de PATH o inyectado).plugins listmuestra los paquetes instalados junto a los plugins de archivos/directorios, y la resolución de paquetes añade<config home>/pluginsa las raíces globales (incluida su variantenode_modules/). - Binario autónomo opcional (
pnpm build:binary, Bun) y workflow de release con binarios linux-x64/arm64, darwin-x64/arm64 y windows-x64,SHA256SUMSe instaladorscripts/install.sh.
Plantillas, pantalla de inicio y TUI
- Sitio de documentación bilingüe (VitePress, inglés y español) desplegado en GitHub Pages.
- Plantillas de prompts: Markdown con frontmatter YAML (
description,argument-hint,requires), sintaxis$ARGUMENTSy$1..$9; fuentes con precedencia documentada (integradas < plugins víaresources.prompts< usuario<config>/prompts< proyecto.alisio/promptssolo si es de confianza) y diagnósticos (prompt_override,prompt_conflict,prompt_invalid,prompt_shadowed). Cada plantilla es un comando slash con descripción en/helpy autocompletado; se envía como turno normal (streaming, herramientas, aprobaciones) y la conversación muestra/nombre args(persistido comodisplay). Headless:alisio run "/init ..."con la misma sintaxis. /initintegrado: analiza el repositorio y crea o actualizaAGENTS.mden el sitio (edit_fileconexpectedHash), solo con hechos verificados; exige escritura (rechazo claro con--read-only; aprobación en la TUI). El comandoalisio setup(antesalisio init, renombrado para no confundirse con/init) sigue creando la configuración y sugiere/init.- Registro genérico de puntos de extensión (
api.extensions.register, campo declarativoextensions) con los puntos tipadosmascotystartup-screen: resolución determinista (prioridad, id del plugin, orden de registro), diagnósticosextension_conflicten/statsyplugins doctor, desregistro al desactivar el plugin. - Pantalla de inicio con mascota reemplazable: mascota original de Alisio (espíritu de nube del viento alisio) con variantes Unicode, ASCII y compacta; pantalla por defecto con secciones reutilizables, disposición lado a lado o apilada, consejos rotativos deterministas; fallback seguro ante proveedores que fallan, devuelven basura o tardan; salida saneada y recortada. Se muestra como primer bloque de la TUI y en stderr (si es TTY) en modo
--no-tui; nunca enrun,--json,--quiet,--no-banner,CIni sin TTY. Ejemplo publicable enexamples/plugins/custom-mascot/. - TUI con
@earendil-works/pi-tui0.87.1 (pantalla alternativa, renderizado diferencial): cabecera con modelo/host/permisos, conversación con Markdown, bloques de herramientas con spinner, duración, vista previa y diff de ediciones, barra de contexto y tokens (ventana real del modelo cuando el catálogo la expone;~9.9k / ?honesto cuando no), comandos/help /model /compact /stats /clear /sessions /resume /tools /exitcon autocompletado (incluido/skillscon sugerencias del catálogo por nombre o descripción), interrupción con Esc y aprobación interactiva dewrite/process.--no-tuiconserva el modo readline. - Renderizado Markdown mejorado: títulos en cian brillante y negrita, tablas con columnas alineadas al ancho (un
transformprevio al parseo normaliza las filas separadoras con guiones em/en/de caja a---ASCII conservando los dos puntos de alineación, sin tocar bloques de código delimitados/sangrados ni---sueltos), y código en bloque sangrado (2 espacios) con resaltado de sintaxis por línea SIN dependencias para ts/tsx/js/jsx/json/ bash/sh/python/yaml/css/html/md (palabras clave, cadenas —incluidas f-strings de Python y llaves de JSON—, comentarios de línea y de bloque con continuidad multilínea, números, llamadas a funciones, atributos/asignaciones y variables$; el tokenizador sanea ANSI y caracteres de control de la entrada, nunca los reemite, y conNO_COLORdevuelve líneas planas). Tono base gris para el cuerpo víadefaultTextStyle. Copiar respuestas: pista atenuada⎘ copy · /copy([copy] · /copyen terminales sin Unicode) bajo cada respuesta completada (nunca durante el streaming), y atajoc/ycon la entrada VACÍA que copia la última respuesta como texto crudo (misma ruta que/copy, mismo avisoCopied (<herramienta>)). - Resultados enriquecidos de herramientas (MCP "Nivel 0"): el conector deja de aplanar
CallToolResult/ReadResourceResultaJSON.stringify. El SDK amplía el contenido deToolResultde forma aditiva —{type:"text"} | {type:"image", mimeType, data} | {type:"ui", block: UiBlock}contextResultintacto— dondeUiBlockes unión discriminada propia de Alisio (tabla{columns, rows}con cabeceras como nombre alternativo y filas de objetos indexadas por columna, clave-valor de escalares planos, árbol{nodes: {label, children?, meta?}}, código y markdown); los tipos MCP nunca se filtran al SDK, el mapeo vive en el adaptador de core (packages/core/src/mcp/rich.ts). Heurística conservadora: solo las formas verificadas destructuredContento de partes de texto parseables como JSON se pliegan a bloques; el resto conserva literalmente el comportamiento anterior (incluidoJSON.stringifypara resultados sin contenido reconocible). SIEMPRE se añade la proyección de texto canónica de cada bloque/imagen (parte de texto adicional; render compacto de tablas como markdown,clave: valor, árboles con├─/└─, código cercado) y marcadores[image: mime (N bytes)]en lugar de bytes base64: el modelo, la compactación (clampMessage/reduceMessagesToBudgetrecortan SOLO partes de texto;serializeForSummaryresume solo la proyección), los proveedores (el runner aplicatextProjectionantes deprovider.stream) y todas las rutas headless (run,resume <id> "prompt",--json,--no-tui,TERM=dumb,NO_COLOR) siguen viendo texto únicamente. El store persiste las partes ricas conendCall/append(JSON completo, sin migración) y el TUI las recupera conSessionStore.callResultsolo para sus eventos (tool_completedsigue emitiendopreviewde texto; JSONL sin cambios). TUI:TranscriptItemde herramienta conui?/image?;ToolBlockrenderiza tablas alineadas con celdas que se ajustan al ancho, clave-valor en dos columnas, árboles con├─/└─/│(ASCII|-/- /|sin Unicode), bloques de código con el mismohighlightCode, markdown con el renderizador habitual, e imágenes en línea vía el componenteImagede pi-tui 0.87.1 (deteccióngetCapabilities), con marcador atenuado[image: mime WxH]si no hay soporte o hayNO_COLOR;itemsFromHistoryreproduce los bloques al reanudar. Documentado endocs/tools.md,docs/es/tools.md,docs/tui.mdydocs/es/tui.md`. - Presentación de herramientas y razonamiento en la TUI (estilo Claude Code/OpenCode): nombres legibles (
read_file→Read File,mcp_*→MCP · …, acronyms como HTTP/API en mayúsculas); razonamiento terminado plegado a+ Thought · 2.9s(expandible, acotado a 40 líneas; la duración aproxima el intervalo de pensamiento desde las marcas de tiempo de los deltas); lotes de llamadas consecutivas del mismo tipo agrupados en una fila al terminar (✓ Read File — 3 reads · 60ms, verbo de tipo para lotes mixtos,✗si falló alguna; las llamadas en ejecución/esperando aprobación siguen siendo filas individuales con su spinner); salida de comando larga plegada con… N more linesy línea finalCommand exited with code 0./code 1.derivada con seguridad del JSON de vista previa (run_process/shell/search_text, tolerando el sufijo<instructions>de las lecturas); alternar conxen la entrada vacía o clic en la fila de cabecera (el clic sintetizado por pi-tui no rompe copiar-al-seleccionar). Modelo puro enstate.ts(humanizeToolName,toolKindOf,groupToolEntries,FoldCandidateyexitCodeOf) sin imports de terminal; componentes con caché de líneas por versión/ancho/plegado (los frames de reloj no re-renderizan filas sin cambios), sincronización por claves estables con reutilización de componentes y orden preservado; eltool_starteddel runner lleva eleffectdel registro (aditivo) para agrupar por capacidad real en vivo. Documentado endocs/tui.mdydocs/es/tui.md. - Comando
/settings(/prefs): menú de ajustes estilo OpenCode — filas de dos columnas (preferencia + valor actual), filtro escribiendo (nombre/clave/categoría/descripción), contador(n/total)y pie con la descripción de la fila resaltada; Enter/Espacio cambia el valor, Esc sale. Dieciséis ajustes REALES y conectados, persistidos de forma atómica en la configuración global de usuario (setConfigValue, puerta de entrada acotada a un conjunto de claves validado con el propio esquema) y aplicados en caliente:compaction.auto/threshold/keepTurns/maxOutputTokensylimits.maxTurns/maxOutputTokens/maxContextChars/timeoutMs(víaAgentRunner.applySettings, surten efecto en la siguiente ejecución;limits.timeoutMsse muestra en segundos y se persiste en milisegundos),context.claudeMdFallback/maxBytes(víaProjectContext.update, siguiente turno),websearch.provider(enum, muta el objeto compartido que la cadena de búsqueda lee en cada llamada;nativerequiere un proveedor que lo soporte),pluginHooks.timeoutMs(víaPluginHost.applyTimeoutSettings, siguiente hook),tui.paddingX(padding del editor, inmediato),tui.contentPaddingX(inset horizontal de cada lado del contenido del transcript, inmediato; acotado en terminales estrechas para no colapsar la columna de contenido),tui.skillSlashCommands(alterna las entradasskill:<id>del autocompletado, inmediato) y el consentimientomcp.allowpor el caminorememberGlobalMcpConsent/revokeGlobalMcpConsent. Bajo--read-onlytodo se muestra en solo lectura. Las filas inferiores navegan a/model,/connect, compactación,/plugins,/skills,/mcpsy/stats; las puntuales (compactar, estadísticas) avisan y reabren la lista. La lista es HONESTA: no se ofrecen ajustes inexistentes (telemetría, Mermaid, modo dirección, doble Esc, transporte automático, idle HTTP, tema, niveles de aviso, confianza persistida, ventana de contexto global...) — ver docs/tui.md./settingsse bloquea mientras un turno está en curso, igual que los gestores a los que da acceso. - Skills en el autocompletado de comandos: cada skill efectiva del catálogo aparece como entrada de primer nivel
skill:<id>(marcador de ámbito[u]/[p]/[c]/[l], pista de estado deshabilitada/bloqueada/sombreada y descripción recortada, como en/skills), de modo que/ski…o el propio nombre de la skill las muestran y elegir una insertaskill:<id>. El enrutado/skill:<id>existente no cambia, ni el completado de argumentos de/skillsni/resume. El ajustetui.skillSlashCommands(por defectotrue) oculta SOLO esas entradasskill:<id>cuando está desactivado: el gestor/skillsy su autocompletado de argumentos siguen disponibles, y el proveedor del editor se reconstruye al guardar, sin reinicio. - Dependencia con parche (
patchedDependenciesenpnpm-workspace.yaml,patches/): pi-tui 0.87.1 filtraba los nombresskill:*quitando el prefijo al emparejar (diseñado para/branch…), lo que hacía imposible que/ski…mostraraskill:branch-pr; el parche empareja el nombre completo. Cualquier comando que reconstruyanode_modules(instalación limpia/CI) aplica el parche solo sipnpm installcorre conpatchedDependenciespresente. - Versión en tiempo de ejecución por paquete: metadatos de plugins,
user-agentpor defecto y el cliente MCP leen la versión de su propiopackage.json(con la inyecciónALISIO_PACKAGE_VERSIONpara binarios autónomos ydevcomo último recurso); la cabecera de la TUI muestra Alisio Code y, junto con el banner y--version, resuelve la versión del CLI caminando hacia arriba hasta elpackage.jsonde@alisio/alisio-code(límite de 8 niveles), sin literales que puedan desincronizarse de la publicación.
Compactación, plugins y memoria
- Compactación de contexto en el núcleo (manual y automática por umbral), con resumen del proveedor actual, emparejamiento de llamadas/resultados preservado y persistencia transaccional (migración 2: columna
messages.compacted). - Plugins: puntos de extensión aditivos en
PluginAPI(apiVersion 1): hooks de compactación (beforeCompactcon campos JSON extra en la misma llamada,afterCompactcon inyección de contexto e informe),session.onStart/onEnd,model.completeagnóstico del proveedor,ui.statusy metadatos de comandos. El host aplica timeouts y aísla fallos. Registro de plugins integrados (packages/cli/src/builtin.ts) con ruta de confianza, nombres sin prefijo y efectointernal; desactivables por configuración o--disable-plugin. - Gestor TUI
/plugins: catálogo filtrable de plugins integrados y externos con nombre, descripción, categoría/origen seguro y estados activo, inactivo, fallido o reinicio necesario. El estado de reinicio desaparece al volver al estado original del runtime; no permite desactivar proveedores retenidos por ninguna sesión enrutada viva. Persiste anulaciones en.alisio/config.jsoncon escritura atómica y conserva campos no relacionados. Los cambios se aplican tras reiniciar (no hay descarga parcial en caliente); los externos requieren confianza y confirmación explícita. Protege el proveedor de modelo activo y los recursos de sesión vivos. - Plugin integrado
memorycon persistencia local: SQLite + FTS5 trigram, BM25 con recencia y accesos, upsert portopic_key, deduplicación con ventana de 15 minutos, borrado lógico, redacción de<private>, fijadas, línea temporal, prompts recientes, resúmenes de sesión; siete herramientasmemory_*, protocolo en el prompt, inyección presupuestada al iniciar sesión, extracción de memorias y archivo del checkpoint en la compactación, resumen al cerrar la TUI y comando/memory. El núcleo no contiene referencias a memoria. - TUI: copiar al seleccionar (ratón capturado) con adaptador de portapapeles aislado y respaldo OSC 52 declarado como no verificable;
/copy; comandos de plugins enrutados genéricamente; estado de plugins en la barra y en/stats. - Pegado de texto e imágenes en la TUI: el pegado de texto (incluido multilínea) ya llega como una única edición atómica gracias al propio componente del editor (pegado con corchetes), sin fragmentarse ni enviar antes de tiempo; los pegados largos colapsan en un marcador
[paste #N ...].Ctrl+Vadjunta la imagen del portapapeles del sistema (PNG/JPEG/GIF/WebP, detección por cabecera; sin reimplementar el sniffing ni las secuencias Kitty/iTerm2, se reutilizan las de pi-tui) yCtrl+Rquita la última adjuntada; se muestran sobre el editor como miniatura en línea cuando la terminal es capaz, o como una línea compacta ([N] image/png WxH, X.X KB) en caso contrario. Límite de 5 MB por imagen y 4 adjuntos por mensaje, aplicado en la TUI (no en@alisio/core). Se envían como partes de contenido de visión compatibles con OpenAI (image_urlen chat,input_imageen Responses) junto al SDKopenai; no hay comprobación previa de si el modelo admite visión — se intenta siempre y un rechazo del proveedor se muestra como un error en línea normal. Los adjuntos se persisten con el mensaje (Message.attachmentsen@alisio/sdk); la compactación describe una imagen resumida solo por tipo MIME y dimensiones, nunca reenvía ni conserva sus bytes en el checkpoint.
Modelo y enrutamiento por sesión
- Cambio de modelo por sesión (
AgentRunner.setModel,sessions.model); el proveedor acepta un modelo por petición./modely/modelscomparten un selector global que agrega solo perfiles creados con/connect, identifica la propiedad proveedor/modelo, marca la pareja activa, aísla fallos de catálogo por perfil y persiste un cambio en una sesión nueva. La configuración heredada queda fuera del selector. CatálogoGET /modelscon ventana de contexto y metadatos estructurados cuando el proveedor los informa, tokens en caché (prompt_tokens_details.cached_tokensoprompt_cache_hit_tokens) y razonamiento visible (reasoning_content/reasoning_text) solo para mostrar. - Enrutamiento explícito proveedor/modelo por sesión: API programática sin credenciales para listar, resolver y crear/cambiar sesiones; sintaxis canónica
proveedor/modeloe IDs desnudos solo si son únicos. Las definiciones de agentes ytask.modelusan el mismo resolvedor. Padre e hijos pueden ejecutar proveedores configurados distintos en paralelo sin mutar el valor global por defecto; cada sesión conserva proveedor, modelo, continuación opaca e ID OpenCode propios. Los destinos ausentes, ambiguos o con catálogo no disponible fallan antes de inferencia con orientación segura. - Lockfile y versiones fijadas; Biome, TypeScript, Vitest y CI Linux con Node 22.16, 22.x y 24.
Agente activo y effort de razonamiento
- Sistema de agente ACTIVO de la sesión principal: integrados
build(por defecto, sin persona añadida: comportamiento idéntico al actual) yplan(planificador de solo lectura que inyecta su prompt de sistema y limita la ejecución a lecturas víaRunOptions.policy/approvals), más las definiciones principal-capaces publicadas por el plugin de subagentes (mode: primary|all, incluidas las de--agents <json>). El prompt de sistema del agente se anexa por ejecución en el runner (RunOptions.instructions, el mismo seam que la persona base), y el cambio de agente se aplica desde el siguiente prompt, sin reiniciar la sesión. - Comando de TUI
/agents: selector navegable con nombre, descripción y marcadores de actual/por defecto/solo lectura; elegir persisteagents.active(escritor atómico de la capa de usuario) y cambia el modelo de la sesión por el enrutamiento existente de proveedores cuando el agente declaramodel(sesión nueva, como/model). Los verbos de gestión de tareas de subagentes (list/open/cancel/kill/resume/merge/discard/defs) siguen enrutándose al plugin de subagentes cuando/agentsrecibe argumentos. El mismo agente activo se aplica en los modosrun,resumey--no-tui(prompt y acotación de solo lectura; el effort es solo de la TUI). - Effort de razonamiento:
ModelInfo.effort(niveles ydefaultLevel) ya descubierto por el proveedor DeepSeek; contrato aditivoreasoningEffortenCompletionRequesty en la petición deModelProvider.stream, que el runner propaga porRunnerOptions/RunOptionsy DeepSeek envía comoreasoning_effort(Chat Completions) oreasoning.effort(Responses); los demás proveedores lo ignoran. Comando/effort [nivel]: sin argumento, selector sobresupportedLevelscon eldefaultLevelmarcado (y una entrada para limpiar el valor guardado); con argumento, valida y persisteagents.effort. El nivel efectivo se resuelve contra el modelo ACTIVO: si el guardado no lo soporta el modelo nuevo, se usa eldefaultLevelcon un aviso único (un solo fallo de catálogo degrada a "sin effort" con honestidad). - Barra de estado bajo el editor y cabecera:
agente · modelo · proveedor · effortcon tres colores distintos (modelo cian negrita, proveedor magenta, effort amarillo); el segmento de effort solo aparece cuando el modelo activo anunciasupportedLevels; en terminales estrechos se descartan primero las piezas de menor prioridad. Lógica de diseño pura y probada (identityParts/fitIdentityParts).
Agentes del usuario (ventana Agentes y /agents)
- Almacenamiento portable y compartido con otras herramientas: un archivo Markdown por agente con frontmatter YAML, en dos ámbitos: proyecto
<workspace>/.agents/agents/<id>.mdy global~/.agents/agents/<id>.md(os.homedir()). El frontmatter sigue la convención de subagentes de Claude Code/OpenCode (name= id en minúsculas con guiones,description,model,toolsopcional,mode: allal crear); el cuerpo son las instrucciones (prompt de sistema). Los ajustes propios de Alisio viven bajo una sola clavealisio:que las demás herramientas ignoran (displayName,reasoning.effort,reasoning.summary,text.format.type,text.verbosity,createdAt,updatedAt). La edición usa el modelo de documento deyaml: claves desconocidas y comentarios escritos por otra herramienta se conservan; solo se tocan las claves de Alisio. La escritura es atómica (archivo temporal +rename). Servicio compartido en el núcleo:AgentDefinitionService(app.agentDefinitions), usado por el servidor web y la TUI. - Almacenamiento previo de Alisio: se LEEN ambos, sin migración. El descubrimiento del plugin de subagentes sigue leyendo
.alisio/agents,<config>/agents,.claude/agents,.opencode/agent(s)y los demás orígenes de siempre, y ahora también~/.agents/agents(orden: proyecto.alisio/agents> proyecto.agents/agents> compatibles ><config>/agents>~/.agents/agents>~/.claude/agents…). El gestor de agentes solo ESCRIBE y lista los dos ámbitos.agents/agents. - Listado: une los dos ámbitos con insignia Proyecto/Global; si el mismo id existe en ambos, el de proyecto prevalece y se marca "Reemplaza al global" (y el global "Reemplazado por el de proyecto"). Al crear se elige el ámbito: Proyecto por defecto con un workspace abierto; Global sin workspace. Mover un agente entre ámbitos no está implementado.
- Recarga en caliente: tras crear, editar o borrar, el servicio pide al registro de agentes del plugin de subagentes que vuelva a descubrir los archivos (verbo
/agents reload, también disponible a mano), y comprueba que la ruta guardada esté cargada (estadodefinitionsdel plugin). La respuesta llevalive:truesolo si el proceso en ejecución ya la usa. En el servidor web se recarga cada workspace abierto y se emitecatalog_changed(agents) para que los clientes refresquen. Sin el plugin de subagentes, con un workspace no confiable (proyecto) o si otro origen de mayor prioridad tapa el archivo,liveesfalsey la web muestra "Agente guardado. Reinicia Alisio para usarlo." (la TUI imprime el aviso equivalente). No hay vigilante de archivos: los cambios hechos fuera de Alisio se recogen al abrir la lista de agentes (web y/agentsen la TUI) o con/agents reload. - Confianza desde la web: un workspace no confiable guarda los agentes de proyecto pero no los carga; el editor y el menú ⋯ del workspace ofrecen "Confiar en este workspace…" (ver Servidor web). Si el workspace se confió antes de tener recursos de proyecto, al guardar su primer agente de proyecto se reabre la aplicación y el agente queda cargado (
live: true); si no, el workspace pasa a mostrarse como no confiable en la barra lateral. - Plantillas compartidas en el núcleo (
AGENT_TEMPLATES, servidas porGET /api/agents/templatesy usadas por la TUI): Code Reviewer, Test Writer, Refactoring Assistant, Documentation Writer, Security Auditor, Bug Triage & Debugger, Migration Assistant, Research Agent, Customer Support Agent, DevOps Assistant, Meeting Assistant y Analytics Agent (sin duplicados), cada una con instrucciones y ajustes por defecto. - Creación asistida: una llamada sin herramientas del modelo ACTIVO (
app.provider, víacompleteText) escribe o refina nombre, descripción, instrucciones y ajustes sugeridos. Guía de autoría: si existe una skill descubierta llamadacreate-agent(oagent-creator) se usa su cuerpo; si no, la skill incluidabundled:create-agent(packages/core/src/agents/create-agent-skill.ts: rol, objetivo, alcance, uso de herramientas, restricciones, formato de salida, ejemplos). En el repositorio no había ninguna skill de ese tipo (soloalisio-publish), así que se usa la incluida. La respuesta indicaguidanceygeneratedBy. - Capacidades: derivadas solo de los metadatos del catálogo (
ModelInfo.effort.supportedLevels,inputModalities/modalities,capabilitiescon claves comoreasoning,tools,structured_outputs,verbosity). Unfalseexplícito oculta/deshabilita la opción. Si el proveedor no declara nada (caso habitual delGET /modelsOpenAI-compatible), se ofrece todo de forma permisiva (known: false, la UI lo indica). Al cambiar de modelo los ajustes no soportados se eliminan o se sustituyen (fitAgentSettings). - Web: entrada "Agentes" justo encima de "Ajustes" en la barra lateral (y su icono en la barra contraída). Reutiliza el armazón del modal de Ajustes. Lista "Tus agentes" + "Plantillas"; editor con migas
Agentes › Nuevo agente/<nombre>, pestañas Configuración/Sesiones, dos columnas (una en pantallas estrechas): definición (nombre, descripción, instrucciones, ámbito), modelo (lista real deGET /api/agents/modelscon etiquetas de capacidad), formato de texto, esfuerzo, verbosidad y resumen según capacidades; botón "Guardar" activo solo con nombre válido, modelo y cambios sin guardar. Columna derecha: peticióncurla la API propia de Alisio (POST /api/agents,PUT /api/agents/:id) con números de línea, resaltado y copiar, y los pasos de inicio marcados con estado real (definición guardada, workspace, sesión con el agente, sesión con título = hubo un mensaje); se descartan con × (guardado enlocalStoragecon try/catch). "Crear con Alisio" muestra el diálogo "Construyendo con Alisio" (progreso indeterminado, tiempo transcurrido, Cancelar que aborta la petición y la llamada al modelo); un error se muestra sin tocar el formulario. Un agente nuevo generado se guarda y se ofrece "Probarlo en un chat nuevo" (por defecto) o "Seguir en el editor". Pestaña Sesiones: sesiones con ese agente (GET /api/sessions?agent=<id>) y botón para iniciar una. - Comandos y cambio de agente: cada agente cargado (integrados, proyecto, global) es el comando
/agent:<id>; el espacio de nombresagent:evita colisiones con comandos integrados, de plugins, plantillas yskill:<id>(si una fuente de mayor prioridad ya tiene ese nombre exacto, el comando del agente se omite). En la web,/agentssin argumentos abre un selector con búsqueda (subcadena en id, nombre y descripción), insignia Proyecto/Global/Integrado, el actual marcado ybuildcomo vuelta al agente predeterminado; la insignia del agente en la cabecera abre el mismo selector. La activación guarda el agente en la sesión y se aplica desde el SIGUIENTE mensaje (nunca a mitad de una ejecución): instrucciones, modo de solo lectura y esfuerzo por defecto. El modelo del agente se aplica en la misma sesión solo si pertenece al proveedor de la sesión; si no, el chat conserva su modelo y lo dice ("inicia un chat nuevo con el agente"). Precedencia del esfuerzo: el de la sesión (web) o/effort(TUI) gana; si no, el del agente. - TUI:
/agentsabre el selector (filtro por subcadena en nombre y descripción, insignias, actual, predeterminado) con las acciones "+ Crear agente…" y "✎ Gestionar…";/agents new [descripción](con descripción: creación asistida directa),/agents templates,/agents manage,/agents edit|delete <id> [project|global]; el resto de verbos (list,defs,reload,open,cancel…) siguen yendo al plugin de subagentes. El editor es una lista de campos (ámbito al crear, nombre, descripción, instrucciones —una línea,\npara saltos—, modelo de la lista configurada, esfuerzo/resumen/verbosidad/formato según capacidades, "✦ Refinar con Alisio", guardar, cerrar). "Construyendo con Alisio" se muestra en la línea de pistas y Esc lo cancela. Tras crear: "▶ Probarlo en una sesión nueva" (por defecto) o seguir en el editor./agent:<id>activa un agente (como el selector, a nivel globalagents.active).
Preguntar al usuario (ask_user_question)
- Herramienta
ask_user_question(núcleo, no un plugin) y comando/ask: el modelo —o un subagente hijo, ya que la condición es que exista alguna UI interactiva enlazada, nunca cuál sesión pregunta— puede hacer de 1 a 4 preguntas de opción múltiple (2-4 opciones, como mucho unarecommended); falla rápido con un error estructurado en modo headless en vez de bloquear. Contrato SDK aditivo:Question/QuestionOption/AskQuestionsRequest/AskQuestionsResultyui.askQuestions, conToolContext.labelnuevo para atribuir la llamada a la sesión hija que pregunta (igual queApprovalRequest.label). Panel de la TUI por pasos (una pregunta a la vez, para legibilidad en terminales estrechas), con navegación ↑↓ (con vuelta, igual queSelectList), alternar con →/Espacio en preguntas de selección múltiple, Enter confirma y avanza, ←/Retroceso corrige una respuesta anterior y Esc omite solo la pregunta actual (decisión documentada: el comportamiento exacto de Claude Code no se pudo verificar de forma independiente, así que se eligió el menos sorprendente). Resumen final compacto en la conversación con etiquetas truncadas con…. Una única cola interactiva (InteractiveQueue, FIFO, sin prioridad de la raíz sobre los subagentes) serializa aprobaciones, elselectde/modelyask_user_question, de modo que nunca hay más de un aviso en pantalla aunque pregunten varios subagentes a la vez; una pregunta retirada (sesión cancelada mientras estaba en cola o en pantalla) se descarta con un aviso, sin dejar un panel obsoleto. El panel de árbol de agentes gana un estado de presentación esperando (distinto de en ejecución) para las sesiones bloqueadas en una pregunta o una aprobación.
Herramientas de red y CLI
- Herramientas de red (núcleo, no plugins):
webfetch(url, format?, timeout?)lee una URL comomarkdown/text/html(conversión conturndown+@mixmark-io/domino, sin navegador ni jsdom), rechaza contenido no textual, limita a 5 MiB y trunca el texto embebido a 20 000 caracteres conservando el texto completo en.alisio/cache/webfetch/<hash>.<ext>(legible conread_file).websearch(query)resuelve un proveedor en orden: una extensiónwebsearchregistrada por un plugin (nuevo punto de extensión, mismo mecanismo quemascot/startup-screen, con reintento seguro y diagnóstico ante un proveedor que falla) →websearch.providerconfigurado (searxng,duckduckgo-instant,duckduckgo-html,tavily,brave,serpapi,native) → una instancia pública de SearXNG por defecto.duckduckgo-htmlimplementa la búsqueda web real sin clave como un raspado tolerante del endpointhttps://lite.duckduckgo.com/lite/(solo confía en anclasresult-linkcuyohrefpasa por el redireccionador//duckduckgo.com/l/?uddg=, decodifica la URL destino y las entidades HTML, y toma el fragmento de la celdaresult-snippetsiguiente). Modonative: añade la herramienta nativa del proveedor (websearch.nativeToolType, por defectoweb_search) a la petición de la Responses API en vez de implementar la llamada HTTP propia; exigeprovider.apiMode: "responses"y no registra la herramientawebsearch.execute(code)("Code Mode") ejecuta un fragmento JS en el módulovmde Node que solo puede invocar otras herramientas ya registradas víacallTool, respetando su propio efecto/permiso (sin poder disparar una aprobación nueva), acotado a 10 s y 20 llamadas anidadas, sin poder llamarse a sí mismo. Nuevo efectoexternalreutilizado parawebfetch/websearch(antes solo cubría MCP/Herdr/plugins) con su propio flag--allow-externaly aprobación interactiva en la TUI igual quewrite/process;executereutiliza el efectoprocessexistente. Se decidió deliberadamente no implementar una herramienta de navegador (browser): el usuario descartó esa pieza por ahora al no tener un navegador adjunto como el de opencode desktop. - Comando
alisio setup(renombrado desdealisio init, sin alias): sigue escribiendo el mismo.alisio/config.jsonde ejemplo;alisio initahora se comporta como cualquier subcomando desconocido (comportamiento propio de commander, sin trato especial).
Confianza de proyecto y diagnóstico
- Confianza de proyecto por directorio, de una sola vez: al iniciar la TUI (nunca en modo headless
run/resume "prompt"/--json, que siguen exigiendo--trust-project/--configexplícitos porque ahí no hay nadie a quien preguntar) en un directorio con recursos de proyecto (.alisio/config.json,.alisio/plugins,.alisio/agents,.agents/agents,.alisio/skillso.alisio/prompts) y sin--trust-project/--config, se pregunta una vez con una explicación clara de lo que implica confiar (puede redirigir el endpoint/clave del proveedor y carga plugins/agentes/skills). La decisión se guarda en<ALISIO_STATE_HOME>/trust.jsonpor ruta de workspace resuelta (realpath), junto con un hash SHA-256 del contenido de.alisio/config.json; un archivo modificado desde la última decisión vuelve a preguntar en vez de mantener la confianza en silencio. Un directorio sin ningún recurso de proyecto nunca recibe la pregunta.--trust-project/--configde una ejecución nunca se persisten como si fueran una concesión interactiva. Nuevos comandosalisio trust list/alisio trust revoke <path>. - Verificación en código (no solo lectura) de que write/process/external ya preguntaban por defecto en la TUI antes de esta tarea (para write/process, desde una función previa;
externalse añadió en la propia tarea anterior de esta sesión): la TUI siempre pasa un manejadorapproveacreateApplicationsalvo con--read-only, así que sin ningún flag el efecto ya se ofrece y pregunta en cada llamada — no estaba «simplemente no disponible» como se asumió al plantear esta tarea. Confirmado con una prueba de integración end-to-end (permission-truth-tableenfixtures/scenarios.ts) que ejercita las tres combinaciones (preguntar / permitir sin preguntar / denegar sin ofrecer) para los tres efectos, y con una verificación real en pseudo-terminal (ver más abajo) que muestra el selector «Allow write_file (write): x.txt?» sin ningún flag. El trabajo funcional nuevo de esta tarea para permisos es, por tanto, únicamente la confianza de proyecto y el aviso de modelo sin configurar; el comportamiento de aprobación en sí no cambió. alisio doctory la pantalla de inicio avisan explícitamente cuando el modelo resuelto está vacío o es el marcadorYOUR_MODEL_IDque escribealisio setup, en vez de dejar que el primer turno real falle contra un modelo inexistente.
Servidor web (alisio serve)
- Paquete nuevo
@alisio/server(solonode:http, sin dependencias de runtime nuevas, sin WebSocket), cargado poralisio serveconimport()dinámico:alisio,alisio runy la TUI no lo cargan (comprobado por traza de resolución de módulos). - Seguridad local: enlace a
127.0.0.1por defecto (--allow-remoteobligatorio para otra dirección), token de arranque de 256 bits por proceso canjeado una vez por una cookieHttpOnly; SameSite=Strictcon nombre por puerto (alisio_session_<port>) y otro secreto, comprobación deHostyOrigin,Content-Type: application/jsonen peticiones con efecto, cabeceras de seguridad y CSP que incluye el hash de los scripts en línea del build web./api/health(sin autenticación),/api/ready,/api/metrics; logs JSON por línea en stderr (ALISIO_LOG_LEVEL). WorkspaceHost: unaApplicationpor workspace (raíz git delrealpath), creada al usarse, desalojo LRU al llegar a--max-workspaces,503 workspace_limitsi todos están ocupados, cierre tras 10 minutos de inactividad y confianza leída del almacén de confianza de la terminal. La web también puede concederla o retirarla (POST /api/workspaces/:wid/trustconconfirmed: true, desde el menú ⋯ del workspace o el editor de Agentes, tras una confirmación que explica lo que se desbloquea): guarda la misma decisión que la pregunta de la terminal (trust.json, ligada al hash de.alisio/config.json) y reabre la aplicación del workspace; se rechaza con ejecuciones activas (409 runs_active), con--read-onlyy con--trust-project/--config. Un workspace conocido cuya carpeta ya no existe (o no es accesible) responde404 workspace_missingal abrirse (crear sesión, prompts, archivos) en lugar de un 500, yGET /api/workspaceslo marca conexists: false; la web lo atenúa y desactiva sus sesiones nuevas. Verificado con tests deWorkspaceHosty de rutas; no se vigila el disco en vivo (el estado se refresca al recargar la lista o al fallar una apertura).- Workspaces archivados (migración v5 aditiva:
workspaces.archived_atnullable).PATCH /api/workspaces/:wid {archived}inserta la fila si el workspace solo se conocía por sus sesiones (también si su carpeta ya no existe), cierra la aplicación inactiva y responde409 runs_activecon ejecuciones activas;GET /api/workspaceslos oculta salvo con?archived=true|all(misma semántica queGET /api/sessions);POST /api/sessionsresponde409 workspace_archived;POST /api/workspacescon la misma carpeta lo desarchiva. Las sesiones no se tocan y siguen legibles. La web tiene un menú de acciones por workspace (Fijar, Archivar / Desarchivar) y Mostrar archivados también muestra los workspaces archivados, al final. - "Abrir un workspace" con diálogo nativo:
POST /api/workspaces/pickabre en el escritorio del servidor zenity/kdialog/yad (Linux/BSD, conDISPLAYoWAYLAND_DISPLAY),osascript choose folder(macOS) o PowerShell-STAconFolderBrowserDialogy, como alternativa,Shell.Application.BrowseForFolder(Windows), conexecFilesin shell y argumentos fijos (título y carpeta inicial validada como argumentos o variables de entorno). Un diálogo a la vez (409 picker_busy), 5 minutos de límite (cuenta como cancelado), cancelación por código de salida 1/5/252,-128de osascript o salida vacía de PowerShell.capabilities.nativePickerycapabilities.folderBrowseren/api/health;ALISIO_NATIVE_PICKER=0lo desactiva. Alternativa garantizada en todos los sistemas:GET /api/fs/dirs(solo nombres de subdirectorios, migas de pan construidas por el servidor, unidadesA:\–Z:\en la raíz de Windows,404/403en lugar de 500). Ambos solo con enlace loopback; con--allow-remotesolo queda escribir la ruta. - Barra lateral: el botón de archivados y la búsqueda recalculan la lista en cada render (antes un
useComputedsolo se actualizaba al cambiar la señal de la barra, no el estado local), y los menús de acciones de sesión y de workspace se posicionan respecto a la ventana para que el contenedor con scroll no los recorte. El explorador de carpetas muestra como máximo 2 000 subcarpetas por directorio (aviso de truncado); para directorios mayores queda "Escribir una ruta". - Control de parada del compositor rediseñado: cuadrado relleno con esquinas redondeadas y un arco de progreso que gira alrededor del botón mientras la ejecución está activa (estático con
prefers-reduced-motion). - Sesiones (crear, listar, leer, modificar; sin borrado: se archivan), mensajes, eventos y ejecuciones paginados; prompts idempotentes por
requestId(índice único deruns), texto encolado durante una ejecución,session_busy,session_locked, cancelación y compactación manual.RunSchedulercon semáforo global--max-runsy cola FIFO. - Stream SSE multiplexado por pestaña con snapshot por sesión construido y registrado en el mismo tick, frames durables con
id=eventId, deltas agrupados cada 33 ms, latido cada 15 s y cola acotada por cliente que termina el stream conresyncsi se desborda. ApprovalBridgeeInteractionBridgecon fallo cerrado (denegar/cancelar sin observadores tras 30 s, al abortar o tras 10 min), primera respuesta gana, aprobaciones de sesiones hijas en su sesión raíz. Presets de permisos por sesión con techo en los flags de arranque; cada sesión recibe su propio objetoRunOptions.policy, así que "permitir para la sesión" no se filtra a otras sesiones del workspace.- Apagado ordenado (
SIGINT/SIGTERM: 503, runs cancelados, aprobaciones denegadas, streams cerrados, apps cerradas con los topes existentes; segundoSIGINTfuerza la salida) y reconciliación de runs al arrancar. - Rutas de la fase 3 en
@alisio/server:GET /api/commands?session=|workspace=(elCommandCatalogcompartido filtrado a la superficieweb) yPOST /api/sessions/:sid/commands({requestId, name, args?}→CommandOutcome: los comandoscorese ejecutan en el servidor; las plantillas de prompt, las skills y/askdevuelven un prompt expandido que el cliente envía;/helplista los comandos web;/effortse guarda por sesión en vez de cambiar el ajuste global;/modely/compactresponden409 session_busydurante una ejecución;requestIdrepetido →{duplicate:true}),GET /api/sessions/:sid/models(SessionModels: catálogo del proveedor de la sesión con niveles de esfuerzo,unavailablesi no se puede listar),GET /api/sessions/:sid/context(SessionContextUsage:runner.estimateContextycontextBudget) yGET /api/sessions/:sid/export(JSONL: eventos durables en orden deevents.seqy después una línea{"type":"message"}por mensaje, incluidos los compactados). Las ejecuciones web aplican ahora el agente de la sesión (sessions.options.agent, si no el agente activo de la app): instrucciones por ejecución y, si es de solo lectura, política sin efectos y sin aprobaciones. Las sesiones sin título muestran su primer prompt (una línea, 60 caracteres), derivado al leer y nunca persistido. - Preguntas laterales
/btw [pregunta](servicioSideQuestionsdel núcleo, expuesto comoapp.sideQuestionsy en elCommandCatalogpara tui, web y api): una llamada sin herramientas al modelo y proveedor de la sesión con su historial activo serializado como transcripción de texto (como la compactación; se descartan los mensajes más antiguos para caber enmaxContextCharsy en la ventana del modelo) más una instrucción de responder de forma concisa sin actuar. No escribe enmessages,tool_calls,runs,eventsni en el uso de la sesión y no toma el bloqueo de la sesión, así que funciona durante un run con su propioAbortSignal. El historial (20 por sesión) vive enplugin_statebajocore:btw, sin migración. TUI: panel en la ranura de selectores (nunca en la transcripción) con "Thinking…", Esc cancela/cierra,←/→recorren respuestas,↑/↓desplazan; modo readline: imprime la respuesta oUsage: /btw <question>. Servidor:GET|POST /api/sessions/:sid/btwyPOST .../btw/cancel(también cancela al cerrarse la petición; código nuevo409 cancelled, fallos del proveedor502 provider_unavailable). Web: el compositor intercepta/btwy abre un panel flotante (chunk diferido) con estado pendiente y Cancelar, respuesta en Markdown, tokens, copiar y navegación2/5. - Interfaz web
@alisio/web(paquete privado; su build viaja dentro de@alisio/serverendist/web, copiado porscripts/copy-web.mjstrastsc): Vite 8 + Preact 10 +@preact/signals, CSS Modules con tokens en custom properties, sin Tailwind ni librerías de componentes. Reductores puros (store/transcript.ts,sessions.ts,pending.ts,composer.ts), cliente SSE con lotes porrequestAnimationFrame, backoff con jitter 0,5→10 s y reapertura trasresync; sidebar de workspaces y sesiones, cabecera con título editable, insignia de agente y preset y descarga del log; transcript con burbuja de usuario, filas compactas de herramientas y razonamiento, Markdown incremental con bloques congelados (tokens demarkedrenderizados como nodos Preact, sininnerHTML), código resaltado de forma diferida con shiki (motor JavaScript, 14 gramáticas cargadas bajo demanda); compositor con paleta/, historial, preset, modelo + esfuerzo, anillo de contexto y enviar/detener; panel de aprobaciones e interacciones que ocupa el lugar del compositor; temas oscuro/claro/sistema sin parpadeo, EN/ES yprefers-reduced-motion.pnpm web:size(también dentro depnpm pack:check) exige JS inicial ≤ 90 KB y CSS inicial ≤ 20 KB gzip. - Fase 4 (renderizadores de desarrollo, explorador y adjuntos):
- Renderizadores web diferidos por kind (un chunk cada uno, resolución probada en T-17 con fallback para kinds desconocidos y cargas fallidas):
diff(parser de parche unificado propio y diff de líneas Myers para bloques con solobefore/after; unificado o lado a lado, hunks plegables, navegación por archivos),terminal(parser SGR propio: 16 colores con tokens por tema, 256 colores y truecolor, negrita/tenue/cursiva/subrayado/inverso; descarta el resto de escapes;\rresuelto; últimas 2 000 líneas con "mostrar todo"; streaming desdetool_progressparashell/run_process),json(árbol plegable, profundidadcollapsedDepth, copia de valor y de JSONPath, > 1 000 hijos truncados),test-results(resumen, filtro de fallos, error yarchivo:línea) yprogress. Sin dependencias nuevas. - Núcleo:
write_file/edit_fileañaden un bloquediff(parche unificado deruntime/diff.ts, ≤ 200 KB cortado por línea) yshell/run_processun bloqueterminal(stdout y luego stderr, últimos 256 KB, código de salida y duración) después del texto actual, que sigue siendo la primera parte y lo único que ve el proveedor (textProjection). La TUI ignora esos dos bloques en esas cuatro herramientas (richPartsOf(result, toolName)) para no duplicar la salida que ya muestra. - Servidor:
GET /api/workspaces/:wid/tree|file|diffyGET /api/sessions/:sid/changes(routes/files.ts:safePathdel núcleo más comprobación delrealpath; páginas de 1 000 entradas; vista previa ≤ 2 MB conX-Truncated/X-File-Sizeydownload=1; imágenes por bytes mágicos, SVG como texto, binarios como descarga;gitconspawnsin shell, 2 s, sin fsmonitor, diff externo ni textconv).POST /api/blobs(cuerpo binario ≤ 10 MB, únicoPOSTsin JSON; PNG/JPEG/GIF/WebP por bytes mágicos con dimensiones;201 BlobRef) yGET /api/blobs/:hash(tipo guardado,inline,private, max-age=31536000, immutable), sobre unBlobStoredel servidor con la misma raíz que el de cada workspace. - Web: panel lateral con Archivos (árbol perezoso), Cambios (diff frente a
HEAD) y Vista previa (código, Markdown, JSON, imagen; mencionar como@ruta); rutas de filas de herramientas, enlaces Markdown relativos y cabeceras de diff abren el archivo. Compositor con adjuntos (+, pegar, arrastrar; miniaturas, subida inmediata, quitar; máx. 8 por mensaje) y miniaturas en las burbujas. Pestaña Trayectoria (eventos durables agrupados por run, cargados por páginas e incrementalmente) y línea de estadísticas bajo el compositor (último run; totales de la sesión al pasar el ratón; caché "—" si el proveedor no la informa). JS inicial 56,3 KB y CSS 7,8 KB gzip.
- Renderizadores web diferidos por kind (un chunk cada uno, resolución probada en T-17 con fallback para kinds desconocidos y cargas fallidas):
- Fase 5 (renderizadores ricos y gestión):
- Web:
mermaid(11.17.2) ymath(KaTeX 0.18.9) como chunks diferidos. La vista de Mermaid (chunk pequeño) importa Mermaid y DOMPurify (3.4.16) solo cuando el bloque entra en pantalla y nunca mientras se genera;securityLevel: "strict",htmlLabels: false, renders serializados y reinicialización al cambiar de tema; el SVG pasa por DOMPurify (perfil SVG) antes del DOM. Fuente/diagrama, zoom, exportar SVG, pantalla completa y copiar. KaTeX va con su vista, su CSS y sus fuentes (servidas como archivos: la CSP no admitedata:en fuentes), contrust: false,throwOnError: false,maxExpand: 500,maxSize: 50y macros por bloque; el HTML se sanea con DOMPurify. Un error de Mermaid o de KaTeX muestra la fuente y el mensaje (renderMath/renderMermaidpuros, T-17). Markdown: extensión demarkedpara\( … \)en línea (tokeninlineMath);```math,```latexy párrafos$$ … $$siguen siendo display;$suelto no se interpreta (T-16). - Servidor (
routes/management.ts,routes/providers.ts):GET/PATCH /api/plugins,GET/PATCH /api/skills,GET/PATCH /api/mcpyPOST /api/mcp/consent,GET /api/agents,GET/PATCH /api/settings,GET /api/providers,PUT /api/providers/:profile,PUT/DELETE /api/providers/:profile/credentials,POST /api/providers/:profile/activateyGET /api/models, todas con?workspace=o{workspace}(id opaco o ruta absoluta) y validación propia. Los cambios emitencatalog_changed(commands,plugins,skills,mcp,models,agents). MCP nunca devuelve comando, argumentos ni URL. Las credenciales son de solo escritura: las respuestas llevan{configured, source: "file"|"env", tail?}contail(…XYZ) solo para secretos de 16 caracteres o más. - Decisión (plugins):
setPluginEnabledescribe el ajuste del proyecto y la app en curso quedarestart-required; el servidor recicla laApplicationdel workspace (WorkspaceHost.recycle) en cuanto no tiene runs (en el momento o cuando termina el último run, vía elonChangedelRunScheduler). Los workspaces no confiables marcan todos los plugins como no gestionables (su.alisio/config.jsonse ignoraría). Las skills se aplican en caliente. - Decisión (proveedores, P-01): activar un perfil responde
409 runs_activesi el workspace tiene runs en cola o en curso; si no,activateProviderProfilecambia la app del workspace y guarda el perfil como activo enproviders.json. Otros workspaces abiertos mantienen su proveedor. - Núcleo (aditivo):
ProviderSettingsStore.saveProfile/setCredentials/deleteCredentials/ credentialStatusymaskSecret;settableSettings()(tipo y opciones de cadaSettableSettingKey);PluginHost.toolsOf(plugin)(dueño de cada tool registrada, también para built-ins);grantMcpRuntimePermissionyrememberGlobalMcpConsentaceptan la fuente"interactive-web"(P-07). - Web: el modal de Ajustes (chunk propio, cargado al abrirlo) con General (idioma y ajustes del agente), Modelos (perfiles, credenciales con campos de contraseña que se vacían al guardar, activación con modelo), Plugins (pestañas, buscador, contador y tarjetas de
image2.png), Skills, Servidores MCP (confirmación explícita antes de conceder acceso), Presets de agente (usar en la sesión abierta) y Apariencia, más "Abrir archivo de configuración" con rutas copiables. Las filas de tools de plugins muestran el nombre de la tool y el plugin como etiqueta. JS inicial 60,6 KB y CSS 7,5 KB gzip (antes 56,3 y 7,8); chunk demath75 KB gzip (KaTeX incluido), Mermaid 32 KB de motor más ~138 KB de núcleo y un chunk por tipo de diagrama.
- Web:
Modos de permisos, /reload y /changelog (fase 1)
Primera fase de specs/archive/alisio-modes-goal-background-v1.md (decisiones del propietario confirmadas el 2026-10-02). Las fases 2 (revisión del plan), 3 (tareas en segundo plano, migración v7) y 4 (/goal, migración v8) están implementadas (ver las secciones siguientes).
- Núcleo (aditivo):
PermissionModeen el SDK yPermissionPresetInfo.mode; la tabla únicaPERMISSION_MODE_TABLE(permissions/modes.ts:askpregunta por todo;autopermite escrituras y pregunta por procesos y externo;fulllo permite todo) de la que derivan los presets del servidor (host/presets.ts) y la TUI;AgentRunner.setPolicy(muta en sitiowrite/process/ external, nuncaanalysis);cycleableAgents/nextAgent(orden establebuild,plan, resto por nombre);reloadApplication(reload.ts: guarda de inactividad, validación de la configuración, construcción de la aplicación nueva junto a la vigente, intercambio y cierre de la antigua; informe por área); el parser puro del changelog (changelog/) y los datos embebidos (changelog/data.ts, generado desdeCHANGELOG.mdporscripts/changelog-data.ts;pnpm buildlo regenera y un test falla si están desactualizados). Comandospermission(aliaspermissions),reloadychangelogen el catálogo (TUI y web). - TUI: Shift+Tab cicla los agentes principales desde el listener de entrada (corre antes del Editor); bloqueado durante un turno y con picker, autocompletado o panel de agentes abiertos; no cambia el modelo ni crea una sesión (a diferencia del selector
/agents)./permissionabre un menú de cinco filas (ask, auto, full access con confirmación «no es un sandbox», Status, Manage saved permissions…) o aceptaask|auto|full|status; el modo inicial se deriva de los flags solo como etiqueta (sin flagsask,--allow-writeauto, los tres flagsfull, otra combinacióncustom,--read-onlybloqueado) y se muestra siempre (cabeceramode:y barra de estadomode:)./reloadreconstruye la aplicación (la variableapppasa alety se re-enlazan catálogo de comandos, proveedor, cachés de modelos, UI interactiva y autocompletado; se reaplica el modo elegido)./changelog [version]abre un panel desplazable y tras una actualización una sola línea avisa (tui-state.jsonjunto a la base de datos, escritura atómica y tolerante a fallos). - Servidor:
WorkspaceHost.reload(construir primero, intercambiar después;recyclecierra primero),POST /api/workspaces/:wid/reload(409 runs_activecon ejecuciones;400con la configuración rota),GET /api/changelog?version=&lastSeen=, los comandospermission,reload(exclusivo) ychangelogenPOST /api/sessions/:sid/commands, yGET /api/agentsen orden estable. Mismas reglas de autenticación, Host y Origen que el resto de/api. - Web: selector
Agente: <nombre>en el compositor y atajo Mayús+Tab con el foco en el textarea (preventDefault; ignorado con IME, paleta abierta u otros modificadores; anuncioaria-live), popover de permisos con los tres modos, estado y «Gestionar permisos guardados…», confirmación al elegir acceso total, diálogo de/changelog(chunk propio) y aviso discreto tras actualizar (localStoragecontry/catch). Textos en EN y ES.
Pestaña Memory de la web y vistas de datos de plugins
Especificada en specs/archive/alisio-web-memory-tab-v1.md. Solo web; todo aditivo.
- SDK:
api.views?.register({ id, description, params?, handler })(ViewDefinition,ViewContext,ViewParamsError) y los códigos de errorview_failed(502),view_timeout(504) yview_too_large(502). El miembro es opcional en el tipo: un plugin lo detecta conapi.views?.registery sigue funcionando en un core anterior. - Core (
PluginHost): registro por plugin con validación (id, descripción, esquema de primitivos sin$ref, duplicados) que se deshace con el plugin,viewsOf(plugin)yrunView(Ajv con conversión de tipos desde la cadena de consulta y valores por defecto;ViewRunError). - Servidor:
GET /api/sessions/:sid/views/:plugin/:view?<params>(routes/plugin-views.ts) con la misma autenticación, Host y Origen que el resto de/api; sesión existente con workspace existente, solo plugins habilitados (también un plugin deshabilitado que sigue corriendo porrestart-required), 404 uniforme para plugin/vista desconocidos o deshabilitados, máximo 16 parámetros de 512 caracteres, timeout de 5 s conAbortSignal, respuesta de 1 MiB como máximo, errores del plugin genéricos (view_failed) y sin parámetros ni contenido en los logs.ServerOptions.viewsbaja los límites en pruebas. - Plugin de memoria: vistas
records,summaryycontext(soloSELECT; paginación por cursor keyset sobrepinned, updated_at, id; búsquedaLIKEescapada) y la tablainjected_context(migración 101) donde el plugin recuerda lo que inyectó al empezar cada sesión. - Web: selector genérico «plugin X habilitado» (
store/plugins.ts, alimentado por la misma petición deGET /api/pluginsque ya se hacía), pestaña Memory solo con el plugin habilitado y vuelta a Conversación si se deshabilita, controlador con descarte de respuestas obsoletas (store/memory.ts), componente, textos y estilos en un chunk perezoso. Para recuperar presupuesto de bundle los cargadores perezosos deapp.tsxpasaron a un único componenteLazyy la pestaña Trayectoria también se carga de forma perezosa.
Tareas en segundo plano (fase 3 de la especificación de modos, goal y tareas)
Especificada en specs/archive/alisio-modes-goal-background-v1.md (§8). Todo aditivo.
- Migración v7 (solo hacia delante, idempotente):
background_tasks(estadosqueued,running,stopping,succeeded,failed,cancelled,lost;abort_origin,error_code,owner_pid,pid, ruta del log relativa a la carpeta de estado,delivered_at) ysession_goals(sin lector hasta la fase 4). - Core:
BackgroundTasks(background/service.ts) sobre el ejecutor de procesos compartido (runProcess, ahora cononSpawnykillProcessTree/isProcessAliveexportados; no hay un segundo spawner). Cada cambio de estado es un compare-and-set en SQL; el origen de la parada (user,model,timeout,shutdown) se conserva aparte de su efecto (cancelledpara el usuario, el modelo y el cierre;failedcontimeoutpara el watchdog). Logs en<estado>/tasks/<sesión raíz>/<id>.logcon escrituras síncronas y lecturas por offset (las lecturas nunca parten un carácter UTF-8); tope por log (cabeza + marcador + cola de 32 KiB al terminar). Límites:tasks.maxPerSessionpor sesión raíz y 16 por proceso. Cierre ordenado (close()): se rechazan tareas nuevas, se aborta, se espera y se mata el grupo de procesos de las que no mueren; además un manejador síncrono deexitmata los grupos si el proceso sale sinclose(). Recuperación al arrancar (recover(), y el servidor al iniciar):lostsolo para tareas cuyoowner_pidya no existe (nunca las de este proceso ni las de otro proceso vivo). - Herramientas
bg_run,bg_list,bg_output,bg_stop(tools/background.ts), las cuatro con efectoprocess(decisión del propietario: lo que no puede iniciar un proceso tampoco lo lee ni lo detiene; la especificación original teníabg_listcomoread). Una sesión solo ve las tareas de su árbol.bg_outputdevuelvenext_offsetyeof; una lectura del modelo (y unbg_list) de una tarea ya terminada la marca como entregada. - Notificación (
background/notify.ts): un mensaje por lote (ventana de 2 s), como mucho uno por sesión cada 10 s y 6 cada 10 min, reintento cada 2 s y al quedar libre la sesión, reclamación dedelivered_atcon compare-and-set antes de despertar y liberación si el despertar no arrancó. Solo para sesiones raíz y tareas que terminaron por sí solas (éxito, fallo, timeout). El despertar lo inicia el host: la TUI porrunPrompt(wakeDecision), el servidor porRunSchedulercon el id de peticiónbg-<id>;runner.enqueueno despierta una sesión inactiva (verificado). - Espejo de subagentes:
subagentTaskslee el panel (PanelProvider.nodes) del plugin de subagentes, que ya existía; no hizo falta publicar estado nuevo desdeplugin-subagents(la especificación lo suponía) y su gestor, herramientas y<task-notification>no cambian. - Retención:
TaskJanitor(patrón del janitor de análisis: temporizador sin referencia, 30 s tras el arranque y después cada día; nunca con--read-onlyni:memory:). - Configuración: sección
tasks(enabled,maxPerSession,maxRunMs,maxOutputBytes,retentionDays; esta última solo global y con aviso si un proyecto la define), todas ajustables con etiquetas EN/ES en la web y las principales en/settings. - Servidor:
GET /api/sessions/:sid/tasks,GET …/tasks/:tid/output?offset=&limit=,POST …/tasks/:tid/stop, códigotask_not_found(404), frametasks_changed. Un workspace con tareas vivas no se expulsa por inactividad, un reciclaje diferido espera a que terminen y/reloadresponde409. - TUI:
/tasks(tui/tasks.ts: reductor puro yTasksView) con lista, salida en vivo, parada con confirmación;/reloadse rechaza con tareas vivas.alisio rundetiene las tareas que siguen corriendo al terminar y lo dice por stderr. - Web: pestaña Tareas del Dock (chunk perezoso con su store y sus textos), contador en la pestaña y punto en el icono del panel, aviso cuando una tarea termina por sí sola,
/tasks. Para recuperar presupuesto de bundle el diccionario del idioma no activo (español) pasó a un chunk que se carga antes del primer render o al cambiar de idioma; el bundle inicial bajó de ~89,9 KB a ~78 KB gzip.
Objetivos de sesión /goal (fase 4 de la especificación de modos, goal y tareas)
Especificada en specs/archive/alisio-modes-goal-background-v1.md (§9). Todo aditivo; decisiones del propietario del 2026-10-02 aplicadas tal cual.
- Migración v8 (aditiva,
ALTER TABLE): la tablasession_goalsde la v7 no tenía lector y le faltaban las columnas del runtime (goal_id,detail,summary,blocked_run,continuations,inflight,last_run_id,kickoff_sent,owner_pid); una base que ya estaba en v7 (las alphas de la fase 3) las recibe sin perder filas. - Máquina de estados pura (
goal/machine.ts):active,paused,blocked,budget_limited,complete, cada una con un código de motivo cerrado (created,resumed,user_paused,user_interrupt,model_complete,model_blocked,policy_denied,run_error,token_budget,max_turns,max_wall,no_progresscon detallerepeated_replyono_tool_turns,restart). Usuario: crear, pausar, reanudar (pausa o bloqueo), editar y borrar en cualquier estado, cambiar el presupuesto en cualquier estado (subirlo o quitarlo reactiva unbudget_limited(token_budget); bajarlo a lo gastado detiene uno activo). Modelo: solocompleteoblocked, solo con el objetivoactivey con evidencia. Sistema: topes y disyuntores, interrupción del usuario, reinicio; nunca reanuda. Reanudar trasmax_turns/max_wallconcede una asignación más de ese tope. - Servicio (
goal/service.ts,app.goals) sobreGoalStore(goal/store.ts): cada cambio es lectura-modificación-escritura en una transacciónBEGIN IMMEDIATE;epoch(cambia con estado, objetivo y presupuesto; los totales no lo mueven) másgoalIdpermiten que un cliente diga lo que vio (expect) y recibaconflictsi otro lo cambió. Un goal de otra pestaña, ventana o proceso que comparte la base no se pisa. - Una lista ordenada de bloqueadores (
goal/blockers.ts,goalBlocker): no activo, desactivado, ejecución en curso o continuación reclamada, entrada del usuario sin enviar (TUI), permiso pendiente, pregunta o revisión de plan pendiente, agenteplan, tareas en segundo plano vivas. Son esperas, no estados;goalWaitingda la etiqueta de la UI. - Continuación idempotente:
next()comprueba los topes, aplica la lista y reclama una continuación con un compare-and-set (inflight= id de peticióngoal-<goalId>-<n>); un segundo disparo ve la reclamación y espera. El host la inicia como una ejecución normal (RunSchedulerconbeginRun(requestId)en el servidor;runPrompten la TUI), así que los ids de llamadas y los datos de continuación del proveedor no cambian. Prompts (goal/prompts.ts): contrato completo una vez (kickoff, con el objetivo citado como dato en<goal_objective>), después una pista corta y estable (la misma cadena cada turno: prefijo estable para la caché del proveedor), recordatorio tipo auditoría cada 5 turnos y avisos tras la segunda repetición o el segundo turno sin herramientas. - Presupuesto duro y acumulado:
tokens_usedsumausage.input + usage.outputde cada ejecución (el mismo recuento que el runner; sinusagese estima por caracteres). Cada continuación arranca conmaxTokens= lo que queda, por lo que el guardia del runner (antes de cada llamada a herramienta y al final de cada lote) detiene la ejecución; como el runner cuenta por petición, el gasto puede pasar del presupuesto como mucho una petición. Una ejecución que lanza (error, tope, timeout) conserva su uso (core/run-stats.ts, unWeakMapjunto al error), de modo que un tope de presupuesto esbudget_limitedy norun_error. No hay turno de cierre. «Turno» = una ejecución (kickoff, continuación o mensaje del usuario con el goal activo); el tiempo es el de las ejecuciones (incluye esperar una aprobación dentro de una ejecución, no la espera entre ejecuciones) y eltimeoutMsde cada continuación se acota a lo que queda. - Disyuntores: respuesta final idéntica (huella normalizada) y turnos sin herramientas; la primera ocurrencia se registra (
onLog), la segunda añade un aviso a la siguiente continuación y el límite pausa conno_progress.blockedexige el mismo informe en turnos consecutivos (goal.blockedRepeats) salvo evidenciadenied. Un fallo de ejecución bloquea con el error como motivo, salvo unRunTimeoutError(cuenta como turno sin herramientas). Esc/Detener/cancelar pausa (user_interrupt). - Herramientas
get_goalyupdate_goal(tools/goal.ts, efectointernal): opt-in (se añaden aOPT_IN_TOOLS; solo se ofrecen a las ejecuciones de una sesión con goalactivey agente distinto deplan; Code Mode no las ve), por lo que sus descripciones no cuestan tokens en el resto. El modelo no tiene herramienta para pausar, reanudar, editar, borrar ni cambiar el presupuesto. - Reinicio:
pauseOrphans(arranque de aplicación y de servidor) pausa conrestartlos goalsactivecuyoowner_pidya no existe (o es desconocido); nunca los de este proceso ni los de otro proceso vivo, y nunca reanuda. Idempotencia por ejecución:last_run_idevita contar dos veces una ejecución. - Configuración: sección
goal(enabled,maxTurns50,maxMinutes120,repeatedReplyLimit3,noToolTurnsLimit3,blockedRepeats2), todas ajustables con etiquetas EN/ES en la web y en/settings. No hay presupuesto de tokens por defecto (se avisa en la documentación). - Servidor:
GET|PUT|PATCH|DELETE /api/sessions/:sid/goal,POST …/goal/pause|resume, comando/goalenPOST …/commands(misma gramática que la TUI,parseGoalCommand;confirmpara reemplazar), framegoal_changed,goalen el snapshot SSE, códigosgoal_not_found,goal_conflict,goal_disabled.GoalDriver(host/goal.ts) es el único camino que inicia continuaciones: tras cada ejecución (RunSchedulerahora informa resultado, error y tiempos), al terminar la última tarea, al cambiar opciones de sesión (agente) y tras las acciones del usuario. - TUI:
/goaly su menú (tui/goal.ts:planGoalCommand, filas del selector,GoalBar), confirmación de reemplazo, continuación tras cadatask(), pausa con Esc,goal.*en/settings. - Web: barra del objetivo (chunk perezoso con su store y sus textos EN/ES), barra de progreso del presupuesto, botones Pausar/Reanudar/Editar/Borrar, filas de la paleta,
/goalcon confirmación; en el bundle inicial solo el signal y el manejo del frame (JS inicial 79,8 KB gzip de 90).
Decision Intelligence (fases 1 a 5 de la especificación)
Especificada en specs/alisio-decision-intelligence-v1.md. Todo aditivo y opcional: sin proveedor Alisio se comporta igual. Solo infraestructura: el paquete no incluye ningún proveedor ni ninguna función que consuma decisiones (los consumidores son especificaciones posteriores). Guía de usuario: Decision Intelligence.
- Contrato en
@alisio/sdk:DecisionProvider(select,boolean,ordinal;health?,activate?,deactivate?,decide),DecisionRequest,DecisionResponse,DecisionProviderError(not_ready,unavailable,timeout,invalid_response,internal),DecisionRequestError,DecisionStats, el helper purosummarizeDecisionEvents, la categoría"decisions", los eventosdecision_completedydecision_fallbackenRunEventDataMap,PluginAPI.decisions?,PluginAPI.paths?,PluginAPI.options?yToolContext.decisions?. Todos opcionales y detectables por presencia. - Núcleo (
packages/core/src/decisions/):DecisionRegistry(registro por plugin que se deshace al descargarlo),DecisionService(tryDecide,attempt,decide), validación de la petición (1 a 16 decisiones, 2 a 20 opciones, 2 a 12 niveles,statede hasta 16 KB y petición de hasta 32 KB, claves eidcon patrón fijo) y validación por clave de cada respuesta (el resultado parcial es válido; los rechazos se explican conlow_confidence,invalid,unsupportedomissing). - Tiempo límite impuesto por el núcleo (
decisions.timeoutMs, 1500 ms por defecto, resultado de la Fase 0) y disyuntor (3 fallos consecutivos abren 30 s; una sonda medio abierta). Cuentantimeout,invalid_response,internaly los errores sin tipo; no cuentannot_ready,unavailable, la baja confianza ni los rechazos por decisión. El núcleo nunca llama ahealth()en la ruta dedecide. - Fallback por decisión (
tryDecidedevuelvenullsolo ante fallo de infraestructura, desactivación o rechazo total); motivos:timeout,not_ready,unavailable,invalid_response,provider_error,circuit_open,unsupported,all_rejected. - Configuración: bloque
decisions(enabled,provider,timeoutMs,minConfidence,telemetry);providerytelemetryson solo globales (una capa de proyecto o--configque las defina se ignora con diagnóstico, sin fallo de arranque); las cinco claves son ajustables en vivo con etiquetas EN/ES en la web.pluginOverrides[id].options(solo global, hasta 8 KB, claves con patrón fijo), que alimentaapi.optionscomo instantánea congelada ensetup;setPluginEnabledconserva lasoptions.api.paths(state,config,cache) con modo0700en la primera lectura, bajo la misma raíz de estado que análisis y artefactos (respeta--db). - Ciclo de vida del proveedor activo:
activate()ydeactivate()se disparan al pasar a ser el activo (arranque, cambio dedecisions.provideren vivo, registro tardío) y al dejar de serlo; acotados por tiempo, serializados por proveedor, nunca fatales y nunca esperados en el arranque. Un fallo se guarda como error de ciclo de vida y se ve solo en/decisions. - Cierre acotado de plugins:
PluginHost.close()libera los plugins en paralelo, cada uno con su tope (pluginHooks.disposeTimeoutMs, 100 a 10000, por defecto 2000), y registra los fallos sin impedir el resto; el proveedor activo se desactiva antes.alisio rungestiona ahoraSIGTERMySIGHUPcomoSIGINT, así que unkilltambién ejecutadispose().discoverPluginssolo trata como plugin un directorio conalisio-plugin.json, porque<config home>/plugins/<id>es también dondeapi.paths.configguarda los archivos de un plugin. - Eventos y métricas:
ToolContext.decisionsligado a la ejecución (las llamadas anidadas de Code Mode no lo reciben); los eventos llevan solo metadatos (nuncastate, instrucciones ni etiquetas) y no se persisten contelemetry: false; métricas en memoria por sesión y por proceso. - Comando
/decisions(TUI y web, mismo texto Markdown) con estado, salud (límite propio de 1 s), capacidades, circuito, ajustes, error de ciclo de vida y métricas; sección «Decision Intelligence» en/stats(solo si hubo decisiones) calculada consummarizeDecisionEventsen las tres rutas (núcleo, TUI y web). En la web las estadísticas de decisiones van en el tooltip de los totales de la sesión. - Activación pedida por el plugin:
api.decisions.activate?(providerId, options?)(options.recommend === truemarca y preselecciona Yes en vez de No, sin saltarse la pregunta; opcional, detectable por presencia;DecisionActivationResultconactivated,already_active,other_provider_active,declined,needs_confirmation,disabled,unavailable). Solo acepta proveedores registrados por el propio plugin; nunca sobrescribe undecisions.providerdistinto; condecisions.providervacío pregunta una vez (Sí/No) por la vía interactiva del host y, sin superficie interactiva (alisio run), devuelveneeds_confirmation. Si se acepta, persiste solodecisions.provideren la configuración global con el mismo escritor atómico que los ajustes en vivo y aplica el cambio en caliente; si falla el guardado,unavailabley sin cambio en memoria. Límites: el texto de la pregunta está en inglés, no hay una confirmación por plugin recordada (cada llamada con la configuración vacía pregunta de nuevo) y no se añade ninguna frontera de confianza (un plugin confiable ya podía actuar con los permisos del usuario; la confirmación es la salvaguarda).
Smart Dashboard (fases 0 a 7 de la especificación)
Especificada en specs/alisio-smart-dashboard-v1.md (release previsto 0.4.0). Estado: la fase 0 está hecha, las fases 1 a 7 están implementadas y la fase 8 se midió el 2026-10-03 con deepseek-flash (resultados en docs/benchmark-dashboard.json; resumen en la guía de usuario). La puerta de §11.2 falla: B (15 ejecuciones, 1 repetición por dataset) redujo los tokens de salida un 28,5 % (exigido 50 %) y los turnos un 8,7 % (exigido 40 %), con 15/15 specs válidos y 0 avisos de lint; el modelo elige dashboard_generate en el 100 % de las ejecuciones (E7), pero sigue usando python_run después. C (5 ejecuciones, informativa): Laya respondió en 4/5. Pendiente: decisión del propietario sobre cómo continuar y la fase 9 (release). Guía de usuario: Smart Dashboard. El proveedor de decisiones refina solo el propósito (PROVIDER_DECISIONS en pack.ts): con Laya 0.3.24 real acertó el 90 % en el propósito y quedó cerca del azar en columnas y gráficos, así que las reglas deciden el resto.
- Herramienta
dashboard_generate(efectointernal): entrada{ datasetId, goal?, title?, locale?, sheet? }; construye un dashboard sin código ni llamada al modelo y lo publica comodashboard.htmlcon procedencia propia (generator,spec,planner,decisionProvider?,fallbacks). Erroresdataset_not_found,no_usable_columns,plan_failedyquery_failed, con la indicación de usarpython_run; un error no deja un artefacto parcial. Una consulta que falla elimina solo su componente y queda anotada en el resultado. - Pipeline puro en
packages/core/src/analysis/dashboard/: perfilador de columnas (rolestime,measure,dimension,identifier,booleanyunknown, calculados con las estadísticas de_alisio_columnssin una nueva pasada de ingesta), candidatos (medidas ≤ 8, dimensiones ≤ 8, tiempo ≤ 4), planificador «reglas primero, decisiones encima»,DashboardSpecv1 (tipos en@alisio/sdk), validador con tabla de reparación determinista (valid,repaired,fallback; nunca se llama al modelo), planificador de consultas y renderizador en TypeScript. Catálogo cerrado:kpi,line,area,bar,hbar,pie,donut,scatterytable; límites de 12 componentes, 6 KPI y 8 columnas de tabla, y de 1 a 20 categorías por ranking. - Planificador de consultas: cada componente es un único
SELECTconstruido desde un AST con identificadores tomados solo del catálogo de columnas del dataset, siempre entrecomillados y sin literales del modelo ni del proveedor; se ejecuta porDatasetService.query()(guardia de sentencia, lector de solo lectura y tiempo límite). La tendencia elige el cubo de tiempo más grueso que deje como máximo 400 cubos en lugar de truncar la cola; los cubos de tiempo exigen una fecha ISO 8601. - Pack de decisiones
smart-dashboard-v1: elstatelleva solo el objetivo (truncado a 300 caracteres) y, por columna preseleccionada, un alias opaco, la etiqueta (≤ 60), el rol, el tipo inferido y el cubo de cardinalidad; nunca valores de celdas. Sin proveedor el plan por reglas es igual de válido. - Renderizador con el mismo contrato DOM que
alisio_runtime.charts: el CSS y el script de arranque pasaron de cadenas decharts.pya archivos del runtime (charts.css,charts-boot.js,html-base.css) que leen ambos renderizadores; se subió la versión del runtime. KPI en notación compacta desde un millón (el valor exacto queda en la ayuda emergente), nombres de columna legibles, títulos y etiquetas por plantillaen/es, y «Otros» en los gráficos de participación. - Configuración:
analysis.smartDashboard(booleano,truepor defecto). Confalsela herramienta no se registra. No es solo global, pero unfalseglobal no lo deshace una capa de proyecto (el bucle de capas conserva el valor global cuando la capa no define la clave). Ajustable en vivo con etiquetas EN/ES en la web y entrada en/settingsde la TUI; se aplica al siguiente arranque. - Registro bajo
analysisEnabled && config.analysis.smartDashboard, igual queartifact_create. - Guía al modelo (fase 6, §10.1 de la especificación): con
smartDashboard: truelas descripciones depython_run,data_inspect,data_queryyartifact_create, la cabecera dedescribeDatasety los avisos dechart-lintmencionandashboard_generatey dejanpython_runpara lo que el catálogo no cubre; confalseson idénticas a las de 0.3.0 (resúmenes SHA-256 entests/dashboard-tool.test.ts). El arnés de benchmark registra ahora el motivo de los fallos (failureReason,failureClass,lastTurnTokens,endedWithoutDashboard;failureReasonspor variante). - Progreso: la herramienta emite
Analyzing dataset…,Planning dashboard…,Building N components…,Query i/N…yDashboard ready; la web muestra la última línea en la fila de la herramienta y la TUI gana el manejo detool_progress(última línea no vacía, ≤ 80 caracteres, borrada al terminar). La ficha del artefacto en la web muestra el planificador y el proveedor de decisiones. - Pruebas golden acotadas de
DashboardSpecentests/golden/dashboard/*.json(excepción documentada enCONTRIBUTING.md); el HTML solo con aserciones estructurales. - Arnés de benchmark
scripts/bench-dashboard.tsyscripts/bench/lib.ts(fase 0); los resultados irán adocs/benchmark-dashboard.json, excluido del sitio.
Validación
Consultar validation.txt para la ejecución final. Las pruebas de proveedor usan un servidor HTTP local determinista, no una cuenta externa. MCP se prueba con el SDK servidor real en procesos/HTTP locales. El binario Linux ejecuta un ciclo completo, carga un plugin externo con dependencia y conserva la sesión. La integración Herdr tiene validación de contrato; el escenario de dos agentes bajo un servidor Herdr real quedó bloqueado por el entorno.
Pendiente para llegar a 1.0.0
- Ejecutar y ajustar matriz Windows/macOS; CI actual cubre Linux, no certifica otros sistemas.
- Validar DeepSeek, OpenCode Console/Zen y OpenCode Go con credenciales del usuario. La inferencia solo se verificó con claves falsas y HTTP simulado/local; los catálogos públicos sin autenticación de Zen y Go se comprobaron por separado. No se usaron ni inspeccionaron credenciales reales.
- Validar Herdr con servidor/PTY reales; añadir launcher/resumer nativo si Herdr lo permite.
- Checkpoints/rewind de sesión y memoria vectorial: no existen.
- Onboarding interactivo; temas de color configurables; vista de razonamiento expandible.
- Release estable con binarios: el script
pnpm publish(scripts/publish.ts, ver Publicación) publica las versiones alpha anteriores y la0.1.0estable (dist-taglatest); SemVer de rangos de plugins (@alisio/sdkcomo peer^0.2.0) y recarga en sesión inactiva. - Discovery automático de rutas Pi y watch incremental.
- OAuth MCP interactivo y capacidades multimedia MCP.
/mcpspermite reconexión explícita y bearer mediante referencia a variable de entorno, pero no flujos de autenticación en navegador. El comportamiento de consentimiento repetido es ahora configurable: la preferencia globalmcp.allowconcede consentimiento MCP de forma persistente y auto-conecta los servidores activados en cada inicio; la concesión por sesión en la TUI es la alternativa cuandomcp.allowno está definido. Conceder persiste entre sesiones: un servidor que auto-conecta al arranque se ejecuta sin sandbox con los privilegios del usuario cada vez que está activado. - OpenTelemetry remoto, métricas de memoria y benchmarks de repositorios grandes.
- Endurecer frente a procesos hostiles y carreras de filesystem. No se ofrece sandbox OS.
Runtime y empaquetado: alcance de la verificación
- Pruebas bajo Node (Vitest): adaptador SQLite (filas planas, transacciones anidadas, FTS5 trigram, permisos 0600, filtro del aviso experimental), helpers de archivos,
which, runner de procesos (salida, truncado, timeout) y resolución de plugins por ruta o paquete. Todos los escenarios de integración se ejecutan en proceso bajo Node y un subconjunto también en Bun. test:cliejecutapackages/cli/dist/main.jscon Node contra un proveedor simulado (verificado en Node 22.19 y en el mínimo 22.16.0);test:compiledhace lo mismo con el binario Bun. Ambos comprueban que no aparezca elExperimentalWarningde SQLite.pack:checkempaqueta los nueve paquetes publicables con pnpm y valida contenido (solodist, README, LICENSE,package.json),exportshaciadist, ausencia deworkspace:y de fuentes.- Instalación global real con npm desde los tarballs locales mediante un registro temporal (
scripts/install-smoke.ts):alisio --helpfunciona desde el empaquetado npm. - La ampliación actual de DeepSeek, OpenCode Console y OpenCode Go se verificó bajo Node puro con el CLI construido, claves falsas e inferencia HTTP simulada/local (
run --read-only, herramientas y JSONL). Los catálogos públicos sin autenticación de Zen y Go se comprobaron por separado; no se usaron credenciales reales ni cuentas OpenCode reales. - El enrutamiento por sesión se verificó con perfiles y claves falsas: padre/hijo concurrentes en proveedores distintos, selectores canónicos/únicos/ausentes/ambiguos, reanudación tras reinicio, aislamiento de continuación, reemplazos de agente/
taske IDs OpenCode estables y distintos. scripts/install.shprobado contra un espejo local (instalación con checksum y rechazo de un binario alterado). No se ejecutó ninguna publicación ni release; los workflows de release y Pages no se ejecutaron en GitHub. Binarios macOS/Windows/arm64 no probados (compilación cruzada de Bun).
Subagentes, AGENTS.md y skills: alcance de la verificación
- Vitest (Node): cargador de AGENTS.md (override, orden, adjunto perezoso por sesión, límite de 32 KiB, CLAUDE.md desactivado por defecto, explain); skills (rutas, confianza, colisiones, nombres, profundidad y límite de directorios); presupuesto proporcional y una ejecución del tamaño de
/initque ya no se agota; periodo de gracia SIGTERM → SIGKILL; definiciones de agentes (formato, compatibilidad Claude Code y opencode, precedencia, confianza, integrados); reductor de foco del panel. Con proveedor simulado: paralelismo y aislamiento de contexto, envoltura y límite de resultados, retirada detaskal límite de profundidad, cola llena, error estructurado, permisos (solo lectura heredada), plugin desactivado,send_messagea un hijo en ejecución, espera acotada y rechazo de esperar a un ancestro, notificación en segundo plano, cancelación en cascada con muerte forzada de un proceso, reanudación tras marcar interrumpido, worktrees en un repositorio real (dos ramas, merge, conflicto con merge abortado y descarte), orden serial y el paso deaskaserialsin terminal interactiva. - Pseudo-terminal: panel con 4 agentes (uno anidado), acorde Ctrl+X ↓, ↓ desde el editor vacío, flechas en el panel, apertura de vistas de solo lectura, navegación padre/hijo/hermanos, colapsar, confirmación de Ctrl+K,
/agents, y la pregunta de worktrees con dos escritores. - Proveedor simulado (
--read-only): dosexploreen paralelo resumidos por el padre, y un caso anidadogeneral→explorepersistido con profundidades 1 y 2. - No verificado: fusión real de worktrees con DeepSeek, Windows/macOS, rendimiento con muchos agentes simultáneos.
Plantillas y /init: alcance de la verificación
- Vitest: parseo y validación del frontmatter, sustitución de argumentos, precedencia y diagnósticos, nombres reservados, confianza del proyecto, expansión slash,
displayen el historial. Escenarios:/initcon proveedor simulado (list_files/read_file → write_file al crear; read_file → edit_file con hash al actualizar, conservando el contenido humano), rechazo con--read-onlyy sin--allow-write, aprobaciones, plantillas de plugin, usuario y proyecto (no cargadas sin confianza).test:cli/test:compiled:run "/init"headless y rechazo con--read-only. - Proveedor simulado sobre una copia temporal de
examples/plugins/custom-mascot: creó unAGENTS.mdde 103 líneas con hechos verificados; una segunda ejecución con foco lo actualizó conedit_fileconservando una nota humana. En esa segunda ejecución se agotólimits.maxTokens(100 000) después de aplicar los cambios; en repositorios grandes conviene subir ese límite.
Pantalla de inicio y extensiones: alcance de la verificación
- Pruebas (Vitest, Node): registro (prioridades, desempate por id, conflictos, fallbacks, desregistro); pantalla por defecto en 36/60/100/160 columnas (secciones presentes, anchos dentro del límite, ASCII sin unicode, sin ANSI sin color); mascota y pantalla de plugins, campo declarativo, prioridades, empate con diagnóstico, proveedores rotos con fallback, saneado de secuencias de control, vuelta a los valores por defecto al cerrar el plugin; política del banner (TUI, readline, run,
--json,--quiet,--no-banner,CI, sin TTY) y capacidades del terminal (TERM=dumb,NO_COLOR,LANG=C, 0 columnas → 80). test:cliytest:compiled: el JSONL derun --jsones idéntico (normalizando ids, marcas de tiempo y duraciones) con y sin un plugin que registra mascota y pantalla, sin banner en stdout ni stderr.- Pseudo-terminal: TUI a 110 y 36 columnas,
TERM=dumb,NO_COLOR=1,--no-banner, plugin de ejemplo (cometa) en ancho y estrecho, y modo--no-tuicon stderr TTY, redirigido y--quiet.
TUI y compactación: alcance de la verificación
- Pruebas automáticas: formateadores, parseo de comandos, ajuste al ancho, diff de ediciones y reducción de eventos a modelo de vista (Vitest); planificador de compactación (Vitest); compactación manual/automática, persistencia tras reabrir la base, rechazo con llamadas inciertas, cambio de modelo persistido, migración de una base v1 y aprobación denegar/permitir en sesión (escenarios Bun);
listModels, modelo por petición, tokens en caché y razonamiento con un servidor HTTP local. - Renderizado y copia en la TUI (Vitest,
tests/tui-markdown.test.ts): tokenizador por línea (segmentos exactos para ts/json/bash/python, docstrings y comentarios de bloque multilínea, llaves y pares de JSON, f-strings de Python, claves YAML, propiedades CSS, tags/atributos HTML);highlightCodecon color (códigos SGR por estilo) y sin color (NO_COLOR→ líneas planas sin\x1b); saneado de ANSI/controles incrustados en la entrada (nunca se reemiten) y caracteres raros (emoji incluido);codeBlockIndentaplicado al renderizar un bloque real conAssistantBlock; pista de copia: presente con respuesta completada, ausente durante streaming, ausente con respuesta vacía, aparece al completarse (update), fallback ASCII conTERM=dumb/LANG=C; enrutado puro del atajoeditorCopyKey(c/ysolo con entrada vacía, sin autocompletado y sin turno en curso); normalización de separadores de tabla (guiones em/en →---con alineación, sin tocar código delimitado/sangrado,---sueltos ni filas de prosa). - Resultados enriquecidos (Vitest, sin red,
tests/mcp-rich.test.ts,tests/rich-results.test.ts,tests/tui-rich-render.test.ts): contrato SDK aditivo (la unión de contenido yUiBlockcompilan;textResultbyte-idéntico;textProjectionconserva orden/isError y devuelve la misma referencia para contenido solo-texto); mapeo MCP → bloques con fixturas: texto solo, parte de imagen (mime + base64 + marcador de proyección), tablas{columns,rows}/{headers,rows}con filas de objetos, clave-valor plano, árboles{nodes}con meta e hijos, texto JSON parseable con forma, fallback a texto para JSON anidado/arrays/no parseable, partes desconocidas como marcador (nunca bytes),structuredContentsin forma → comportamiento anterior,isErrorpreservado, recursos (texto, blob de imagen, blob no-imagen → marcador, listado sincontents→ JSON plano) y proyección canónica SIEMPRE presente junto a un bloque; persistencia:endCall/callResult/append/messagesredondean ui/image intactos ycallResultsolo responde para llamadas completadas; compactación:reduceMessageSizes/reduceMessagesToBudgetrecortan solo partes de texto (las ui/image pasan intactas y no cuentan contra el tope),serializeForSummaryemite solo la proyección (sin bytes niundefined); runner: con un proveedor controlado y el adaptador real, el modelo recibe solo["text","text"](texto + proyección), el store conserva ui/image ytool_completedemitepreviewsolo-texto; TUI:renderUiBlockalinea tablas y envuelve celdas largas, clave-valor en dos columnas, árboles con├─/└─/│y ASCII|-/- /|, código a través dehighlightCode, markdown con el renderizador, imágenes: marcador con dimensiones sin protocolo, secuencia iTerm2 consetCapabilities("iterm2"), marcador conNO_COLORincluso con soporte, bloques sin color conNO_COLOR(módulo fresco);reduceEventeitemsFromHistoryredondean ui/image yisUiBlockrechaza payloads malformados. Sin red: las fixturas llegaron solo hasta el adaptador puro; los tests de conector existentes (mcp.test.ts` stdio/http) pasaron sin cambios de expectativas. - Verificación manual en pseudo-terminal (Linux, xterm-256color, emulado con
pyte) contra un servidor simulado local: arranque, streaming Markdown, herramientas, aprobación,/stats,/model,/compact,/tools,/sessions,/resume,/clear, Esc, Ctrl+C, Ctrl+D, redimensionado a 60 y 40 columnas sin líneas desbordadas, y el binario compilado. No se probó en Windows/macOS ni en emuladores reales distintos (kitty, iTerm2, Windows Terminal); Shift+Enter depende de la terminal. - No se ejecutó una compactación automática con inferencia real autenticada.
- Métrica de contexto coherente (Vitest, proveedores simulados, sin red): el runner compacta con el presupuesto de caracteres de respaldo cuando la ventana es desconocida, NO compacta pronto cuando la ventana conocida es grande (el desajuste DeepSeek ~1M de ventana frente a 800k de caracteres), compacta en
ventana × thresholdaunque esté muy por debajo del presupuesto de caracteres, y trata las ventanas declaradas por encima de 2M de tokens como desconocidas para que el respaldo siga protegiendo.app.contextBudgetinforma la ventana del modelo cuando el catálogo la expone (carga perezosa deGET /modelsy refresco tras el cambio de modelo; verificado con fixturas oficiales de DeepSeek/OpenCode) y una base honestabasis: "unknown"sin total inventado en caso contrario; la barra de la TUI pinta~9.9k / ?para ese caso (Vitest de reducción/formateo) y expone el punto de compactación a la TUI. - Cambio:
limits.maxContextCharsahora tiene por defecto800000caracteres (≈200000tokens, antes160000≈ 40k) en el esquema de configuración y en el menú de ajustes (/settings→ Presupuesto de caracteres de contexto, que ahora ofrece 800k entre sus valores). Es una suposición para ventanas de modelo desconocidas — el mismo presupuesto de ~200k tokens que OpenCode asume para proveedores personalizados — pensada para servidores locales que no informan su ventana (p. ej. llama.cpp); una ventana conocida (≤ 2M tokens) siempre la anula (compactación enventana × threshold) y el usuario puede bajarla desde/settings. - Recuperación del presupuesto de contexto tras una compactación insuficiente (Vitest, proveedores simulados, sin red): si tras compactar los mensajes conservados aún superan
limits.maxContextChars, el runner reduce el contenido retenido contra un OBJETIVO TOTAL de caracteres (objetivo = max(4 000, límite − instrucciones − herramientas)), recortando primero los mensajes más grandes en rondas de límites descendentes (resultados de herramientas 8k → 4 096 → 2 048 → 1 024 → 512; textos usuario/asistente 16k → 8 192 → 4 096 → 2 048 → 1 024; marcador… [truncated by context budget], solo contenido: roles/IDs de llamada/fronteras intactos, revisado en el transcript persistido) y completa el prompt. Esto cubre también sesiones con MUCHOS resultados medios de herramientas (3–8k cada uno, p. ej. salidas MCP), que individualmente quedaban bajo los límites por mensaje y antes producíantruncated: 0y el error fatal; el eventocontext_reducedinforma cuántos mensajes se cortaron. Solo una sesión patológica (instrucciones + herramientas que ya superan el límite por sí solas, de manera que ni el suelo de 4 000 caracteres cabe) lanza el error accionable (tamaño aproximado +/compact+ nueva sesión) y, como la reducción persiste, un prompt posterior en la misma sesión completa sin errores. El límite duro, el umbral de ventana, el respaldo de caracteres y la semántica deshouldCompactContextno cambian; los checkpoints (summary) nunca se cortan; el reducer es determinista (orden total estable: más grande primero, empates por posición). - Versión en tiempo de ejecución (Vitest): un cargador por paquete lee la versión del propio
package.json(ALISIO_PACKAGE_VERSIONgana en binarios autónomos;devsi no hay manifest ni inyección). Metadatos de plugins,user-agentpor defecto y el cliente MCP dejan de llevar literales de versión; un test de regresión falla si cualquier fuente depackages/*/srcvuelve a contener una literal de versión de publicación. - MCP: la auto-conexión de arranque con
mcp.allow:true(o--allow-mcp) se verifica de extremo a extremo por stdio con una fixtura real: cada servidorenabledconecta sin tocar/mcps, losdisabledno, los definidos en un.alisio/config.jsonde proyecto de confianza también, y--read-onlylo bloquea todo. - ripgrep ausente: el escenario
searchde integración se salta limpiamente cuandorgno está en el PATH (VitestskipIfy guard en la fixtura independiente);search_text/list_filesyalisio doctorexplican cómo instalarlo por plataforma en lugar de un ENOENT crudo. - Script de publicación (
scripts/publish.ts, Vitest con directorios temporales y sin red): orden sdk → core → cli, rechazo del chequeo de fugas (manifiestoworkspace:), dry-run sin efectos secundarios (no escribe ni publica), matemática del bump de versión (escritura atómica y restauración ante fallo) y error de paquete desconocido. El diagrama de flujo de herramientasdocs/assets/Flujo de Ejecución de Herramientas y Modelo de Permisos.webp(referenciado en/toolsy/es/tools) es un binario sin rastrear que debe añadirse a git. - Truncamiento (Vitest, proveedores simulados, sin red): los cuatro adaptadores (openai-compatible, deepseek, opencode, opencode-go) emiten
completedcontruncated: truecuando el corte (finish_reasonlength,response.incompleteostop_reasonmax_tokens) dejó texto aprovechable y llamadas completas, y siguen lanzando con texto vacío, llamadas parciales o final abrupto; el runner completa una respuesta cortada sin llamadas con el eventoresponse_truncatedytruncated: trueenrun_completed, ejecuta las llamadas de un turno cortado y continúa, acepta un resumen cortado pero aprovechable como checkpoint parcial (partial: trueencompaction_completed), falla con mensaje accionable cuando el resumen no produjo nada (compaction.maxOutputTokens) y usacompaction.maxOutputTokens(por defecto 16000 en el esquema de configuración, 4096 si el runner se construye sin configuración), nunca el presupuesto del bucle del agente; la TUI muestra el aviso y el marcadorpartial(Vitest de reducción de eventos) y el esquema de configuración acepta el nuevo campo con su dato por defecto.
Presupuesto de tokens de salida del agente: alcance de la verificación
- Cambio:
limits.maxOutputTokensahora tiene por defecto16384(antes4096) en el esquema de configuración y en el menú de ajustes (/settings→ "Agent max output tokens"), porque un tope de 4096 deja a los modelos con razonamiento (p. ej. DeepSeek) agotar todo el presupuesto en el razonamiento antes de producir texto útil; eso hacía fallar subagentes de exploración/auditoría con «cut off by max output tokens before any usable content» tras 20–50 s de trabajo. - Nuevo: las sesiones hijas (subagentes) reciben un presupuesto de salida por llamada propio.
builtinPlugins.subagents.maxOutputTokensPerChild(positivo, por defecto16384) se introduce en la especificación del hijo (maxOutputTokensenChildSessionSpec), se persiste en sus opciones y se reenvía a cada llamada del runner; sin él, el hijo usaría el defecto global de 4096 del runner. El presupuesto acumuladomaxTokensPerChildno cambia (sigue siendo la suma por ejecución, no el tope por llamada). El runner ahora aceptamaxOutputTokenspor ejecución enRunOptionsy lo aplica delante del presupuesto del runner: opción por ejecución > opciones del runner > 4096. - Mensajes: el error «raise limits.maxOutputTokens» y el aviso de la TUI ahora señalan también
/settings → Agent max output tokens. - Verificación (Vitest, sin red): esquema de configuración (
config-layers,subagents); reenvío del presupuesto en hijos y rechazo de valores no positivos (children-output-tokens,subagents); regresión de la sesión principal que mantienelimits.maxOutputTokens; aviso de la TUI (tui-state); truncación sin cambios (la ruta de compactación conserva su propio presupuesto de 16000).
Límite de contexto frente al catálogo de herramientas y velocidad de salida: alcance de la verificación
- Límite duro posterior a la compactación corregido: ahora mide SOLO el contenido reducible (instrucciones + transcript), no el catálogo fijo de herramientas (
toolsText). Antes, un catálogo grande (p. ej. un servidor MCP con ~95 herramientas y los tools asignables) podía valer 100k+ caracteres por sí solo: el objetivo de reducciónmax(4 000, límite − instrucciones − herramientas)quedaba en el suelo de 4 000 y aun asíchars()(con el catálogo) superaba el límite, matando la ejecución con «Context budget exceeded» incluso con un transcript casi vacío — la falla reportada en subagentes/exploraciones. Ahoraobjetivo = max(4 000, límite − instrucciones)y la comprobación fatal usainstrucciones + JSON.stringify(mensajes); el catálogo es una realidad de despliegue (decisión de/plugins), no crecimiento de sesión, y el mensaje fatal (que solo se dispara si instrucciones + transcript siguen superando el límite tras la reducción) menciona/compact, recortar salidas grandes y desactivar servidores MCP innecesarios con/plugins. La compactación automática (shouldCompactContext) sigue midiendo la petición COMPLETA (ventanas y respaldo de caracteres protegen lo que el modelo ve, herramientas incluidas); solo el límite duro posterior y la reducción del transcript excluyen el catálogo. El eventocontext_reducedy todos los invariantes del reducer no cambian. - Velocidad de salida corregida (
/exit, doble Ctrl+C, Ctrl+D, SIGINT/SIGTERM): el cierre deMcpConnectoracota cadaclient.close()por servidor a 800 ms (el cierre stdio del SDK puede tardar ~4 s con un servidor que ignore la terminación) y toda la fase a 1500 ms, solo en elclose()terminal;disconnect()en caliente conserva su semántica sin tope para el reconexado interactivo.app.close()ejecuta herdr+MCP en paralelo y luego proveedor+plugins (store al final), todo conPromise.allSettledy topes de etapa de 2500 ms, con el mismo tratamiento acotado en la ruta de error decreateApplication. La TUI acota el apagado: turno en vuelo ≤ 3 s y hooks de fin de sesión ≤ 1,5 s al salir (el/clearconserva el límite completo depluginHooks.sessionEndTimeoutMs). - Verificación (Vitest, sin red): un sesión con
toolsTextde ~200k caracteres y transcript pequeño completa sin error fatal, sincontext_reducedy sin compactar, y su transcript queda intacto (runner-truncation); el caso patológico irreducible (solo instrucciones) sigue fallando con el error accionable que ahora sugiere/plugins;McpConnector.close()vuelve en < 3 s frente a un servidor stdio real que ignora fin de stdin y SIGTERM (el SDK tardaría ~4 s), con registro limpio y estadodisconnected(mcp-close-timeout, fixturamcp-slow-server);app.close()vuelve en < 6 s aunquedisposede proveedor y plugin nunca resuelvan (exit-speed);app.endSessionqueda acotado porsessionEndTimeoutMscon un hook colgado (exit-speed); los topes de la TUI (3 s/1,5 s) están cubiertos por el helperbounded(tui-exit); suites existentes (MCP, consentimiento MCP en caliente, presupuesto de contexto, compactación, truncación, subagentes, tokens de salida por hijo, tui-state) sin cambios o con ajustes de redacción. - Limitación documentada: un catálogo MCP enorme debe gestionarse con
/plugins(desactivar el servidor;/mcpsmuestra el recuento de herramientas); el runner nunca corrompe un transcript sano por el catálogo, y la compactación automática con ventana desconocida sigue disparándose por la petición completa (herramientas incluidas) como protección del contexto real del modelo.
Memoria y plugins: alcance de la verificación
- Pruebas automáticas (escenarios Bun y Vitest): store (upsert por
topic_keyconrevision_county sesión más reciente, filas independientes por proyecto y alcance, deduplicación dentro/fuera de la ventana, ranking, AND/any, alcance proyecto/personal yall_projects, redacción, límite de 50 000 caracteres, fijadas, línea temporal, prompts, resúmenes, borrado lógico y físico, reapertura); migración sobre una base de sesiones v2 existente (versiones 1, 2, 100, idempotente); herramientas a través del plugin con política de solo lectura; compactación con extracción de memorias, JSON inválido, emparejamiento e IDs intactos, archivoconfirmedy presupuesto de recuerdo; inyección al iniciar sesión dentro del presupuesto; contrato de hooks (invocación, campos extra, fallo aislado, timeouts con aborto,model.complete, nombres/efectos de plugins externos frente a integrados); memoria activada frente a desactivada (sin herramientas, prompt, comando, estado ni archivo; compactación genérica); selección de comandos de portapapeles con un spawner falso. - Verificación manual en pseudo-terminal con servidor simulado:
memory_savecon--read-only, contador en la barra,/memory(lista, búsqueda, detalle), compactación con informe de memoria y archivo confirmado, desplazamiento con la rueda, copiar al seleccionar y/copy(conDISPLAYretirado, por lo que se ejercitó el respaldo OSC 52),/stats,/help, resumen al salir con/exite inyección de contexto en el siguiente arranque. - Proveedor simulado headless: el modelo guardó una memoria con
memory_saveen--read-onlyy, en una sesión nueva, respondió desde el contexto inyectado. - No verificado: copia real mediante
xclip/wl-copy/pbcopy/Windows (para no modificar el portapapeles del usuario); compactación automática con inferencia real; Windows/macOS.
Pegado y adjuntos de imagen: alcance de la verificación
- Vitest: sniffing de PNG/JPEG/GIF/WebP por cabecera, límites de tamaño (5 MB) y cantidad (4), captions compactas, mensajes de rechazo, mapeo al tipo
Attachmentdel SDK, y el flujo deCtrl+Vsin portapapeles (no-op explicado), congetImage()devolviendonull(vacío) oundefined(no disponible) tratados por separado, y un fallo de lectura capturado sin lanzar. Compactación: un adjunto en el tramo resumido se describe al modelo solo por tipo MIME y dimensiones (nunca sus bytes en base64), y uno conservado sobrevive intacto. Persistencia: cierre y reapertura de la base reproduce el adjunto byte a byte (JSON genérico existente, sin cambios de esquema). Proveedor: las partesimage_url(chat) einput_image(Responses) se verifican contra un servidor HTTP local con la forma exacta que espera cada modo. - Verificación real en pseudo-terminal (Linux, X11 disponible en este entorno) con el binario construido: un pegado multilínea real con marcadores de pegado con corchetes se insertó como una sola operación, sin fragmentarse ni enviarse antes de tiempo, y se envió correctamente al presionar Enter. Se colocó una imagen PNG real (no simulada) en el portapapeles X11 con
xclip,Ctrl+Vla adjuntó mostrando la línea compacta[1] image/png 2x2, 0.1 KB(el terminal de prueba no soporta gráficos Kitty/iTerm2),Ctrl+Rla quitó, y al reenviarla y enviar el mensaje la petición HTTP capturada contenía la parteimage_urlcon los bytes base64 decodificados idénticos a los del PNG original. - Modo
--no-tui: verificado con el mismo binario. El pegado de una sola línea funciona igual que escribir. Un pegado real de varias líneas (sin marcadores, tal como lo entrega una terminal real a un programa que nunca activó el pegado con corchetes) se fragmenta: cada línea se envía como un mensaje independiente, confirmado por la petición capturada.Ctrl+Ves un no-op inocuo (el byte se descarta, sin insertar nada ni bloquear). - Con un servidor local, una imagen PNG de 64×64 enviada como
image_urlconservó el formato exacto que produce esta implementación. Otro servidor local devuelve 400 para contenido de imagen; el SDKopenailanza unBadRequestErrorcon mensaje legible que el runner ya convierte en unrun_failedlimpio (sin caída ni traza cruda). - No verificado: recepción de imágenes por el portapapeles nativo en macOS o Windows (sin acceso a esas plataformas); miniaturas en línea reales en una terminal con protocolo Kitty o iTerm2 (el entorno de prueba solo tiene xterm-256color, así que solo se ejerció la ruta de compatibilidad de texto).
Preguntar al usuario (ask_user_question): alcance de la verificación
- Vitest: reductor puro del panel (
questions.ts, 19 pruebas) — cursor inicial en la opción recomendada, navegación con vuelta, confirmar de selección única y múltiple (incluida la confirmación explícita sin nada marcado, distinta de omitir), omitir (Esc) solo la pregunta actual sin abortar el lote, retroceder restaurando la respuesta previa exacta sin borrarla, truncado de etiquetas y resumen final.InteractiveQueue(queue.ts, 8 pruebas) — orden FIFO estricto, un trabajo rechazado no bloquea el siguiente, retirada limpia de un elemento ya abortado antes de su turno o mientras espera en cola (la cola sigue avanzando tras el hueco), visibilidad decurrent()/isQueued()/isWaiting()para el panel de agentes. Escenario de integración (fixtures/scenarios.ts,ask-user-question): sinuien absoluto y coninteractive()enfalsefallan rápido sin invocaraskQuestions; con unauifalsa interactiva, la petición reenvíasession/label/signaly las respuestas se asignan de vuelta en el orden de las preguntas (incluida una omitida, que se traduce enselected: []conskipped: true, distinto de una selección múltiple vacía y explícita); validación de esquema (1-4 preguntas, 2-4 opciones) a través deToolRegistry.parse; reglas de negocio en tiempo de ejecución (como mucho una opciónrecommended, etiquetas únicas por pregunta). - Pseudo-terminal (Linux, xterm-256color) con un servidor simulado real: lote de dos preguntas (una de selección única con una opción
recommendedmarcada visualmente, otra de selección múltiple) a 100 columnas — navegación con vuelta verificada (↑ dos veces desde la opción recomendada pasa por la primera y da la vuelta a la última), alternar con →, retroceder restaurando el cursor exacto de una pregunta ya confirmada, reavanzar, omitir con Esc la última pregunta (envía el lote sin abortarlo) y el bloque de resumen final con las etiquetas correctas. Confirmado también a 28 columnas con el flujo/ask: el modelo propuso 3 opciones marcandoNodecomo recomendada, Enter la confirmó de inmediato y el resumen (Runtime: Node) se renderizó sin desbordar ni truncar mal a ese ancho. - No verificado en pseudo-terminal (solo con pruebas unitarias de
InteractiveQueue, que son deterministas y agnósticas de qué sesión pregunta): dos o más subagentes reales llamando aask_user_questiona la vez a través de@alisio/plugin-subagentsy del panel de agentes real, con la migaja de pan mostrando qué agente pregunta y la retirada limpia de una pregunta en cola al cancelar ese subagente. El diseño y el enrutamiento (cola única, respuesta solo a la sesión exacta que preguntó) están probados de forma aislada pero no se ejerció con subagentes reales en una terminal; ver «Límites conocidos». - No se intentó
/askcon inferencia real autenticada (se priorizó el servidor simulado determinista, que permite fijar exactamente las opciones y así verificar cada tecla del panel; las tareas previas de este proyecto ya validaron por separado que DeepSeek responde de forma fiable a llamadas de herramientas).
Herramientas de red (webfetch, websearch, execute): alcance de la verificación
- Escenarios de integración (Vitest, servidor HTTP local determinista vía
fixtures/http.ts):webfetch— redirección seguida, HTML a markdown con script/style eliminados, rechazo de contenido binario/imagen, rechazo de esquemas no http(s), truncado confullTextPathy verificación del texto completo en disco.websearch— cadena SearXNG consearxngUrlapuntando al servidor local, DuckDuckGo Instant Answer (confetchglobal reemplazado temporalmente para no depender de la red), guarda SSRF (validateSearxngUrl), el nuevo punto de extensiónwebsearchcon prioridad ganando sobre lo integrado, su reintento seguro con diagnóstico ante un proveedor que lanza excepción, y la restauración de la cadena integrada al cerrar elPluginHost.execute— llamada exitosa a una herramienta permitida, denegación de un efecto no autorizado (sin tocar el sistema de archivos), éxito cuando la política sí lo permite, cumplimiento del timeout (contimeoutMsinyectable para pruebas rápidas), límite de 20 llamadas anidadas, prohibición de llamarse a sí mismo, y ausencia derequire/process/fetch/setTimeoutdentro del snippet aislado. - Verificación real (Bun, sin clave alguna del entorno de Alisio expuesta): una llamada real a
https://example.com/conwebfetchdevolvió HTML convertido correctamente a markdown (# Example Domain, enlace conservado); una llamada real aexecutecombinandocallTool("webfetch", ...)dentro del snippet aislado conpolicy.external: trueconfirmó el contenido de la página y devolvió solo un resumen calculado (no la página completa). Una llamada real awebsearchconduckduckgo-instantdevolvió resultados reales para «Node.js» (tras ajustar el proveedor a unUser-Agentde navegador y a detectar JSON por contenido en vez de por cabeceraContent-Type, ya que la API de DuckDuckGo devuelve JSON válido con unContent-Type: application/x-javascripten ciertas condiciones). - Hallazgo empírico nuevo sobre DuckDuckGo (2026-09): una solicitud automatizada a
https://html.duckduckgo.com/html/?q=...devuelve HTTP 202 con un desafío de captcha, mientras quehttps://lite.duckduckgo.com/lite/?q=...&kl=us-enresponde HTTP 200 con resultados reales y sin clave. Sobre esa base se añadió el proveedorduckduckgo-html(sin clave, sin cookies) con un analizador tolerante de HTML; su cobertura sin red (Vitest) incluye: extracción de 3+ resultados con URL destino decodificada desdeuddg, entidades HTML decodificadas en títulos, fragmentos (snippet) extraídos y sin etiquetas, anclas no resultantes (anuncios/relacionados) ignoradas, respuesta de desafío/captcha (HTTP 202 o página no reconocible) que lanza un error accionable que nombra al proveedor y sus causas posibles, y una página lite legítima sin resultados que devuelve[];searchWithFallbackresuelve el proveedor tanto por config como el menú/settingslo lista. Una llamada real alite.duckduckgo.com/lite/con elUser-Agentde navegador del proveedor devolvió resultados reales para «node.js web framework». - Hallazgo empírico honesto sobre el proveedor SearXNG por defecto (sin configurar nada): se probaron 9 instancias públicas distintas listadas en searx.space (incluida la que se dejó como URL por defecto,
searx.be) con una única solicitud automatizada fresca cada una; todas devolvieron un captcha/verificación de bot (HTTP 200 con una página de desafío) o429 Too Many Requests. La cadena por defecto es correcta arquitectónicamente (sin clave, autoalojable, sin bloqueo de proveedor) pero en la práctica actual no debe asumirse funcional sin autoalojar una instancia propia; el mensaje delimitationdel resultado y la documentación lo dicen así de forma explícita. - No verificado: una llamada real a un proveedor de pago (
tavily/brave/serpapi) — ninguna clave de esos servicios está disponible en este entorno y no se fabricó ninguna; solo se probaron con un servidor HTTP local simulando su forma de respuesta. Tampoco se verificó el modonativecontra un proveedor real que lo soporte (la configuración de DeepSeek no lo soporta, como se documenta); su validación (apiMode: "responses"requerido) y el paso del tipo de herramienta nativa hacia la petición sí están cubiertos por el flujo normal de tipos y por inspección de código, no por una llamada real.
Confianza de proyecto y permisos por defecto: alcance de la verificación
- Vitest (
fixtures/scenarios.ts, escenarioproject-trust): un directorio sin recursos de proyecto nunca necesita confianza; un directorio con.alisio/config.jsonfresco marcaneedsPrompt; guardar una decisión de confianza hace que deje de pedirse; modificar el contenido de.alisio/config.jsonfuerzaneedsPromptde nuevo con un hash distinto; una decisión de «no confiar» tampoco vuelve a preguntar;listTrust/revokeTrustreflejan y deshacen correctamente el estado, y revocar hace que vuelva a pedirse. - Vitest (
fixtures/scenarios.ts, escenariopermission-truth-table): parawrite_file(write),run_process(process) ywebfetch(external) por separado, conAgentRunnerreal ejecutando un turno completo contra un proveedor simulado — sin flag y con un manejadorapprove(igual que la TUI real) el efecto se ofrece y el manejador es preguntado de verdad; con el flag de permiso correspondiente, se permite sin preguntar nunca; sin manejadorapprovey con la política enfalse(equivalente a--read-only), la herramienta no se ofrece en absoluto. fixtures/cli-e2e.ts(Node y binario Bun):alisio doctorsin--trust-projectcontra un.alisio/config.jsoncon unabaseURLdistintiva nunca la lee (usa el valor por defecto del esquema) y no crea ninguna entrada en el almacén de confianza; con--trust-projectsí la lee, y tampoco persiste ninguna entrada (la confianza explícita de una ejecución sigue sin guardarse).- Pseudo-terminal (Linux, xterm-256color) con un servidor simulado real: primera ejecución en un proyecto nuevo con
.alisio/config.jsonmuestra el aviso de confianza con el texto explicativo completo; aceptar («y») carga la configuración del proyecto (la TUI arranca mostrando el modelo y el host del servidor simulado del proyecto, y la cabecera muestrawrite:ask process:ask); una segunda ejecución no vuelve a preguntar; editar.alisio/config.jsonsí fuerza una nueva pregunta; declinar (tanto escribiendo «n» como pulsando Enter, que por defecto es «No») dejar la configuración sin cargar, verificado porque la aplicación falla entonces con el mismo error claro de siempre («Set ALISIO_MODEL, --model, or provider.model») en vez de usar el modelo del proyecto; un directorio sin ningún recurso de proyecto arranca sin ninguna pregunta de confianza. - No verificado en pseudo-terminal de forma aislada (sí por inspección de código y por la prueba
permission-truth-table): el selector de aprobación real apareciendo pararun_process/webfetchespecíficamente sin ningún flag (se verificó explícitamente parawrite_file, que comparte exactamente el mismo mecanismo genérico que los otros dos efectos). - No verificado:
alisio trust list/alisio trust revokeen pseudo-terminal (sí se probó su lógica de forma aislada en Vitest); comportamiento en Windows/macOS.
Agente activo y effort: alcance de la verificación
- Vitest:
tests/active-agent.test.ts(catálogo y resolución del agente activo, opciones por ejecución —elplaninyecta su prompt de sistema víaRunOptions.instructionsy acota a solo lectura conpolicy/approvals—, resolución del effort por modelo con respaldo silencioso aldefaultLevel, validación de/effort [nivel], filas del selector y piezas de la línea de estado con roles de color, truncación y omisión del segmento de effort sin niveles soportados, comandos reservados/agentsy/effort, y publicación de definiciones principal-capaces por el plugin de subagentes a través depluginState);tests/deepseek-effort.test.ts(fixture HTTP local:reasoning_efforten Chat Completions yreasoning.efforten Responses, presentes solo cuando se fija el valor);tests/settings-persistence.test.tsytests/config-layers.test.ts(clavesagents.active/agents.efforten el esquema, el allowlist estricto y el escritor atómico, con el valor por defectobuild);tests/subagents-defs.test.ts(modeen--agents <json>). Sin red: los catálogos reales de DeepSeek no se consultan en las pruebas. - No verificado en pseudo-terminal: la interacción visual del selector
/agentsy del picker de/effort(sí su lógica pura y las piezas de estado), ni el envío efectivo del effort contra la API real de DeepSeek (sí el cuerpo de la petición contra el servidor de pruebas).
Agentes del usuario: alcance de la verificación
- Vitest:
tests/agent-definitions.test.ts(validación, ids, archivo Markdown legible por el parser de subagentes sin avisos, conservación de claves y comentarios ajenos, archivos inválidos, mezcla de ámbitos con reemplazo,livecon registro falso, recarga real con la aplicación y el plugin de subagentes —el agente aparece y desaparece sin reiniciar— y aviso de reinicio sin plugin, capacidades y ajuste, borradores y plantillas);tests/server-agents.test.ts(CRUD HTTP en ambos ámbitos,/api/agentsy/api/commandsreflejan el agente al momento, validación,--read-only,live: falsesin plugin, modelos, borrador con la guía incluida y con una skillcreate-agentdescubierta, crear → sesión nueva con el agente y sus instrucciones/effort en la siguiente petición,/agent:<id>y vuelta abuild, filtro?agent=, confianza desde la web con confirmación —los agentes de proyecto se cargan y se descargan al retirarla—, primer agente de un workspace confiado sin recursos y rechazo con--read-only);tests/tui-agent-manager.test.ts(verbos de/agents, filas e insignias del selector, filtro, filas del editor según capacidades, ajuste al guardar, instrucciones en una línea, registro de/agent:<id>y regla de colisión en el catálogo);tests/web-agents.test.ts(formulario, guardar solo con cambios válidos, ajuste por capacidades,curlde la API propia, resaltado, pasos de inicio, filtro e insignias del selector, cliente API con cancelación y paridad de i18n). - Verificado a mano en navegador (Playwright contra
alisio serveaislado): entrada Agentes encima de Ajustes, lista con plantillas, editor a dos columnas con el panel de configuración y los pasos, y aviso de workspace no confiable. - No verificado: la generación con un modelo real (solo con proveedor de prueba), la interacción visual completa de la TUI en pseudo-terminal (sí su lógica pura) y Windows/macOS.
Rutas externas y aprobación de directorios: alcance de la verificación
- Vitest:
tests/external-paths.test.ts(unidad dePathAccess: dentro del workspace, raíz extra declarada, aprobación por directorio contenedor con cobertura de subárbol en modo sesión,onceacotado al archivo, denegación con ruta y remedios, modo no interactivo sin aviso,--read-onlysin raíces extra ni aviso, endurecimiento de symlinks y mensaje con la ruta; integración concreateApplicationy un proveedor simulado: lectura externa aprobada, lectura vía--add-dir, lectura víaadditionalDirectories, denegación headless con--add-diryadditionalDirectories,--read-onlybloqueado con--add-dir, y la escritura externa que sigue exigiendo su política de escritura);tests/config-layers.test.ts(fusión aditiva deadditionalDirectories, incluido que un array vacío en una capa inferior no borra la global, y canonización/orden). - La mediación reutiliza
safePath(sin symlinks,lstatpor segmento) y es mediación, no un sandbox del sistema operativo; no es a prueba de carreras frente a procesos hostiles concurrentes. - No verificado en pseudo-terminal: la interacción visual del aviso de directorio externo (sí su lógica con un decisor inyectado). Las llamadas anidadas de
executenunca abren un aviso nuevo: solo alcanzan directorios ya aprobados para la sesión.
Contratos de eventos y bloques UI: alcance de la verificación
- Vitest:
tests/run-events-contract.test.tsejecuta elAgentRunnerreal conSQLiteStorey un proveedor simulado (razonamiento, texto, llamada con aprobación, progreso, segundo turno, fallo, cancelación, cambio de modelo y compactación omitida) y valida cada evento contra un espejo en tiempo de ejecución deRunEventDataMap(exhaustivo por tipo en compilación); comprueba que los eventos durables llevaneventIdigual aevents.seq, que los efímeros no, queseqsigue siendo por ejecución, querunId/correlationIdse propagan y que un store que no devuelve nada deja los eventos sineventId.tests/ui-blocks-fallback.test.tsrenderiza cada kind nuevo en la TUI (con y sin Unicode) y en la proyección de texto, y comprueba que un kind desconocido o mal formado no lanza. - No verificado en pseudo-terminal: el aspecto visual de los nuevos bloques en la TUI (sí sus líneas). Ningún productor integrado emite todavía los kinds nuevos (las herramientas los añadirán en la fase 4).
ttftMsmide hasta el primer delta de texto o razonamiento; un proveedor que solo entrega la respuesta completa no lo informa. Los tipos del protocolo web no tienen implementación todavía y pueden cambiar hasta que exista@alisio/server.
Persistencia v4, blobs y catálogo de comandos: alcance de la verificación
- Vitest:
tests/store-migration-v4.test.tsconstruye una base v3 con el DDL antiguo y datos, la abre conSQLiteStore(sin pérdida de filas, versión 4 registrada, migración idempotente al reabrir), comprueba la unicidad derunspor(session, request_id)(reintento devuelve la ejecución existente; el índice parcial rechaza un duplicado directo), la transiciónqueued→running→ terminal sin sobrescribir un estado terminal,interruptRuns(solo dueños muertos o ausentes; se conservan los de este proceso y los de otro proceso vivo), marcas de tiempo de sesiones raíz, columnas nuevas detool_calls, paginación de mensajes y eventos, el registro de ejecuciones del runner (completada, preasignada en cola, fallida, cancelada,turns_exceeded, store sin métodos opcionales) y la reconciliación encreateApplication.tests/blobs.test.tscubre deduplicación, permisos0600/0700, hashes inválidos, contenido manipulado, la resolución a base64 que llega al proveedor y los adjuntos en línea heredados.tests/command-catalog.test.tscubre fuentes, superficies, alias, colisiones y los manejadores core;tests/command-catalog-tui-parity.test.tscompara la lista de comandos, la resolución y la salida de/toolsy/sessionscon las implementaciones previas de la TUI. - Bun: el escenario
store-migrationdefixtures/scenarios.ts(ejecutado en Node y Bun portests/integration.test.ts) verifica la migración y el índice único parcial en Bun, ypnpm test:compiledcomprueba que el binario Bun escribe filas enrunsal ejecutaralisio run. - No verificado: la TUI en pseudo-terminal tras delegar
/toolsy/sessions(sí su texto).workspaces,sessions.pinned/archived_at,messagesPageyeventsPagelos consume@alisio/server(ver la sección del servidor web).
Servidor web (alisio serve): alcance de la verificación
/btw:tests/side-questions.test.ts(núcleo y catálogo: sin escrituras enmessages,events,runs,tool_callsni en el uso de la sesión; funciona con la sesión ocupada; cancelación; errores del proveedor; validación; límite de 20; recorte al presupuesto; precedencia del built-in; línea de uso),tests/server-side-questions.test.ts(HTTP real en puerto efímero con proveedor falso: pregunta durante un run, validación, cancelación explícita y por desconexión del cliente, 502, ruta de comandos),tests/tui-btw.test.ts(estado puro y render del panel) ytests/web-btw.test.ts(intercepción del compositor, navegación, cliente de la API). Verificado a mano: la TUI y el modo--no-tuien tmux, y la web con Playwright contra un proveedor OpenAI-compatible simulado (pregunta durante un run, transcripción intacta tras recargar, navegación, cancelación, línea de uso, 390 px de ancho). No hay arnés de pseudo-terminal automatizado para la TUI.- Vitest, con el servidor real en un puerto efímero, base de datos temporal y proveedor falso inyectado por
AppOptions.provider:tests/server-auth.test.ts(T-07: cookie ausente, canje del token con 303, token erróneo,HostyOriginajenos,Content-Type,--allow-remote, salud, cabeceras, assets estáticos, fallback SPA y confinamiento de rutas),tests/server-workspaces.test.ts(T-18),tests/server-prompts.test.ts(T-09: idempotencia secuencial y concurrente,enqueue,session_busy,session_lockedcon un PID vivo ajeno, cola FIFO con--max-runs 1, cancelación en cola y en curso, compactación),tests/server-sse.test.ts(T-08: snapshot y deltas sin huecos, reconexión a mitad de ejecución sin duplicar texto,tool_result, estado de sesión para el sidebar, latido, límites; el desbordamiento conresyncse prueba sobre el hub con un socket que no drena),tests/server-approvals.test.ts(T-10) ytests/server-shutdown.test.ts(T-13, apagado llamado en proceso).tests/store-web-metadata.test.tscubre los métodos nuevos deSQLiteStore. - Workspaces archivados:
tests/store-migration-v5.test.ts(bases v4 y v3 migradas a v5, idempotencia, workspace conocido solo por sesiones) y el bloque "archived workspaces" detests/server-workspaces.test.ts(filtroarchived, cierre de la aplicación,409 runs_activecon un proveedor retenido,409 workspace_archived, carpeta desaparecida, desarchivado al reabrir); la agrupación de la barra lateral entests/web-api-sessions.test.ts. - Selector de carpetas:
tests/folder-picker.test.tsprueba, en cualquier sistema anfitrión, la búsqueda enPATH(delimitadores yPATHEXT), la elección de herramienta por plataforma, los argumentos exactos de zenity/kdialog/yad/osascript/PowerShell, el análisis de la salida de los tres sistemas (barra final de macOS, barras invertidas y raíces de unidad de Windows, CRLF) y la detección de cancelación, además de migas de pan y carpeta superior conpath.win32ypath.posix.tests/server-folder-picker.test.tsusa un selector falso inyectado (nunca abre un diálogo real): ruta elegida, cancelado,409 picker_busy,503 picker_unavailable, desactivado con--allow-remote, y el explorador (404,403con un directorio sin permisos, sin archivos, cookie obligatoria). El diálogo nativo no se abre en ninguna prueba automatizada: en Linux/GNOME solo se comprobó la detección (zenity --version) y el diálogo real queda pendiente de verificación manual; macOS y Windows quedan cubiertos únicamente por los tests de construcción de comandos y análisis de salida. El listado de unidades de Windows no se ha ejecutado en Windows. - T-12 (
tests/startup-no-server.test.ts): ejecuta el código fuente de la CLI en Node con un hookmodule.registerHooksque registra cada módulo resuelto y comprueba que--help,run, el modo sin argumentos yserve --helpno carganpackages/serverninode:http. Bun no tiene un hook equivalente: en el binario solo se compruebaserve --help. pnpm test:cli(Node) ypnpm test:compiled(binario Bun):serve --help, rechazo de--host 0.0.0.0sin--allow-remote, arranque con--no-open --port 0,/api/healthsin cookie,401sin cookie, canje del token y parada conSIGTERM(código 0).tests/server-web-routes.test.ts: catálogo web sin comandos solo de TUI, ejecución de comandoscore, expansión de plantillas y/ask,/help,/clearcon sesión nueva,/effortpor sesión,unknown_command, idempotencia,session_busyde/modeldurante una ejecución, modelos (con y sin catálogo), contexto con ventana conocida, exportación JSONL ordenada, título derivado del primer prompt y agenteplanaplicado (instrucciones y sinwrite_file).- Interfaz web: Vitest sin DOM sobre los módulos puros —
tests/web-transcript-store.test.ts(T-15: snapshot, deltas,messageque sustituye el eco local y el texto en curso, resultados de herramientas en cualquier orden, avisos, notas locales tras un snapshot, páginas anteriores),tests/web-events.test.ts(lotes por frame, reapertura por cambio de sesiones y trasresync, backoff,nudge, protocolo distinto),tests/web-markdown-incremental.test.ts(T-16: mismo resultado que un parseo completo con cualquier tamaño de trozo, identidad de bloques congelados, fence sin cerrar, fences especiales),tests/web-composer.test.ts(paleta, historial, aprobaciones pendientes, todos los kinds deUiBlockcon renderer y paridad de claves y marcadores EN/ES),tests/web-api-sessions.test.tsytests/web-tools.test.ts. - Prueba manual con Playwright (Chromium) contra
node packages/cli/dist/main.js servey un proveedor OpenAI-compatible simulado: canje del token, crear sesión, streaming con razonamiento, dos lecturas y Markdown con tabla y código resaltado, fallo de herramienta visible, panel de aprobación con foco y respuesta por teclado (O) que devuelve el foco al compositor, paleta/y/stats, tema claro tras recargar y ancho de 390 px sin desbordamiento horizontal; consola sin errores. No verificado: lectores de pantalla reales, Firefox/Safari, otros sistemas, la interacción de preguntas de plugins en un navegador (solo sus reductores), la reconexión tras un corte de red real y el rendimiento con sesiones de 10 000 mensajes. - No verificado: la apertura automática del navegador, el apagado por señal con ejecuciones activas fuera de las pruebas en proceso, Windows/macOS y la contención de SQLite con varias apps y runs concurrentes reales (P-03).
- Fase 4:
tests/server-files.test.ts(T-11: orden y tamaños del árbol, páginas de 1 000,.gitignore, traversal con.., rutas absolutas y symlinks de directorio y de archivo hacia fuera → 403 sin filtrar contenido, 404, truncado a 2 MB y descarga completa, detección de imágenes, SVG y binarios, cambios de la sesión con estado git, diff frente aHEAD, archivo sin seguimiento y409 not_a_git_repo),tests/server-blobs.test.ts(detección y dimensiones de PNG/JPEG/GIF/WebP, deduplicación, 415 por contenido y por tipo declarado, vacío,Originajeno, 401, 413 por encima de 10 MB, servicio con tipo y caché, y T-09: un prompt conBlobRefllega al proveedor como adjunto base64 y un hash desconocido da 400),tests/standard-tools-ui.test.ts(parches unificados, bloques de las cuatro herramientas con el texto intacto como primera parte, límite de 200 KB y la TUI sin esos bloques),tests/web-renderers.test.ts(parser de parches, diff Myers, filas lado a lado, SGR,\r, JSONPath y resumen de tests),tests/web-renderer-registry.test.ts(T-17),tests/web-dock.test.ts,tests/web-attachments.test.ts,tests/web-stats-trajectory.test.ts(RF-16 y agrupación de la trayectoria) y T-15 ampliado (miniaturas y sustitución del eco con adjuntos). Los componentes Preact no tienen pruebas con DOM: se verificaron a mano con Playwright (Chromium, 1440 px y 390 px) contraalisio servecon un proveedor OpenAI-compatible simulado y un plugin local que devuelve bloquestest-results,json,progressy un kind desconocido: diff dewrite_file/edit_file, salida ANSI deshellcon 2 306 líneas y "mostrar todo", código de salida, árbol JSON de un fence largo, fallback de kind desconocido, panel de archivos (árbol sindist/ni*.logignorados, vista previa de código, imagen y Markdown, Cambios con estado git y diff frente aHEAD), subida de una imagen con+que llega al proveedor comoimage_url, miniatura persistida tras recargar, Trayectoria con duraciones y línea de estadísticas; consola sin errores ni avisos y sin desbordamiento horizontal a 390 px. La prueba encontró y corrigió dos defectos: el límite de 48 KB del runner medía también los bloques de visualización (ahora mide la proyección de texto) y la tabla de la trayectoria ocultaba la columna de duración. No verificado: rendimiento del árbol con 50 000 entradas reales en el navegador,gitausente y lectores de pantalla. - Fase 5:
tests/web-rich-renderers.test.ts(T-17: opciones de KaTeX, display/inline, error de análisis → fuente y mensaje,\href{javascript:}sin enlace, fallo inesperado del motor; configuración estricta de Mermaid, SVG saneado, diagrama inválido sin llamar arender), T-16 ampliado (\( … \)en línea,$de precios,\(sin cerrar, código en línea,```mathy$$durante el streaming),tests/web-tools.test.ts(etiquetas de tools de plugins),tests/provider-credentials-store.test.ts(máscara, estado sin valores, 0600, borrado, perfil sin cambiar el activo, metadatos de ajustes),tests/server-management.test.ts(plugins con tools/comandos, reciclado inmediato y diferido hasta el fin del run,catalog_changedy paleta actualizada, 400/403/404, workspace no confiable, skills fuera de la paleta y sin rutas, MCP sin comando ni argumentos,mcp_not_permitted, consentimiento que exigeconfirmed: true, agentes y ajustes con validación) ytests/server-secrets.test.ts(T-14: recorre todas las respuestas de gestión, sus cabeceras, los frames SSE y las líneas de log buscando dos claves guardadas;credentials.jsonen 0600;source: "env"sintail; perfiles sin valores secretos; activación real y409 runs_active). Verificado a mano con Playwright (Chromium, 1400 px) contraalisio serve --trust-projectcon un proveedor OpenAI-compatible simulado, un plugin de proyecto, una skill y un servidor MCP inexistente: diagrama Mermaid en oscuro y claro, pantalla completa, fórmulas en línea y en bloque, fallback de Mermaid y KaTeX inválidos, cada página de Ajustes, "Abrir archivo de configuración", guardar una credencial (ni el DOM, nilocalStorage, ni/api/providerscontienen el valor; solo…XYZ), activarfake-small, deshabilitar el plugin y la skill (la paleta/pierde/smoke-tools:smoke-helloy/skill:tidy), conceder MCP y ver el fallo de conexión saneado. La prueba encontró y corrigió fuentes de KaTeX inlineadas comodata:que la CSP bloqueaba. No verificado: 390 px del modal de Ajustes, lectores de pantalla, Firefox/Safari y un servidor MCP real conectado desde la web.
Análisis en Python y artefactos (fase 1): alcance de la verificación
Fase 1 de specs/archive/alisio-data-analysis-runtime-v1.2.md (§21): python_run, artifact_create, artifact_list, capability analysis.run con permisos persistidos, --allow-analysis, --python, migración v6, rutas de artefactos y permisos, tarjeta de descarga y popover de permisos en la web, anuncio, /artifacts y /permissions en la TUI.
- Decisiones del propietario (§23.2) adoptadas según la recomendación del documento y confirmadas por el propietario el 2026-10-01: D1 core (no plugin), D2
analysis.enabledactivo por defecto, D3 el permiso de sesión persistido aplica enresumeheadless, D4 flag--allow-analysis, D5 contenido de los extras (analysis/science, lockfiles con hashes generados conuv pip compile --universal), D7 helper XLSX como efectoread(fase 3; sin efecto aún), D8 origen separado del visor diferido, D11 tabladatasetscreada en v6, D12ctx.artifactssolo para built-ins (el host de plugins lo retira, junto concapability, de las herramientas de plugins externos), D14 sin devolver imágenes al modelo. - Vitest:
store-migration-v6(v5 con datos → v6 sin pérdida, idempotencia,CHECK, raíz de sesión),artifact-kinds(extensión frente a bytes; conflicto →file),artifact-store(copia byte a byte, dashboard multiarchivo, ZIP de carpeta,outputs.json, idempotencia por ejecución, rechazo completo por symlink, hard link,..,maxFilesymaxFileBytessin filas ni carpetas),zip-writer(CRC, nombres UTF-8;python -m zipfile -tcuando hay Python),analysis-install-hints(Windows, macOS con y sin Homebrew, Debian/Ubuntu, Fedora/RHEL, Arch, openSUSE, Alpine, otro Linux, Python < 3.10),analysis-runtime-discovery(arranque sin procesos, una sonda ydiscovery.json, caché pormtime, orden--python→uv→py -3→python3→python, alias de Microsoft Store,PATHEXT, intérprete del venv por plataforma),analysis-python-run(intérprete falso portable enfixtures/fake-python.mjs: dosartifact_published, separación de scripts/logs, entorno sin claves, salida ≠ 0,publishOnError,timed_out, cancelación ≤ 6 s, rechazo sin publicar,runtime_unavailable; más un caso con Python real que se salta si no hay Python 3.10+),analysis-capabilities(matriz de §10.3 con unaApplicationreal),analysis-runtime-sources,server-artifacts,server-capabilities,web-artifacts-store,tui-artifacts,open-pathy ampliaciones derun-events-contract,ui-blocks-fallback,command-catalog-tui-parityyserver-approvals. - Verificado a mano el 2026-10-01 en Linux (Ubuntu 22.04, Python 3.10.12 encontrado vía
uv) con el CLI compilado y un proveedor OpenAI-compatible simulado:alisio run --allow-analysis --jsonemite tresartifact_publishedconpath; sin--jsonimprime las líneasartifact: …;alisio analysis statusmuestra el intérprete y, con--pythoninexistente, el motivo y la guía; enalisio serve(Playwright, Chromium) el panel de aprobación muestra el título, la advertencia y el script, S guarda el permiso, aparecen las tarjetas bajo la herramienta (también tras recargar), la descarga desde el botón y desde la tarjeta entrega los bytes publicados, y el popover lista y revoca el permiso. No verificado: Windows y macOS (el jobportabilityde CI ejecuta las pruebas marcadas), la TUI interactiva en un terminal real,alisio analysis setup --extras(requiere red; los lockfiles se generaron pero no se instalaron) y lectores de pantalla.
Análisis en Python y artefactos (fase 2): alcance de la verificación
Fase 2 de specs/archive/alisio-data-analysis-runtime-v1.2.md (§21): comodín del router, visor aislado /artifact-view/<token>/* con enlace firmado (HMAC con el secreto del proceso, 10 min), rutas files/*, view, export, sources y DELETE, runToolCall (un run sin modelo con las mismas puertas que una llamada del modelo), artifact_read y artifact_export; en la web "Abrir archivo" en las tarjetas previsualizables, ArtifactPanel con asa compartida con el Dock, desplegable, menú, pantalla completa, expandir, renderers por tipo, modo estrecho, Detalles y /artifacts; en la TUI "Preview here", "Copy to workspace…", "Reveal analysis sources" y "Details".
- Vitest:
server-http(comodín del router),server-artifact-view(CSP exacta, sinX-Frame-Options,CORP cross-origin,private, no-store, token caducado o falsificado → 403, token de A sin acceso a B,..,%2e%2e%2fy rutas absolutas → 404,Hostinválido → 403, solo GET/HEAD, tokens ausentes de los logs, PDF sinsandbox,files/*en línea salvo HTML,sourcescon y sin?logs=1,DELETE),server-artifacts-export(aprobaciónwriteen la web, archivo enChanges, transcripción válida,409 runs_active),artifact-tools(artifact_readtruncado y confinado a la sesión,artifact_exportcon y sin--allow-write, separadores\y/, sin sobrescribir, confinado al workspace, carpeta multiarchivo, ausente con--read-only),web-artifact-panel(clampPanelWidth, pasos de teclado, ancho guardado con almacenamiento que lanza, exclusividad de la ranura derecha,cardSubtitle, renderer por tipo, imágenes relativas de Markdown, filtro y caché de enlaces),tui-artifact-preview(límites, leyenda,truncated, desplazamiento y teclas) y ampliaciones detui-artifacts,web-artifacts-storeycommand-catalog. - Verificado a mano el 2026-10-01 en Linux con Chromium (Playwright),
alisio servecompilado y un proveedor OpenAI-compatible simulado que publica un Markdown, un dashboard HTML, un JSON y un binario: en reposo la tarjeta muestra el tipo y con hover o foco de teclado "Abrir archivo" (el binario nunca); el clic abre el panel (aria-expanded="true") y la descarga no lo abre; el asa respeta 320 px y el máximo (viewport − sidebar − 360), ←/→, Shift, Inicio/Fin, doble clic y arrastre, y el ancho persiste tras recargar; el desplegable lista los artefactos con el actual marcado y cambia con flechas y Enter; Descargar, Pantalla completa (aria-pressed) y Cerrar (el foco vuelve a la tarjeta); Esc cierra primero el menú; abrir el Dock cierra el panel y viceversa; con Ajustes abiertos el panel quedainertsin recargar el iframe; a 600 px es un diálogo modal con el foco atrapado; Expandir oculta el chat; Copiar al workspace pide la aprobación y el archivo aparece en Cambios; Eliminar confirma y la tarjeta pasa a "Eliminado";/artifacts dashabre el panel filtrado. El dashboard de prueba, dentro del iframe y en una pestaña nueva, no pudo leerdocument.cookienilocalStorage(SecurityError), su origen esnullyfetcha/api/sessionsy a un sitio externo falló porconnect-src 'none'; en el iframeparent.documentlanzóSecurityError(en la pestaña nuevaparentes la propia página). No verificado: Firefox y Safari, Windows y macOS (explorer.exe /select,yopen -Rsolo tienen pruebas de construcción de argumentos), la TUI interactiva en un terminal real y lectores de pantalla.
Datos tabulares con node:sqlite (fase 3): alcance de la verificación
Fase 3 de specs/archive/alisio-data-analysis-runtime-v1.2.md (§21): ingesta de CSV, TSV, JSON, JSONL y XLSX en un archivo SQLite por dataset, data_inspect y data_query, guardia SQL, rutas de datasets y frames dataset_ready/dataset_failed, subida desde el compositor web con chips y resumen en el prompt, SpreadsheetView (también para artefactos spreadsheet), helper XLSX en Python con la biblioteca estándar (D6, opción B), datasetId en python_run y alisio_runtime.datasets.
- Desviaciones del documento (editadas en la especificación): el motor de datos es un proceso hijo, no
worker_threads, porqueworker.terminate()no interrumpe una llamada nativa de SQLite (comprobado en Node 22.19 y Bun 1.4.2 con una CTE recursiva infinita); el límite de tiempo mata el proceso. El motor se empaqueta enengine-source.ts(regenerar connode --experimental-strip-types scripts/analysis-data-engine.ts; un test detecta el desfase). El cursor de página es[rowid].node:sqliteenlaza todo número como REAL, así que los enteros se enlazan como BigInt. La migración v6 no cambió. No hayingest.ts/query.tsseparados. - Fuera de esta fase por §21/§18: la página Data analysis de Settings y las claves editables de
analysis(fase 4, con la retención). - Vitest:
data-csv-parser(RFC 4180, trozos arbitrarios, delimitador, codificaciones, conversión sin pérdida),data-sql-guard(tabla de aceptadas/rechazadas),data-ingest(valores exactos, BOM UTF-8/UTF-16, CRLF,;, windows-1252, nombres, filas irregulares, JSONL con claves tardías, límites sin dataset parcial, reutilización por sha256, borrar y reemplazar con el motor activo, lectura consqlite3de Python),data-query(solo lectura, truncado, CTE recursiva detenida en ≤queryTimeoutMs + 1 sy consulta siguiente válida, dataset de otra sesión →not_found),data-rowsyweb-spreadsheet(keyset con empates, NULL y tipos mezclados sin duplicados ni huecos, filtro, saltos,maxInteractiveRows),data-xlsx(hojas, fechas, épocas, fórmulas, equivalencia con el CSV exportado, zip bomb, DOCTYPE,maxRows; sin Python →dataset_unsupported),data-tools(herramientas con un runner real ypython_runcondatasetId),server-datasets(subida, esquema, páginas, límites, resumen del prompt, artefacto perezoso y 1 000 000 de filas con/api/health< 100 ms y latido SSE estable),analysis-data-engine-sources,web-attachments,web-transcript-store,artifact-kindsytui-artifact-preview.fixtures/cli-e2e.tsejecutadata_inspect+data_querycon Node y con el binario Bun (que se relanza como su propio motor conBUN_BE_BUN=1). - Verificado a mano el 2026-10-01 en Linux con Chromium (Playwright),
alisio servecompilado y un proveedor OpenAI-compatible simulado: se adjuntó un CSV de 4 000 filas (con007,N/A, vacíos y comillas), el chip mostró4,000 filas × 5 columnasy el mensaje no se duplicó al llegar el durable; el modelo llamó adata_inspect,data_queryypython_run { inputs: [{ datasetId }] }, cuyo script leyó el mismo archivo consqlite3(intento deDELETE→ solo lectura) y publicó un dashboard y un CSV; el chip abreSpreadsheetViewcon 4 001 filas aria, orden por cabecera (aria-sort, ascendente/descendente), recorrido por todo el rango, flechas, Ctrl+C y Ctrl+Mayús+C (celda y fila en TSV), filtro con recuento (207 coincidencias) y el artefacto CSV generado se abrió en la vista (ingesta perezosa). Sin errores en la consola. No verificado: Windows y macOS (el jobportabilityejecuta las pruebas de datos), Firefox y Safari, la TUI interactiva en un terminal real, lectores de pantalla, el efecto dePRAGMA hard_heap_limity el redimensionado de columnas con el puntero (solo probadas las funciones puras).
OCI, extras, retención y rerun (fase 4): alcance de la verificación
Fase 4 de specs/archive/alisio-data-analysis-runtime-v1.2.md (§21): runtime oci opcional (Docker o Podman, imagen fijada por digest), instalación de extras bajo demanda con la capability analysis.install, AnalysisJanitor (retención de artefactos, datasets, originales en blobs/ y trabajos internos), Rerun con procedencia (modelo y proveedor incluidos), la página Análisis de datos de Ajustes y las claves analysis.* editables (con claves de tres niveles en setConfigValue). Decisiones del propietario: D1–D14 según la recomendación, confirmadas el 2026-10-01 (D10: retención 30 / 7 días y artefactos sin caducidad).
- Desviaciones del documento (editadas en la especificación):
analysis.retention.*es solo global comoruntimeyoci.*(el barrido cubre todos los workspaces; las claves ignoradas de una capa de proyecto se listan enalisio doctor);0significa "no borrar nunca" en las tres claves de retención;input/se conserva conscript/mientras haya un artefactoready; el "último uso" de un dataset es la fecha de modificación de su archivo (sin migración v7); el rerun usa las copias deinput/del trabajo original verificadas por sha256 (no vuelve a leer el archivo del workspace) y se pide conpython_run { rerunOf }; la instalación desde el chat usaToolContext.approveInstallyPendingApproval.install; los artefactos expirados siguen listados conDetailsyRerun; el comprobador de wheels es un job propio (extras-wheels) además del paso deportability. No se implementaron los opcionales de §23: origen separado del visor (D8), lector XLSX en Node (D6), plantillas de artefacto ni XLS/ODS/Parquet. - Marcas "(verificar)" de la especificación comprobadas y corregidas:
@tanstack/preact-tablesí existe en npm (9.2.4);xlsx@0.18.5sigue siendolatestynpm auditlo marca high;exceljs@4.4.0instala 78 paquetes y 36 MB;--userse omite en macOS y Windows sin verificar (no hay Docker Desktop aquí). - Vitest:
analysis-config(imagen sin digest rechazada al cargar, runtime/oci/retención ignorados desde un proyecto y listados, claves de tres niveles que conservan hermanas, solo las cinco claves),analysis-oci(argumentos exactos de §8.3 con rutas de Windows, macOS y con espacios,--usery rootless, SELinux, ruta con:rechazada; CLI de contenedor falsa: sin Python del host, cancelar y agotar el tiempo ejecutankilly no queda ningúnalisio-*, motor ausente con el otro instalado, sin imagen; los casos con motor real, red bloqueada, escritura fuera de/job/outy/job/worky cancelación, corren conALISIO_TEST_OCI_IMAGEy se saltan sin él),analysis-extras(pip falso:--require-hashesy--only-binary=:all:,uv, fallo sin red sin entorno a medias ni cambio del activo, instalaciones concurrentes, aprobación soloonce, nunca persistida, sin flag, headless con el remedio),analysis-rerun(mismo hash de script, artefactos nuevos conrerunOf, los anteriores intactos, entrada cambiada o ausente, script ausente o alterado, referencias ajenas, expirado, gate de capability con el script guardado, datasets, extras),analysis-janitor(reloj inyectado:work/a los 7 días, logs a los 30, script mientras haya artefactosready, expiración, datasets y originales, una vez cada 24 h, bloqueo, temporizador sin referencia, nunca fuera deanalysis/jobs),analysis-application,server-analysis-phase4(POST …/rerun,GET /api/analysis, ajustes),web-analysis-settings, ampliaciones detui-artifacts,settings,settings-menuyrun-events-contract, yfixtures/cli-e2e.ts(analysis statusyanalysis sweep, también con el binario Bun). - Verificado a mano el 2026-10-01 en Linux: Docker real con
python:3.12-slimfijada por digest (las pruebas con motor real pasan);alisio analysis setup --extras analysisinstaló los extras de verdad conuv(3 s con caché) y, sin red (proxy inalcanzable),--extras sciencefalló con el mensaje limpio, código de salida 1, sin carpeta nueva y con el entorno activo intacto; enalisio serve(Playwright, Chromium) con un proveedor simulado y Python real: ejecución con aprobación S, Ejecutar de nuevo desde el menú del panel (nueva ejecución conrerunOf, modelo y proveedor en la procedencia, la anterior intacta), la página Análisis de datos (estado del runtime, edición dejobsDayspersistida en el archivo global, última limpieza), un artefacto envejecido y expirado conalisio analysis sweep --force(la tarjeta dice "Caducado", el desplegable lo lista, el menú ofrece solo Detalles y Ejecutar de nuevo y este recrea un artefacto nuevo) y la aprobación de instalación (paquetes, 75 MB, red; sin botón de sesión, la tecla S no hace nada, D deniega y el resultado nombra el comando opcional). Wheels:scripts/analysis-extras-wheels.tscomprobó 6 plataformas × Python 3.10/3.12/3.13 contra PyPI; huecos reales: Windows Arm (analysissolo con 3.12+,sciencesolo con 3.13) y Alpine/musl (sciencesin scikit-learn). No verificado: Podman, Docker Desktop en macOS y Windows, Windows y macOS (jobportability), la TUI interactiva en un terminal real, Firefox y Safari y lectores de pantalla.
Estado en vivo de la ejecución y tiempos de espera: alcance de la verificación
- Diagnóstico (proveedor DeepSeek, 36 herramientas, petición de ~37 KB): la misma petición respondió en 2 a 7 s en casi todos los intentos (directos y por Alisio), pero de forma intermitente el proveedor aceptó la conexión (HTTP 200 en menos de 1 s) y solo envió comentarios SSE
: keep-alivedurante 54 s a 300 s sin ningún token (una vez el token llegó tras 56 s de cola). No depende del tamaño del prompt ni de las herramientas de análisis. El SDK de OpenAI descarta esos comentarios, por lo que ni el núcleo ni el plugin pueden distinguir «en cola» de «conexión muerta»; el temporizador de 120 s del cliente solo cubre hasta las cabeceras. limits.timeoutMs(300 s) es un límite de reloj de toda la ejecución, no de inactividad. Ahora una ejecución que lo alcanza termina comofailedconrun_failed { code: "timeout", timeout }y un mensaje que nombra modelo, proveedor y qué hacer (antes:cancelledcon «The operation was aborted due to timeout»). La detención del usuario sigue siendocancelled.- Nueva clave
limits.firstTokenTimeoutMs(por defecto120000, ahora90000, ver abajo;0la desactiva): detiene una petición totalmente silenciosa. El propietario decidió (2026-10-01) activarla en 2 min y subirlimits.timeoutMsde 300000 a 600000: DeepSeek llegó a responder tras 56 s de cola, pero un modelo que no emite su razonamiento en streaming puede callar más de 2 min (riesgo conocido: en ese caso hay que subirla o poner0). - Decisión del propietario (2026-10-01): una petición silenciosa se reintenta una vez y el valor por defecto de
limits.firstTokenTimeoutMsbaja de 120000 a 90000 (la petición con 56 s de cola sigue cabiendo). Nueva clavelimits.firstTokenRetries(entero 0 a 3, por defecto1;0lo desactiva). El reintento es genérico y vive en el runner (ningún plugin de proveedor cambia): solo si saltó el temporizador de primer token, no llegó ningún delta de la petición y quedan reintentos; reenvía exactamente las mismas entradas (sin mensajes ni eventos duplicados), no cuenta como turno (maxTurnsintacto) y no se inicia si no cabe enlimits.timeoutMs. Evento aditivorequest_retry { attempt, of, reason, afterMs };RunTimeoutInfo.attempts(aditivo) y mensaje «did not respond after N attempts of 90 s each». Peor caso con los valores por defecto: unos 2 × 90 s más una pausa de 250 ms. Contabilidad: el intento abortado no devuelveusage, así que no suma tokens;requests(usado solo parafirstRequestdel mensaje de timeout) cuenta turnos, no reintentos. Límite conocido: un modelo que no emite su razonamiento en streaming puede callar más de 90 s y recibir un reintento innecesario (hay que subirfirstTokenTimeoutMso poner0). Web, TUI y modo texto sin interfaz muestran «The model did not respond; retrying (1/1)…». InflightState.startedAt(aditivo) permite que una recarga conserve el tiempo transcurrido.- Web: línea de estado (
RunStatus) con fase, tiempo de ejecución y del paso, última actividad, aviso a los 15 s y a los 60 s con Detener, regiónaria-liveque cambia solo con la fase o el nivel. TUI: la pieza de estado del pie muestra la fase yno response for N s. - Pruebas:
tests/run-timeout.test.ts(mensaje, evento y estado en el runner con un proveedor silencioso, parada del usuario,firstTokenTimeoutMs),tests/web-run-progress.test.ts(derivación de fases, umbrales con reloj falso, avisos de tiempo en el transcript),tests/tui-run-phase.test.ts,tests/server-inflight.test.ts. Verificado a mano en Chromium conalisio servey un proveedor falso: fases en orden (esperando, pensando, aprobación, Python, esperando, redactando), silencio a 15 s y 60 s, Detener, y el mensaje de tiempo agotado en EN y ES. - Decisión del propietario (2026-10-01): recuperación automática de una respuesta cortada por
limits.maxOutputTokens(caso real: DeepSeek V4.1 Flash con@alisio/plugin-deepseek0.1.1 al pedir un dashboard; el razonamiento o elargumentsde una llamadapython_runlarga agotaron los 16384 tokens y la ejecución fallaba con «cut off by max output tokens before any usable content»). Nueva clavelimits.truncationRecoveries(entero 0 a 5, por defecto2;0la desactiva). Detección en el núcleo, sin depender del proveedor: mensajecompletedcontruncated: truey (a) sin texto ni llamadas, o (b) alguna llamada con id/nombre vacío oargumentsque no son un objeto JSON completo (argumentos vacíos solo cuentan en la última llamada); un fallo del proveedor concode: "output_truncated"(nuevoOutputTruncatedErroraditivo en@alisio/sdk) o, por compatibilidad con plugins que no podemos cambiar, cuyo mensaje contiene «cut off by max output tokens before any usable content» (la única coincidencia de texto vive enpackages/core/src/core/truncation.ts,isOutputTruncationError). Las llamadas válidas y completas de una respuesta cortada no se descartan (siguen ejecutándose, como antes); si UNA llamada está cortada se descartan TODAS (y losproviderDatadel mensaje). Una respuesta cortada con texto útil y sin llamadas conserva el comportamiento anterior (response_truncated). Recuperación: las llamadas cortadas nunca se ejecutan ni se persisten (los ids siguen consistentes); el texto visible se guarda como mensaje normal del asistente; el aviso de continuación (en inglés) se añade solo a la petición siguiente y no se persiste (la UI web mostraría un mensajesummarycomo «compactado»); no consumemaxTurnsni emiteturn_completed; no se subemaxOutputTokens. En la primera recuperación de una respuesta vacía se baja un nivel el esfuerzo de razonamiento solo para esa petición, si el catálogo del modelo (ModelInfo.effort.supportedLevels) permite ordenar los niveles (nombres conocidosnone…max); con el error heredado no se sabe si fue razonamiento o una llamada larga, y se trata como respuesta vacía. Agotadas las recuperaciones:run_failedconcode: "output_truncated"ytruncation { attempts, maxOutputTokens, model }, mensaje legible (web localizada EN/ES). Evento aditivotruncation_recovery.python_runañade una frase que pide código corto por llamada. El adaptador OpenAI-compatible (chat y responses) ya no lanza error antelengthsin texto: devuelvecompletedcontruncated: true. Pruebas:tests/truncation-recovery.test.ts,tests/truncation.test.ts,tests/web-run-progress.test.ts,tests/tui-run-phase.test.ts,tests/config-layers.test.ts. No cableado: el adaptador@alisio/plugin-deepseek(otro repositorio) sigue lanzando el error genérico (se recupera por la coincidencia heredada) y no distingue razonamiento de llamada cortada; las llamadas de resumen de compactación y de subagentes no usan esta recuperación. - Decisión del propietario (2026-10-01): el presupuesto de tokens de salida efectivo sale del catálogo del modelo.
limits.maxOutputTokensya no tiene valor por defecto en el esquema (se distingue «fijado por el usuario» de «por defecto»); lo que se consume esresolveMaxOutputTokens(packages/core/src/core/output-limit.ts): un valor explícito (cualquier capa de configuración,/settings, el selector web u opción por ejecución) siempre gana, incluso por encima del máximo declarado (no se recorta ni se reintenta; si el proveedor lo rechaza, el error actual no cambia); si no,min(ModelInfo.maxOutputTokens, 65536); si no, 16384. Se resuelve para el modelo de cada petición (cambios de modelo o de agente se notan en la siguiente ejecución) y el valor y su origen (user,model,default) aparecen enresponse_truncated, enrun_failed { code: "output_truncated" }, en el aviso web y TUI y enalisio doctor. El catálogo es el del proveedor activo (GET /models; DeepSeek lo informa conmax_output_tokens, hasta 393216 en V4.1 Flash, que queda en 65536); la primera petición espera hasta 2 s a un catálogo que aún se carga, y los catálogos de otros perfiles solo se consultan si ya estaban listados (se toma el menor valor declarado). Los subagentes siguen con su valor por ejecución (maxOutputTokensPerChild, 16384), la compactación concompaction.maxOutputTokens, y las preguntas laterales (/btw) y los hijos sin presupuesto propio heredan el valor efectivo. Límites: el adaptador OpenAI-compatible integrado no lee un máximo de salida deGET /models(solo la ventana de contexto), así que ahí aplica 16384 salvo que se fije el valor; el valor efectivo no se muestra en/settings(la fila muestra 16384 mientras no esté fijado); el aviso web localizado nombra el origen en EN/ES. Pruebas:tests/output-limit.test.ts,tests/config-layers.test.ts. - No verificado: lectores de pantalla reales, Firefox y Safari, la TUI en un terminal real, y la detección de
: keep-alive: el plugin@alisio/plugin-deepseek(repositorio aparte) tendría que envolverfetchy emitir un evento de latido; no se inventó esa señal.
Modos de permisos, /reload y /changelog (fase 1): alcance de la verificación
- Verificado con Vitest (sin red ni credenciales): la tabla de modos y su coherencia con los presets del servidor (incluidos el techo de
alisio serveyread-only), la política real del runner trassetPolicy(un efectowritepregunta enasky no enauto; unprocesspregunta salvo enfull;analysisno se toca), el orden y la envoltura del ciclo con agentes personalizados (en core, en la decisión de Shift+Tab de la TUI y en la web), los bloqueos de Shift+Tab (turno en curso, picker, autocompletado, panel), la transición de modos con--read-onlybloqueado y el aviso de acceso total, el orquestador de recarga con aplicaciones simuladas y concreateApplicationreales (una configuración rota deja viva la aplicación y su sesión; una corrección se aplica y la sesión persiste), la guarda de inactividad, el parser del changelog (versiones, secciones, saltos de línea de Windows, archivo ausente), el orden de versiones, la lógica delastSeenVersionen la TUI y la web (primer arranque, actualización, bajada de versión, almacenamiento que lanza), los datos embebidos al día, el catálogo de comandos y su paridad con la TUI, y las rutas HTTP (orden de agentes,/permission,/reloadconcatalog_changed,runs_active, configuración rota, 401/403/415,/changelog). - Navegador: comprobado con Chromium (Playwright) contra
alisio servecon un proveedor falso compatible con OpenAI; el resultado detallado está en la entrega. - No verificado: la TUI en un terminal real (el listener de Shift+Tab, el panel de novedades y el menú de modos se probaron como lógica pura y compilados, no en un pseudo-terminal), terminales que no distinguen Shift+Tab, otros navegadores (Firefox, Safari), lectores de pantalla reales y Windows/macOS.
Pestaña Memory de la web y vistas de datos de plugins: alcance de la verificación
- Verificado con Vitest (sin red): registro y validación de vistas, detección sin
api.views(el plugin de memoria arranca en un host sin ella), la ruta HTTP (autenticación, Host, Origen, sesión y workspace inexistentes, plugin deshabilitado, también enrestart-required, vista desconocida, parámetros no válidos sin eco de valores, límite de tamaño, timeout con aborto, errores genéricos, ausencia de parámetros en los logs), las tres vistas contra una base SQLite temporal (solo la sesión pedida, fijadas primero, filtro, búsqueda con comodines literales, cursor sin duplicados ni huecos al añadir filas, solo lectura, resumen, contexto igual a lo devuelto por el hook de inicio), el extremo a extremo con el plugin real en un servidor real, la migración 101 y la lógica de la web (selector, visibilidad y vuelta a Conversación, paginación, filtros, descarte de respuestas obsoletas, errores por sección, paridad EN/ES de los textos). - Navegador: comprobado con Chromium (Playwright) contra
alisio servecon un proveedor falso compatible con OpenAI y una base de memoria temporal: la pestaña aparece solo con el plugin habilitado; las tres secciones (el contexto cargado salió de una ejecución real); filtro, búsqueda y «Cargar más» (20 de 27, sin duplicados); cambio de chat; deshabilitar el plugin en Ajustes → Plugins quita la pestaña, vuelve a Conversación y la ruta responde 404; ancho móvil (390 px) sin scroll horizontal. Presupuesto del bundle inicial dentro del límite (pnpm pack:check). - No verificado: Firefox y Safari, lectores de pantalla reales, Windows y macOS, bases de memoria grandes (miles de entradas por sesión) y el comportamiento con varios workspaces abiertos a la vez.
Tareas en segundo plano (fase 3): alcance de la verificación
- Verificado con Vitest y procesos hijos reales (un script de Node, sin sintaxis específica de shell): salida por offsets sin repetir, marcador y cola de un log truncado, lecturas acotadas sin partir caracteres, código de salida,
cancelledcon origen al detener (usuario y modelo, idempotente) con el árbol de procesos muerto (un nieto), watchdog (failed/timeout) y tope detimeoutMs, carreras parada/salida, compare-and-set sin salir de un estado terminal,lostsolo para propietarios muertos, límites por sesión y por proceso, alcance por árbol de sesiones, cierre ordenado (incluido un proceso que ignora SIGTERM) y la migración v7 (base nueva, desde v6, idempotente). - Política con el ejecutor real:
--read-onlyy el agente plan deniegan las cuatro herramientas (en los tres modos),ask/autopreguntan cada llamada,fullno pregunta, y esquemas, directorio de trabajo fuera del workspace o inexistente. - Notificación: coalescencia, límite de frecuencia y de ráfaga, reintento mientras está ocupada sin pérdidas ni duplicados,
poke,skip, lectura posterior (no se anuncia), sin aviso para tareas detenidas, de sesiones hijas o tras el cierre; de extremo a extremo con el servidor real, el despertar inicia exactamente una ejecución (idbg-<id>), espera mientras la sesión corre y no ocurre en una sesión archivada. - Rutas (401, 403 por Origen, 415, sesión ajena, offset, límite, parada), frame
tasks_changed, tareaslostal arrancar el servidor,/reloadrechazado y cierre del servidor sin procesos vivos; lógica de la TUI (reductor, panel con dependencias simuladas,wakeDecision, guarda de recarga); store de la web (lista, frames, avisos, salida por offset, parada), textos EN/ES y carga perezosa; paridad del catálogo de comandos; configuración y etiquetas. - Navegador: comprobado con Chromium (Playwright) contra
alisio serve(packages/cli/dist/main.js) y un proveedor falso compatible con OpenAI que llama abg_run: salida en vivo, botón Detener, el despertar del agente exactamente una vez al terminar una tarea, ningún despertar tras una parada del usuario y una tarealosttras matar el servidor con SIGKILL. - No verificado: la TUI en un terminal real, Windows y macOS (
taskkill /T /Fy el grupo de procesos), otros navegadores, lectores de pantalla reales y cargas con muchas tareas simultáneas.
Límites conocidos
Objetivos de sesión /goal (fase 4): alcance de la verificación
- Verificado con Vitest: gramática del comando y del presupuesto (
50k,1.5M,clear|none|off|0,budget=duplicado, límites), máquina de estados (transiciones por actor, reactivación al subir el presupuesto, reanudación con asignación nueva, reinicio), lista de bloqueadores y su orden, prompts (contrato una vez, pista estable, recordatorio cada 5 turnos, avisos); servicio con SQLite real y reloj falso (recuento acumulado entre ejecuciones, tope duro y por ejecución, turnos, tiempo, disyuntores, auditoría deblocked, error →blocked, reclamación idempotente, kickoff una vez, carreras de dos superficies porepoch/goalId, pausa por reinicio); ejecutor real con proveedor guionado (herramientas opt-in, objetivo completado en tres turnos, presupuesto duro, lo que el modelo no puede hacer); servidor real (rutas, frames, snapshot, 401/403, esperas por tarea, modo plan y aprobación pendiente, cancelación, reinicio, comando/goal,goal.enabled); lógica de la TUI (planGoalCommand, selector, barra, aviso de parada); store de la web (acciones conexpect, conflicto, reemplazo con confirmación, formulario, filas de la paleta, textos EN/ES de cada código, carga perezosa); migración v8; paridad del catálogo de comandos; etiquetas de ajustes. - Navegador: Chromium (Playwright, interfaz en español) contra
alisio serve(packages/cli/dist/main.js) y un proveedor falso compatible con OpenAI: un objetivo que continúa 3 turnos y termina conupdate_goal complete; presupuesto pequeño → Presupuesto agotado sin más ejecuciones y reactivado al subirlo desde el formulario; respuesta repetida → pausano_progress; botones Pausar, Reanudar, Editar y Borrar; espera por permiso pendiente; espera por una tareabg_runy continuación al terminar; sin continuación en modo plan y continuación al pasar abuild; recarga de la página y reinicio del servidor con SIGKILL (el objetivo vuelve en pausa,restart). - No verificado: la TUI en un terminal real (su flujo
task()→ continuación está cubierto por la lógica pura y por las pruebas del núcleo, no por un terminal), otros navegadores, Windows y macOS, lectores de pantalla reales, proveedores reales (el recuento depende de que informenusage) y goals de muchas horas.
Decision Intelligence: alcance de la verificación
- Verificado con Vitest (sin red; proveedor falso en memoria en
tests/fixtures/decision-provider.ts): servicio (resultado parcial, fallback por motivo, tiempo límite con proveedor colgado, aborto del llamador), validación de peticiones y respuestas, disyuntor con reloj inyectado, eventos y prueba de privacidad con cadenas centinela sembradas enstate, frontera del núcleo (el código de decisiones no menciona ningún motor concreto yrunner.tsypermissionsno importandecisions/), configuración y capas global-only, ciclo de vidaactivate/deactivate, rutas y opciones por plugin, cierre en paralelo con tope por plugin, comando/decisions, coincidencia de los tres cálculos de/stats, contrato de eventos, paridad del catálogo de comandos y etiquetas EN/ES.fixtures/cli-e2e.tscomprueba quealisio runejecutadispose()conSIGTERM,SIGHUPySIGINT. - Navegador: Chromium (Playwright, interfaz en español) contra
alisio serveconstruido, un modelo falso compatible con OpenAI y un plugin local temporal con un proveedor falso: una sesión con decisiones completadas y una con alternativa, y/decisionscon las métricas. Es la captura de la documentación; no es una prueba de calidad de decisiones. - Fase 0 (E1 a E7, fuera del repositorio, con Laya 0.3.24 y el punto de control
multilingual, en CPU y CUDA): el valor por defecto detimeoutMs(1500 ms) sale de ahí, y E2 mostró queconfidenceno está calibrada. - No verificado: un proveedor real (no hay ninguno incluido ni publicado), la TUI en un terminal real, otros navegadores, Windows y macOS, lectores de pantalla, un proveedor remoto, el comportamiento con muchas decisiones concurrentes bajo carga y la calidad de las decisiones de cualquier motor.
Smart Dashboard: alcance de la verificación
- Verificado con Vitest (sin red ni proveedor real): perfilador y candidatos, pack de decisiones con una prueba de privacidad de cadenas centinela sembradas en celdas, valores frecuentes, mínimos y máximos, planificador con y sin decisiones, validador y su tabla de reparación, planificador de consultas sobre un dataset SQLite real, renderizador (estructura, escape y formato de KPI), prueba de paridad con
alisio_runtime.charts(se salta sin Python), la herramienta de extremo a extremo con un publicador real, pruebas golden de losDashboardSpecde unos pocos datasets pequeños revisados a mano, capas de configuración (globalfalsefrente a un bloqueanalysisde proyecto) y paridad de etiquetas EN/ES. - Fase 0 (E1 a E5, fuera del repositorio): las reglas de roles funcionan con las estadísticas actuales en 8 datasets; la consulta más lenta tardó 370 ms con 500 000 filas (límite de 5000 ms), así que no hay muestreo; la expresión de semana de SQLite se comprobó sobre 1461 días;
chart-linty el visor no dependen del marcadoac-*. - Navegador: Chromium (Playwright) contra
alisio serveconstruido y un modelo falso compatible con OpenAI, con datos de ejemplo: el dashboard en el visor aislado y la fila de progreso. Son las capturas de la documentación; no son una prueba de calidad de los dashboards. - Benchmark (fase 8, 2026-10-03, un solo modelo:
deepseek-flash): A 45 ejecuciones, B 15 (1 repetición por dataset), C 5 (informativa, en paralelo con B, tiempos no comparables); la puerta de §11.2 falla en tokens de salida y turnos. La línea base A está limitada por el presupuesto de tokens, y el orden de las llamadas solo se reconstruyó en C. - Ejecución real con Laya (2026-10-04, interfaz web, compilación candidata de 0.4.3,
@alisio/plugin-laya0.1.1,deepseek-flash, un CSV de ventas de 4000 filas renombradoventas.csv, prompt en español sin preguntas y modo «Acceso total», sin esperas del usuario; un solo dataset, un solo modelo y una sola sesión por configuración): con Laya el primer dashboard se publicó a los 5,0 s del envío,dashboard_generatetardó 161 ms y hubo 2 decisiones completadas (101 ms y 30 ms, ninguna rechazada, sin fallback; procedenciarules+decisions); el modelo no usópython_rundespués y la ejecución completa tomó 32,3 s. Sin proveedor y con la memoria desactivada, el primer dashboard llegó a los 4,5 s (52 ms), el modelo hizo 4 llamadas apython_runy publicó un dashboard extra en Python (canal x región), 45,2 s en total; con una sola ejecución por configuración no se puede atribuir esa diferencia a Laya. Una tercera ejecución (memoria activada, sin proveedor) se descartó porque una nota guardada por una ejecución anterior orientó al agente./decisionsinformó 2 peticiones, 0 fallbacks, latencia media de 66 ms y p95 de 101 ms. La primera ejecución (0.4.2 publicada) tardó 672 s por las preguntas del agente. Capturas en la documentación. - No verificado: otros modelos en el benchmark; la variante C a mayor escala; la línea de progreso en un terminal real; otros navegadores, Windows y macOS; lectores de pantalla; y datasets muy grandes en el navegador.
Límites conocidos
Runtime y empaquetado
- Runtime: Node no carga
.envautomáticamente (Bun sí); use variables de entorno onode --env-file=.env. Los plugins.tslocales requieren Bun o Node >=22.18; los paquetes npm de plugins deben publicarse en JavaScript. La condición de exportalisio-sourcesolo se usa en desarrollo dentro del monorepo y no se publica.
Agentes del usuario
- Solo se escriben y listan los ámbitos
.agents/agents(proyecto y global); los agentes en.alisio/agents,<config>/agents,.claude/agentsu.opencode/agent(s)se siguen cargando pero no se editan desde la ventana Agentes ni desde/agents manage. No se puede mover un agente entre ámbitos. reasoning.summary,text.format.typeytext.verbosityse guardan y se muestran, pero el contratoModelProvider.streamaún no tiene campos para ellos: no se envían al proveedor.reasoning.effortsí se aplica (como effort por defecto del agente) y el modelo del agente se usa al iniciar un chat con él.- La descripción de la herramienta
task(lista desubagent_typeanunciados al modelo) se fija al arrancar: un agente nuevo se puede delegar en cuanto se recarga, pero el modelo solo lo ve anunciado tras reiniciar. Las tareas de subagentes en curso conservan su definición. - El estado publicado por el plugin de subagentes (
mainAgents,definitions) es global en la base de datos, no por workspace: con varios workspaces abiertos enalisio serve, el último en recargarse define el catálogo (limitación previa; tras cada escritura se recarga el workspace que escribió en último lugar). - Sin vigilante de archivos; las ediciones externas se recogen al abrir la lista o con
/agents reload. - Las instrucciones en la TUI se editan en una línea (
\npara saltos); para textos largos use "✦ Refinar con Alisio", la web o un editor externo. - En la web, activar un agente cuyo modelo es de otro proveedor no cambia el modelo de ese chat (lo indica); "Probarlo en un chat nuevo" sí lo usa si los perfiles configurados lo resuelven.
Subagentes
- Subagentes: los mensajes en cola (
send_message, notificaciones) viven en memoria y se pierden al salir; una notificación en segundo plano llega con el siguiente turno del padre; las skills de una definición no se preinyectan (se pide cargarlas conskill_load); los worktrees solo se usan cuando se solapan escritores;/agents mergeexige árbol limpio y no resuelve conflictos; el panel muestra las tareas iniciadas en este proceso;killequivale acancel.
Proveedores, plantillas y licencia
- Proveedores: la resolución cruzada solo ve perfiles globales creados mediante
/connect; la configuración raíz heredada no es un catálogo oculto. El catálogo se mantiene en caché hasta 15 segundos por proceso. - Plantillas: sin inclusiones ni parciales, sin ejecución de comandos ni inyección de archivos; solo
$1..$9posicionales;/initdepende del modelo para limitarse a hechos verificados y consume bastantes tokens en repositorios grandes (limits.maxTokens). - Pantalla de inicio: con
TERM=dumbsolo la pantalla de inicio pasa a ASCII; el resto de la TUI (cabecera, barras) sigue usando glifos Unicode. El ancho se cuenta por punto de código, así que glifos anchos (CJK, emoji) en mascotas personalizadas pueden desalinear. Los proveedores son síncronos: un proveedor lento no puede interrumpirse, solo descartarse. - Licencia MIT.
Memoria
- Memoria: la búsqueda usa el tokenizador trigram, así que los términos de menos de 3 caracteres se ignoran. Sin búsqueda semántica. El resumen automático de cierre solo se ejecuta en la TUI (no en
runheadless) y está acotado porpluginHooks.sessionEndTimeoutMs; si vence, la salida continúa sin resumen. El resumen de cierre reemplaza al checkpoint archivado de la misma sesión (un resumen por sesión). Los prompts de usuario se copian a la base de memoria (redactando<private>) al compactar y al cerrar; la base es local con permisos 0600. - Detalles de implementación de memoria: la tabla de sesiones se llama
memory_sessions(la base puede compartir archivo con las sesiones de Alisio); no hay FTS de prompts; eltopic_keyen alcancepersonalhace upsert entre proyectos (para que las preferencias sean realmente personales); las líneas de contexto incluyen#idparamemory_get; el checkpoint de compactación se archiva como resumen de sesión y no como observaciónsession/compaction-recovery, y la recuperación se inyecta de forma determinista sin pedir al modelo que llame herramientas. No hay sincronización en la nube, relaciones, juicio de conflictos ni ciclo de revisión. - Efecto
internal: las herramientas de memoria no modifican el workspace ni la red y se permiten incluso con--read-only. Si se prefiere un modo estrictamente sin escrituras, use--disable-plugin memory.
Plugins e instalación
- Plugins:
model.completeusa el proveedor configurado (el modelo de la sesión cuando el plugin lo pasa) y no descuenta del presupuestolimits.maxTokens. Los hooks corren en proceso: el timeout aborta la espera y señala elAbortSignal, pero no puede detener código síncrono bloqueante. - Instalación de plugins (
alisio install, herramientaplugin_install): solo npm y global — los paquetes aterrizan en<config home>/pluginsmediantenpm install --prefixy su nombre npm se guarda en el arraypluginsde la configuración global. Instalar es una acción por usuario; la carga sigue la política existente de plugins ejecutables (confianza del proyecto para la configuración y plugins del proyecto;--read-onlyimpide cargar). No hay sandbox de scripts:npm installpuede ejecutar scripts de ciclo de vida con tus privilegios y la herramienta solo avisa/pide confirmación (headless exige--yes/--trust-plugin).alisio install --updatesin spec actualiza todos los plugins instalados en una sola llamada de npm, y un falloERESOLVEindica ese comando (verificado con un npm falso, sin red). No se soportan URLs de registro (registry:,git:,file:), ni resolución de dependencias propia de Alisio: el paquete debe declarar la keywordalisio-pluginpara poder cargarse.
Portapapeles, pegado y TUI
- Portapapeles: OSC 52 no puede confirmarse; la TUI lo informa como no verificado.
- Pegado y adjuntos: el acceso al portapapeles de imágenes necesita un ayudante nativo de la plataforma (o
wl-pasteen Wayland); suele faltar en sesiones SSH simples. No hay comprobación de capacidades antes de enviar una imagen: el rechazo del propio modelo aparece como un error en línea normal. Los límites de 5 MB por imagen y 4 adjuntos por mensaje los aplica la TUI, no@alisio/core(quien use el runner directamente puede enviar más o mayores). El pegado de varias líneas en modo--no-tuino es atómico:readlinede Node no admite pegado con corchetes, así que cada salto de línea envía su propio mensaje; el pegado de una sola línea no se ve afectado. Las imágenes no tienen ningún soporte en modo--no-tui. - TUI: las estadísticas de
/statscubren solo el proceso actual de la TUI para la sesión activa; no se reconstruyen desde eventos persistidos. La TUI necesita una terminal con pantalla alternativa; en otros casos use--no-tuiorun. - TUI (plegables de presentación): el clic para alternar es opcional — el atajo
xcon la entrada vacía es el camino garantizado; el mapeo del clic usa la altura renderizada en el momento del clic y el ancho de ese frame, por lo que en anchos muy estrechos o con filas de altura variable entre frames el acierto puede desviarse una fila (nunca rompe la selección: un clic sintetizado solo se detona sin arrastre). El marcador de plegado aparece solo cuando la fila es plegable: las filas sin marcar (run_processcon salida corta, diffs, rich ui/imagenes) no alternan nada. Las vistas previas dentro de un grupo expandido van acotadas (3/6 líneas) y no son plegables individualmente; el razonamiento expandido se acota a 40 líneas. La duraciónThoughtes una aproximación del primer al último delta de razonamiento y solo existe cuando el evento llevaba marcas de tiempo (no en sesiones reanudadas).
Skills y contexto
- Skills: el coste de tokens mostrado por
/skillses una aproximación uniforme de bytes/4, no el tokenizador del proveedor. Una skill de un plugin desactivado tras reiniciar deja de existir en el catálogo; un cambio pendiente del plugin conserva la skill bloqueada y marca su origen como pendiente de reinicio. Los diagnósticos headless pueden incluir rutas de confianza; la TUI no. provider.contextWindowse aplica solo al modelo configurado; tras/model, la ventana proviene del overridevalues.contextWindowdel perfil activo de/connect(preguntado en/connectcuando el catálogo no informa la ventana del modelo seleccionado, pensado para servidores locales como llama.cpp que omitencontext_window; prevalece sobre el catálogo por intención del usuario), deGET /models(el catálogo se carga de forma perezosa al arrancar y se refresca tras cada cambio de proveedor/modelo) o queda como desconocida: la barra de contexto muestra un honesto~9.9k / ?sin total inventado, y la compactación automática por umbral se desactiva para ese modelo salvo porlimits.maxContextChars(salvaguarda interna, nunca total mostrado). La cobertura usa perfiles guardados y activación de/connectcon catálogos ficticios; la entrada interactiva de/connectse verifica por tipos y manualmente, no con pruebas de UI.
Compactación y truncamiento
- La compactación usa el proveedor actual; su consumo de tokens no se suma al presupuesto
limits.maxTokens. Las estimaciones antes/después son aproximadas (≈4 caracteres/token). Los items opacos de Responses del tramo resumido se descartan; los conservados no cambian. Una sesión con resultados de herramientas inciertos no se compacta hasta recuperarla. - Respuestas truncadas: cuando una respuesta se corta por
limits.maxOutputTokens(o el resumen de compactación porcompaction.maxOutputTokens), el texto producido se conserva tal cual. Una respuesta cortada completa la ejecución con aviso y marcatruncated; un resumen cortado se guarda como checkpoint parcial — la información producida antes del corte se preserva, pero un resumen truncado puede omitir contexto posterior. El resumidor estima tokens por su cuenta (≈4 caracteres/token), así que un resumen cerca de su presupuesto puede cortarse aunque el modelo no esté cerca de su límite; el presupuesto real consumido lo informa el proveedor y no puede comprobarse de antemano.
Permisos, aprobaciones y confianza
- Aprobaciones: para los efectos
write,processyexternal, y solo en la TUI (la propia TUI ya pasa siempre un manejadorapprovesalvo con--read-only, así que sin ningún flag el efecto se ofrece y se pregunta en cada llamada; ver la tabla de verdad endocs/tools.md); los modos headless (run,resume "prompt",--json) nunca tienen un manejador y por tanto nunca preguntan — sin flag, el efecto simplemente no está disponible ahí. "Permitir en la sesión" dura mientras viva el proceso. La espera no cuenta paralimits.timeoutMs(tiempo activo, ver «Revisión del plan»). El contratoPolicyno cambió: la aprobación es una opción adicional deRunnerOptions. - Confianza de proyecto: el hash guardado cubre solo el contenido de
.alisio/config.json; si cambia únicamente otro recurso de proyecto (por ejemplo se añade.alisio/agentssin tocarconfig.json) no se vuelve a preguntar automáticamente — revóquelo conalisio trust revokesi hace falta. El prompt de confianza es unreadlinesimple antes de la pantalla alterna, no la cola deask_user_question: esta última se construye a partir de unaApplicationya creada, y crear esaApplicationes exactamente lo que la decisión de confianza controla, así que no podía usarse aquí. El almacén vive en<ALISIO_STATE_HOME>/trust.jsoncon permisos 0600 (directorio 0700); no está pensado para compartirse entre máquinas ni usuarios.
Preguntas y herramientas de red
ask_user_question//ask: la cola interactiva compartida cubre aprobaciones, elselectde plugins y las preguntas, pero deliberadamente NO incluye los selectores propios de/modelni/resume(siguen con su mecanismo previo sin cambios): son comandos que el usuario escribe él mismo, nunca concurrentes con la pregunta de un subagente, ychooseModel()ya no esperaba la resolución del selector antes de esta tarea, así que integrarlos habría exigido una reestructuración ajena al alcance. Retroceder a una pregunta de selección múltiple sin confirmarla descarta su selección provisional (solo las respuestas ya confirmadas sobreviven a ir hacia atrás y hacia adelante); al volver a entrar en una pregunta de selección múltiple sin respuesta previa, su opción por defecto (la recomendada, o la primera) queda premarcada, para que confirmar sin tocar nada sea una elección deliberada y no una selección vacía accidental — esto no aplica a la propiainitialQuestionStatede un lote nuevo, que empieza sin nada marcado. No se probó con subagentes reales concurrentes en una terminal (ver la sección de verificación).Un
resumecon otro modelo ya no falla: la sesión es la fuente del modelo y--modello cambia explícitamente para los turnos siguientes.Plugins en proceso pueden bloquear el event loop o saltarse servicios mediados. Solo código confiable; los timeouts del motor no pueden detener código síncrono hostil.
executeusa el módulovmde Node, que no es un mecanismo de seguridad (documentación oficial de Node): aísla el ámbito global del snippet y acota su tiempo, pero no es una frontera a nivel de sistema operativo; un exploit de V8 podría escapar. CadacallToolanidado respeta el efecto/permiso ya concedido a la sesión, pero nunca puede solicitar uno nuevo. Límite fijo de 10 s y 20 llamadas anidadas (no configurables por el usuario final en esta versión).webfetch/websearch(efectoexternal) siguen sin sandbox de red:--allow-externalda al modelo acceso a cualquier URL http(s) alcanzable, igual que--allow-processda acceso a cualquier ejecutable. El proveedor SearXNG por defecto (sin configurar nada) es poco fiable en la práctica frente a instancias públicas con protección antibots; véase la sección de verificación. DuckDuckGo Instant Answer solo responde consultas factuales directas, nunca búsqueda general.
Persistencia, estadísticas y Herdr
- El bloqueo SQLite por PID está diseñado para procesos locales en un host, no para una base compartida en red. La reutilización de PID puede exigir intervención del usuario.
- La validación de rutas no es un aislamiento OS. La shell y plugins tienen permisos del usuario.
- Las estadísticas dependen del proveedor. Si no envía usage, el límite de tokens no es exacto; siguen aplicando límites de turnos, tiempo, longitud del contexto y salida por petición.
- El adaptador chat soporta texto y function tools; bloques privados de razonamiento de proveedores de terceros no se normalizan. Para continuation de OpenAI use Responses.
- La sesión restaura el historial activo (el compactado queda archivado). Hay migraciones hacia adelante e idempotentes (v1 → v4); no hay migraciones hacia atrás.
- Los blobs no tienen recolección de basura. Un adjunto resuelto desde un blob se persiste en el mensaje como base64 (igual que un adjunto en línea), porque
Attachment.datasigue siendo obligatorio; el blob solo evita repetir la subida. interruptRuns()conserva las ejecuciones cuyoowner_pides el del proceso actual (otraApplicationdel mismo proceso); si el PID de un proceso muerto se reutiliza, esas filas siguen enrunninghasta el siguiente arranque con otro PID.- Los manejadores core del catálogo son deliberadamente mínimos:
modelcambia el id de modelo de la sesión sin cambiar de proveedor,effortpersiste el nivel sin validarlo contra el catálogo del modelo (la TUI sí lo valida) ystatsresume el registro de ejecuciones, no las estadísticas en memoria de la TUI. La TUI solo delega/toolsy/sessions; el resto de comandos sigue en suswitch. - Lectura/edición de texto limitada a 1 MiB. Búsquedas/salidas extensas se truncan explícitamente.
- La integración Herdr permite intercambio por terminales; no promete autonomía multiagente completa ni planificación distribuida.
Agente activo y effort
- El effort se resuelve contra el catálogo del modelo activo en la TUI (carga asíncrona de
GET /models): hasta que el catálogo llega, o si la consulta falla, no se envía ningún effort (degradación honesta, nunca un nivel inventado) y el segmento de effort se omite; cuando el catálogo por fin llega se repinta. En los modos headless (run,resume,--no-tui) el effort NO se envía (es una función de la TUI); el agente activo sí se aplica (prompt y acotación de solo lectura). - El agente activo es una función del proceso actual: el cambio de agente se persiste en la capa de usuario, pero el cambio de modelo por agente sigue la semántica de
/model(sesión nueva). Un agente conmodelno se re-aplica automáticamente si el usuario cambia el modelo después con/model— esa elección explícita del usuario gana hasta que se vuelva a elegir el agente. reasoning_effort/reasoning.effortse envían tal cual (validados contrasupportedLevelsdel catálogo del modelo activo): el proveedor remoto es la autoridad final y puede rechazar un nivel que su catálogo ya no soporte; la aceptación real de cada nivel solo se verifica contra el servidor de pruebas, no contra la API pública.- Los comandos
/agents//effortusan el nombre reservadoagentsque también registraba el plugin de subagentes: los verbos de gestión de tareas (con argumento) siguen enrutándose al plugin, pero el autocompletado del editor y/helpmuestran solo el comando de la TUI; la gestión de tareas sigue siempre disponible como/agents <verbo>y/command agents <verbo>.
Servidor web
/btw: la transcripción que ve la pregunta lateral es texto (cada mensaje recortado a 4 000 caracteres, adjuntos solo como metadatos, sin datos de continuación del proveedor), no el historial nativo; el effort de razonamiento es el por defecto del runner (no el effort por sesión de la web); el historial compartido entre TUI y servidor es lectura-modificación-escritura sin bloqueo entre procesos (dos preguntas simultáneas desde procesos distintos pueden perder una entrada); la ruta no tiene idempotencia porrequestId; en la TUI, una aprobación que llega mientras el panel está abierto lo sustituye (la respuesta queda en el historial y un aviso lo indica).- Bloqueo de un solo host (PID en
sessions.locked_pid); la web no recibe en vivo los cambios que hace una TUI en una sesión: se ven al reabrirla. Una sesión usada por otro proceso responde409 session_locked. - Sin TLS;
--allow-remotees opcional y pensado para túneles SSH. Con enlace a una dirección comodín (0.0.0.0,::) se aceptan cabecerasHostcon IP literal además de las de loopback. - El proveedor es por workspace (una
Application): el cambio de perfil de proveedor afecta a todas sus sesiones; por sesión solo cambia el modelo (runner.setModel), y el eventomodel_changedno llevacorrelationId(tampoco la compactación manual). - La idempotencia de los prompts encolados es en memoria (100 ids por sesión, 10 min) y la cola de
enqueuedel runner se pierde si el proceso cae; un prompt con adjuntos durante una ejecución, o cualquier prompt mientras la sesión espera en cola o compacta, responde409 session_busy. - Gestión (fase 5): no se instalan plugins desde la web; un cambio de plugin se aplica cuando el workspace queda sin runs (se recarga su app); el permiso MCP concedido desde la web vale para un workspace hasta que se detiene el servidor (salvo "recordar"); la credencial nueva de un perfil activo se usa al volver a activarlo;
DELETE .../credentialsborra todas las del perfil; la edición de definiciones de agentes queda fuera de v1; los estados de servidores MCP se muestran con su identificador sin traducir. - Renderizadores ricos (fase 5): Mermaid pesa varios cientos de KB gzip repartidos en chunks (se carga solo con un diagrama visible); Mermaid incluye su propia copia de KaTeX para diagramas con fórmulas, distinta de la del renderizador
math. - Archivos (fase 4): el árbol, la lectura y el diff se limitan al workspace (las raíces de
--add-dirno se exponen);safePathrechaza cualquier segmento que sea un enlace simbólico, así que los symlinks se listan pero no se abren aunque apunten dentro del workspace. El filtrado por.gitignorese hace por página (una página puede traer menos de 1 000 entradas) y necesitagit; singitse muestra todo salvo.git. Cambios deriva de las llamadas con efectowrite(argumentopathdewrite_file/edit_file) de la sesión y sus hijas y solo se anota congit status: los archivos que cambia un comando de shell no aparecen. Rutas fuera del workspace se omiten. El diff de un archivo sin seguimiento o de un repositorio sin commits se genera frente a/dev/null(máx. 1 MB leído); el de un archivo con seguimiento esgit diff HEAD(incluye lo preparado y lo no preparado). La vista previa HTML/SVG es solo código (sin iframe). - Blobs (fase 4): sin recolección de basura (spec §9.3); el tipo guardado es el detectado por bytes mágicos, no el declarado; la cabecera
Content-Typede la subida solo admite los cuatro tipos de imagen oapplication/octet-stream. - Métricas (fase 4): la línea de estadísticas se calcula en el cliente a partir de
run_started/turn_completed/tool_completed(paginados porGET /eventscontypes=); los tokens por segundo solo cuentan turnos que informanusage.outputydurationMs; con eventos de versiones anteriores sindurationMslos tiempos LLM salen en 0. - La interfaz web no vigila el stream con un temporizador de 45 s: el latido del servidor es un comentario SSE que
EventSourceno expone, así que la reconexión depende del propio navegador y deonline/visibilitychange. La salida de comandos, los avisos y el razonamiento solo existen mientras la página está abierta; tras recargar, las filas de herramientas no muestran su duración. Ctrl+Klleva el foco a la búsqueda del sidebar (no hay una paleta de sesiones aparte), y los turnos terminados no se pliegan a un resumen "N pasos".- Cada reconexión SSE recibe un snapshot completo (no se reenvían solo los eventos desde
Last-Event-ID); la trayectoria enGET /api/sessions/:sid/eventsusaevents.seqglobal comoseq. Tras una compactación el cliente debe recargar los mensajes (el eventocompaction_completedse lo indica). - El binario independiente sirve solo la API y una página provisional: los assets web van con el paquete npm.
Análisis en Python y artefactos
- Python administrado no es un sandbox: el script se ejecuta con los permisos del usuario, puede leer archivos, usar la red y modificar el repositorio; un proceso que haga
setsidpuede escapar del grupo de procesos al cancelar. El modooci(Docker o Podman, imagen fijada por digest) bloquea la red, monta solo las carpetas del trabajo y limita memoria, CPU y procesos, pero un contenedor no es una frontera frente a una vulnerabilidad del kernel o del motor; solo se verificó en Linux con Docker. La imagen debe traer sus paquetes (el entorno de extras no se usa enoci). - Panel de artefactos (fase 2):
Escpulsado dentro de un dashboard no llega a la app (el iframe aislado se queda con el teclado; queda el botón Cerrar); los dashboards no tienen red,localStorage, scripts de módulo ni fuentes desde archivos (sinAccess-Control-Allow-Originlas peticiones CORS de un origen opaco fallan: fuentes en línea comodata:); el enlace del visor caduca a los 10 minutos y los recursos que un dashboard pide más tarde fallan hasta reabrirlo; el PDF usa el visor del navegador sinsandbox(D9) y, sin visor integrado, la descarga; CSV/TSV/XLSX se abren enSpreadsheetView(fase 3). La página de Ajustes "Análisis de datos" (fase 4) editaanalysis.enabled, el tiempo máximo y la retención;runtimeyoci.*solo se editan en el archivo. runToolCall(copiar al workspace desde la web o la TUI) deja en el historial una nota de usuario, una llamada del asistente con un id de 9 caracteres, el resultado y un resumen del asistente: el modelo ve la copia en su siguiente turno como si la hubiera pedido él.- El permiso persistido se registra para la sesión raíz;
executesolo alcanzapython_runcon--allow-analysis/--allow-process, nunca con un permiso guardado. El uso de un permiso guardado no crea fila de auditoría (sí las decisiones y las ejecuciones por flag). - La aprobación no muestra la ruta del intérprete (sí lo hace la página de Ajustes y
alisio analysis status). La procedencia incluye modelo y proveedor desde la fase 4. - No existe la operación de borrar sesión, así que sus permisos no se limpian. La retención (
AnalysisJanitor) corre como máximo una vez al día y solo con Alisio abierto y escribible; el "último uso" de un dataset es la fecha de modificación de su archivo; los valores son solo globales. Un rerun usa las copias de entrada del trabajo original y se rechaza cuando el barrido ya borró el script. Extras: sin red no se instalan; sin wheel descienceen Alpine ni en Windows Arm anterior a Python 3.13. No se implementaron el origen separado del visor, el lector XLSX en Node, las plantillas de artefacto ni XLS/ODS/Parquet (opcionales de §23). - Las entradas (
inputs) se copian siempre (sin enlace duro), tambiéndatasetId(fase 3). alisio servesigue abriendo el navegador con su comando anterior (cmd /c starten Windows);open-path.tssolo se usa para archivos de la TUI.- La línea
artifact: …de los modos sin TUI va a stderr y no la oculta--quiet(no es una pista, es la ubicación del resultado).
Datos tabulares
- Los datasets son archivos SQLite de
node:sqlite(experimental en Node 22) leídos por un proceso aparte; sininterrupt()ni authorizer, la seguridad es la conexión de solo lectura más la guardia léxica de sentencia única. Sin índices: ordenar y filtrar recorren la hoja y se desactivan por encima deanalysis.data.maxInteractiveRows; la hoja con orden o filtro solo alcanza por salto las primeras 100 000 filas (después se carga al desplazarse). - XLSX requiere Python 3.10+ (helper con la biblioteca estándar; omite filas vacías; las celdas combinadas conservan el valor de la esquina superior izquierda); XLS, ODS y Parquet no se leen. Los JSON (no JSONL) se leen en memoria hasta 50 MiB. La cuadrícula recorta celdas de más de 4 096 caracteres y el modelo ve 2 KiB por celda.
- La ingesta de más de 1 000 000 filas calcula
distinct/más frecuentes sobre una muestra (distinct_exact=0). Los originales subidos desde la web quedan enblobs/hasta que la retención borra su dataset (30 días sin uso por defecto).
Modos de permisos, recarga y novedades
- Un modo solo decide qué efectos se ejecutan sin preguntar: no es un sandbox, no cambia el confinamiento de rutas (los directorios externos siguen preguntando) ni la preconcesión de
--allow-analysis.autoson reglas fijas, sin clasificador de IA. - En la TUI el modo inicial es solo una etiqueta derivada de los flags: si plugins o MCP ya permitían
externalal arrancar, la etiquetaaskno lo refleja hasta elegir un modo (/permission statusmuestra la política real). Cambiar de modo reinicia las aprobaciones «para esta sesión»; se rechaza durante un turno y bajo--read-only. - Shift+Tab en la TUI depende de que la terminal lo distinga (
\x1b[Zo el protocolo Kitty); en la web intercepta una tecla de navegación y por eso el selector de agente es la vía accesible. Alternar no aplica el modelo del agente (usa/agents). /reloadreconstruye la aplicación completa: los servidores MCP y plugins se reinician, el código de plugins ya importado no se recarga (el informe lo dice), los flags de arranque conservan su valor y se rechaza con subagentes o aprobaciones pendientes. Durante la recarga web coexisten un instante dos aplicaciones del mismo workspace sobre la misma base de datos. No hay/reloadni/changelogenalisio run.- El changelog es solo inglés, manual y curado (
CHANGELOG.md); una versión sin entrada no genera aviso, yUnreleasedse muestra como «sin publicar».
Pestaña Memory y vistas de plugins
- Las vistas de datos son de solo lectura por contrato, no por aislamiento: el host solo controla el método, los parámetros validados, el tiempo (5 s) y el tamaño (1 MiB); no puede impedir que el código de un plugin escriba y un handler síncrono que bloquee el bucle de eventos no se interrumpe con el timeout. Un plugin no es un sandbox.
- La pestaña no se actualiza en vivo (botón Refresh). Una memoria actualizada o fijada mientras se pagina puede saltar al principio de la lista (no se repite); una memoria con
topic_keyque otro chat actualiza pasa a ese chat (el upsert reasignasession_id) y sale de la lista del primero. Las sesiones hijas (subagentes) tienen su propiosession: no aparecen en la pestaña del chat padre. - «Context loaded» es lo que el plugin devolvió en
session.onStart, guardado por el plugin; no prueba que el runner lo persistiera (si se aborta entre el hook y elappend, queda registrado sin llegar al transcript; el runner además trunca cada texto) y se muestra completo hasta 50 000 caracteres. Los chats anteriores a esta versión no tienen contexto registrado y el contexto recuperado tras una compactación no se guarda. - La memoria puede contener datos sensibles del proyecto: la pestaña los muestra a quien tenga la cookie de sesión del servidor. Cada entrada se recorta a 10 000 caracteres en la lista.
- El selector «plugin habilitado» de la web depende de
GET /api/plugins(se refresca al abrir una sesión y concatalog_changed); un plugin habilitado pero de una versión sinapi.viewsmuestra el error de la sección con reintento en lugar de ocultar la pestaña. - Ajustes → General: las etiquetas de los ajustes viven en
components/settings/labels.ts(carga perezosa con la página) y una prueba falla si una clave ajustable del servidor no tiene etiqueta en inglés o en español; una clave desconocida muestra su nombre y el traductort()nunca lanza.
Revisión del plan (exit_plan)
- El agente
planes de solo lectura porque la política de su ejecución no permite escritura, procesos ni red y no tiene aprobaciones: ningún modo de permisos ni preset la amplía.exit_plan(efectoread) es opt-in: solo la ejecución del agenteplanintegrado lo ve;build, los subagentes y Code Mode no pueden llamarlo. Un agente personalizado de solo lectura no lo recibe. - El estado vive en
sessions.options.plan(sin migración): una entrada por sesión, la última propuesta. Todas las transiciones son compare-and-set en una transacciónBEGIN IMMEDIATE, por lo que aprobar dos veces (doble clic, dos pestañas, recarga) inicia un turno. La instantánea aprobada se conserva solo mientras estáapproved; después vive en el mensaje de usuario del turno de implementación. - El cambio a
buildy el turno de implementación ocurren cuando termina la ejecución del plan, no en mitad de ella (el modelo recibe antes el resultadoapprovedy responde una última vez). En la TUI el cambio esagents.active(global, como/agents); en la web,session.options.agent, en la misma transacción que la reclamación. Si el cambio de la TUI falla, la aprobación se pierde y se avisa cómo continuar a mano. - Cancelar la ejecución retira la revisión pendiente y descarta una aprobación que no había empezado. El tiempo máximo de la ejecución (
limits.timeoutMs, 10 min por defecto) cuenta tiempo activo: la espera de la decisión no lo consume (ver abajo). Una revisión sin respuesta equivale a «Skip for now» cuando la ejecución termina por otra causa. En la web, sin ningún cliente conectado durante 30 s (o 60 min con cliente; las aprobaciones de herramientas siguen en 10 min) la revisión se omite igual que las demás preguntas interactivas; en la TUI no hay tope de espera. - Sin interfaz interactiva (
alisio run,--json) o con--read-only, la herramienta devuelveunavailabley pide al modelo el plan completo como respuesta final; Alisio no lo imprime por su cuenta. El artefactoplan.mdse crea igualmente cuando hay almacén de artefactos. - Reloj de la ejecución pausable.
limits.timeoutMsya no es un límite de reloj: es un reloj de tiempo activo (RunClock) que se pausa mientras la ejecución espera a una persona. Una espera se marca en un único registro por aplicación (HumanWaits) que comparten el runner (aprobaciones de herramientas e instalación), la interfaz interactiva del host de plugins (ask_user_question,exit_plan, plugins; envuelta una sola vez ensetInteractiveUI, así la TUI, el servidor web y cualquier integrador la heredan) y la aprobación de directorios externos. Las esperas simultáneas usan un contador: el reloj se reanuda al cerrarse la última. Una sesión hija que espera a una persona pausa también los relojes de sus ancestros; un padre que espera a su hijo no se pausa (es trabajo). Una espera sin sesión (ui.select) pausa todas las ejecuciones activas.firstTokenTimeoutMsy su reintento no cambian;tasks.maxRunMstampoco. La ejecución devuelveactiveMsygoalOutcomelo usa paragoal.maxMinutes. Mensaje: «reached its limit of N s of active time (time spent waiting for you is not counted)». - Estado atascado tras una revisión interrumpida. Una llamada de efecto
read(comoexit_plan) interrumpida por una detención o por el límite mientras lanzaba un error dejaba su fila del diario comopending, y el siguiente mensaje fallaba con «Uncertain tool outcome … sessions recover --acknowledge». Ahora una llamadareadcancelada se cierra en el diario con el error (no pudo dejar efectos); las dewrite/process/externalsiguen pendientes a propósito. La propuestapendingde una ejecución terminada se marcaskipped(settlePlanRun, en la TUI y en el servidor) y el artefacto del plan se conserva. Un proceso que muere con la revisión abierta deja la propuestapendinghasta la siguiente llamada aexit_plan, que la sustituye: no bloquea nada. - Verificado: Vitest (reloj con fuente de tiempo falsa, registro
HumanWaits, ejecuciones reales con aprobación, pregunta y revisión del plan que tardan más que el límite, un tope que sigue venciendo con trabajo real, esperas en paralelo, el caso del estado atascado) y Chromium contraalisio servecon un proveedor falso. No verificado: una TUI en un terminal real, otros navegadores, Windows/macOS, un proveedor real. No hay un tope duro de tiempo total (incluidas las esperas humanas) como límite aparte; en la web las esperas siguen acotadas porapprovalTimeoutMs(10 min). - La TUI edita el contexto en una línea (sin varias líneas ni historial) y su flujo de turno (reclamar, cambiar de agente, lanzar el turno) solo está probado por la lógica pura compartida y por el panel; no se ha verificado en un terminal real. En la web se comprobó con Chromium contra
alisio servey un proveedor falso; no con otros navegadores, Windows ni macOS. - El presupuesto inicial de JS de la web quedó en unos 100 bytes de margen tras esta fase; la fase de tareas lo resolvió cargando bajo demanda el diccionario español (el bundle inicial bajó a ~78 KB).
Diagramas del plan y visor del plan
- Contrato aditivo de
exit_plan:diagrams?: [{id, title, explanation, section?, type?, mermaid}]. Sin él, la herramienta, el artefactoplan.mdúnico y el resultado son los de siempre. Ajustesplan.diagrams(por defectotrue) yplan.maxDiagrams(0–8, por defecto 5), vivos; el esquema que ve el modelo y la guía de estilo dePLAN_INSTRUCTIONS(agenteplanintegrado) siguen el ajuste en cada petición, mientras que la validación del esquema siempre aceptadiagrams(se compila una vez al registrar la herramienta). - Los diagramas son Mermaid (
.mmd) escritos por el modelo. La validación es ligera y no dibuja (Mermaid necesita DOM): 8 KB por diagrama, lista de tipos (flowchart,graph,sequenceDiagram,stateDiagram,stateDiagram-v2,erDiagram,classDiagram,gantt,mindmap,timeline,journey), estimación de 40 nodos por diagrama (por exceso; no es un analizador), y rechazo por texto declick/link/callback,href,javascript:/vbscript:/data:,url(), etiquetas HTML y directivas%%{init}o front matter consecurityLevel,htmlLabels,secure,themeCSS, etc. Un diagrama rechazado se descarta con el motivo en el resultado de la herramienta; nunca rompe la revisión. Un diagrama que pasa pero no se dibuja en la web muestra su código y el error. Nada comprueba que el diagrama diga la verdad del plan. - Con diagramas aceptados (o quitados respecto a la revisión anterior) el plan es una carpeta por revisión con entrada
plan.md(tipodocument, nombreplan.md; la descarga es un ZIP). La carpeta se escribe en un directorio temporal del sistema que el publicador copia y se borra. Si publicar la carpeta falla, se publicaplan.mdsolo y se avisa. Para que el nombre fueraplan.mdy noplan.zip, el almacén admite unfileNameopcional enpublish(solo interno de core; el SDK no cambia enArtifactPublishInput). plan.json(version: 1) lo genera Alisio de forma determinista a partir del Markdown (secciones Goal, Context, Steps, Decisions, Risks y Verification con alias en inglés y español; un#de título no cuenta como sección) y de los diagramas: resumen, objetivos, etapas, consideraciones, encabezados con ids estables, y por diagrama id, título, explicación, sección resuelta, tipo, sintaxis, archivo, hash y estado (new/updated/unchanged) másremoved. El visor lo lee de forma tolerante: ignora campos desconocidos, descarta elementos mal formados y solo acepta rutasdiagrams/<id>.mmd.- Sincronización de revisiones: la revisión anterior se lee del estado del plan (
sessions.options.plan. diagrams: id, título y hash), no del manifiesto del artefacto anterior; así funciona aunque el artefacto se haya borrado o caducado.planHashsin diagramas es el SHA-256 del Markdown de antes; con diagramas también cubre sus hashes, de modo que un cambio solo en un diagrama es otra propuesta. Los estados guardados antes de esta versión siguen siendo válidos (el campo es opcional). - Visor web (
components/plan/PlanViewer.tsx, carga diferida; el contenedorLazyPlanViewerpesa ~1 KB en el panel): se abre para un artefacto con entradaplan.mdyplan.jsonen sus archivos, por la ruta autenticada existente de archivos (sin rutas nuevas). Los diagramas usan el renderizador Mermaid existente conthemed(temabasecon variables de Alisio y un bloqueclassDefpara las clases semánticas que el diagrama use sin definir); los bloques Mermaid del chat no cambian. Es Preact sin HTML del plan (solo el SVG saneado por el camino existente). Presupuesto inicial de JS: 80,2 KB de 90 KB. - La TUI no dibuja: muestra título, propósito, sección, explicación y las primeras 8 líneas del código tras el plan (como Markdown, no con
renderCappedCode), la ruta de la carpeta (resuelta desde el almacén) y se abre desde/artifacts(la vista previa leeplan.md). Sin interfaz o con--read-onlyel plan vuelve como texto y los archivos se escriben. - No hay visor HTML autónomo descargable (exigiría incrustar Mermaid, unos 138 KB comprimidos de núcleo). Verificado en Chromium contra
alisio servecon un proveedor falso (tema claro y oscuro, 390 px, teclado, revisión 2 con actualizado y quitado, ZIP,plan.diagrams: false); no en otros navegadores, Windows ni macOS, y la TUI solo por pruebas de su lógica, no en un terminal real. - Guía de usuario: Modo plan y revisión del plan.
Tareas en segundo plano
- Una tarea es un proceso hijo corriente de Alisio: no es un sandbox y no es detached. Muere con Alisio (grupo de procesos;
taskkill /T /Fen Windows, sin verificar en una máquina Windows) y no sobrevive a un reinicio. Tras una muerte abrupta (SIGKILL, corte de luz) ningún código puede correr: el siguiente arranque marcalostlo que quedó sin terminar y sus procesos pueden seguir vivos (la tarea muestra supid). La detección usaowner_pid: un pid reutilizado por otro proceso puede ocultar una tarea perdida hasta que ese proceso termine. - Un comando que deja hijos en segundo plano (
cmd &) los pierde: se mata el grupo. La tarea termina cuando se cierran todas sus salidas. - El log conserva la cabeza y los últimos 32 KiB: la parte central de una salida muy larga se pierde. Las lecturas del modelo están acotadas a 64 KiB; el panel web conserva 200 000 caracteres.
- La notificación solo sale de procesos con un anfitrión que sepa despertar (TUI y servidor);
alisio runnunca la envía. La TUI espera a que el usuario vuelva a la sesión propietaria; una tarea que termina mientras el servidor está caído no se anuncia. Enask/autocada llamada debg_*pide aprobación (tambiénbg_listybg_output; «Allow for this session» cubre el resto). - No hay
run_in_backgroundni cesión a segundo plano de un comando en primer plano: solobg_runexplícito. Los subagentes aparecen en la lista solo como espejo de solo lectura. - Un workspace con tareas vivas no se expulsa por inactividad y
/reloadse rechaza hasta que terminen; reciclar el workspace (por ejemplo al activar un plugin) espera igual.
Objetivos de sesión (/goal)
- Sin presupuesto de tokens por defecto: un goal sin
budget=solo se detiene porgoal.maxTurnsygoal.maxMinutes. El presupuesto es duro pero se aplica por petición: el gasto puede pasar del tope como mucho una petición (más si el proveedor no informausage, donde se estima por caracteres). No hay turno de cierre. Cuenta entrada y salida de cada petición, es decir, un contexto largo se paga de nuevo en cada turno. - No hay modelo evaluador: el modelo decide cuándo termina o se bloquea, con evidencia que Alisio guarda y muestra pero no verifica. Un modelo puede dar por terminado algo que no lo está.
- Un «turno» es una ejecución, no un paso del modelo; el tiempo cuenta el tiempo activo de las ejecuciones (esperar a la persona dentro de una ejecución —una aprobación, una pregunta, la revisión del plan— no cuenta; antes sí contaba; esperar entre ejecuciones tampoco). La continuación recibe como límite de ejecución lo que queda de
goal.maxMinutes, también en tiempo activo. - Los disyuntores usan una huella del texto final (normalizada) y el número de llamadas a herramientas (
get_goal/update_goalcuentan como llamadas): un modelo que varía una frase evita el primero. - Un goal pausado por reinicio nunca se reanuda solo, y el reinicio se decide por
owner_pid: un pid reutilizado por otro proceso podría ocultar un goal huérfano hasta que ese proceso termine. - La TUI no continúa mientras haya texto sin enviar en el editor ni con otra sesión abierta; la web no ve en vivo los cambios que una TUI hace en un goal.
alisio runno tiene/goalen la v1. - Un goal no amplía permisos y nunca se ejecuta en modo plan, pero en
fullel agente actúa sin preguntar durante horas: no es un sandbox. El objetivo se cita como dato, no como instrucción privilegiada, lo que reduce pero no elimina la inyección desde un texto malicioso que el usuario pegue. - La TUI se verificó por lógica pura y fake terminal, no en un terminal real; la web, en Chromium.
Decision Intelligence
- No se incluye ningún proveedor: sin un plugin de proveedor registrado y nombrado en
decisions.provider, la función no hace nada. El plugin oficial de Laya está previsto en el repositorioalisio-pluginsy no está publicado. La única función integrada que consume decisiones es Smart Dashboard; los plugins también pueden usarctx.decisionsen sus herramientas. confidenceno está calibrada (Fase 0, E2):decisions.minConfidencees un filtro heurístico y un umbral de 0.6 no separó de forma fiable los casos claros de los ambiguos en una muestra pequeña etiquetada a mano. Alisio no la trata como probabilidad; la calidad debe medirse en la función que consume la decisión.dispose()no se llama al deshabilitar un plugin ni con/reload(ambos requieren reiniciar); solo al cerrar la aplicación. Una muerte súbita (SIGKILL, una caída) no se cubre: un plugin que posee un proceso del sistema debe limpiar los huérfanos él mismo. Undispose()que superapluginHooks.disposeTimeoutMsse abandona y se registra, pero no se puede interrumpir código síncrono bloqueante.- Los errores de ciclo de vida solo se ven en
/decisions: unactivate()odeactivate()fallido no genera evento ni aviso en otro sitio. - El
/statsde la TUI cuenta solo el proceso de TUI actual, como el resto de sus estadísticas, y conserva como máximo los últimos 2000 eventos de decisión; la web muestra las estadísticas de decisiones solo en el tooltip de los totales de la sesión, no en la línea principal. - Ajustes en la TUI:
pluginHooks.disposeTimeoutMsy las clavesdecisions.*no figuran en el menú/settingsde la terminal; se editan en el archivo de configuración. En la web sí son ajustables (Ajustes → General). - Con
decisions.telemetryenfalselos eventos no se persisten: no hay sección en/statsni estadísticas web, y/decisionssolo muestra las métricas en memoria del proceso. Unstatepuede salir del proceso si el adaptador del proveedor lo decide; el núcleo solo garantiza que los eventos y las métricas no lo contienen. Los plugins no son un sandbox.
Smart Dashboard
- No está disponible con
--read-only: la herramienta publica un artefacto, así que se elimina igual queartifact_create. También la eliminaanalysis.enabled: falseyanalysis.smartDashboard: false; el interruptor se aplica al siguiente arranque, y unfalseglobal no lo deshace un proyecto. - Un dataset por llamada, sin uniones entre hojas ni archivos, sin filtros interactivos y sin regeneración incremental con memoria del dashboard anterior (cada llamada es independiente).
- Sin tablas cruzadas, sin filtros de filas y sin métricas derivadas (margen, variación interanual, tasas): en una ejecución real el modelo recurrió a
python_runpara un cruce canal x región y para excluir pedidos cancelados. Además, en la primera ejecución real la frase «ventas netas» no coincidía con la columna de ventas netas y el dashboard usó ventas brutas; corregido en 0.4.3. - Solo las fechas ISO 8601 son columnas de tiempo: otros formatos de fecha no producen tendencia. Las marcas con zona horaria se normalizan a UTC y un texto como
"2024"no se trata como fecha. - Catálogo cerrado (
kpi,line,area,bar,hbar,pie,donut,scatter,table; hasta 12 componentes y 6 KPI): mapas, embudos, tablas dinámicas y exportación a PDF quedan fuera; lo demás se hace conpython_run. - La moneda no se infiere de los datos: el dinero se muestra como número. Los porcentajes solo se formatean como tales si el nombre de la medida lo indica y todos los valores son fracciones.
- Barras horizontales: la ayuda emergente seguía la fila vecina porque la interacción
indexde Chart.js busca por el eje x. El script compartidocharts-boot.jsfijainteraction.axis = 'y'solo parahbar(Fase 0, E5), igual en los dashboards de Python y en el renderizador TypeScript. Verificado en un navegador real (Chromium, Playwright): 8 de 8 filas en cada uno, serie única (TS) y tres series (Python); la prueba unitaria comprueba la configuración, no el hover. - El color sigue
prefers-color-schemedel navegador, igual que los dashboards de Python. - Un componente cuya consulta falla (por ejemplo por tiempo límite) se omite y se anota; si ninguna responde, la herramienta falla y no publica nada.
- Un proveedor de decisiones recibe solo el objetivo y metadatos de columna, nunca valores; el núcleo no decide adónde envía esos datos un adaptador.
- Pendiente de esta entrega: la medición E7, los resultados del benchmark B/C y el release.
- Guía de usuario: Smart Dashboard.
Gráficos de los dashboards (alisio_runtime.charts y svg)
- Chart.js 4.5.1 (MIT,
chart.umd.min.js, 208 KB y unos 70 KB comprimido) viaja dentro de@alisio/corecomo cadena enanalysis/python/sources.ts(generada porscripts/analysis-runtime-sources.ts; un test falla si queda desactualizada) y se copia con el resto dealisio_runtimea cada ejecución.charts.write/charts.pagelo incrustan una vez en el HTML: cada dashboard con gráficos pesa unos 215 KB más. No hay CDN nieval. - Es el único motor de gráficos. Plotly sigue como extra opcional pesado.
svges solo salida estática. - El color del dashboard sigue
prefers-color-schemedel navegador: el iframe aislado no conoce el tema elegido en Alisio, así que un dashboard puede verse claro dentro de una interfaz oscura. - Las cifras se formatean en el navegador con
Intl.NumberFormaty ellocaleque pase el modelo (por defecto el del visor); lossvg.*usan un formato compacto fijo (1.2k). Las tarjetas de gráficos no tienen paginación: una tabla de datos con miles de filas pesa en el HTML. - La puerta de calidad (
artifacts/chart-lint.ts) solo avisa: arcos SVG de tarta escritos a mano, SVG de tamaño fijo sinviewBoxy scripts u hojas de estilo remotos. Son heurísticas sobre el HTML (no ejecuta la página) y pueden fallar en ambos sentidos; nunca rechaza un artefacto. - Verificado: Vitest (geometría pura de las tartas con los tres conjuntos de datos del informe, estructura de
charts, aviso de la puerta, publicación con Python real) y Chromium contraalisio servecon un proveedor falso: CSP real sin errores de consola, 1280 px y 390 px sin desbordamiento horizontal, esquema claro y oscuro. No verificado: otros navegadores, Windows o macOS, un modelo real con las nuevas pautas, lectores de pantalla ni impresión a PDF. - Guía de usuario: Gráficos.
Compactación automática por presupuesto de caracteres
- El presupuesto
limits.maxContextChars(800000 por defecto) es ahora también un disparador: la compactación automática se ejecuta cuando instrucciones + transcript alcanzanthreshold(0,85) de ese límite, además del criterio por ventana. Antes, con una ventana grande (DeepSeek, ~1M) el límite duro se alcanzaba mucho antes de que la compactación se disparara y la ejecución fallaba. - Último recurso: si tras recortar la conversación sigue sobre el límite y no se intentó compactar en ese turno, se ejecuta una compactación (motivo
budget, ampliación aditiva de los eventos y hooks del SDK) y se recorta de nuevo; como máximo un intento por turno. Una compactación que falla en este punto se informa en el error final en vez de ocultarlo. - El error empieza igual (
Context budget exceeded) y ahora dice si la compactación se ejecutó, se omitió (sin frontera segura), falló o está desactivada. - Web:
compaction_startedy sus finales marcan un indicadorcompactingpor sesión (sin ejecución, como en/compactmanual): muestra «Compactando el contexto…» y bloquea el envío, conservando el borrador. El servidor sigue respondiendosession_busy; la TUI no cambió. - Límites: el recorte sigue sin tocar los argumentos de las llamadas a herramientas ni
providerData; un único turno enorme sin frontera segura todavía puede fallar (ahora con un mensaje exacto); tras compactar, si la cola conservada sigue por encima del 85 %, cada turno vuelve a intentarlo. Verificado con tests de módulo (runner y reductor web puro); no verificado con un DeepSeek real ni con una captura del navegador.
