Your First Harness Plugin
Bootstrap your first DeepSeek Harness plugin from scratch, mastering plugin scaffolding, lifecycle mounting, and hot-reload debugging.
In DeepSeek's engineering paradigm, an autonomous AI agent is not a monolithic black box, but an organic synthesis following the “Brain (Model) + Nervous System & Scaffold (Harness)” collaborative framework.
Serving as the central nervous system of DeepSeek Harness, the Cordis microkernel delivers infinite extensibility—from file retrieval and code execution sandboxes, to enterprise API gateways and custom security audits, all capabilities are attached as modular, declarative plugins.
This tutorial guides you through creating, coding, and debugging a production-grade “Agent Security & Action Auditor Plugin” from scratch, uncovering the core mechanics of Cordis context injection and zero-downtime hot-reloads.
Architectural Perspective: Why Modular Plugins Matter for Agents
Monolithic agent scripts frequently interleave prompt templates, external API calls, and OS shell commands in a single unstructured file. When migrating across heterogeneous infrastructure or implementing strict compliance guardrails, such designs quickly break down.
DeepSeek Harness embraces Inversion of Control (IoC) with a pure microkernel architecture:
┌─────────────────────────────────────────────────────────────┐
│ Cordis Microkernel Nervous System │
├─────────────────────────────────────────────────────────────┤
│ • Unified Service Bus (Service Registry & Dependency DAG) │
│ • Asynchronous Event Bus (emit / bail / waterfall) │
│ • Sandbox Lifecycle & Effect Cleanups │
└───────────────┬─────────────────────────────┬───────────────┘
│ Context Injection (Fiber) │
┌───────▼────────┐ ┌────────▼───────┐
│ Official Core │ │ Custom Plugin │
│ (Bash / PTC) │ │ (Audit Guard) │
└────────────────┘ └────────────────┘
- Zero-Bias Microkernel: The core runtime contains no hardcoded LLM prompts or shell execution logic; it solely orchestrates discovery, dependency graphs, and resource reclamation;
- Isolated Context Scopes: Every loaded plugin receives a dedicated
Contextinstance. All attached event listeners, heartbeat intervals, and service registrations are strictly tracked within its Fiber lifecycle, preventing memory leaks.
Step 1: Scaffold an Isolated Plugin Workspace
Keep your project cleanly organized by creating a dedicated directory for custom extensions:
# 1. Create directory tree
mkdir -p custom-plugins/agent-guard/src
cd custom-plugins/agent-guard
# 2. Initialize npm package.json
npm init -y
# 3. Install official Cordis microkernel types
npm install @deepseek-ai/cordis --save-dev
Configure custom-plugins/agent-guard/tsconfig.json for modern Node.js environments:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Step 2: Implement the Security Guard Plugin Logic
Create custom-plugins/agent-guard/src/index.ts. This plugin monitors agent reasoning turn events and runs periodic memory health diagnostics in the background:
import type { Context } from '@deepseek-ai/cordis';
// 1. Declare unique plugin identifier
export const name = 'agent-security-guard';
// 2. Core entry point: invoked by the microkernel once all dependencies are satisfied
export function apply(ctx: Context) {
let turnSequence = 0;
// Listen for Harness runtime ready event
ctx.on('ready', () => {
console.log('[Guard] Security auditor plugin mounted successfully into Harness runtime!');
});
// Listen before LLM reasoning begins
ctx.on('session/before-turn', (session) => {
turnSequence++;
console.log(`[Guard] >>> Turn #${turnSequence} started for session: ${session?.id ?? 'main-session'}`);
});
// Listen after turn reasoning and tool executions finish
ctx.on('session/after-turn', (session) => {
console.log(`[Guard] <<< Turn #${turnSequence} completed. Trajectory audit snapshot persisted.`);
});
// Register heartbeat memory probe (managed by ctx; purged automatically on reload without clearInterval)
ctx.setInterval(() => {
const memMB = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1);
console.log(`[Guard] Runtime memory health check: heap usage ${memMB} MB`);
}, 45000);
}
Step 3: Mount in cordis.yml and Test Live HMR
Register the local plugin path inside the root cordis.yml configuration:
mode: standard
# Model configuration
model:
provider: deepseek
name: deepseek-coder
apiKey: env(DEEPSEEK_API_KEY)
# Plugins list
plugins:
# Mount our custom local plugin
- name: "./custom-plugins/agent-guard"
# Official execution plugins
- name: "@deepseek-ai/dsh-plugin-bash"
config:
timeoutMs: 30000
- name: "@deepseek-ai/dsh-plugin-str-replace"
Start the web dashboard in patch mode:
pnpm dsh web --patch ./cordis.yml
The terminal will log [Guard] Security auditor plugin mounted successfully. Try editing the log strings inside src/index.ts; Cordis's built-in HMR will update the running instance in real time without dropping active sessions.
Enterprise Best Practices & Pitfalls to Avoid
- Avoid Unmanaged Global Timers: Never call
global.setIntervaldirectly. Always usectx.setIntervalso the microkernel disposes of timers when reloading; - State Isolation: Do not store state in module-level global variables. Use Cordis
Servicesingletons for persistent data; - Error Boundaries: Wrap high-risk I/O inside event handlers with localized
try-catchblocks to prevent single-listener exceptions from disrupting the pipeline.
Frequently Asked Questions (FAQ)
Q1: Why does relative module loading in cordis.yml occasionally fail?
Answer: Ensure paths start with ./ (e.g. ./custom-plugins/agent-guard). When pointing directly to TypeScript source, run Harness with --import tsx or build beforehand using npm run build.
Q2: Exactly when is the apply function invoked?
Answer: apply(ctx, config) is called only after the microkernel resolves the dependency DAG and all services declared in export const inject are verified in the ACTIVE state.
Q3: How does Cordis eliminate memory leaks upon plugin disposal?
Answer: The Context proxy records every listener, timer, and child sub-fiber. When unmounting, Cordis iterates the disposal tree in reverse order and clears all associated handles.
Q4: How should sensitive credentials (like DB tokens or API keys) be injected?
Answer: Export a typed Config schema via @deepseek-ai/schemastery, bind environment variables in cordis.yml using apiKey: env(MY_TOKEN), and chain .role('secret') to mask values across logs and UI cards.