Master advanced from 5143fac7f to be363166e with the Cordis and loader vendor update after the previous PR merge was validated. GitHub therefore tested a new synthetic merge where packages/util/home still selected Cordis rc.6 with loader rc.4 while the updated workspace graph requires Cordis rc.7 with loader rc.5. That stale importer made pnpm-lock.yaml semantically incomplete even though Git merged it without a textual conflict, so every Node, sandbox, and real-API job failed during immutable install before running tests. Merge the exact current master tip and regenerate the lockfile so the home package resolves the same peer graph as the updated workspace. Verified the repaired merge with pnpm install --frozen-lockfile; the full pre-push gate runs on the committed merge before it is published.
@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.
Read this package for the whole plugin tree and its composition order.
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 runtime 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
llmservice; the leaf registers a concrete adapter onctx.llm(llm-deepseek,llm-pi-ai,llm-replay). - the bash executor — the bundle ships
tool-bash(the consumer schema); the leaf providesctx.bash(bash-localor a sandboxed impl). - non-local skill providers — the bundle ships the skill registry, the local filesystem provider, and the
skilltool; 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.timeris 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?, dshHome?, 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; and skills.registry, skills.local, and skills.tool to the skill registry, local provider, and model-facing consumer. It resolves dshHome once through @deepseek-ai/dsh-home and forwards that absolute value to tool-bash's managed environment and local skill discovery. An absent top-level dshHome adopts skills.local.dshHome; supplying both with different resolved paths fails loudly.
Why a code bundle, not a shared YAML include
A YAML include can deduplicate config but cannot own a bin or provide front-door defaults. App packages make stdout-safe ACP wiring the default, though a leaf can still add an unsafe logger. Bundle children register services in the root isolate-keyed store, so injected leaf siblings see them without load-order coupling.
Model Experience
Indirectly, through dsh-system-prompt, dsh-tool-skill, dsh-tool-bash, and dsh-tools, which this bundle mounts without adding model-bound wrapper content.
Known Limitations and Deferred Work
- The spine set is fixed in code —
apply()mounts every child unconditionally (includingtool-bash); no config excludes or replaces one, so swapping the loop or dropping a spine member means composing a different bundle. dsh-invariantsmounts unconditionally — this bundle has no toggle, so every composition using it pays the dev-mode relational assertions; Session's always-on validation and freezing are separate.