better-dsh 0.3.0-r → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +46 -176
  2. package/cordis.patch.yml +3 -2
  3. package/docs/specs/omp-python-repl-port/runtime-guide.md +33 -0
  4. package/docs/specs/omp-python-repl-port/spec.md +61 -0
  5. package/eval-description.md +7 -3
  6. package/lib/{LaunchOptions-CLEBmZeM.js → LaunchOptions-5eg7E0ye.js} +1 -1
  7. package/lib/LaunchOptions-DxQQ0s-A.js +4 -0
  8. package/lib/{bidi-Bs8owg1e.js → bidi-CwabFH07.js} +1 -1
  9. package/lib/client/index.js +2 -2
  10. package/lib/fs-aware/sandbox-plugin.js +1 -1
  11. package/lib/{index-browser-pHtsz007.js → index-browser-B-9jm12n.js} +4 -4
  12. package/lib/index.d.ts +17 -41
  13. package/lib/index.js +470 -1675
  14. package/lib/kernel-env-BOF0fNHn.js +91 -0
  15. package/lib/kernel-env.d.ts +4 -58
  16. package/lib/kernel-env.js +2 -2
  17. package/lib/{main-CBC7_uE-.js → main-BeCgUd20.js} +2 -2
  18. package/lib/{main-RqgDOjH9.js → main-Dv_UQqG2.js} +1 -1
  19. package/lib/omp-python/DILL-LICENSE +35 -0
  20. package/lib/omp-python/OMP-LICENSE +23 -0
  21. package/lib/omp-python/better_snapshot.py +391 -0
  22. package/lib/omp-python/better_support.py +425 -0
  23. package/lib/omp-python/dill-provenance.json +55 -0
  24. package/lib/omp-python/provenance.json +13 -0
  25. package/lib/omp-python/runner.py +2499 -0
  26. package/lib/omp-python/vendor/dill/__diff.py +234 -0
  27. package/lib/omp-python/vendor/dill/__info__.py +291 -0
  28. package/lib/omp-python/vendor/dill/__init__.py +119 -0
  29. package/lib/omp-python/vendor/dill/_dill.py +2265 -0
  30. package/lib/omp-python/vendor/dill/_objects.py +545 -0
  31. package/lib/omp-python/vendor/dill/_shims.py +193 -0
  32. package/lib/omp-python/vendor/dill/detect.py +287 -0
  33. package/lib/omp-python/vendor/dill/logger.py +285 -0
  34. package/lib/omp-python/vendor/dill/objtypes.py +24 -0
  35. package/lib/omp-python/vendor/dill/pointers.py +122 -0
  36. package/lib/omp-python/vendor/dill/session.py +612 -0
  37. package/lib/omp-python/vendor/dill/settings.py +25 -0
  38. package/lib/omp-python/vendor/dill/source.py +1025 -0
  39. package/lib/omp-python/vendor/dill/temp.py +252 -0
  40. package/lib/{puppeteer-core-DnI7gBFU.js → puppeteer-core-Dnd7eqel.js} +6 -6
  41. package/lib/remote/plugin.js +1 -1
  42. package/lib/url-schemes/index.d.ts +11 -11
  43. package/lib/url-schemes/index.js +3 -3
  44. package/lib/web-password.d.ts +8 -8
  45. package/package.json +3 -3
  46. package/scripts/kernel-provision.mjs +4 -34
  47. package/lib/LaunchOptions-CjYFNZFS.js +0 -4
  48. package/lib/kernel-bridge.py +0 -172
  49. package/lib/kernel-env-BLPZE-I3.js +0 -195
package/README.md CHANGED
@@ -1,197 +1,70 @@
1
- > Part of [Super D](../README.md). This is the independently installable `better-dsh` package. Super Dash embeds the same local build and registers it as a separate native Plugin Manager row, with its own enable/disable switch; both packages share version 0.3.0-a. Active source is this directory, with shared specifications and references under root `docs/`.
2
-
3
- # Better Dsh — Dashr
4
-
5
- DASHR is a persistent-kernel REPL for the DeepSeek Harness (dsh): a
6
- **stateful `ctx.replRuntime` provider** (one persistent IPython kernel
7
- subprocess **per session**, the run's `principal`, held in a map inside the
8
- one service instance per mount). Each `run()` is one cell on the calling
9
- session's kernel; variables, imports, and definitions assigned in run N
10
- survive into run N+1 — *state codification* (blueprint §1.1 channel ②),
11
- deliberately NOT the per-run isolation a one-shot execution backend
12
- provides — and two sessions sharing one service instance never see each
13
- other's variables.
14
-
15
- Naming (v0.1.5 layer model): the runtime class is **`DashrRuntime`** — the
16
- standing-mount-layer component (one instance per mount holding the
17
- cross-session kernel map), and therefore the de facto daemon while the
18
- profile-level `DashrDaemon` concept stays an empty shell; the session layer
19
- is the ipykernel subprocess itself, a pure interpreter with no harness
20
- awareness.
21
-
22
- The presentation half — the `eval` transport tool, the Python SDK renderer,
23
- and the tool→binding bridge — lives in the SAME package (merged in v0.1.8;
24
- the pre-merge `dsh-rlm-mode`/`dashr-presentation` sibling split is gone).
25
- The runtime registers the service key `replRuntime` through its own vendored
26
- Service Definition (see `src/vendored/repl-runtime.ts`), so it carries **zero
27
- dsh runtime package dependencies**: only `@deepseek-ai/cordis` (peer),
28
- `schemastery`, and `zeromq`.
29
-
30
- ## Package positioning
31
-
32
- - npm name: `better-dsh` (unscoped; formerly `@pgmi-builds/better-dsh`, which
33
- now stays published only as a deprecated pointer to this name).
34
- - A standard Cordis plugin (`Context` + schemastery `Config`, every tunable
35
- configurable from `cordis.yml`, no hardcoded tunables).
36
- - "Registrations are effects": the kernel lifecycle (lazy spawn on a key's
37
- first `run()`, teardown on that session's `agent/disposed`, optional
38
- snapshot at either teardown) is effect-owned, so plugin disposal tears
39
- every subprocess down.
40
- - Published surface: `lib/` only (`files: ["lib"]`, `main`/`types`/`exports`
41
- pointing at `lib/index.js` / `lib/index.d.ts`) — the build emits beside the
42
- manifest (`outDir: 'lib'`; the tsdown default `dist/` left the exports map
43
- dangling, fixed in M2B). The root also re-exports the vendored Service
44
- Definition's public contract (`ReplRuntime` plus the `CodeRun*` /
45
- `CodeBinding*` / `CodeJsonValue` types) so consumers depend on the
46
- published shape instead of reaching into sources. The declaration keeps
47
- dependency imports external (`dts: { resolve: false }`): bundled copies
48
- would create duplicate type identities in a consumer's program.
1
+ > Part of [Super D](../README.md). `better-dsh` is an independently installable native DSH bundle. Super Dash installs it through the native Plugin Manager; both packages use synchronized versions and independent enable/disable controls. Canonical shared specifications are in root `docs/`.
2
+
3
+ # Better Dash — Dashr
4
+
5
+ Better provides a stateful `ctx.replRuntime` and the model-facing `eval` tool. Each DSH session has its own persistent Python child: variables, imports and definitions survive across cells, while sessions remain isolated. `DashrRuntime` owns the children and their lifecycle; the presentation layer exposes the Python SDK and bridges calls to the current session's host tools.
6
+
7
+ The execution path is Node → JSON lines → an unchanged, pinned OMP Python runner → Python. Better adapts the host interfaces and packages the runner resources with compiled JavaScript. No OMP package, Bun, IPython, Jupyter or ZeroMQ is required by this path. Host modules remain optional peers and keep their runtime identity; Better's own JavaScript dependencies are bundled at build time.
49
8
 
50
9
  ## Native Web policies
51
10
 
52
- The bundle owns native DSH Web policies as independently toggleable components.
53
- `dashr-web-trust` accepts authenticated serving Host/Origin values while retaining
54
- native cookie authentication. The native WebServer and HMR remain in place;
55
- Better contributes WebTrust, mobile behavior and its password page through the
56
- public WebServer interfaces. Listener address and port follow native CLI policy.
57
- Automatic credential onboarding is disabled; users can still configure models.
58
-
59
- `dashr-welcome-notice` shadows only the native `welcome-notice` onboarding entry
60
- at a higher precedence and calls the shell's public completion callback every
61
- time it mounts, including new chats and explicitly reopened notices. It renders
62
- no dialog and writes no native acknowledgement state. Disabling the component
63
- withdraws its client contribution and restores the native welcome item; other
64
- onboarding steps retain their own behavior.
65
-
66
- ```yaml
67
- - id: dashr-welcome-notice
68
- disabled: true
69
- ```
11
+ The bundle owns independently toggleable WebTrust, mobile behavior, password-page and onboarding contributions through native DSH interfaces. Native WebServer/HMR and CLI listener policy remain in place. `dashr-welcome-notice` shadows the native welcome entry and calls its public completion callback; disabling that component restores the native welcome item.
70
12
 
71
- Super Dash installs Better through the native Plugin Manager as a separate
72
- bundle. Better Dash remains independently installable. `../dev-webmcp` is an
73
- unshipped development-only bundle owned by this repository.
13
+ Super Dash installs Better as a separate native bundle. `../dev-webmcp` is a development-only test bundle and is not shipped in either product tarball.
74
14
 
75
- ## Install
15
+ ## Install and interpreter
76
16
 
77
17
  ```sh
78
18
  npm install better-dsh
79
19
  ```
80
20
 
81
- The runtime OWNS its kernel environment. With `python` left at the
82
- `python3` sentinel (or absent), it provisions a managed venv under the
83
- package (`.venv-kernel`) on first use, installing `ipykernel` + `dill`
84
- (CPython 3.11) — no `/tmp` symlink, no blind trust in a host `python3`.
85
- An explicit `python` (or `DASHR_KERNEL_PYTHON`) is verified instead. For
86
- development and tests, pre-create the venv:
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.
87
22
 
88
- ```sh
89
- npm run kernel:venv # uv venv .venv-kernel --python 3.11 + ipykernel + dill
90
- ```
91
-
92
- Tests pick the kernel interpreter from `DASHR_TEST_PYTHON`, falling back to
93
- `./.venv-kernel/bin/python`, then `python3` — see `test/helpers.ts`.
23
+ 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.
94
24
 
95
25
  ## Configuration
96
26
 
97
- Every field of the plugin `Config` (schemastery defaults shown):
27
+ Runtime fields remain configurable through the ordinary Cordis row. The implementation's schema is authoritative; the principal controls are:
98
28
 
99
29
  | Field | Default | Meaning |
100
30
  | --- | --- | --- |
101
- | `python` | `python3` | Explicit interpreter with `ipykernel`; the bare `python3` sentinel (or absent) selects a managed venv under `kernelEnvDir`. |
102
- | `startupTimeoutMs` | `30000` | Budget for kernel spawn → ready, in milliseconds. |
103
- | `runTimeoutMs` | `120000` | Wall budget per run; expiry interrupts the kernel then force-settles. |
104
- | `interruptGraceMs` | `2000` | Grace between a timeout/abort interrupt and the force-settle. |
105
- | `interruptConfirmMs` | `250` | Confirm window between the control-channel interrupt and the SIGALRM escalation (must be `< interruptGraceMs`); see "Interrupts" below. |
106
- | `disposeTimeoutMs` | `5000` | Budget for graceful kernel teardown (shutdown_request → SIGKILL). |
107
- | `snapshotTimeoutMs` | `30000` | Budget for internal snapshot/restore cells (dill dump/load). |
108
- | `maxOutputBytes` | `67108864` | Hard cap for serialized log-array, completion-value, and failure-message payloads. |
109
- | `snapshotDir` | *(unset)* | Base directory for per-session namespace snapshots (`<dir>/<principal>/state.dill` + `manifest.json`); none when absent. |
110
- | `snapshotSizeCapBytes` | `268435456` | Serialized-size cap for a turn-end snapshot; over-cap snapshots are skipped (one-time model warning). |
111
- | `username` | `dashr` | Jupyter username stamped on wire messages. |
112
- | `kernelEnvDir` | *(unset)* | Managed venv directory (defaults to `<package>/.venv-kernel`). |
113
- | `kernelPythonVersion` | *(unset)* | Preferred CPython version for a managed venv (default `3.11`). |
114
- | `kernelAutoInstall` | `true` | Provision the managed venv (`ipykernel` + `dill`) on first use. |
115
-
116
- ## Persistent-state semantics
117
-
118
- - **Cell semantics**: each `run({ program })` is one cell on the calling
119
- session's kernel namespace (`user_ns`) — a pure IPython REPL. Top-level
120
- `await` works; a top-level `return` is a SyntaxError, exactly as in a
121
- native IPython cell. The completion value is the LAST expression's value
122
- (REPL displayhook-style): a statement-ending cell or a `None` final
123
- expression yields no `value` field, and a non-JSON value comes back as its
124
- repr text.
125
- - **Session keying** (M3-A): one kernel per distinct `request.principal`
126
- (the presentation bridge passes the calling agent's session id); runs
127
- without a principal share one default key, preserving M1 semantics. The
128
- service instance count is unchanged — one per mount — the keying is a
129
- `Map<principal, kernel>` inside the provider.
130
- - **Kernel lifetime**: lazy start on a key's first `run()`; teardown when
131
- that session's agent is disposed (the dsh `agent/disposed` event, payload
132
- `{ agent: { id } }`, listened through the untyped cordis event service to
133
- keep this package's zero-dsh-dependency rule) and on plugin disposal
134
- (`shutdown_request`, then SIGKILL after `disposeTimeoutMs`). A kernel that
135
- dies unexpectedly is never reused in-process: it respawns onto its nearest
136
- replayable snapshot (or a fresh empty kernel when none exists) and the run
137
- that observed the death gets an explicit `worker-exit` naming what was lost.
138
- - **Turn-end snapshots** (M3-B): with `snapshotDir` configured, every
139
- successful run is followed by a size-capped snapshot cell that dumps the
140
- user namespace to `<snapshotDir>/<principal>/state.dill` + `manifest.json`
141
- (`turn`, `pythonVersion`, `venvPath` = the kernel's own `sys.executable`,
142
- `skills`, `names`, `sizeBytes`). A namespace whose serialized size exceeds
143
- `snapshotSizeCapBytes` is skipped — estimated BEFORE any dill IO by a
144
- bounded walk that reads numpy/pandas in-memory footprints, then confirmed
145
- against the actual `.part` dump — and the model is warned once through the
146
- run's own logs. Skipped snapshots never replace the previous good one.
147
- - **Restore-on-first-boot** (M3-B): a key's first kernel boot restores its
148
- on-disk snapshot before running user code. The kernel validates the
149
- manifest itself (python version, interpreter identity, skills); a
150
- non-replayable snapshot degrades to an EMPTY namespace and the first run
151
- tells the model so. Variable state and the append-only transcript are NOT
152
- transactionally consistent (blueprint §8.3): the snapshot is a point-in-time
153
- namespace capture that can lag the transcript, and a degraded restore never
154
- fabricates variables the transcript once saw.
155
- - **Interrupts** (M3-A hardened): aborts/timeouts escalate in two phases —
156
- the zmq control `interrupt_request` first, then SIGALRM only after
157
- `interruptConfirmMs` if the cell has still not settled. The kernel-side
158
- bootstrap installs a busy guard that only raises `KeyboardInterrupt` while
159
- a dashr cell is actually executing, so a signal landing on an idle or
160
- booting kernel is swallowed instead of terminating the process (the M1
161
- same-tick dual send killed idle kernels deterministically — 10/10
162
- same-tick, 8/10 at +1-2ms, 40/40 during cold boot; see
163
- `test/interrupt-race.spec.ts`). The hard-abort contract is intact: a busy
164
- `while True: pass` still breaks inside the grace (blueprint §10.4).
165
- - **Concurrency**: the bridge serializes cells per kernel (`executeCell`
166
- awaits the previous cell), so concurrent `run()` calls on one session
167
- queue rather than interleave; see `test/parallel.spec.ts`. Runs on
168
- DIFFERENT principals execute on their own kernels concurrently.
169
- - **What snapshots do NOT carry**: nothing outside the kernel namespace is
170
- ever in scope — the v0.1.8b removal deleted the Continual Harness
171
- (`refine()`, `harnessDir`) entirely, so the snapshot/restore cycle's only
172
- persistence channel is the kernel namespace itself (blueprint §8.4).
173
- Anything a cell stores in ordinary variables follows the snapshot rules
174
- above as before.
31
+ | `python` | `python3` | Interpreter selection; no Jupyter installation requirement. |
32
+ | `startupTimeoutMs` | `30000` | Budget for child startup and initialization. |
33
+ | `runTimeoutMs` | `120000` | Wall budget per user cell. |
34
+ | `interruptGraceMs` | `2000` | Grace after an interrupt before terminating an unresponsive child. |
35
+ | `interruptConfirmMs` | `250` | Delay before confirming an interrupt that has not finished the cell. |
36
+ | `disposeTimeoutMs` | `5000` | Bound on graceful child teardown before forced cleanup. |
37
+ | `snapshotTimeoutMs` | `30000` | Budget for internal save/restore operations. |
38
+ | `maxOutputBytes` | `67108864` | Cap for serialized logs, completion and error output; overflow is reported. |
39
+ | `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
+ | `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
+ | `username` | `dashr` | Retained compatibility field. |
175
45
 
176
- ## Testing
46
+ ## Cells and recovery
177
47
 
178
- ```sh
179
- npm install
180
- npm run kernel:venv # once; or export DASHR_TEST_PYTHON=/path/to/python
181
- npm run typecheck # tsc --noEmit
182
- npm test # vitest --run (fileParallelism: false)
183
- ```
48
+ One `eval` call runs one Python cell. Top-level `await` works and top-level `return` is invalid. The final expression supplies the completion value; a final `None` or statement produces no value. Non-JSON completions return repr text. Printed stdout/stderr become logs. Tools use `await tool.<name>({...})` and failures raise `ToolCallError`.
49
+
50
+ Operations for one session serialize, including initialization, user cells, checkpointing, variable access and reset. Different sessions can execute concurrently; one cell can fan out independent asynchronous host tool calls.
51
+
52
+ Completed cells attempt a checkpoint, including cells that raise after changing state. The helper saves an admitted user object graph, preserving shared references among retained values. Serialization failures, resource exclusions and partial coverage are reported to the model. A complete generation is committed atomically; a failed attempt before commit retains the previous valid checkpoint. If interruption prevents confirmation of the commit, Better reports that uncertainty.
53
+
54
+ On a fresh child, Better validates and attempts restoration once before admitting user code. The notice identifies the saved cell and any omissions. A damaged or incompatible checkpoint starts empty and reports why. Runner plumbing, tool bindings and OS resources are excluded. Current tools are recreated from the host; files, sockets, subprocesses, threads, locks, connections, environment mutations and external side effects are not restored as a Python process.
184
55
 
185
- Teardown discipline: every test context is disposed through
186
- `onTestFinished`, and CI must assert no orphan kernels remain:
56
+ A dying or interrupted cell may already have performed side effects. Better never automatically retries it or blindly replays historical cells. Read the recovery notice and inspect real state before deciding which work to repeat. `reset: true` clears the session's memory and disk checkpoints.
57
+
58
+ For the full boundary and source provenance contract, see [the runtime guide](docs/specs/omp-python-repl-port/runtime-guide.md) and [spec](docs/specs/omp-python-repl-port/spec.md).
59
+
60
+ ## Testing
187
61
 
188
62
  ```sh
189
- pgrep -cf -- '-[m] ipykernel_launcher' || echo no-orphans
63
+ npm run typecheck
64
+ DASHR_TEST_PYTHON=python3 npm test
190
65
  ```
191
66
 
192
- (The `-[m]` trick prevents `pgrep` from matching itself; 208 orphaned
193
- kernels once exhausted machine memory while every unit test stayed green —
194
- blueprint §10.8/§10.9.)
67
+ Tests create real Python children and dispose their contexts. Stdlib-only interpreter coverage verifies that external kernel packages are unnecessary. Consumer acceptance additionally requires the root dual-tarball launcher and dev3 installed behavior; source tests and process liveness alone are insufficient.
195
68
 
196
69
  ---
197
70
 
@@ -199,7 +72,7 @@ blueprint §10.8/§10.9.)
199
72
 
200
73
  The DASHR agent-plane presentation (blueprint §7.4): the plugin half that
201
74
  presents the persistent-kernel runtime to the model as **cells on one
202
- persistent IPython kernel**. It contributes:
75
+ persistent Python runner**. It contributes:
203
76
 
204
77
  - **`eval`** — the cell transport tool: one call = one cell on the calling
205
78
  session's kernel (the `ctx.replRuntime` service). Variables, imports, and
@@ -304,15 +177,12 @@ unknown id, or an infrastructure rejection all map to an `error` string.
304
177
 
305
178
  ```sh
306
179
  npm install
307
- npm run kernel:venv # once; or export DASHR_TEST_PYTHON=/path/to/python
180
+ export DASHR_TEST_PYTHON=python3 # or an explicit supported interpreter
308
181
  npm run typecheck
309
182
  npm test # vitest --run
310
183
  ```
311
184
 
312
- The suite needs a Python interpreter with `ipykernel` (+ `dill` for the
313
- snapshot tiers) for the real-kernel specs; it resolves one from
314
- `DASHR_TEST_PYTHON`, then `./.venv-kernel/bin/python`, then `python3` —
315
- see `test/helpers.ts`.
185
+ The suite needs a supported Python interpreter; the runner and checkpoint serializer are supplied by Better. `DASHR_TEST_PYTHON` selects the test interpreter.
316
186
 
317
187
  ## Relationship to upstream
318
188
 
package/cordis.patch.yml CHANGED
@@ -14,8 +14,9 @@
14
14
  # a PTC session resolves to UNKNOWN_TOOL). DASHR ships no PTC mode, so that
15
15
  # rule never bites here.
16
16
  #
17
- # `python` is only a hint: the runtime OWNS the kernel environment. An
18
- # explicit interpreter (DASHR_KERNEL_PYTHON) is verified; the bare
17
+ # An explicit DASHR_KERNEL_PYTHON interpreter is verified. The default
18
+ # python3 compatibility path may prepare a managed Python environment;
19
+ # the packaged OMP runner needs no Jupyter or kernel package installation.
19
20
  - insert:
20
21
  - id: dashr-repl
21
22
  name: 'better-dsh'
@@ -0,0 +1,33 @@
1
+ # Better Dash Python REPL execution and recovery
2
+
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
+
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.
6
+
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
+
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
+
11
+ ## Checkpoint boundary
12
+
13
+ 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.
14
+
15
+ A completed cell attempts a new checkpoint, including a cell that raises after changing variables. Saving a checkpoint can fail or skip values. Better reports the coverage and latest committed cell instead of claiming complete process recovery. Aliases and dependencies among retained values must survive as one object graph; values whose dependencies cannot safely be preserved are omitted or the checkpoint is rejected.
16
+
17
+ The serializer does not preserve runner state, host tool bindings, open files, sockets, subprocesses, threads, locks or database connections. Tool bindings are recreated from the current host. Current directory, environment changes, module caches and library global state are not generally restored. File modifications and network/tool side effects have their own real sources and are never replayed or rolled back by a variable checkpoint.
18
+
19
+ The checkpoint manifest records format, completed-cell sequence, interpreter/environment identity, payload size/integrity, saved names and omissions. Only a complete committed generation is eligible for restoration. A failed attempt before commit preserves the previous valid generation. If interruption prevents commit confirmation, the result reports that uncertainty; the manifest remains the authority on the latest complete generation. Restore validates the owned session checkpoint before loading it and runs once before admitting a new user cell. Damage or incompatibility produces an explicit empty-state notice. A partial restore lists the missing coverage.
20
+
21
+ `reset: true` discards the session child and its persisted checkpoints. Do not use it when you intend to retain state.
22
+
23
+ ## After a crash or interruption
24
+
25
+ Read the recovery notice, check the latest successful checkpoint and inspect real external state. A cell that died after starting may already have performed side effects. Better does not automatically retry it and does not instruct blind replay. Recreate omitted resources and rerun only the operations you determine are still needed.
26
+
27
+ Snapshots are private executable serialization, not an interchange format. Do not copy arbitrary external dill files into the checkpoint directory or treat them as data imports. The owned directory, session isolation and permissions are part of the recovery contract.
28
+
29
+ ## Delivery verification
30
+
31
+ The original OMP runner, its MIT license and source/hash provenance travel inside the npm package. Vendored dill carries its version, original source hashes and BSD-3-Clause license. The build verifies these resources and copies them beside compiled JavaScript; consumers never execute TypeScript or load OMP/Bun packages.
32
+
33
+ Acceptance uses the normal dual tarball launcher and a dev3 test installation. Source tests, successful build or a live Python process alone are insufficient. The final test report records exact artifacts and the observed model-visible recovery behavior.
@@ -0,0 +1,61 @@
1
+ # Better Dash Python REPL:移植 OMP 执行链与快照边界
2
+
3
+ 状态:两阶段已实现并完成 `0.3.0-s` 整包消费者验收;整体验审发现的检查点目录失败边界已修复,37 项相关回归与两条类型检查通过;`0.3.0-t` 在 PC 与 dev3 的实装增量复验也已完成。尚未获得发布放行。2026-10-12 user 明确批准第一、第二阶段一起实施。原调研日期:2026-10-11。见 [执行计划](../../superpowers/plans/2026-10-12-omp-python-repl-port.md) 与 [验收报告](../../test-reports/2026-10-12-omp-python-repl-port.md)。
4
+
5
+ ## 1. 方向
6
+
7
+ “抄 OMP”指将所需源码复制到 Better Dash 自有源码中,完成本地 Node、DSH、Cordis 接口适配并随 `better-dsh` 交付;不 import OMP 包,也不要求消费者安装 Bun。现有 AST device 已有同类剥离先例:`better-dsh/src/devices/ast/ast-device.ts`。
8
+
9
+ 第一阶段以 OMP Python REPL 的运行语义为基线,**`runner.py` 逐字节复制,保持原文件不改**。复制的 Python runner 本身不依赖 IPython、ipykernel、Jupyter 或 ZeroMQ,只使用 Python 标准库。根据实际采用的便利函数,将 OMP `prelude.py` 一同纳入并注明来源;它和 OMP 专有的 Agent 桥接需要在 Better 的工具面上作取舍。宿主侧从 OMP 的 TypeScript kernel、共享 IPC 与会话管理中抽取所需逻辑,接上 Better 的 `ReplRuntime`、工具绑定、输出、取消、reset 和生命周期接口。`Bun.spawn` 换成 Node `child_process.spawn`;Bun 的脚本文本导入换成包内 `.py` 资源路径。Better 的 TS 在构建时编译为 JS,运行时不执行 TS。
10
+
11
+ 新路径是 `Better/Node → stdin/stdout JSON 行 → Python runner → Python 解释器`。因此可移除这条路径上的 IPython、ipykernel、Jupyter client 和 ZeroMQ 需求;**Python 解释器仍是运行前提**。是否随包交付解释器是另一项决策,不由移植 OMP 自动解决。第一阶段按 OMP 策略保留进程内会话状态,不承诺进程重启后的变量恢复。
12
+
13
+ 复制时固定一个 OMP commit,保存原始文件、许可证和来源记录;若 `runner.py` 原样复制,校验其字节哈希。本文调研时的 OMP HEAD 为 `fa5ff4a3a977a28b93d025f4cf50f046e019f18c`,实施时应以实际复制的 commit 为准。MIT 许可声明随交付物保留。
14
+
15
+ 资料:[OMP Python REPL 说明](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/docs/python-repl.md)、[runner.py](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/packages/coding-agent/src/eval/py/runner.py)、[TS kernel](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/packages/coding-agent/src/eval/py/kernel.ts)、[共享 IPC](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/packages/coding-agent/src/eval/kernel-base.ts)、[MIT 许可](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/LICENSE)。
16
+
17
+ ## 2. “快照恢复”恢复什么
18
+
19
+ 快照是**会话运行时 Python 用户命名空间的一份持久检查点**,用于 Python 子进程死亡、插件重启或同一会话恢复时,让新进程尽量接续旧 REPL 中由模型创建的值。例如 `x = 3`、列表、字典、对象实例、可序列化的函数或类,以及 Better 经 `setVar` 放入命名空间的值。它不是模型聊天记录或 compaction 摘要;那些属于 DSH 会话数据。也不是“把整个 Python 进程冻住”。
20
+
21
+ | 状态 | 是否属于快照目标 | 说明 |
22
+ | --- | --- | --- |
23
+ | 每会话的用户变量及其可序列化对象图 | 是,best effort | 核心恢复对象;按会话键隔离 |
24
+ | 检查点序号、格式、解释器及环境标识、跳过项 | 是 | 判断能否加载、丢失了多少轮、向模型准确报告 |
25
+ | Better 的宿主工具绑定、prelude、IPC、事件循环 | 否 | 新 runner 启动时重新建立;不得把旧进程句柄反序列化 |
26
+ | DSH 对话、工具日志、compaction、文件内容 | 否 | 各有独立持久化和真实来源;快照不复制它们 |
27
+ | 打开的文件/套接字、子进程、线程、数据库连接、锁 | 否 | 与旧 OS 进程绑定;恢复后需重新打开 |
28
+ | `cwd`、环境变量、`sys.path`、模块缓存和库内部全局状态 | 默认不保证 | 启动配置可重新应用;用户代码造成的任意进程副作用不能由变量快照完整重建 |
29
+ | 对外副作用,如文件修改、网络请求、工具调用 | 否 | 已发生的副作用不可通过恢复变量撤销,也不可盲目重放代码 |
30
+
31
+ 特别要区分:会话**活着时**,OMP runner 中的变量自然跨 cell 保留;需要快照的是**旧 runner 退出、新 runner 启动**这个边界。OMP 自身的 compaction 只提供有限的运行时提示,不恢复新进程中的历史变量值。[OMP eval 文档](https://github.com/can1357/oh-my-pi/blob/fa5ff4a3a977a28b93d025f4cf50f046e019f18c/docs/tools/eval.md)
32
+
33
+ ## 3. 若第二阶段加快照:技术边界
34
+
35
+ 快照功能放在 Better 自有扩展层,仍保持复制的 `runner.py` 字节不变:在没有用户 cell 并发执行的时点,由宿主发送内部 cell,在同一用户 namespace 中读取 `globals()`、过滤 runner/prelude/Better 内部绑定,并调用随包交付的快照辅助逻辑。恢复时先启动 runner、完成初始化与 prelude,再运行内部恢复 cell,最后允许用户 cell 进入。内部 cell 必须不污染可见结果和用户变量清单。若这一方式经实测无法满足隔离要求,再单独裁决是否改 runner;不能暗中把“原样复制”改成 fork。
36
+
37
+ 触发点:完成的用户 cell 后尝试保存最新检查点;正常关闭时可再尝试一次。`reset` 清除内存与磁盘检查点。异常终止时只使用**已经完整提交**的最近检查点,不重放死亡时的 cell,也不假装它执行成功。检查点写入临时文件、验证大小与格式后再原子替换;失败或超限保留上一份可用检查点。每个会话单独存放,首次启动或故障重启只尝试恢复一次。
38
+
39
+ ### 3.1 `dill` 随包源码交付的裁决
40
+
41
+ 以 Better 当前锁定的 `dill==0.4.1` 为基线,将其 Python 包源码复制到 Better 的交付资源中,让子进程直接从包内路径 `import dill`,不在消费者机器上执行 `pip install dill`。保留上游 BSD-3-Clause 许可、版本和来源记录。优先保持 vendored 源码原样;Better 特有的 namespace 过滤、检查点格式和错误报告放在自有快照辅助层,避免改动 dill 内部。`dill` 自身不是解释器,Python 解释器仍需由宿主或另行交付。
42
+
43
+ 包体积实测(PyPI 的 0.4.1 `py3-none-any` wheel):压缩 wheel **120,019 bytes**;其中 `dill/` 目录的 46 个 `.py` 文件展开为 **350,717 bytes**,排除自带测试后 14 个 `.py` 文件为 **245,541 bytes**。这些是 Python 源文件体积,不含打包后的 npm 压缩率或解释器体积。wheel 元数据没有默认 `Requires-Dist`;`objgraph`、`gprof2dot` 是可选诊断功能的 extra。PyPI 页面列出的 `setuptools` 是构建/安装要求,不是把已复制的 `dill/` 源码导入并运行时须安装的包。上游声明支持 Python/PyPy ≥3.9。资料:[PyPI dill 0.4.1](https://pypi.org/project/dill/0.4.1/)、[dill 源码](https://github.com/uqfoundation/dill)、[BSD 许可](https://github.com/uqfoundation/dill/blob/master/LICENSE)。
44
+
45
+ 衔接点是 OMP runner 的用户 namespace,与 IPython 或 Jupyter 协议无关。在 2026-10-11 的历史隔离探针中,未修改的 OMP `runner.py` 使用随进程路径提供的 dill 0.4.1:第一个 Python 子进程创建 `x = [1, 2]` 与引用 `x` 的 lambda,内部 cell 用 `dill.dump` 保存变量字典;第二个全新子进程用 `dill.load` 和 `globals().update` 恢复,随后 `f(4)` 得到 `5`。这项历史探针只证明基本衔接可行,不能证明任意对象都可恢复。当时尚未经过 Better 真实交付测试;2026-10-12 的 `0.3.0-s` 整包已在 PC4999 与 dev3 独立实装完成原生模型验收,具体结果与限制见上方验收报告。
46
+
47
+ Best effort 不应退回普通标准库 `pickle`,否则对 REPL 函数、闭包等的覆盖可能明显缩小。仍会有 dill 无法序列化或恢复的对象,以及依赖缺失或环境不一致的对象;这些值允许跳过并报告。要注意:把整个变量字典一次 `dill.dump` 时,一个坏值可使整份失败;若要“丢弃坏值并保住其他变量”,须由 Better 自有层实现筛选与部分恢复记录,并验证对象间引用关系。不能只捕获一次异常就声称做到了按变量 best effort。也不能重放任意历史 cell 来假装恢复,因为副作用可能重复发生。
48
+
49
+ `dill.load` 会执行反序列化逻辑,因此只加载本会话受控目录中的检查点,目录权限和会话键隔离属于恢复边界;不要把外来 `.dill` 文件当作数据导入。
50
+
51
+ ## 4. Best effort 的具体承诺
52
+
53
+ 可以沿用现有 best effort 标准,但必须**如实暴露恢复结果**,不能把“找到快照文件”等同于“状态完整恢复”——当前 `dill` 实现也会因不可序列化对象、超限、超时或环境变化而退化。
54
+
55
+ 1. 保存与恢复都允许失败,失败不阻止 REPL 从空命名空间继续运行;失败原因写入日志和模型可见结果。
56
+ 2. Manifest 记录检查点轮次、格式、Python/环境标识、大小和保存覆盖情况。恢复前校验;不兼容或损坏时明确报告空状态。
57
+ 3. 若只恢复部分变量,应报告“部分恢复”及跳过的名称或数量;**不能报告“已恢复整个 namespace”**。对象间共享引用和依赖关系无法保持时也应视作部分恢复,或整份拒绝。
58
+ 4. 模型须知道最新成功检查点的轮次,以及检查点之后哪些 cell 可能丢失;故障时观察到的那次调用不自动重试。
59
+ 5. 资源句柄、网络连接和其他进程副作用始终超出承诺;用户代码应能检测和重建这些资源。
60
+
61
+ 因此,第二阶段的目标是“尽可能恢复模型的 Python scratchpad 变量,并准确报告缺口”,不是恢复整个 Python 进程。是否加这一阶段,不影响先完成 OMP 执行链移植与去除 Jupyter/ZMQ 依赖。
@@ -1,13 +1,17 @@
1
1
  Run one Python cell on a session-persistent scripting pad. One `eval` call = one cell; the pad's state persists across calls and turns — variables, imports, and definitions from earlier cells stay alive.
2
2
 
3
- Work incrementally: imports → define → test → use, each its own cell. Re-run setup only after `reset` or a crash.
3
+ Work incrementally: imports → define → test → use, each its own cell. The pad uses an OMP-backed Python interpreter; Python must be available on the host. Better supplies its runner and checkpoint serializer without installing Jupyter or Python packages.
4
+
5
+ After a restart, read the recovery notice before relying on earlier variables. Checkpoints restore Python scratchpad values on a best-effort basis; notices identify the last saved cell and any omitted values. Open files, sockets, subprocesses, locks and other live resources must be recreated. A checkpoint does not restore files, undo tool calls or recover the entire Python process.
6
+
7
+ An interrupted or dying cell may already have changed variables, files or external systems. It is never automatically retried. Inspect the reported checkpoint and actual external state before deciding what to run again. A failed checkpoint leaves the previous valid checkpoint available; a failed restore starts with empty state and reports the reason.
4
8
 
5
9
  ## Arguments
6
10
 
7
11
  - `cell` (required): one Python program body. Top-level `await` works; top-level `return` is a SyntaxError — the cell runs in module scope (REPL semantics, not a function body).
8
12
  - `description` (required): a short summary of what the cell does — shown as the call's title in the UI.
9
13
  - `timeout` (optional): wall-clock budget in seconds; the cell is interrupted (then force-stopped) when exceeded. Omit for the runtime default.
10
- - `reset` (optional): restart the pad EMPTY first — earlier state is discarded.
14
+ - `reset` (optional): restart the pad EMPTY first and delete its checkpoints — earlier state is discarded.
11
15
 
12
16
  ## Direct calls vs cells
13
17
 
@@ -20,7 +24,7 @@ Delegation: `agent` is the unified agent-spawn entry; `subagent` is its native a
20
24
  Every session tool is callable from a cell as `await tool.<name>(args)` with ONE positional argument: `args` is the tool's parameter object written as a Python dict literal, same field names (`true`/`false`/`null` become `True`/`False`/`None`). A failed call raises `ToolCallError` with `.toolName` naming the tool. Tool names that are not plain identifiers (e.g. contain hyphens) have no `tool.<name>` member — call those as direct tool calls.
21
25
 
22
26
  ```python
23
- print(await tool.read({"path": "docs/README.md", "limit": 5}))
27
+ print(await tool.read({"file_path": "docs/README.md", "limit": 5}))
24
28
 
25
29
  r = await tool.bash({"command": "ls -la src/", "description": "List source directory"})
26
30
  print(r["stdout"]["text"])
@@ -1,4 +1,4 @@
1
- import { b as ChromeReleaseChannel } from "./main-CBC7_uE-.js";
1
+ import { b as ChromeReleaseChannel } from "./main-BeCgUd20.js";
2
2
 
3
3
  //#region node_modules/puppeteer-core/lib/puppeteer/node/LaunchOptions.js
4
4
  /**
@@ -0,0 +1,4 @@
1
+ import "./main-BeCgUd20.js";
2
+ import { t as convertPuppeteerChannelToBrowsersChannel } from "./LaunchOptions-5eg7E0ye.js";
3
+
4
+ export { convertPuppeteerChannelToBrowsersChannel };
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "./chunk-DQxbwPpZ.js";
2
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-pHtsz007.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
4
  import "./BrowserWebSocketTransport-wm99h94r.js";
5
5
 
6
6
  //#region node_modules/mitt/dist/mitt.mjs
@@ -64,7 +64,7 @@ const zh = {
64
64
  };
65
65
 
66
66
  //#endregion
67
- //#region \0dsh-css:/home/u1/workspaces/superd/.worktrees/remote-native-shortcut/better-dsh/src/failover/client/FailoverRow.module.css.mjs
67
+ //#region \0dsh-css:/home/u1/workspaces/superd/better-dsh/src/failover/client/FailoverRow.module.css.mjs
68
68
  const css$1 = "\n._440e1d7b_row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 0;\n}\n\n._a4f2ceec_rowText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._9865b509_title {\n font-weight: 600;\n font-size: 14px;\n}\n\n\n._3cfec826_slots {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 6px;\n}\n\n._11c2662d_select {\n width: 280px;\n max-width: 100%;\n min-width: 0;\n}\n\n._b7c358f9_alert {\n color: var(--dsw-alias-danger, #c0392b);\n font-size: 12px;\n}\n";
69
69
  const tagId$1 = "better-dsh/FailoverRow.module.css";
70
70
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
@@ -463,7 +463,7 @@ const zh$1 = {
463
463
  };
464
464
 
465
465
  //#endregion
466
- //#region \0dsh-css:/home/u1/workspaces/superd/.worktrees/remote-native-shortcut/better-dsh/src/compaction/client/CompactionRow.module.css.mjs
466
+ //#region \0dsh-css:/home/u1/workspaces/superd/better-dsh/src/compaction/client/CompactionRow.module.css.mjs
467
467
  const css = "\n\n\n._13e3747c_thresholdRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 0;\n border-bottom: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n._8ad3ff3f_thresholdText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding-right: 48px;\n}\n\n._9ffb627e_thresholdTitle {\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n._01bea389_thresholdDesc {\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._7f9578de_thresholdError {\n font-size: 12px;\n font-weight: 400;\n line-height: 18px;\n color: var(--dsw-alias-danger, #c0392b);\n}\n\n\n._cddd78cd_thresholdControl {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n\n._6058fea9_thresholdStepper {\n position: relative; \n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 72px;\n height: 36px;\n border-radius: 18px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._39d30469_thresholdValue {\n min-width: 18px;\n text-align: center;\n font-size: 14px;\n line-height: 22px;\n font-variant-numeric: tabular-nums;\n color: var(--dsw-alias-label-primary);\n}\n\n._68bd35c6_thresholdUnit {\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._904da366_thresholdArrows {\n position: absolute;\n right: 8px;\n display: flex;\n flex-direction: column;\n gap: 2px;\n opacity: 0;\n}\n\n._6058fea9_thresholdStepper:hover ._904da366_thresholdArrows,\n._6058fea9_thresholdStepper:focus-within ._904da366_thresholdArrows {\n opacity: 1;\n}\n\n\n._ae9106b1_thresholdArrow {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 17px;\n height: 12px;\n padding: 0;\n border: none;\n border-radius: 3px;\n background: color-mix(in srgb, var(--dsw-alias-bg-layer-1) 75%, transparent);\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._ae9106b1_thresholdArrow:hover:not(:disabled) {\n background: var(--dsw-alias-bg-layer-1);\n}\n\n._ae9106b1_thresholdArrow:disabled {\n color: var(--dsw-alias-label-caption);\n cursor: default;\n}\n";
468
468
  const tagId = "better-dsh/CompactionRow.module.css";
469
469
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
@@ -6,7 +6,7 @@ import { LocalFileSystem } from "@deepseek-ai/dsh-fs-local";
6
6
  import { FsError } from "@deepseek-ai/dsh-fs";
7
7
  import { writableRoots } from "@deepseek-ai/dsh-sandbox";
8
8
 
9
- //#region ../../../upstream/deepseek-harness/packages/fs/fs-sandbox/lib/index.js
9
+ //#region ../upstream/deepseek-harness/packages/fs/fs-sandbox/lib/index.js
10
10
  /**
11
11
  * Path-containment mechanics for the filesystem sandbox. Canonical spellings
12
12
  * take the fast lexical path; filesystem identity supplies the conservative
@@ -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-Bs8owg1e.js"
20795
+ "./bidi-CwabFH07.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-Bs8owg1e.js"
20818
+ "./bidi-CwabFH07.js"
20819
20819
  );
20820
20820
  const { slowMo = 0, protocolTimeout, idGenerator = createIncrementalIdGenerator() } = options;
20821
20821
  const pureBidiConnection = new BiDi.BidiConnection(url, connectionTransport, idGenerator, slowMo, protocolTimeout);
@@ -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-RqgDOjH9.js");
20890
+ const { detectBrowserPlatform, resolveDefaultUserDataDir, Browser: Browser$1 } = await import("./main-Dv_UQqG2.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-CjYFNZFS.js");
20893
+ const { convertPuppeteerChannelToBrowsersChannel } = await import("./LaunchOptions-DxQQ0s-A.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 {