dsh-paoding 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -22
- package/README_en.md +26 -22
- package/bin/dsh-paoding.mjs +73 -11
- package/docs/architecture.md +36 -31
- package/docs/architecture_en.md +54 -38
- package/docs/configuration.md +151 -57
- package/docs/configuration_en.md +155 -60
- package/docs/installation.md +96 -234
- package/docs/installation_en.md +111 -252
- package/docs/orchestration.md +24 -24
- package/docs/orchestration_en.md +24 -24
- package/package.json +8 -5
- package/plugins/paoding-config-ui/api-core.mjs +215 -18
- package/plugins/paoding-config-ui/index.mjs +413 -154
- package/plugins/paoding-config-ui/lib/base.css +1083 -42
- package/plugins/paoding-config-ui/lib/client.js +2266 -252
- package/plugins/paoding-config-ui/package.json +2 -2
- package/plugins/paoding-config-ui/runtime-inventory.mjs +27 -26
- package/presets/orchestrator/agent.cordis.yml +19 -9
- package/presets/orchestrator/preset.yml +3 -2
- package/presets/orchestrator/restrict.mjs +2 -1
- package/tools/install.mjs +9 -4
- package/tools/lib/alloc.mjs +60 -29
- package/tools/lib/cli.mjs +127 -279
- package/tools/lib/compose.mjs +261 -95
- package/tools/lib/config.mjs +266 -86
- package/tools/lib/host.mjs +55 -24
- package/tools/lib/skills.mjs +72 -45
- package/tools/lib/state.mjs +172 -40
- package/tools/lib/upgrade.mjs +170 -0
- package/tools/lib/util.mjs +7 -0
- package/tools/lib/version.mjs +233 -0
- package/tools/lib/workspaces.mjs +74 -0
- package/tools/lib/yaml.mjs +92 -15
- package/install.sh +0 -31
- package/tools/lib/wizard.mjs +0 -516
package/docs/configuration_en.md
CHANGED
|
@@ -4,27 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
# Configuration
|
|
6
6
|
|
|
7
|
-
dsh-paoding makes the role-based orchestration preset **configuration-driven**: you hand-write (or
|
|
7
|
+
dsh-paoding makes the role-based orchestration preset **configuration-driven**: you hand-write (or click together in the 庖丁配置 panel) a YAML config file, and the generator (`tools/`; the panel's Save & Apply and the in-repo fallback CLI share the same generation pipeline, `collectState` / `generateAndInstall`) rewrites the target preset — `~/.dsh/.agent-presets/orchestrator/agent.cordis.yml` — when it is applied, covering the role allows, role personas, the main-agent allow list (`config.allow` injection) and the main-agent persona's skill rows / inlines and persona extra.
|
|
8
8
|
|
|
9
|
-
The config file describes **declarative intent**. The target file is regenerated from scratch every time — "source preset + this config file": edit the config →
|
|
9
|
+
The config file describes **declarative intent**. The target file is regenerated from scratch every time — "source preset + this config file": edit the config → hit Save & Apply in the panel = idempotent sync, with no accumulating hand patches.
|
|
10
10
|
|
|
11
11
|
## 1. Configuration file
|
|
12
12
|
|
|
13
13
|
### 1.1 Location and precedence
|
|
14
14
|
|
|
15
|
-
The config file's default location is `$DSH_HOME/dsh-paoding.config.yml`; when `$DSH_HOME` is unset, it defaults to `~/.dsh`. The CLI
|
|
15
|
+
The config file's default location is `$DSH_HOME/dsh-paoding.config.yml`; when `$DSH_HOME` is unset, it defaults to `~/.dsh`. The `DSH_PAODING_CONFIG` environment variable moves the path for the panel; the in-repo fallback CLI's `--config <file>` flag selects any other path.
|
|
16
|
+
|
|
17
|
+
How an edit gets applied ("re-apply" below always means the first row):
|
|
16
18
|
|
|
17
19
|
| Scenario | Behavior |
|
|
18
20
|
|---|---|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| `./install.sh --auto --config <file>` | Uses the given file instead of the default location |
|
|
23
|
-
| Web config UI (Settings → 庖丁配置) | The panel's「保存并应用」(save & apply) writes the preset and saves the assignments to the config file — equivalent to `--auto` plus the wizard's save |
|
|
21
|
+
| Web panel (庖丁配置 in the left sidebar) | **The main path**: opening the panel loads the existing config as the current values;「保存并应用」(Save & Apply) writes the preset and saves the assignments to the config file (default `~/.dsh/dsh-paoding.config.yml`, written with `0o600` permissions). If you hand-edited the file, open the panel first and then apply |
|
|
22
|
+
| First-install automation | At startup the plugin applies the config file automatically once it finds the preset missing or carrying a stale version marker (no config → base template) — no manual trigger needed in day-to-day use |
|
|
23
|
+
| Fallback CLI (developers who cloned the repo only) | `node tools/install.mjs --auto`: applies the config file idempotently when present, else writes the base template; `--dry-run` prints only the detection report and the allow / persona changes that would be generated — writes nothing (including the config file), good for previewing first; `--config <file>` uses the given file instead of the default location |
|
|
24
24
|
|
|
25
|
-
A config file that **exists but fails to parse** is a hard error (the
|
|
25
|
+
A config file that **exists but fails to parse** is a hard error (the pipeline aborts); a **missing** file simply means "no configuration" (the base template). Top-level keys such as `profile` and `roles` are described in the next subsection.
|
|
26
26
|
|
|
27
|
-
CLI precedence: an explicit `--profile` overrides the `profile` key in the config file; otherwise the config file's value wins; the fallback is `web`.
|
|
27
|
+
Fallback-CLI precedence: an explicit `--profile` overrides the `profile` key in the config file; otherwise the config file's value wins; the fallback is `web`.
|
|
28
28
|
|
|
29
29
|
### 1.2 Top-level keys
|
|
30
30
|
|
|
@@ -33,15 +33,16 @@ The key names below match `normalizeConfig` in `tools/install.mjs` — the singl
|
|
|
33
33
|
| Top-level key | Type | Semantics |
|
|
34
34
|
|---|---|---|
|
|
35
35
|
| `profile` | string | Which profile's patch layer (`$DSH_HOME/profiles/<profile>/cordis.patch.yml`) is scanned during detection; default `web` |
|
|
36
|
-
| `roles` | map | `toolName` → `{ persona?, name?, tools[], model?, provider? }`. For the three built-in roles (`search_external` / `design` / `implement`): overrides the persona and the allow intent, and may carry a `name` display name (rewrites only the subject of that role's default persona identity sentence — see 2.4)
|
|
36
|
+
| `roles` | map | `toolName` → `{ persona?, name?, tools[], model?, provider?, background_mode? }`. For the three built-in roles (`search_external` / `design` / `implement`): overrides the persona and the allow intent, and may carry a `name` display name (rewrites only the subject of that role's default persona identity sentence — see 2.4), a dedicated model (`model` / `provider`, see 2.5) and a session mode (`background_mode`, see 2.6). Any other key is a **custom role**: the installer emits a fresh `delegation-<toolName>` block and injects the toolName into the main agent's `config.allow` (sections 2 and 6) |
|
|
37
37
|
| `roles_remove` | string[] | toolNames of built-in roles deleted wholesale (only `search_external` / `design` / `implement` are accepted; other names are ignored with a warning; default `[]`). Deleting means the delegation block / tool surface / role persona / main-agent delegation guidance are no longer generated (see 2.3) |
|
|
38
38
|
| `skills` | map | `skill` → the list of role toolNames the skill is assigned to; the installer appends an `Available skills: …` guidance sentence to those roles' personas (section 7) |
|
|
39
|
-
| `main_agent_extra` | string[] | Tools appended to the main-agent allow list (host tools such as `mcp__codegraph__codegraph_explore`, `memory_search`, `mnemon_*`);
|
|
39
|
+
| `main_agent_extra` | string[] | Tools appended to the main-agent allow list (host tools such as `mcp__codegraph__codegraph_explore`, `memory_search`, `mnemon_*`); names are checked against the preset's own tool face ∪ the detection inventory — a name in neither is not injected |
|
|
40
40
|
| `main_agent_remove` | string[] | Base tools stripped from the main-agent allow list (e.g. the goal family, `exit_plan_mode`); names absent from the current allow are warned about and skipped; removing everything is a hard error |
|
|
41
41
|
| `main_agent_skills` | string[] | Main-agent skills in soft-guidance mode (the default channel): one compact skill row + a `Read full rules:` path per skill, appended to the persona |
|
|
42
42
|
| `main_agent_skills_inline` | string[] | Main-agent skills in hard-inline mode (optional): the selected SKILL.md bodies are inlined into the persona and apply to every response |
|
|
43
43
|
| `main_agent_persona_extra` | string \| null | Text appended to the tail of the main-agent persona. `null` / absent = use the default constant; `''` = append nothing; any other string = override the default |
|
|
44
|
-
| `
|
|
44
|
+
| `main_agent_display_name` | string \| null | The main agent's display name — changes **only the deployed preset display name** (the name shown in the GUI preset picker), never the persona identity line. Constraints: trimmed, 1–60 chars, no newlines; empty / absent = the default「编排模式 (Orchestrator)」(workspace presets use the derived name; see 5.1) |
|
|
45
|
+
| `workspaces` | map | Absolute workspace (project) directory → that workspace's own config entry, same fields as the top level (except `profile`); each entry generates its own `orchestrator-<slug>` preset, independent of the global one (section 9) |
|
|
45
46
|
|
|
46
47
|
`roles` details:
|
|
47
48
|
|
|
@@ -50,6 +51,7 @@ The key names below match `normalizeConfig` in `tools/install.mjs` — the singl
|
|
|
50
51
|
- `roles.<toolName>.name`: an optional `string | null` display name for the built-in trio only (absent / `null` = the role's built-in default identity). It rewrites only the **subject of that role's default persona identity sentence** — design's default `You are the design agent.` with `name: UI 设计` becomes `You are the UI 设计 agent.`, with the rest of the sentence untouched; constraints, boundaries and an example are in 2.4.
|
|
51
52
|
- `roles.<toolName>.model`: a dedicated model id for the role (optional `string`). When set, every sub-agent the role spawns runs on that model instead of following the main agent's current session model; absent = nothing is injected and the child inherits the main agent's model (see 2.5).
|
|
52
53
|
- `roles.<toolName>.provider`: the provider route the role's model lives on (optional `string`). It only takes effect paired with `model`; a lone `provider` is warned about and ignored by the installer, while a lone `model` rides the main agent's provider route (see 2.5).
|
|
54
|
+
- `roles.<toolName>.background_mode`: the role's session mode (optional `'one-shot' | 'continuable'`; default `one-shot`) — run-and-discard, or continuable: the child conversation is kept across turns so the main agent can resume in place with `send_message`. Built-in and custom role entries share the semantics; the continuable prerequisites, costs and panel path are in 2.6.
|
|
53
55
|
- Keys under `roles` outside the built-in trio are **custom roles** (section 6). Do not put the static role `search_internal_deep` under `roles` (see 2.2).
|
|
54
56
|
- Built-in roles can be **deleted wholesale**: the `roles_remove` key accepts only the trio's names — deletion effect, consistency with the `roles` key, and the restore paths are in 2.3.
|
|
55
57
|
|
|
@@ -57,11 +59,10 @@ When `main_agent_extra` is absent or empty it falls back to the smart defaults o
|
|
|
57
59
|
|
|
58
60
|
### 1.3 Complete minimal example
|
|
59
61
|
|
|
60
|
-
A config file covering all commonly used keys (the key order matches `serializeConfig`, which is what the
|
|
62
|
+
A config file covering all commonly used keys (the key order matches `serializeConfig`, which is what the panel writes):
|
|
61
63
|
|
|
62
64
|
```yaml
|
|
63
|
-
# dsh-paoding installation config — hand-editable; after editing, re-run
|
|
64
|
-
# The interactive wizard / config UI also writes this file (0o600).
|
|
65
|
+
# dsh-paoding installation config — hand-editable; after editing, apply in Settings → 庖丁配置 (Paoding Config), or re-run node tools/install.mjs --auto.
|
|
65
66
|
|
|
66
67
|
profile: web # scan profiles/<profile>/cordis.patch.yml during detection
|
|
67
68
|
|
|
@@ -116,7 +117,7 @@ skills: # skill -> the roles it is assigned to (writes the Avail
|
|
|
116
117
|
|
|
117
118
|
Notes:
|
|
118
119
|
|
|
119
|
-
- The `tools` under `roles.search_external` express **intent**: if tavily is not enabled, the
|
|
120
|
+
- The `tools` under `roles.search_external` express **intent**: if tavily is not enabled, the generator drops the five `mcp__tavily__*` names and keeps the rest (the Preview "removed" list states the reason for each).
|
|
120
121
|
- `roles.design` omits `tools`, so the static allow is kept and only the persona is replaced.
|
|
121
122
|
- After installation, `roles.code-reviewer` becomes a generated `delegation-code-reviewer` block, `code-reviewer` joins the main-agent allow list, and the main agent can delegate to it directly by calling that toolName.
|
|
122
123
|
- `{{cwd}}` and `{{model}}` inside `main_agent_persona_extra` are persona placeholders that DSH substitutes at runtime — keep them verbatim.
|
|
@@ -143,6 +144,7 @@ roles:
|
|
|
143
144
|
operation: report the failure compactly so the orchestrator can re-delegate.
|
|
144
145
|
tools:
|
|
145
146
|
- read
|
|
147
|
+
- read_image
|
|
146
148
|
- write
|
|
147
149
|
- edit
|
|
148
150
|
- glob
|
|
@@ -168,11 +170,11 @@ Therefore:
|
|
|
168
170
|
|
|
169
171
|
- **Do not** put `search_internal_deep` under the `roles` key of the config file — keys outside the trio are treated as **custom roles**, and the installer would emit a second, duplicate `delegation-search-internal-deep` block that clashes with the static one.
|
|
170
172
|
- It is likewise **outside** the `roles_remove` deletable set (that key accepts only the trio's names, see 2.3) — it is deliberately kept as a static role, appears in no config key or UI, and this key cannot delete it either.
|
|
171
|
-
- To change its persona or tool surface, edit the `persona` and `toolFilter.allow` of the `delegation-search-internal-deep` block in `presets/orchestrator/agent.cordis.yml` directly, then
|
|
173
|
+
- To change its persona or tool surface, edit the `persona` and `toolFilter.allow` of the `delegation-search-internal-deep` block in `presets/orchestrator/agent.cordis.yml` directly, then hit Save & Apply in 庖丁配置 to regenerate (developers can also run the fallback CLI, `node tools/install.mjs --auto`). The generator rewrites only the limited spans of the trio roles and the main-agent persona; everything else in the static source (including your edit) carries over verbatim into the generated file.
|
|
172
174
|
|
|
173
175
|
### 2.3 Deleting built-in roles: roles_remove
|
|
174
176
|
|
|
175
|
-
The optional top-level key `roles_remove` (string[], default `[]`) deletes built-in delegation roles **wholesale**; its values are the toolNames of the roles to remove. The only deletable names are `search_external` / `design` / `implement` — exactly the three default roles the `roles` key can override (the three cards with the「内置」("built-in") badge in the
|
|
177
|
+
The optional top-level key `roles_remove` (string[], default `[]`) deletes built-in delegation roles **wholesale**; its values are the toolNames of the roles to remove. The only deletable names are `search_external` / `design` / `implement` — exactly the three default roles the `roles` key can override (the three cards with the「内置」("built-in") badge in the panel). Names outside that set are **ignored with a warning**; `search_internal_deep` cannot be deleted (it is a deliberately static role, see 2.2).
|
|
176
178
|
|
|
177
179
|
Deletion removes the role entirely — none of the following is generated anymore:
|
|
178
180
|
|
|
@@ -193,8 +195,8 @@ Relationship with the `roles` key: `roles` keeps the entries of built-in roles t
|
|
|
193
195
|
|
|
194
196
|
Restoring:
|
|
195
197
|
|
|
196
|
-
- **Hand-editing**: remove the name from `roles_remove` (and, if you want your custom persona / tools back, put the entry back under `roles`), then
|
|
197
|
-
- **
|
|
198
|
+
- **Hand-editing**: remove the name from `roles_remove` (and, if you want your custom persona / tools back, put the entry back under `roles`), then hit Save & Apply in 庖丁配置 — the role regenerates with its SRC-default persona and tool surface.
|
|
199
|
+
- **Panel**: the「已删除的内置角色」("deleted built-in roles") area at the end of the role list restores it with one click (restored to the SRC-default tool surface).
|
|
198
200
|
|
|
199
201
|
### 2.4 Built-in role display names: roles.\<toolName>.name
|
|
200
202
|
|
|
@@ -217,10 +219,10 @@ Boundaries:
|
|
|
217
219
|
- **Deleted roles are not involved**: a role deleted wholesale by `roles_remove` has no persona left to rewrite (see 2.3).
|
|
218
220
|
- **The static role is out of scope**: `search_internal_deep` is outside the installer-managed trio and has no such sub-key (see 2.2).
|
|
219
221
|
|
|
220
|
-
Applying it
|
|
222
|
+
Applying it works just like the main-agent display name (see 5.1):
|
|
221
223
|
|
|
222
|
-
- **Hand-editing**: write the sub-key and
|
|
223
|
-
- **
|
|
224
|
+
- **Hand-editing**: write the sub-key and hit Save & Apply in 庖丁配置, then restart DSH or open a new session.
|
|
225
|
+
- **Panel**: 庖丁配置 (left sidebar) → the「角色显示名」("role display name") input at the top of a built-in role card (the「内置」("built-in") badge); empty = the default; after「保存并应用」(save & apply) it takes effect on a **GUI restart / new session**.
|
|
224
226
|
|
|
225
227
|
### 2.5 A dedicated model per role: roles.\<toolName>.model / .provider
|
|
226
228
|
|
|
@@ -244,19 +246,46 @@ Rules and boundaries:
|
|
|
244
246
|
- **Deleted roles are not involved**: for a role deleted wholesale by `roles_remove`, any `model` / `provider` left in its entry is ignored with a warning (see 2.3).
|
|
245
247
|
- **The static role is out of scope**: `search_internal_deep` has neither sub-key (see 2.2).
|
|
246
248
|
|
|
247
|
-
In the config
|
|
249
|
+
In the config page (庖丁配置 in the left sidebar → the「专用模型」("dedicated model") control on a role card) this is a dropdown fed by the model routes registered in your DSH deployment; values outside the catalog (e.g. from a hand-edited config) are preserved as a「当前配置」("current config") option instead of silently disappearing.
|
|
248
250
|
|
|
249
|
-
Applying works exactly like every other config key:
|
|
251
|
+
Applying works exactly like every other config key: after editing, hit「保存并应用」(save & apply) in 庖丁配置, then restart DSH or open a new session.
|
|
250
252
|
|
|
251
253
|
These two sub-keys are the config-layer equivalent of the static-source `agentOptions` in 8.1 (no source edits — prefer them); drop to the static source only when you also need `maxTokens` (see 8.1).
|
|
252
254
|
|
|
255
|
+
### 2.6 Role session mode: roles.\<toolName>.background_mode
|
|
256
|
+
|
|
257
|
+
The optional `background_mode` sub-key of a role entry decides how that role's child sessions end: `'one-shot'` (run-and-discard) or `'continuable'` (kept across turns). Default `one-shot`; built-in and custom role entries share the semantics, with no per-role differences.
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
roles:
|
|
261
|
+
implement:
|
|
262
|
+
background_mode: continuable # this role's delegations run in the background and persist across turns; the main agent resumes in place with send_message
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Generation semantics: the base template ships a `backgroundMode: one-shot` row in each of the four built-in roles' delegation blocks (pure visibility, so the generated file never makes you guess the default); a role configured as `continuable` has that row **rewritten in place** to `backgroundMode: continuable` (a fallback insert happens only when the row is missing — two rows can never appear), while `one-shot` / absent keeps it as is. Custom-role blocks always carry the row too. For the orchestration-level trade-offs and the failure-recovery branch of each mode, see "One-shot vs continuable" in [Orchestration](orchestration_en.md).
|
|
266
|
+
|
|
267
|
+
Prerequisites and costs of continuable (stated plainly):
|
|
268
|
+
|
|
269
|
+
- **Prerequisite: a `sessionPersistence` backend mounted at the host layer.** Mount `@deepseek-ai/dsh-session-persistence-jsonl` in `~/.dsh/cordis.patch.yml` (`config.root` required). Without it, continuable creation/resumption fails loudly (error code `PERSISTENCE_UNAVAILABLE`). This is a machine-level, global change; uninstalling the preset does not undo it.
|
|
270
|
+
- **Costs: no TTL, on-disk accumulation, and accumulated context on every turn.** Persisted sessions have no TTL and keep writing to disk; a continuable child carries the context it has accumulated on every turn, so a long-lived child only gets more expensive. Reserve it for multi-round polishing delegations; `interrupt_agent` sessions you no longer need and clean up the on-disk files.
|
|
271
|
+
|
|
272
|
+
Panel path: 庖丁配置 in the left sidebar → the「会话模式」("session mode") radio on a role card (「一次性(one-shot)」/「可续(continuable)」). When `sessionPersistence` is not detected the panel warns in place but does not block — the warning only reminds you to mount the persistence backend first; saving works as usual.
|
|
273
|
+
|
|
274
|
+
Boundaries:
|
|
275
|
+
|
|
276
|
+
- **The static role is out of scope**: `search_internal_deep` is not configured through the `roles` key (see 2.2). Its delegation block carries the same explicit `backgroundMode: one-shot` marker in the base template; to make it continuable, edit that row in the static source and re-apply (the generator never rewrites that row, it carries over verbatim).
|
|
277
|
+
- **Deleted roles are not involved**: for a role deleted wholesale by `roles_remove`, any `background_mode` left in its entry is ignored with a warning (see 2.3).
|
|
278
|
+
- **The main persona already carries the matching branch**: when a continuable role fails or disappoints, the main agent's delegation SOP resumes it in the same session with `send_message` first, and re-delegation drops to a fallback (see [Orchestration](orchestration_en.md)).
|
|
279
|
+
|
|
280
|
+
Applying works exactly like every other config key: after editing, hit「保存并应用」(save & apply) in 庖丁配置, then restart DSH or open a new session.
|
|
281
|
+
|
|
253
282
|
## 3. Main-agent tool surface
|
|
254
283
|
|
|
255
284
|
### 3.1 Mechanism and the 21-name base allow list
|
|
256
285
|
|
|
257
286
|
The main agent's tool surface is filtered against an allow list by `presets/orchestrator/restrict.mjs` (the `orchestrator-restrict` plugin) on the `system-prompt/assemble` waterfall. The filter is **fail-open** (a bug in the filter itself can never brick a session); names are matched one-by-one against the resolved tools, so **unmounted names are simply absent — never an error**.
|
|
258
287
|
|
|
259
|
-
The `MAIN_AGENT_ALLOW` constant in `restrict.mjs` is the **base** (21 names) that the generated `config.allow` starts from; at runtime the filter falls back to it when no `config.allow` is injected. To reshape the main-agent tool surface, **do not edit `restrict.mjs`** — always use the config file (`main_agent_extra` / `main_agent_remove`) or the
|
|
288
|
+
The `MAIN_AGENT_ALLOW` constant in `restrict.mjs` is the **base** (21 names) that the generated `config.allow` starts from; at runtime the filter falls back to it when no `config.allow` is injected. To reshape the main-agent tool surface, **do not edit `restrict.mjs`** — always use the config file (`main_agent_extra` / `main_agent_remove`) or the main-agent card in the panel instead.
|
|
260
289
|
|
|
261
290
|
| Group | Tools (21) |
|
|
262
291
|
|---|---|
|
|
@@ -280,9 +309,9 @@ main_agent_extra:
|
|
|
280
309
|
|
|
281
310
|
Points:
|
|
282
311
|
|
|
283
|
-
- The entries land in the generated `orchestrator-restrict config.allow`.
|
|
284
|
-
- The concrete
|
|
285
|
-
- When `main_agent_extra` is absent or empty the
|
|
312
|
+
- The entries land in the generated `orchestrator-restrict config.allow`. Injection follows the same whitelist: a name must belong to the **preset's own tool face** (the `restrict.mjs` main-agent allow ∪ the static role allows in the source preset) or to the **detection inventory**, and host tools (`mcp__*`, plugin tools like `mnemon_*`) go by what was actually detected — undetected names (MCP/plugin disabled, typo) are silently not injected, and the removed list in Preview shows the reason.
|
|
313
|
+
- The concrete MCP / plugin tools checkable on the panel's "main agent" card are those shown by the detection results (the tool pool).
|
|
314
|
+
- When `main_agent_extra` is absent or empty the generator falls back to smart defaults (detected codegraph / `memory_search` tools are suggested for the main agent).
|
|
286
315
|
|
|
287
316
|
### 3.3 Removing base tools: main_agent_remove
|
|
288
317
|
|
|
@@ -362,28 +391,35 @@ The generator inlines the full body of the selected SKILL.md (**YAML frontmatter
|
|
|
362
391
|
| No matching SKILL.md found | **Warning and skip** (does not abort the install); warned once per name across both keys |
|
|
363
392
|
| SKILL.md exists but its body is empty | Treated as not found; skipped |
|
|
364
393
|
| `description` parsing | Handles single-line, quoted (single/double), folded (`>`) and literal (`\|`) blocks (including a block marker on its own line); collapses whitespace, strips one pair of surrounding quotes, caps at 200 chars; empty string when absent |
|
|
365
|
-
| SKILL.md scan roots | `$DSH_HOME/skills` (default `~/.dsh/skills`), `$DSH_AGENTS_HOME/skills` (default `~/.agents/skills`)
|
|
394
|
+
| SKILL.md scan roots | Global (fallback CLI / panel "Global default") scans the two user roots only: `$DSH_HOME/skills` (default `~/.dsh/skills`), `$DSH_AGENTS_HOME/skills` (default `~/.agents/skills`); generating for a workspace additionally scans that workspace's `.dsh/skills` and `.agents/skills` (section 9). Within a root, `<root>/<name>/SKILL.md` wins over a bare `<root>/<name>.md` |
|
|
366
395
|
|
|
367
|
-
Skill names come from
|
|
396
|
+
Skill names come from apply-time skill detection (the same four roots); the panel only offers detected skills.
|
|
368
397
|
|
|
369
398
|
## 5. Main-agent persona
|
|
370
399
|
|
|
371
|
-
### 5.1
|
|
400
|
+
### 5.1 Display name only: main_agent_display_name
|
|
401
|
+
|
|
402
|
+
`main_agent_display_name` (an optional top-level key, string | null) changes **only the deployed preset display name** — the name shown in the GUI preset picker — and leaves the persona identity line `You are the orchestrator agent powered by the {{model}} model.` untouched. The orchestrator's identity is part of the orchestration preset's prompt semantics and **cannot be renamed** (the legacy "main agent name" key has been removed; leftover lines in old configs are dropped automatically the next time you hit Save & Apply, and the installer notes that the key is ignored).
|
|
372
403
|
|
|
373
|
-
|
|
404
|
+
Constraints: 1–60 characters after trimming, no newlines; violations raise a hard error. Empty / absent = the default「编排模式 (Orchestrator)」(workspace presets use the derived name).
|
|
374
405
|
|
|
375
|
-
|
|
376
|
-
- **The persona identity line**: `You are the <name> agent powered by the {{model}} model.` in the main-agent persona, with `<name>` = this key's value.
|
|
406
|
+
When generating `preset.yml`, the display name is resolved in this order:
|
|
377
407
|
|
|
378
|
-
|
|
408
|
+
1. `main_agent_display_name` (this key, highest priority when non-empty);
|
|
409
|
+
2. the derived「SRC 名·目录名」name (workspace presets only, see 9.2);
|
|
410
|
+
3. the SRC name verbatim.
|
|
411
|
+
|
|
412
|
+
Without this key, behavior stays byte-for-byte identical to before (backward compatible; existing configs upgrade with zero friction).
|
|
379
413
|
|
|
380
414
|
```yaml
|
|
381
|
-
|
|
415
|
+
main_agent_display_name: 庖丁 # only the preset display name becomes「庖丁」; the identity line stays
|
|
382
416
|
```
|
|
383
417
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
-
|
|
418
|
+
Where to set it:
|
|
419
|
+
|
|
420
|
+
- **Hand-editing**: write the key and hit Save & Apply in 庖丁配置, then restart the GUI or open a new session.
|
|
421
|
+
- **Panel**: 庖丁配置 (left sidebar) → main-agent card, the「主 agent 显示名(仅 preset 显示名)」("main agent display name (preset display name only)") input, with its「恢复默认」("restore default") button; after「保存并应用」(save & apply) it takes effect on a **GUI restart / new session**.
|
|
422
|
+
- For contrast: there are two independent naming lines that never interfere with each other: `main_agent_display_name` (the main agent's preset display name only, see 5.1) and `roles.<toolName>.name` (the subject of a built-in role's default persona identity sentence, never the tool call name — see 2.4). Configure each on its own.
|
|
387
423
|
|
|
388
424
|
### 5.2 Persona extra: three-state semantics
|
|
389
425
|
|
|
@@ -407,9 +443,8 @@ It matters only when the codegraph MCP is mounted (it constrains codegraph tools
|
|
|
407
443
|
|
|
408
444
|
### 5.4 Where to edit
|
|
409
445
|
|
|
410
|
-
- **Hand-editing**: edit the `main_agent_persona_extra` key in the config file
|
|
411
|
-
- **
|
|
412
|
-
- The interactive wizard has no dedicated prompt step for the persona extra (it inherits the existing config value) — under the CLI, hand-edit the file or use the UI.
|
|
446
|
+
- **Hand-editing**: edit the `main_agent_persona_extra` key in the config file, then hit Save & Apply in 庖丁配置.
|
|
447
|
+
- **Panel**: 庖丁配置 (left sidebar) → main-agent card, the「人设追加」(persona extra) editor (with a "restore default" button) lets you edit / clear / restore the default; after「保存并应用」(save & apply) the change takes effect on a **GUI restart / new session**.
|
|
413
448
|
|
|
414
449
|
## 6. Custom roles
|
|
415
450
|
|
|
@@ -421,29 +456,39 @@ loads skill rules on demand through the skill tool) plus `read` / `write` / `edi
|
|
|
421
456
|
that says to build HTML slides with the `html-ppt` skill, and assign the `html-ppt` skill to it (section 7).
|
|
422
457
|
After install, a main-agent "turn this outline into a PPT" delegates to a dedicated slide-making sub-agent
|
|
423
458
|
instead of asking a generalist role to handle it. Any installed skill can become a specialized role the same
|
|
424
|
-
way (frontend drafts, charts, document layout, …). This section covers the
|
|
459
|
+
way (frontend drafts, charts, document layout, …). This section covers the panel path (6.1), the
|
|
425
460
|
hand-written static-source path (6.2), and configuration examples (6.3, including a skill-typed PPT agent).
|
|
426
461
|
|
|
427
462
|
Two generation-time behaviors are worth stating up front. First, **a custom role with an empty tool
|
|
428
463
|
surface is rejected at apply time** — a missing or empty `tools` list yields a blank allow list
|
|
429
464
|
(`toolFilter.allow:` collapses to YAML null), leaving the sub-agent without a single tool; the generator
|
|
430
|
-
treats custom roles exactly like built-ins here and refuses to
|
|
431
|
-
least one tool before applying
|
|
465
|
+
treats custom roles exactly like built-ins here and refuses to apply such a role, so give the role at
|
|
466
|
+
least one tool before applying. Second, the generator
|
|
432
467
|
**appends the role's persona first line — its duty sentence — to the main-agent persona as a delegation
|
|
433
468
|
row** (`- <duty>: delegate to <toolName>.`, right after the existing delegation rows); that row is the
|
|
434
469
|
main agent's only routing hint for the role, so make the persona's first line a short duty sentence
|
|
435
470
|
(e.g. "Find the skill that matches the user's need").
|
|
436
471
|
|
|
437
|
-
### 6.1 Path 1:
|
|
472
|
+
### 6.1 Path 1: the 庖丁配置 panel (recommended)
|
|
438
473
|
|
|
439
|
-
The
|
|
474
|
+
The panel flow (庖丁配置 in the left sidebar → agent tool assignment → "Create a custom agent role" at the bottom):
|
|
440
475
|
|
|
441
476
|
1. Create a role: enter a `toolName` matching `/^[a-z][a-z0-9_]*$/`;
|
|
442
|
-
2. Multi-select tools from the tool pool (the trio's static allows ∪ detected
|
|
443
|
-
3. Write a
|
|
444
|
-
4.
|
|
445
|
-
|
|
446
|
-
|
|
477
|
+
2. Multi-select tools from the tool pool (the trio's static allows ∪ detected MCP / plugin tools);
|
|
478
|
+
3. Write a persona (a full persona is accepted; make the first line a short duty sentence);
|
|
479
|
+
4. Hit Save & Apply — the generator automatically injects the new `toolName` into the main agent's `orchestrator-restrict config.allow` — **`restrict.mjs` itself is never touched** — and persists the assignment to the config file (a new entry under the `roles` key).
|
|
480
|
+
|
|
481
|
+
A note on where the role card's tool checklist comes from. Candidates are the static allow face from the
|
|
482
|
+
source preset (built-in roles have it, core tools included) ∪ the main agent's core allow list (`restrictBase`,
|
|
483
|
+
the fallback for custom roles) ∪ the detection inventory, presented in three groups: "**Base tools**" = core
|
|
484
|
+
names that are neither `mcp__`-prefixed nor in the inventory (`read` / `glob` / `grep` / `bash` / `todo_write`
|
|
485
|
+
etc.; the delegation names `search_external` / `design` / `implement` / `search_internal_deep` are not toggled
|
|
486
|
+
in the grid); "**MCP tools**" = `mcp__`-prefixed names (including rows shipped in the source preset, such as
|
|
487
|
+
tavily); "**Plugin tools**" = inventory names registered by plugins under their own names, no `mcp__` prefix
|
|
488
|
+
(`mnemon_*` etc.). A newly created role ships with the core face plus the inventory pre-checked in its factory
|
|
489
|
+
`tools`; trim or extend it before Save & Apply.
|
|
490
|
+
|
|
491
|
+
You can then delegate to that role directly by calling the toolName in conversation. To delete a role, delete its card in the panel (or remove the entry from the config file) and hit Save & Apply again (the target file is regenerated from scratch each run — no leftover blocks). Deleting a **built-in** role wholesale is different — that is `roles_remove` (see 2.3); simply not writing it under `roles` only stops overriding it.
|
|
447
492
|
|
|
448
493
|
### 6.2 Path 2: hand-written static source
|
|
449
494
|
|
|
@@ -452,7 +497,7 @@ When you prefer to skip the config layer, add the role in the source preset dire
|
|
|
452
497
|
1. Copy a `delegation-*` block inside the delegation group of `presets/orchestrator/agent.cordis.yml`;
|
|
453
498
|
2. Change its `toolName` / `persona` / `toolFilter.allow`;
|
|
454
499
|
3. Add the new `toolName` to `MAIN_AGENT_ALLOW` in `presets/orchestrator/restrict.mjs` (that constant is the base of the generated `config.allow`; without the name in the main-agent allow list the delegation tool is not callable);
|
|
455
|
-
4. Re-run
|
|
500
|
+
4. Re-apply (Save & Apply in 庖丁配置; developers can also run `node tools/install.mjs --auto`) — the generator rewrites only the limited trio/main-agent persona spans, so your added block carries over verbatim.
|
|
456
501
|
|
|
457
502
|
Both paths produce the same structure (`delegation-<toolName>` plus the toolName in the allow list); path 1 needs no source edits and is UI-manageable — prefer it.
|
|
458
503
|
|
|
@@ -476,7 +521,7 @@ roles:
|
|
|
476
521
|
- todo_write
|
|
477
522
|
```
|
|
478
523
|
|
|
479
|
-
After
|
|
524
|
+
After Save & Apply in 庖丁配置: a `delegation-code-reviewer` block is generated (persona as-is, tools verbatim into its allow) and `code-reviewer` is injected into the main agent's `config.allow`. Note that a custom role's `tools` are written into the allow **verbatim — no detection intersection** (the panel's tool pool guarantees the choices exist in the registry) — when hand-writing, list only actually-mounted tools, otherwise child-agent creation is rejected by `tools.restrict()` for unknown tools.
|
|
480
525
|
|
|
481
526
|
**Skill-typed example: a `ppt` agent (a dedicated slide-making sub-agent)** — the tool surface includes
|
|
482
527
|
`skill` plus read/write/edit, the persona guides the agent to the `html-ppt` skill, and the `skills` key
|
|
@@ -508,14 +553,14 @@ The effect is the same as `code-reviewer` (a `delegation-ppt` block is generated
|
|
|
508
553
|
the difference is the extra `skills` mapping. Once the sub-agent exists, the skill catalog is visible to it in
|
|
509
554
|
full (a DSH mechanism), so the persona guidance line is only a **soft constraint** — the actual work happens
|
|
510
555
|
when the sub-agent loads the rules via the `skill` tool; mounting other skills (resumes, charts, …) works the
|
|
511
|
-
same way. The
|
|
512
|
-
write a one-line persona, and assign `html-ppt` to `ppt` in the skill-assignment
|
|
556
|
+
same way. The 庖丁配置 panel can build the same role: tick `skill` plus read/write/edit in the tool pool,
|
|
557
|
+
write a one-line persona, and assign `html-ppt` to `ppt` in the skill-assignment grid.
|
|
513
558
|
|
|
514
559
|
## 7. Skills to roles
|
|
515
560
|
|
|
516
561
|
### 7.1 Writing and effect
|
|
517
562
|
|
|
518
|
-
The top-level `skills` key assigns skills to roles (the value is a list of role toolNames — the same mapping as the
|
|
563
|
+
The top-level `skills` key assigns skills to roles (the value is a list of role toolNames — the same mapping as the panel's skill-assignment grid):
|
|
519
564
|
|
|
520
565
|
```yaml
|
|
521
566
|
skills:
|
|
@@ -557,4 +602,54 @@ Omitting `agentOptions` means inheriting the main agent's model. The config sub-
|
|
|
557
602
|
|
|
558
603
|
### 8.3 Syncing after disabling MCPs / plugins
|
|
559
604
|
|
|
560
|
-
When you disable tavily / codegraph / magic-memory (comment out the entry in `~/.dsh/cordis.patch.yml` or the profile patch), **no manual allow editing is needed**:
|
|
605
|
+
When you disable tavily / codegraph / magic-memory (comment out the entry in `~/.dsh/cordis.patch.yml` or the profile patch), **no manual allow editing is needed**: hit Save & Apply once in 庖丁配置 and the generator automatically prunes the matching `mcp__tavily__*` / `memory_search` names from the trio's allows (use Preview to see the pruned names first). MCP servers that cannot be detected or whose handshake fails are skipped: the affected roles simply lack those tools and applying does not error. Detection and patch-layer details chain to [Installation](installation_en.md).
|
|
606
|
+
|
|
607
|
+
## 9. Per-workspace configuration
|
|
608
|
+
|
|
609
|
+
### 9.1 One global set, one per workspace
|
|
610
|
+
|
|
611
|
+
DSH organizes sessions by workspace (the project directory). The top-level `workspaces` key gives each workspace its own main/sub-agent configuration: the global entry keeps generating the shared `orchestrator` preset, while each workspace entry generates its own `orchestrator-<slug>` preset. They coexist in the preset roster and never overwrite each other.
|
|
612
|
+
|
|
613
|
+
Each workspace entry is a **complete configuration set**, not a delta: same fields as the top level (except `profile` — detection layers stay global). Role sub-keys carry over as-is — `background_mode` (see 2.6) works per workspace too: let `implement` run continuable in one project while every other project stays one-shot. The first time you configure a workspace in the panel, it starts from the current global config; edit, then hit "Save & Apply" to generate that workspace's preset.
|
|
614
|
+
|
|
615
|
+
```yaml
|
|
616
|
+
workspaces:
|
|
617
|
+
'/Users/me/code/shop-api': # key = absolute workspace directory (normalized on write, always quoted)
|
|
618
|
+
main_agent_display_name: Shop Backend Lead # that workspace preset's display name only
|
|
619
|
+
roles:
|
|
620
|
+
implement:
|
|
621
|
+
model: deepseek-reasoner # this project codes with the reasoning model
|
|
622
|
+
'/Users/me/code/blog':
|
|
623
|
+
roles_remove: [design] # no design helper needed here
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### 9.2 Preset naming
|
|
627
|
+
|
|
628
|
+
- The **slug** comes from the workspace directory's basename: lowercased, invalid characters collapsed to `-`; if that yields an empty string or exactly `orchestrator` (colliding with the global preset), `ws` is used instead.
|
|
629
|
+
- When multiple workspaces share a basename, a path-hash suffix (first 6 hex chars of sha1) is appended — two projects both named `Shop` become `orchestrator-shop-acd95b` and `orchestrator-shop-34b6aa`. Rename or move the directory and re-apply; stale preset directories are not cleaned automatically — remove the ones you no longer want on the DSH presets page.
|
|
630
|
+
- Preset display name: the entry's `main_agent_display_name` if set; otherwise the default name gets the `·<basename>` suffix (e.g. `编排模式 (Orchestrator)·Shop`) so entries are distinguishable at a glance in the roster.
|
|
631
|
+
|
|
632
|
+
### 9.3 Using the panel
|
|
633
|
+
|
|
634
|
+
At the top of the 庖丁配置 page sits the **configuration target** tab strip:
|
|
635
|
+
|
|
636
|
+
- **Global default**: edits and writes the shared `orchestrator` preset, exactly as before;
|
|
637
|
+
- **Workspace tabs**: the list comes from the host workspace registry (directories register once a session has opened there); a green dot means the preset has been generated, and hovering shows the path, session count, and preset id;
|
|
638
|
+
- **Add workspace**: type an absolute directory path to register it (idempotent). If the registry is unreachable, the panel falls back to showing only the workspaces already in the config file — everything else keeps working.
|
|
639
|
+
|
|
640
|
+
With a workspace selected, preview and apply target it alone; the success note reports the preset id.
|
|
641
|
+
|
|
642
|
+
### 9.4 How it takes effect, and boundaries
|
|
643
|
+
|
|
644
|
+
- **Preset choice happens at session creation**: pick the workspace on the new-session screen, then pick the matching `orchestrator-<slug>` in the preset selector. The global default preset is untouched. A new session (or a DSH restart) is needed for a freshly generated preset to appear.
|
|
645
|
+
- Preset directories live under `$DSH_HOME/.agent-presets/` and are shared machine-wide: separate DSH instances launched from different projects never clobber each other, because their slugs differ.
|
|
646
|
+
- The fallback CLI still targets the global entry only; the `workspaces` section survives a CLI apply unchanged.
|
|
647
|
+
- A running session never switches presets — a session's roles and tool surfaces are assembled at creation and fixed for its lifetime; to change the lineup, start a new session with the new preset.
|
|
648
|
+
|
|
649
|
+
### 9.5 Skill scan roots follow the configuration target
|
|
650
|
+
|
|
651
|
+
Skill candidates do not follow the directory the GUI was launched from: the **global default** view scans the two user roots (`~/.dsh/skills`, `~/.agents/skills`), while a **workspace** view additionally scans that workspace's `.dsh/skills` and `.agents/skills`. Consequences:
|
|
652
|
+
|
|
653
|
+
- A skill installed only inside one project (say `bento-slides` living in a knowledge-base repo) is invisible — and unassignable — in the global config; select that workspace and it appears in the skill grid with a "project" badge, assignable to that workspace preset's main agent or roles.
|
|
654
|
+
- Generation resolves skills through the same lens: a workspace preset can emit the absolute SKILL.md path of a project-local skill (the main agent reads it on demand with `read` — any on-disk path is readable); the global preset resolves user roots only and warns-and-skips project-local skill names — a global preset travels across all projects and should not reference a single project's skill anyway.
|
|
655
|
+
- The fallback CLI has no workspace concept and scans the two user roots only (same as the global default).
|