CH 08 · Modelo mental del árbol de plugins: todo es un plugin
Objetivo del capítulo
CH 02 dijo "todo es un plugin"; este capítulo convierte eso en un modelo mental que puedas llevarte: qué es realmente un dsh en ejecución, por qué cada parte se puede reemplazar, y por qué se cumple la frase "cambia el Provider y cambias el producto".
Una frase para sentar las bases: un dsh en ejecución = un árbol de plugins
El documento oficial de arquitectura lo dice así: un dsh en ejecución es un árbol de plugins, compuesto a partir de capas ordenadas al arrancar. Divídelo en tres capas para recordarlo y toda la arquitectura se sostiene:
Leyendo el diagrama de arriba hacia abajo, la lógica es: ① Primero elige un Profile → ② Apila las capas de plugins en orden → ③ Todas las capas corren sobre el mismo kernel Cordis. Los tres puntos correspondientes:
- La capa inferior es el kernel Cordis: un contexto compartido que permite a los plugins inyectar servicios, emitir eventos y montar efectos.
- En el medio está el propio árbol de plugins: varios plugins apilados en un árbol, cada uno haciendo una cosa concreta.
- La capa superior es el Profile: no añade capacidades nuevas, solo decide "cómo se ve este árbol": web, headless o tu propia combinación personalizada (se cubre en la siguiente sección).
Cordis: el framework que lo impulsa todo
dsh corre sobre un framework de plugins llamado Cordis (el README oficial dice explícitamente "powered by Cordis"). Cordis tiene solo tres convenciones centrales:
- Los plugins inyectan servicios en un contexto compartido. ¿Quieres añadir una capacidad? Escribe un plugin y regístralo.
- Los plugins emiten eventos tipados. Otros escuchan estos eventos para coordinarse, sin necesidad de importarlos directamente.
- El registro del plugin es un efecto reversible — cuando un plugin se descarga, lo que registró se deshace automáticamente, sin dejar restos.
La parte más importante es la última — es la razón por la que "todo es un plugin" puede sostenerse: dsh no tiene un núcleo privilegiado que parchear. El adaptador del modelo es un plugin, el registro de herramientas es un plugin, el log de sesión es un plugin, incluso el bucle principal del agent es un plugin. La forma de extender dsh es "colgar un plugin al lado", no "ir a editar algún archivo del núcleo". Esto hace que el coste de cambio sea muy bajo: si no puedes cambiarlo, reemplázalo; si no puedes reemplazarlo, añade uno.
¿En qué se diferencia del resto? Pon los dos CLI Agents más usados uno al lado del otro y la diferencia salta a la vista:
| Comparación | Forma de extensión | ¿Puedes tocar el "esqueleto"? |
|---|---|---|
| Claude Code | Las herramientas se pueden extender (MCP, skills), pero el modelo y el bucle principal no se pueden reemplazar | Semiabierto: las herramientas se pueden añadir, pero la adaptación del modelo, la sesión y el agent loop están soldados, y el modelo está bloqueado a su propio ecosistema |
| Codex | Las herramientas se pueden extender, pero el modelo y el bucle principal no se pueden reemplazar | Semiabierto: las herramientas se pueden añadir, pero la adaptación del modelo, la sesión y el agent loop están soldados, y el modelo está bloqueado a su propio ecosistema |
| dsh | Cuelga un plugin al lado, cambia la configuración para reemplazar | Totalmente abierto: incluso el log de sesión, el agent loop y el adaptador del modelo son plugins, sin núcleo privilegiado |
Resumen en una línea: el "esqueleto" de Claude Code y de Codex está soldado; el "esqueleto" de dsh también es un plugin. Por eso puedes conectar cualquier modelo (la comparación con Codex de CH 06 es el ejemplo) y puedes reemplazar la sesión por tu propia implementación — el esqueleto mismo es reemplazable, el único límite es si quieres cambiarlo.
Mira un árbol real con --dump-config
Decir la teoría diez veces no es tan bueno como mirarla una vez. En CH 05 ejecutaste dsh --profile headless --dump-config una vez; lo que se imprime es un árbol largo de plugins @deepseek-ai/dsh-* — llm (modelo), session (sesión), credentials (claves), session-persistence-jsonl (persistencia de sesión)... estos plugins manejan cada uno lo suyo, apilados en un dsh ejecutable.
El equipo oficial lista los plugins centrales con claridad; cada uno corresponde a una "rama grande" del árbol:
| Plugin central | Qué maneja |
|---|---|
core/session | Log de sesión append-only (todo lo que ve el modelo queda registrado) |
core/system-prompt | Cómo se ensambla el system prompt, cómo se empaquetan los schemas de las herramientas |
core/tools | Registro de herramientas y pipeline de ejecución controlada |
core/agent | Interfaz del Agent y registro en vivo |
core/agent-loop | Driver por defecto del bucle "request → tool call" |
llm/llm | Vocabulario de mensajes y streaming + costura del adaptador del modelo |
Fíjate en "costura" en la última línea — ese es el siguiente concepto clave.
Profile y Bundle: cómo los plugins se combinan en un "modo"
Plugin = un plato (arroz frito, sopa de la casa, guarnición); bundle = un menú (arroz, principal, cubiertos preempaquetados); profile = la carta (lista qué platos, en qué orden); el wok en la cocina es el kernel Cordis de la siguiente sección.
Lo que forma un árbol lo decide el Profile — piensa en él como la carta en tu mano:
- Profile es una combinación con nombre, guardada en tu directorio Harness. Lista qué Bundles apilar, qué plugins extra instalar, y también guarda tu propio
cordis.patch.yml. La carta no cocina platos, solo dice "qué lleva esta comida": la fábrica oficial trae las cartaswebyheadlesslistas para usar; los "cuatro modos de ejecución" de CH 02 son cuatro presets de combinación de plugins por debajo, equivalentes a unas cuantas cartas más. - Bundle es un formato de distribución para "un grupo de plugins + la configuración que montan", equivalente a un menú preempaquetado:
dsh-basees la primera capa para cada profile (fundación: modelo, herramientas, persistencia, sandbox, aprobación, ajustes, claves, telemetría);web-appañade "comer en el local" al conjunto base (interfaz del navegador);headlessañade "para llevar" (línea de comandos de un solo uso, sin servidor).
Primero un punto que se confunde fácilmente: Profile no es un plugin. Un plugin es "algo que hace trabajo" (un plato); Profile es solo "la carta" (solo pide, no cocina). Elegir "modo mínimo" no instala ningún plugin nuevo — solo cambia el árbol de plugins a una combinación más mínima — carta distinta, misma cocina.
El apilamiento de plugins tiene un orden estricto (oficial), como la cocina que prepara según la carta, y las notas posteriores pueden sobrescribir las anteriores:
Cada bundle (en el orden listado en el profile) ← primero el menú
→ cordis.patch.yml propio del profile ← notas en la carta
→ nivel directorio Harness ← requisitos uniformes del jefe
→ overlay --patch por línea de comandos ← notas extra temporales, el último ganaCada capa puede sobrescribir la de arriba — los patches localizan una fila por id, reemplazan toda su configuración o insertan una fila nueva. Así que el --patch de CH 05 no es "un agujero extra", es modificar abiertamente este árbol: cualquier línea impresa por --dump-config puede ser reemplazada por tu propio patch.
Construir tu propio Profile no significa escribir código desde cero — la esencia es "copia una carta y modifícala":
- ¿Quieres menos que web? → Copia la configuración de web y quita lo que no quieras ("comer en el local, pero sin sopa");
- ¿Quieres más que headless (p. ej. montar por defecto un plugin de herramientas que escribiste)? → Copia headless y añade una línea a su lista de bundles ("para llevar, más una guarnición que trajiste");
- Lo más ligero es no cambiar la carta, solo añadir notas: para instalar un plugin en un profile existente, usa
dsh plugin --profile <nombre> add <paquete>("añade una guarnición a la carta existente"), para sobrescribir temporalmente, usa--patch("esta nota de la comida: cambia la sopa por Sprite") — ya hemos visto ambos en CH 05.
Encadena el proceso de carga en una sola línea de tiempo y todo lo anterior encaja:
- Al arrancar, dsh primero lee el Profile que elegiste — es un manifiesto de carga, diciendo "en qué orden apilo qué capas";
- Carga capa por capa en el orden del manifiesto: primero la capa 1 base (dsh-base), luego la capa 2 de añadido (web-app / headless / tus plugins), finalmente la capa 3 de sobrescritura (cordis.patch.yml +
--patch) — cuanto más tarde se carga, más puede sobrescribir lo anterior; - Todas las capas se instalan sobre el mismo kernel Cordis, donde inyectan servicios y se comunican entre sí.
Una frase de cierre: Profile = orden de pedido al arrancar; árbol de plugins = platos instalados en ese orden; Cordis = el wok usado para instalar los platos.
Eventos y costuras de capacidad: cómo hablan los plugins
Los plugins no se importan entre sí; se coordinan a través de dos cosas:
Los eventos son puntos de extensión, en tres dominios:
- Eventos de sesión: hechos persistentes, añadidos al log y emitidos, sobreviven a reinicios (p. ej. "este mensaje ha sido enviado").
- Eventos de Agent (
agent/*): llevan el Agent en vivo, se usan para observar o interceptar el trabajo en ejecución (p. ej. "interceptar antes de que empiece este step"). - Eventos de Capability (
fs/*,tools/*,telemetry/*): adjuntan políticas y adaptadores a una costura sin cambiar el bucle principal.
La costura de capacidad es una capacidad reemplazable; tiene tres roles fijos:
- Service Definition: declara cómo es la interfaz;
- Service Provider: la implementa de verdad;
- Consumer: la usa (normalmente herramientas que llama el modelo).
¿Por qué "cambia el Provider y cambias el producto"? Porque los providers del sistema de archivos y de subprocesos comparten el mismo "mundo de ejecución" — apúntalos a un sandbox remoto y Bash, PTY, LSP se mueven con ellos, sin necesidad de escribir un conjunto aparte para cada capacidad. Lo mismo aplica al adaptador del modelo: el adaptador que esté registrado en ctx.llm es el modelo que usa todo el producto; nada más necesita cambiar.
El modelo mental en una línea
Si solo puedes llevarte tres frases, recuerda estas tres (junto con el pequeño restaurante de arriba):
- dsh no tiene núcleo — cada parte es un plugin, incluyéndose a sí mismo; añade un plato al lado, quita uno y se deshace automáticamente.
- Profile decide cómo se ve el árbol — web / headless / personalizado son todas "cartas" distintas del mismo árbol; no cocina, solo pide.
- Los eventos y las costuras son la interfaz para la colaboración entre plugins — móntalo y funciona, quítalo y se deshace, así que cambia o añade con libertad; cambia el Provider y cambias el producto.
Lo que aprendiste en este capítulo
- [ ] Afirmar que "un dsh en ejecución = un árbol de plugins" y que se compone a partir de capas ordenadas
- [ ] Enunciar las tres convenciones centrales de Cordis (inyección de servicios / eventos / efectos reversibles), y por qué no hay un núcleo privilegiado
- [ ] Reconocer los plugins centrales (llm / session / credentials / tools, etc.) en la salida de
--dump-config - [ ] Explicar la relación entre Profile y Bundle, y el orden de capas del apilamiento de plugins
- [ ] Usar la analogía del "pedido" para explicar la relación entre plugin / bundle / profile, y saber que construir tu propio profile es "copia una carta y modifícala", mientras que
plugin add/--patches la modificación más ligera - [ ] Nombrar los tres roles de una costura de capacidad (Definition / Provider / Consumer), y explicar por qué "cambia el Provider y cambias el producto"
