Files
deepseek-harness/packages
Tianyi Cui 7792347c4f simplify(seams): prune dead methods from the persistence and bash seams
Two capability seams carried abstract methods no production consumer calls.
A method no consumer programs against is not a seam — it is speculative
surface every implementation must still provide and test.

- SessionPersistence: remove has() and delete(), the coordinator's
  has/delete/deleteCore, and the PersistenceBackend.deleteStored hook (with its
  jsonl + sqlite + in-spec memory-stub impls). Surviving service surface:
  create/append/load/list. Production uses only load() (resume) and list()
  (ACP session/list).
- BashExecutor: remove get(id) and list(), the abstract decls and the
  LocalBashExecutor impls. The internal tasks map survives (it backs
  ownerOf/readOutput/kill); get/list were pure public accessors over it with no
  shipping caller and no bash_list tool.
- Migrate tests that reached through ctx.bash.get(id) to the public completion
  seam: a doneFor(id) helper over onTaskDone awaits a task by id, and the
  HMR-reload ownership test now proves task survival through A's own bash_output
  ([status: running]) plus ownerOf + B-rejection — a stronger through-the-tool
  assertion than the removed lookup peek.
- Update seam READMEs (six -> four service methods, drop the deleteStored hook
  and the get/list row) and the two implemented persistence RFCs in place.

Implements docs/rfc/implemented/simplification/2026-06-20-prune-dead-seam-methods.md
2026-06-21 02:17:27 +08:00
..

Packages

Harness packages, all under the @deepseek-ai/dsh-* scope. Each package is a Cordis plugin (microkernel-style): it exports either a default Service subclass or a functional plugin that gets registered via ctx.plugin(), declares its ctx key/events where applicable through declaration merging, and exposes extension points through ctx.effect(), ctx.on(), and ctx.waterfall().

Hierarchy

Packages are grouped by modular role at packages/<group>/<pkg>/. The group directory is a pure container (no package.json of its own); the package name stays @deepseek-ai/dsh-<pkg> regardless of group. Each group has a README.md describing its role and whether it is product or support infrastructure.

Group Role Release expectation
core/ Product API spine: session, system-prompt, tools, agent, and the concrete loop Product — stable surface
llm/ LLM capability family: the abstract service + provider adapters Product — stable surface
bash/ Bash capability family: the executor seam, a local impl, and the model-facing tool Product — stable surface
session-persistence/ Persistence capability family: the seam + JSONL/SQLite backends Product — stable surface
ui/ Editor/client integration surfaces (the ACP bridge) Product — stable surface
support/ Dev/test/example infrastructure (invariants, stdio UI, replay adapter) Support — lower compatibility expectations

The split is the point: a package's group says whether it is part of the product API or support/test/example infrastructure, so release and removal decisions do not have to treat every package as an equal public contract. New packages join an existing group; adding a new top-level group is a deliberate act (extend the group READMEs and the hierarchy docs).

Dependency graph

dsh-llm          (no harness deps — pure vocabulary)
dsh-bash          (no harness deps — abstract executor seam)
dsh-session       ← dsh-llm
dsh-system-prompt ← dsh-llm
dsh-agent         ← dsh-llm, dsh-session
dsh-tools         ← dsh-llm, dsh-system-prompt, dsh-agent
dsh-bash-local    ← dsh-bash                       (BashExecutor impl)
dsh-tool-bash     ← dsh-bash, dsh-tools            (bash tool schemas)
dsh-llm-deepseek  ← dsh-llm                        (DeepSeek adapter)
dsh-llm-pi-ai     ← dsh-llm                        (pi-ai-backed adapter)
dsh-agent-loop    ← dsh-llm, dsh-session, dsh-system-prompt, dsh-tools, dsh-agent
dsh-invariants    ← dsh-llm, dsh-session, dsh-agent (dev-mode contract checks)
dsh-acp           ← dsh-agent, dsh-llm, dsh-session, dsh-session-persistence  (ACP JSON-RPC bridge)
dsh-ui-stdio      ← dsh-agent, dsh-llm, dsh-session (stdio readline UI plugin)
dsh-llm-replay    ← dsh-llm, dsh-session            (record/replay adapter for keyless snapshot tests)

The rule: plugins depend on interfaces, never on the concrete loop. dsh-agent-loop is swappable — UI/hook/tool plugins keep working against the dsh-agent vocabulary if the loop is replaced. A swappable capability splits into interface / implementation / consumer packages (the bash trio is the template — see capability seams).

What goes where

Package Group Role ctx key
llm/ llm Abstract LLM service + content-block vocabulary + chunk assembler ctx.llm
session/ core Event-sourced session log + in-memory store ctx.sessions
system-prompt/ core Prompt-section + tool-schema assembly registry ctx.systemPrompt
tools/ core Tool registry + tools/execute waterfall ctx.tools
agent/ core Agent interface, registry, agent/* event vocabulary ctx.agents
agent-loop/ core THE concrete loop plugin: ReactLoopAgent + the loop driver ctx.agentLoop
bash/ bash Abstract bash executor seam (interface + vocabulary) ctx.bash
bash-local/ bash Local-subprocess BashExecutor implementation (registers ctx.bash)
tool-bash/ bash Model-facing bash/bash_output/bash_kill tool schemas (registers on ctx.tools)
llm-deepseek/ llm DeepSeek API adapter (hand-rolled fetch/SSE) (registers on ctx.llm)
llm-pi-ai/ llm DeepSeek adapter via @earendil-works/pi-ai (design twin) (registers on ctx.llm)
session-persistence/ session-persistence Persistence seam + write coordinator ctx.sessionPersistence
session-persistence-jsonl/ session-persistence JSONL-sidecar persistence backend (registers ctx.sessionPersistence)
session-persistence-sqlite/ session-persistence SQLite persistence backend (registers ctx.sessionPersistence)
invariants/ support Dev-mode event-contract invariants + session-log freeze (listens on session/*, agent/*)
acp/ ui Agent Client Protocol bridge: serves the agent to an ACP editor over JSON-RPC stdio (drives ctx.agents/ctx.sessions)
ui-stdio/ support Minimal stdio (readline) UI plugin: renders agent/* events, feeds stdin lines to the agent (drives ctx.agents)
llm-replay/ support Record/replay adapter: short-circuits llm/stream with chunks from a recorded session JSONL (keyless snapshot tests) (listens on llm/stream)

Each package has its own README.md with purpose, service API, events, extension points, and deliberate non-goals (TODOs).

Conventions (applied across all harness packages)

  • Registrations are effects: every contribution (adapter, tool, section, agent, event listener) goes through ctx.effect() / ctx.on(), so disposal and HMR clean up automatically. Every register() returns the disposer.
  • Declaration merging for events and ctx: services declare their events in declare module 'cordis' { interface Events { ... } } and their ctx key in interface Context.
  • Waterfall semantics: ctx.waterfall listeners receive (...args, next) and MUST call next() to delegate; returning without it short-circuits (the veto mechanism).
  • Extensible unions: ContentBlockMap, MessageSourceMap, FinishReasonMap, TurnTriggerMap, TurnEndReasonMap, and SessionEventMap use the merge-extensible-map pattern so plugins can add variants via declaration merging.
  • ESM everywhere; imports use package names across package boundaries, .ts extensions within a package.
  • Tests: vitest, colocated under packages/<group>/<pkg>/tests/*.spec.ts. Every registry needs an HMR-safety test. Err on the side of more tests.