Replaces the 20-line stub with a bilingual README pair covering:
- getting started (`pnpm run demo:repl` + Node ^22.19 || ≥24 + pnpm 11.7)
- editor integration (Zed / VS Code / Cursor over ACP)
- programmatic embedding via `@deepseek-ai/dsh-app-boot` (with the
two failure paths of `boot()` vs `installFailLoud` spelled out)
- writing a plugin (`echo-tool` grounded in `examples/echo-agent`, with
the runnable command `pnpm run demo:echo`)
- Packages table grouped by family, including `session-query`,
`context`, and Support (with `invariants` called out as a runtime
diagnostic mounted by `dsh-agent-core`, not test-only)
- deep-dive links, community, license
Bilingual pairing: English and Chinese sides share byte-identical code
blocks and mirroring link ordinals; language-asymmetric community
channels (Discord/X in EN, WeCom in ZH) sit inside HTML `<a href>`
tags that the pairing gate's structural signature excludes by design.
ZH side follows `docs/i18n/terminology.md` — first `agent` occurrence
annotated as `agent(智能体)`, plain `agent` thereafter.
Ships an in-repo skill at `.agents/skills/dsh-arch-diagram/` that owns
the two architecture PNGs (`assets/arch-{en,zh}.png`). HTML templates
+ shared CSS + a `render.sh` that renders via Chrome headless. The
renderer is portable (auto-detects Chrome/Chromium on macOS + Linux,
uses `--no-sandbox` under root for container envs, discovers the local
http.server port from Python's own startup line so `lsof` isn't
required). The 12-card capability row uses `flex: 0 0 auto` so
adjacent card backgrounds cannot clip descriptions; layout tuned to
1560px page / 96% row-width / 1820×580 render viewport.
Consolidates the four review-response commits from the initial round
of ds-review-bot feedback; each review round is documented in a
top-level PR comment for traceability.
DeepSeek Harness
The plugin-first agent SDK. Every capability — including the loop — is a plugin.
English | 中文
What is this?
DeepSeek Harness is a TypeScript SDK for building AI agents on top of the Cordis microkernel. Every service, including the ReAct loop, is a plugin registered through ctx.*. A batteries-included service registry ships in the box — LLM adapters, sandboxed execution, filesystem with policy, web search, sub-agents, dynamic workflows, session persistence, and more — and a cordis.yml at your project root chooses which get loaded. You can replace any of them, add your own, or leave the shipped defaults alone.
Getting started
New project (one-command scaffold):
npm create @deepseek-ai/harness # coming soon, not yet on npm
From source (read the code / run demos / contribute):
git clone https://github.com/deepseek-harness/deepseek-harness.git
cd deepseek-harness
pnpm install
export DEEPSEEK_API_KEY=sk-... # optional — omit and use pnpm run demo:echo (mock, no key)
pnpm run demo:repl
Requires Node ^22.19 || ≥24 and pnpm ≥ 11.7 (Node engine and pnpm pinned in package.json; corepack enable picks up the exact pnpm version). Node 23 is not on the support matrix.
Heads up: demo:repl runs real read / write / edit file tools and bash in your current working directory — best run from a scratch dir or a git-clean project so you can review the changes.
Use it in your editor
Harness ships an Agent Client Protocol (ACP) server. ACP lets an editor drive an agent from its sidebar; Zed supports it natively.
The ACP server command (from your local clone):
pnpm run demo:acp
Zed side — Zed's settings.json (Cmd-Shift-P → "zed: open settings") takes an agent_servers entry:
{
"agent_servers": {
"DeepSeek Harness": {
"command": "pnpm",
"args": ["--dir", "/path/to/deepseek-harness", "run", "demo:acp"],
"env": { "DEEPSEEK_API_KEY": "sk-..." }
}
}
}
--dir points at your local clone. Zed launches the agent as a subprocess; each Zed session maps to its own agent instance, with chat in the sidebar and tool calls (arguments, results, file diffs) rendered inline in the editor. Configuration details in examples/acp-agent (including the snapshot-tested surface).
VS Code / Cursor — install an ACP client extension for either editor, such as ACP Client (formulahendry.acp-client) or ACP Pro (duclvz.acp-pro), and point a custom agent at pnpm run demo:acp.
Other ACP clients — same launch. Feature-by-feature support matrix in packages/ui/acp/acp-feature-support.md.
Embed it in your own app
Harness bootstraps from a cordis.yml via @deepseek-ai/dsh-app-boot. For library-style integration into your own Node.js service, the same boot helpers apply:
// my-app.ts
import {
boot,
installFailLoud,
loadEnv,
resolveConfigPath,
} from '@deepseek-ai/dsh-app-boot'
installFailLoud('my-app')
loadEnv('my-app')
const ctx = await boot('my-app', resolveConfigPath('./cordis.yml', undefined))
// ctx is the Cordis root Context; every service you mount in cordis.yml
// is reachable via ctx.* (ctx.agents, ctx.sessions, ctx.tools, …).
// The app plugins loaded from cordis.yml keep the process alive on their own
// (stdio agents hold stdin; the ACP agent holds an RPC connection).
// To shut down programmatically, call `await ctx.fiber.dispose()`.
boot() returns once the whole plugin tree has settled. Two separate failure paths: a module-import failure rejects the boot() Promise directly, so the caller's await throws — handle it with try/catch. installFailLoud covers a different case — a late plugin-init rejection surfacing after boot() has already resolved, which would otherwise become an unhandled rejection and die silently. In cordis.yml, the entry-point app plugin — dsh-stdio-agent for a REPL, dsh-acp-agent for an ACP server, or a custom one — sits alongside whichever services should load. Full helper surface: packages/ui/app-boot.
For end-to-end examples, see examples/:
echo-agent— a minimal setup with a mock LLM and an echo toolcoding-agent— a full coding agent wired to the real DeepSeek LLMacp-agent— ACP server, with a sandbox composition variant
Demo
Harness driving Zed as an ACP agent — chat in the sidebar, tool calls (bash, file edits, diffs) rendered inline in the editor:
Write a plugin
A Harness function/namespace plugin exports name, inject, and apply — cordis's Loader reads those separately. export default breaks this shape (why): the Loader keeps only the apply function and silently drops inject / name, so the plugin fails to load with cannot get property … without inject. Inside apply(ctx), tools / LLM adapters / services register through ctx.*.
The minimal echo tool from examples/echo-agent:
// echo-tool.ts
import type { Context } from 'cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'echo-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'echo',
description: 'Echo the given text back, uppercased.',
parameters: {
text: { type: 'string', required: true },
},
async execute(args) {
// args is typed: { text: string }
return [{ type: 'text', text: `ECHO: ${args.text.toUpperCase()}` }]
},
}))
}
parameters uses the schemastery JSON-Schema-shaped DSL — one field per property, with required: true for mandatory ones. A leaf cordis.yml is a flat EntryOptions[] the Loader iterates; this tool's entry looks like:
- id: echo-tool
name: './echo-tool.ts' # your tool
Alongside it, a bootable config also needs an LLM adapter and a stdio-agent app entry whose config.model points at an id that adapter registers. The minimal runnable version — mock LLM + this echo tool + a stdio-agent entry wired to mock-echo — is examples/echo-agent, launched via:
pnpm run demo:echo
LLM-adapter and UI-plugin shapes: docs/cookbook/extension-cookbook.md.
Packages
All packages ship under the @deepseek-ai/dsh-* scope. Grouped by family:
| Family | What lives here |
|---|---|
Core (packages/core/) |
dsh-scope · dsh-session · dsh-tools · dsh-agent · dsh-agent-loop · dsh-system-prompt |
LLM (packages/llm/) |
dsh-llm (the seam) + dsh-llm-deepseek (hand-rolled) and dsh-llm-pi-ai (library-backed twin — same DeepSeek endpoint, different internals, kept for design verification) |
Bash (packages/bash/) |
Shell execution: local + sandboxed backends, model-facing bash tool |
Filesystem (packages/fs/) |
Filesystem service with a policy layer, read / write / edit tools |
Web (packages/web/) |
Web search (Perplexity, Exa, DeepSeek) + fetch, model-facing tool |
Sandbox (packages/sandbox/) |
Process-confinement seam (bwrap / Landlock / Seatbelt) — wraps a caller's argv under a per-call policy; execution itself lives in ctx.bash |
Code runtime (packages/code-runtime/) |
JS worker runtime that Code Mode dispatches into |
Sub-agents (packages/subagent/) |
spawn, fork, plus in-process / subprocess / ACP-backed backends |
Workflows (packages/workflow/) |
Dynamic workflow orchestration (worker-thread execution) |
Skills (packages/skill/) |
Skill-provider registry (ctx.skills) + a local-filesystem provider |
Session persistence (packages/session-persistence/) |
Event-log persistence: JSONL and SQLite backends |
Session query (packages/session-query/) |
ctx.sessionQuery — unified logical-corpus reads over live sessions + persistence |
Compact (packages/compact/) |
Context compression / summarization |
Context (packages/context/) |
Opt-in request-context enrichment (e.g. dsh-time-context — dynamic time-in-prompt) |
Cordis toolset (packages/cordis/) |
Model-facing tools that inspect / mount / unmount cordis plugins at runtime |
UI apps (packages/ui/) |
dsh-stdio-agent (REPL) · dsh-acp-agent (ACP server) · dsh-app-boot · approval + ask-user primitives |
Hooks (packages/hooks/) |
Hook protocol + Claude Code / OpenAI Codex hook-config bridges |
Guards (packages/guard/) |
Advisory loop-hygiene plugins (e.g. repeat-tool-guard for repeated-call escalation) |
Timeouts (packages/timeout/) |
timeout-policy — a zero-config tools/execute wrapper enforcing per-tool timeoutMs |
Todo (packages/todo/) |
The model-facing todo_write tool (whole-list task tracker) |
Support (packages/support/) |
invariants — runtime diagnostic plugin mounted unconditionally by the shipped dsh-agent-spine-demo bundle; plus test/dev-only helpers (llm-replay, acp-snapshot, subagent-mock) |
Example bundles (packages/examples/) |
Ready-to-run demo compositions the top-level demo:* scripts launch: dsh-agent-spine-demo (default spine + capabilities), dsh-stdio-demo (REPL), dsh-acp-demo (ACP server), dsh-jsonrpc-demo |
Utils (packages/util/) |
Internal utility packages (brand, timeout) |
For the full module dependency graph, see docs/module-graph.md.
Deep dives
To understand what makes DeepSeek Harness different, start here:
- Architecture — the service taxonomy and the microkernel structure
- Agent lifecycle — how a turn flows through the loop, with sequence diagrams
- Cordis primer — a working introduction to the underlying plugin framework
- Tool execution pipeline — how a tool call passes through permission gates, hooks, and logging
- Capability seams — the extension points each service exposes
- Code Mode — the model writes one JavaScript program per turn that chains many bash / tool calls, executed in a single runtime pass. One model round-trip per multi-step operation, not one per call.
- Dynamic Workflows — the model writes a plain-JS orchestrator that fans out sub-agents in parallel, joins their results, and returns to the parent — instead of a chain of sub-agent tool calls.
- Self-referential Cordis toolset — the SDK's own plumbing (
cordis_inspect,cordis_mount,cordis_unmount) is exposed as tools, so the model can inspect its own runtime and load new plugins on the fly.
Docs site: deepseek.com/harness-sdk/docs.
Community
- GitHub Issues — bug reports
- GitHub Discussions — questions, ideas, RFCs
Real-time chat on Discord. Release announcements on X / Twitter.
License
BSD 3-Clause © DeepSeek
