better-dsh 0.0.0 → 0.2.3
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 +24 -0
- package/README.md +294 -4
- package/control-prompt.md +37 -0
- package/cordis.patch.yml +53 -0
- package/docs/00_adr/0001-bridge-tool-layer-not-service-layer.md +14 -0
- package/docs/00_adr/0002-masking-is-presentation-only.md +15 -0
- package/docs/10_plans/A2A-messaging-channel-test-archive.md +256 -0
- package/docs/10_plans/code-mode-vs-rlm-ipython-comparison.md +137 -0
- package/docs/10_plans/dashr-blueprint-review.md +201 -0
- package/docs/10_plans/dashr-blueprint.md +561 -0
- package/docs/10_plans/dashr-compaction-window-and-archive.md +307 -0
- package/docs/10_plans/dashr-profile-layer-feasibility.md +367 -0
- package/docs/10_plans/dashr-sandbox-escalation-semantics-gap.md +171 -0
- package/docs/10_plans/dashr-security-sandbox-analysis.md +187 -0
- package/docs/10_plans/dashr-surface-invariant-and-omp-imports.md +97 -0
- package/docs/10_plans/ipython-kernel-interactive-interface-test-report.md +152 -0
- package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +146 -0
- package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +50 -0
- package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +79 -0
- package/docs/10_plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +138 -0
- package/docs/10_plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +161 -0
- package/docs/10_plans/kernel-refactoring/V0.1.5-development-plan.md +109 -0
- package/docs/10_plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +50 -0
- package/docs/10_plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +113 -0
- package/docs/10_plans/recallable-compaction.md +147 -0
- package/docs/10_plans/spike-tag-repro.mjs +102 -0
- package/docs/10_plans/upstream-analysis.md +128 -0
- package/docs/50_test-reports/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +110 -0
- package/docs/50_test-reports/kernel-provisioning.md +44 -0
- package/docs/50_test-reports/repl-kernel-provisioning-test-report.md +87 -0
- package/docs/50_test-reports/upstream-dsh-0.1.2-alpha.5-local-test-report.md +81 -0
- package/docs/50_test-reports/upstream-dsh-0.1.2-alpha.5-report.md +93 -0
- package/docs/50_test-reports/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +142 -0
- package/docs/50_test-reports/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +193 -0
- package/docs/50_test-reports/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +96 -0
- package/docs/50_test-reports/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +127 -0
- package/docs/50_test-reports/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +150 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/README.md +138 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/code-mode-repl-only.observation.md +74 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +3890 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +544 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/functions.json +592 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/skills-catalog.snapshot.md +30 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.output-schemas.json +1236 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.python.txt +592 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.typescript.txt +516 -0
- package/docs/50_test-reports/v0.1.8d_artifacts/wire-vs-transcription.diff.md +54 -0
- package/docs/50_test-reports/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +224 -0
- package/docs/50_test-reports/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +168 -0
- package/docs/50_test-reports/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +123 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/Cargo.lock +7 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/Cargo.toml +6 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +8 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/src/main.rs +4 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/hashline-probe.md +5 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/Cargo.lock +7 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/Cargo.toml +7 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/build.rs +4 -0
- package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/src/main.rs +13 -0
- package/docs/50_test-reports/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +110 -0
- package/docs/50_test-reports/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +86 -0
- package/docs/50_test-reports/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +66 -0
- package/docs/50_test-reports/v0.2.1d-/345/256/236/346/265/213/346/212/245/345/221/212.md +67 -0
- package/docs/50_test-reports/v0.2.1e-P1-/345/256/236/346/265/213/346/212/245/345/221/212.md +136 -0
- package/docs/50_test-reports/v0.2.1ef-dev-audit-report.md +73 -0
- package/docs/50_test-reports/v0.2.1f-plugin-shipped-ui-patches/345/256/236/346/265/213/346/212/245/345/221/212.md +102 -0
- package/docs/60_exploration-and-research/cordis-research.md +350 -0
- package/docs/60_exploration-and-research/dsh-web-profile-package-map.md +186 -0
- package/docs/60_exploration-and-research/dsh-web-ui-slot-system-research.md +310 -0
- package/docs/60_exploration-and-research/dsh-webui-strip-boundary-research.md +300 -0
- package/docs/60_exploration-and-research/ios-chat-app-bridge-research.md +324 -0
- package/docs/60_exploration-and-research/web-frontend-composability-research.md +191 -0
- package/docs/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +110 -0
- package/docs/adr/0001-bridge-tool-layer-not-service-layer.md +14 -0
- package/docs/adr/0002-masking-is-presentation-only.md +15 -0
- package/docs/distro-blueprint.md +81 -0
- package/docs/dsh-webUI-with-rlm-mode.png +0 -0
- package/docs/plans/A2A-messaging-channel-test-archive.md +256 -0
- package/docs/plans/code-mode-vs-rlm-ipython-comparison.md +137 -0
- package/docs/plans/dashr-blueprint-review.md +201 -0
- package/docs/plans/dashr-blueprint.md +561 -0
- package/docs/plans/dashr-compaction-window-and-archive.md +307 -0
- package/docs/plans/dashr-profile-layer-feasibility.md +367 -0
- package/docs/plans/dashr-sandbox-escalation-semantics-gap.md +171 -0
- package/docs/plans/dashr-security-sandbox-analysis.md +187 -0
- package/docs/plans/dashr-surface-invariant-and-omp-imports.md +97 -0
- package/docs/plans/ipython-kernel-interactive-interface-test-report.md +152 -0
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +146 -0
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +50 -0
- package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +79 -0
- package/docs/plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +138 -0
- package/docs/plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +161 -0
- package/docs/plans/kernel-refactoring/V0.1.5-development-plan.md +109 -0
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +50 -0
- package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +113 -0
- package/docs/plans/recallable-compaction.md +147 -0
- package/docs/plans/spike-tag-repro.mjs +102 -0
- package/docs/plans/upstream-analysis.md +128 -0
- package/docs/repositioning-and-rebranding.md +102 -0
- package/docs/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +142 -0
- package/docs/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +193 -0
- package/docs/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +96 -0
- package/docs/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +127 -0
- package/docs/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +150 -0
- package/docs/v0.1.8d_artifacts/README.md +138 -0
- package/docs/v0.1.8d_artifacts/code-mode-repl-only.observation.md +74 -0
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +3890 -0
- package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +544 -0
- package/docs/v0.1.8d_artifacts/functions.json +592 -0
- package/docs/v0.1.8d_artifacts/skills-catalog.snapshot.md +30 -0
- package/docs/v0.1.8d_artifacts/tools-sdk.output-schemas.json +1236 -0
- package/docs/v0.1.8d_artifacts/tools-sdk.python.txt +592 -0
- package/docs/v0.1.8d_artifacts/tools-sdk.typescript.txt +516 -0
- package/docs/v0.1.8d_artifacts/wire-vs-transcription.diff.md +54 -0
- package/docs/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +224 -0
- package/docs/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +168 -0
- package/docs/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +123 -0
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.lock +7 -0
- package/docs/v0.2.0b_artifacts/f2probe/Cargo.toml +6 -0
- package/docs/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +8 -0
- package/docs/v0.2.0b_artifacts/f2probe/src/main.rs +4 -0
- package/docs/v0.2.0b_artifacts/hashline-probe.md +5 -0
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.lock +7 -0
- package/docs/v0.2.0b_artifacts/slowprobe/Cargo.toml +7 -0
- package/docs/v0.2.0b_artifacts/slowprobe/build.rs +4 -0
- package/docs/v0.2.0b_artifacts/slowprobe/src/main.rs +13 -0
- package/docs/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +110 -0
- package/docs/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +86 -0
- package/docs/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +66 -0
- package/lib/client/index.js +473 -0
- package/lib/index.d.ts +736 -0
- package/lib/index.js +11518 -0
- package/lib/kernel-env-hxaihi9C.js +195 -0
- package/lib/kernel-env.d.ts +80 -0
- package/lib/kernel-env.js +3 -0
- package/lib/py-sdk-BCaOGYz7.d.ts +125 -0
- package/lib/py-sdk-CbgYiX8O.js +691 -0
- package/lib/py-sdk.d.ts +2 -0
- package/lib/py-sdk.js +3 -0
- package/package.json +325 -4
- package/scripts/kernel-provision.mjs +35 -0
- package/index.js +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RimuruW and Yugimob (original pi-hashline-edit / pi-hashline-edit-pro)
|
|
4
|
+
Copyright (c) 2026 pi-hashline-edit-lsz contributors (pi port, hashline core)
|
|
5
|
+
Copyright (c) 2026 dsh-better-edit contributors (dsh port)
|
|
6
|
+
Copyright (c) 2026 DASHR contributors (dsh-url-schema vendoring)
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,9 +1,299 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Better Dsh — Dashr
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
DASHR is a persistent-kernel REPL for the DeepSeek Harness (dsh): a
|
|
4
|
+
**stateful `ctx.replRuntime` provider** (one persistent IPython kernel
|
|
5
|
+
subprocess **per session**, the run's `principal`, held in a map inside the
|
|
6
|
+
one service instance per mount). Each `run()` is one cell on the calling
|
|
7
|
+
session's kernel; variables, imports, and definitions assigned in run N
|
|
8
|
+
survive into run N+1 — *state codification* (blueprint §1.1 channel ②),
|
|
9
|
+
deliberately NOT the per-run isolation a one-shot execution backend
|
|
10
|
+
provides — and two sessions sharing one service instance never see each
|
|
11
|
+
other's variables.
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
Naming (v0.1.5 layer model): the runtime class is **`DashrRuntime`** — the
|
|
14
|
+
standing-mount-layer component (one instance per mount holding the
|
|
15
|
+
cross-session kernel map), and therefore the de facto daemon while the
|
|
16
|
+
profile-level `DashrDaemon` concept stays an empty shell; the session layer
|
|
17
|
+
is the ipykernel subprocess itself, a pure interpreter with no harness
|
|
18
|
+
awareness.
|
|
19
|
+
|
|
20
|
+
The presentation half — the `eval` transport tool, the Python SDK renderer,
|
|
21
|
+
and the tool→binding bridge — lives in the SAME package (merged in v0.1.8;
|
|
22
|
+
the pre-merge `dsh-rlm-mode`/`dashr-presentation` sibling split is gone).
|
|
23
|
+
The runtime registers the service key `replRuntime` through its own vendored
|
|
24
|
+
Service Definition (see `src/vendored/repl-runtime.ts`), so it carries **zero
|
|
25
|
+
dsh runtime package dependencies**: only `@deepseek-ai/cordis` (peer),
|
|
26
|
+
`schemastery`, and `zeromq`.
|
|
27
|
+
|
|
28
|
+
## Package positioning
|
|
29
|
+
|
|
30
|
+
- npm name: `better-dsh` (unscoped; formerly `@pgmi-builds/better-dsh`, which
|
|
31
|
+
now stays published only as a deprecated pointer to this name).
|
|
32
|
+
- A standard Cordis plugin (`Context` + schemastery `Config`, every tunable
|
|
33
|
+
configurable from `cordis.yml`, no hardcoded tunables).
|
|
34
|
+
- "Registrations are effects": the kernel lifecycle (lazy spawn on a key's
|
|
35
|
+
first `run()`, teardown on that session's `agent/disposed`, optional
|
|
36
|
+
snapshot at either teardown) is effect-owned, so plugin disposal tears
|
|
37
|
+
every subprocess down.
|
|
38
|
+
- Published surface: `lib/` only (`files: ["lib"]`, `main`/`types`/`exports`
|
|
39
|
+
pointing at `lib/index.js` / `lib/index.d.ts`) — the build emits beside the
|
|
40
|
+
manifest (`outDir: 'lib'`; the tsdown default `dist/` left the exports map
|
|
41
|
+
dangling, fixed in M2B). The root also re-exports the vendored Service
|
|
42
|
+
Definition's public contract (`ReplRuntime` plus the `CodeRun*` /
|
|
43
|
+
`CodeBinding*` / `CodeJsonValue` types) so consumers depend on the
|
|
44
|
+
published shape instead of reaching into sources. The declaration keeps
|
|
45
|
+
dependency imports external (`dts: { resolve: false }`): bundled copies
|
|
46
|
+
would create duplicate type identities in a consumer's program.
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
npm install better-dsh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The runtime OWNS its kernel environment. With `python` left at the
|
|
55
|
+
`python3` sentinel (or absent), it provisions a managed venv under the
|
|
56
|
+
package (`.venv-kernel`) on first use, installing `ipykernel` + `dill`
|
|
57
|
+
(CPython 3.11) — no `/tmp` symlink, no blind trust in a host `python3`.
|
|
58
|
+
An explicit `python` (or `DASHR_KERNEL_PYTHON`) is verified instead. For
|
|
59
|
+
development and tests, pre-create the venv:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm run kernel:venv # uv venv .venv-kernel --python 3.11 + ipykernel + dill
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Tests pick the kernel interpreter from `DASHR_TEST_PYTHON`, falling back to
|
|
66
|
+
`./.venv-kernel/bin/python`, then `python3` — see `test/helpers.ts`.
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
Every field of the plugin `Config` (schemastery defaults shown):
|
|
71
|
+
|
|
72
|
+
| Field | Default | Meaning |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `python` | `python3` | Explicit interpreter with `ipykernel`; the bare `python3` sentinel (or absent) selects a managed venv under `kernelEnvDir`. |
|
|
75
|
+
| `startupTimeoutMs` | `30000` | Budget for kernel spawn → ready, in milliseconds. |
|
|
76
|
+
| `runTimeoutMs` | `120000` | Wall budget per run; expiry interrupts the kernel then force-settles. |
|
|
77
|
+
| `interruptGraceMs` | `2000` | Grace between a timeout/abort interrupt and the force-settle. |
|
|
78
|
+
| `interruptConfirmMs` | `250` | Confirm window between the control-channel interrupt and the SIGALRM escalation (must be `< interruptGraceMs`); see "Interrupts" below. |
|
|
79
|
+
| `disposeTimeoutMs` | `5000` | Budget for graceful kernel teardown (shutdown_request → SIGKILL). |
|
|
80
|
+
| `snapshotTimeoutMs` | `30000` | Budget for internal snapshot/restore cells (dill dump/load). |
|
|
81
|
+
| `maxOutputBytes` | `67108864` | Hard cap for serialized log-array, completion-value, and failure-message payloads. |
|
|
82
|
+
| `snapshotDir` | *(unset)* | Base directory for per-session namespace snapshots (`<dir>/<principal>/state.dill` + `manifest.json`); none when absent. |
|
|
83
|
+
| `snapshotSizeCapBytes` | `268435456` | Serialized-size cap for a turn-end snapshot; over-cap snapshots are skipped (one-time model warning). |
|
|
84
|
+
| `username` | `dashr` | Jupyter username stamped on wire messages. |
|
|
85
|
+
| `kernelEnvDir` | *(unset)* | Managed venv directory (defaults to `<package>/.venv-kernel`). |
|
|
86
|
+
| `kernelPythonVersion` | *(unset)* | Preferred CPython version for a managed venv (default `3.11`). |
|
|
87
|
+
| `kernelAutoInstall` | `true` | Provision the managed venv (`ipykernel` + `dill`) on first use. |
|
|
88
|
+
|
|
89
|
+
## Persistent-state semantics
|
|
90
|
+
|
|
91
|
+
- **Cell semantics**: each `run({ program })` is one cell on the calling
|
|
92
|
+
session's kernel namespace (`user_ns`) — a pure IPython REPL. Top-level
|
|
93
|
+
`await` works; a top-level `return` is a SyntaxError, exactly as in a
|
|
94
|
+
native IPython cell. The completion value is the LAST expression's value
|
|
95
|
+
(REPL displayhook-style): a statement-ending cell or a `None` final
|
|
96
|
+
expression yields no `value` field, and a non-JSON value comes back as its
|
|
97
|
+
repr text.
|
|
98
|
+
- **Session keying** (M3-A): one kernel per distinct `request.principal`
|
|
99
|
+
(the presentation bridge passes the calling agent's session id); runs
|
|
100
|
+
without a principal share one default key, preserving M1 semantics. The
|
|
101
|
+
service instance count is unchanged — one per mount — the keying is a
|
|
102
|
+
`Map<principal, kernel>` inside the provider.
|
|
103
|
+
- **Kernel lifetime**: lazy start on a key's first `run()`; teardown when
|
|
104
|
+
that session's agent is disposed (the dsh `agent/disposed` event, payload
|
|
105
|
+
`{ agent: { id } }`, listened through the untyped cordis event service to
|
|
106
|
+
keep this package's zero-dsh-dependency rule) and on plugin disposal
|
|
107
|
+
(`shutdown_request`, then SIGKILL after `disposeTimeoutMs`). A kernel that
|
|
108
|
+
dies unexpectedly is never reused in-process: it respawns onto its nearest
|
|
109
|
+
replayable snapshot (or a fresh empty kernel when none exists) and the run
|
|
110
|
+
that observed the death gets an explicit `worker-exit` naming what was lost.
|
|
111
|
+
- **Turn-end snapshots** (M3-B): with `snapshotDir` configured, every
|
|
112
|
+
successful run is followed by a size-capped snapshot cell that dumps the
|
|
113
|
+
user namespace to `<snapshotDir>/<principal>/state.dill` + `manifest.json`
|
|
114
|
+
(`turn`, `pythonVersion`, `venvPath` = the kernel's own `sys.executable`,
|
|
115
|
+
`skills`, `names`, `sizeBytes`). A namespace whose serialized size exceeds
|
|
116
|
+
`snapshotSizeCapBytes` is skipped — estimated BEFORE any dill IO by a
|
|
117
|
+
bounded walk that reads numpy/pandas in-memory footprints, then confirmed
|
|
118
|
+
against the actual `.part` dump — and the model is warned once through the
|
|
119
|
+
run's own logs. Skipped snapshots never replace the previous good one.
|
|
120
|
+
- **Restore-on-first-boot** (M3-B): a key's first kernel boot restores its
|
|
121
|
+
on-disk snapshot before running user code. The kernel validates the
|
|
122
|
+
manifest itself (python version, interpreter identity, skills); a
|
|
123
|
+
non-replayable snapshot degrades to an EMPTY namespace and the first run
|
|
124
|
+
tells the model so. Variable state and the append-only transcript are NOT
|
|
125
|
+
transactionally consistent (blueprint §8.3): the snapshot is a point-in-time
|
|
126
|
+
namespace capture that can lag the transcript, and a degraded restore never
|
|
127
|
+
fabricates variables the transcript once saw.
|
|
128
|
+
- **Interrupts** (M3-A hardened): aborts/timeouts escalate in two phases —
|
|
129
|
+
the zmq control `interrupt_request` first, then SIGALRM only after
|
|
130
|
+
`interruptConfirmMs` if the cell has still not settled. The kernel-side
|
|
131
|
+
bootstrap installs a busy guard that only raises `KeyboardInterrupt` while
|
|
132
|
+
a dashr cell is actually executing, so a signal landing on an idle or
|
|
133
|
+
booting kernel is swallowed instead of terminating the process (the M1
|
|
134
|
+
same-tick dual send killed idle kernels deterministically — 10/10
|
|
135
|
+
same-tick, 8/10 at +1-2ms, 40/40 during cold boot; see
|
|
136
|
+
`test/interrupt-race.spec.ts`). The hard-abort contract is intact: a busy
|
|
137
|
+
`while True: pass` still breaks inside the grace (blueprint §10.4).
|
|
138
|
+
- **Concurrency**: the bridge serializes cells per kernel (`executeCell`
|
|
139
|
+
awaits the previous cell), so concurrent `run()` calls on one session
|
|
140
|
+
queue rather than interleave; see `test/parallel.spec.ts`. Runs on
|
|
141
|
+
DIFFERENT principals execute on their own kernels concurrently.
|
|
142
|
+
- **What snapshots do NOT carry**: nothing outside the kernel namespace is
|
|
143
|
+
ever in scope — the v0.1.8b removal deleted the Continual Harness
|
|
144
|
+
(`refine()`, `harnessDir`) entirely, so the snapshot/restore cycle's only
|
|
145
|
+
persistence channel is the kernel namespace itself (blueprint §8.4).
|
|
146
|
+
Anything a cell stores in ordinary variables follows the snapshot rules
|
|
147
|
+
above as before.
|
|
148
|
+
|
|
149
|
+
## Testing
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
npm install
|
|
153
|
+
npm run kernel:venv # once; or export DASHR_TEST_PYTHON=/path/to/python
|
|
154
|
+
npm run typecheck # tsc --noEmit
|
|
155
|
+
npm test # vitest --run (fileParallelism: false)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Teardown discipline: every test context is disposed through
|
|
159
|
+
`onTestFinished`, and CI must assert no orphan kernels remain:
|
|
6
160
|
|
|
7
161
|
```sh
|
|
8
|
-
|
|
162
|
+
pgrep -cf -- '-[m] ipykernel_launcher' || echo no-orphans
|
|
9
163
|
```
|
|
164
|
+
|
|
165
|
+
(The `-[m]` trick prevents `pgrep` from matching itself; 208 orphaned
|
|
166
|
+
kernels once exhausted machine memory while every unit test stayed green —
|
|
167
|
+
blueprint §10.8/§10.9.)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
# Presentation half (eval transport, SDK, bindings)
|
|
172
|
+
|
|
173
|
+
The DASHR agent-plane presentation (blueprint §7.4): the plugin half that
|
|
174
|
+
presents the persistent-kernel runtime to the model as **cells on one
|
|
175
|
+
persistent IPython kernel**. It contributes:
|
|
176
|
+
|
|
177
|
+
- **`eval`** — the cell transport tool: one call = one cell on the calling
|
|
178
|
+
session's kernel (the `ctx.replRuntime` service). Variables, imports, and
|
|
179
|
+
definitions survive across calls. Nested tool calls ride the host
|
|
180
|
+
registry's native scheduling pipeline as `await tool.name({...})` inside
|
|
181
|
+
the cell — one positional arguments object per call, keyword arguments
|
|
182
|
+
rejected. Every registry-visible tool is bound as a member; the
|
|
183
|
+
`send_message` bridge callable joins them in the same catalog block.
|
|
184
|
+
- **`dashr:tool-catalog`** — a generated Python SDK prompt section: one
|
|
185
|
+
`async def name(args) -> Output` per visible tool (flat `tool.*` shape)
|
|
186
|
+
plus the cell contract (persistent namespace, completion-value rules,
|
|
187
|
+
`ToolCallError`, sub-call concurrency).
|
|
188
|
+
- **The model-direct collapse** — an assembly filter leaves `eval` the only
|
|
189
|
+
contributed tool schema, and a monotonic guard denies a model-direct call
|
|
190
|
+
naming anything else with the route back into a cell. Both are scoped to
|
|
191
|
+
the mounting composition, so a PTC (native Code Mode) preset in the same
|
|
192
|
+
process keeps its own presentation.
|
|
193
|
+
- **Masking (ADR-0002)** — exactly two upstream tool names are displaced
|
|
194
|
+
from the model's surface (`send_message` and the child-scoped `report`),
|
|
195
|
+
collapsed into the single dual-direction `send_message` bridge. Every
|
|
196
|
+
other delegation tool (`subagent`, `subagent_fork`, `list_agents`,
|
|
197
|
+
`interrupt_agent`, `workflow`, `ralph`) stays **directly exposed** as a
|
|
198
|
+
native `tool.*` binding — the model calls it exactly as the host ships it.
|
|
199
|
+
Masking is presentation-only: the registry is never touched.
|
|
200
|
+
|
|
201
|
+
Removed in v0.1.8b: the `refine` Continual Harness and the `compact` REPL
|
|
202
|
+
bridge (harness/refine became third-party territory; context compaction is
|
|
203
|
+
the host runtime's business, not the REPL's).
|
|
204
|
+
|
|
205
|
+
## Install
|
|
206
|
+
|
|
207
|
+
The canonical path is the repo-root one-click installer (`install.sh`): it
|
|
208
|
+
installs the plugin from the npm registry and notes the restart. The
|
|
209
|
+
equivalent manual steps, for reference:
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
# 1. plugin — the pnpm registry-metadata cache can lag a fresh npm publish
|
|
213
|
+
# by minutes-to-hours, so drop it first, or `@latest` resolves the OLD
|
|
214
|
+
# version right after a release:
|
|
215
|
+
rm -rf ~/.cache/pnpm
|
|
216
|
+
dsh plugin --profile web add --config.auto-install-peers=false better-dsh@latest
|
|
217
|
+
|
|
218
|
+
# 2. restart the running instance: systemctl --user restart dsh
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Notes: `--config.auto-install-peers=false` is MANDATORY — the profile
|
|
222
|
+
already resolves `@deepseek-ai/*` peers through the harness install; letting
|
|
223
|
+
the package manager auto-install them adds a second, divergent copy of
|
|
224
|
+
cordis and friends. The version is deliberately unpinned (`@latest`).
|
|
225
|
+
|
|
226
|
+
## Coexistence with a PTC Code-Mode session
|
|
227
|
+
|
|
228
|
+
`eval` is our own transport name (the registry reserves `run_code`), so a
|
|
229
|
+
Code-Mode preset (`@deepseek-ai/dsh-agent-tool-presentation` with
|
|
230
|
+
`mode: code` over the host-plane worker-thread `codeRuntime`) composes
|
|
231
|
+
beside a DASHR session in one process: the PTC agent's assembly shows
|
|
232
|
+
`run_code` plus the TS `tools:sdk` section, the DASHR agent's shows `eval`
|
|
233
|
+
plus the Python `dashr:tool-catalog`, and neither execution path touches
|
|
234
|
+
the other's runtime.
|
|
235
|
+
|
|
236
|
+
## Composition
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import dashr from 'better-dsh'
|
|
240
|
+
|
|
241
|
+
ctx.plugin(dashr, { maxParallelSubCalls: 10 })
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
One plugin, one row: `apply` mounts `DashrRuntime` first (providing
|
|
245
|
+
`ctx.replRuntime`), then the presentation half injects on `replRuntime` —
|
|
246
|
+
a composition without the runtime fails AT MOUNT, named in the preset's
|
|
247
|
+
activation audit, instead of at the first prompt. The dashr bundle patch
|
|
248
|
+
mounts the row on the HOST plane, so every agent in every preset sees the
|
|
249
|
+
`eval` tool (`agent → preset → global`).
|
|
250
|
+
|
|
251
|
+
## Delegation and messaging
|
|
252
|
+
|
|
253
|
+
The upstream delegation tools stay REGISTERED, EXECUTABLE, and directly
|
|
254
|
+
bound — `subagent`, `subagent_fork`, `list_agents`, `interrupt_agent`,
|
|
255
|
+
`workflow`, `ralph` are ordinary `tool.*` members the model calls exactly
|
|
256
|
+
as the host ships them. Two names are displaced by the single
|
|
257
|
+
`send_message` bridge (ADR-0001): upstream `send_message` (the
|
|
258
|
+
parent→child downlink) and the child-scoped `report` (the child→parent
|
|
259
|
+
uplink) collapse into one dual-direction channel:
|
|
260
|
+
|
|
261
|
+
- `await tool.send_message({"receiver": "child", "message": ..., "subagent_id": ...})`
|
|
262
|
+
delivers down through the tool layer;
|
|
263
|
+
- `await tool.send_message({"receiver": "parent", "message": ...})` reports
|
|
264
|
+
up through the SERVICE layer (`ctx.subagents.reportFrom(...)`) — live
|
|
265
|
+
continuable children only; a root agent gets a structured `UNAUTHORIZED`.
|
|
266
|
+
|
|
267
|
+
Every binding returns structured JSON; errors are a FIELD on the result,
|
|
268
|
+
never a host crash — a missing `ctx.subagents` service, a depth cap, an
|
|
269
|
+
unknown id, or an infrastructure rejection all map to an `error` string.
|
|
270
|
+
|
|
271
|
+
## Config
|
|
272
|
+
|
|
273
|
+
| Field | Default | Meaning |
|
|
274
|
+
| --- | --- | --- |
|
|
275
|
+
| `maxParallelSubCalls` | `10` | Cap on one cell's overlapping sub-calls (native scheduler contract; `1` = strictly serial). |
|
|
276
|
+
| *(runtime keys)* | — | The runtime slice (`python`, `snapshotDir`, timeouts, caps, kernel-env knobs) is documented in the Configuration table above. |
|
|
277
|
+
|
|
278
|
+
## Tests
|
|
279
|
+
|
|
280
|
+
```sh
|
|
281
|
+
npm install
|
|
282
|
+
npm run kernel:venv # once; or export DASHR_TEST_PYTHON=/path/to/python
|
|
283
|
+
npm run typecheck
|
|
284
|
+
npm test # vitest --run
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The suite needs a Python interpreter with `ipykernel` (+ `dill` for the
|
|
288
|
+
snapshot tiers) for the real-kernel specs; it resolves one from
|
|
289
|
+
`DASHR_TEST_PYTHON`, then `./.venv-kernel/bin/python`, then `python3` —
|
|
290
|
+
see `test/helpers.ts`.
|
|
291
|
+
|
|
292
|
+
## Relationship to upstream
|
|
293
|
+
|
|
294
|
+
Structure mirrors `@deepseek-ai/dsh-agent-tool-presentation` and the Code
|
|
295
|
+
Mode half of `@deepseek-ai/dsh-tools` (0.1.1-rc.2), re-pointed at the
|
|
296
|
+
vendored `replRuntime` Service Definition. See the module docs in
|
|
297
|
+
`src/index.ts` for the deliberate deltas (`eval` vs `run_code`, ordinary
|
|
298
|
+
scoped registration, guard-based collapse, mirrored `tools/code-dispatch-log`
|
|
299
|
+
waterfall).
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## The DASHR REPL interface
|
|
2
|
+
|
|
3
|
+
This agent has TWO ways to act:
|
|
4
|
+
|
|
5
|
+
1. **Direct tool calls** — call native tools (`read`/`write`/`edit`/`bash`/…) as ordinary function calls. Use these for payload-shaped work: one long read, one edit, one command.
|
|
6
|
+
2. **`eval` cells** — one `eval` call runs one Python program on a session-persistent scripting pad. Use it when you need logic: loops, conditions, fan-out, or composing many tool results into one step.
|
|
7
|
+
|
|
8
|
+
`eval` takes two required arguments: `cell` (one Python program; top-level `await` works; top-level `return` is a SyntaxError — the cell runs in module scope; variables/imports/definitions from earlier cells are still alive) and `description` (a short summary).
|
|
9
|
+
|
|
10
|
+
## Tools inside a cell
|
|
11
|
+
|
|
12
|
+
Inside a cell, every native tool is a member of the `tool` object, called as `await tool.name({...})` with ONE positional arguments object — `await tool.read({"file_path": "x"})`, never `tool.read(file_path="x")`. A failed call raises `ToolCallError`. Tool names that are not plain identifiers (non-identifier characters, e.g. hyphens) have no `tool.<name>` member — call those as direct tool calls. Delegation: `agent` is the unified agent-spawn entry; `subagent` is its native alias — both delegate through the same runtime, so call either.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
# One step cell
|
|
16
|
+
print(await tool.read({"file_path": "docs/README.md"}))
|
|
17
|
+
|
|
18
|
+
# shell is another tool
|
|
19
|
+
r = await tool.bash({"command": "ls -la src/", "description": "List source directory"})
|
|
20
|
+
print(r["stdout"]["text"])
|
|
21
|
+
|
|
22
|
+
# fan-out with gather
|
|
23
|
+
import asyncio
|
|
24
|
+
matches, files = await asyncio.gather(
|
|
25
|
+
tool.grep({"pattern": "TODO", "path": "src"}),
|
|
26
|
+
tool.glob({"pattern": "**/*.ts", "path": "src"}),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# variables persist across cells and turns
|
|
30
|
+
cfg = await tool.read({"file_path": "config.yaml"}) # cfg stays alive in later cells
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Rules
|
|
34
|
+
|
|
35
|
+
- Payload-shaped work (a long read, a big write, a single command) → direct tool call. Logic-shaped work (loops, conditions, composition) → an `eval` cell.
|
|
36
|
+
- Only print or return what you need next; everything else stays in the scripting pad.
|
|
37
|
+
- Variables persist across cells and turns, but they live in the pad's process: keep durable state in files.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# DASHR bundle patch: mounts `dashr-repl` on the HOST plane. Its `eval` tool
|
|
2
|
+
# and persistent-kernel REPL runtime land in the global scope layer, so every
|
|
3
|
+
# agent in every preset sees them (`agent → preset → global`). One
|
|
4
|
+
# process-global runtime keys one kernel per Session/Agent, torn down on
|
|
5
|
+
# `agent/disposed` — the promotion of the v0.1.5 DashrDaemon shell.
|
|
6
|
+
#
|
|
7
|
+
# The tool is an ordinary registration: upstream's `code` preset still applies
|
|
8
|
+
# its own `tools:code-only` rule (a direct `eval` call in a PTC session resolves
|
|
9
|
+
# to UNKNOWN_TOOL). DASHR ships no PTC mode, so that rule never bites here.
|
|
10
|
+
#
|
|
11
|
+
# `python` is only a hint: the runtime OWNS the kernel environment. An
|
|
12
|
+
# explicit interpreter (DASHR_KERNEL_PYTHON) is verified; the bare
|
|
13
|
+
- insert:
|
|
14
|
+
- id: dashr-repl
|
|
15
|
+
name: 'better-dsh'
|
|
16
|
+
config:
|
|
17
|
+
python: !!js process.env.DASHR_KERNEL_PYTHON ?? 'python3'
|
|
18
|
+
# NOTE (v0.2.3): trustedPageAuthorities' DERIVED default lives in the
|
|
19
|
+
# plugin's Config schema (src/index.ts), NOT here — a profile/home
|
|
20
|
+
# layer row with this id whole-row-overrides this config object, which
|
|
21
|
+
# would wipe any default declared at the bundle layer (empirically
|
|
22
|
+
# pinned on 4999). Schema defaults fill per-key at plugin load and
|
|
23
|
+
# survive every overlay layer; the default derives from the
|
|
24
|
+
# DSH_TRUSTED_HOSTS environment — the same single source the fence
|
|
25
|
+
# leg reads first. An explicit value in any later layer still wins.
|
|
26
|
+
|
|
27
|
+
# Native compaction at the host plane: the base bundle ships the engine, the
|
|
28
|
+
# `/compact` command, and the tool-result pruner as host rows; dsh-web-app
|
|
29
|
+
# disables them to move compaction behind per-session presets. DASHR is
|
|
30
|
+
# host-plane, so re-enable them here (upstream defaults: threshold 0.8, retain
|
|
31
|
+
# 0.16). Applied last in bundle order, overriding dsh-web-app's disable.
|
|
32
|
+
- id: compaction-basic
|
|
33
|
+
disabled: false
|
|
34
|
+
- id: command-compact
|
|
35
|
+
disabled: false
|
|
36
|
+
- id: tool-result-pruner
|
|
37
|
+
disabled: false
|
|
38
|
+
|
|
39
|
+
# Web-trust fence authorities (v0.2.1f): override the connection row the
|
|
40
|
+
# dsh-web-app bundle declares (same id), restating its full shape per the
|
|
41
|
+
# patch-layer contract, with `trustedHosts` merging the DSH_TRUSTED_HOSTS
|
|
42
|
+
# environment (whitespace-separated, operator-declared serving authorities)
|
|
43
|
+
# ahead of the upstream webRuntime-derived ones (LAN literals from an
|
|
44
|
+
# all-interface bind plus --trusted-host extras). Unset/empty env ⇒ identical
|
|
45
|
+
# behavior to the unpatched row. Malformed entries fail plugin load loudly
|
|
46
|
+
# (upstream assertTrustedAuthority). The browser-side twin (page-authority
|
|
47
|
+
# loopback verdict) is the plugin's webserver/index-inject boot script —
|
|
48
|
+
# see src/web-trust.ts.
|
|
49
|
+
- id: connection
|
|
50
|
+
name: '@deepseek-ai/dsh-client-connection'
|
|
51
|
+
inject: [webRuntime]
|
|
52
|
+
config:
|
|
53
|
+
trustedHosts: !!js (process.env.DSH_TRUSTED_HOSTS ?? '').split(/\s+/).filter(Boolean).concat(ctx.webRuntime.trustedHosts)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Bridge the tool layer, not the service layer
|
|
2
|
+
|
|
3
|
+
DASHR's `rlm()` family dispatches upstream delegation tools through the tool registry (nested sub-dispatch), not `ctx.subagents` service methods. The tool layer carries the deployment's enforcement surface — approval pipeline, sandbox policy, per-instance config (maxDepth, backgroundMode, persona) — that a direct service call would silently bypass. The cost: per-call model selection is impossible, because the tool schema exposes no `model` parameter; `rlm(model=...)` from 0.1.4 is dropped, and `subagentModel` degrades to a static `agentOptions.model` in the preset patch.
|
|
4
|
+
|
|
5
|
+
## Considered Options
|
|
6
|
+
|
|
7
|
+
- **Service layer direct** (0.1.4's approach): `ctx.subagents.start()` with a hand-built request. Full control over request fields (including `agentOptions.model` and `maxDepth`), but every policy the tool instance would have applied must be re-implemented or lost.
|
|
8
|
+
- **Tool layer nested dispatch** (chosen): `rlm("spawn")` executes the registry's `subagent` tool with a parent token. Upstream policy is inherited wholesale; the schema boundary is the tool's own contract.
|
|
9
|
+
|
|
10
|
+
## Consequences
|
|
11
|
+
|
|
12
|
+
- `rlm(mode, prompt, *, label, run_in_background)` — no `model` kwarg. Parent-model inheritance is the default; a different child model requires a preset patch, not a call argument.
|
|
13
|
+
- Depth enforcement comes from the tool instance's `maxDepth` config, patched to 10 in the preset (see `dev/kernel-refactoring/V0.1.5-development-plan.md` Q22/Q23).
|
|
14
|
+
- The upstream delegation tools (`subagent`, `subagent_fork`, `interrupt_agent`) must stay registered and executable even though the model never sees their names — which is why masking is presentation-only (ADR-0002).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Masking is presentation-only
|
|
2
|
+
|
|
3
|
+
Hiding exactly two upstream A2A tool names (`send_message` — the parent→child downlink — and `report` — the child→parent uplink) from the model is done by excluding them from the generated Tool Catalog text and from the kernel binding names — nothing else. The two tools stay registered, executable, and dispatchable; the single `send_message` bridge dispatches them internally. Every OTHER upstream delegation tool (`subagent`, `subagent_fork`, `list_agents`, `interrupt_agent`, `workflow`, `ralph`) is exposed directly as a native `tool.*` member — no re-wrapping (v0.1.9).
|
|
4
|
+
|
|
5
|
+
## Considered Options
|
|
6
|
+
|
|
7
|
+
- **`restrict()` at runtime**: hides names in the registry's model-facing view, but validates against the live view at call time — ordering hazards against late tool registration can fail the whole preset mount.
|
|
8
|
+
- **`disabled: true` include patches**: physically unregisters the tool, which also removes the bridge's dispatch target. Masking must not break the bridge.
|
|
9
|
+
- **Presentation-only exclusion** (chosen): the registry is never touched. The model's surface (wire schema collapse to `eval`, Tool Catalog text, kernel bindings) is entirely DASHR-generated, so exclusion happens at the two points DASHR owns.
|
|
10
|
+
|
|
11
|
+
## Consequences
|
|
12
|
+
|
|
13
|
+
- The masked tools remain in the registry and are reachable via nested sub-dispatch with a parent token, which passes the model-direct guard.
|
|
14
|
+
- **Field-verified (v0.1.8d, `both` presentation mode)**: reachable by MODEL-DIRECT native call too — no parent token needed. A probe calling the masked `skill({"name":…})` through the API function-call surface executed in full. Cause: the mask registers no visibility filter, so the name stays in `view(scope).visible`, and `resolveExecution` collapses model-direct calls only under `code` mode. The mask is an ADVERTISING cut, not an enforcement point; the hard gate is the REPL binding allowlist (`unknown binding`). If a deployment ever needs true model-direct rejection, the mechanisms are a visibility-layer `restrict()` (rejected here for ordering hazards) or a `code`-mode collapse — both are deployment-level decisions, not mask-level ones.
|
|
15
|
+
- Zero upstream mutation means zero interference with host-plane modules that enumerate or interact with the delegation tools.
|