Estándar de desarrollo de plugins DSH
Esta habilidad es el estándar único para desarrollar plugins de DeepSeek Harness (DSH): unifica las convenciones dispersas en la documentación oficial de DSH (tutoriales, manuales de referencia y directorios generados) en flujos de trabajo ejecutables, reglas estrictas y listas de verificación. Todo en DSH es un plugin (adaptadores de modelo, registro de herramientas, registros de sesión e incluso el bucle del agente), por lo que actuar según esta habilidad es extender DSH según su propia arquitectura.
Alcance
- Desarrollo de plugins DSH dentro de un repositorio, basados en archivos: escribir paquetes de plugins, registrar líneas
cordis.yml, parches overlay, desarrollar herramientas, conectar modelos, empaquetar e instalar. - El desarrollo de paquetes workspace dentro del monorepo de DSH (
packages/<group>/<pkg>) también está cubierto por esta habilidad (references/workspace-package.md). - Esta habilidad no cubre: plugins dinámicos en sesión (flujos
cordis_define/cordis_run) ni edición de combinaciones de presets de agente; estos son cubiertos por habilidades especializadas de cada despliegue o por herramientas oficiales.
Reglas estrictas (obligatorias en cualquier escenario)
- La interfaz se rige por la referencia generada. Los nombres de servicios, métodos públicos, firmas de eventos y claves
ctxse basan en las páginas de subsistemas generadas automáticamente y en las interfaces TypeScript del repositorio; no inferir la API completa a partir de nombres de servicio, ejemplos o código antiguo, ni mantener una lista estática separada. - Todas las contribuciones son efectos secundarios. Todo lo que se registre a través de
ctx(escucha de eventos, herramientas, adaptadores,ctx.effect) se revierte automáticamente al descargar el plugin; no crear efectos secundarios a nivel de proceso o página en el ámbito del módulo; para suscripciones de terceros que no devuelvan undisposer, averiguar activamente el mecanismo de limpieza. - Los listeners waterfall deben llamar a
next(). No llamar anext()provoca un cortocircuito intencionado aguas abajo (para interceptación/gateway), no es opcional. - Fallar de forma ruidosa. Si
applylanza una excepción, el proceso termina; si la validación de configuración falla, se debe reportar el error explícitamente; el esquema (schema) debe expresar restricciones completas, no tragar errores silenciosamente en tiempo de ejecución. - Declarar dependencias obligatorias con
inject, dependencias opcionales conctx.get()y comprobación de nulidad. No usarinjectpara evitar la comprobación deundefined; tampoco acceder directamente actx.xxxsin declarar la inyección: los servicios no declarados pueden evaluarse comoundefineda través del resolvedor de servicios. - La configuración siempre usa Schemastery. Exportar
interface Configy unSchemacon el mismo nombre; los valores predeterminados se escriben en el schema; no exportar objetos simples comoConfig; todos los parámetros que puedan variar entre despliegues deben estar en la configuración. - La ejecución de herramientas devuelve valores JSON estándar, no bloques de contenido. El texto legible por humanos se coloca en
output.render; las estrategias de despliegue y los hooks no deben integrarse dentro de la herramienta. - Lo que el modelo ve ya está registrado. Cualquier entrada nueva visible para el modelo debe residir en un mecanismo que pueda reconstruirse a partir de los registros de sesión (nuevos eventos persistentes o mediante
agent.inject()), con aserciones de invariantes en tiempo de ejecución. - Priorizar
ctx.connection.rpcpara paneles de configuración npm. Los paneles semiantendidos del navegador que llaman a métodos del proceso principal deben priorizar Connection RPC (ctx.connection.rpc.handle+ctx.connection.rpc.call); la plantilla está en references/connection-rpc.md. Prohibido el uso de@Remotesin envoltura, manifiestos typert escritos a mano, ocreateRequirecolgando del código fuente de harness. Siguen siendo válidas las rutas generadas por Typert generator y exportadas como./typert+./remoteenpackage.json, consumidas porctx.remote.$mount().
Flujo de trabajo estándar
Escenario A: Crear un nuevo plugin
- Determinar qué aporta el plugin (¿servicio? ¿herramienta? ¿escucha?), usar la tabla de «Ubicación de nuevo comportamiento» en references/seams.md para seleccionar el mecanismo.
- Crear
src/<name>.ts, exportarname,inject(opcional),apply(ctx, config?). Escribir primero como función; cambiar a la forma de claseServicecuando se necesite exponer un servicio. - Bucle de desarrollo local (checkout del código fuente):
mkdir -p scratch-plugin/src
# scratch-plugin/cordis.yml —— patch overlay; la ruta del plugin debe ser absoluta
- insert:
- id: hello
name: '/ruta/absoluta/hacia/deepseek-harness/scratch-plugin/src/mi-plugin.ts'
pnpm dsh web --patch ./scratch-plugin/cordis.yml # abrir http://127.0.0.1:3080
- Verificar carga y descarga: el registro de inicio aparece, los recursos se limpian al detener (references/plugin-anatomy.md).
Escenario B: Añadir una herramienta a un modelo
inject: ['tools'], usardefineToolpara definir (name/description/parameters/output/execute).- Implementar según las convenciones de
executeen references/tools.md: schema DSL para declarar parámetros y salida,executedevuelve valores estándar, respetaexec.signal, opcionalmentepresentationMeta/tarjetas UI. - Cuando se necesiten estrategias, colgarlas de eventos
tools/*(pre-execute/guard/execute/post-execute/result), no incrustar la estrategia en la herramienta (ejemplo de control de acceso en references/plugin-forms.md). - Reiniciar y verificar la invocación real por parte del modelo.
Escenario C: Capacidad reemplazable (múltiples proveedores)
- Según references/three-roles.md, dividir en tres paquetes: Definition / Provider / Consumer (fusionar roles según sea necesario, no dividir de forma preventiva).
- Definition posee los tipos Request/Result y el Service abstracto; Provider implementa; Consumer expone como herramienta o consumidor de servicio.
- En
cordis.yml, listar solo las líneas de Provider y Consumer; cambiar de proveedor solo reemplaza una línea.
Escenario D: Conectar un nuevo proveedor de modelo
- Heredar de
LlmAdaptere implementarstream(), emitir fragmentos según el protocoloStreamChunk; errores conLlmErrorcon código estable (o terminar en banda confinish { kind: 'error' | 'aborted' }); cada solicitud HTTP debe combinarattributionHeaders()y pasarsignal. ctx.llm.registerAdapter(['<provider>'], adapter); opcionalmente sobrescribirresolveModel()/listModels().- En la composición, configurar
provider/modeldel agente-loop para verificar. Ver detalles en references/llm-adapter.md (incluye 7 obligaciones de protocolo).
Escenario E: Empaquetado e instalación para entrega
- Crear paquete de composición:
package.jsondeclaradsh.bundle(apuntando acordis.patch.yml), las líneas de parche referencian el plugin por nombre de paquete; los paquetes de biblioteca solo para importación no declarandsh.bundle. - Instalación:
dsh plugin --profile <name> add <pkg>; primero ejecutardsh --profile <name> --dump-configpara verificar las capas, luego iniciar. - Recordar el orden de las capas y la semántica de reemplazo de líneas completas (references/packaging.md).
Escenario F: Crear un nuevo paquete dentro del monorepo de DSH
Seguir la lista de verificación archivo por archivo en references/workspace-package.md: crear el paquete (package.json invariantes/tsconfig/src/README) → registrar en la configuración raíz → topología y nomenclatura del paquete (tabla de roles) → estructura README Model Experience → comandos de verificación.
Consulta rápida de decisiones
| Quiero… | Mecanismo | Detalles |
|---|---|---|
| Exponer capacidad a otros plugins | Service (forma de clase) | references/services.md |
| Consumir una capacidad existente | inject o ctx.get() | references/services.md |
| Comunicación entre plugins / puntos de extensión | ctx.on / ctx.emit y otros eventos (cinco modos de distribución) | references/events.md |
| Hacer que el usuario pueda configurar | Config + Schemastery | references/config.md |
| Añadir capacidad a un modelo | defineTool → ctx.tools.register (o JSON Schema sin procesar) | references/tools.md |
| Conectar nuevo proveedor de modelo | LlmAdapter + ctx.llm.registerAdapter | references/llm-adapter.md |
| Dividir capacidades reemplazables | Tres roles (Definition/Provider/Consumer) | references/three-roles.md |
| Distribuir un plugin | Paquete de composición + profile | references/packaging.md |
| Consultar servicios integrados / ubicación | Directorio seam, mapeo de arquitectura | references/seams.md |
| Ámbito/Almacenamiento de servicios/Fiber API | ctx.extend/isolate/provide/mixin, fiber.* | references/context-api.md |
| Hooks / UI / Paneles / Plugins puente de protocolo | Cinco formas de extensión + mapeo funcionalidad→mecanismo | references/plugin-forms.md |
| Panel semiantendido del navegador llamando al proceso principal | ctx.connection.rpc.handle/call, endpoint + sobre | references/connection-rpc.md |
| Crear un nuevo paquete dentro del repositorio / nomenclatura | Lista de workspace + tabla de roles | references/workspace-package.md |
Lista de verificación antes de finalizar
- Interfaz verificada contra la referencia generada (servicios/eventos/claves
ctxbasadas en lo generado), no adivinar la API por el nombre. - Todo el registro se realiza a través de
ctx(eventos/herramientas/adaptadores/ctx.effect), se puede limpiar al descargar; la limpieza sensible al orden se coloca en el mismoctx.effect. - Dependencias obligatorias declaradas correctamente con
inject; dependencias opcionales con manejo deundefined. - Si el plugin tiene configuración:
Configusa Schemastery, los valores predeterminados están en el schema, la configuración inválida falla ruidosamente al cargar. - Si tiene herramientas:
executedevuelve valores JSON estándar;renderse encarga de lo legible por humanos; las estrategias van por eventostools/*; respetaexec.signal; declaraisConcurrencySafepara concurrencia segura (no declarado → exclusivo). - Si tiene listener waterfall: llama y devuelve
next()(a menos que sea un cortocircuito intencionado). - Si tiene adaptador LLM: protocolo
StreamChunkcompleto (pares de fragmentos,indexsegún primera aparición,usageantes definish,argumentscomo cadena JSON sin procesar); errores por una de las dos rutas legales; lanzaUNSUPPORTEDpara campos no soportados; envíafinish.replayStatesi necesita reproducción nativa; pasóattributionHeadersysignal. - Líneas de composición y orden de capas correctos; las nuevas líneas son visibles en
--dump-config; sin registros residuales tras HMR/reinicio. - Si el contenido visible por el modelo cambia: reside en un mecanismo que puede reconstruirse desde los registros.
- Panel semiantendido del navegador: priorizar
ctx.connection.rpc.handle/call; prohibido@Remotesin envoltura, manifiesto typert escrito a mano,createRequirecolgando de harness; siguen siendo válidos los artefactos de Typert generator completo +./typert+./remote. - Nuevo paquete workspace: invariantes de
package.json, un solo aggregate, nomenclatura acorde a la tabla de roles, README con estructura Model Experience, yconstraints/typecheck/lint/build/hygienetodo en verde.
Archivos de referencia (cargar según sea necesario, no leer todos a la vez)
El índice completo y la tabla de «cuándo leer» están en references/README.md.
- references/plugin-anatomy.md —— Leer al escribir/modificar un plugin, manejar el ciclo de vida o HMR.
- references/services.md —— Leer al definir o consumir un servicio, o inyectar dependencias.
- references/events.md —— Leer al usar eventos para comunicación o escuchar puntos de extensión.
- references/config.md —— Leer al hacer que un plugin sea configurable.
- references/context-api.md —— Leer al usar ámbito, almacenamiento de servicios o Fiber API.
- references/three-roles.md —— Leer al dividir capacidades reemplazables.
- references/tools.md —— Leer al desarrollar herramientas para modelos.
- references/llm-adapter.md —— Leer al conectar un nuevo proveedor de modelo.
- references/plugin-forms.md —— Leer al escribir hooks, UI o plugins puente de protocolo.
- references/connection-rpc.md —— Leer cuando un panel de configuración npm necesite llamar a métodos del proceso principal; también al encontrar
@Remotesin envoltura, manifiesto escrito a mano ocreateRequirecomo puente de código fuente. - references/packaging.md —— Leer al empaquetar, instalar o entregar un plugin.
- references/workspace-package.md —— Leer al crear un nuevo paquete dentro del monorepo.
- references/seams.md —— Leer al consultar servicios integrados, ubicación o mapeo de arquitectura.
Verificación
- Autocomprobación de formato:
nameen kebab-case y coincide con el nombre del directorio;descriptionno vacío y conciso (esta habilidad se mantiene dentro de 500 caracteres); el cuerpo usa Markdown estándar; todos los archivosreferencesreferenciados existen realmente. - Nomenclatura del directorio: El nombre de esta habilidad (
name) esdsh-plugin-dev, que es diferente del nombre del repositoriodsh-plugin-dev-skills: al clonar/descomprimir, la carpeta debe renombrarse adsh-plugin-dev(coincidiendo conname), de lo contrario, los cargadores que se dirigen por nombre de directorio podrían no encontrar esta habilidad. - En entornos con CLI
skills-ref: ejecutarskills-ref validate ./dsh-plugin-dev. - Regresión: después de modificar
description, ejecutar el conjunto de evaluaciónevals/trigger-queries.json(método en evals/README.md del repositorio). - Verificación de comportamiento: completar un plugin mínimo según el Escenario A (registro de carga + limpieza de descarga), y luego una herramienta
greetsegún el Escenario B.