The dsh-tools half of the Code Mode RFC (its fourth, final change): the registry gains its first config — mode: native | code | both — and OWNS how its tools reach the model. 'code' contributes exactly one wire tool, run_code, plus a lazy tools:sdk prompt section declaring every other tool as a generated TypeScript API (jsonSchemaToTs: total over the defineTool subset, unknown degradation, lexicographic byte-identical rendering); 'both' ships both representations; 'native' is byte-for-byte the old behavior. Non-native modes fail every assembly loudly without a typescript-language ctx.codeRuntime. run_code's dispatch bridge: JSON-normalizes each binding argument before dispatch (what dispatches is what the tool/code-dispatch event logs — the append can never fail on payload shape; BigInt/circulars reject that one call), serializes all program tool calls through a per-run queue (even Promise.all — no concurrency-safety metadata yet), routes every sub-call through tools/pre-execute → tools/post-execute (a deny rejects the program-side promise), drops sub-call additionalContext (no safe outlet mid-run; pinned), owns a run-scoped abort that follows the outer signal in and fires on settlement (in-flight sub-dispatch aborted, queued abandoned, queue drained before returning), and converts a failed run into CodeRunFailedError → a structured isError carrying kind + captured logs. tool/code-dispatch joins SessionEventMap by declaration merging (log-only; deriveMessages ignores it). The composed surface: the tools config forwards through agent-core and both app packages; examples/code-agent + demo:code run the worker runtime under mode code (keyless boot smoke + a with-key e2e proving the collapsed [run_code] header, the dispatch events, and the file the program wrote); two new snapshot scenarios (code-mode-turn, both-mode-turn) record the SDK section, collapsed header, dispatch events, and result card — each its own header-pinning class (the harness gains per-scenario config overlays and per-class pins). Catalogs, graphs, cookbook, hooks-bridge notes, and the RFC (moved to implemented/, restructured to decision-era headings) updated in the same change.
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, declares its ctx key/events through declaration merging, and exposes extension points through ctx.effect(), ctx.on(), and ctx.waterfall(). Authoring conventions: AGENTS.md (subtree) and the root AGENTS.md § Conventions.
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 README is the canonical per-package map — package roles, ctx keys, and the product-vs-support split live there, next to the code.
| 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 |
code-runtime/ |
Code-execution capability family: the abstract runtime seam for model-written programs + a worker-thread backend | Product — stable surface |
fs/ |
Filesystem capability family: the abstract seam, a local impl, and the model-facing file tools | Product — stable surface |
compact/ |
Compaction capability family: the abstract seam + a basic backend (tool deferred) | Product — stable surface |
subagent/ |
Subagent capability family: the provider-registry seam and the model-facing delegation tool | Product — stable surface |
web/ |
Web capability family: the abstract seam, search/fetch provider impls, and the model-facing web tools | Product — stable surface |
todo/ |
Todo/planning family: the model-facing todo_write tool (whole-list task tracking on the session log) |
Product — stable surface |
hooks/ |
Hook bridges + the shared Claude Code / Codex wire-protocol library | Product — stable surface |
session-persistence/ |
Persistence capability family: the seam + JSONL/SQLite backends | Product — stable surface |
ui/ |
Editor/client integration surfaces (the ACP bridge) + the app packages | Product — stable surface |
support/ |
Dev/test/example infrastructure (invariants, replay adapter, subagent mock) | Support — lower compatibility expectations |
util/ |
Low-level zero-dependency utilities shared across groups (the Branded<B> primitive) |
Support — small, stable, harness-dep-free |
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 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 this table).
Dependencies
The inter-package dependency graph is generated: docs/module-graph.md (pnpm run gen-module-graph, freshness-gated in CI).
The rule it must obey: extension 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. The sanctioned exception is a composition/bundle package like dsh-agent-core, whose whole job is to assemble the concrete spine: it depends on dsh-agent-loop (and the other concrete spine plugins) on purpose. The rule constrains plugins that EXTEND the system, not the bundle that COMPOSES it — swapping the loop means shipping a different bundle, not rewiring every extension. A swappable capability splits into interface / implementation / consumer packages (the bash trio is the template — see capability seams).
Each package has its own README.md with purpose, service API, events, extension points, and deliberate non-goals (TODOs).