DSH Capability Seams: The Design Pattern Behind Everything-Is-a-Plugin

How DeepSeek Harness makes everything-is-a-plugin actually work. Deep dive into Capability Seams: the three-role pattern (Definition, Provider, Consumer) that lets you swap any component.

TL;DR: “Everything is a plugin” is easy to say, hard to architect. DeepSeek Harness solves it with Capability Seams — a three-role pattern where each capability splits into a Service Definition (the contract), a Service Provider (the implementation), and a Consumer (typically a model-facing tool). The provider and consumer never depend on each other — only on the definition. Swap one provider and the entire downstream stack follows. This is how one config change moves bash, PTY, and LSP to a remote sandbox simultaneously.

The Problem With “Everything Is a Plugin”

Every plugin framework claims extensibility. Few deliver on it at the system level. The typical failure mode: plugins can add features but can’t replace core behavior without forking. You can add a new tool, but can you swap how all tools execute? You can add a model adapter, but can you change how the execution sandbox works without touching ten other packages?

DeepSeek Harness solves this with a specific architectural pattern they call Capability Seams. It’s not a marketing term — it’s a structural design enforced by the package graph. Understanding seams is understanding why DSH’s “everything is a plugin” claim actually holds up under inspection.

The Three-Role Pattern

A capability seam consists of exactly three roles, typically in separate packages:

┌─────────────────┐       ┌──────────────────┐       ┌─────────────────┐
│ Service         │       │ Service          │       │ Consumer        │
│ Definition      │◀──────│ Provider         │       │ (tool/policy)   │
│ (contract)      │       │ (implementation) │       │                 │
└────────┬────────┘       └──────────────────┘       └────────┬────────┘
         │                                                     │
         └─────────────────────────────────────────────────────┘
                    Consumer injects the Definition,
                    never the Provider directly.

Service Definition — Declares the interface on ctx.<key>. Owns the TypeScript types, error codes, and event names. Changes rarely after consumers depend on it.

Service Provider — Implements the interface. Can be swapped by changing one line in cordis.yml. Multiple providers can exist (local, Docker, remote, E2B).

Consumer — Uses the capability, typically to expose it as a model-facing tool. Depends only on the Definition, never on any specific Provider.

The critical constraint: Provider and Consumer do not depend on each other. This is what makes the swap possible — you can replace the provider without the consumer knowing or caring.

A Concrete Example: Bash Execution

DeepSeek Harness packages directory showing the seam structure The packages/ directory in DSH — each capability seam is split across definition, provider, and consumer packages.

The Bash capability in DSH consists of three packages:

RolePackageWhat It Does
Definitiondsh-shellDefines ctx.shell service, bash request/result types
Providerdsh-bash-localExecutes commands on the local machine
Consumerdsh-tool-bashExposes bash as the model-callable bash tool
# cordis.yml — swap the provider, everything else stays
- name: '@deepseek-ai/dsh-bash-local'
# Replace with:
# - name: '@deepseek-ai/dsh-bash-sandbox'    # sandboxed execution
# - name: '@deepseek-ai/dsh-bash-docker'     # Docker container
# - name: '@deepseek-ai/dsh-bash-remote'     # remote machine

The model sees the same bash tool with the same schema. The consumer (dsh-tool-bash) doesn’t change. Only the provider row changes in config.

Why One Swap Moves Everything

Here’s where the design gets powerful. The subprocess seam (ctx.subprocess) sits beneath multiple consumers:

  • The bash executor uses it for collected batch output
  • LSP uses it for raw protocol pipes to language servers
  • The PTY backend uses it for terminal sessions
  • The ACP subagent backend uses it for piped ndjson

All of these share one execution world. Point ctx.subprocess at a remote sandbox (via subprocess-e2b instead of subprocess-local), and bash, PTY, and LSP all move to that sandbox simultaneously, with no code changes in any consumer.

This is the deeper meaning of “capability seam” — it’s not just pluggability, it’s coordinated pluggability. Related capabilities share underlying providers, so a single infrastructure swap cascades through the entire tool surface.

The Filesystem Seam: Four Providers, Many Consumers

The filesystem seam (ctx.fs) demonstrates the pattern at scale:

PackageRoleNotes
dsh-fsDefinitionDefines ctx.fs with read/write/edit/stat/glob operations
dsh-fs-localProviderLocal filesystem access
dsh-fs-e2bProviderE2B sandbox filesystem
dsh-fs-sandboxProviderSandboxed/restricted filesystem
dsh-tool-fsConsumerModel tools: edit, read, read_image, write
dsh-tool-fs-searchConsumerModel tools: glob, grep
dsh-fs-observation-policyPolicyRead-before-write enforcement via fs/* events

The filesystem also demonstrates event-based policy injection. dsh-fs-observation-policy doesn’t implement the filesystem — it listens to fs/* events and enforces that the model must read a file before editing it. This policy works regardless of which provider backs ctx.fs.

The Complete Seam Inventory

DSH ships with 30+ capability seams. Here are the major families:

Seamctx KeyDefinitionProvidersConsumers
LLMctx.llmdsh-llmllm-deepseek, llm-pi-ai, llm-replayagent-loop, compaction-basic
Shell/Bashctx.shelldsh-shellbash-local, bash-sandbox, pwsh-localtool-bash, tool-pwsh
Subprocessctx.subprocessdsh-subprocesssubprocess-local, subprocess-e2bbash-local, lsp-stdio, terminal-bash, subagent-acp
Filesystemctx.fsdsh-fsfs-local, fs-e2b, fs-sandboxtool-fs, tool-fs-search
Sandboxctx.sandboxdsh-sandboxsandbox-localfs-sandbox, bash-sandbox
Session Persistencectx.sessionPersistencedsh-session-persistencepersistence-jsonl, persistence-sqlitesession-persistence
Webctx.webdsh-webweb-search-exa, web-search-perplexity, web-search-deepseek, web-fetch-httptool-web
LSPctx.lspdsh-lsplsp-stdio, lsp-localtool-lsp
Compactionctx.compactiondsh-compactioncompaction-basiccommand-compact
Subagentsctx.subagentsdsh-subagentsubagent-spawn-in-process, subagent-fork-in-process, subagent-acp, subagent-codex, subagent-claude-code, subagent-dsh-sdktool-subagent, tool-subagent-control
Terminalsctx.terminalsdsh-terminalterminal-bashtool-terminal
Storagectx.storagedsh-storagestorage-json, storage-sqlitestorage-domain
Credentialsctx.credentialsdsh-credentialscredentials-localMultiple
Telemetryctx.sessionTelemetrydsh-session-telemetrysession-telemetry-otelMultiple

How It Works in Cordis

Cordis academic paper on Spatiotemporal Composability The Cordis paper — “A Programming Paradigm for Spatiotemporal Composability” — the theoretical foundation of DSH’s seam pattern.

DeepSeek Harness GitHub repository deepseek-ai/deepseek-harness — 18.5k stars, 12,293 commits, MIT license.

Under the hood, seams use Cordis’s service dependency system:

// Service Definition — declares ctx.shell
export abstract class ShellService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'shell')
  }
  abstract execute(request: BashRequest): Promise<BashResult>
}

// Provider — implements ctx.shell
export function apply(ctx: Context) {
  ctx.plugin(BashLocalProvider)  // extends ShellService
}

// Consumer — uses ctx.shell
export const inject = ['tools', 'shell']  // declare dependency
export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'bash',
    // ... schema
    execute: (args) => ctx.shell.execute(args)
  }))
}

The inject declaration tells Cordis: “don’t activate this plugin until ctx.shell exists.” Load order is expressed through service requirements, not manual sequencing. And registrations are reversible effects — if the provider unloads, the consumer’s tool automatically deregisters.

Design Principles Behind Seams

Reading the DSH architecture docs reveals several deliberate principles:

1. No role alone is a seam. A Definition without a Provider and Consumer is just an interface. Adding a capability means designing all three roles.

2. Closed unions for safety. The LSP seam exposes exactly four operations (goToDefinition, findReferences, goToImplementation, hover). Adding a fifth is a compile-enforced change across the seam, all providers, and the tool — you can’t accidentally add an operation only one provider handles.

3. Events for interception, methods for capability. Policy (like “read before write”) attaches via events; direct execution uses service methods. This keeps the method surface small while allowing arbitrary policy injection.

4. Registrations are effects. Every tool schema, prompt section, adapter, and listener is installed through ctx.effect(). When a plugin unloads, everything it registered unwinds automatically. No orphaned state.

5. The seam owns normalization. Consumers never see provider quirks. The web search seam truncates results to maxResults regardless of whether the provider over-returns. The LSP seam normalizes all responses into a closed discriminated union.

What This Means for Plugin Developers

If you’re building a DSH plugin, the seam pattern gives you a decision framework:

  • Adding a new capability? Design all three roles. Put them in separate packages if they’ll evolve independently.
  • Adding a new provider for an existing capability? Implement the Service Definition’s abstract methods. Your provider competes with existing ones purely through config.
  • Adding a new consumer? Inject the Definition (e.g., inject: ['shell']). You’ll never import a concrete provider.
  • Adding policy? Listen to the seam’s events. You don’t need to wrap the provider or modify the consumer.

Comparison: How Others Do It

DSH (Capability Seams)LangChainOpenClawClaude Managed Agents
Extensibility modelThree-role seam, config swapRunnable interface chainFork + adapterNot extensible
Provider swap costOne config lineRefactor consumer codeFork pipelineN/A
Coordinated swapYes (subprocess moves bash+LSP+PTY)NoNoN/A
Compile-time safetyClosed unions, inject declarationsRuntime duck typingRuntimeN/A
Reversible registrationBuilt-in (Cordis effects)Manual cleanupManualN/A

The unique contribution isn’t “plugins” — it’s coordinated infrastructure swap through shared capability ownership. That’s what other frameworks can’t do without forking.

For more on DSH’s architecture, see our DeepSeek Harness developer preview overview and the three-way framework comparison.

FAQ

What happens if no provider is loaded for a seam?

Consumers that inject the service simply don’t activate. Their tools never register, their prompt sections never appear. The system degrades gracefully — no crashes, just missing capabilities.

Can I run multiple providers for the same seam?

Some seams support multiple providers (like ctx.web with separate search and fetch providers). Others are single-provider by design (like ctx.shell). The Definition’s interface determines this.

How do I know which seams exist?

Run dsh --profile web --dump-config to see your active plugin tree. The capability-seams doc has a full mermaid graph of all service relationships.

Is this pattern unique to DSH?

The three-role separation exists in other systems (e.g., driver models in operating systems). What’s distinctive is combining it with Cordis’s reversible effects, typed event system, and config-driven composition — making the swap a user-facing config change rather than a code change.

What about performance overhead?

Minimal. The indirection is one ctx.shell.execute() call rather than a direct function call. The real cost is Cordis’s dependency resolution at boot time, which is a one-time O(n) pass over the plugin graph.