Desenvolvimento de Plugins dsh
Desenvolva plugins para o DeepSeek Harness
(dsh), um framework de agentes baseado em Cordis. Plugins são módulos TypeScript que exportam
apply(ctx: Context), compostos via cordis.yml.
Referência Rápida
Estrutura do Plugin
import type { Context } from '@deepseek-ai/cordis'
export const name = 'meu-plugin'
export function apply(ctx: Context) { /* registrar capacidades */ }
Três formas: função (padrão), objeto ({ name, inject, apply }), classe
(extends Service — use apenas ao fornecer um serviço para outros plugins).
Dependências: export const inject = ['tools', 'llm'] — o framework mantém o plugin
no estado PENDING até que todos os serviços injetados estejam prontos.
Limpeza automática: Tudo registrado através de ctx é um efeito — ao descarregar,
a limpeza ocorre automaticamente. Para recursos personalizados (temporizadores, conexões),
use ctx.effect(() => { ... return () => cleanup }).
Plugins filhos: const fiber = ctx.plugin(pluginFilho) — filhos herdam o contexto
do pai; await fiber.dispose() descarrega recursivamente.
Estados do Fiber
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
↘ FAILED
PENDING → dependência de serviço ausente. FAILED → apply lançou exceção ou a
validação de configuração falhou. O desaparecimento de um serviço em tempo de execução
descarta automaticamente os dependentes.
Ferramentas (Tools)
import { defineTool } from '@deepseek-ai/dsh-tools'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'saudacao', description: '...',
parameters: { nome: { type: 'string', required: true } },
output: { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: value }] },
async execute(args) { return `Olá, ${args.nome}!` },
}))
}
defineTool converte parâmetros em JSON Schema, valida argumentos e cancela o registro
automaticamente ao descarregar. Consulte references/basic/tool.md para detalhes.
Configuração
import Schema from '@deepseek-ai/schemastery'
export interface Config { saudacao: string; maxTentativas: number }
export const Config: Schema<Config> = Schema.object({
saudacao: Schema.string().default('Olá'),
maxTentativas: Schema.number().default(3),
})
export function apply(ctx: Context, config: Config) { /* configuração validada */ }
É necessário exportar TANTO a interface Config QUANTO const Config: Schema<Config>.
Configuração inválida → estado FAILED com erro claro. Use !!js no YAML para valores
dinâmicos: config: { apiKey: !!js process.env.API_KEY }.
Consulte references/basic/config.md.
Serviços
Fornecer: class extends Service { constructor(ctx) { super(ctx, 'nome') } }
declare module '@deepseek-ai/cordis' { interface Context { nome: NomeServico } }
Consumir: export const inject = ['nome'] → ctx.nome.metodo().
Opcional: ctx.get('nome') → retorna undefined quando ausente.
Isolamento de serviço: group: true + isolate: { shell: true } fornece a cada grupo
sua própria instância do provedor. Consulte references/framework/service.md.
Eventos
| Modo | Chamada | Uso |
|---|---|---|
emit | ctx.emit(n, ...args) | Broadcast síncrono, ignora retornos |
parallel | await ctx.parallel(n, ...args) | Listeners concorrentes |
serial | await ctx.serial(n, ...args) | Sequencial, primeiro não-nulo vence |
bail | ctx.bail(n, ...args) | Serial síncrono |
waterfall | ctx.waterfall(n, ...args, next) | Cadeia de middleware |
Listeners waterfall devem chamar next() a menos que intencionalmente interrompam o fluxo.
Eventos-chave do harness: agent/step, tools/result, session/event.
Consulte references/framework/events.md.
Adaptadores LLM
class MeuAdaptador extends LlmAdapter {
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
// Converter → API do provedor → yield StreamChunk
}
}
ctx.llm.registerAdapter(['meu-provedor'], adaptador)
Ordem do StreamChunk por bloco: block-start → text-delta/tool-call-delta →
block-end → usage → finish. Lance LlmError com códigos estáveis.
Consulte references/practice/llm-adapter.md.
Design em Três Camadas de Capacidade
Definição (classe abstrata Service) → Provedor (implementa) → Consumidor (tool)
Todos os três compartilham o mesmo nome de serviço via inject. Separe em pacotes
distintos apenas quando os papéis precisarem evoluir independentemente.
Consulte references/practice/index.md.
cordis.yml / Pacotes
Desenvolvimento local: --patch ./cordis.patch.yml
- insert:
- id: meu-plugin
name: '/caminho/absoluto/para/src/meu-plugin.ts'
config: { saudacao: 'Oi' }
Distribuição: pacote npm com manifesto dsh.bundle + cordis.patch.yml.
Instalação: dsh plugin --profile demo add ./hello-plugin.
Ordem das camadas: pacotes do perfil → patch do perfil → patch do diretório home → sobreposições --patch.
Camadas posteriores vencem por linha (sem mesclagem profunda).
Consulte references/basic/publish.md.
HMR
Carregue @deepseek-ai/cordis-plugin-hmr + @deepseek-ai/cordis-plugin-logger-console
@deepseek-ai/cordis-plugin-timerpara recarregar automaticamente ao salvar arquivos.
Diagnosticando PENDING
import { FiberState, type Context } from '@deepseek-ai/cordis'
for (const fiber of ctx.registry.values().flatMap(r => r.fibers))
if (fiber.state === FiberState.PENDING)
console.log(`${fiber.name} está PENDING`)
Documentação de Referência
Guias detalhados no diretório references/:
| Caminho | Abrange |
|---|---|
references/basic/ | Primeiro plugin, ferramentas, configuração, publicação |
references/framework/ | Ciclo de vida, serviços, eventos |
references/practice/ | Design em três camadas, adaptadores LLM |
references/cordis-tutorial/ | Tutorial passo a passo do framework Cordis |