Files
deepseek-harness/packages/core/agent-core/README.md
Tianyi Cui ecb8aa5b8e Add a gated Known Limitations and Deferred Work section to every package README
Every packages/*/* README now carries a canonical '## Known Limitations and
Deferred Work' section: condensed, evidence-backed bullets for consumer-visible
gaps (unimplemented features, platform caveats, MVP cuts) and consciously
postponed work (TODO/FIXME/XXX markers, RFC deferrals still open). The ten
pre-existing ad-hoc variants ('What is NOT here (TODO)', 'Deferred',
'Limitations (MVP)', 'Known limitations (tracked TODOs)', ...) are normalized
into the canonical heading.

A new doc-sync gate, scripts/verify-readme-limitations.ts, enforces the shape:
exactly one limitations-like heading per package README, byte-equal to the
canonical h2, with at least one bullet; near-miss headings fail so variants
cannot creep back. Packages with genuinely nothing to declare (dsh-brand,
dsh-timeout, dsh-subagent-mock, dsh-app-boot) are whitelisted in the script and
must NOT carry the section; whitelist entries are validated against the scanned
package set so a rename fails loud.

Wired into the doc-sync chain (package.json) and the run-gates doc-sync leaf
set; the standing rule lands in packages/AGENTS.md and the adding-a-package
cookbook; decision record in
docs/rfc/implemented/process/2026-07-10-readme-known-limitations-gate.md
(RFC index regenerated).

Also fixes two stale '(deferred)' markers claiming dsh-compact-basic is
unimplemented (the dsh-compact seam README's package table and the seam's
module doc comment).
2026-07-12 01:46:34 +08:00

5.2 KiB

@deepseek-ai/dsh-agent-core

The default executor-less, UI-less agent spine as ONE Cordis bundle plugin. It loads the fixed set of services every harness agent needs, including the local skill provider, and forwards the loop's agents list as its own config — so an app package composes a working agent by adding only a front door and the swappable backends.

This is the package to read to see the whole plugin tree at once — the teaching role the inlined echo-agent cordis.yml used to play before the spine moved behind this bundle.

The tree it loads

apply(ctx, config) mounts each of these as a child of the bundle fiber:

@cordisjs/plugin-timer            timer service (writes nothing to stdout)
@deepseek-ai/dsh-llm              abstract LLM service + content-block vocabulary
@deepseek-ai/dsh-session          event-sourced session log + store
@deepseek-ai/dsh-system-prompt    prompt-section + tool-schema assembly
@deepseek-ai/dsh-tools            registry + guarded pre/around/post/final-result pipeline
@deepseek-ai/dsh-skill            skill provider registry
@deepseek-ai/dsh-skill-local      local filesystem skill provider
@deepseek-ai/dsh-agent            agent registry + agent/* event vocabulary
@deepseek-ai/dsh-invariants       dev-mode event-contract assertions
@deepseek-ai/dsh-tool-bash        the model-facing bash/bash_output/bash_kill schemas
@deepseek-ai/dsh-tool-skill       session-prefix skill catalog + model-facing loader schema
@deepseek-ai/dsh-agent-loop       THE concrete loop (gets the forwarded `agents`)
                                  (dsh-system-prompt gets the forwarded `persona`)

What it deliberately leaves OUTSIDE the bundle

The spine is everything COMMON to every front door. The swappable and front-door-coupled pieces stay out, picked by whatever loads the bundle:

  • the LLM adapter — the bundle ships the abstract llm service; the leaf registers a concrete adapter on ctx.llm (llm-deepseek, llm-pi-ai, llm-replay).
  • the bash executor — the bundle ships tool-bash (the consumer schema); the leaf provides ctx.bash (bash-local or a sandboxed impl).
  • non-local skill providers — the bundle ships the skill registry, the local filesystem provider, and the skill tool; deployments can add other providers such as embedded or remote catalogs as siblings.
  • presentation + per-app infra — the stdio UI / ACP bridge, a console logger, hmr. These form the coupled "front-door cluster" that the app packages (dsh-stdio-agent, dsh-acp-agent) bake in. timer is in the spine (common to both, stdout-silent); a console logger is NOT (it writes to stdout, which the ACP bridge reserves for JSON-RPC).

This is the interface/implementation/consumer seam raised to the composition level: the bundle owns the shared spine, the leaf owns the backends, the app package owns the front door.

Config

import type { Config } from '@deepseek-ai/dsh-agent-core'
// { agents?, persona?, toolOrder?, tools?, skills? } — the schema intersects the owner schemas,
// so validation and defaulting can never drift from the owners.

The bundle FORWARDS each field to the child that owns it: agents to agent-loop (default []), so each app supplies its own pre-created agents — a stdio app pre-creates a main; the ACP app pre-creates none (it creates agents on demand at session/new) — persona and toolOrder to dsh-system-prompt; tools to the tool registry for its presentation mode; and skills.registry, skills.local, and skills.tool to the skill registry, local provider, and model-facing consumer. Forwarding is exactly why the owners can live in the shared spine even though the apps disagree on what to configure.

Why a code bundle, not a shared YAML include

A YAML include can dedupe the config, but it cannot OWN a bin, and it can only describe the front-door coupling in a comment and trust each leaf to obey. Moving the spine into a package, and the front-door cluster into the app packages, means the default leaf for an ACP server has no logger entry to copy wrong — "the ACP app never logs to stdout" stops being a prose warning a leaf must remember and becomes the app package's default shape (a leaf can still add a sibling logger, so the rule stays documented — but it has nothing to get wrong by default). Services register in the root store keyed by their isolate symbol, so a child loaded here is visible to the bundle's siblings (the leaf's adapter and executor) exactly as a nested plugin-include subtree's services were — cordis gates every read on inject, never on load order.

Known Limitations and Deferred Work

  • The spine set is fixed in codeapply() mounts every child unconditionally (including tool-bash); no config excludes or replaces one, so swapping the loop or dropping a spine member means composing a different bundle.
  • dsh-invariants mounts unconditionally — a dev/test plugin with default freeze: true and no reachable toggle in this bundle's Config, so every deployment currently pays the assertion/freeze cost.