DeepSeek Harness: carga impulsada por dependencias, recarga automática y contexto anidado

Hemos aprendido la máquina de estados Fiber anteriormente; en esta entrada veremos cómo la máquina de estados es impulsada por las dependencias de servicios.

¿Por qué el plugin se desinstala automáticamente cuando el servicio del que depende desaparece? ¿Por qué puede recargarse automáticamente cuando el servicio se restaura?


Carga impulsada por dependencias

Un plugin que declara inject espera a que todos los servicios necesarios estén listos antes de entrar en LOADING y ejecutar apply.

Este mecanismo expresa el orden de carga mediante dependencias de servicios, sin necesidad de orquestar manualmente el orden de inicio.

Ejemplo

// Ruta del archivo: scratch-plugin/src/my-plugin.ts
// Declara que este plugin depende de los servicios tools y llm
export const inject = ['tools', 'llm']

export function apply(ctx: Context) {
  // En este punto ctx.tools y ctx.llm ya están listos y se pueden usar de forma segura
}

Si el servicio del que depende desaparece (por ejemplo, cuando el proveedor es reemplazado), el plugin se desinstala automáticamente, pasando directamente de ACTIVE a DISPOSED.

Cuando el servicio se restaura, el plugin se recarga y recorre nuevamente PENDING → LOADING → ACTIVE.

卸载与重载时序图

Las palabras textuales de la documentación oficial: si el servicio del que depende desaparece (por ejemplo, cuando el proveedor es reemplazado), el plugin se desinstala automáticamente (ACTIVE → DISPOSED) y se recarga cuando el servicio se restaura.


Garantías de la recarga automática

La recarga automática es segura porque todos los registros realizados a través de ctx se revierten al desinstalar.

Los listeners, herramientas y adaptadores de la instancia antigua no quedan residuales; la nueva instancia los vuelve a registrar.

Esto evita que el plugin llame a servicios que ya no existen, y también evita escuchas o registros duplicados.


Contexto anidado: ctx.plugin()

ctx.plugin() se usa para registrar un subplugin dentro del plugin actual.

El subplugin crea un Fiber hijo, que hereda el contexto del padre pero tiene un ciclo de vida independiente.

El Fiber hijo se desinstala junto con el plugin padre.

Ejemplo

// Ruta del archivo: scratch-plugin/src/parent-plugin.ts
export function apply(ctx: Context) {
  // Registra un subplugin; childPlugin puede ser una función, un objeto o una subclase de Service
  ctx.plugin(childPlugin)

  // El subplugin tiene su propio Fiber y se desinstala junto con el plugin padre
}

El contexto anidado es muy común en la composición de plugins: un plugin "agregador" organiza varios plugins pequeños y gestiona sus ciclos de vida de manera unificada.


Terminación manual anticipada: fiber.dispose()

En la mayoría de los casos, el plugin se desinstala cuando el contexto se destruye, pero a veces necesitas terminar una instancia de plugin anticipadamente durante la ejecución.

El objeto Fiber devuelto por ctx.plugin() proporciona el métododispose()método.

Ejemplo

// Ruta del archivo: scratch-plugin/src/manual-dispose.ts
import type { Context } from '@deepseek-ai/cordis'

// Declaración de tipos: context y función de plugin (ilustrativo)
declare const ctx: Context
declare function myPlugin(ctx: Context): void

// Crea una instancia del plugin y obtén su Fiber
const fiber = ctx.plugin(myPlugin)

// Cuando necesites terminar manualmente, llama a dispose
// Nota: dispose devuelve una Promise; debes usar await para esperar a que se complete la limpieza asíncrona
await fiber.dispose()

dispose garantiza tres cosas:

GarantíaDescripción
Todos los registros se eliminanTodos los registros de listeners, herramientas, adaptadores, etc. que posee el plugin se revierten
Los subplugins se desinstalan recursivamenteLos Fibers hijos creados mediante ctx.plugin() también se limpian junto con él
Espera a que la limpieza asíncrona termineLa Promise devuelta se resuelve solo después de que todos los manejadores asíncronos de disposición hayan terminado

En otras palabras, cuando await fiber.dispose() retorna, este plugin junto con su subárbol se han detenido por completo.


Resumen y autoevaluación

La carga impulsada por dependencias hace que el plugin se inicie solo cuando el servicio está listo; si el servicio desaparece se desinstala automáticamente, y si se restaura se recarga automáticamente; ctx.plugin() crea Fibers hijos que se pueden desinstalar recursivamente.

Autoevaluación:

  1. ¿En qué estado se detiene el plugin cuando el servicio declarado por inject no está listo?
  2. ¿Qué relación hay entre el Fiber hijo creado por ctx.plugin() y el Fiber padre?
  3. Cuando await fiber.dispose() retorna, ¿qué se garantiza que ya se ha completado?
Otras extensiones