dsh-root-planner-preset 1.4.0 → 1.4.2
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/build-patch.mjs +18 -8
- package/child-tools.mjs +19 -5
- package/cordis.patch.yml +18 -8
- package/package.json +1 -3
- package/verify-depth-override.mjs +13 -0
- package/verify-patch.mjs +79 -94
- package/worker-mask.js +0 -30
package/build-patch.mjs
CHANGED
|
@@ -177,7 +177,12 @@ const patch = `# Generated by build-patch.mjs from persona.txt. Do not hand-edit
|
|
|
177
177
|
config:
|
|
178
178
|
provider: fork
|
|
179
179
|
toolName: subagent_fork
|
|
180
|
-
agentPreset
|
|
180
|
+
# No agentPreset, on purpose. The fork provider declares
|
|
181
|
+
# inheritsParentContext true and agentPreset false, and configuring a
|
|
182
|
+
# preset for it makes the row throw at activation — which is exactly how
|
|
183
|
+
# the first attempt at this migration made the whole preset fail to load.
|
|
184
|
+
# A forked child inherits its parent's preset: from a worker that is
|
|
185
|
+
# still the worker preset, from the root it is the root preset.
|
|
181
186
|
- id: tool-subagent-codex
|
|
182
187
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
183
188
|
disabled: true
|
|
@@ -231,10 +236,17 @@ const patch = `# Generated by build-patch.mjs from persona.txt. Do not hand-edit
|
|
|
231
236
|
# default persona instead of this preset's.
|
|
232
237
|
#
|
|
233
238
|
# The worker preset carries the same persona (it addresses both roles) and only the tool
|
|
234
|
-
# plugins a worker needs.
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
239
|
+
# plugins a worker needs. What the worker can call is decided HERE, by which rows exist:
|
|
240
|
+
# in 0.2.1 a preset's rows register their tools per agent scope, and tools.restrict()
|
|
241
|
+
# filters GLOBAL tools only — "restrictions intersect; scoped registrations remain
|
|
242
|
+
# visible". A restriction therefore cannot narrow a preset's own tools, which is why an
|
|
243
|
+
# earlier attempt on this branch mounted a mask row and failed with
|
|
244
|
+
# tools.restrict() names unknown global tools "read", "glob", "write", ...
|
|
245
|
+
# known global tools: ops_glob, ops_grep, ops_inspect_local_file, ops_replace,
|
|
246
|
+
# working_directory
|
|
247
|
+
# That short list is the whole set of globally mounted tools on this machine; every other
|
|
248
|
+
# tool a worker sees comes from a row below. So the curated set is the row list itself,
|
|
249
|
+
# and omitting a row really does remove its tools.
|
|
238
250
|
- insert:
|
|
239
251
|
- id: preset-root-planner-worker
|
|
240
252
|
name: '@deepseek-ai/dsh-agent-preset'
|
|
@@ -254,8 +266,6 @@ const patch = `# Generated by build-patch.mjs from persona.txt. Do not hand-edit
|
|
|
254
266
|
name: '@deepseek-ai/dsh-agent-instructions'
|
|
255
267
|
config:
|
|
256
268
|
maxBytes: 65536
|
|
257
|
-
- id: worker-mask
|
|
258
|
-
name: 'dsh-root-planner-preset/worker-mask'
|
|
259
269
|
- id: tool-bash
|
|
260
270
|
name: '@deepseek-ai/dsh-tool-bash'
|
|
261
271
|
disabled: !!js process.platform === 'win32'
|
|
@@ -319,7 +329,7 @@ const patch = `# Generated by build-patch.mjs from persona.txt. Do not hand-edit
|
|
|
319
329
|
config:
|
|
320
330
|
provider: fork
|
|
321
331
|
toolName: subagent_fork
|
|
322
|
-
|
|
332
|
+
# Inherits its parent's preset; see the note on the root preset's fork row.
|
|
323
333
|
- id: tool-ask-user
|
|
324
334
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
325
335
|
- id: tool-todo
|
package/child-tools.mjs
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
*
|
|
2
|
+
* The expected worker toolset — the list this bundle wants a subagent to end up with, kept
|
|
3
|
+
* in one place so the intent is stated once.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* ## It is no longer a runtime mask, and cannot be one
|
|
6
|
+
*
|
|
7
|
+
* Until DSH 0.2.0 this list was handed to `dsh-tool-subagent` as `toolFilter.allow`.
|
|
8
|
+
* 0.2.1 removed that field: zod strips it silently and the child then runs the platform's
|
|
9
|
+
* default preset. The replacement the error message names is a preset (`agentPreset`), so
|
|
10
|
+
* what a worker can call is decided by which rows the `root-planner-worker` preset mounts.
|
|
11
|
+
*
|
|
12
|
+
* A `tools.restrict()` stand-in was tried and cannot work either: a preset's rows register
|
|
13
|
+
* their tools in the agent scope, and a restriction filters GLOBAL tools only
|
|
14
|
+
* ("restrictions intersect; scoped registrations remain visible"). On this machine the
|
|
15
|
+
* globally mounted set is just `ops_glob`, `ops_grep`, `ops_inspect_local_file`,
|
|
16
|
+
* `ops_replace` and `working_directory`; everything else a worker sees comes from a row.
|
|
17
|
+
*
|
|
18
|
+
* So this file is the documented expectation, verified against a live session rather than
|
|
19
|
+
* asserted against a snapshot: `verify-patch.mjs` proves the worker preset's row list, and
|
|
20
|
+
* `tools/extract-offered-tools.mjs` reads what a real child was actually offered.
|
|
7
21
|
*
|
|
8
22
|
* ## What a subagent MAY do
|
|
9
23
|
*
|
|
@@ -12,7 +26,7 @@
|
|
|
12
26
|
* project cannot run tests, git, or a verification script, so every executable
|
|
13
27
|
* step bounces back to the root and the parallelism the tree exists for buys
|
|
14
28
|
* nothing. `write`, `edit`, `ops_replace`, `ops_bash` and `pwsh` are all in the
|
|
15
|
-
*
|
|
29
|
+
* list for that reason.
|
|
16
30
|
*
|
|
17
31
|
* ## What is forbidden, and how it is enforced
|
|
18
32
|
*
|
package/cordis.patch.yml
CHANGED
|
@@ -194,7 +194,12 @@
|
|
|
194
194
|
config:
|
|
195
195
|
provider: fork
|
|
196
196
|
toolName: subagent_fork
|
|
197
|
-
agentPreset
|
|
197
|
+
# No agentPreset, on purpose. The fork provider declares
|
|
198
|
+
# inheritsParentContext true and agentPreset false, and configuring a
|
|
199
|
+
# preset for it makes the row throw at activation — which is exactly how
|
|
200
|
+
# the first attempt at this migration made the whole preset fail to load.
|
|
201
|
+
# A forked child inherits its parent's preset: from a worker that is
|
|
202
|
+
# still the worker preset, from the root it is the root preset.
|
|
198
203
|
- id: tool-subagent-codex
|
|
199
204
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
200
205
|
disabled: true
|
|
@@ -248,10 +253,17 @@
|
|
|
248
253
|
# default persona instead of this preset's.
|
|
249
254
|
#
|
|
250
255
|
# The worker preset carries the same persona (it addresses both roles) and only the tool
|
|
251
|
-
# plugins a worker needs.
|
|
252
|
-
#
|
|
253
|
-
#
|
|
254
|
-
#
|
|
256
|
+
# plugins a worker needs. What the worker can call is decided HERE, by which rows exist:
|
|
257
|
+
# in 0.2.1 a preset's rows register their tools per agent scope, and tools.restrict()
|
|
258
|
+
# filters GLOBAL tools only — "restrictions intersect; scoped registrations remain
|
|
259
|
+
# visible". A restriction therefore cannot narrow a preset's own tools, which is why an
|
|
260
|
+
# earlier attempt on this branch mounted a mask row and failed with
|
|
261
|
+
# tools.restrict() names unknown global tools "read", "glob", "write", ...
|
|
262
|
+
# known global tools: ops_glob, ops_grep, ops_inspect_local_file, ops_replace,
|
|
263
|
+
# working_directory
|
|
264
|
+
# That short list is the whole set of globally mounted tools on this machine; every other
|
|
265
|
+
# tool a worker sees comes from a row below. So the curated set is the row list itself,
|
|
266
|
+
# and omitting a row really does remove its tools.
|
|
255
267
|
- insert:
|
|
256
268
|
- id: preset-root-planner-worker
|
|
257
269
|
name: '@deepseek-ai/dsh-agent-preset'
|
|
@@ -342,8 +354,6 @@
|
|
|
342
354
|
name: '@deepseek-ai/dsh-agent-instructions'
|
|
343
355
|
config:
|
|
344
356
|
maxBytes: 65536
|
|
345
|
-
- id: worker-mask
|
|
346
|
-
name: 'dsh-root-planner-preset/worker-mask'
|
|
347
357
|
- id: tool-bash
|
|
348
358
|
name: '@deepseek-ai/dsh-tool-bash'
|
|
349
359
|
disabled: !!js process.platform === 'win32'
|
|
@@ -407,7 +417,7 @@
|
|
|
407
417
|
config:
|
|
408
418
|
provider: fork
|
|
409
419
|
toolName: subagent_fork
|
|
410
|
-
|
|
420
|
+
# Inherits its parent's preset; see the note on the root preset's fork row.
|
|
411
421
|
- id: tool-ask-user
|
|
412
422
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
413
423
|
- id: tool-todo
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-root-planner-preset",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DSH agent preset: the root agent plans and decides, subagent workers read, write and run commands inside their own _agents/ workspace, and nothing outlives its task.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,12 +8,10 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./guard.js",
|
|
10
10
|
"./guard": "./guard.js",
|
|
11
|
-
"./worker-mask": "./worker-mask.js",
|
|
12
11
|
"./package.json": "./package.json"
|
|
13
12
|
},
|
|
14
13
|
"files": [
|
|
15
14
|
"guard.js",
|
|
16
|
-
"worker-mask.js",
|
|
17
15
|
"persona.txt",
|
|
18
16
|
"child-tools.mjs",
|
|
19
17
|
"build-patch.mjs",
|
|
@@ -74,6 +74,19 @@ check(depthPatch?.insert === undefined, 'override is not an insert patch');
|
|
|
74
74
|
// whenever the profile re-composes — so its content is the evidence that the
|
|
75
75
|
// edit was picked up rather than merely written.
|
|
76
76
|
const composed = yaml.load(readFileSync(COMPOSED, 'utf8'), { schema: loaderSchema });
|
|
77
|
+
// `cordis.yml` is a runtime cache, not a source: its own header calls it "an empty entry
|
|
78
|
+
// list" and says the tree is composed from patches at startup. An empty file therefore
|
|
79
|
+
// means the harness has not rewritten the composition yet on this machine, not that the
|
|
80
|
+
// override is missing. Treating it as failure produced four red checks after a
|
|
81
|
+
// `--dump-config` reset the file — a red that said nothing about the preset.
|
|
82
|
+
if (!Array.isArray(composed) || composed.length === 0) {
|
|
83
|
+
process.stdout.write(
|
|
84
|
+
'SKIP the composed-profile checks: cordis.yml still holds the empty template that the\n' +
|
|
85
|
+
'harness rewrites at startup. Start dsh once, then re-run this script.\n',
|
|
86
|
+
);
|
|
87
|
+
process.stdout.write(failures.length === 0 ? '\nALL CHECKS PASSED\n' : `\n${failures.length} CHECK(S) FAILED\n`);
|
|
88
|
+
process.exit(failures.length === 0 ? 0 : 1);
|
|
89
|
+
}
|
|
77
90
|
const subagentRows = composed.filter((row) => row.id === 'subagent');
|
|
78
91
|
check(subagentRows.length === 1, `exactly one subagent row in the composition (found ${subagentRows.length})`);
|
|
79
92
|
check(subagentRows[0]?.name === '@deepseek-ai/dsh-subagent', 'the composition row matches the asserted name');
|
package/verify-patch.mjs
CHANGED
|
@@ -144,43 +144,99 @@ check(existsSync(join(here, 'guard.js')), 'guard.js ships inside the package');
|
|
|
144
144
|
}
|
|
145
145
|
const delegationRows = delegation?.config ?? [];
|
|
146
146
|
|
|
147
|
-
// DSH 0.2.1 removed `toolFilter` from the delegation tool
|
|
148
|
-
// the key and the child then runs the platform's DEFAULT preset
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
147
|
+
// DSH 0.2.1 removed `toolFilter` from the delegation tool, and it does not warn: zod
|
|
148
|
+
// strips the key and the child then runs the platform's DEFAULT preset. The worker's tool
|
|
149
|
+
// set is now decided by which rows the worker preset mounts, because a preset's rows
|
|
150
|
+
// register their tools in the agent scope while `tools.restrict()` filters GLOBAL tools
|
|
151
|
+
// only ("restrictions intersect; scoped registrations remain visible"). A mask row was
|
|
152
|
+
// tried first and failed at activation with:
|
|
153
|
+
// tools.restrict() names unknown global tools "read", "glob", "write", ...
|
|
154
|
+
// known global tools: ops_glob, ops_grep, ops_inspect_local_file, ops_replace,
|
|
155
|
+
// working_directory
|
|
156
|
+
// Those five are the entire global set on this machine; everything else a worker sees
|
|
157
|
+
// comes from a row. So the assertions below are about rows, and they are two-sided:
|
|
158
|
+
// required rows must exist, forbidden rows must not.
|
|
152
159
|
const workerRow = inserted.find((row) => row.id === 'preset-root-planner-worker');
|
|
153
160
|
check(workerRow !== undefined, 'the patch declares a worker preset');
|
|
154
161
|
check(workerRow?.config?.id === 'root-planner-worker', 'the worker preset id is root-planner-worker');
|
|
155
162
|
check(workerRow?.config?.order === 6, 'the worker preset order is 6');
|
|
156
163
|
const workerPlugins = workerRow?.config?.plugins ?? [];
|
|
164
|
+
const workerRowIds = new Set(workerPlugins.map((row) => row.id));
|
|
165
|
+
|
|
166
|
+
/** Rows a worker needs: persona, the working tools, and delegation so the tree recurses. */
|
|
167
|
+
const WORKER_REQUIRED_ROWS = [
|
|
168
|
+
'persona',
|
|
169
|
+
'agent-instructions',
|
|
170
|
+
'tool-pwsh',
|
|
171
|
+
'tool-fs',
|
|
172
|
+
'tool-fs-search',
|
|
173
|
+
'tool-skill',
|
|
174
|
+
'tool-web',
|
|
175
|
+
'tool-todo',
|
|
176
|
+
'tool-ask-user',
|
|
177
|
+
'delegation',
|
|
178
|
+
];
|
|
179
|
+
for (const id of WORKER_REQUIRED_ROWS) {
|
|
180
|
+
check(workerRowIds.has(id), `the worker preset mounts ${id}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Rows a worker must not have. Each one was in the root preset and not here: workflow
|
|
185
|
+
* orchestration, deliverables, job control, plan mode, plugin management. Anything that
|
|
186
|
+
* lets a worker outlive its task or re-plan the tree belongs to the root.
|
|
187
|
+
*/
|
|
188
|
+
const WORKER_FORBIDDEN_ROWS = ['tool-workflow', 'workflow-ptc', 'present', 'tool-jobs', 'tool-plugin-manager', 'planning'];
|
|
189
|
+
for (const id of WORKER_FORBIDDEN_ROWS) {
|
|
190
|
+
check(!workerRowIds.has(id), `the worker preset does not mount ${id}`);
|
|
191
|
+
}
|
|
157
192
|
check(
|
|
158
|
-
workerPlugins.some((row) =>
|
|
159
|
-
'
|
|
193
|
+
!workerPlugins.some((row) => /worker-mask/.test(String(row?.id)) || /worker-mask/.test(String(row?.name))),
|
|
194
|
+
'no mask row is mounted (a restriction cannot narrow a preset\'s own tools)',
|
|
160
195
|
);
|
|
196
|
+
|
|
161
197
|
const workerDelegation = workerPlugins.find((row) => row.id === 'delegation');
|
|
162
198
|
const workerDelegationRows = workerDelegation?.config ?? [];
|
|
163
199
|
|
|
164
|
-
// Loader row id -> the model-facing tool name that row publishes
|
|
200
|
+
// Loader row id -> the model-facing tool name that row publishes, plus the provider whose
|
|
201
|
+
// capability decides whether a preset may be selected for it.
|
|
165
202
|
const childTools = [
|
|
166
|
-
{ id: 'tool-subagent', toolName: 'subagent' },
|
|
167
|
-
{ id: 'tool-subagent-fork', toolName: 'subagent_fork' },
|
|
168
|
-
].map(({ id, toolName }) => {
|
|
203
|
+
{ id: 'tool-subagent', toolName: 'subagent', provider: 'spawn' },
|
|
204
|
+
{ id: 'tool-subagent-fork', toolName: 'subagent_fork', provider: 'fork' },
|
|
205
|
+
].map(({ id, toolName, provider }) => {
|
|
169
206
|
const row = delegationRows.find((entry) => entry.id === id);
|
|
170
207
|
check(row !== undefined, `delegation declares ${id}`);
|
|
171
208
|
check(row?.config?.toolName === toolName, `${id} publishes the tool name ${toolName}`);
|
|
172
|
-
check(row?.config?.
|
|
209
|
+
check(row?.config?.provider === provider, `${id} uses the ${provider} provider`);
|
|
173
210
|
check(
|
|
174
211
|
row?.config?.toolFilter === undefined,
|
|
175
212
|
`${id} carries no toolFilter (DSH 0.2.1 removed it and strips it silently)`,
|
|
176
213
|
);
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
214
|
+
// Only the spawn provider supports independent preset selection. A grandchild spawned
|
|
215
|
+
// through it must land in the worker preset too, or the tree degrades at depth 2: the
|
|
216
|
+
// platform default persona and the full tool list come back one level down.
|
|
217
|
+
if (provider === 'spawn') {
|
|
218
|
+
check(row?.config?.agentPreset === 'root-planner-worker', `${id} spawns into the worker preset`);
|
|
219
|
+
const nested = workerDelegationRows.find((entry) => entry.id === id);
|
|
220
|
+
check(nested?.config?.agentPreset === 'root-planner-worker', `worker ${id} spawns into the worker preset too`);
|
|
221
|
+
}
|
|
222
|
+
return { id, toolName, provider };
|
|
182
223
|
});
|
|
183
224
|
|
|
225
|
+
// The fork provider declares `agentPreset: false` together with
|
|
226
|
+
// `inheritsParentContext: true`, and its row throws at activation when a preset is
|
|
227
|
+
// configured for it — which is what made the whole preset fail to load on DSH 0.2.1.
|
|
228
|
+
// Assert the absence in both presets so a later edit cannot reintroduce it.
|
|
229
|
+
for (const [label, rows] of [
|
|
230
|
+
['the root preset', delegationRows],
|
|
231
|
+
['the worker preset', workerDelegationRows],
|
|
232
|
+
]) {
|
|
233
|
+
const fork = rows.find((entry) => entry.id === 'tool-subagent-fork');
|
|
234
|
+
check(
|
|
235
|
+
fork?.config?.agentPreset === undefined,
|
|
236
|
+
`${label} sets no agentPreset on the fork row (the fork provider rejects one)`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
184
240
|
// A subagent is a worker: the curated set keeps the write and shell tools.
|
|
185
241
|
for (const worker of ['read', 'write', 'edit', 'ops_replace', 'pwsh']) {
|
|
186
242
|
check(CHILD_TOOLS.includes(worker), `the curated set keeps ${worker} (a subagent must be able to work)`);
|
|
@@ -207,83 +263,12 @@ for (const [label, rows] of [
|
|
|
207
263
|
);
|
|
208
264
|
}
|
|
209
265
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
* Provenance: transcribed on DSH 0.2.1-rc.2. The upgrade from 0.2.0-rc.2 added
|
|
217
|
-
* `working_directory`, the four `schedule_*` tools, `cordis_inspect_list`,
|
|
218
|
-
* `cordis_inspect_query` and `plugin_manager`; all are registered by profile-level tool
|
|
219
|
-
* packages, so they are global. None of them is in the child mask, which is the intended
|
|
220
|
-
* behaviour of an allow list — a new tool is invisible to children until it is named.
|
|
221
|
-
*
|
|
222
|
-
* Regenerate after a DSH upgrade: break the mask on purpose (add a bogus name), let the
|
|
223
|
-
* agent-creation error print the list, and transcribe it — `tools/extract-tool-inventory.mjs`
|
|
224
|
-
* reads exactly that line out of a session log.
|
|
225
|
-
*/
|
|
226
|
-
const OBSERVED_GLOBAL_TOOLS = new Set([
|
|
227
|
-
'ask_user_question',
|
|
228
|
-
'cordis_inspect_list',
|
|
229
|
-
'cordis_inspect_query',
|
|
230
|
-
'create_goal',
|
|
231
|
-
'edit',
|
|
232
|
-
'exit_plan_mode',
|
|
233
|
-
'get_goal',
|
|
234
|
-
'glob',
|
|
235
|
-
'grep',
|
|
236
|
-
'interrupt_agent',
|
|
237
|
-
'job_kill',
|
|
238
|
-
'job_list',
|
|
239
|
-
'job_output',
|
|
240
|
-
'list_agents',
|
|
241
|
-
'ops_glob',
|
|
242
|
-
'ops_grep',
|
|
243
|
-
'ops_inspect_local_file',
|
|
244
|
-
'ops_replace',
|
|
245
|
-
'plugin_manager',
|
|
246
|
-
'present',
|
|
247
|
-
'pwsh',
|
|
248
|
-
'read',
|
|
249
|
-
'read_image',
|
|
250
|
-
'schedule_create',
|
|
251
|
-
'schedule_delete',
|
|
252
|
-
'schedule_list',
|
|
253
|
-
'schedule_update',
|
|
254
|
-
'send_message',
|
|
255
|
-
'skill',
|
|
256
|
-
'subagent_fork',
|
|
257
|
-
'todo_write',
|
|
258
|
-
'update_goal',
|
|
259
|
-
'web_fetch',
|
|
260
|
-
'web_search',
|
|
261
|
-
'workflow',
|
|
262
|
-
'working_directory',
|
|
263
|
-
'write',
|
|
264
|
-
]);
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Tools that are agent-scoped rather than global, so `tools.restrict()` can never name
|
|
268
|
-
* them. `subagent` is the one this bundle ran into: with `modelSelectionSettings: true`
|
|
269
|
-
* it is mounted inside each composed Agent's own scope, and a restriction accepts global
|
|
270
|
-
* names only. The curated set is the source of truth now — the old per-call mask is gone.
|
|
271
|
-
*/
|
|
272
|
-
{
|
|
273
|
-
const unknown = CHILD_TOOLS.filter((name) => !OBSERVED_GLOBAL_TOOLS.has(name));
|
|
274
|
-
const scoped = new Set(SCOPE_LOCAL_TOOLS);
|
|
275
|
-
const unexpected = unknown.filter((name) => !scoped.has(name));
|
|
276
|
-
check(
|
|
277
|
-
unexpected.length === 0,
|
|
278
|
-
`the curated set names only tools the runtime reports as global` +
|
|
279
|
-
(unexpected.length ? ` (not global: ${unexpected.join(', ')})` : ''),
|
|
280
|
-
);
|
|
281
|
-
// `subagent` reaches a worker through the preset's own delegation row rather than the
|
|
282
|
-
// restriction, so it may legitimately sit in the curated set while being scope-local.
|
|
283
|
-
for (const name of unknown) {
|
|
284
|
-
check(scoped.has(name), `the only scope-local name in the curated set is declared as such (${name})`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
266
|
+
// The curated child toolset is not checkable against a static global inventory, and this
|
|
267
|
+
// file must not pretend otherwise. In DSH 0.2.1 a preset row registers its tool in the
|
|
268
|
+
// agent scope, and `tools.restrict()` filters GLOBAL tools only, so the names in
|
|
269
|
+
// `child-tools.mjs` are not global names. What proves the toolset is the row list —
|
|
270
|
+
// asserted above — plus one live run: spawn from a Root Planner session and read the
|
|
271
|
+
// child's `request/header` with `node tools/extract-offered-tools.mjs <session-dir>`.
|
|
287
272
|
|
|
288
273
|
process.stdout.write(failures.length === 0 ? '\nALL CHECKS PASSED\n' : `\n${failures.length} CHECK(S) FAILED\n`);
|
|
289
274
|
process.exit(failures.length === 0 ? 0 : 1);
|
package/worker-mask.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Restricts a Root Planner worker to the curated child toolset.
|
|
2
|
-
//
|
|
3
|
-
// DSH 0.2.1 removed `toolFilter` from `dsh-tool-subagent` — its error says to "configure
|
|
4
|
-
// persona and tool plugins in an Agent Preset and select it with agentPreset (spawn
|
|
5
|
-
// only)". A preset decides which tool plugins are mounted for that agent, but not which
|
|
6
|
-
// globally mounted tools it may call: several newer tools (tool-cordis, plugin-manager,
|
|
7
|
-
// working-directory, schedule) are registered at the app level and would reach every
|
|
8
|
-
// agent regardless of the preset's rows. `tools.restrict()` is the mechanism that still
|
|
9
|
-
// subtracts them, and this module is that call, mounted as a row inside the worker
|
|
10
|
-
// preset so it applies to the worker's own agent scope.
|
|
11
|
-
//
|
|
12
|
-
// The allow list lives in `child-tools.mjs` — the same file the previous mask used, so
|
|
13
|
-
// the curated set is still declared once.
|
|
14
|
-
|
|
15
|
-
import { CHILD_TOOLS } from './child-tools.mjs'
|
|
16
|
-
|
|
17
|
-
export const name = 'root-planner-worker-mask'
|
|
18
|
-
|
|
19
|
-
export const inject = ['tools']
|
|
20
|
-
|
|
21
|
-
export function apply(ctx) {
|
|
22
|
-
// `tools.restrict()` requires a scoped context: it refuses to run from a global one
|
|
23
|
-
// because that would mask every agent in the process. A preset row is mounted into the
|
|
24
|
-
// agent's own scope, which is exactly the scope this needs.
|
|
25
|
-
ctx.effect(() => {
|
|
26
|
-
const tools = typeof ctx.get === 'function' ? ctx.get('tools') : ctx.tools
|
|
27
|
-
if (tools === undefined) return () => {}
|
|
28
|
-
return tools.restrict({ allow: CHILD_TOOLS })
|
|
29
|
-
})
|
|
30
|
-
}
|