dsh-paoding 0.2.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/LICENSE +21 -0
- package/README.md +95 -0
- package/README_en.md +95 -0
- package/bin/dsh-paoding.mjs +12 -0
- package/cordis.patch.yml +6 -0
- package/docs/architecture.md +292 -0
- package/docs/architecture_en.md +363 -0
- package/docs/configuration.md +551 -0
- package/docs/configuration_en.md +560 -0
- package/docs/images/1.png +0 -0
- package/docs/images/2.png +0 -0
- package/docs/images/3.png +0 -0
- package/docs/images/4.png +0 -0
- package/docs/images/5.png +0 -0
- package/docs/images/6.png +0 -0
- package/docs/installation.md +364 -0
- package/docs/installation_en.md +367 -0
- package/docs/orchestration.md +254 -0
- package/docs/orchestration_en.md +255 -0
- package/install.sh +31 -0
- package/package.json +29 -0
- package/plugins/paoding-config-ui/api-core.mjs +100 -0
- package/plugins/paoding-config-ui/index.mjs +327 -0
- package/plugins/paoding-config-ui/lib/base.css +615 -0
- package/plugins/paoding-config-ui/lib/client.js +1154 -0
- package/plugins/paoding-config-ui/package.json +23 -0
- package/plugins/paoding-config-ui/runtime-inventory.mjs +212 -0
- package/presets/orchestrator/agent.cordis.yml +323 -0
- package/presets/orchestrator/preset.yml +6 -0
- package/presets/orchestrator/restrict.mjs +191 -0
- package/tools/install.mjs +24 -0
- package/tools/lib/alloc.mjs +138 -0
- package/tools/lib/cli.mjs +522 -0
- package/tools/lib/compose.mjs +647 -0
- package/tools/lib/config.mjs +297 -0
- package/tools/lib/host.mjs +288 -0
- package/tools/lib/skills.mjs +146 -0
- package/tools/lib/spans.mjs +188 -0
- package/tools/lib/state.mjs +328 -0
- package/tools/lib/util.mjs +26 -0
- package/tools/lib/wizard.mjs +516 -0
- package/tools/lib/yaml.mjs +266 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
<!-- docs/architecture_en.md — dsh-paoding (Pao Ding) architecture, English version -->
|
|
2
|
+
|
|
3
|
+
← [dsh-paoding](../README_en.md) | [Installation](installation_en.md) | **Architecture** | [Orchestration](orchestration_en.md) | [Configuration](configuration_en.md)
|
|
4
|
+
|
|
5
|
+
[中文](architecture.md) · English
|
|
6
|
+
|
|
7
|
+
# Architecture
|
|
8
|
+
|
|
9
|
+
> Reader: technical users and contributors who want to understand why it is designed this way, the
|
|
10
|
+
> costs and benefits, and the mechanisms underneath. Facts follow the repository source
|
|
11
|
+
> (`presets/orchestrator/agent.cordis.yml`, `presets/orchestrator/restrict.mjs`,
|
|
12
|
+
> `presets/orchestrator/preset.yml`); where they conflict with the README, the source wins.
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
dsh-paoding (Chinese brand name 庖丁 / Pao Ding, from the Zhuangzi parable "庖丁解牛" — the butcher whose
|
|
17
|
+
knife never dulls because he cuts along the natural seams) is an **orchestrator-preset generator and
|
|
18
|
+
configurator** for DSH (DeepSeek Harness, an agent runtime). It reshapes DSH's default shape — a monolithic
|
|
19
|
+
agent that loads every tool on every request (~60 tools, ~16.2k tokens of tool tax per request) — into an
|
|
20
|
+
architecture of **one orchestrating main agent plus role-specialized sub-agents that load tools on demand**:
|
|
21
|
+
|
|
22
|
+
- **The hot path stays on the main agent**: internal file search (glob / grep / read / read_image / bash)
|
|
23
|
+
is reached directly with zero delegation round-trip;
|
|
24
|
+
- **Cold paths are delegated to role sub-agents**: external web research (search_external), design (design),
|
|
25
|
+
code implementation (implement), and whole-repository deep search (search_internal_deep) are each handed
|
|
26
|
+
to a sub-agent carrying only the tools it needs — **pay per use, once used once paid**;
|
|
27
|
+
- **Context isolation**: every sub-agent keeps a small, specialized context, and the main agent integrates
|
|
28
|
+
only summaries.
|
|
29
|
+
|
|
30
|
+
The whole solution is "pure configuration plus one waterfall-filter plugin whose core logic is roughly 130
|
|
31
|
+
lines (`restrict.mjs`, ~190 lines with comments)" — **zero changes to DSH source code**, hot-swappable
|
|
32
|
+
(install / uninstall / modify are all plain file operations).
|
|
33
|
+
|
|
34
|
+
The essential differences from monolithic mode:
|
|
35
|
+
|
|
36
|
+
| Dimension | Monolithic mode (default) | Orchestrator mode (this preset) |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| Responsibility | One agent does everything | Main agent decomposes and integrates; role sub-agents each do one job |
|
|
39
|
+
| Cost | ~16.2k tokens tool tax paid on every request | Main agent ~5.5k; role surfaces ~3.0k–3.7k, paid only when delegated |
|
|
40
|
+
| Context | Every search dump, design draft and diff accumulates in the main context | Worker contexts are small and specialized and discarded after use; the main agent receives only summaries |
|
|
41
|
+
|
|
42
|
+
The static source of the composition is `presets/orchestrator/agent.cordis.yml`, layered on top of DSH's
|
|
43
|
+
builtin `standard` preset in full (see the "Under the hood" section below); at runtime
|
|
44
|
+
`restrict.mjs` trims the main agent's tool surface. The four role delegation instances are
|
|
45
|
+
`delegation-search-external` / `delegation-design` / `delegation-implement` /
|
|
46
|
+
`delegation-search-internal-deep`, all inside the composition's delegation group. That group also carries the
|
|
47
|
+
generic delegation tools `subagent` / `subagent_fork` / `workflow` / `ralph` (they are not in the main-agent
|
|
48
|
+
whitelist; role delegation is the preset's primary path).
|
|
49
|
+
|
|
50
|
+
The architecture diagram below shows the main-agent tool surface and the four role delegations (the complete
|
|
51
|
+
allow lists are in the "Roles & tool surfaces" section below):
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
┌─ main agent · orchestrator (orchestrator-mode preset, 21-tool whitelist)─────────────┐
|
|
55
|
+
│ delegate : search_external / design / implement / search_internal_deep │
|
|
56
|
+
│ manage : send_message / list_agents / interrupt_agent │
|
|
57
|
+
│ hot path : glob / grep / read / read_image / bash ◄── zero delegation round-trip │
|
|
58
|
+
│ coordinate: todo_write / ask_user_question / get_goal / create_goal / │
|
|
59
|
+
│ update_goal / exit_plan_mode / job_output / job_list / job_kill │
|
|
60
|
+
│ job : decompose → solve internal problems in place → delegate only │
|
|
61
|
+
│ research / design / implementation / whole-repo deep search │
|
|
62
|
+
│ → integrate summaries only │
|
|
63
|
+
└───────────┬───────────────────┬───────────────────┬───────────────────┬──────────────┘
|
|
64
|
+
│ │ │ │
|
|
65
|
+
▼ ▼ ▼ ▼
|
|
66
|
+
search_external design implement search_internal_deep
|
|
67
|
+
(web research) (design) (implementation) (repo deep search)
|
|
68
|
+
(on demand) (on demand) (on demand) (on demand)
|
|
69
|
+
│ │ │ │
|
|
70
|
+
▼ ▼ ▼ ▼
|
|
71
|
+
web_search read/read_image read/write/edit glob/grep/read
|
|
72
|
+
mcp__tavily__* glob/grep/bash glob/grep/bash read_image/bash
|
|
73
|
+
(5 tools) skill/write/edit skill/web_search ask_user_question
|
|
74
|
+
glob/grep/read ask_user_question ask_user_question todo_write
|
|
75
|
+
ask_user_question todo_write todo_write job_output
|
|
76
|
+
(no write/edit) job_output job_output job_list
|
|
77
|
+
job_list job_list/job_kill job_kill
|
|
78
|
+
job_kill get_goal (read-only; no
|
|
79
|
+
create_goal file writes,
|
|
80
|
+
update_goal no network)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
In the diagram:
|
|
84
|
+
|
|
85
|
+
- The **main agent** box lists its 21-entry whitelist, grouped into delegation (search_external / design /
|
|
86
|
+
implement / search_internal_deep), child management (send_message / list_agents / interrupt_agent), the
|
|
87
|
+
internal-search hot path (glob / grep / read / read_image / bash), and coordination (todo_write /
|
|
88
|
+
ask_user_question / get_goal / create_goal / update_goal / exit_plan_mode / job_output / job_list /
|
|
89
|
+
job_kill);
|
|
90
|
+
- Four arrows fan out to the **role sub-agents**, each created on demand (cold path). search_external
|
|
91
|
+
carries web_search, the five `mcp__tavily__*` tools, plus glob/grep/read and ask_user_question, with no
|
|
92
|
+
write/edit; design carries read/read_image/glob/grep/bash/skill/write/edit plus coordination tools;
|
|
93
|
+
implement carries read/write/edit/glob/grep/bash/skill/web_search plus coordination and goal tools;
|
|
94
|
+
search_internal_deep carries the full read-only internal-search surface (glob/grep/read/read_image/bash
|
|
95
|
+
plus ask_user_question and job tools) — no file modification, no network.
|
|
96
|
+
|
|
97
|
+
## Why it costs less
|
|
98
|
+
|
|
99
|
+
The core benefit of this preset is **pay-per-use plus context isolation**; the tool tax is only the most
|
|
100
|
+
visible symptom. Per-request tool-tax estimates (source: the header comment of `agent.cordis.yml` and
|
|
101
|
+
measured calibration; figures are order-of-magnitude estimates):
|
|
102
|
+
|
|
103
|
+
| Setup | Tool surface | tokens/request | Notes |
|
|
104
|
+
|---|---|---|---|
|
|
105
|
+
| Monolithic (status quo, untrimmed) | All 60 tools | **~16.2k** | Paid in full on every request |
|
|
106
|
+
| Orchestrator main agent | 21-entry whitelist (∩ registry) | **~5.5k** | ↓66%; zero delegation latency for internal search |
|
|
107
|
+
| search_external sub-agent | 10 allowed entries | ~3.0k | Paid only when web research actually runs |
|
|
108
|
+
| design sub-agent | 13 allowed entries | ~3.2k | Paid only when a design task is delegated |
|
|
109
|
+
| implement sub-agent | 16 allowed entries | ~3.7k | Paid only when an implementation task is delegated |
|
|
110
|
+
| search_internal_deep sub-agent | 10 allowed entries | not separately estimated | Paid only when a deep repo search is delegated |
|
|
111
|
+
|
|
112
|
+
Note: role tool counts above follow the actual `allow` entries in `agent.cordis.yml` (design 13 / implement
|
|
113
|
+
16); the old README figures of 14 / 17 and a 22-entry whitelist are outdated.
|
|
114
|
+
|
|
115
|
+
**The real win is context isolation, not the few thousand tokens saved per se**:
|
|
116
|
+
|
|
117
|
+
- A monolithic agent's context accumulates every search dump, every design draft and every diff — even when a
|
|
118
|
+
tool is unused in the current request, its past outputs still occupy the window;
|
|
119
|
+
- In the orchestrator architecture every worker keeps a small, specialized context (its own role tools and
|
|
120
|
+
outputs only); roles are one-shot by default and discarded after use. The main agent receives only
|
|
121
|
+
structured summaries and integrates them — it never pours a sub-agent's full context into its own window;
|
|
122
|
+
- Consequently the main agent's window size is decoupled from "how much work this round delegated"; in long
|
|
123
|
+
sessions the token level is dominated by the orchestrator's own summaries and decisions (combined with
|
|
124
|
+
compaction governance, see the "Performance: token governance" section below).
|
|
125
|
+
|
|
126
|
+
**Skill-catalog injection into the main agent is zero** (measured ≈1.3k tokens/round for a 4455-byte
|
|
127
|
+
catalog of 10 skills; eliminated by a registry-level deny — mechanism ⑤ in the "Under the hood" section).
|
|
128
|
+
The main agent carries no `skill` tool yet must still be able to use skills on demand, through two channels:
|
|
129
|
+
|
|
130
|
+
- `main_agent_skills` (soft guidance, the default channel): each skill occupies a single persona line
|
|
131
|
+
(skill name + description + SKILL.md path) — zero catalog tax; when a task matches a skill the main agent
|
|
132
|
+
reads the full SKILL.md on demand with its own `read` tool and follows it. Unused skills cost nothing;
|
|
133
|
+
- `main_agent_skills_inline` (optional hard inlining): the full SKILL.md body of the listed skills is
|
|
134
|
+
inlined into the persona at a fixed per-round cost — for style skills such as caveman that must apply
|
|
135
|
+
unconditionally.
|
|
136
|
+
|
|
137
|
+
Configuration details for both channels: [docs/configuration_en.md](configuration_en.md).
|
|
138
|
+
|
|
139
|
+
## Roles & tool surfaces
|
|
140
|
+
|
|
141
|
+
### Main-agent tool surface (21-entry whitelist)
|
|
142
|
+
|
|
143
|
+
The hardcoded `MAIN_AGENT_ALLOW` in `restrict.mjs` has **21 entries** in four groups (order follows the
|
|
144
|
+
source comments):
|
|
145
|
+
|
|
146
|
+
| Group | Tools |
|
|
147
|
+
|---|---|
|
|
148
|
+
| Role delegation (4; the default full set, three can be deleted via config) | `search_external` `design` `implement` `search_internal_deep` |
|
|
149
|
+
| Child management (3) | `send_message` `list_agents` `interrupt_agent` |
|
|
150
|
+
| Internal search · hot path (5) | `glob` `grep` `read` `read_image` `bash` |
|
|
151
|
+
| Coordination (9) | `todo_write` `ask_user_question` `get_goal` `create_goal` `update_goal` `exit_plan_mode` `job_output` `job_list` `job_kill` |
|
|
152
|
+
|
|
153
|
+
**Host tools are not hardcoded in the whitelist constant**: `mcp__*` and plugin tools (codegraph /
|
|
154
|
+
memory_search / mnemon_* etc.) are "install-time facts" — when the main agent needs one, it is ticked in
|
|
155
|
+
`main_agent_extra` in the configuration file `$DSH_HOME/dsh-paoding.config.yml`, the generator injects the
|
|
156
|
+
still-enabled ones into the restrict `config.allow` based on runtime detection, and disabled ones are
|
|
157
|
+
dropped automatically (see [docs/configuration_en.md](configuration_en.md)). The main agent's default surface
|
|
158
|
+
therefore excludes `web_search`, every `mcp__tavily__*`, `write` / `edit` / `str_replace_editor`, `skill`,
|
|
159
|
+
`workflow`, `ralph`, and the unfiltered `subagent` / `subagent_fork` — either because they belong to role
|
|
160
|
+
sub-agents or because their cost is deliberately zeroed out.
|
|
161
|
+
|
|
162
|
+
### Built-in role agents at a glance
|
|
163
|
+
|
|
164
|
+
Each role is a `dsh-tool-subagent` instance: which delegation tool the main agent calls selects that
|
|
165
|
+
sub-agent's tool surface (the sub-agent is created with a `tools.restrict()` applied from its `toolFilter`,
|
|
166
|
+
see mechanism ④ in the "Under the hood" section below). The role personas embedded in the main-agent
|
|
167
|
+
persona and the delegation-failure SOP live in [docs/orchestration_en.md](orchestration_en.md).
|
|
168
|
+
|
|
169
|
+
The four built-in roles are just the factory division of labor: through the same mechanism, any combination
|
|
170
|
+
of tools and skills becomes a new delegation tool — write a toolName under the config `roles` key (or create
|
|
171
|
+
it in the wizard / config UI), the installer generates a `delegation-<toolName>` block, injects the
|
|
172
|
+
toolName into the main-agent allow list, and appends the role's persona first line (its duty sentence)
|
|
173
|
+
to the main-agent persona as a delegation row (a role with no tools is rejected at apply time); see
|
|
174
|
+
section 6 of [docs/configuration_en.md](configuration_en.md).
|
|
175
|
+
Custom roles support the same `model` / `provider` dedicated-model sub-keys as the built-in roles (same
|
|
176
|
+
sub-keys, same generation mechanism — see 2.5).
|
|
177
|
+
For example, a `ppt` agent carrying the `html-ppt` skill gives slide-making its own dedicated sub-agent.
|
|
178
|
+
|
|
179
|
+
| Delegation tool | Instance ID | Persona in one sentence | Load cost |
|
|
180
|
+
|---|---|---|---|
|
|
181
|
+
| `search_external` | `delegation-search-external` | Web research only (web_search + tavily), never modifies files; returns a structured research summary (findings, sources, open questions) | ~3.0k, only when delegated |
|
|
182
|
+
| `design` | `delegation-design` | Produces UI/UX designs, wireframes and frontend specs; may load design skills (frontend-design, html-ppt) via the skill tool; reads reference material first, delivers a spec, does not implement final code | ~3.2k, only when delegated |
|
|
183
|
+
| `implement` | `delegation-implement` | Writes and edits code: read surrounding context → surgical change → verify (build/test/grep); does not redesign architecture | ~3.7k, only when delegated |
|
|
184
|
+
| `search_internal_deep` | `delegation-search-internal-deep` | Repository exploration only: never modifies files, never uses the network; handles tasks too heavy for the main agent's context (whole-repo grep dumps, full-file reads beyond a page, cross-directory symbol/definition tracking); returns condensed summaries only | not separately estimated, only when delegated |
|
|
185
|
+
|
|
186
|
+
The table above is the factory default; the configuration layer can trim and customize it (see
|
|
187
|
+
[docs/configuration_en.md](configuration_en.md)): the trio `search_external` / `design` / `implement` can be
|
|
188
|
+
deleted wholesale via `roles_remove` (2.3; `search_internal_deep` cannot); a built-in role can carry a display
|
|
189
|
+
name `roles.<toolName>.name`, which rewrites only the subject of its persona identity sentence (2.4); it can
|
|
190
|
+
also be pinned to a dedicated model `roles.<toolName>.model` / `.provider`, injected by the generator as
|
|
191
|
+
`agentOptions` in the delegation block, defaulting to following the main agent (2.5); and the main agent itself
|
|
192
|
+
can be renamed via `main_agent_name` — one key, two places (section 5).
|
|
193
|
+
|
|
194
|
+
### Per-role toolFilter.allow detail
|
|
195
|
+
|
|
196
|
+
The allow lists below are transcribed verbatim from `agent.cordis.yml` (at install time the generator
|
|
197
|
+
rewrites each list as "config/static intent ∩ runtime-detected host tools", see
|
|
198
|
+
[docs/configuration_en.md](configuration_en.md)).
|
|
199
|
+
|
|
200
|
+
**`search_external` (external research, 10 entries)**
|
|
201
|
+
|
|
202
|
+
| Field | Value |
|
|
203
|
+
|---|---|
|
|
204
|
+
| Persona job | Web research only, returning a structured research summary (findings, sources, open questions); never modifies files |
|
|
205
|
+
| toolFilter.allow | `web_search`, `mcp__tavily__tavily_search`, `mcp__tavily__tavily_crawl`, `mcp__tavily__tavily_extract`, `mcp__tavily__tavily_map`, `mcp__tavily__tavily_research`, `glob`, `grep`, `read`, `ask_user_question` |
|
|
206
|
+
| Load cost | ~3.0k tokens, incurred only when web research actually runs |
|
|
207
|
+
|
|
208
|
+
**`design` (design, 13 entries)**
|
|
209
|
+
|
|
210
|
+
| Field | Value |
|
|
211
|
+
|---|---|
|
|
212
|
+
| Persona job | Produces UI/UX designs, wireframes and frontend specs; loads design skills (frontend-design, html-ppt) via the skill tool; reads reference material first, then delivers a spec; does not implement final code |
|
|
213
|
+
| toolFilter.allow | `read`, `read_image`, `glob`, `grep`, `bash`, `skill`, `write`, `edit`, `ask_user_question`, `todo_write`, `job_output`, `job_list`, `job_kill` |
|
|
214
|
+
| Load cost | ~3.2k tokens, incurred only when a design task is delegated |
|
|
215
|
+
|
|
216
|
+
**`implement` (implementation, 16 entries)**
|
|
217
|
+
|
|
218
|
+
| Field | Value |
|
|
219
|
+
|---|---|
|
|
220
|
+
| Persona job | Writes and edits code: read surrounding context first → surgical changes → verify (build/test/grep), reporting what changed and how it was verified; does not redesign architecture |
|
|
221
|
+
| toolFilter.allow | `read`, `write`, `edit`, `glob`, `grep`, `bash`, `skill`, `web_search`, `ask_user_question`, `todo_write`, `job_output`, `job_list`, `job_kill`, `get_goal`, `create_goal`, `update_goal` |
|
|
222
|
+
| Load cost | ~3.7k tokens, incurred only when an implementation task is delegated |
|
|
223
|
+
|
|
224
|
+
**`search_internal_deep` (whole-repo deep search, 10 entries)**
|
|
225
|
+
|
|
226
|
+
| Field | Value |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Persona job | Repository exploration only: never modifies files, never uses the network; handles whole-repo grep dumps, full-file reads beyond a page, and cross-directory symbol/definition tracking; searches thoroughly, then returns a CONCISE structured summary (what was searched, key files with line numbers, definitions/symbols found, and the takeaway for the next decision) — never echoes large dumps back |
|
|
229
|
+
| toolFilter.allow | `glob`, `grep`, `read`, `read_image`, `bash`, `ask_user_question`, `todo_write`, `job_output`, `job_list`, `job_kill` |
|
|
230
|
+
| Load cost | not separately estimated, incurred only when a deep repo search is delegated |
|
|
231
|
+
|
|
232
|
+
> Note: `codegraph` and `memory_search` were removed from this role on 2026-09-02 — when their plugins
|
|
233
|
+
> (codegraph MCP / magic-memory) are disabled they are outside the sub-agent's visible registry, and
|
|
234
|
+
> `tools.restrict()` would refuse to create the role with unknown tools. Re-add them on demand after
|
|
235
|
+
> re-enabling the plugins (allow lists use exact name matching, no globs — see the "Known limitations"
|
|
236
|
+
> section below).
|
|
237
|
+
|
|
238
|
+
## Under the hood
|
|
239
|
+
|
|
240
|
+
This preset invents no new mechanism — every layer maps onto a native DSH capability, and the preset merely
|
|
241
|
+
composes them. Each item below is stated as a design decision (the first release failed at runtime because
|
|
242
|
+
① was missing; that lesson is now a hard constraint of the composition).
|
|
243
|
+
|
|
244
|
+
**① Basic tools are registered at the preset layer; the orchestrator must compose the full standard
|
|
245
|
+
preset.** In DSH, base tools such as read/write/edit/glob/grep/bash/skill/web_search are **not provided by
|
|
246
|
+
the global layer** — they are mounted by the composition of the builtin `standard` preset. The orchestrator
|
|
247
|
+
composition must therefore include every row of `standard` verbatim — tool-bash / tool-pwsh / fs / fs-search
|
|
248
|
+
/ jobs / skill / goal / planning / compaction / delegation / ask-user / todo / web — otherwise a sub-agent's
|
|
249
|
+
`tools.restrict()` fails with `unknown global tools`. This is a hard design constraint: a sub-agent inherits
|
|
250
|
+
the tools **registered by the composition itself**, and a missing row means the tool is absent from the
|
|
251
|
+
sub-agent's registry. The host layer (e.g. `~/.dsh/cordis.patch.yml`) mounts only MCP servers and local
|
|
252
|
+
plugin tools.
|
|
253
|
+
|
|
254
|
+
**② The main-agent surface trim is a waterfall filter on the model-facing catalog.** `restrict.mjs` hooks
|
|
255
|
+
the `system-prompt/assemble` waterfall (`prepend: true` places the filter outermost, so `await next()`
|
|
256
|
+
observes the fully assembled downstream result before the allow list is applied to `assembly.tools`).
|
|
257
|
+
Design points:
|
|
258
|
+
|
|
259
|
+
- **Fail-open**: any unexpected error inside the filter returns the assembly unchanged, so a filter bug can
|
|
260
|
+
never brick every request of a session;
|
|
261
|
+
- **An empty allow list refuses to load**: `apply()` throws a `TypeError` when `config.allow` is empty —
|
|
262
|
+
preventing the main agent from being stripped of every tool (the install-time generator also rejects a
|
|
263
|
+
removal that results in an empty list).
|
|
264
|
+
|
|
265
|
+
**③ Main vs. child is distinguished by the session header.** A child session's header is stamped
|
|
266
|
+
`origin: 'subagent'` (`delegationDepth >= 1` as a fallback; see dsh-subagent's childSessionMeta). The
|
|
267
|
+
waterfall lets children through untouched — a role's surface is already constrained by its own `toolFilter`,
|
|
268
|
+
and re-filtering children here would intersect with (and wrongly shrink) those role surfaces.
|
|
269
|
+
|
|
270
|
+
**④ A role sub-agent's surface is a per-child `tools.restrict()`.** Each role is a `dsh-tool-subagent`
|
|
271
|
+
instance declaring a `toolName` (the tool name the main agent calls) and a `toolFilter.allow`. At child
|
|
272
|
+
creation the child context runs `childCtx.tools.restrict(toolFilter)`: every allow name must exist in the
|
|
273
|
+
sub-agent's visible registry (= this composition in full + host-layer MCP/plugins), otherwise creation is
|
|
274
|
+
rejected with unknown tools. Hence "which delegation tool the main agent calls selects which tool surface";
|
|
275
|
+
tools not explicitly allowed are entirely invisible to the child.
|
|
276
|
+
|
|
277
|
+
**⑤ Zeroing the main agent's skill catalog needs two layers.** Removing `skill` from the model-facing
|
|
278
|
+
surface alone is insufficient: the `dsh-tool-skill` catalog-injection guard checks **registry
|
|
279
|
+
visibility** (`ctx.tools.get('skill', agent)`), not the model-facing list, so a prompt-level filter cannot
|
|
280
|
+
keep the catalog out (measured: a 4455-byte / 10-skill catalog ≈1.3k tokens/round). Therefore:
|
|
281
|
+
|
|
282
|
+
- the `system-prompt/assemble` waterfall filters `skill` out of the main agent's model-facing catalog
|
|
283
|
+
(layer 1);
|
|
284
|
+
- `MAIN_AGENT_REGISTRY_DENY = ['skill']` applies a registry-level deny to the main agent at `agent/created`
|
|
285
|
+
(layer 2); the catalog injection then stops and its cost drops to zero;
|
|
286
|
+
- children are exempt: their surface comes from each delegation's `toolFilter`, and per-agent restrictions
|
|
287
|
+
never cross scope layers (a child scope is a sibling under the shared standing mount, not a descendant of
|
|
288
|
+
the main agent's scope);
|
|
289
|
+
- design/implement and other sub-agents keep `skill` — DSH has no per-agent catalog filtering; catalogs are
|
|
290
|
+
collected by cwd and fully visible to sub-agents, guided by persona text. Explicitly whitelisting `skill`
|
|
291
|
+
in the main agent's allow list opts it back into the catalog (the registry deny is skipped accordingly).
|
|
292
|
+
|
|
293
|
+
**⑥ Role personas are the `persona` field of `dsh-tool-subagent`.** Each delegation instance carries its
|
|
294
|
+
own persona (the child agent's deployment:persona section), including the role boundary and a failure
|
|
295
|
+
reporting protocol; the main-agent persona additionally carries the delegation-failure SOP (branching on
|
|
296
|
+
stop reason: `error` → re-delegate once; `max-tokens` → split the task; `refusal` → do not retry; `aborted`
|
|
297
|
+
→ retry only if the cancellation was accidental; poor output → re-delegate with concrete gaps; after two
|
|
298
|
+
failures of the same task, stop and report to the user; every re-delegation must carry the previous
|
|
299
|
+
integration summary) — see [docs/orchestration_en.md](orchestration_en.md).
|
|
300
|
+
|
|
301
|
+
**⑦ Hot-swap means presets are static directories.** Install, uninstall and modification are plain file
|
|
302
|
+
operations: the generator copies/rewrites the static sources into `$DSH_HOME/.agent-presets/orchestrator`;
|
|
303
|
+
editing the YAML / mjs, re-running the generator, and restarting the host or opening a new session takes
|
|
304
|
+
effect. Uninstalling means deleting the directory. No DSH source changes; the preset leaves nothing behind
|
|
305
|
+
when removed.
|
|
306
|
+
|
|
307
|
+
## Performance: token governance
|
|
308
|
+
|
|
309
|
+
This section presents long-session token governance as a benchmark-style write-up: symptom (measurement) →
|
|
310
|
+
root cause → landed configuration → tunable parameters.
|
|
311
|
+
|
|
312
|
+
**Symptom and baseline (calibrated 2026-08-20).** In the same working directory with equivalent tasks, DSH's
|
|
313
|
+
average tokens per request in long sessions was ~2× that of a baseline implementation. After ruling out
|
|
314
|
+
output-truncation accounting differences (read defaults of 2000 lines plus a 50KB byte cap, bash tail+spill
|
|
315
|
+
to disk, grep inline caps — all same design as the baseline), the root cause was pinned down: **compaction
|
|
316
|
+
never fired with the default settings**. `dsh-compaction-basic` defaults to `thresholdRatio: 0.8` — inside
|
|
317
|
+
a 256K-token window compaction would only trigger at ≈209,715 tokens — so per-request tokens in long
|
|
318
|
+
sessions grew to a ~137K–142K plateau that never fell back (the level stayed below the trigger line).
|
|
319
|
+
Sessions on the baseline implementation for the same project had all undergone periodic compaction, with
|
|
320
|
+
history periodically compressed back down.
|
|
321
|
+
|
|
322
|
+
**Landed configuration.** The compaction-basic row in `agent.cordis.yml` now sets
|
|
323
|
+
`thresholdRatio: 0.45` (triggering at ≈117,965 tokens), with retainRatio keeping the default ~16% tail
|
|
324
|
+
(≈42K) plus a summary checkpoint; the steady-state average falls to the ~80K range. Each compaction run
|
|
325
|
+
performs one LLM summarization call that reuses the prefix cache, so its cost is negligible — triggering
|
|
326
|
+
somewhat more often costs almost nothing extra; 0.45 is the balance point between saving tokens and not
|
|
327
|
+
summarizing too frequently.
|
|
328
|
+
|
|
329
|
+
**Parameters and effect.**
|
|
330
|
+
|
|
331
|
+
| thresholdRatio | Trigger line (≈) | Steady-state average (≈) | Notes |
|
|
332
|
+
|---|---|---|---|
|
|
333
|
+
| 0.8 (DSH default) | 209,715 tokens (256K window) | 137K–142K, never falls back | Long sessions rarely compact |
|
|
334
|
+
| 0.45 (landed in this preset) | 117,965 tokens | ~80K | ~16% tail ≈42K + summary checkpoint |
|
|
335
|
+
| 0.35 (optional, more aggressive) | ~92K tokens | ~67K | Saves more tokens, summarizes more often |
|
|
336
|
+
|
|
337
|
+
After changing the threshold, re-run `./install.sh --auto` to apply. Companion components in the same
|
|
338
|
+
composition:
|
|
339
|
+
|
|
340
|
+
- `tool-result-pruner`: oversized tool results are trimmed with `thresholdChars: 8192 / headChars: 4096 /
|
|
341
|
+
tailChars: 1024` (keep head and tail), preventing a single giant output from inflating the window;
|
|
342
|
+
- `command-compact`: manual `/compact` for immediate compression at any point in a long session;
|
|
343
|
+
- the `search_internal_deep` role: whole-repo dumps and other large outputs are isolated out of the main
|
|
344
|
+
context — the hot path (single-file reads, precise greps) still runs on the main agent with zero
|
|
345
|
+
delegation latency; only whole-repo dumps / full-file reads / cross-directory symbol tracking are
|
|
346
|
+
delegated, and the child digests large outputs internally, returning condensed summaries only (see the
|
|
347
|
+
"Roles & tool surfaces" section above).
|
|
348
|
+
|
|
349
|
+
## Known limitations
|
|
350
|
+
|
|
351
|
+
- **Role tool surfaces are statically bound** (decided by configuration / `toolFilter.allow`), not chosen
|
|
352
|
+
arbitrarily by the model at runtime. "Picking a role tool picks a tool surface" is an advantage for fixed
|
|
353
|
+
division of labor, but a tool cannot be granted to a role mid-conversation — changing it requires editing
|
|
354
|
+
the configuration / static source and re-running the generator.
|
|
355
|
+
- **`toolFilter.allow` uses exact name matching and supports no globs**: the five tavily tools must be
|
|
356
|
+
listed in full (`mcp__tavily__tavily_search/crawl/extract/map/research`).
|
|
357
|
+
- **Tools absent from the main-agent whitelist raise no error**: the waterfall filter only matches by name
|
|
358
|
+
against the resolved `assembly.tools`, so a tool that is not mounted (e.g. a commented-out MCP) simply
|
|
359
|
+
never appears — install and runtime both pass silently. Config intent and actual mounts are reconciled by
|
|
360
|
+
install-time detection (`install.sh --auto` / `--dry-run`); re-run it after changing host patches.
|
|
361
|
+
- **Whole-repo exploration tasks should be delegated to `search_internal_deep` rather than done by the
|
|
362
|
+
main agent**: whole-repo grep dumps and full-file reads that repeatedly flood the main context are
|
|
363
|
+
exactly what that role isolates (read-only, no network, condensed summaries only).
|