Docs: per-folder README.md for packages/ (family overview + one per package: service, events, API, extension points, TODOs), examples/, and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md symlinks) for packages/ and vendor/; module-level doc comments in every packages/*/src file; richer JSDoc on all exported API (event side effects, disposal contracts, error behavior). Root AGENTS.md gains a "Type Safety and Documentation" policy section: the codebase aims to be very type-safe and well documented; type gymnastics are acceptable in core packages when they improve plugin-author DX; verbose docs are fine as long as they stay strictly in sync with the code. Type safety: removed the upstream-inherited "noImplicitAny": false from tsconfig.base.json — packages/* now compile under full strict mode; vendor/loader and vendor/include set it locally (vendor/cordis already did). Eliminated every `: any` / `as any` from packages and examples (catch clauses use unknown + a CodedError narrowing type; event data access uses discriminated-union narrowing). Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL — SchemaSpec with per-property `required: true` booleans, type-level InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and defineTool() so first-party tools get typed execute(args) with zero casts (raw JSON Schema still accepted for MCP interop; chosen over schemastery because it targets JSON Schema generation directly). echo-tool and all test tools migrated; +7 tests.
2.7 KiB
2.7 KiB
Packages
Harness packages, all under the @deepseek-ai/dsh-* scope. Each package is a
Cordis service (microkernel plugin-style): it exports a default Service class
that gets registered via ctx.plugin(), declares its ctx key and events through
declaration merging, and exposes extension points through ctx.effect(),
ctx.on(), and ctx.waterfall().
Dependency graph
dsh-llm (no harness deps — pure vocabulary)
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-agent-loop ← dsh-llm, dsh-session, dsh-system-prompt, dsh-tools, dsh-agent
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.
What goes where
| Package | Role | ctx key |
|---|---|---|
llm/ |
Abstract LLM service + content-block vocabulary + chunk assembler | ctx.llm |
session/ |
Event-sourced session log + in-memory store | ctx.sessions |
system-prompt/ |
Prompt-section + tool-schema assembly registry | ctx.systemPrompt |
tools/ |
Tool registry + tools/execute waterfall |
ctx.tools |
agent/ |
Agent interface, registry, agent/* event vocabulary |
ctx.agents |
agent-loop/ |
THE concrete plugin: LoopAgent + the loop driver |
ctx.agentLoop |
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. Everyregister()returns the disposer. - Declaration merging for events and ctx: services declare their events in
declare module 'cordis' { interface Events { ... } }and their ctx key ininterface Context. - Waterfall semantics:
ctx.waterfalllisteners receive(...args, next)and MUST callnext()to delegate; returning without it short-circuits (the veto mechanism). - Extensible unions:
ContentBlockMap,MessageSourceMap,FinishReasonMap,TurnTriggerMap,TurnEndReasonMap, andSessionEventMapuse the merge-extensible-map pattern so plugins can add variants via declaration merging. - ESM everywhere; imports use package names across package boundaries,
.tsextensions within a package. - Tests: vitest, colocated under
packages/<name>/tests/*.spec.ts. Every registry needs an HMR-safety test. Err on the side of more tests.