dsh-root-planner-preset 1.3.0 → 1.3.1

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 CHANGED
@@ -226,17 +226,21 @@ const patch = `# Generated by build-patch.mjs from persona.txt. Do not hand-edit
226
226
  - id: tool-plugin-manager
227
227
  name: '@deepseek-ai/dsh-plugin-manager/tools'
228
228
  disabled: true
229
- # The workspace fence. Mounted as a preset row so one install carries the
230
- # protocol and its enforcement together: the persona tells a child to write
231
- # under its own _agents/ directory, and this refuses anything else before
232
- # the call runs. It is preset-scoped rather than a separate profile bundle
233
- # because the _agents/ convention only exists here — a guard without this
234
- # persona would fence agents that were never told where they may write.
235
- - id: root-planner-guard
236
- name: 'dsh-root-planner-preset/guard'
237
- config:
238
- directoryName: _agents
239
- guardShell: true
229
+
230
+ # The workspace fence travels in the same package, but as a SIBLING entry rather than a
231
+ # row inside the preset above. A preset row is mounted per agent, and measured against a
232
+ # live session that mount never took effect: six probe steps (write outside, .. escape,
233
+ # shell without workdir, shell absolute path, plus the two allowed controls) all came
234
+ # back allowed. As a profile-level entry — the shape the standalone guard bundle was
235
+ # proved in — one instance sees every call and decides from the calling agent's session
236
+ # header, which is all this guard needs. Shipping it here still means one install carries
237
+ # the protocol and its enforcement together.
238
+ - insert:
239
+ - id: root-planner-guard
240
+ name: 'dsh-root-planner-preset/guard'
241
+ config:
242
+ directoryName: _agents
243
+ guardShell: true
240
244
  `;
241
245
 
242
246
  // Substitution happens after the template so the emitted indentation is owned by
package/cordis.patch.yml CHANGED
@@ -289,14 +289,18 @@
289
289
  - id: tool-plugin-manager
290
290
  name: '@deepseek-ai/dsh-plugin-manager/tools'
291
291
  disabled: true
292
- # The workspace fence. Mounted as a preset row so one install carries the
293
- # protocol and its enforcement together: the persona tells a child to write
294
- # under its own _agents/ directory, and this refuses anything else before
295
- # the call runs. It is preset-scoped rather than a separate profile bundle
296
- # because the _agents/ convention only exists here — a guard without this
297
- # persona would fence agents that were never told where they may write.
298
- - id: root-planner-guard
299
- name: 'dsh-root-planner-preset/guard'
300
- config:
301
- directoryName: _agents
302
- guardShell: true
292
+
293
+ # The workspace fence travels in the same package, but as a SIBLING entry rather than a
294
+ # row inside the preset above. A preset row is mounted per agent, and measured against a
295
+ # live session that mount never took effect: six probe steps (write outside, .. escape,
296
+ # shell without workdir, shell absolute path, plus the two allowed controls) all came
297
+ # back allowed. As a profile-level entry — the shape the standalone guard bundle was
298
+ # proved in — one instance sees every call and decides from the calling agent's session
299
+ # header, which is all this guard needs. Shipping it here still means one install carries
300
+ # the protocol and its enforcement together.
301
+ - insert:
302
+ - id: root-planner-guard
303
+ name: 'dsh-root-planner-preset/guard'
304
+ config:
305
+ directoryName: _agents
306
+ guardShell: true
package/guard.js CHANGED
@@ -170,8 +170,17 @@ export function apply(ctx, config = {}) {
170
170
  // Registered on the plugin's own context: a plain-context guard applies globally,
171
171
  // and the decision is made per call from `exec.agent`, so no per-agent wiring is
172
172
  // needed and no registration leaks when an agent is disposed.
173
+ //
174
+ // The service is read with `ctx.get` rather than `ctx.tools` on purpose: a missing
175
+ // service should leave the guard absent and silent, not throw inside the effect where
176
+ // the failure would look like a plugin that simply does nothing.
173
177
  ctx.effect(() => {
174
- const dispose = ctx.tools.guard((exec) => {
178
+ const tools = typeof ctx.get === 'function' ? ctx.get('tools') : ctx.tools
179
+ if (tools === undefined) {
180
+ trace('no tools service — guard not registered')
181
+ return () => {}
182
+ }
183
+ const dispose = tools.guard((exec) => {
175
184
  const header = exec?.agent?.session?.header
176
185
  // Only delegated sessions are fenced: the root owns the workspace and must be
177
186
  // able to write anywhere in it, or it could not integrate a child's artifacts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-root-planner-preset",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
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",
package/verify-patch.mjs CHANGED
@@ -116,10 +116,15 @@ const delegation = plugins.find((row) => row.id === 'delegation');
116
116
  check(delegation?.group === true, 'delegation is a group row');
117
117
 
118
118
  // The workspace fence travels with the preset, so one install carries the protocol and
119
- // its enforcement. These assertions keep that promise honest: the row must exist, must
120
- // resolve to this package's own module, and must ship that module.
121
- const guardRow = plugins.find((row) => row.id === 'root-planner-guard');
122
- check(guardRow !== undefined, 'the preset mounts the workspace guard as its own row');
119
+ // its enforcement. It must be a TOP-LEVEL insert, not a row inside `config.plugins`.
120
+ //
121
+ // That distinction is measured, not stylistic: while this guard was declared as a preset
122
+ // row, a live six-step probe came back all-allowed — the row exists in the composed
123
+ // config yet registers nothing that sees a tool call. As a sibling entry (the shape the
124
+ // standalone guard bundle was proved in) one instance sees every call. The last check
125
+ // here keeps a future refactor from quietly moving it back.
126
+ const guardRow = inserted.find((row) => row.id === 'root-planner-guard');
127
+ check(guardRow !== undefined, 'the patch inserts the workspace guard as a top-level entry');
123
128
  check(
124
129
  guardRow?.name === 'dsh-root-planner-preset/guard',
125
130
  `the guard row resolves to this package's subpath (found ${JSON.stringify(guardRow?.name)})`,
@@ -127,6 +132,10 @@ check(
127
132
  check(guardRow?.disabled !== true, 'the guard row is enabled');
128
133
  check(guardRow?.config?.directoryName === '_agents', 'the guard fences the _agents/ directory');
129
134
  check(guardRow?.config?.guardShell === true, 'the guard also applies the shell rules');
135
+ check(
136
+ plugins.every((row) => row.id !== 'root-planner-guard'),
137
+ 'the guard is NOT declared as a preset plugin row (that mount registers nothing)',
138
+ );
130
139
  check(existsSync(join(here, 'guard.js')), 'guard.js ships inside the package');
131
140
  {
132
141
  const manifest = JSON.parse(readFileSync(join(here, 'package.json'), 'utf8'));