English | 中文
DeepSeek Harness SDK uses Cordis: everything is a plugin, including the loop.
Harnesses are Cordis contexts with package-contributed services, typed events, and disposable registrations.
packages/core/ groups the default agent flow; capabilities remain plugins.
| 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 |
| 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.subprocess |
subprocess/ |
managed child-process trees for the bash executors, the LSP host, and the ACP subagent backend |
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/ |
Live-preferred exact/filter/trace interface, SQLite FTS backend, and workspace-authorized model tools |
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 |
Events form the service extension API; see the catalog and producer/consumer map.
session/event.Agent for status, prompt admission, request shaping, validation, and continuation.Waterfall events behave like around-middleware: a listener delegates by calling next(); returning without it vetoes or takes over. Full rule: Cordis waterfall semantics.
The loop runs through plugin services and events.
A session is append-only. Each ordinary turn claims one queued message; injection claims none. Successors await the preceding checkpoint but may share its running interval (decision). A step is one model request plus tools; quotes in the sequence below mark durable events.
Creation without an id 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.
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
Steps assemble ordered prompt sections, tool schemas, and variables; unknown references fail turns. dsh-system-prompt owns identity and persona; the loop supplies model and cwd (ownership).
Async inject() and post-tool additionalContexts settle after results; steering drains before agent/post-step. Leftovers queue. Terminal agent/turn-stop remains authoritative through close/flush and discards later steering, not queued prompts.
Pruning precedes summaries; overflow retries require durable progress. Bounded retries compose on agent/request-error; cancellation wins (compaction, retry).
Adapter failures close the step before agent/request-error with exact Error, LlmFailure, and history. Retries open steps; success clears history; exhaustion stores failure on turn/end. Failed chunks commit nothing.
Other failures use agent/error. Cancellation and disposal beat recovery; undispatched tools get synthetic tool/call/ABORTED_BEFORE_DISPATCH pairs. The signal retires before turn/end. Effective cancel() emits its cause, clears queues, and aborts; 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 use agent/error. Each turn has one TurnEndReason.
ctx.agents returns AgentHandle { agent, dispose() }. Plugins use intent helpers followup(), queue(), steer(), and inject(); callers with exact routing facts use mandatory-field send() (decision). cancel() and whenIdle() control lifecycle. Caller, provider, and handle co-own teardown.
Each agent owns a scoped agent.ctx over global tool, prompt, and command storage (decision); scoped listeners filter and contributions unwind with awaited cleanup. CreateAgentOptions.setup(agentCtx) composes before publication; typed resolvers derive carrier checks from Events and scopeTarget (gates). AgentLoop runs inside ctx.agents.withInitiator(); private orchestration derives agent.session, while turn, step, signal, cwd, and authority stay explicit (decision). See agent scope and subagent composition.
The session log is authoritative. deriveMessages() projects model history; raw assistant/chunk events preserve replay and UI fidelity. Fork, resume, transcripts, telemetry, and persistence share that stream.
Model-visible ⟺ logged: step/start messages plus the header's session prefix and folded request/header reconstruct every request; dsh-agent-loop/invariant asserts this through ctx.invariants (decision).
Durability is a plugin concern; backends buffer synchronous session/event notifications. Checkpoints drain before adapter dispatch, recorded top-level tool calls before tool dispatch, complete response/result batches at agent/post-step, and final turn ends. SessionPersistence stores SessionEvent plus SessionHeader metadata; 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).
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).
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.
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).
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 (the local one spawns through ctx.subprocess) |
| 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.