better-dsh 0.3.0-r → 0.3.1-a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -175
- package/cordis.patch.yml +4 -3
- package/docs/20_specs/kernel-provisioning/spec.md +2 -0
- package/docs/specs/kernel-provisioning/spec.md +12 -40
- package/docs/specs/omp-python-repl-port/runtime-guide.md +35 -0
- package/docs/specs/omp-python-repl-port/spec.md +68 -0
- package/eval-description.md +7 -3
- package/lib/LaunchOptions-0Mr6meJc.js +4 -0
- package/lib/{LaunchOptions-CLEBmZeM.js → LaunchOptions-iE9M-STS.js} +1 -1
- package/lib/{bidi-Bs8owg1e.js → bidi-CaQeBZHY.js} +1 -1
- package/lib/client/index.js +2 -2
- package/lib/fs-aware/sandbox-plugin.js +1 -1
- package/lib/{index-browser-pHtsz007.js → index-browser-CJzxT9Vi.js} +4 -4
- package/lib/index.d.ts +16 -50
- package/lib/index.js +479 -1717
- package/lib/kernel-env-x_WkG6Fr.js +76 -0
- package/lib/kernel-env.d.ts +5 -69
- package/lib/kernel-env.js +2 -2
- package/lib/{main-RqgDOjH9.js → main-taSMz1KX.js} +1 -1
- package/lib/{main-CBC7_uE-.js → main-uqGqYYnA.js} +2 -2
- package/lib/mobile/plugin.d.ts +1 -1
- package/lib/omp-python/DILL-LICENSE +35 -0
- package/lib/omp-python/OMP-LICENSE +23 -0
- package/lib/omp-python/better_snapshot.py +391 -0
- package/lib/omp-python/better_support.py +425 -0
- package/lib/omp-python/dill-provenance.json +55 -0
- package/lib/omp-python/provenance.json +13 -0
- package/lib/omp-python/runner.py +2499 -0
- package/lib/omp-python/vendor/dill/__diff.py +234 -0
- package/lib/omp-python/vendor/dill/__info__.py +291 -0
- package/lib/omp-python/vendor/dill/__init__.py +119 -0
- package/lib/omp-python/vendor/dill/_dill.py +2265 -0
- package/lib/omp-python/vendor/dill/_objects.py +545 -0
- package/lib/omp-python/vendor/dill/_shims.py +193 -0
- package/lib/omp-python/vendor/dill/detect.py +287 -0
- package/lib/omp-python/vendor/dill/logger.py +285 -0
- package/lib/omp-python/vendor/dill/objtypes.py +24 -0
- package/lib/omp-python/vendor/dill/pointers.py +122 -0
- package/lib/omp-python/vendor/dill/session.py +612 -0
- package/lib/omp-python/vendor/dill/settings.py +25 -0
- package/lib/omp-python/vendor/dill/source.py +1025 -0
- package/lib/omp-python/vendor/dill/temp.py +252 -0
- package/lib/{puppeteer-core-DnI7gBFU.js → puppeteer-core-C-4NyOpY.js} +5 -5
- package/lib/url-schemes/index.js +3 -3
- package/lib/web-password.d.ts +1 -1
- package/lib/{web-trust-CwZrTBcl.d.ts → web-trust-C_Y6o8Z2.d.ts} +11 -11
- package/lib/web-trust.d.ts +1 -1
- package/lib/welcome-notice.d.ts +3 -3
- package/package.json +5 -5
- package/scripts/kernel-check.mjs +5 -0
- package/lib/LaunchOptions-CjYFNZFS.js +0 -4
- package/lib/kernel-bridge.py +0 -172
- package/lib/kernel-env-BLPZE-I3.js +0 -195
- package/scripts/kernel-provision.mjs +0 -35
package/README.md
CHANGED
|
@@ -1,197 +1,71 @@
|
|
|
1
|
-
> Part of [Super D](../README.md).
|
|
2
|
-
|
|
3
|
-
# Better
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
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.
|
|
87
22
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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.
|
|
91
26
|
|
|
92
|
-
|
|
93
|
-
`./.venv-kernel/bin/python`, then `python3` — see `test/helpers.ts`.
|
|
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.
|
|
94
28
|
|
|
95
29
|
## Configuration
|
|
96
30
|
|
|
97
|
-
|
|
31
|
+
Runtime fields remain configurable through the ordinary Cordis row. The implementation's schema is authoritative; the principal controls are:
|
|
98
32
|
|
|
99
33
|
| Field | Default | Meaning |
|
|
100
34
|
| --- | --- | --- |
|
|
101
|
-
| `python` |
|
|
102
|
-
| `startupTimeoutMs` | `30000` | Budget for
|
|
103
|
-
| `runTimeoutMs` | `120000` | Wall budget per
|
|
104
|
-
| `interruptGraceMs` | `2000` | Grace
|
|
105
|
-
| `interruptConfirmMs` | `250` |
|
|
106
|
-
| `disposeTimeoutMs` | `5000` |
|
|
107
|
-
| `snapshotTimeoutMs` | `30000` | Budget for internal
|
|
108
|
-
| `maxOutputBytes` | `67108864` |
|
|
109
|
-
| `snapshotDir` |
|
|
110
|
-
| `snapshotSizeCapBytes` | `268435456` |
|
|
111
|
-
| `username` | `dashr` |
|
|
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.
|
|
35
|
+
| `python` | automatic discovery | Existing executable name/path; explicit selection bypasses discovery. |
|
|
36
|
+
| `startupTimeoutMs` | `30000` | Budget for child startup and initialization. |
|
|
37
|
+
| `runTimeoutMs` | `120000` | Wall budget per user cell. |
|
|
38
|
+
| `interruptGraceMs` | `2000` | Grace after an interrupt before terminating an unresponsive child. |
|
|
39
|
+
| `interruptConfirmMs` | `250` | Delay before confirming an interrupt that has not finished the cell. |
|
|
40
|
+
| `disposeTimeoutMs` | `5000` | Bound on graceful child teardown before forced cleanup. |
|
|
41
|
+
| `snapshotTimeoutMs` | `30000` | Budget for internal save/restore operations. |
|
|
42
|
+
| `maxOutputBytes` | `67108864` | Cap for serialized logs, completion and error output; overflow is reported. |
|
|
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. |
|
|
44
|
+
| `snapshotSizeCapBytes` | `268435456` | Checkpoint payload cap; failed or over-cap saves preserve the previous committed generation. |
|
|
45
|
+
| `username` | `dashr` | Retained compatibility field. |
|
|
175
46
|
|
|
176
|
-
##
|
|
47
|
+
## Cells and recovery
|
|
177
48
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
49
|
+
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`.
|
|
50
|
+
|
|
51
|
+
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.
|
|
52
|
+
|
|
53
|
+
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.
|
|
54
|
+
|
|
55
|
+
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
56
|
|
|
185
|
-
|
|
186
|
-
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
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).
|
|
60
|
+
|
|
61
|
+
## Testing
|
|
187
62
|
|
|
188
63
|
```sh
|
|
189
|
-
|
|
64
|
+
npm run typecheck
|
|
65
|
+
DASHR_TEST_PYTHON=python3 npm test
|
|
190
66
|
```
|
|
191
67
|
|
|
192
|
-
|
|
193
|
-
kernels once exhausted machine memory while every unit test stayed green —
|
|
194
|
-
blueprint §10.8/§10.9.)
|
|
68
|
+
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
69
|
|
|
196
70
|
---
|
|
197
71
|
|
|
@@ -199,7 +73,7 @@ blueprint §10.8/§10.9.)
|
|
|
199
73
|
|
|
200
74
|
The DASHR agent-plane presentation (blueprint §7.4): the plugin half that
|
|
201
75
|
presents the persistent-kernel runtime to the model as **cells on one
|
|
202
|
-
persistent
|
|
76
|
+
persistent Python runner**. It contributes:
|
|
203
77
|
|
|
204
78
|
- **`eval`** — the cell transport tool: one call = one cell on the calling
|
|
205
79
|
session's kernel (the `ctx.replRuntime` service). Variables, imports, and
|
|
@@ -304,15 +178,12 @@ unknown id, or an infrastructure rejection all map to an `error` string.
|
|
|
304
178
|
|
|
305
179
|
```sh
|
|
306
180
|
npm install
|
|
307
|
-
|
|
181
|
+
export DASHR_TEST_PYTHON=python3 # or an explicit supported interpreter
|
|
308
182
|
npm run typecheck
|
|
309
183
|
npm test # vitest --run
|
|
310
184
|
```
|
|
311
185
|
|
|
312
|
-
The suite needs a Python interpreter
|
|
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`.
|
|
186
|
+
The suite needs a supported Python interpreter; the runner and checkpoint serializer are supplied by Better. `DASHR_TEST_PYTHON` selects the test interpreter.
|
|
316
187
|
|
|
317
188
|
## Relationship to upstream
|
|
318
189
|
|
package/cordis.patch.yml
CHANGED
|
@@ -14,13 +14,14 @@
|
|
|
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
|
-
#
|
|
18
|
-
#
|
|
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'
|
|
22
23
|
config:
|
|
23
|
-
python: !!js process.env.DASHR_KERNEL_PYTHON
|
|
24
|
+
python: !!js process.env.DASHR_KERNEL_PYTHON
|
|
24
25
|
|
|
25
26
|
# URL-aware I/O + hashline + devices. Gates ride THIS row's config
|
|
26
27
|
# (schema defaults: both ON — opt-out), no longer the core row's.
|
|
@@ -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
|
|
|
@@ -1,53 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Existing Python interpreter discovery
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
###
|
|
7
|
+
### Existing interpreters only
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
11
|
+
### Explicit selection
|
|
13
12
|
|
|
14
|
-
|
|
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
|
-
|
|
15
|
+
### Automatic discovery
|
|
18
16
|
|
|
19
|
-
|
|
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
|
-
###
|
|
19
|
+
### Readiness and failure
|
|
23
20
|
|
|
24
|
-
|
|
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
|
-
|
|
23
|
+
### Runtime state and migration
|
|
27
24
|
|
|
28
|
-
|
|
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.
|
|
@@ -0,0 +1,35 @@
|
|
|
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. 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
|
+
|
|
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
|
+
`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
|
+
|
|
13
|
+
## Checkpoint boundary
|
|
14
|
+
|
|
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.
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
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.
|
|
22
|
+
|
|
23
|
+
`reset: true` discards the session child and its persisted checkpoints. Do not use it when you intend to retain state.
|
|
24
|
+
|
|
25
|
+
## After a crash or interruption
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+
## Delivery verification
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
35
|
+
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,68 @@
|
|
|
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 依赖。
|
|
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.
|
package/eval-description.md
CHANGED
|
@@ -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.
|
|
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({"
|
|
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,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-
|
|
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-CJzxT9Vi.js";
|
|
4
4
|
import "./BrowserWebSocketTransport-wm99h94r.js";
|
|
5
5
|
|
|
6
6
|
//#region node_modules/mitt/dist/mitt.mjs
|
package/lib/client/index.js
CHANGED
|
@@ -64,7 +64,7 @@ const zh = {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
//#endregion
|
|
67
|
-
//#region \0dsh-css:/home/u1/workspaces/superd
|
|
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
|
|
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
|
|
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
|