better-dsh 0.3.0 → 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.
Files changed (76) hide show
  1. package/README.md +49 -177
  2. package/cordis.patch.yml +5 -36
  3. package/docs/20_specs/kernel-provisioning/spec.md +2 -0
  4. package/docs/adr/0015-independent-bundles-agent-remote.md +40 -0
  5. package/docs/remote-tools.md +56 -0
  6. package/docs/specs/kernel-provisioning/spec.md +12 -40
  7. package/docs/specs/omp-python-repl-port/runtime-guide.md +35 -0
  8. package/docs/specs/omp-python-repl-port/spec.md +68 -0
  9. package/docs/specs/remote-tool-enhancement/spec.md +203 -0
  10. package/dsh-docs/baseline.md +13 -0
  11. package/dsh-docs/subsystems/invariants.md +3 -0
  12. package/dsh-docs/subsystems/invariants.zh.md +3 -0
  13. package/eval-description.md +7 -3
  14. package/lib/LaunchOptions-0Mr6meJc.js +4 -0
  15. package/lib/{LaunchOptions-kGXcZOe0.js → LaunchOptions-iE9M-STS.js} +1 -1
  16. package/lib/{bidi-BalpKaJc.js → bidi-CaQeBZHY.js} +1 -1
  17. package/lib/client/index.js +30 -6
  18. package/lib/failover/index.js +1 -1
  19. package/lib/fs-aware/sandbox-plugin.js +3 -3
  20. package/lib/{index-browser-CysrjMyM.js → index-browser-CJzxT9Vi.js} +4 -4
  21. package/lib/index.d.ts +16 -50
  22. package/lib/index.js +482 -1720
  23. package/lib/kernel-env-x_WkG6Fr.js +76 -0
  24. package/lib/kernel-env.d.ts +5 -69
  25. package/lib/kernel-env.js +2 -2
  26. package/lib/{lsp-server-registry-BXvUTBY7.js → lsp-server-registry-BcdkHROD.js} +1 -1
  27. package/lib/{lsp-server-registry-CH9WNFFt.js → lsp-server-registry-D4VLs1Sm.js} +1 -1
  28. package/lib/{main-BQeFrBM1.js → main-taSMz1KX.js} +1 -1
  29. package/lib/{main-CupOhMp5.js → main-uqGqYYnA.js} +7 -5
  30. package/lib/mobile/plugin.d.ts +1 -1
  31. package/lib/omp-python/DILL-LICENSE +35 -0
  32. package/lib/omp-python/OMP-LICENSE +23 -0
  33. package/lib/omp-python/better_snapshot.py +391 -0
  34. package/lib/omp-python/better_support.py +425 -0
  35. package/lib/omp-python/dill-provenance.json +55 -0
  36. package/lib/omp-python/provenance.json +13 -0
  37. package/lib/omp-python/runner.py +2499 -0
  38. package/lib/omp-python/vendor/dill/__diff.py +234 -0
  39. package/lib/omp-python/vendor/dill/__info__.py +291 -0
  40. package/lib/omp-python/vendor/dill/__init__.py +119 -0
  41. package/lib/omp-python/vendor/dill/_dill.py +2265 -0
  42. package/lib/omp-python/vendor/dill/_objects.py +545 -0
  43. package/lib/omp-python/vendor/dill/_shims.py +193 -0
  44. package/lib/omp-python/vendor/dill/detect.py +287 -0
  45. package/lib/omp-python/vendor/dill/logger.py +285 -0
  46. package/lib/omp-python/vendor/dill/objtypes.py +24 -0
  47. package/lib/omp-python/vendor/dill/pointers.py +122 -0
  48. package/lib/omp-python/vendor/dill/session.py +612 -0
  49. package/lib/omp-python/vendor/dill/settings.py +25 -0
  50. package/lib/omp-python/vendor/dill/source.py +1025 -0
  51. package/lib/omp-python/vendor/dill/temp.py +252 -0
  52. package/lib/{puppeteer-core-CSk2PtSm.js → puppeteer-core-C-4NyOpY.js} +8 -8
  53. package/lib/remote/plugin.d.ts +459 -5
  54. package/lib/remote/plugin.js +2389 -210
  55. package/lib/url-schemes/index.d.ts +11 -11
  56. package/lib/url-schemes/index.js +11 -11
  57. package/lib/web-password.d.ts +8 -8
  58. package/lib/web-password.js +5 -1
  59. package/lib/{web-trust-Dej_RI2B.d.ts → web-trust-C_Y6o8Z2.d.ts} +11 -11
  60. package/lib/web-trust.d.ts +1 -2
  61. package/lib/welcome-notice.d.ts +5 -7
  62. package/lib/welcome-notice.js +9 -48
  63. package/lib/{wrap-T4I0nfPM.js → wrap-0VA0uFx8.js} +1 -1
  64. package/package.json +7 -24
  65. package/scripts/kernel-check.mjs +5 -0
  66. package/lib/LaunchOptions-B874eBu2.js +0 -4
  67. package/lib/bundle-delivery.d.ts +0 -20
  68. package/lib/bundle-delivery.js +0 -209
  69. package/lib/hmr.d.ts +0 -32
  70. package/lib/hmr.js +0 -49
  71. package/lib/kernel-bridge.py +0 -172
  72. package/lib/kernel-env-D8UOWeji.js +0 -195
  73. package/lib/webserver.d.ts +0 -10
  74. package/lib/webserver.js +0 -19
  75. package/scripts/kernel-provision.mjs +0 -35
  76. package/scripts/register-bundle.mjs +0 -80
@@ -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.
@@ -0,0 +1,203 @@
1
+ # Remote 工具 enhancement — 交付、生命周期与传输规格
2
+
3
+ - 日期:2026-10-10(Asia/Hong_Kong)
4
+ - 状态:2026-10-10 user 已审阅并明确授权执行;Task0–7 已独立审阅通过;Task8 完整双包消费者验收进行中,L 实装暴露的 worker/SSH cleanup 缺陷已进入修复验证。用户亲验与发布尚未放行。
5
+ - 输入:[2026-10-09 方向稿](../../10_plans/2026-10-09-remote-tool-optimization-directions.md)及 2026-10-10 会话裁决。
6
+ - 执行计划:[2026-10-10-remote-tool-enhancement.md](../../superpowers/plans/2026-10-10-remote-tool-enhancement.md)。本规格优先于方向稿的冲突条款。
7
+
8
+ ## 1. 目标与本轮范围
9
+
10
+ 为 Agent 提供省步骤、减少转义和连接错误的 remote 便利层。一个 `remote` 工具覆盖 SSH、Docker、Incus、BYO-PTY,以及 Super 在场时的 Hub machine。Super 和 Better 分别可独立交付,不因另一包关闭或卸载丢失自身代码。
11
+
12
+ 本轮实现 P0/P1:
13
+
14
+ 1. Better 单一源码、Super 构建副本;两条插件行默认开启,Agent 实例级一次注册竞争。
15
+ 2. 可选 Hub 接入、动态 machine 清单;无远端机器时 Hub 清单为空,不执行本机。
16
+ 3. oneshot `script` 文件载体与 Hub oneshot stdin parity。
17
+ 4. 取消 Hub 模型工具缺省/显式本机执行入口,保留执行服务端与本地文件端点。
18
+ 5. BYO-SSH 注册、复用及连接参数;BYO-PTY 保持既有语义。
19
+ 6. oneshot 后台作业复用 DSH 原生 jobs 的 identity、pull-delta、完成通知及取消。
20
+ 7. `copy` 跨受支持端点的二进制传输、校验与暂存清理。
21
+ 8. `rsync` 仅 SSH;两个远端经发起机的 SSH 隧道中继,身份不下放。
22
+ 9. 真实双包交付和模型 Surface 验收。
23
+
24
+ 后续范围,不在本轮实现:P2 PTY 已完成命令的 backlog/offset 续读;P3 运行中 probe。取消:3.5M 提醒、一次性 `ssh://` target、`cmd[]` fan-out、push stdout、非 SSH/混合 transport rsync、BYO-WebSocket/Docker/Incus、命令黑名单、额外文件管理工具、inline cmd 扩容。
25
+
26
+ ## 2. 包归属与交付
27
+
28
+ - 唯一可编辑源码:`better-dsh/src/remote/`。
29
+ - Super 构建时从该源码生成独立 remote 交付闭包,导出 `super-dsh/remote`。不复制 Better 整包、客户端、REPL、文档或其他工具;不建立 Super→Better 的 npm dependency/optional/peer 边。
30
+ - 生成目录及 provenance manifest 不手改;构建必须检查源码哈希与输出闭包,防止旧副本或跨包裸 import。
31
+ - Better 行保留 `id: dashr-remote`、`name: better-dsh/remote`;Super 新行 `id: superd-remote`、`name: super-dsh/remote`。两个 YAML id 不相同,两行默认开,模型工具名相同:`remote`。
32
+ - 不在 Super YAML 中禁用 Better remote,不写安装时脚本化的 profile enable/disable;已存在的用户开关选择保留。
33
+ - Super 的 companion 原生安装流程不改变;remote 是“Super 不携带 Better 文件”契约的窄范围自有源码复用例外,而不是恢复整包捆绑。
34
+ - 宿主模块继续 optional peer、真实类型和单实例契约;私有 schema 真实构建输入在生成闭包内解决,消费者不解析 Better 的 `#schemastery`。
35
+ - 根、Better、Super 同步版本;实现轮经 `npm run version:release -- <同步字母版本>` 和双 tarball 通道,不发布 registry。
36
+
37
+ ## 3. Agent 实例级注册
38
+
39
+ ### 3.1 上游事实
40
+
41
+ 当前上游 `tools.register()` 同步插入 scoped registry,同 scope 同名立即抛错,不是先收集后统一去重。`agent/created` 是 setup 完成后、首轮工作放行前的 serial lifecycle 事件。preset revision/generation 被多个 Agent 复用,不能假设每个 session 重跑 preset 插件。
42
+
43
+ 证据位置:
44
+
45
+ - `upstream/deepseek-harness/packages/core/tools/src/index.ts:1063`。
46
+ - `upstream/deepseek-harness/packages/core/scope/src/store.ts:43`。
47
+ - `upstream/deepseek-harness/packages/core/agent/src/runtime-types.ts:250`。
48
+ - `upstream/deepseek-harness/packages/preset/agent-preset-registry/src/index.ts:104`。
49
+ - `upstream/deepseek-harness/packages/api/session-controller/src/agent.ts:185`。
50
+
51
+ ### 3.2 行为
52
+
53
+ - host 插件行只安装生命周期监听器,不在全局工具作用域注册 `remote`。
54
+ - 每次 `agent/created`,两份行各自在该 Agent 的真实 scope 尝试注册工具。先成功者供该 Agent 使用,后者只跳过该实例。
55
+ - 不自行判断 new/cold resume,不存 session 启动类型、不记录“已退出/待命”状态;官方创建事件本身给出尝试时机。必要改动仅为把现有 host 全局注册移到此事件的 scoped 注册,并局部处理同名冲突。
56
+ - 不保证 Super 优先,不按 UI/YAML 展示顺序推断赢家。两份相同源码均可接入 Hub,赢家不影响能力集合。
57
+ - 只处理实际的 `remote` 重名冲突,其他 schema、生命周期、服务异常继续抛出;不吞掉所有错误。
58
+ - 重名跳过不写 `disabled`,不让 host 行变成失败状态,也不使 Agent 创建失败。
59
+ - 注册、driver、PTY、BYO-SSH label 及 jobs 同时归该 Agent scope 和来源插件行所有:任一 owner 退出都撤注册并清理资源;不能仅依附 Agent 而留下已卸载包的执行闭包。
60
+ - 不轮询、不设接管队列、不在空位出现时重试。已存活 Agent 中途卸载赢家,当前实例不自动补位。
61
+ - new session 和 cold resume 创建新 Agent 时重新尝试;hot continuation、重开页面、给存活会话发消息不重试。
62
+ - 首轮插件热安装不追溯给既有 Agent 补注册,Surface 提示新建会话或 cold resume;普通 disable→enable 若该插件本身支持恢复,须记录实际窗口,不承诺热接管。
63
+ - foreign runtime 的既有 Hub 注入面与 native `remote` 不混淆:本轮不宣称所有 foreign agent 直接拥有 DSH scoped remote 工具。
64
+
65
+ ## 4. 可选 Hub 能力
66
+
67
+ Better 不 import Super 包。共享 remote 定义 owned structural contract;Super 在官方 Cordis scope 提供 `superdRemoteTransport`,remote 用 `ctx.get()` 在能力消费时可选取得。absence 不形成硬 inject 等待,不修改宿主。
68
+
69
+ - provider 在场 → 工具描述与 roster 说明 `hub:<machineId>`;机器目录可为空。机器变化只改变 roster,不增删 target 字段。
70
+ - provider 不在场 → SSH/Docker/Incus/BYO 继续可用,Hub target 返回有恢复提示的 unavailable 错误,不静默改走 SSH。
71
+ - provider 的出现/消失通过官方服务通知更新模型 Surface;不检查 npm 包目录,不轮询各机器可达性。
72
+ - roster 来自 pairing 表中的远端 m4,排除 self;执行时检查 unknown machine、`execEnabled` 与连接错误,保留原始原因。
73
+ - 远端执行服务端和文件 transport 随 Hub 本体提供,不依附模型侧 remote 行。关闭某台机器的工具行不应拆掉别人访问它的服务端。
74
+ - PTY 沿用现有 pairing cookie、auth/Host/Origin fence 与 WS executor。oneshot 沿用既有 authenticated RPC,不声称现有 HTTP exec 已改为 WS。
75
+ - 文件独立使用 binary WS lane;文本 PTY 不作为二进制文件通道。
76
+ - 对端旧版本无文件 lane 时明确 unsupported;不以 base64 手搓冒充新增能力。
77
+
78
+ ## 5. 执行入口
79
+
80
+ ### 5.1 script 与 stdin
81
+
82
+ `{ target, script: "/abs/local/script.sh", mode: "oneshot" }`:从工具所在运行时读文件,原文作为 `bash -s` 的 stdin。文件不要求能在目标机直接访问,不把源码拼入 argv。
83
+
84
+ - `script`、`cmd`、`stdin` 的选择:script 与 cmd 互斥,script 与 stdin 互斥;stdin+cmd 沿用现有模式。
85
+ - script 首轮仅 oneshot,BYO-PTY 或 mode pty 请求在读文件/拨号前拒绝。
86
+ - 本地路径必须绝对、无 NUL;文件读取失败在连接/执行前报告。
87
+ - 不做 script 自带业务 stdin 的第二通道;需要数据时用文件/参数,或用 cmd+stdin。
88
+ - 保留 Hub inline cmd 4096 字符上限;script 固定命令短于上限,正文走 stdin。
89
+ - Hub API、wire sender、legacy Hub tool exec Surface 同步支持 string stdin;PTY 不把 legacy stdin 支持悄悄扩成 script 模式。
90
+
91
+ ### 5.2 local
92
+
93
+ - Hub 模型工具 exec 不再缺省本机;未传 machine 返回“请选择远端”提示,显式 self 默认也拒绝。
94
+ - `localExec` 配置默认 false,可作为显式兼容入口,但不替正常消费者加入测试专用业务守卫。
95
+ - 接收对端请求的本地 oneshot/PTY executor 保留;copy 的本地文件端点及读取 script 保留。
96
+
97
+ ### 5.3 输出
98
+
99
+ - native/PTy 现有 4,000,000 UTF-16 code units 与 Hub oneshot 4MiB byte ring 是不同单位,回执必须真实说明;本轮不配置化,不凭文案把两者说成同一精确 4MB。
100
+ - 保留约 30k 返回尾窗;统计总输出、保留量、已丢头部,超限明确标注。修复 native oneshot 现有收头却被上层当作尾窗的偏差。
101
+ - 单次 dispatch 重新计数;达到上限不要求重启 PTY,重启不能找回已丢数据。
102
+ - 不新增 3.5M 预警、输出预测或 push 监控。
103
+
104
+ ## 6. BYO-SSH
105
+
106
+ 调用形态:`{target:"byossh:label", ssh:{host,user?,port?,key?,proxyJump?}, cmd}`;首次注册连接,随后只用 target 复用。使用 `ssh` 字段而非复用现有 string `spawn`,避免破坏 BYO-PTY schema。
107
+
108
+ - label 归 Agent 实例;不改 `~/.ssh/config`,不持久化凭据,不接受 inline 私钥内容;key 是发起机已有文件路径。
109
+ - 首次可登记后执行或仅登记;复用相同参数幂等,不同参数给出冲突与换 label 提示。
110
+ - host、user、port、key、proxyJump 通过参数数组、受控 config 文件或官方 SSH options 编排,不能拼出第二层任意 shell。
111
+ - 所有执行、copy、rsync 和中继复用同一已解析 SSH connection spec,不能 rsync 又退回只读系统 alias。
112
+ - roster 区分 SSH config 与 byossh labels,不导出密钥、cookie 或完整凭据。
113
+
114
+ ## 7. oneshot jobs
115
+
116
+ `{target, cmd|script, run_in_background:true}` → 原生 jobId;读取/取消使用 DSH `job_output` / `job_kill`,完成由宿主通知。PTY mode 明确拒绝此选项。
117
+
118
+ - 使用官方 `ctx.jobs.start`、owner、JobHooks、output source/pump;不自建第二份 job registry、poller 或通知渠道。
119
+ - 子进程/远端任务在 job admission 后启动;启动被拒不能留下已经运行的进程。
120
+ - job 输出必须运行中真实增量可读,不能等 command 完成后才一次 append 全量。
121
+ - done 等到进程、远端任务及资源释放后结算;cancel 终止真实本地组/远端 job,不只把本地 Promise 标为取消。
122
+ - provider 需要对 Hub oneshot 提供 job-owned start/read/cancel/done 协议;仅 abort HTTP fetch 不视为远端取消。
123
+ - jobs service 不在场时同步 exec 仍可用,background 请求 fail-loud 并说明缺失的官方能力。
124
+ - 前台 timeout 保持现有上限;background 无前台等待截止但受 owner cancellation 和 job kill 管理。
125
+
126
+ ## 8. 文件操作
127
+
128
+ ### 8.1 寻址及能力边界
129
+
130
+ `{action:"copy", src, dst, verify:true, mkdir:true}` 与 `{action:"rsync", src, dst, delete:false, excludes:[], dryRun:true, preserve:true}`。
131
+
132
+ 端点使用 `[target:]绝对路径`;例如 `/tmp/a`、`ssh:dev3:/data/a`、`byossh:review:/data/a`、`docker:build:/data/a`、`hub:skc8:/data/a`。按最后一个 `:/` 边界解析 target/path,避免把 transport 前缀冒充 host。裸 label 的兼容解析复用 remote roster 规则,不另建命名系统。
133
+
134
+ - copy 首轮支持文件与目录的 local、SSH/byossh、Docker、Incus、Hub 端点;每条腿使用实际 binary stream,保持精确路径与明确的内容/目录语义。
135
+ - byop 是任意交互 shell,不保证 binary I/O 或 filesystem;copy/rsync 不承诺可用,拒绝并建议用显式 transport target。
136
+ - rm/mv/mkdir/chmod/stat 不增加 action;mkdir 仅 copy 的建父目录选项。
137
+ - 校验使用 SHA-256;目录使用排序、长度编码的相对路径/条目类型/内容 manifest,明确 symlink 不跟随,避免文件名含空白/换行破坏回执。
138
+ - 覆盖默认允许普通文件,目录合并无删除;不得悄悄把 copy 变为 rsync delete。路径/type 不兼容返回错误。
139
+
140
+ ### 8.2 copy 中转
141
+
142
+ 任意两远端通过工具所在发起机中转,各腿使用其已有 target 通道,不向其中一端复制另一端连接身份。中转采用任务级 `.scratch` 暂存,先收完整载体,再向目标发送;不宣称提供源到目标流式复制、delta 或断点恢复。
143
+
144
+ - 二进制 lane 需要 backpressure、传输错误/关闭、显式完成帧、有限 metadata/frame buffer 和 cancellation。
145
+ - tar/archive 或等价 carrier 在解包前验证成员路径,不接受绝对成员、`..` 出界或借 symlink 写出目标目录。
146
+ - `verify:true` 返回独立源/目标 hash 或 manifest digest、条目/字节统计、校验结果;数据端失败不能给成功回执。
147
+ - 中转任务成功后清理 owned 暂存;失败保留可诊断载体,回执给出位置和原因。保留不意味着自动重试。
148
+ - 远端临时进程、监听/临时接收文件必须取消/结束清理,不能因为“失败保留诊断”而留后台服务。
149
+ - `verify:false` 不显示已校验;successful transfer 也清理中转载体,不无限堆垃圾。
150
+
151
+ ### 8.3 SSH-only rsync
152
+
153
+ - 支持 local↔SSH,以及 SSH↔SSH;目标连接来自系统 SSH config 或 byossh 注册。
154
+ - 两个 remote 参数不能直接传给普通 rsync;工具负责变成原生 rsync 可接受的一端本地视角。
155
+ - remote↔remote 通过发起机建 SSH 隧道中继;一种实现为目标端短命 loopback rsync daemon + 发起机 local forward + 源端 reverse forward。rsync delta 算法仍为原生实现。
156
+ - 两端 identity 仅由发起机 SSH 进程使用;禁止复制 private key、导出登录凭据、ssh-agent forwarding 或修改远端 SSH config。
157
+ - daemon 仅绑定 loopback、模块限定目标目录,以当前登录用户启动;不安装永久 daemon,不公开监听。SSH 禁止 forwarding 时返回原始拒绝。
158
+ - `dryRun` 默认 true、`delete` 默认 false;`excludes`、preserve、trailing slash 内容语义通过参数表达。真正传输显式 dryRun false。
159
+ - 预检两端 rsync 和所需 SSH forwarding;缺失不静默 fallback copy,更不能对 delete/excludes 回报假成功。
160
+ - 捕获 rsync 原始退出码与 itemized changes,保持 stderr;非零不标成成功。
161
+ - 无论成功、失败、cancel 都清理 tunnel、短命 daemon 与本任务临时 config;复用 transport 不等于关掉用户既有 SSH 会话。
162
+
163
+ ## 9. 验收和完成定义
164
+
165
+ 代码、传统测试、打包成功只说明阶段进度,不等于产品验收。
166
+
167
+ 1. scoped 真实 Cordis/Agent fixture:双包仅一个 remote;Agent 创建不失败;非重名异常不被吞;不同 Agent 互不争抢;赢家卸载当前实例不补位;new/cold resume 可恢复;hot continuation 不假装恢复;退出清理。
168
+ 2. 单包 Better tarball:无 Super import/安装,SSH/Docker/Incus/BYO 按已有能力工作;jobs 缺失错误真实。
169
+ 3. Super tarball:禁用/卸载 Better 后 remote 仍可用;两包启用顺序相反都可用;Hub 存在但 machine 清单空的 Surface 正确;关闭模型工具行不拆 Hub executor。
170
+ 4. 4999 完整 launcher:安装双 tarball、真实模型 roster/script/stdin/PTY/后台任务/job_output/job_kill/copy/rsync 与错误恢复;工具 catalog 和 REPL 路由属于验收。
171
+ 5. dev3 同版本实际安装:配对但无 SSH 的 Hub PTY 与 binary copy;dev3↔另一 SSH 目标经发起机中继,远端无对方 key/config;第二次改少量内容证明真实 rsync delta;cancel/异常证明无残留。
172
+ 6. 记录上游实际 checkout、目标宿主版本、同步版本、tarball SHA-256、安装步骤、模型可见 Surface 和失败证据至 `docs/test-reports/`。
173
+ 7. Web 测试线保持运行,报告真实访问 URL,供 user 亲验。
174
+ 8. 不触碰生产 home/3080/3081/Caddy,不修改上游,不 npm publish、不 commit/tag/push 定版。发布需要本次单独放行。
175
+
176
+ ## 10. 书面确认边界
177
+
178
+ 本稿实现上述会话裁决,没有把 P2/P3、非 SSH rsync 或热接管重新塞入首轮。字段 `ssh`、端点解析、copy 的 BYO-PTY 不支持回执是保持已有接口并使交付可实现的具体化;user 可在书面规格审阅时修订。收到书面审阅后,按执行计划进入实现,不再为已批准的 routine 实现步骤重复请求授权。
179
+
180
+
181
+ ### 2026-10-10 consumer worker delivery correction
182
+
183
+ The generated Super `remote/` contains exactly `plugin.js`, `plugin.d.ts`, private `file-worker.js`, and `provenance.json`. The worker leaf is built solely from canonical `better-dsh/src/remote/file-worker.ts`, has Node-builtins-only imports, and is hashed/checked with the closure. It has no public package export, host peer imports, duplicated host modules, source loader or Better dependency. The public plugin contract remains unchanged. The existing owned child-process/group cleanup remains the receiver boundary; no worker-thread lifecycle replacement. This narrow extra generated leaf fixes actual installed receiver resolution, not a general guarantee that host plugins import outside their host resolver.
184
+
185
+ ### Task8 delivery correction: owned SSH oneshots (Ruling22)
186
+
187
+ Real consumer job-kill exposed local-only SSH termination. Foreground/background/script/stdin SSH oneshots therefore reuse the portable receiving sideband owner, preserving native literal stdio and connection normalization. Remote Bash4.1+, Python3.7+ (macOS3.10+), shared non-adjustable cross-process monotonic clock, Unix fcntl.flock and mktemp/mv/rm/sleep are required. Ordinary commands never require rsync; origin needs no Python, and this path needs no Linux/pidfd. Three SSH startups (clock, control, work), with authenticated same-identity cleanup reconnect after ambiguous startup; existing lease/bounds remain. Receiver helper/group cleanup precedes settlement, followed by up to2500ms work-stream drain; unavailable drain explicitly marks incomplete received output without fabricated counters. PTY/BYOPTY and rsync-specific restrictions remain unchanged. No second jobs registry or availability polling.
188
+
189
+
190
+ ### Task8 container oneshot ownership correction (Rulings23–24)
191
+
192
+ Actual installed Docker/Incus native job cancellation reported killed while receiving children remained live. Canonical oneshot foreground/background/script/stdin therefore share the SSH portable receiving gate/control/work barrier through native container exec argv, not local CLI termination. Each command requires three native exec startups (clock, owner, literal-stdio work), remote Bash4.1+, Python3.7+, Unix fcntl.flock/shared non-adjustable cross-process monotonic clock, mktemp/mv/rm/sleep. Linux containers additionally require stdlib ctypes and permitted unprivileged PR_SET_CHILD_SUBREAPER before business admission; the work helper reaps only its own/adopted children and stays alive until its work group is absent. This supports ordinary non-reaping PID1 containers without forcing --init, privilege/capability, image/config mutation, rsync, pidfd or origin Python. PTY/BYOPTY stay unchanged; SSH portable prerequisites are not broadened. The same eight-second counted lease, gate recovery, actual cleanup barrier and 2500ms honest final-output drain apply, with no enlarged grace, second job registry or availability polling. Consumer iteration N must prove real native killed status together with owned process/group/helper/state absence; M failures remain historical evidence.
193
+
194
+ Ruling25 supersedes the original two-remote source -R/private-master/sshd-FD/pidfd design after actual default dev3 source failed its permission requirement. A task-owned portable Python loopback proxy, inside the existing receiving helper/group/lease, carries bounded binary frames over authenticated SSH work stdio to an origin TCP bridge into the destination -L tunnel. A real full-route @RSYNCD greeting precedes business admission. No source Linux /proc, pidfd, privileged sshd access or source forwarding permission is required; native find, native rsync and the existing portable Bash/Python/fcntl/shared-monotonic prerequisites remain. No origin Python, credentials on peers, copy fallback, lease increase or second registry. Five-byte type/length frames cap data at64KiB and Python queues at256KiB; one active connection, real backpressure and half-close, prior reader/socket close before next OPEN. Ordinary two-remote transfer measures12 SSH startups: source preflight+inspection clock/work3, relay source/destination preflight2, destination clock/daemon+tunnel2, source proxy clock/owner/work3, native source rsync clock/work2. Fault recovery adds only task-owned authenticated cleanup reconnects, never transfer retries. Same real helper/listener/group/metadata/tunnel/socket cleanup barrier; receiving progress remains an explicit assumption. Historical M permission failure remains in the acceptance report; current default-dev3-source source test passes, installed next-artifact proof is separate.
195
+
196
+
197
+ ### CLI binary receiving ownership (consumer correction, Ruling26)
198
+
199
+ Actual N native SSH copy cancellation settled while the remote import process and its unnamed staging file remained live under a finite work-channel EOF hold. CLI export/import/digest now reuse the canonical portable receiving owner, with raw binary work streams and the same counted lease, admission gate and authenticated recovery. Cancellation/failure waits for receiving helper/group/temp cleanup before local transport teardown/settlement. Normal success must completely drain the binary stream; the oneshot text lane's 2500ms incomplete-output rule does not apply. Archive validation, selected manifest digests, merge/no-delete behavior and canonical SSH identity/ProxyJump are unchanged. Local and Hub file lanes are unchanged.
200
+
201
+ This intentionally replaces the former Python-only minimum for CLI peers: Bash4.1+, Python3.7+ (macOS3.10+), Unix fcntl.flock/shared non-adjustable cross-process monotonic clock, mktemp/mv/rm/sleep; Linux containers also need stdlib ctypes and permitted unprivileged PR_SET_CHILD_SUBREAPER. No rsync or origin Python requirement, auto-install, second registry, fallback, peer polling or increased lease. Minimal Python-only peers no longer support this CLI lane; use a supported peer or authenticated Hub.
202
+
203
+ Each remote export/import/digest costs three native transport starts: clock, independent owner/control, literal binary work. Thus verified local→CLI or CLI→local costs six; CLI→CLI twelve. verify:false skips digest operations, reducing counts to three/three/six. Local/Hub operations do not contribute CLI starts. Fault recovery may add same-identity authenticated cleanup starts and may remain pending until genuine cleanup observation is possible. Artifact O must re-prove installed cancellation and complete binary integrity; N failures remain historical evidence.
@@ -0,0 +1,13 @@
1
+ # DSH runtime compatibility
2
+
3
+ Better Dash and Super Dash use the same DSH host APIs. The verified upstream source baseline for this alignment is `dsh-v0.2.1-alpha.1` (`5badb15009ae1756c3afe0ae0cef1faafc290ccc`). The pages in this directory include older official reference snapshots; check this baseline before using an API described by a snapshot.
4
+
5
+ On `0.2.1-alpha.1`:
6
+
7
+ - The runtime `dsh-invariants` plugin, `ctx.invariants`, and companion `./invariant` exports are removed. The archived invariant reference does not define an available extension API.
8
+ - Plugin subpaths provide display text through `<specifier>/locale/en.json` and localized sibling exports, and icons through `<specifier>/icon`. Subpaths do not read a separate `package.json`.
9
+ - Composer statistics use separate `activity` and `usage` entries.
10
+ - The native Plugin Manager refreshes profile package resolution when bundles are installed, enabled, disabled or removed. Replacing installed package code still requires a restart.
11
+ - Automation is part of the Web composition. Reminder tools depend on agent mode; they are absent in Minimal mode and subagents.
12
+
13
+ See the [official release notes](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.1) and the actual host package exports for the complete interface contract.
@@ -1,5 +1,8 @@
1
1
  # Runtime Invariants
2
2
 
3
+ > DSH `0.2.1-alpha.1` removes this plugin and all companion `./invariant` exports. This page is an older reference snapshot; use the [runtime compatibility baseline](../baseline.md) for the current host contract.
4
+
5
+
3
6
  English | [中文](invariants.zh.md)
4
7
 
5
8
  [dsh-invariants](../../packages/runtime-diagnostics/invariants) is the configurable registry service (`ctx.invariants`) for package-owned runtime invariant checks. It is one support-group package, not a three-package capability seam, and not part of the agent-loop spine: the registry owns selection, name reservation, child-fiber lifecycle, and package-attributed failure, while every workspace package publishes a `./invariant` companion plugin that registers checks under its exact npm package name. What a check may assert — authoritative event streams or mutable data, never service or method presence — is the runtime-invariants convention in [AGENTS.md](../../AGENTS.md#conventions); the registry design is owned by the [invariant-service Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.md).
@@ -1,5 +1,8 @@
1
1
  # 运行时不变式
2
2
 
3
+ > DSH `0.2.1-alpha.1` 已移除此插件及所有配套 `./invariant` 导出。本页保留旧版参考快照;当前宿主契约见[运行时兼容基线](../baseline.md)。
4
+
5
+
3
6
  [English](invariants.md) | 中文
4
7
 
5
8
  [dsh-invariants](../../packages/runtime-diagnostics/invariants) 是面向包自有运行时不变式检查的可配置注册表服务(`ctx.invariants`)。它是一个 support 组的包,不是三包能力 seam,也不属于 agent loop(智能体循环)主干:注册表拥有选择逻辑、名称保留、子 fiber 生命周期和归因到包的失败,而每个工作区包发布一个 `./invariant` 配套插件,以自己确切的 npm 包名注册检查。检查可以断言什么(权威事件流或可变数据,绝不是服务或方法是否存在)是 [AGENTS.md](../../AGENTS.md#conventions) 中的运行时不变式约定;注册表设计由[不变式服务 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.zh.md)规定。
@@ -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"])
@@ -0,0 +1,4 @@
1
+ import "./main-uqGqYYnA.js";
2
+ import { t as convertPuppeteerChannelToBrowsersChannel } from "./LaunchOptions-iE9M-STS.js";
3
+
4
+ export { convertPuppeteerChannelToBrowsersChannel };
@@ -1,4 +1,4 @@
1
- import { b as ChromeReleaseChannel } from "./main-CupOhMp5.js";
1
+ import { b as ChromeReleaseChannel } from "./main-uqGqYYnA.js";
2
2
 
3
3
  //#region node_modules/puppeteer-core/lib/puppeteer/node/LaunchOptions.js
4
4
  /**
@@ -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-CysrjMyM.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-CJzxT9Vi.js";
4
4
  import "./BrowserWebSocketTransport-wm99h94r.js";
5
5
 
6
6
  //#region node_modules/mitt/dist/mitt.mjs
@@ -613,7 +613,7 @@ function CompactionRow({ t, loadConfig, save }) {
613
613
  * Register the dictionaries and the General settings row.
614
614
  *
615
615
  * Called from the plugin's client entry, which already injects `locale`,
616
- * `slots` and `remote.settings`.
616
+ * `slots`, `remote.settings` and `configForms`.
617
617
  *
618
618
  * @param ctx - client root context.
619
619
  */
@@ -622,7 +622,7 @@ function setupCompactionRow(ctx) {
622
622
  zh: zh$1,
623
623
  en: en$1
624
624
  }), "compaction-tuning: dictionaries");
625
- ctx.slots.inject("settings.general.item", function* () {
625
+ ctx.effect(() => ctx.configForms.whileServed([COMPACTION_SETTINGS_ENTRY], () => ctx.slots.inject("settings.general.item", function* () {
626
626
  yield ctx.slots.register({
627
627
  name: "settings.general.item",
628
628
  id: COMPACTION_SETTINGS_NS,
@@ -647,7 +647,29 @@ function setupCompactionRow(ctx) {
647
647
  }
648
648
  })
649
649
  }, CompactionRow);
650
- });
650
+ })), "compaction-tuning: served settings row");
651
+ }
652
+
653
+ //#endregion
654
+ //#region src/welcome-notice/client/index.ts
655
+ /** Complete each newly selected notice without persisting native acknowledgement state. */
656
+ function SkipWelcomeNotice({ complete }) {
657
+ const completed = (0, react.useRef)(false);
658
+ (0, react.useEffect)(() => {
659
+ if (completed.current) return;
660
+ completed.current = true;
661
+ complete();
662
+ }, [complete]);
663
+ return null;
664
+ }
665
+ /** A package client can outlive this independently toggleable Host component. */
666
+ function setupWelcomeNotice(ctx) {
667
+ ctx.effect(() => ctx.configForms.whileServed(["dashr-welcome-notice"], () => ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
668
+ name: "settings.onboarding",
669
+ id: "welcome-notice",
670
+ priority: -1,
671
+ order: -100
672
+ }, SkipWelcomeNotice))), "welcome notice: served skip policy");
651
673
  }
652
674
 
653
675
  //#endregion
@@ -658,7 +680,8 @@ const inject = [
658
680
  "locale",
659
681
  "remote",
660
682
  "remote.settings",
661
- "remote.session"
683
+ "remote.session",
684
+ "configForms"
662
685
  ];
663
686
  /**
664
687
  * Register the `failover` dictionaries and the General settings row.
@@ -671,7 +694,8 @@ function apply(ctx) {
671
694
  }), "llm-failover: dictionaries");
672
695
  setupMobileLayout(ctx);
673
696
  setupCompactionRow(ctx);
674
- ctx.slots.inject("settings.general.item", function* () {
697
+ setupWelcomeNotice(ctx);
698
+ ctx.effect(() => ctx.configForms.whileServed([FAILOVER_SETTINGS_ENTRY], () => ctx.slots.inject("settings.general.item", function* () {
675
699
  yield ctx.slots.register({
676
700
  name: "settings.general.item",
677
701
  id: "failover",
@@ -702,7 +726,7 @@ function apply(ctx) {
702
726
  }
703
727
  })
704
728
  }, FailoverRow);
705
- });
729
+ })), "llm-failover: served settings row");
706
730
  }
707
731
 
708
732
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import { t as Schema } from "../lib-C8_jXvwA.js";
2
- import { randomUUID } from "node:crypto";
3
2
  import { Context } from "@deepseek-ai/cordis";
3
+ import { randomUUID } from "node:crypto";
4
4
  import { RetryId } from "@deepseek-ai/dsh-llm-retry";
5
5
 
6
6
  //#region src/failover/config.ts
@@ -1,7 +1,7 @@
1
- import { f as UrlSchemesError, t as buildFsLayerResolver } from "../wrap-T4I0nfPM.js";
2
- import { stat } from "node:fs/promises";
3
- import { dirname, sep } from "node:path";
1
+ import { f as UrlSchemesError, t as buildFsLayerResolver } from "../wrap-0VA0uFx8.js";
4
2
  import { Context } from "@deepseek-ai/cordis";
3
+ import { dirname, sep } from "node:path";
4
+ import { stat } from "node:fs/promises";
5
5
  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";
@@ -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-BalpKaJc.js"
20795
+ "./bidi-CaQeBZHY.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-BalpKaJc.js"
20818
+ "./bidi-CaQeBZHY.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-BQeFrBM1.js");
20890
+ const { detectBrowserPlatform, resolveDefaultUserDataDir, Browser: Browser$1 } = await import("./main-taSMz1KX.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-B874eBu2.js");
20893
+ const { convertPuppeteerChannelToBrowsersChannel } = await import("./LaunchOptions-0Mr6meJc.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 {