DeepSeek Harness extension points: DSH plugin mechanism map
A DSH plugin feature is never attached at random: the official docs publish a feature-to-mechanism map that ties every product feature to a listener on a documented extension point, which is what makes the microkernel claim verifiable — no line edits the agent loop itself (source). This article turns that table into a searchable "I want X → attach at Y" handbook and explains the three-way ownership test; the three extension-point kinds (core / seam / bundle) are already covered in the site's DSH plugin core services and capability seams, so this article does not repeat them.
Why the DeepSeek Harness feature-to-mechanism map makes the microkernel verifiable
The official docs turn the microkernel claim into a checkable statement: every product feature maps to a listener on a documented extension point, so you can audit features one by one instead of trusting any "we changed one line in the loop" story (source). The cookbook further groups extensions into four extension patterns:
- Tool plugin — registers on
ctx.tools;defineToolis the typed helper, but raw JSON Schema works too (that is how MCP-sourced tools arrive). - Hook plugin — runs at interception points, illustrated by a permission gate (
tools/pre-executereturns{ kind: 'deny', reason }or callsnext()); sandbox, permission, and plan-mode plugins reuse the same extension point. - UI plugin — renders from the
session/eventstream and drives input back withagent.followup()/agent.steer(). - External protocol driver — bridges a protocol peer into
ctx.agents, serving a UI or an automation client.
The four patterns are themselves a first answer: whichever pattern your plugin matches tells you the extension point it mainly attaches to.
The DeepSeek Harness feature-to-mechanism table
The official table lines product features up with plugin mechanisms, covering everything from the hook system to plugin hot reload (source). An excerpt:
| Product feature | Plugin mechanism |
|---|---|
| Hook system (user + project level) | Listeners on agent/session-start, agent/pre-step, agent/request, tools/pre-execute, tools/post-execute, agent/turn-stopping; the last can trigger a next step via steering |
/goal | ctx.goals holds persistent state; dsh-goal-round-driver schedules same-session Rounds over the public Agent |
/loop | followup() the next iteration on the turn/end session event, or force continuation |
| Queued messages + steering | Core Agent.followup() / Agent.steer() |
| Context compaction (auto + manual) | ctx.compaction seam + dsh-compaction-basic; pressure checks on serial agent/pre-step, overflow recovery on agent/request-error |
| Configurable system prompt | ctx.systemPrompt.section(), with ordering and scoped local overrides |
| AGENTS.md (root) | A section provider that reads the file |
| Built-in tools | ctx.tools.register(); schemas flow into assembly (dsh-tool-* series) |
| ToolSearch / progressive disclosure | Swap a scoped ctx.tools.restrict() registration when the visible set changes |
| Tool deadlines / retries / metrics | Wrap core dispatch with tools/execute (only exec.signal is replaceable) |
| Final tool-result audit / capture | Observe immutable results with tools/result; use tools/post-execute only to transform |
| Monotonic terminal turn policy | Call ToolExecution.concludeTurn() from a successful terminal tool call |
| Subprocess sandbox | ctx.sandbox backends; capability-level denial via tools/pre-execute |
| Permission system / AskUserQuestion | Return ask from tools/pre-execute and answer via ctx.approval; register a separate model-facing ask tool for general user questions |
| Plan mode | Logged plan/mode state, a plan:policy guidance section, a /plan [message] entry, and a user-reviewed exit_plan_mode exit |
| Subagent delegation | ctx.subagents provider registry + dsh-tool-subagent exposing one configured provider to the model |
| MCP | One plugin per server: discover tools → ctx.tools.register() |
| Skills | Section + tool registration; inject() the skill content on invocation |
| Memory | A section provider + tools |
| Cron | A plugin registers a model-facing scheduling tool; followup(…, { source: { kind: 'cron', … } }) when idle, inject() when busy |
| UI (GUI; CLI emits JSONL) | Listen to session/event (assistant chunks, boundaries, tool activity); input → followup() |
| Web Client chat business node | Register a ConversationNodeDefinition and a conversation.chat.node keyed renderer |
| Telemetry / replayable trace | session/event → JSONL; replay = sessions.create(id, { seed }) |
| Model adapter | Register an LlmAdapter subclass via registerAdapter |
| Plugin hot reload | Every registration is a ctx.effect, so the repo's HMR applies directly |
Use it in reverse: write the feature name you want, find the plugin mechanism it maps to, then read that mechanism's detail doc. If there is no row for it, you are building a cohesive standalone domain service rather than attaching a listener to an existing extension point.
Extension-point ownership in a DSH plugin: the three-way test and self-checks
There is only one test — what are you replacing (source). In practice, four steps:
- Replace the implementation of a capability — plug into an existing service seam such as
ctx.llm,ctx.fs,ctx.shell,ctx.compaction, orctx.sessionPersistence. Expect: zero changes to upper tools. - Extend a pipeline stage — add an event listener whose waterfall returns a typed decision; do not replace a spine service. Expect: a reversible, unloadable extension; see DSH plugin events.
- Assemble a product form — pick a provider at the composition layer with composition packages and bundle points. Expect: swapping an implementation only touches the composition.
- None of the above — implement it as a cohesive standalone domain service, such as a domain-scoped registry. Expect: no confusion with the spine or a seam.
Two official caveats are easy to miss: first, system-prompt/assemble is a collaborative whole-assembly transform whose returned result is authoritative, so listener authors are responsible for preserving the contributions of active Code Mode and structured output protocols; second, tool filtering that must keep display, lookup, and execution aligned should prefer ctx.tools.restrict() over maintaining your own visible set. Three self-checks after writing: does the feature really map to some extension point; did you mistake "extend the flow" for "replace an implementation"; and are all subscriptions and registrations kept inside apply so they roll back with the fiber. To compare community implementations on the same extension point, search the DSH Plugin Hub; for the three extension-point kinds see core services and capability seams, and for tool contract details see tool authoring.
FAQ
A DSH plugin should ask what it is replacing: to replace an implementation, plug into an existing service seam; to extend a pipeline stage, add an event listener; to assemble a product form, go through composition packages and bundle points; and only when none of those fit, build a cohesive standalone domain service. The official feature-to-mechanism map makes this test verifiable because every feature maps to a listener on a documented extension point.
The DeepSeek Harness feature-to-mechanism map shows that the microkernel claim is verifiable: every product feature — the hook system, /goal, /loop, context compaction, MCP, skills, cron, replay, and more — corresponds to a listener on a documented extension point, with no line editing the agent loop itself.
A DSH plugin picks the extension point by feature: /goal uses ctx.goals with a Round driver over the public Agent, /loop calls followup() on the turn/end session event, cron registers a model-facing scheduling tool and calls followup() when idle or inject() when busy, and subagent delegation uses the ctx.subagents provider registry plus dsh-tool-subagent to expose one configured provider to the model.
A DSH plugin should prefer ctx.tools.restrict() for tool filtering so display, lookup, and execution stay aligned; for prompt changes, know that system-prompt/assemble is a collaborative whole-assembly transform whose returned result is authoritative, so listener authors are responsible for preserving the contributions of active Code Mode and structured output protocols.
After choosing an extension point from the map, search DSH Plugin Hub for community plugins on the same extension point and compare their listeners and providers. For the core/seam/bundle distinction itself, read the capability seams article on this site, and for event dispatch see the events article.
Related Terms
- feature-to-mechanism mapping
- Feature-to-mechanism mapping is the official DeepSeek Harness table that maps every product feature to a listener on a documented extension point, making the microkernel claim verifiable, with no line editing the agent loop itself.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/extension-cookbook
- microkernel
- The microkernel is DeepSeek Harness's architectural claim: the kernel keeps only the agent loop, and every product feature listens as a plugin on a documented extension point, so features can be checked one by one without editing the loop.— https://deepseek-harness.github.io/deepseek-harness/en/reference/
- extension pattern
- An extension pattern is a plugin shape distilled by the official cookbook — tool plugin, hook plugin, UI plugin, and external protocol driver — each pairing a typical set of extension points; the snippets omit imports and helpers and are not runnable as-is.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/extension-cookbook
- typed decision
- A typed decision is the object a waterfall hook returns at an interception point, such as tools/pre-execute returning deny or calling next() to allow; the policy layer is reorderable, while a monotonic final denial uses ctx.tools.guard().— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/extension-cookbook