Files
deepseek-harness/docs/architecture.md
Tianyi Cui 5569f3f8ac docs: repair rewritten rationale and stale claims from the ACP reduction
The automation-only rewrite edited many implemented Agent Notes; several
edits replaced still-live or historical rationale instead of reframing:

- llm-model-catalog: restore the prompt/request consistency section and
  selection-ownership alternatives — installAgentLlmTarget and the TUI
  /model selector still ship that design; only the ACP wire is gone.
- plan-specific-collaboration-state, acp-multi-session, todo-write,
  ask-user-question: link the superseding automation-only note instead
  of silently rewriting the original decision or motivation; drop a
  paragraph duplicating the Web-provider facts stated two paragraphs up.
- sandbox: stop claiming unit coverage for turn-enclosed config writes
  (that mechanism left with the bridge) and retitle the commit-boundary
  paragraph accordingly.
- Fix the missing blank line before '## Consequences' in the
  plugin-command-registration pair, the JSON-RPC/Web render-intent
  consumer misattribution (the second consumer is the host/client
  runtime), stale bash_output/bash_kill names, and 'optional goals' in
  architecture.md.
- examples/acp-agent/README.md: point at the package contract instead
  of restating it; packages/ui/permission and plan-mode READMEs record
  the consumer-less preset service and the exit_plan_mode coverage gap
  under Known Limitations.
- 2026-06-19-acp-snapshot-tests: the new note defers the corpus
  migration rather than committing to it; say so.

Re-record the touched bilingual pairs.
2026-07-24 22:12:23 +08:00

17 KiB

DeepSeek Harness Architecture

English | 中文

DeepSeek Harness SDK uses Cordis: everything is a plugin, including the loop.

Overview

Harnesses are Cordis contexts whose packages contribute services, typed events, and disposable registrations.

packages/core/ groups the default agent flow; capabilities remain plugins.

Default Services

ctx key Package Role
dsh-scope scoped-context registration and shared layer storage (library)
ctx.sessions dsh-session in-memory event-sourced sessions
ctx.systemPrompt dsh-system-prompt ordered prompt sections, tool schemas, and prompt variables
ctx.tools dsh-tools tool registry and execution pipeline
ctx.agents dsh-agent live agents, delegated creation, agent/* events, and process-local initiator scope
ctx.agentLoop dsh-agent-loop concrete Agent driver

Capability Services

ctx key Package family Role
ctx.llm llm/ adapter registry and streaming model calls
ctx.tokenMeter llm/token-meter singleton replay-aware request/surface pressure
ctx.bash bash/ foreground/background command execution
ctx.pty pty/ owner-scoped persistent terminal sessions
ctx.sandbox sandbox/ same-world process confinement (argv wrapping, per-call policy)
ctx.sandboxPolicy sandbox/ shared sandbox policy home
ctx.codeRuntime code-runtime/ model-written program execution
ctx.fs fs/ filesystem provider primitives and policy events
ctx.lsp lsp/ semantic navigation registry
ctx.skills skill/ skill provider registry and progressive disclosure
ctx.web web/ search/fetch provider registries
ctx.compact, ctx.toolResultPrune compact//compact-tool-result-prune summary compaction; optional model-free result pruning
ctx.subagents subagent/ named delegation providers
ctx.planMode plan/ logged plan collaboration state
ctx.tasks tasks/ background task registry + generic task_* control tools
ctx.workflows workflow/ script-driven multi-agent orchestration
ctx.goals goal/ persisted same-session goals
ctx.sessionPersistence session-persistence/ durable session-log storage
ctx.sessionQuery session-query/ session-query interface: concrete live-preferred exact/filter/trace; exactly two abstract FTS methods via session-query-sqlite
ctx.sessionTitle session-title/ log-backed fallbacks plus one optional asynchronous provider
ctx.invariants support/invariants package-name-selected registry for package-owned runtime checks

Event

Events form the service extension API; see the exhaustive events catalog and producer/consumer map.

Event Domains

  • Session events are durable facts appended to the log and emitted through session/event.
  • Agent events carry the live Agent for status, prompt admission, request shaping, validation, and continuation.
  • Capability events let owning seams attach policy and adapters without importing the loop.

Interception Semantics

Waterfall events behave like around-middleware: a listener delegates by calling next(); returning without it vetoes or takes over. Full rule: Cordis waterfall semantics.

Default Loop Lifecycle

The shipped loop runs prompt-to-checkpoint work through plugin services and events.

A session is append-only. Each ordinary turn claims one queued send() item; injection claims none. A successor awaits the preceding claimed turn's checkpoint but may share its running interval (decision). A turn ends when model and plugins stop it; a step is one model request plus tools. In the sequence below, quotes mark durable events.

Without an id, creation mints <config-id>-session-<uuid>; sessionId resumes or creates, while resumeSessionId requires history. Resume restores lineage and delegation depth before publication. Setup failures emit agent-loop/config-start-failed; teardown is silent.

Turn Flow

choose declarative identity and fresh/resume path
  -> prepare private session + agent.ctx -> await unpublished setup
  -> enter session + agent -> session/created -> agent/created
  -> enable driving -> agent/session-start(source) -> start driver
forever:
  wait for a queued message
  emit agent/status(running)
  TURN:
    'turn/start'
    claimed message + contexts -> agent/prompt-submit
      allowed prompt -> 'user/message' with prompt-prefix context baked in; append separate contexts
      blocked prompt -> 'prompt/blocked' -> 'turn/end'(rejected)
    STEP loop:
      drain steering with the same prefix/separate context placement (no prompt-submit)
      assemble system prompt and tool schemas
      agent/session-prefix (first step)
      agent/pre-step
      snapshot the derived messages (the reconstruction boundary)
      'step/start'
      agent/request (config only) -> log request/header -> checkpoint -> llm/stream (frozen)
      on final adapter-path or terminal in-band failure:
        'step/end'
        agent/request-error(original error, failure facts, immutable prior failures, signal)
        retry in the next numbered step or preserve the original error
      otherwise:
        'assistant/chunk'
        agent/step-result
        'assistant/message' (transformed content or empty success anchor after step-result rejection)
        schedule tool calls by ctx.tools.executionMode:
          exclusive -> one-call barrier
          parallel -> rolling pool, <= maxParallelToolCalls in flight; reclassify before start
          each start -> 'tool/call' -> ordered tools/pre-execute -> checkpoint -> concurrent tools/execute
          each model-order result -> ordered tools/post-execute -> 'tool/result'
        append accepted tool-batch context after all recorded results, then steering
        agent/post-step -> checkpoint complete response/results
        'step/end'
        agent/turn-continuation
        agent/turn-stop (terminal policy)
        stop unless tools or continuation policy ask for another step
    'turn/end'
    checkpoint persistence and notify idle/running status

Each step assembles ordered prompt sections, tool schemas, and variables; unknown references fail the turn. dsh-system-prompt owns identity and persona, while the loop supplies model and cwd (prompt ownership).

Tool-time context—including async inject() and post-tool additionalContexts—settles after results. Steering drains before agent/post-step, which sees durable output, results, context, and steering. Leftovers queue. Terminal agent/turn-stop remains authoritative through close/flush; later steering is discarded while queued prompts remain.

Pruning precedes summaries; overflow retries require durable progress. Bounded transient retries compose on agent/request-error; cancellation wins (compaction, retry).

Failure Boundaries

Adapter failures close the step before agent/request-error with exact Error, LlmFailure, and history. Retry opens another step; success clears history; exhaustion stores failure on turn/end. Failed chunks commit no message/tool.

Other failures use agent/error. Cancellation and disposal beat recovery; undispatched tool calls get synthetic tool/call/ABORTED_BEFORE_DISPATCH pairs. The turn signal retires before turn/end. Effective cancel() emits its typed cause before clearing queues and aborting; observers cannot veto, idle calls emit nothing, and durability records aborted. Disposal awaits quiescence (decision).

Session events are turn-enclosed. Reload closes an interrupted tail with a synthetic interrupted turn end. Post-close failures report only through agent/error; no safe in-turn position remains. Each turn has one TurnEndReason; TurnEndReasonMap owns the variants.

Agent Handles

ctx.agents owns live agents and returns AgentHandle { agent, dispose() }. Plugins use send(), steer(), inject(), cancel(), and whenIdle(). The caller fiber, factory provider, and consumer handle co-own teardown through one awaited disposer.

Agent Scope

Each agent owns a scoped agent.ctx; shared storage overlays global tool, prompt, and command entries while preserving domain views (decision). Scoped listeners filter dispatch, and every scoped contribution unwinds with awaited cleanup. CreateAgentOptions.setup(agentCtx) composes before publication. Typed resolvers derive carrier checks from merged Events and scopeTarget (semantic gates). See agent scope and subagent composition. AgentLoop runs inside ctx.agents.withInitiator(); private orchestration derives agent.session, while turn, step, signal, cwd, and authority remain explicit (decision).

State

Session Log

The session log is authoritative. deriveMessages() projects model history; raw assistant/chunk events remain for replay and UI fidelity. Fork, resume, transcript rendering, telemetry, and persistence derive from the same stream.

Model-visible ⟺ logged: the log reconstructs every request — messages at step/start fronted by the header's session prefix, and headers by folding request/header — and the package-owned dsh-agent-loop/invariant can assert it through ctx.invariants (reconstructability).

Durability is a plugin concern. Backends buffer synchronous session/event notifications. The semantic checkpoint policy drains requests before adapter dispatch, recorded top-level calls before tool dispatch, and complete response/result batches at agent/post-step; the loop retains the final turn-end checkpoint. SessionPersistence stores SessionEvent directly and metadata in SessionHeader; JSONL defaults to checksummed Zstandard, with SQLite under one contract (decision).

ctx.sessions.appendOutOfBand() joins plugin-owned log-only events to an open turn or creates a balanced, flushed zero-step turn. session/title folds latest-wins with source seqs and provenance; its immediate fallback and sole optional async provider never delay the agent response. Forks inherit titles (decision).

Model Content

Messages use typed blocks from merge-extensible ContentBlockMap; the same pattern types MessageSource, FinishReason, TurnTrigger, and TurnEndReason. New blocks coordinate adapters, UI, compaction, token metering, and persistence; replay measurements live in token-meter.md.

Streaming uses raw chunks and BlockAssembler. Each LlmAdapter.stream() is one provider attempt; adapters report facts and agent/request-error owns recovery. The loop logs chunks and successful provenance/replay state. Remote adapters use per-read idle watchdogs. Replay state crosses routes only when they share an adapter instance (contract).

Extension And Composition

Capability Pattern

A swappable capability usually splits into interface / implementation / consumer: service/events, a backend, and model-facing tools/prompts. Bash is the reference; the capability graph maps each family.

Exceptions combine layers: LLM interface/consumer; filesystem policy; web registries; named skill/subagent providers. Subagents spawn fresh, fork a completed-turn prefix, or use ACP children (subagent.md).

dsh-workspace-context composes baselines on agent/session-prefix and appends ctx.fs-discovered nested changes on tools/post-execute; its decision records isolation. dsh-paths owns shared paths.

Bundles And Apps

dsh-agent-spine-demo bundles a spine and optional goals. App packages own TUI, CLI, ACP automation, and JSON-RPC front doors (README, acp/, ui/). dsh-jsonrpc-agent boots external cordis.yml; the Python SDK supplies a default only without explicit config (Python SDK). Thin deployments use swappable backends and optional tools (examples/, runnable wirings, graph atlas).

Where New Behavior Goes

New behavior attaches to a documented extension point; a loop change updates this map.

Goal Mechanism
Add a model provider register an adapter on ctx.llm
Add a model-facing capability register on ctx.tools; schemas enter prompt assembly
Add shell execution implement and register a ctx.bash backend
Add persistent terminal execution register a ctx.pty backend and dsh-tool-pty
Add a human command register on ctx.commands; adapters discover and dispatch it without a model turn
Add background work register on ctx.tasks; generic task_* tools collect or stop it
Add filesystem access or policy implement a ctx.fs provider or listen on fs/* policy events
Confine spawned processes a ctx.sandbox backend; consumers wrap their argv before spawning
Intercept a request, tool, or turn use its agent/* or tools/* event; agent/turn-stop is the serial terminal stop
Add a session-stable prefix outside history compose agent/session-prefix; the request header logs it
Add UI or editor integration drive ctx.agents and render from session/event; terminal-only overlays use ctx.tui
Add durable session state add a SessionEventMap member and render/replay from the log
Add asynchronous session-title generation register the sole provider on ctx.sessionTitle
Manage a same-session objective use ctx.goals; continue through Agent and agent/*
Fork a live session use ctx.sessions.fork(source, boundary?, childSessionId?)
Scope a registration to one agent use that agent's agent.ctx (see Agent Scope)

The extension cookbook carries plugin skeletons and the feature-to-seam map; step-by-step guides cover packages, tools, LLM adapters, and vendored packages.

Quick Reference