Files
deepseek-harness/docs/architecture.md
2026-07-20 01:43:32 +08:00

15 KiB

DeepSeek Harness Architecture

The DeepSeek Harness SDK builds on Cordis: everything is a plugin, including the shipped loop.

Overview

A harness is one Cordis context. Packages add services (ctx.llm, ctx.tools, ctx.sessions), typed events (agent/request, tools/pre-execute, session/event), and disposable prompt, tool, provider, adapter, and listener registrations.

packages/core/ groups the default agent flow; surrounding capabilities are equally first-class Cordis plugins.

Default Services

ctx key Package Role
dsh-scope scoped-context registration primitive (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.sandbox sandbox/ same-world process confinement (argv wrapping, per-call policy)
ctx.codeRuntime code-runtime/ model-written program execution
ctx.fs fs/ filesystem provider primitives and policy events
ctx.skills skill/ skill provider registry and progressive disclosure
ctx.web web/ search/fetch provider registries
ctx.compact compact/ session-log compaction
ctx.subagents subagent/ named delegation providers
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 storage for session logs
ctx.sessionQuery session-query/ live-preferred logical-corpus exact reads and relationship traces

Event

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

Event Domains

  • Session events are durable, replayable facts: boundaries, messages, tool activity, steering, compaction, and tool-owned records append to the log and flow through session/event.
  • Agent events carry the live Agent handle for status, diagnostics, prompt admission, request shaping, result validation, and continuation policy.
  • Capability events belong to their owning seam; tools/*, llm/*, system-prompt/*, fs/*, and subagent/* 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 drains prompts through checkpoints; every pause is exposed to plugins through services or events.

A turn drains queued input in an append-only session until the model requests no more tools or plugin continuation. A step is one model request plus its tool executions. In the flow below (sequence companion), quoted names are durable session events and event names are extension points.

Startup resolves identity. No id mints <config-id>-session-<uuid>; sessionId resumes or creates; resumeSessionId requires history. Active failures emit agent-loop/config-start-failed(sessionId, error), so front doors reject work; teardown stays 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 queued messages
  emit agent/status(running)
  TURN:
    'turn/start'
    each queued message -> agent/prompt-submit
      allowed prompt -> 'user/message' plus injected context
    every prompt blocked -> 'turn/end'(rejected)
    STEP loop:
      drain steering
      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 -> llm/stream (frozen)
      on final adapter-path or terminal in-band failure:
        'step/end'
        agent/request-error(original error, consecutive retry attempt, 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 -> 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
        '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 {{name}} variables; unknown or valueless references fail the turn. dsh-system-prompt owns the harness identity and default persona, which an agent scope may shadow. The loop supplies model and cwd (prompt ownership).

Tool-time context—including async agent.inject() notices and post-tool additionalContexts—settles after recorded results. Steering drains before agent/post-step, which observes durable output, results, context, and steering before signal closure. Leftovers become queued input. Terminal agent/turn-stop runs after continuation and steering folding, remains authoritative through turn close and flush, and discards later steering while preserving queued prompts.

dsh-compact-basic handles pressure and canonical overflow at checkpoints; retry requires a balanced surface replacement (decision).

Failure Boundaries

The turn is the containment boundary. Final adapter-path and terminal in-band failures close the step before agent/request-error; retry opens a numbered step; otherwise, the provider error survives. Attempts reset on success.

Other failures use agent/error. Cancellation beats recovery; undispatched calls get synthetic ABORTED results. Effective cancel() emits agent/cancel-requested before queue clearing or abort; observers cannot veto it, and idle calls emit nothing. Disposal awaits quiescence.

Every session event is turn-enclosed. Reloading preserves an interrupted tail and closes it with a synthetic interrupted turn end. Failures after durable turn close report only through agent/error because 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 drive Agent through send(), steer(), inject(), cancel(), and whenIdle(). The caller fiber and factory provider structurally co-own programmatic lifecycles; the consumer handle is the only other teardown capability. All owners await one disposer.

Agent Scope

Every live agent owns a scoped agent.ctx. Its registrations shadow globals, receive only that agent's dispatches, and unwind with it; async effects such as background-task cleanup are awaited. CreateAgentOptions.setup(agentCtx) composes the scope before publication. Typed resolvers derive carrier checks from merged Events signatures and scopeTarget (semantic gates). See agent scope and subagent composition controls. AgentLoop runs drivers inside ctx.agents.withInitiator(); private orchestration derives agent.session; other identities stay explicit (decision).

State

Session Log

The session log is the source of truth. deriveMessages() projects session events into the Message[] sent to the model; raw assistant/chunk events stay in the log for replay and UI fidelity. Replay, fork, resume, transcript rendering, telemetry, and persistence all derive from the same event stream.

Model-visible ⟺ logged: the log reconstructs every request — messages at step/start fronted by the header's session prefix, headers by folding request/header — and dev invariants assert this (reconstructability).

Durability is a plugin concern. Persistence backends buffer synchronous session/event notifications and the loop awaits a turn-end checkpoint before moving on. The SessionPersistence seam stores SessionEvent directly, with metadata in SessionHeader; JSONL and SQLite share one contract suite.

Model Content

Messages contain typed blocks (text, reasoning, tool-call, tool-result) derived from merge-extensible ContentBlockMap; the same pattern types MessageSource, FinishReason, TurnTrigger, and TurnEndReason. New block types coordinate adapters, UI bridges, compaction pricing, token metering, and persistence as one repo-wide contract; replay measurement types live in token-meter.md.

Streaming uses raw chunks (block-start through finish) and BlockAssembler. The loop logs and assembles chunks, storing provider/model provenance plus replay state. An LlmAdapter implements stream(), registers provider routes, and may expose selector metadata; it resolves and validates model ids. Replay state reaches targets only when both routes map to one adapter instance, which owns validation and conversion. The contract lives in llm-streaming.md.

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 the default spine and an opt-in persisted-goal stack (README). Terminal and ACP apps enable goals plus the shared /goal command by default; other apps choose explicitly. dsh-jsonrpc-agent boots external cordis.yml, including the Python SDK default (Python SDK). Deployments stay thin with swappable backends/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 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
Add durable session state add a SessionEventMap member and render/replay from the log
Manage a same-session objective call ctx.goals; drive continuation through Agent and agent/* seams
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