better-dsh 0.3.1 → 0.3.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.
Files changed (72) hide show
  1. package/README.md +6 -5
  2. package/cordis.patch.yml +14 -9
  3. package/docs/20_specs/kernel-provisioning/spec.md +2 -0
  4. package/docs/remote-tools.md +15 -9
  5. package/docs/specs/kernel-provisioning/spec.md +12 -40
  6. package/docs/specs/omp-python-repl-port/runtime-guide.md +3 -1
  7. package/docs/specs/omp-python-repl-port/spec.md +7 -0
  8. package/docs/specs/remote-pty-label/spec.md +2 -0
  9. package/docs/specs/remote-tool-enhancement/spec.md +1 -0
  10. package/lib/BrowserWebSocketTransport.js +4 -0
  11. package/lib/{BrowserWebSocketTransport-wm99h94r.js → BrowserWebSocketTransport2.js} +1 -1
  12. package/lib/LaunchOptions.js +4 -0
  13. package/lib/{LaunchOptions-5eg7E0ye.js → LaunchOptions2.js} +1 -1
  14. package/lib/{NodeWebSocketTransport-D9ysYGLM.js → NodeWebSocketTransport.js} +2 -2
  15. package/lib/NodeWebSocketTransport2.js +4 -0
  16. package/lib/{bidi-CwabFH07.js → bidi.js} +4 -4
  17. package/lib/client/index.js +59 -6
  18. package/lib/compaction/index.d.ts +1 -1
  19. package/lib/compaction/index.js +1 -1
  20. package/lib/failover/index.d.ts +1 -1
  21. package/lib/failover/index.js +1 -1
  22. package/lib/fs-aware/sandbox-plugin.js +1 -1
  23. package/lib/{index-browser-B-9jm12n.js → index-browser.js} +6 -6
  24. package/lib/index.d.ts +2 -12
  25. package/lib/index.js +58 -62
  26. package/lib/kernel-env.d.ts +5 -15
  27. package/lib/kernel-env.js +2 -2
  28. package/lib/kernel-env2.js +76 -0
  29. package/lib/{lsp-server-registry-BcdkHROD.js → lsp-server-registry2.js} +1 -1
  30. package/lib/{main-BeCgUd20.js → main.js} +1 -1
  31. package/lib/{main-Dv_UQqG2.js → main2.js} +1 -1
  32. package/lib/mobile/plugin.d.ts +2 -2
  33. package/lib/mobile/plugin.js +2 -2
  34. package/lib/{puppeteer-core-Dnd7eqel.js → puppeteer-core.js} +9 -9
  35. package/lib/py-sdk.js +1 -1
  36. package/lib/remote/helper-bin/MANIFEST.json +35 -0
  37. package/lib/remote/helper-bin/aarch64-apple-darwin/BUILT-VER +1 -0
  38. package/lib/remote/helper-bin/aarch64-apple-darwin/superd-run +0 -0
  39. package/lib/remote/helper-bin/aarch64-unknown-linux-musl/BUILT-VER +1 -0
  40. package/lib/remote/helper-bin/aarch64-unknown-linux-musl/superd-run +0 -0
  41. package/lib/remote/helper-bin/x86_64-pc-windows-gnu/BUILT-VER +1 -0
  42. package/lib/remote/helper-bin/x86_64-pc-windows-gnu/superd-run.exe +0 -0
  43. package/lib/remote/helper-bin/x86_64-unknown-linux-musl/BUILT-VER +1 -0
  44. package/lib/remote/helper-bin/x86_64-unknown-linux-musl/superd-run +0 -0
  45. package/lib/remote/plugin.d.ts +172 -51
  46. package/lib/remote/plugin.js +2168 -718
  47. package/lib/url-schemes/index.d.ts +11 -11
  48. package/lib/url-schemes/index.js +12 -12
  49. package/lib/{wasm_bg-CNXhHjVg.js → wasm_bg.js} +1 -1
  50. package/lib/web-password.d.ts +7 -7
  51. package/lib/web-password.js +1 -1
  52. package/lib/web-trust.d.ts +1 -1
  53. package/lib/web-trust.js +2 -2
  54. package/lib/{web-trust-D6gJpUMf.js → web-trust2.js} +20 -8
  55. package/lib/welcome-notice.js +1 -1
  56. package/package.json +4 -4
  57. package/scripts/kernel-check.mjs +5 -0
  58. package/lib/BrowserWebSocketTransport-D1aFReu_.js +0 -4
  59. package/lib/LaunchOptions-DxQQ0s-A.js +0 -4
  60. package/lib/NodeWebSocketTransport-BRLjmu2R.js +0 -4
  61. package/lib/kernel-env-BOF0fNHn.js +0 -91
  62. package/scripts/kernel-provision.mjs +0 -5
  63. /package/lib/{chunk-DQxbwPpZ.js → chunk.js} +0 -0
  64. /package/lib/{index-DTDXUe6m.d.ts → index2.d.ts} +0 -0
  65. /package/lib/{lib-C8_jXvwA.js → lib.js} +0 -0
  66. /package/lib/{lsp-server-registry-D4VLs1Sm.js → lsp-server-registry.js} +0 -0
  67. /package/lib/{native-capture-BEByDXYM.js → native-capture.js} +0 -0
  68. /package/lib/{py-sdk-D7klhpWb.js → py-sdk2.js} +0 -0
  69. /package/lib/{util-BHL_dfV_.js → util.js} +0 -0
  70. /package/lib/{web-tree-sitter-CPrPD4J2.js → web-tree-sitter.js} +0 -0
  71. /package/lib/{web-trust-CwZrTBcl.d.ts → web-trust2.d.ts} +0 -0
  72. /package/lib/{wrap-0VA0uFx8.js → wrap.js} +0 -0
package/README.md CHANGED
@@ -18,7 +18,11 @@ Super Dash installs Better as a separate native bundle. `../dev-webmcp` is a dev
18
18
  npm install better-dsh
19
19
  ```
20
20
 
21
- The runtime requires Python >=3.9. Configure `python` (or the bundle's `DASHR_KERNEL_PYTHON` hint) to select one. The default `python3` compatibility path prepares a managed environment using uv or system Python, without kernel package installs. uv may download the selected interpreter when it is absent; an explicit interpreter path uses your existing installation. Better probes the interpreter and ships the runner, checkpoint helper and dill0.4.1 sources; it does not run pip to install kernel packages. Python is not bundled by this change. A missing or unsupported interpreter produces an actionable error.
21
+ Better uses an existing Python interpreter. An explicit `python` setting (or `DASHR_KERNEL_PYTHON`) takes precedence; otherwise discovery checks the active virtual/Conda environment, an existing workspace `.venv` or `venv`, then PATH `python3` and `python`. Candidates are actually probed, and a broken automatically discovered candidate does not hide a working fallback. Relative explicit paths resolve against the session workspace. No CPython implementation or Python3.11 version is selected. The packaged dill0.4.1 checkpoint helper requires Python >=3.9.
22
+
23
+ Better does not install Python, invoke uv/pip, or create a virtual environment. When none of the existing candidates works, the error lists the attempts and asks for an existing interpreter or a Python installation. `npm run kernel:check` checks availability without changing the environment. The runner and dill sources ship in the package; runtime checkpoints belong under the native profile home, and package upgrades do not require rebuilding a package-local Python environment.
24
+
25
+ The old `kernelEnvDir`, `kernelPythonVersion` and `kernelAutoInstall` provisioning fields and `kernel:venv` command are removed. Existing package-local `.venv-kernel` / `.uv-cache` directories are no longer selected automatically or modified; an existing interpreter can still be selected explicitly. A checkpoint made with another interpreter/environment may be incompatible and produces the usual explicit recovery notice; it is never replayed into a new environment.
22
26
 
23
27
  The original OMP runner's source commit/hash and MIT license, and dill's source hashes/version and BSD-3-Clause license, accompany the packaged resources. Better's integration code is separate from these original files. OMP-specific Agent helpers are not exposed as DSH features.
24
28
 
@@ -28,7 +32,7 @@ Runtime fields remain configurable through the ordinary Cordis row. The implemen
28
32
 
29
33
  | Field | Default | Meaning |
30
34
  | --- | --- | --- |
31
- | `python` | `python3` | Interpreter selection; no Jupyter installation requirement. |
35
+ | `python` | automatic discovery | Existing executable name/path; explicit selection bypasses discovery. |
32
36
  | `startupTimeoutMs` | `30000` | Budget for child startup and initialization. |
33
37
  | `runTimeoutMs` | `120000` | Wall budget per user cell. |
34
38
  | `interruptGraceMs` | `2000` | Grace after an interrupt before terminating an unresponsive child. |
@@ -38,9 +42,6 @@ Runtime fields remain configurable through the ordinary Cordis row. The implemen
38
42
  | `maxOutputBytes` | `67108864` | Cap for serialized logs, completion and error output; overflow is reported. |
39
43
  | `snapshotDir` | native profile checkpoint directory | Explicit directory overrides the native profile default; `""` disables persistence and retains only live process state. Standalone runtimes must explicitly configure persistence. |
40
44
  | `snapshotSizeCapBytes` | `268435456` | Checkpoint payload cap; failed or over-cap saves preserve the previous committed generation. |
41
- | `kernelEnvDir` | `<package>/.venv-kernel` | Managed environment location used by the `python3` compatibility selection. |
42
- | `kernelPythonVersion` | `3.11` | Interpreter version requested from uv for a new managed environment. |
43
- | `kernelAutoInstall` | `true` | Allow creation of the managed environment on first use. |
44
45
  | `username` | `dashr` | Retained compatibility field. |
45
46
 
46
47
  ## Cells and recovery
package/cordis.patch.yml CHANGED
@@ -21,7 +21,7 @@
21
21
  - id: dashr-repl
22
22
  name: 'better-dsh'
23
23
  config:
24
- python: !!js process.env.DASHR_KERNEL_PYTHON ?? 'python3'
24
+ python: !!js process.env.DASHR_KERNEL_PYTHON
25
25
 
26
26
  # URL-aware I/O + hashline + devices. Gates ride THIS row's config
27
27
  # (schema defaults: both ON — opt-out), no longer the core row's.
@@ -90,17 +90,22 @@
90
90
  # dsh-web-app bundle declares (same id), restating its full shape per the
91
91
  # patch-layer contract, with `trustedHosts` merging the DSH_TRUSTED_HOSTS
92
92
  # environment (whitespace-separated, operator-declared serving authorities)
93
- # ahead of the upstream webRuntime-derived ones (LAN literals from an
94
- # all-interface bind plus --trusted-host extras). Unset/empty env ⇒ identical
95
- # behavior to the unpatched row. Malformed entries fail plugin load loudly
96
- # (upstream assertTrustedAuthority). The browser-side twin (page-authority
97
- # loopback verdict) is the plugin's webserver/index-inject boot script —
98
- # see src/web-trust.ts.
93
+ # ahead of the upstream invocation authorities. Since DSH 0.1.7 the parsed
94
+ # Web flags live on the `webStartup` service and every host in the declared
95
+ # peer range provides it, so the row injects webStartup and reads
96
+ # `ctx.webStartup.trustedHosts` (the --trusted-host values) — the 0.2.1+
97
+ # upstream row's own source; on pre-0.2.1 hosts this drops only the LAN
98
+ # literals an all-interface bind used to derive (wildcard binds are rejected
99
+ # there too, and this repo's lines always declare authorities explicitly).
100
+ # Unset/empty env ⇒ identical behavior to the unpatched row. Malformed entries
101
+ # fail plugin load loudly (upstream assertTrustedAuthority). The browser-side
102
+ # twin (page-authority loopback verdict) is the plugin's
103
+ # webserver/index-inject boot script — see src/web-trust.ts.
99
104
  - id: connection
100
105
  name: '@deepseek-ai/dsh-client-connection'
101
- inject: [webRuntime]
106
+ inject: [webStartup]
102
107
  config:
103
- trustedHosts: !!js (process.env.DSH_TRUSTED_HOSTS ?? '').split(/\s+/).filter(Boolean).concat(ctx.webRuntime.trustedHosts)
108
+ trustedHosts: !!js (process.env.DSH_TRUSTED_HOSTS ?? '').split(/\s+/).filter(Boolean).concat(ctx.webStartup.trustedHosts)
104
109
 
105
110
  - id: ui-settings-models
106
111
  name: '@deepseek-ai/dsh-client-ui-settings-models'
@@ -1,5 +1,7 @@
1
1
  # kernel-provisioning Specification
2
2
 
3
+ > Historical iKernel contract, superseded on2026-10-12. Do not apply the provisioning requirements below to current Better. The active [existing-interpreter discovery contract](../../specs/kernel-provisioning/spec.md) removes uv/pip provisioning,3.11 selection and package-local venv creation.
4
+
3
5
  ## Purpose
4
6
  better-dsh 的 REPL kernel(IPython)供给契约:独占 managed venv(插件包目录内,`kernelEnvDir` 可显式覆盖)、版本锁定(ipykernel 7.3.0 / dill 0.4.1,uv 与 pip 双路径)、三级触发供给(postinstall 加速器 / daemon spin-up 主路径 / 首用 lazy 兜底)全 fail-open 且幂等、供给状态可观测;npm 包不内嵌预建 venv(供给能力随包,供给结果不随包)。
5
7
 
@@ -8,21 +8,27 @@ Better Dash and Super Dash each provide the native `remote` tool and REPL route.
8
8
  | --- | --- |
9
9
  | List locally known targets without dialing | `{}` |
10
10
  | Probe one target | `{target:"ssh:alias"}` |
11
- | Register an Agent-local SSH identity | `{target:"byossh:build",ssh:{host:"host",user:"user",key:"/origin/key"}}` |
12
- | Stateless command and stdin | `{target:"byossh:build",cmd:"cat",stdin:"hello"}` |
13
- | Stream a local script to fixed `bash -s` | `{target:"ssh:alias",script:"/absolute/script.sh"}` |
14
- | Persistent shell | `{target:"ssh:alias",mode:"pty",cmd:"cd /tmp; export CHECK=ready"}` |
15
- | Official background job | `{target:"ssh:alias",cmd:"long-command",run_in_background:true}` |
11
+ | Register an Agent-local SSH identity (no dialing) | `{target:"ssh:build",ssh:{host:"host",user:"user",key:"/origin/key"}}`; omit `target` for an auto id (`ssh-xxxx`) |
12
+ | Stateless command and stdin over an identity | `{target:"ssh:build",cmd:"cat",stdin:"hello"}` |
13
+ | Stream a local script to fixed `bash -s` | `{target:"ssh:alias",scriptPath:"/absolute/script.sh"}` |
14
+ | Create a persistent shell (tab semantics) | `{target:"ssh:alias:pty",cmd:"cd /tmp; export CHECK=ready"}`; append `:label` (`ssh:alias:pty:build`) to name it |
15
+ | Reuse a persistent shell | `{target:"pty:<id\|label>",cmd:"…"}` |
16
+ | Session card (state + ledger, no command) | `{target:"pty:<id\|label>"}` |
17
+ | Create a BYO PTY from a spawn command | `{spawn:"docker exec -it img bash",target:"pty:build",cmd:"…"}`; later `{target:"pty:build",cmd:"…"}` |
18
+ | Background job (oneshot or PTY) | `{target:"ssh:alias",cmd:"long-command",run_in_background:true}` |
19
+ | Close a PTY session | `{action:"close",target:"pty:<id\|label>",force:true}` |
16
20
  | Native CLI copy | `{action:"copy",src:"/absolute/source",dst:"ssh:alias:/absolute/destination",argv:["-p"]}` |
17
21
  | Native rsync | `{action:"rsync",src:"/absolute/source/",dst:"ssh:alias:/existing/destination/",argv:["-a","--partial","--dry-run"]}` |
18
22
 
19
- `script` cannot combine with cmd/stdin/spawn/PTY. Direct local command execution is not provided; use native local shell tools. Background jobs are oneshot-only, require the official jobs service/controller, and are read/killed with `job_output`/`job_kill`; they have no default foreground timeout. An explicit timeout still applies. SSH keys/config stay on the origin; BYO identities are not persisted across new/cold Agents. PTY backlog recovery and live probes are not part of this release.
23
+ `mode` no longer exists: append `:pty` to a transport target to create, address a live shell as `pty:<id|label>`. `scriptPath` cannot combine with cmd/stdin/spawn/PTY. Direct local command execution is not provided; use native local shell tools. Background jobs require the official jobs service/controller and are read/killed with `job_output`/`job_kill`; on PTY lanes a background job's jobId is the commandId (a timeout handoff keeps them as separate ledger fields). SSH keys/config stay on the origin; BYO identities are not persisted across new/cold Agents. PTY backlog recovery and live probes are not part of this release.
24
+
25
+ PTY state is durable and readable: every dispatch returns exit, cwd and `envDelta` (variable names and change markers only — values never leave the endpoint; fetch them yourself if needed). `close` is idempotent (`closed:false, reason:"not-found"|"already-closed-or-reaped"`); a session with a running job refuses close unless `force:true`, which kills the group and reports `killed`. A busy session rejects new commands instead of queueing; open another PTY or use a oneshot. Idle PTYs are reaped after 15 minutes; an Agent holds at most 10 live sessions across connections, and dead sessions do not count. `hub:<m4>:pty` selects the Hub lane's PTY but exposes no session id, label, parallelism or background works there.
20
26
 
21
27
  Native/PTY output retains at most 4,000,000 UTF-16 units per stream; Hub oneshot retains 4 MiB bytes. Normal returned tails are smaller. Read `outputStats` units and dropped/returned counters rather than treating character counts as bytes. Discarded output is not recoverable after restart.
22
28
 
23
29
  ## File operations: native CLI semantics
24
30
 
25
- `remote` addresses targets once (`ssh:<name>`, `byossh:<label>`, `docker:<name>`, `incus:<name>`, `hub:<machine>`, local paths) and hands the actual transfer to the native CLI. `argv` is an ordered array of native option tokens — one element = one argv token, appended verbatim before the parsed `src`/`dst`. There is no option whitelist, no shell parsing, and no forced defaults: no hidden `--dry-run`, no forced archive/timeout, no automatic verification. A dry run means passing `--dry-run` yourself; unknown or incompatible options fail in the native CLI and its exit code and stderr come back as-is. Success of a native command is never reported as independently verified — `verified:false` on native copy receipts means exactly that.
31
+ `remote` addresses targets once (`ssh:<name>`, `ssh:<label>` for a BYO identity, `docker:<name>`, `incus:<name>`, `hub:<machine>`, local paths) and hands the actual transfer to the native CLI. `argv` is an ordered array of native option tokens — one element = one argv token, appended verbatim before the parsed `src`/`dst`. There is no option whitelist, no shell parsing, and no forced defaults: no hidden `--dry-run`, no forced archive/timeout, no automatic verification. A dry run means passing `--dry-run` yourself; unknown or incompatible options fail in the native CLI and its exit code and stderr come back as-is. Success of a native command is never reported as independently verified — `verified:false` on native copy receipts means exactly that.
26
32
 
27
33
  ### copy routes
28
34
 
@@ -39,7 +45,7 @@ The native CLIs do not create destination parents or verify results on their own
39
45
 
40
46
  ### rsync routes
41
47
 
42
- rsync supports SSH/byossh endpoints with at most one local endpoint. Local↔SSH runs `rsync` on the origin with the endpoint's SSH identity wired through `RSYNC_RSH`; an explicit `-e`/`--rsh` in `argv` replaces that wiring per rsync's own rules (the command must then carry its own port/key/config). Nothing else is injected — no archive, no timeout, no dry-run, no remote owner, no `--rsync-path`.
48
+ rsync supports SSH endpoints (native aliases and BYO identities) with at most one local endpoint. Local↔SSH runs `rsync` on the origin with the endpoint's SSH identity wired through `RSYNC_RSH`; an explicit `-e`/`--rsh` in `argv` replaces that wiring per rsync's own rules (the command must then carry its own port/key/config). Nothing else is injected — no archive, no timeout, no dry-run, no remote owner, no `--rsync-path`.
43
49
 
44
50
  SSH↔SSH prefers the native forwarding route: the destination runs a temporary loopback-only rsync daemon, `ssh -L` brings it to an origin port, `ssh -R` carries it onto the source's loopback, and the source's own native rsync client performs one protocol conversation through the chain. The origin only forwards rsync protocol bytes; the destination's existing file data feeds real delta matching (`--stats` shows Matched data dominating for a small change on a large file). If an endpoint refuses TCP forwarding (`AllowTcpForwarding no`), a standard-stream route would be attempted; on the current OpenSSH/rsync combination (10.2 / 3.4.1) that route reports itself unavailable — the error says so and nothing is copied. rsync never falls back to a full copy or to Python.
45
51
 
@@ -51,6 +57,6 @@ File operations require the corresponding CLI on the origin (`scp`, `rsync`, `do
51
57
 
52
58
  ## One-shot command requirements
53
59
 
54
- One-shot SSH/byossh commands (foreground, background, script and literal stdin) run through ONE native SSH exec: the command executes under bash with a small POSIX wrapper whose TERM/HUP trap kills the command's whole process group, so backgrounded children do not outlive the session teardown. Remote prerequisites: bash (POSIX) — **no Python, no owner/lease protocol, no flock/monotonic clock, no extra sessions**. On normal completion the wrapper's EXIT trap reaps the command's whole process group (backgrounded children included — they neither outlive the call nor hold the channel open), and the exit code passes through verbatim. On timeout/cancellation the local transport dies at once and a dedicated cleanup session reads the pidfile the wrapper left and kills the group (TERM→KILL); sshd delivers no signal to a no-PTY session, so this explicit cleanup is the remote kill path.
60
+ One-shot SSH commands (native alias or BYO identity; foreground, background, scriptPath and literal stdin) run through ONE native SSH exec: the command executes under bash with a small POSIX wrapper whose TERM/HUP trap kills the command's whole process group, so backgrounded children do not outlive the session teardown. Remote prerequisites: bash (POSIX) — **no Python, no owner/lease protocol, no flock/monotonic clock, no extra sessions**. On normal completion the wrapper's EXIT trap reaps the command's whole process group (backgrounded children included — they neither outlive the call nor hold the channel open), and the exit code passes through verbatim. On timeout/cancellation the local transport dies at once and a dedicated cleanup session reads the pidfile the wrapper left and kills the group (TERM→KILL); sshd delivers no signal to a no-PTY session, so this explicit cleanup is the remote kill path.
55
61
 
56
62
  Docker/Incus one-shots use the same pure-CLI lane (`docker exec` / `incus exec` with the same wrapper); container prerequisites are the container's own bash. PTY paths are unchanged (`ssh -tt bash`, docker via script(1), incus interactive) and never required Python.
@@ -1,53 +1,25 @@
1
- # kernel-provisioning Specification
1
+ # Existing Python interpreter discovery
2
2
 
3
- ## Purpose
4
- better-dsh 的 REPL kernel(IPython)供给契约:独占 managed venv(插件包目录内,`kernelEnvDir` 可显式覆盖)、版本锁定(ipykernel 7.3.0 / dill 0.4.1,uv 与 pip 双路径)、三级触发供给(postinstall 加速器 / daemon spin-up 主路径 / 首用 lazy 兜底)全 fail-open 且幂等、供给状态可观测;npm 包不内嵌预建 venv(供给能力随包,供给结果不随包)。
3
+ Updated 2026-10-12 by explicit user correction. This replaces the former iKernel managed-provisioning contract. Execution and recovery are specified in [OMP Python REPL](../omp-python-repl-port/spec.md); consumer behavior is in its [runtime guide](../omp-python-repl-port/runtime-guide.md).
5
4
 
6
5
  ## Requirements
7
6
 
8
- ### Requirement: Exclusive managed kernel venv
7
+ ### Existing interpreters only
9
8
 
10
- The plugin SHALL provision its REPL kernel interpreter exclusively inside a managed venv rooted in the plugin's own package directory (web-profile scope in production deployments, `kernelEnvDir` as the explicit override), and SHALL NOT install into, mutate, or depend on the user's global Python environment; kernel probes MUST NOT import user-site modules, and the distributed npm package MUST NOT embed a prebuilt venv (provisioning capability ships, not provisioning results).
9
+ Better SHALL run an existing Python interpreter. It SHALL NOT invoke uv/pip, download Python, create or repair a virtual environment, or place Python environment/cache state in the installed npm package. No CPython implementation or Python3.11 preference is applied. The packaged dill0.4.1 checkpoint helper requires Python >=3.9.
11
10
 
12
- #### Scenario: Venv lives inside the plugin territory
11
+ ### Explicit selection
13
12
 
14
- - **WHEN** the plugin is installed into a web profile and the kernel is provisioned
15
- - **THEN** the venv root is inside the plugin's package directory (or the explicitly configured `kernelEnvDir`), and no file outside that territory is required for kernel operation
13
+ An explicit `python` executable name/path SHALL bypass automatic discovery. Bare `python3` is an explicit executable name, not a provisioning sentinel. Relative paths SHALL resolve against the session workspace and `~/` against the user's home. An unusable explicit selection SHALL report the actual failure without silently substituting another interpreter. Existing explicit virtual environments SHALL receive their activation environment.
16
14
 
17
- #### Scenario: User global environment untouched
15
+ ### Automatic discovery
18
16
 
19
- - **WHEN** provisioning runs on a host with an existing global Python installation
20
- - **THEN** no package is installed into, and no configuration of, the user's global environment is modified
17
+ Without explicit selection, Better SHALL probe an existing active virtual/Conda environment or workspace `.venv`/`venv`, then PATH `python3` and `python`. A broken automatically discovered candidate SHALL permit a working fallback without repair. Resolved executable identity and activation environment SHALL reach the spawned child. One session's project interpreter SHALL NOT be cached as the choice for another workspace.
21
18
 
22
- ### Requirement: Robust provisioning with pinned versions
19
+ ### Readiness and failure
23
20
 
24
- Provisioning SHALL redirect its tool caches (uv) to a plugin-territory location so read-only home directories cannot block it, SHALL fall back to `python3 -m venv` + ensurepip when `uv` is unavailable, and SHALL install pinned, tested versions of `ipykernel` and `dill` alongside the pinned CPython version; provisioning MUST be idempotent (a complete venv is reused; a partial one is repaired).
21
+ Availability is checked when the session needs its Python child. Plugin mounting SHALL NOT provision an environment. With no working candidate, the error SHALL identify attempted interpreters and explain how to install Python or configure an existing executable. `npm run kernel:check` SHALL perform discovery only, with no installation side effects.
25
22
 
26
- #### Scenario: Read-only home cache does not block
23
+ ### Runtime state and migration
27
24
 
28
- - **WHEN** the kernel is provisioned on a host whose `~/.cache` is read-only
29
- - **THEN** provisioning succeeds using the redirected cache location
30
-
31
- #### Scenario: Pinned versions installed
32
-
33
- - **WHEN** provisioning creates or repairs the venv
34
- - **THEN** the installed `ipykernel` and `dill` match the pinned tested versions, verifiable by the kernel probe
35
-
36
- ### Requirement: Provisioning ladder — install-time, daemon spin-up, first-use
37
-
38
- The plugin SHALL provision the kernel through a three-trigger ladder converging on the same managed venv, every trigger fail-open (a failure or suppression at any level degrades to a logged status message and NEVER blocks package installation, daemon startup, or plugin mounting, and never asks the user for approval): (1) an install-time postinstall best-effort step that runs only when the package manager executes build scripts; (2) a daemon spin-up check as the PRIMARY path — when the plugin is brought up on the host plane by the host daemon, it asynchronously verifies the kernel environment and provisions it immediately when absent (uv → `python3 -m venv` → explicit configuration), so an agent session never discovers a missing kernel at first use; (3) first-use lazy provisioning as the final fallback. Provisioning status (provisioning/ready/degraded/failed) SHALL be logged, and failure messages MUST name the available remedies (`npm run kernel:venv`, `kernelAutoInstall`, explicit `python`).
39
-
40
- #### Scenario: Daemon spin-up provisions before any agent session
41
-
42
- - **WHEN** the host daemon starts and brings the plugin up on the host plane, and the managed venv is absent
43
- - **THEN** the plugin asynchronously provisions the kernel within the spin-up window without blocking daemon startup or plugin mounting, and a new agent session's first REPL cell finds the kernel already in place
44
-
45
- #### Scenario: Suppressed postinstall still reaches a working kernel
46
-
47
- - **WHEN** the package manager skips the plugin's postinstall script and the daemon later starts
48
- - **THEN** the spin-up check provisions the kernel and REPL cells execute, with the install itself never having failed and no approval ever having been requested
49
-
50
- #### Scenario: Failure is non-blocking and visible
51
-
52
- - **WHEN** provisioning fails on a constrained host at any trigger
53
- - **THEN** the installation, daemon startup, and plugin mounting all succeed; the surfaced error names the concrete remedy commands/configuration paths
25
+ Checkpoints SHALL remain under the native profile home (or explicitly configured snapshot directory), isolated by profile/session. Old `kernelEnvDir`, `kernelPythonVersion`, `kernelAutoInstall` fields and `kernel:venv` command are removed. Existing package-local `.venv-kernel`/`.uv-cache` files SHALL be left untouched and SHALL NOT be selected automatically. An interpreter/environment change SHALL pass the existing checkpoint compatibility checks and report incompatible recovery explicitly; no user cell is replayed.
@@ -2,12 +2,14 @@
2
2
 
3
3
  Implementation and acceptance are tracked in the [plan](../../superpowers/plans/2026-10-12-omp-python-repl-port.md). The [spec](spec.md) defines the recovery boundary; this guide explains the consumer contract being implemented.
4
4
 
5
- Better runs a persistent Python child for each DSH session. Node communicates with an unchanged, pinned OMP runner over JSON lines. Better owns tool bindings, cancellation, output mapping and checkpoint management. The runtime requires Python >=3.9; the compatibility managed-environment path may let uv download that interpreter, while an explicit path uses an existing installation. Better ships the Python runner and dill 0.4.1 source. It does not install Jupyter, IPython, ZeroMQ or dill into the consumer's interpreter. OMP's Agent-specific prelude is not exposed as a DSH feature.
5
+ Better runs a persistent Python child for each DSH session. Node communicates with an unchanged, pinned OMP runner over JSON lines. Better owns tool bindings, cancellation, output mapping and checkpoint management. Interpreter discovery reuses an explicitly configured interpreter, otherwise an active/project environment then PATH `python3` or `python`. It probes candidates and falls back from broken automatically discovered environments; it never invokes uv/pip, installs Python or creates a venv. Explicit relative paths and project environments resolve against the session workspace. Python >=3.9 is required by the packaged dill0.4.1 checkpoint helper; no3.11 or CPython implementation is selected. Better ships the Python runner and dill 0.4.1 source. It does not install Jupyter, IPython, ZeroMQ or dill into the consumer's interpreter. OMP's Agent-specific prelude is not exposed as a DSH feature.
6
6
 
7
7
  Variables, functions, classes and imports remain available across cells while the child lives. Top-level await works; a top-level return is invalid. The final expression supplies the completion value and print supplies logs. Host tools remain callable with `await tool.<name>({...})`. Internal initialization and checkpoint cells do not appear as user results or user scratchpad variables.
8
8
 
9
9
  `tool.read` forwards to the current DSH native read tool with its usual permissions, scheduling and results. Better supplies the call-site helper required by OMP's compiler without enabling OMP speculative reads. Interactive `input()` is unsupported and returns an actionable error. Output uses the existing text/log/result contract; a nontext-only display produces an explicit MIME notice.
10
10
 
11
+ `npm run kernel:check` checks existing interpreter availability without provisioning. Legacy provisioning fields `kernelEnvDir`, `kernelPythonVersion`, `kernelAutoInstall` and the `kernel:venv` command are removed. Old package-local environments/caches are left untouched and are not auto-selected. An interpreter change may make existing checkpoints incompatible; recovery reports that condition explicitly.
12
+
11
13
  ## Checkpoint boundary
12
14
 
13
15
  In a native DSH profile, checkpoints belong under that profile's resolved DSH home and are isolated by profile and session. An explicitly configured checkpoint directory takes precedence; `snapshotDir: ""` disables persistence and leaves process-local state only. A standalone runtime without a native profile must explicitly choose a checkpoint directory.
@@ -59,3 +59,10 @@ Best effort 不应退回普通标准库 `pickle`,否则对 REPL 函数、闭
59
59
  5. 资源句柄、网络连接和其他进程副作用始终超出承诺;用户代码应能检测和重建这些资源。
60
60
 
61
61
  因此,第二阶段的目标是“尽可能恢复模型的 Python scratchpad 变量,并准确报告缺口”,不是恢复整个 Python 进程。是否加这一阶段,不影响先完成 OMP 执行链移植与去除 Jupyter/ZMQ 依赖。
62
+
63
+
64
+ ## 2026-10-12 interpreter discovery correction
65
+
66
+ After the0.3.1 release, user rejected retaining the old iKernel-era uv/3.11 provisioning path and package-local venv. The correction reuses an explicitly configured interpreter; otherwise it probes an existing active/project environment and PATH `python3` then `python`. A broken automatic candidate may fall back; a broken explicit selection is reported. Relative paths/project environments belong to the session workspace. No interpreter is downloaded, no uv/pip process is launched, and no venv or cache is created in the installed package. Original OMP/dill resources and checkpoint ownership remain unchanged. Packaged dill0.4.1 has a Python >=3.9 support floor; there is no3.11 or CPython implementation preference.
67
+
68
+ Legacy provisioning fields and command are removed, replaced by a read-only `kernel:check`. Existing old environment files are left untouched. Interpreter/environment changes may invalidate old checkpoints and must produce an explicit recovery notice. Delivery and target acceptance are recorded in [environment correction report](../../test-reports/2026-10-12-python-environment-discovery.md). This correction has separate test artifacts and does not authorize an npm release.
@@ -1,5 +1,7 @@
1
1
  # remote-pty-label Specification
2
2
 
3
+ > **Superseded(2026-10-19)**:本 spec 的 `byop:<label>` 入口、`mode` 锁定、`p:<label>` 池键与「同 label 复用」语义已被 [PTY Workbench v2 计划](../../superpowers/plans/2026-10-19-pty-workbench.md) 取代:BYO-PTY 并入 `pty:` selector(`spawn` 在场 = BYO)、`byossh:`/`byop:` 前缀移除、`mode` 字段移除且未知参数不强制拒绝、label 与机器名统一池、id 为 `pty-xxxx` / `ssh-xxxx`。下文保留为历史契约参照。
4
+
3
5
  ## Purpose
4
6
 
5
7
  `remote` 工具的持久化 BYO-PTY 通道从「`spawn` 命令原文即会话键」改为「`target` 选择器寻址的具名会话」。旧实现的池键就是整条 spawn 文本,于是「同一条命令」与「同一个会话」被绑死:改一个 token 就**静默**新开一个 shell(cwd/env 全丢、零提示),roster 又拿整整一条 shell 命令当会话名。本规格固定新契约——`byop:<label>` 是唯一入口、label 由调用方(模型)自选并因此成为复用入口、`spawn` 降格为该 label 首次创建时的伴生参数——并据此定义创建/复用/冲突三分支、失败面,以及裸名候选表的末位新增项。
@@ -2,6 +2,7 @@
2
2
 
3
3
  - 日期:2026-10-10(Asia/Hong_Kong)
4
4
  - 状态:2026-10-10 user 已审阅并明确授权执行;Task0–7 已独立审阅通过;Task8 完整双包消费者验收进行中,L 实装暴露的 worker/SSH cleanup 缺陷已进入修复验证。用户亲验与发布尚未放行。
5
+ - 0.3.2-b 对齐(2026-10-19):`script` 已定名 `scriptPath`;`mode` 字段已移除(用 `<transport>:pty[:label]` 创建、`pty:<id|label>` 复用);`byossh:`/`byop:` 前缀已移除(`ssh`/`spawn` 参数在场即 BYO)。下文旧调用形仅作历史。
5
6
  - 输入:[2026-10-09 方向稿](../../10_plans/2026-10-09-remote-tool-optimization-directions.md)及 2026-10-10 会话裁决。
6
7
  - 执行计划:[2026-10-10-remote-tool-enhancement.md](../../superpowers/plans/2026-10-10-remote-tool-enhancement.md)。本规格优先于方向稿的冲突条款。
7
8
 
@@ -0,0 +1,4 @@
1
+ import "./util.js";
2
+ import { t as BrowserWebSocketTransport } from "./BrowserWebSocketTransport2.js";
3
+
4
+ export { BrowserWebSocketTransport };
@@ -1,4 +1,4 @@
1
- import { o as debugCatchError } from "./util-BHL_dfV_.js";
1
+ import { o as debugCatchError } from "./util.js";
2
2
 
3
3
  //#region node_modules/puppeteer-core/lib/puppeteer/common/BrowserWebSocketTransport.js
4
4
  /**
@@ -0,0 +1,4 @@
1
+ import "./main.js";
2
+ import { t as convertPuppeteerChannelToBrowsersChannel } from "./LaunchOptions2.js";
3
+
4
+ export { convertPuppeteerChannelToBrowsersChannel };
@@ -1,4 +1,4 @@
1
- import { b as ChromeReleaseChannel } from "./main-BeCgUd20.js";
1
+ import { b as ChromeReleaseChannel } from "./main.js";
2
2
 
3
3
  //#region node_modules/puppeteer-core/lib/puppeteer/node/LaunchOptions.js
4
4
  /**
@@ -1,5 +1,5 @@
1
- import { n as __require, r as __toESM, t as __commonJS } from "./chunk-DQxbwPpZ.js";
2
- import { D as packageVersion, o as debugCatchError } from "./util-BHL_dfV_.js";
1
+ import { n as __require, r as __toESM, t as __commonJS } from "./chunk.js";
2
+ import { D as packageVersion, o as debugCatchError } from "./util.js";
3
3
 
4
4
  //#region node_modules/ws/lib/constants.js
5
5
  var require_constants = /* @__PURE__ */ __commonJS({ "node_modules/ws/lib/constants.js": ((exports, module) => {
@@ -0,0 +1,4 @@
1
+ import "./util.js";
2
+ import { t as NodeWebSocketTransport } from "./NodeWebSocketTransport.js";
3
+
4
+ export { NodeWebSocketTransport };
@@ -1,7 +1,7 @@
1
- import { n as __require } from "./chunk-DQxbwPpZ.js";
2
- import { A as stringToTypedArray, G as first, H as delayWhen, K as firstValueFrom, L as combineLatest, M as assert, Q as map, S as timeout, St as debug, Tt as environment, V as defer, W as filter, at as race, b as isString, bt as TouchError, c as evaluationString, d as fromEmitterEvent, g as isDate, gt as ProtocolError, h as getSourceUrlComment, ht as ConnectionClosedError, i as SOURCE_URL_REGEX, k as stringToBase64, lt as switchMap, m as getSourcePuppeteerURLIfAvailable, o as debugCatchError, ot as raceWith, q as from, r as PuppeteerURL, rt as of, s as debugError, u as fromAbortSignal, v as isPlainObject, vt as TargetCloseError, x as parsePDFOptions, xt as UnsupportedOperation, y as isRegExp, yt as TimeoutError } from "./util-BHL_dfV_.js";
3
- import { $n as Deferred, $t as scriptInjector, Bt as throwIfDetached, Ct as WebWorkerEvent, Dn as DeviceRequestPrompt, Dt as normalizeHeaderValue, Fn as Browser, Hn as DisposableStack, Ht as bindIsolatedHandle, In as WEB_PERMISSION_TO_PROTOCOL_PERMISSION, It as Dialog, Jn as CallbackRegistry, Kn as disposeSymbol, Ln as CDPSession, Lt as Accessibility, Mn as invokeAtMostOnceForArguments, Mt as InterceptResolutionAction, Nn as throwIfDisposed, Nt as STATUS_TEXTS, On as BrowserContext, Ot as SecurityDetails, Pt as handleError, Rt as Frame, St as WebWorker, Ut as JSHandle, Vt as ElementHandle, Zn as isErrorLike, _t as Mouse, an as stringifyFunction, cn as Tracing, dt as ConsoleMessage, en as ARIAQueryHandler, fn as Coverage, gt as Keyboard, ht as TargetType, in as interpolateFunction, jn as inertIfDisposed, jt as HTTPRequest, kn as bubble, kt as HTTPResponse, mn as Page, mt as Target, nn as LazyArg, on as AsyncIterableUtil, sn as FileChooser, un as EmulationManager, vt as MouseButton, wt as Realm, yt as Touchscreen, zn as EventEmitter } from "./index-browser-B-9jm12n.js";
4
- import "./BrowserWebSocketTransport-wm99h94r.js";
1
+ import { n as __require } from "./chunk.js";
2
+ import { A as stringToTypedArray, G as first, H as delayWhen, K as firstValueFrom, L as combineLatest, M as assert, Q as map, S as timeout, St as debug, Tt as environment, V as defer, W as filter, at as race, b as isString, bt as TouchError, c as evaluationString, d as fromEmitterEvent, g as isDate, gt as ProtocolError, h as getSourceUrlComment, ht as ConnectionClosedError, i as SOURCE_URL_REGEX, k as stringToBase64, lt as switchMap, m as getSourcePuppeteerURLIfAvailable, o as debugCatchError, ot as raceWith, q as from, r as PuppeteerURL, rt as of, s as debugError, u as fromAbortSignal, v as isPlainObject, vt as TargetCloseError, x as parsePDFOptions, xt as UnsupportedOperation, y as isRegExp, yt as TimeoutError } from "./util.js";
3
+ import { $n as Deferred, $t as scriptInjector, Bt as throwIfDetached, Ct as WebWorkerEvent, Dn as DeviceRequestPrompt, Dt as normalizeHeaderValue, Fn as Browser, Hn as DisposableStack, Ht as bindIsolatedHandle, In as WEB_PERMISSION_TO_PROTOCOL_PERMISSION, It as Dialog, Jn as CallbackRegistry, Kn as disposeSymbol, Ln as CDPSession, Lt as Accessibility, Mn as invokeAtMostOnceForArguments, Mt as InterceptResolutionAction, Nn as throwIfDisposed, Nt as STATUS_TEXTS, On as BrowserContext, Ot as SecurityDetails, Pt as handleError, Rt as Frame, St as WebWorker, Ut as JSHandle, Vt as ElementHandle, Zn as isErrorLike, _t as Mouse, an as stringifyFunction, cn as Tracing, dt as ConsoleMessage, en as ARIAQueryHandler, fn as Coverage, gt as Keyboard, ht as TargetType, in as interpolateFunction, jn as inertIfDisposed, jt as HTTPRequest, kn as bubble, kt as HTTPResponse, mn as Page, mt as Target, nn as LazyArg, on as AsyncIterableUtil, sn as FileChooser, un as EmulationManager, vt as MouseButton, wt as Realm, yt as Touchscreen, zn as EventEmitter } from "./index-browser.js";
4
+ import "./BrowserWebSocketTransport2.js";
5
5
 
6
6
  //#region node_modules/mitt/dist/mitt.mjs
7
7
  function mitt_default(n) {
@@ -172,6 +172,20 @@ function FailoverRow({ catalog, loadConfig, save, t }) {
172
172
 
173
173
  //#endregion
174
174
  //#region src/mobile/gesture.ts
175
+ /**
176
+ * Whether the mobile adaptation mounts at all (2026-10-18 user ruling):
177
+ * touch-first means the device's PRIMARY input is a finger. A query
178
+ * answering FALSE — a hover-capable or fine primary pointer — decides
179
+ * desktop on its own (a touchscreen laptop keeps desktop behavior); the
180
+ * pair answering true is the standard idiom; and only when the queries
181
+ * are entirely unavailable does `maxTouchPoints` become the honest
182
+ * degrade (every such legacy device is a phone or tablet anyway).
183
+ */
184
+ function isTouchFirstDevice(input) {
185
+ if (input.hoverNone === false || input.pointerCoarse === false) return false;
186
+ if (input.hoverNone === true && input.pointerCoarse === true) return true;
187
+ return (input.maxTouchPoints ?? 0) > 0;
188
+ }
175
189
  /** Resolved defaults — the `1706b81` constants (120 / viewport÷4 / 40 / 1.3)
176
190
  * plus the one addition, the velocity gate. A slow text-selection drag runs
177
191
  * ~0.13 px/ms; 0.15 px/ms filters those while any comfortable sweep (0.4
@@ -260,6 +274,18 @@ function resolveMobileConfig(pageConfig) {
260
274
 
261
275
  //#endregion
262
276
  //#region src/mobile/client/index.ts
277
+ /**
278
+ * One media query, or `undefined` when the environment has none (old
279
+ * webviews throw or omit `matchMedia`) — the probe then rides the
280
+ * touch-points fallback in {@link isTouchFirstDevice}.
281
+ */
282
+ function safeMatchMedia(query) {
283
+ try {
284
+ return globalThis.matchMedia?.(query) ?? void 0;
285
+ } catch {
286
+ return;
287
+ }
288
+ }
263
289
  /** Left sidebar state, live off AppFrame's semantic attribute. */
264
290
  function readLeftCollapsed() {
265
291
  return document.querySelector("[data-sidebar-collapsed]") !== null;
@@ -340,17 +366,46 @@ function mobileCss() {
340
366
  }
341
367
  /**
342
368
  * Mount the mobile feature: style injection plus the swipe listeners.
343
- * Inert unless the host's boot script opted the page in.
369
+ * Inert unless the host's boot script opted the page in AND the device is
370
+ * touch-first (2026-10-18 user ruling). Capability flips (a tablet's
371
+ * primary pointer hardware changing) re-evaluate and mount/unmount the
372
+ * surface live; the media queries answer on every browser since ~2015 and
373
+ * a webview without `matchMedia` degrades to the touch-points probe.
344
374
  *
345
375
  * @param ctx - client root context.
346
- * @returns the effect disposer (style tag + listeners removed on unload).
347
376
  */
348
377
  function setupMobileLayout(ctx) {
349
378
  const pageConfig = globalThis.__DASHR_MOBILE__;
350
379
  const config = resolveMobileConfig(pageConfig);
351
380
  if (!config.enabled) return;
352
- const styleTagId = "better-dsh/mobile";
381
+ const coarseQuery = safeMatchMedia("(pointer: coarse)");
382
+ const hoverQuery = safeMatchMedia("(hover: none)");
353
383
  ctx.effect(() => {
384
+ let disposeSurface;
385
+ const apply$1 = () => {
386
+ const touchFirst = isTouchFirstDevice({
387
+ hoverNone: hoverQuery?.matches,
388
+ pointerCoarse: coarseQuery?.matches,
389
+ maxTouchPoints: navigator.maxTouchPoints
390
+ });
391
+ if (touchFirst && disposeSurface === void 0) disposeSurface = mountTouchSurface();
392
+ else if (!touchFirst && disposeSurface !== void 0) {
393
+ disposeSurface();
394
+ disposeSurface = void 0;
395
+ }
396
+ };
397
+ coarseQuery?.addEventListener("change", apply$1);
398
+ hoverQuery?.addEventListener("change", apply$1);
399
+ apply$1();
400
+ return () => {
401
+ coarseQuery?.removeEventListener("change", apply$1);
402
+ hoverQuery?.removeEventListener("change", apply$1);
403
+ disposeSurface?.();
404
+ };
405
+ }, "dashr: mobile layout");
406
+ /** The style tag + gesture listeners, mounted and disposed as one surface. */
407
+ function mountTouchSurface() {
408
+ const styleTagId = "better-dsh/mobile";
354
409
  const tag = document.createElement("style");
355
410
  tag.dataset.plugin = "better-dsh";
356
411
  tag.dataset.pluginCss = styleTagId;
@@ -363,9 +418,7 @@ function setupMobileLayout(ctx) {
363
418
  const onPointerDown = (event) => {
364
419
  start = void 0;
365
420
  const viewport = window.innerWidth;
366
- const coarsePointer = window.matchMedia?.("(pointer: coarse)").matches === true;
367
421
  const panels = readPanelState();
368
- if (!coarsePointer && !panels.leftCollapsed && !panels.rightOpen) return;
369
422
  if (!admitsSwipeStart(event.clientX, viewport, panels, config)) return;
370
423
  start = {
371
424
  x: event.clientX,
@@ -405,7 +458,7 @@ function setupMobileLayout(ctx) {
405
458
  tag.remove();
406
459
  Promise.resolve(offLayout.dispose());
407
460
  };
408
- }, "dashr: mobile layout");
461
+ }
409
462
  }
410
463
 
411
464
  //#endregion
@@ -1,4 +1,4 @@
1
- import { t as Schema } from "../index-DTDXUe6m.js";
1
+ import { t as Schema } from "../index2.js";
2
2
  import { Context, Volatile } from "@deepseek-ai/cordis";
3
3
 
4
4
  //#region src/compaction/config.d.ts
@@ -1,4 +1,4 @@
1
- import { t as Schema } from "../lib-C8_jXvwA.js";
1
+ import { t as Schema } from "../lib.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
 
4
4
  //#region src/compaction/config.ts
@@ -1,4 +1,4 @@
1
- import { t as Schema } from "../index-DTDXUe6m.js";
1
+ import { t as Schema } from "../index2.js";
2
2
  import { Context, Volatile } from "@deepseek-ai/cordis";
3
3
 
4
4
  //#region src/failover/config.d.ts
@@ -1,4 +1,4 @@
1
- import { t as Schema } from "../lib-C8_jXvwA.js";
1
+ import { t as Schema } from "../lib.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { RetryId } from "@deepseek-ai/dsh-llm-retry";
@@ -1,4 +1,4 @@
1
- import { f as UrlSchemesError, t as buildFsLayerResolver } from "../wrap-0VA0uFx8.js";
1
+ import { f as UrlSchemesError, t as buildFsLayerResolver } from "../wrap.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  import { dirname, sep } from "node:path";
4
4
  import { stat } from "node:fs/promises";
@@ -1,4 +1,4 @@
1
- import { $ as merge, A as stringToTypedArray, B as defaultIfEmpty, Et as isNode, G as first, I as catchError, K as firstValueFrom, M as assert, N as EMPTY, O as mergeUint8Arrays, P as ReplaySubject, Q as map, R as concat, S as timeout, St as debug, T as withSourcePuppeteerURLIfNone, Tt as environment, U as distinctUntilChanged, V as defer, W as filter, X as ignoreElements, Y as identity, a as UTILITY_WORLD_NAME, at as race, b as isString, bt as TouchError, c as evaluationString, ct as startWith, d as fromEmitterEvent, dt as takeUntil, et as mergeMap, f as getReadableAsTypedArray, ft as tap, gt as ProtocolError, h as getSourceUrlComment, ht as ConnectionClosedError, i as SOURCE_URL_REGEX, it as pipe, j as typedArrayToBase64, k as stringToBase64, l as filterAsync, lt as switchMap, m as getSourcePuppeteerURLIfAvailable, mt as timer, n as NETWORK_IDLE_TIME, nt as noop, o as debugCatchError, ot as raceWith, p as getReadableFromProtocolStream, pt as throwIfEmpty, q as from, r as PuppeteerURL, rt as of, s as debugError, st as retry, t as DEFAULT_VIEWPORT, tt as mergeScan, u as fromAbortSignal, ut as take, vt as TargetCloseError, w as validateDialogType, x as parsePDFOptions, xt as UnsupportedOperation, yt as TimeoutError } from "./util-BHL_dfV_.js";
1
+ import { $ as merge, A as stringToTypedArray, B as defaultIfEmpty, Et as isNode, G as first, I as catchError, K as firstValueFrom, M as assert, N as EMPTY, O as mergeUint8Arrays, P as ReplaySubject, Q as map, R as concat, S as timeout, St as debug, T as withSourcePuppeteerURLIfNone, Tt as environment, U as distinctUntilChanged, V as defer, W as filter, X as ignoreElements, Y as identity, a as UTILITY_WORLD_NAME, at as race, b as isString, bt as TouchError, c as evaluationString, ct as startWith, d as fromEmitterEvent, dt as takeUntil, et as mergeMap, f as getReadableAsTypedArray, ft as tap, gt as ProtocolError, h as getSourceUrlComment, ht as ConnectionClosedError, i as SOURCE_URL_REGEX, it as pipe, j as typedArrayToBase64, k as stringToBase64, l as filterAsync, lt as switchMap, m as getSourcePuppeteerURLIfAvailable, mt as timer, n as NETWORK_IDLE_TIME, nt as noop, o as debugCatchError, ot as raceWith, p as getReadableFromProtocolStream, pt as throwIfEmpty, q as from, r as PuppeteerURL, rt as of, s as debugError, st as retry, t as DEFAULT_VIEWPORT, tt as mergeScan, u as fromAbortSignal, ut as take, vt as TargetCloseError, w as validateDialogType, x as parsePDFOptions, xt as UnsupportedOperation, yt as TimeoutError } from "./util.js";
2
2
 
3
3
  //#region node_modules/puppeteer-core/lib/puppeteer/util/Deferred.js
4
4
  /**
@@ -20792,7 +20792,7 @@ async function _connectToBiDiBrowser(connectionTransport, url, options) {
20792
20792
  const { bidiConnection, cdpConnection, closeCallback } = await getBiDiConnection(connectionTransport, url, options);
20793
20793
  return await (await import(
20794
20794
  /* webpackIgnore: true */
20795
- "./bidi-CwabFH07.js"
20795
+ "./bidi.js"
20796
20796
  )).BidiBrowser.create({
20797
20797
  connection: bidiConnection,
20798
20798
  cdpConnection,
@@ -20815,7 +20815,7 @@ async function _connectToBiDiBrowser(connectionTransport, url, options) {
20815
20815
  async function getBiDiConnection(connectionTransport, url, options) {
20816
20816
  const BiDi = await import(
20817
20817
  /* webpackIgnore: true */
20818
- "./bidi-CwabFH07.js"
20818
+ "./bidi.js"
20819
20819
  );
20820
20820
  const { slowMo = 0, protocolTimeout, idGenerator = createIncrementalIdGenerator() } = options;
20821
20821
  const pureBidiConnection = new BiDi.BidiConnection(url, connectionTransport, idGenerator, slowMo, protocolTimeout);
@@ -20845,7 +20845,7 @@ async function getBiDiConnection(connectionTransport, url, options) {
20845
20845
  //#endregion
20846
20846
  //#region node_modules/puppeteer-core/lib/puppeteer/common/BrowserConnector.js
20847
20847
  const getWebSocketTransportClass = async () => {
20848
- return isNode ? (await import("./NodeWebSocketTransport-BRLjmu2R.js")).NodeWebSocketTransport : (await import("./BrowserWebSocketTransport-D1aFReu_.js")).BrowserWebSocketTransport;
20848
+ return isNode ? (await import("./NodeWebSocketTransport2.js")).NodeWebSocketTransport : (await import("./BrowserWebSocketTransport.js")).BrowserWebSocketTransport;
20849
20849
  };
20850
20850
  /**
20851
20851
  * Users should never call this directly; it's called when calling
@@ -20887,10 +20887,10 @@ async function getConnectionTransport(options) {
20887
20887
  endpointUrl: connectionURL
20888
20888
  };
20889
20889
  } else if (options.channel && isNode) {
20890
- const { detectBrowserPlatform, resolveDefaultUserDataDir, Browser: Browser$1 } = await import("./main-Dv_UQqG2.js");
20890
+ const { detectBrowserPlatform, resolveDefaultUserDataDir, Browser: Browser$1 } = await import("./main2.js");
20891
20891
  const platform = detectBrowserPlatform();
20892
20892
  if (!platform) throw new Error("Could not detect required browser platform");
20893
- const { convertPuppeteerChannelToBrowsersChannel } = await import("./LaunchOptions-DxQQ0s-A.js");
20893
+ const { convertPuppeteerChannelToBrowsersChannel } = await import("./LaunchOptions.js");
20894
20894
  const { join } = await import("node:path");
20895
20895
  const portPath = join(resolveDefaultUserDataDir(Browser$1.CHROME, platform, convertPuppeteerChannelToBrowsersChannel(options.channel)), "DevToolsActivePort");
20896
20896
  try {
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Schema } from "./index-DTDXUe6m.js";
1
+ import { t as Schema } from "./index2.js";
2
2
  import { Context, Service } from "@deepseek-ai/cordis";
3
3
  import { ToolDefinition, ToolExecutionInput, ToolRunContext, ToolRuntime } from "@deepseek-ai/dsh-tools";
4
4
  import { ContentBlock, HarnessError } from "@deepseek-ai/dsh-llm";
@@ -379,7 +379,7 @@ interface ReplRuntimeSurface {
379
379
  //#region src/runtime.d.ts
380
380
  /** Plugin config: every tunable, changeable from `cordis.yml` (no hardcoded tunables). */
381
381
  interface Config$1 {
382
- /** Python >=3.9 interpreter. The bare sentinel `python3` (or absent) selects a managed venv under {@link Config.kernelEnvDir}. */
382
+ /** Existing interpreter path/name; absent discovers active/project environments then PATH. */
383
383
  python?: string;
384
384
  /** Budget for kernel spawn → ready, in milliseconds. */
385
385
  startupTimeoutMs?: number;
@@ -402,12 +402,6 @@ interface Config$1 {
402
402
  snapshotDir?: string;
403
403
  /** Serialized graph cap in bytes; each over-cap attempt reports the last successful checkpoint. */
404
404
  snapshotSizeCapBytes?: number;
405
- /** Managed venv directory (used when `python` is absent/`python3`); defaults to `<package>/.venv-kernel`. */
406
- kernelEnvDir?: string;
407
- /** Preferred CPython version for a managed venv. */
408
- kernelPythonVersion?: string;
409
- /** Provision a stdlib-only managed venv on first use; default true. */
410
- kernelAutoInstall?: boolean;
411
405
  username?: string;
412
406
  }
413
407
  /**
@@ -428,8 +422,6 @@ declare class DashrRuntime extends ReplRuntime {
428
422
  private readonly kernels;
429
423
  private disposed;
430
424
  private readonly transactions;
431
- /** Lazily-resolved (and, when managed, provisioned) kernel interpreter. */
432
- private kernelEnvPromise;
433
425
  constructor(ctx: Context, config: Config$1);
434
426
  /**
435
427
  * The agentless-default kernel's pid, for lifecycle diagnostics; absent
@@ -480,8 +472,6 @@ declare class DashrRuntime extends ReplRuntime {
480
472
  private validateBindings;
481
473
  /** Dispatch one kernel-side host request; unknown types and bad payloads become error replies. */
482
474
  private dispatchHostRequest;
483
- /** Resolve (and provision, when managed) the kernel interpreter once per runtime. */
484
- private resolveKernelPython;
485
475
  /**
486
476
  * Spawn-or-reuse the kernel for one key. The lazy map guarantees one
487
477
  * entry is created here and nowhere else, so a key that never runs never