Lesson 1: Boot & Configuration: One Line of Config Changes the Entire Agent
In one sentence: In DSH, "configuration is composition" — a single
cordis.ymldecides which plugins the entire agent loads, which model it uses, and which tools it has; changing one line of config can swap the model, add tools, or change the capability set, without touching a single line of code.
1. User Story: Why Is "Editing the Config" Enough?
Yesterday, Little D ran a task with dsh --profile headless "help me summarize this repo", and the agent used the deepseek-v4-flash model. Today he wants to do three things:
- Swap the agent to a different model;
- Add a "search the web" tool to the agent;
- Build a "read-only audit" agent — let it only read files, not run commands.
In a traditional framework, the answer to all three is almost the same: modify the source code. The model name is hardcoded in the framework, tools must be registered into the main loop, and capability combinations are maintained by keeping different fork branches. When upgrading the framework, you also have to painfully merge the code you changed.
In DSH, the answer is completely different: models, tools, policies, and even the agent's main loop itself are all plugins; and "which plugins to load, with what parameters" is entirely decided by the configuration file. Hence:
| What you want to do | Traditional framework | DSH |
|---|---|---|
| Swap the model | Fork the source, hardcode a new model name | Change the config of the model-related plugin entry in cordis.yml |
| Add a tool | Modify the framework source, register the tool into the main loop | Add one plugin entry to the config |
| Change the capability set | Maintain multiple forks, each with its own code changes | Use profile layers: same foundation, many combinations |
So "configuration" in DSH is not "passing a few parameters to the program" — it is composition — deciding which building blocks get assembled into this agent. That is exactly what this lesson's title means: one line of config, changing the entire agent.
2. Configuration Is Composition: One cordis.yml Decides the Whole Agent
DSH uses cordis.yml to describe which plugins the agent loads and what parameters each plugin carries. The official docs state it plainly: "the configuration file is responsible for composing capabilities" (source: docs/user/develop/basic/config.zh.md).
A minimal config is just a set of plugin entries (source: docs/user/develop/basic/config.zh.md):
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: agent-loop
name: '@deepseek-ai/dsh-agent-loop'
config:
agents:
- id: main
provider: deepseek-official
model: deepseek-v4-flash
Understand this file and you understand half of DSH:
name: specifies which npm package (or local module relative tocordis.yml) to load;id: gives this plugin instance a stable identifier, so other config layers can find this entry by id and patch it;config: parameters passed to the plugin itself — for example,agent-loop'sconfig.agentsdeclares "start an agent namedmain, using thedeepseek-officialprovider and thedeepseek-v4-flashmodel".
So: swapping the model = changing the value of the model: line (or switching to another provider plugin); adding a tool = adding a tool plugin entry; dropping a capability = removing the entry, or marking it disabled: true to temporarily skip it.
The real world is much richer than this minimal example: DSH packages the capabilities "shared by every profile" into the dsh-base bundle, whose cordis.patch.yml is a long plugin manifest — model adapters, session persistence, sandbox, file tools, subagents, workflows, telemetry... (source: packages/bundle/base/cordis.patch.yml). For instance, the default model routing defined there:
- id: agent-default-model
name: '@deepseek-ai/dsh-agent-default-model'
config:
provider: deepseek-official
model: deepseek-v4-flash
🎁 Analogy:
cordis.ymlis like the agent's "assembly sheet" — on the same production line, swap the assembly sheet and you get a machine with different capabilities. Models, tools, and the loop are all parts listed on the assembly sheet.
3. Profile Layering: Official Default Layer → Profile Plugin Layer → User Override Layer
"Configuration is composition" also has a key mechanism: layering. You don't have to write a complete config from scratch; instead, you override someone else's config to produce your own version.
The CLI docs define the dsh command as "a product launcher for profiles (config profiles): an ordered stack of plugin bundle patch layers, with the user's own override layer underneath" (source: apps/cli/README.md). dsh web and dsh --profile headless are really just different profile combinations:
dsh --profile headless "task"starts theheadlessprofile:dsh-baseanddsh-headlesscombine on top of an empty root, then the runner directly drives core's Agent and Session services (source:docs/user/guide/index.zh.md);dsh webis an alias for--profile web:dsh-basecombines withdsh-web-app, adding a browser host plus HTTP and client plugins.
The final form of a complete config results from stacking multiple layers, with later layers overriding earlier ones — for the same line, the later layer wins. The layering order, verbatim (source: apps/cli/README.md, line 20):
The composition tree stacks on top of an empty root: first apply each bundle's patch layer in the order of the
dsh.profile.bundleslist, then the profile's owncordis.patch.yml, then the home-level$DSH_HOME/cordis.patch.yml, then each--patch <path>overlay, and finally the CLI flag patches.
配置即组合:一个 cordis.yml / profile 决定整个智能体的能力组合
This maps to a three-layer intuition:
| Layer | Where | Whose it is |
|---|---|---|
| Official default layer | Patches built into bundles (e.g. @deepseek-ai/dsh-base) | Provided by the platform |
| Profile plugin layer | Plugins and cordis.patch.yml in the profile directory (managed with dsh plugin --profile <name> ...) | Your "assembly plan" |
| User override layer | $DSH_HOME/profiles/<name>/cordis.patch.yml, etc. | Your personal preferences |
⚠️ A common pitfall: patches replace the whole
configline, they are not a deep merge of individual keys (source:docs/user/develop/basic/config.zh.md). If you only writeconfig: { thinking: disabled }to patch thellm-deepseekline, you will wipe out the line's originalapiKeyEnvandbaseURL— when overriding, rewrite every key you need to keep.💡 Want to confirm the layering result? Use
--dump-default-configand--dump-configto directly view the combined full config tree without actually starting up (source:apps/cli/README.md).
4. Boot Flow: Boot Assembly → Scoped ctx Ready → Publish the Agent
The config is written — how does dsh turn it into a "living agent"? Roughly four steps:
① Boot assembly. app-boot is the startup glue shared by all app bins: it loads .env, includes a Loader protection mechanism that fails with clear errors, snapshot-aware config parsing, and a startup sequence that waits for the whole tree to settle (source: packages/boot/README.zh.md). It resolves the layers above into one final config and loads all plugin packages.
② Plugins activate on demand. Cordis drives activation by "service availability": plugins declare which services they need via inject, and only start when their dependencies are all present — so the order of entries in the config file does not determine the load order (source: docs/user/develop/basic/config.zh.md).
③ Scoped ctx ready. The dsh-scope package provides scoped registration primitives: createScope(ctx, key) creates a labeled Cordis context, and every registration made through it has both scope visibility and scope lifecycle (source: packages/core/scope/README.zh.md). The agent loop creates a scope for every live agent — each agent gets its own independent ctx, and the tools and services each registers never interfere with one another. This continues the "context types" theme from chapter 2: making context itself a first-class, runtime-manipulable entity (intuition reference: cordis.txt, lines 300-304).
④ Publish the agent. Only once the scope is ready does the agent officially get "published": dsh --profile headless directly drives core's Agent and Session services to finish one task and then exits; dsh web instead waits for the browser layer to connect before creating a session.
This brings us to the most important sentence of the lesson: all capabilities are plugins, and plugins attach to ctx via registration — loading takes effect immediately, unloading restores the original state. This is chapter 2's "spatio-temporal composability" in action: installed plugins register tools, services, and policies into the context; on unload, Cordis revokes those registrations according to the scope lifecycle, and the system returns to its pre-assembly state. So "changing one line of config" is not a crude replacement — it is a clean re-assembly.
Key Points Recap
- Configuration is composition: the plugin entries (
name+id+config) incordis.ymldecide the entire agent's capability set; swapping models, adding tools, and changing policies are all config edits, not code changes. - All capabilities are plugins: model adapters, tools, the sandbox, even the agent loop itself are plugins; registering into ctx takes effect immediately, unloading restores the original state (echoing chapter 2).
- Profile layering: official default layer → profile plugin layer → user override layer, later layers overriding earlier ones; patches replace the whole
configline, they are not a deep merge. - Four boot steps: boot assembly (resolving layers) → plugins activate by service availability → the agent loop creates a scoped ctx for every live agent → publish the agent.
🚀 In the next lesson we step into the world "after assembly": Lesson 2, "Agent Loop & Sessions" — how the agent loops, and how sessions get persisted.
Self-check · Boot & Configuration
Answer each question, then submit to check your result.
