This page explains DeepSeek Harness and, more generally, the modern shape of state-of-the-art agent harnesses. It assumes the reader already understands agents and LLMs. The goal here is not to define agents from scratch, but to make the harness architecture, execution model, abstractions, and technology choices easy to understand.
master at
b150a55 (21 August 2026).
DeepSeek Harness is still a developer preview, so its interfaces and composition may change.
The LLM does the reasoning, planning, interpretation, and generation. The harness provides the machinery that makes that intelligence useful in practice: prompt assembly, tool access, state, execution, persistence, policy, scheduling, UI, APIs, and orchestration.
DeepSeek Harness—published as the dsh CLI—is an open-source implementation of that runtime. It is currently a developer preview, so it is best read as both a usable system and a fast-moving architectural reference.

Plans and reasons, interprets results, chooses the next action, and produces explanations.
Builds requests and prompts, governs tools, tracks history and state, and coordinates the outside world.
Requires Node.js and starts locally on port 3080 by default.
npx @deepseek-ai/dsh webThe stable spine is small: sessions, system-prompt assembly, tools, the agent registry and loop, scoped registrations, and the LLM adapter seam.
DeepSeek Harness uses Cordis as its runtime substrate. Plugins contribute services, typed events, and reversible effects to a shared context. Even the model adapter, tool registry, session log, and default agent loop are plugins, so the architecture has no privileged monolith that extensions must patch.
Consumers depend on stable context keys such as ctx.tools, ctx.fs, and ctx.llm, while providers implement those seams. Registrations unwind when a plugin unloads, which makes lifecycle, hot reload, and replacement part of the design.

A running dsh is an ordered plugin tree. A profile selects bundle layers; base supplies the shared runtime, while web-app and headless add different product surfaces. User patch layers can replace or insert configuration rows.
A complete seam normally has three roles: a service definition, one or more providers, and a consumer such as a model-facing tool. Replacing a provider can move a whole capability to another execution environment without rewriting the consumer.
profile → ordered bundles → Cordis config rows → mounted plugin tree → services + events + reversible effects
The monorepo is grouped by capability family. The important boundary is not simply “frontend versus backend,” but service definitions, providers, consumers, composition layers, and product surfaces.

apps/cli and apps/web are product entry points. packages/boot, bundle, and preset assemble profiles and per-session agent configurations.
packages/core owns sessions, prompt assembly, tools, agents, scopes, and the default loop. llm, session, and storage provide adapters and durable data planes.
fs, subprocess, shell, terminal, sandbox, code-runtime, and lsp define the agent’s execution world.
subagent, jobs, workflow, goal, schedule, skill, mcp, acp, and sdk extend orchestration and external access.
A step is one model request plus the tools produced by that response. A turn contains zero or more steps: it opens before input is admitted and closes when no tool result, queued next-step input, or continuation is owed. A rejected first input can therefore produce a durable zero-step turn.
The agent inbox is the single admission queue. Before each request, plugins may rewrite or reject claimed input, prompt sections and tool schemas are assembled for the active scope, and model history is derived from the session log.

turn/*, step/*, user messages, assistant chunks and messages, plus tool calls and results are appended to the session log.
agent/pre-step, request and streaming waterfalls, tool-policy events, status updates, and agent/turn-stopping coordinate work in flight.
inbox → turn/start → pre-step → step/start → model stream → tool batch → step/end → next step or turn/end
A session is not a mutable transcript. Its append-only SessionEvent log is the source for model-visible history, replayable UI, forks, resume, telemetry, and persistence. The upstream rule is especially useful: if the model can see it, it must be reconstructable from the log.

Raw events preserve turn boundaries, streaming chunks, messages, tool activity, settings, and plugin-contributed facts. A conversation surface folds message-producing events and supports replacement operations used by compaction and pruning.
The persistence seam ships JSONL and opt-in SQLite providers. It preserves contiguous events, detects interrupted turns, and can restore, inspect, fork, or resume sessions without rewriting committed history.
Independent projection units fold the same stream into client-ready state such as conversation nodes, job state, goals, plans, and other views. Checkpoints speed restoration while the log remains authoritative.
Compaction records its own durable lifecycle, can prune oversized tool results, and replaces a balanced surface range with a summary while preserving the original log facts for audit and replay.
Tools are typed registrations, not raw callbacks. Each definition separates its model-visible schema from canonical JSON output, execution code, scheduling metadata, and replay-safe UI presentation. The registry validates arguments and results so callers, policies, Code Mode, and clients share one contract.

tools/pre-execute may allow, deny, or ask. User approval resolves one-shot asks; registered guards can only deny or abstain, so later hooks cannot weaken owner policy.
tools/execute can wrap dispatch for timeouts, retries, or metrics. Post-policy may accept, replace, block, or attach context before the tool’s final content invariant and observe-only result event.
Cancellation is cooperative for same-process tools. Calls are exclusive by default; only a tool that explicitly classifies a call as concurrency-safe may overlap with siblings in the loop’s bounded rolling pool.
Native calls, the reserved Code Mode transport, and each serialized Code Mode sub-call pass through the same registry. Denials and normalized failures remain ordinary tool results the model can react to.
tool/call → pre-execute → approval/guards → execute wrapper → body → post-execute → finalizeContent → tools/result → durable tool/result
The tool registry supports three presentation modes: native (the default), code, or both. Native mode publishes ordinary function schemas. Code Mode instead exposes a reserved run_code transport and generates a typed tools SDK for the current tool scope.

A program can branch, loop, run independent calls together, and combine typed JSON values without paying for a new model step after every micro-operation. Only the outer run_code result enters model context.
The shipped TypeScript provider uses one fresh Node worker thread per run, with bounded time, memory, and output plus hard termination. The runtime seam also defines language and isolation descriptors for alternate providers.
A worker thread provides containment and termination, not a security boundary. Upstream describes its trust posture as bash-equivalent; policy still belongs to the bridged tools and their execution providers.
Every SDK sub-dispatch travels back through ctx.tools, carries a parent execution token, and is serialized for deterministic logs. Code Mode changes orchestration shape, not authority.
The process sandbox wraps an argument vector before the shell or PowerShell consumer spawns it. Policy is resolved per call from the session and workspace, then a local provider selects an OS backend. If a confined mode is requested and no backend is usable, execution fails closed with SANDBOX_UNAVAILABLE; it never silently runs unconfined.

read-only denies writes except required sinks, workspace-write permits the workspace and a backend-defined temporary area, and danger-full-access deliberately bypasses confinement.
Linux prefers bubblewrap and falls back to Landlock, macOS uses Seatbelt through sandbox-exec, and Windows uses a restricted token plus ACLs.
The mode vocabulary governs filesystem effects only. Network access and process visibility are outside this contract, and Windows ACL or older Landlock enforcement may be reported as partial.
Containers, microVMs, and remote environments are not backends of this same-world sandbox seam. They replace coherent filesystem, subprocess, and shell providers so every capability operates in the same execution world.
These are separate, composable capabilities rather than one vague “multi-agent mode.” Subagents own delegated reasoning, jobs own background lifecycle and collection, and workflows run model-written orchestration scripts that start subagents through the same provider seam.

Named providers can coexist: in-process spawn and fork variants, ACP, Codex, Claude Code, and the dsh SDK. Requests can require structured output, a persona, tool filters, or depth limits, and unsupported capabilities fail loudly.
A continuable child is a durable child session that may be live, waiting, or cold-resumed. Follow-ups enter its ordinary FIFO inbox; controls can list descendants, queue a later turn, or interrupt current work without deleting the child.
The owner-scoped registry handles bash and subagent jobs through one lifecycle. job_list, job_output, and job_kill work across kinds, while completion notices wake or inject into the owning agent.
The workflow engine runs one model-written JavaScript body in a worker thread. Its agent(), parallel(), and pipeline() primitives orchestrate bounded child runs and produce durable parent-session records.
The implementation combines a TypeScript application platform with native helpers and protocol adapters. The important observation is how each technology sits behind a capability or transport boundary instead of leaking across the whole codebase.

TypeScript + Node.js power the host runtime, pnpm manages the workspace, and React + Vite build the Web UI. Cordis supplies service contexts, typed events, injection, scoping, and lifecycle.
Worker Threads run TypeScript Code Mode and workflow scripts. A packaged ripgrep binary powers glob and grep, while stdio language servers provide definition, reference, implementation, and hover queries.
Sessions can persist through checksummed compressed JSONL artifacts or an opt-in SQLite backend. OpenTelemetry provides the optional session telemetry provider.
MCP discovers and registers external tools. ACP provides an automation server, while the out-of-process SDK uses a newline-delimited JSON-RPC protocol with TypeScript and Python-facing distribution surfaces.
DeepSeek Harness is one implementation, but its source makes several production concerns unusually explicit. The useful lesson is not that every agent must copy its package graph; it is that state, policy, composition, execution, and presentation need stable ownership boundaries.
Composable capability seams, typed tool contracts with centralized policy, durable replayable state, scoped prompt assembly, and reversible lifecycle effects.
Explicit filesystem confinement, managed background work, bounded cancellation, subagent delegation, workflows, and multiple tool-presentation modes.
Dynamic prompt context is logged when model-visible; compaction and tool-result pruning control context pressure; skills load detailed instructions only when selected.
Approval policy, permission presets, plan state, user questions, goals, feedback, and commands are separate plugins rather than special cases hidden inside the loop.
| Dimension | Traditional Prototype Agents | DeepSeek Harness Architecture |
|---|---|---|
| Runtime Kernel | Monolithic Ad-hoc procedural scripts with hardcoded glue |
Cordis Substrate Profile-composed plugin tree with typed events and reversible effects |
| State & Memory | Volatile Simple ephemeral in-memory message arrays (messages[]) |
Log + Projections Append-only events, derived surfaces, JSONL/SQLite persistence, replay and resume |
| Tool Governance | Unguarded Direct callback invocation without unified controls |
Policy Pipeline Typed I/O, approvals, monotonic guards, wrappers, finalization and observation |
| Orchestration | Single Loop Chat-driven turn loops exclusively |
Multi-Surface Native/code/both tool presentation, subagents, jobs and workflow scripts |
| Execution Isolation | Unsafe Direct host subprocess execution (exec()) |
Explicit Boundaries Fail-closed filesystem confinement; remote worlds replace coherent provider seams |
| Concurrency | Synchronous Single-threaded sequential processing |
Bounded & Managed Opt-in parallel tool calls, owner-scoped jobs, child agents and worker-backed scripts |
Start with the contracts and the composition that actually boots, then follow one request through the runtime. The UI becomes much easier to understand after you know which durable events and projections it renders.
Read docs/architecture.md, then the Cordis primer and capability-seam graph. This establishes contexts, services, providers, consumers, events, effects, profiles, and bundles.
Run dsh --profile web --dump-config. Follow dsh-base first, then the Web or headless bundle and any profile patches layered above it.
Follow core/agent-loop into system-prompt assembly, ctx.llm, session events, and the complete tool pipeline. Keep durable session/* facts separate from live agent/* controls.
Choose one seam—filesystem and shell, Code Mode, sandbox, subagents, jobs, workflows, compaction, skills, or integrations—and read its definition, provider, consumer, subsystem doc, and tests together.
architecture → booted plugin tree → agent lifecycle → sessions + tools → one capability seam → client projections
The concise Markdown companion remains in README.md. All original visuals live in visuals/.