@deepseek-ai/dsh-experimental-webworker-runtime 0.1.6-alpha.2 → 0.1.7-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/experimental/webworker-runtime/README.md
5
- README.md: 6a6911824c8c71b3deb5037d1ecf629771211a26
6
- README.zh.md: c0ef4275507725bc6f1178ab2568c4fb6127479a
5
+ README.md: 3a5d702c2163c19daa3230ffd14270b4f236f2d9
6
+ README.zh.md: a987fed1db5f607aee4e95e2faf96575c1d0c904
package/README.md CHANGED
@@ -27,12 +27,12 @@ The public npm package exposes the runtime library at its root, the self-contain
27
27
 
28
28
  Three build artifacts and one source-owned process layer:
29
29
 
30
- - **`lib/index.js` (assembly library)** — `createWorkerHost`/`startWorkerHost` mount the base image and any ordered data overlays (`storage/`), install the module loader (`module-system/`) and the `process` shim, boot the tree through the image's own `dsh-app-boot`, and hand the tunnel its serving seams. Overlays may replace files only under `home/` and `workspace/`; they cannot replace the base manifest, configuration, or modules. The image layout contract (`image-layout.ts`: virtual root, config/manifest paths, empty directories, the `lowered` wrapper-contract gate) is shared with the packer. Boot patches force the deployment-shaped rows: frontend serving off, JSONL session logs on the plaintext path, preset roots onto the image's `config/agent-presets`.
31
- - **`lib/worker.js` (worker bundle)** — the assembly plus this package's Node-compatibility layer as one self-contained ES module. The module proxy table (`module-proxies.ts`) is the only platform fork: `node:*` builtins over VFS/tunnel/browser primitives, structural stubs that fail loud on the console for what a browser cannot do, and native/binary package replacements. `node:module` supplies `createRequire().resolve` and `.resolve.paths()` over the image package root, so unchanged packages can discover manifests without evaluating their modules. The global `process` shim carries Node detection fields including `title`, preventing Worker execution from entering DOM-only branches. The pack-time parser reports statically named module requests, including module-scope direct calls of the form `createRequire(import.meta.url)('pkg')` through a named `node:module` or `module` import, to the packer's reachability walk. Stored, CommonJS-obtained, and rebased `createRequire` calls require image entry seeds. VFS mutations drive `node:fs` callback, polling, and promise watchers; open descriptors retain file identity and access mode across rename, replacement, and unlink, `FileHandle.stat({ bigint: true })` reports the same device and inode identity as a path stat while the name still refers to that file, and `FileHandle.chmod()` updates the opened file identity; `readable-stream` supplies the stream state machine used by file streams and unchanged image packages such as Chokidar and readdirp. AsyncLocalStorage carries sync-stack causality across `await` through the snapshot/restore faces the pack-time lowering injects. The worker holds no compiler: an image the packer did not lower is refused at mount. The callback `fs.realpath.native` alias resolves within the mounted VFS and supports `util.promisify`.
30
+ - **`lib/index.js` (assembly library)** — `createWorkerHost`/`startWorkerHost` mount the base image and any ordered data overlays (`storage/`), install the module loader (`module-system/`) and the `process` shim, boot the tree through the image's own `dsh-app-boot`, and hand the tunnel its serving seams. Overlays may replace files only under `home/` and `workspace/`; they cannot replace the base manifest, configuration, or modules. The image layout contract (`image-layout.ts`: virtual root, config/manifest paths, empty directories, the `lowered` wrapper-contract gate) is shared with the packer. The Worker supplies `profileContext` for an editable profile at `home/profiles/preview/`: the packed rows seed its insertion patch, and ConfigEditor persists overrides in that VFS patch. ConfigEditor reconciles each write and Loader commits live values; package management and Node module HMR stay disabled. Deployment overlays keep JSONL session logs plaintext and preset roots at the image's `config/agent-presets`. Configuration edits last for the current Worker lifetime.
31
+ - **`lib/worker.js` (worker bundle)** — the assembly plus this package's Node-compatibility layer as one self-contained ES module. The module proxy table (`module-proxies.ts`) is the only platform fork: `node:*` builtins over VFS/tunnel/browser primitives, structural stubs that fail loud on the console for what a browser cannot do, and native/binary package replacements. `node:module` supplies `createRequire().resolve` and `.resolve.paths()` over the image package root, so unchanged packages can discover manifests without evaluating their modules. The `node:util` JSON-data comparator compares own enumerable properties, preserving Node admission decisions for recorded fields such as `__proto__`. The global `process` shim carries Node detection fields including `title`, preventing Worker execution from entering DOM-only branches. The pack-time parser reports statically named module requests, including module-scope direct calls of the form `createRequire(import.meta.url)('pkg')` through a named `node:module` or `module` import, to the packer's reachability walk. Stored, CommonJS-obtained, and rebased `createRequire` calls require image entry seeds. VFS mutations drive `node:fs` callback, polling, and promise watchers; open descriptors retain file identity and access mode across rename, replacement, and unlink, `FileHandle.stat({ bigint: true })` reports the same device and inode identity as a path stat while the name still refers to that file, and `FileHandle.chmod()` updates the opened file identity; `readable-stream` supplies `node:stream/promises` completion and pipeline helpers and the stream state machine used by file streams and unchanged image packages such as Chokidar and readdirp. AsyncLocalStorage carries sync-stack causality across `await` through the snapshot/restore faces the pack-time lowering injects. The worker holds no compiler: an image the packer did not lower is refused at mount. The callback `fs.realpath.native` alias resolves within the mounted VFS and supports `util.promisify`.
32
32
  - **`src/shell/` (the worker's own process layer)** — a browser worker cannot fork, so `node:child_process` is not a stub but an implementation: `spawn` starts the command in its own Web Worker — this same bundle, told by its first frame to be a shell process — and reports it through the `ChildProcess` surface the subprocess service consumes. The command runs off the host's thread, `SIGKILL` terminates it whatever it is doing, and it reaches the VFS only by message (the host serves those frames). Worker platform executables preserve native-package protocols such as Landlock without replacing their JavaScript packages or coupling their implementations to `node:child_process`; ordinary commands use the package's evaluator and coreutils command table. The grammar is `@yarnpkg/parsers`' `parseShell`, while `execSync`/`fork` still refuse because they need a real process.
33
- - **`lib/client.js` (page half)** — startup has two independent stages. `chooseWorkerHostSource({ image?, fixtureManifest? })` optionally owns the boot barrier and fixture manifest: without `preview-fixture` it waits at the source chooser, while a valid query selects directly; either path returns ordered overlays. `connectWorkerHost(worker, { image?, overlays? })` remains the public base-runtime connector; callers that skip the chooser get an empty overlay list. `apps/web` invokes both and supplies its statically bundled Worker. The opening `init` frame carries the base and ordered overlay URLs, the boot payload delivers the structured index-injection table, and `applyIndexInjections` executes it before the shell entry runs. Script preload rows are advisory and skipped because `/plugins` resources resolve only through the tunnel; `loadBundle` fetches each combo on first demand, embeds its tunnel-only source map as a Base64 data URL, and executes the script as a Blob. The tunnel also exposes fetch-shaped transport, the independent file-upload carrier, and the API client. Request frames preserve Blob bodies through structured clone and transfer `ReadableStream<Uint8Array>` ownership. The Host Worker streams both forms into the route, so neither browser thread creates a complete byte array for a generic-file upload.
33
+ - **`lib/client.js` (page half)** — startup has two independent stages. `chooseWorkerHostSource({ image?, fixtureManifest? })` optionally owns the boot barrier and fixture manifest: without `preview-fixture` it waits at the source chooser, while a valid query selects directly; either path returns ordered overlays. `connectWorkerHost(worker, { image?, overlays? })` remains the public base-runtime connector; callers that skip the chooser get an empty overlay list. `apps/web` invokes both and supplies its statically bundled Worker. The opening `init` frame carries the base and ordered overlay URLs, the boot payload delivers the structured index-injection table, and `applyIndexInjections` executes it before the shell entry runs. Script preload rows are advisory and skipped because `/plugins` resources resolve only through the tunnel; `loadBundle` fetches each combo on first demand, embeds its tunnel-only source map as a Base64 data URL, and executes the script as a Blob. The tunnel also exposes fetch-shaped transport, the independent file-upload carrier, and the API client. Request frames preserve Blob bodies through structured clone and transfer `ReadableStream<Uint8Array>` ownership. The Host Worker streams both forms into the route, so neither browser thread creates a complete byte array for a generic-file upload. A Remote stream's uplink crosses as `stream-uplink-item` / `stream-uplink-end` frames into an unbounded `TunnelUplink` that drops items arriving after `end`: the page and its worker are one trusted same-origin boundary, so the Host inbox cap does not apply.
34
34
 
35
- Acceptance lives in `apps/web/tests/preview-boot.e2e.ts`, which serves the real built pages and drives the pre-boot chooser plus Worker activation in headless Chromium. The empty selection exercises first-run startup. The `vfs-example` overlay supplies ordinary workspace files and plaintext persistence artifacts for cold Workspace/Session discovery, tool presentation, subagent navigation, and history paging without a model request. The fixture generator owns current-generation logs and the projection cache; committed predecessor logs remain byte-identical alongside them. The chooser reserves WebFS as a separate user-authorized source; that provider does not read the built-in fixture.
35
+ Acceptance lives in `apps/web/tests/preview-boot.e2e.ts`, which serves the real built pages and drives the pre-boot chooser plus Worker activation in headless Chromium. The empty selection exercises first-run startup. The `vfs-example` overlay supplies ordinary workspace files and plaintext persistence artifacts for cold Workspace/Session discovery, tool presentation, subagent navigation, and history paging without a model request. The [packer](../webworker-packer/README.md) prepares current-generation successors from committed historical logs in Node; those source logs remain byte-identical in the overlay. The fixture generator owns the acceptance test's current projection cache. The chooser reserves WebFS as a separate user-authorized source; that provider does not read the built-in fixture.
36
36
 
37
37
  The [built-bundle import sweep](tests/compile/transform-corpus-check.ts) checks bare Node imports after the library build. Its Dockkit exception accepts Node’s unknown-`.css`-extension refusal for any stylesheet rather than one pinned path; another extension, error code, or message, and an unexpectedly successful exempt import, remain errors. See the [stylesheet exemption decision](../../../.agents/notes/implemented/bug-fix/2026-09-10-built-bundle-css-exemption.md).
38
38
 
package/README.zh.md CHANGED
@@ -27,12 +27,12 @@ kind: "package-library"
27
27
 
28
28
  一条 tsdown 管线产出三个构建产物;另有一层由源码维护的进程实现:
29
29
 
30
- - **`lib/index.js`(装配库)**——`createWorkerHost`/`startWorkerHost` 挂载基础镜像和按序排列的数据 overlays(`storage/`)、安装模块加载器(`module-system/`)与 `process` shim、经镜像自带的 `dsh-app-boot` 启动插件树,并把服务 seam 交给隧道。Overlay 只能替换 `home/` 与 `workspace/` 下的文件,不能替换基础 manifest、配置或模块。镜像布局契约(`image-layout.ts`:虚拟根、config/manifest 路径、空目录、`lowered` 包装契约门)与 packer 共享。boot patch 强制部署形态行:关前端静态服务、JSONL 会话日志走明文、preset 根指向镜像内 `config/agent-presets`。
31
- - **`lib/worker.js`(worker 束)**——装配库加本包的 Node 兼容层,合成一个自含 ES module。模块代理表(`module-proxies.ts`)是唯一平台叉口:`node:*` 内建走 VFS、隧道和浏览器原语,浏览器做不到的走结构化 stub(调用即在 console 报错并抛出),native/binary 包则替换执行后端。`node:module` 在镜像 package 根之上提供 `createRequire().resolve` 与 `.resolve.paths()`,使未修改的包无需执行目标模块即可发现 manifest。全局 `process` shim 带有包括 `title` 在内的 Node 环境识别字段,避免 Worker 执行误入仅适用于 DOM 的分支。pack 期解析器会把名称静态可知的模块请求报告给 packer 的可达性遍历,其中包括通过 `node:module` 或 `module` 具名导入在模块作用域直接发起的 `createRequire(import.meta.url)('pkg')` 调用。保存、经 CommonJS 获取或另设基准的 `createRequire` 调用需要镜像入口种子。VFS mutation 驱动 `node:fs` 的 callback、polling 和 promise watcher;打开的 descriptor 在 rename、replacement 和 unlink 后仍保留文件身份与访问模式,只要文件名仍指向该文件,`FileHandle.stat({ bigint: true })` 报告的 device 与 inode 身份就与路径 stat 相同,`FileHandle.chmod()` 则更新打开文件身份的权限;`readable-stream` 提供文件流以及 Chokidar、readdirp 等未修改镜像包所用的流状态机。AsyncLocalStorage 经 pack 时降低注入的 snapshot/restore 面在 `await` 间携带同步栈因果。worker 不带编译器:packer 未降低的镜像在挂载时被拒。回调式 `fs.realpath.native` 别名在已挂载的 VFS 内解析路径,并支持 `util.promisify`。
30
+ - **`lib/index.js`(装配库)**——`createWorkerHost`/`startWorkerHost` 挂载基础镜像和按序排列的数据 overlays(`storage/`)、安装模块加载器(`module-system/`)与 `process` shim、经镜像自带的 `dsh-app-boot` 启动插件树,并把服务 seam 交给隧道。Overlay 只能替换 `home/` 与 `workspace/` 下的文件,不能替换基础 manifest、配置或模块。镜像布局契约(`image-layout.ts`:虚拟根、config/manifest 路径、空目录、`lowered` 包装契约门)与 packer 共享。Worker 为 `home/profiles/preview/` 下可编辑的 profile 提供 `profileContext`:打包行初始化其插入 patch,ConfigEditor 将覆盖值持久化到该 VFS patch。ConfigEditor 协调每次写入,Loader 提交实时值;包管理和 Node 模块 HMR 保持禁用。部署 overlay 保持 JSONL 会话日志为明文,preset 根指向镜像内 `config/agent-presets`。配置修改在当前 Worker 生命周期内有效。预设声明包含在组合后的配置中。
31
+ - **`lib/worker.js`(worker 束)**——装配库加本包的 Node 兼容层,合成一个自含 ES module。模块代理表(`module-proxies.ts`)是唯一平台叉口:`node:*` 内建走 VFS、隧道和浏览器原语,浏览器做不到的走结构化 stub(调用即在 console 报错并抛出),native/binary 包则替换执行后端。`node:module` 在镜像 package 根之上提供 `createRequire().resolve` 与 `.resolve.paths()`,使未修改的包无需执行目标模块即可发现 manifest。`node:util` 的 JSON 数据比较器检查可枚举的自有属性,使 `__proto__` 等记录字段的准入判定与 Node 一致。全局 `process` shim 带有包括 `title` 在内的 Node 环境识别字段,避免 Worker 执行误入仅适用于 DOM 的分支。pack 期解析器会把名称静态可知的模块请求报告给 packer 的可达性遍历,其中包括通过 `node:module` 或 `module` 具名导入在模块作用域直接发起的 `createRequire(import.meta.url)('pkg')` 调用。保存、经 CommonJS 获取或另设基准的 `createRequire` 调用需要镜像入口种子。VFS mutation 驱动 `node:fs` 的 callback、polling 和 promise watcher;打开的 descriptor 在 rename、replacement 和 unlink 后仍保留文件身份与访问模式,只要文件名仍指向该文件,`FileHandle.stat({ bigint: true })` 报告的 device 与 inode 身份就与路径 stat 相同,`FileHandle.chmod()` 则更新打开文件身份的权限;`readable-stream` 提供 `node:stream/promises` 的完成等待与管道方法,以及文件流以及 Chokidar、readdirp 等未修改镜像包所用的流状态机。AsyncLocalStorage 经 pack 时降低注入的 snapshot/restore 面在 `await` 间携带同步栈因果。worker 不带编译器:packer 未降低的镜像在挂载时被拒。回调式 `fs.realpath.native` 别名在已挂载的 VFS 内解析路径,并支持 `util.promisify`。
32
32
  - **`src/shell/`(worker 自己的进程层)**——浏览器 worker 无法 fork,所以 `node:child_process` 不是 stub 而是实现:`spawn` 把命令放进它自己的 Web Worker——就是这同一个束,由首帧告诉它「你是 shell 进程」——并以 subprocess 服务消费的 `ChildProcess` 面报告结果。命令不占宿主线程,`SIGKILL` 不管它在干什么都能终止它,而它只能靠消息触达 VFS(由宿主应答这些帧)。Worker 平台 executable 在不替换 JavaScript 包、也不把具体实现耦合进 `node:child_process` 的情况下保持 Landlock 等 native 包协议;普通命令使用本包的求值器与 coreutils 命令表。语法来自 `@yarnpkg/parsers` 的 `parseShell`,而 `execSync`/`fork` 依然拒绝,因为它们需要真进程。
33
- - **`lib/client.js`(页面半)**——启动分为相互独立的两段。`chooseWorkerHostSource({ image?, fixtureManifest? })` 可选地拥有 boot barrier 与 fixture manifest:没有 `preview-fixture` 时停在来源选择面板,合法 query 则直接选择;两条路径都返回按序排列的 overlays。`connectWorkerHost(worker, { image?, overlays? })` 仍是公开的基础运行态连接器;调用方跳过选择器时 overlay 列表为空。`apps/web` 调用这两段并提供静态打包的 Worker。开局 `init` 帧携带基础镜像与按序排列的 overlay URL,boot 载荷送达结构化 index 注入表,`applyIndexInjections` 在壳入口运行前逐行执行。脚本 preload 行只是提示,因此会被跳过:`/plugins` 资源只能经 tunnel 解析,`loadBundle` 会在首次需要时获取 combo、把仅 tunnel 可达的 sourcemap 内嵌为 Base64 data URL,再以 Blob 执行脚本。Tunnel 还暴露 fetch 形式的传输、独立文件上传载体和 API 客户端。请求帧通过结构化克隆保留 Blob 请求体,并转移 `ReadableStream<Uint8Array>` 的所有权。Host Worker 将两种请求体都逐块送入路由,因此通用文件上传不会在任何浏览器线程创建完整字节数组。
33
+ - **`lib/client.js`(页面半)**——启动分为相互独立的两段。`chooseWorkerHostSource({ image?, fixtureManifest? })` 可选地拥有 boot barrier 与 fixture manifest:没有 `preview-fixture` 时停在来源选择面板,合法 query 则直接选择;两条路径都返回按序排列的 overlays。`connectWorkerHost(worker, { image?, overlays? })` 仍是公开的基础运行态连接器;调用方跳过选择器时 overlay 列表为空。`apps/web` 调用这两段并提供静态打包的 Worker。开局 `init` 帧携带基础镜像与按序排列的 overlay URL,boot 载荷送达结构化 index 注入表,`applyIndexInjections` 在壳入口运行前逐行执行。脚本 preload 行只是提示,因此会被跳过:`/plugins` 资源只能经 tunnel 解析,`loadBundle` 会在首次需要时获取 combo、把仅 tunnel 可达的 sourcemap 内嵌为 Base64 data URL,再以 Blob 执行脚本。Tunnel 还暴露 fetch 形式的传输、独立文件上传载体和 API 客户端。请求帧通过结构化克隆保留 Blob 请求体,并转移 `ReadableStream<Uint8Array>` 的所有权。Host Worker 将两种请求体都逐块送入路由,因此通用文件上传不会在任何浏览器线程创建完整字节数组。Remote 流的上行以 `stream-uplink-item` / `stream-uplink-end` 帧进入没有上限的 `TunnelUplink`,`end` 之后到达的项被丢弃:页面与其 worker 是同一个可信的同源边界,Host 的 inbox 上限在此不适用。
34
34
 
35
- 验收在 `apps/web/tests/preview-boot.e2e.ts`:静态服务真实构建页面,在 headless Chromium 里驱动 pre-boot 选择面板与 Worker 激活。空白选择验证首次启动;`vfs-example` overlay 提供普通 workspace 文件与明文 persistence 产物,无需模型请求即可验证 Workspace/Session 冷发现、工具呈现、subagent 导航和历史分页。fixture 生成器负责当前代日志与投影缓存;已提交的前代日志逐字节保持不变,与它们并存。选择面板为 WebFS 保留独立的用户授权来源;该 provider 不读取内置 fixture。
35
+ 验收在 `apps/web/tests/preview-boot.e2e.ts`:静态服务真实构建页面,在 headless Chromium 里驱动 pre-boot 选择面板与 Worker 激活。空白选择验证首次启动;`vfs-example` overlay 提供普通 workspace 文件与明文 persistence 产物,无需模型请求即可验证 Workspace/Session 冷发现、工具呈现、subagent 导航和历史分页。[打包器](../webworker-packer/README.zh.md)在 Node 中根据已提交的历史日志准备当前代后继文件;这些源日志在 overlay 中逐字节保持不变。fixture 生成器负责验收测试的当前投影缓存。选择面板为 WebFS 保留独立的用户授权来源;该 provider 不读取内置 fixture。
36
36
 
37
37
  [已构建 bundle 导入检查](tests/compile/transform-corpus-check.ts)在库构建后检查裸 Node 导入。Dockkit 例外接受 Node 针对任意样式表报告的未知 `.css` 扩展名错误,而不限定某一固定路径;其他扩展名、错误码或消息,以及意外成功的豁免导入,仍然报错。参见 [样式表豁免决策](../../../.agents/notes/implemented/bug-fix/2026-09-10-built-bundle-css-exemption.zh.md)。
38
38
 
package/lib/client.js CHANGED
@@ -179,7 +179,7 @@ var WorkerTunnel = class {
179
179
  kind: "carrier",
180
180
  message: `web-preview tunnel: worker failed: ${event.message}`
181
181
  }, { cause: reason });
182
- for (const inbox of this.logicalStreams.values()) inbox.fail(failure);
182
+ for (const { inbox } of this.logicalStreams.values()) inbox.fail(failure);
183
183
  this.logicalStreams.clear();
184
184
  for (const release of this.releases.values()) release();
185
185
  this.releases.clear();
@@ -235,19 +235,24 @@ var WorkerTunnel = class {
235
235
  * @param endpoint - canonical Gateway Remote endpoint.
236
236
  * @param payload - decoded endpoint payload.
237
237
  * @param signal - logical-stream cancellation.
238
+ * @param uplink - the stream's uplink, posted as `stream-uplink-item` frames and closed with `stream-uplink-end`.
238
239
  * @returns decoded stream values from the worker Host.
239
240
  */
240
- async *open(endpoint, payload, signal) {
241
+ async *open(endpoint, payload, signal, uplink) {
241
242
  signal.throwIfAborted();
242
243
  const id = this.nextId++;
243
244
  const inbox = new LogicalStreamInbox();
245
+ const stream = {
246
+ inbox,
247
+ pump: void 0
248
+ };
244
249
  let opened = false;
245
250
  let terminal = false;
246
251
  const onAbort = () => {
247
252
  inbox.fail(signal.reason);
248
253
  };
249
254
  signal.addEventListener("abort", onAbort, { once: true });
250
- this.logicalStreams.set(id, inbox);
255
+ this.logicalStreams.set(id, stream);
251
256
  this.inFlight.set(id, `STREAM ${endpoint}`);
252
257
  try {
253
258
  const frame = {
@@ -265,6 +270,7 @@ var WorkerTunnel = class {
265
270
  message: `web-preview tunnel: failed to open Remote stream ${endpoint}`
266
271
  }, { cause });
267
272
  }
273
+ if (uplink !== void 0) stream.pump = this.pumpUplink(id, uplink, signal, inbox);
268
274
  while (true) {
269
275
  const response = await inbox.next();
270
276
  signal.throwIfAborted();
@@ -280,8 +286,67 @@ var WorkerTunnel = class {
280
286
  signal.removeEventListener("abort", onAbort);
281
287
  this.logicalStreams.delete(id);
282
288
  this.inFlight.delete(id);
289
+ stream.pump?.stop();
283
290
  if (opened && !terminal) this.abortWorkerOperation(id);
291
+ if (stream.pump !== void 0) await stream.pump.done;
292
+ }
293
+ }
294
+ /**
295
+ * Post the caller's uplink items for one logical stream. A failing uplink
296
+ * fails the downlink, and the enclosing `open` then aborts the worker side.
297
+ * `stop()` interrupts a pump blocked on `uplink.next()` and returns the
298
+ * caller's iterator at once, so a handle's queue closes and `send()` throws
299
+ * from then on; a generator blocked in `next()` completes that return only
300
+ * once it yields, so it is not awaited.
301
+ */
302
+ pumpUplink(id, uplink, signal, inbox) {
303
+ const interrupt = Promise.withResolvers();
304
+ const iterator = uplink[Symbol.asyncIterator]();
305
+ const state = {
306
+ active: true,
307
+ released: false
308
+ };
309
+ const release = () => {
310
+ if (state.released) return;
311
+ state.released = true;
312
+ Promise.resolve(iterator.return?.()).catch(() => void 0);
313
+ };
314
+ return {
315
+ done: this.forwardUplink(id, iterator, interrupt.promise, () => state.active && !signal.aborted).then((exhausted) => {
316
+ state.released ||= exhausted;
317
+ }, (error) => {
318
+ inbox.fail(error);
319
+ }).then(release),
320
+ stop: () => {
321
+ state.active = false;
322
+ interrupt.resolve({
323
+ value: void 0,
324
+ done: true
325
+ });
326
+ release();
327
+ }
328
+ };
329
+ }
330
+ /**
331
+ * Post items until the caller's iterator ends, the pump is stopped, or the page aborts the stream.
332
+ * @returns whether the caller's iterator was exhausted and the uplink end posted.
333
+ */
334
+ async forwardUplink(id, iterator, interrupt, isActive) {
335
+ for (;;) {
336
+ const next = await Promise.race([iterator.next(), interrupt]);
337
+ if (!isActive()) return false;
338
+ if (next.done === true) break;
339
+ this.worker.postMessage({
340
+ t: "stream-uplink-item",
341
+ id,
342
+ value: next.value
343
+ });
284
344
  }
345
+ this.worker.postMessage({
346
+ t: "stream-uplink-end",
347
+ id
348
+ });
349
+ return true;
285
350
  }
286
351
  /**
287
352
  * Read the pre-cordis boot payload (the injection table).
@@ -298,7 +363,8 @@ var WorkerTunnel = class {
298
363
  * The image packs each bundle with a trailing `sourceURL` naming its image
299
364
  * path, so the blob shows under that name in the debugger instead of as an
300
365
  * anonymous blob entry.
301
- * @param url - Graph combo URL (`/plugins/??<id>/client.js&rev=...`).
366
+ * @param url - Graph combo reference (`plugins/??<id>/client.js&rev=...`), which
367
+ * this tunnel resolves against the page origin it maps from.
302
368
  */
303
369
  async loadBundle(url) {
304
370
  const response = await this.fetch(url);
@@ -474,9 +540,13 @@ var WorkerTunnel = class {
474
540
  }
475
541
  case "stream-item":
476
542
  case "stream-end":
477
- case "stream-error":
478
- this.logicalStreams.get(frame.id)?.push(frame);
543
+ case "stream-error": {
544
+ const stream = this.logicalStreams.get(frame.id);
545
+ if (stream === void 0) return;
546
+ stream.inbox.push(frame);
547
+ if (frame.t !== "stream-item") stream.pump?.stop();
479
548
  return;
549
+ }
480
550
  default: throw new Error(`web-preview tunnel: unknown frame ${JSON.stringify(frame)}`);
481
551
  }
482
552
  }
@@ -796,7 +866,7 @@ async function connectWorkerHost(worker, options) {
796
866
  const payload = await tunnel.bootPayload();
797
867
  globalThis.__DSH_TRANSPORT__ = {
798
868
  fetch: (input, init) => tunnel.fetch(input, init),
799
- openStream: (endpoint, payload, signal) => tunnel.open(endpoint, payload, signal),
869
+ openStream: (endpoint, payload, signal, uplink) => tunnel.open(endpoint, payload, signal, uplink),
800
870
  loadBundle: (url) => tunnel.loadBundle(url),
801
871
  ownsHost: true
802
872
  };
package/lib/index.js CHANGED
@@ -107,6 +107,15 @@ function parseInboundFrame(data) {
107
107
  t: "abort",
108
108
  id
109
109
  };
110
+ if (frame.t === "stream-uplink-end") return {
111
+ t: "stream-uplink-end",
112
+ id
113
+ };
114
+ if (frame.t === "stream-uplink-item") return {
115
+ t: "stream-uplink-item",
116
+ id,
117
+ value: frame.value
118
+ };
110
119
  if (frame.t === "stream-open") {
111
120
  if (typeof frame.endpoint !== "string" || frame.endpoint.length === 0) throw new Error(`webworker tunnel: stream ${String(id)} needs a non-empty endpoint`);
112
121
  return {
@@ -725,6 +734,8 @@ const MODULE_PROXIES = {
725
734
  "dns/promises": "./node/builtin_modules/mock/dns/promises.ts",
726
735
  "node:net": "./node/builtin_modules/mock/net.ts",
727
736
  "node:stream": "./node/builtin_modules/implemented/stream.ts",
737
+ "node:stream/promises": "./node/builtin_modules/implemented/stream/promises.ts",
738
+ "stream/promises": "./node/builtin_modules/implemented/stream/promises.ts",
728
739
  "node:vm": "./node/builtin_modules/mock/vm.ts",
729
740
  "node:worker_threads": "./node/builtin_modules/mock/worker_threads.ts",
730
741
  "node:sqlite": "./node/builtin_modules/mock/sqlite.ts",
@@ -1519,6 +1530,61 @@ var BufferedSink = class {
1519
1530
  for (const call of this.calls.splice(0)) call();
1520
1531
  }
1521
1532
  };
1533
+ /** Uplink items of one worker-local logical stream, read once by the Host method as its `uplink`. */
1534
+ var TunnelUplink = class {
1535
+ items = [];
1536
+ ended = false;
1537
+ closed = false;
1538
+ wake;
1539
+ push(value) {
1540
+ if (this.ended || this.closed) return;
1541
+ this.items.push(value);
1542
+ this.signal();
1543
+ }
1544
+ /** Page half-close or stream end; buffered items still drain. */
1545
+ end() {
1546
+ this.ended = true;
1547
+ this.signal();
1548
+ }
1549
+ [Symbol.asyncIterator]() {
1550
+ return this;
1551
+ }
1552
+ async next() {
1553
+ while (true) {
1554
+ if (this.closed) return {
1555
+ value: void 0,
1556
+ done: true
1557
+ };
1558
+ if (this.items.length > 0) return {
1559
+ value: this.items.shift(),
1560
+ done: false
1561
+ };
1562
+ if (this.ended) return {
1563
+ value: void 0,
1564
+ done: true
1565
+ };
1566
+ if (this.wake !== void 0) throw new Error("webworker tunnel: stream uplink has one pending read");
1567
+ await new Promise((resolve) => {
1568
+ this.wake = resolve;
1569
+ });
1570
+ }
1571
+ }
1572
+ /** The Host stopped reading: buffered and later items are dropped. */
1573
+ return() {
1574
+ this.closed = true;
1575
+ this.items.length = 0;
1576
+ this.signal();
1577
+ return Promise.resolve({
1578
+ value: void 0,
1579
+ done: true
1580
+ });
1581
+ }
1582
+ signal() {
1583
+ const wake = this.wake;
1584
+ this.wake = void 0;
1585
+ wake?.();
1586
+ }
1587
+ };
1522
1588
  /** One tunnel per worker; wire {@link TunnelServer.handleMessage} to `onmessage` first. */
1523
1589
  var TunnelServer = class {
1524
1590
  port;
@@ -1527,6 +1593,8 @@ var TunnelServer = class {
1527
1593
  unaryApiLane;
1528
1594
  queue = [];
1529
1595
  inFlight = /* @__PURE__ */ new Map();
1596
+ /** Uplinks of accepted `stream-open` frames, buffering items that arrive before or while the stream serves. */
1597
+ uplinks = /* @__PURE__ */ new Map();
1530
1598
  seams;
1531
1599
  failure;
1532
1600
  listener;
@@ -1543,9 +1611,18 @@ var TunnelServer = class {
1543
1611
  handleMessage(data) {
1544
1612
  const frame = parseInboundFrame(data);
1545
1613
  if (frame.t === "init") throw new Error("webworker tunnel: duplicate init frame; the tunnel is already open");
1614
+ if (frame.t === "stream-uplink-item") {
1615
+ this.uplinks.get(frame.id)?.push(frame.value);
1616
+ return;
1617
+ }
1618
+ if (frame.t === "stream-uplink-end") {
1619
+ this.uplinks.get(frame.id)?.end();
1620
+ return;
1621
+ }
1546
1622
  if (frame.t === "abort") {
1547
1623
  this.inFlight.get(frame.id)?.abort();
1548
1624
  this.inFlight.delete(frame.id);
1625
+ this.dropUplink(frame.id);
1549
1626
  const queued = this.queue.findIndex((request) => request.id === frame.id);
1550
1627
  if (queued !== -1) this.queue.splice(queued, 1);
1551
1628
  return;
@@ -1554,6 +1631,7 @@ var TunnelServer = class {
1554
1631
  this.refuse(frame, this.failure);
1555
1632
  return;
1556
1633
  }
1634
+ if (frame.t === "stream-open") this.uplinks.set(frame.id, new TunnelUplink());
1557
1635
  if (this.seams === void 0) {
1558
1636
  this.queue.push(frame);
1559
1637
  return;
@@ -1584,6 +1662,7 @@ var TunnelServer = class {
1584
1662
  }
1585
1663
  refuse(frame, message) {
1586
1664
  if (frame.t === "stream-open") {
1665
+ this.dropUplink(frame.id);
1587
1666
  this.send({
1588
1667
  t: "stream-error",
1589
1668
  id: frame.id,
@@ -1614,12 +1693,13 @@ var TunnelServer = class {
1614
1693
  return;
1615
1694
  }
1616
1695
  const seams = this.seams;
1696
+ const uplink = this.uplinks.get(frame.id) ?? new TunnelUplink();
1617
1697
  const controller = new AbortController();
1618
1698
  this.inFlight.set(frame.id, { abort: () => {
1619
1699
  controller.abort();
1620
1700
  } });
1621
1701
  try {
1622
- const source = await seams.openStream(frame.endpoint, frame.payload, controller.signal);
1702
+ const source = await seams.openStream(frame.endpoint, frame.payload, uplink, controller.signal);
1623
1703
  for await (const value of source) {
1624
1704
  if (controller.signal.aborted) return;
1625
1705
  this.send({
@@ -1646,8 +1726,15 @@ var TunnelServer = class {
1646
1726
  }
1647
1727
  } finally {
1648
1728
  this.inFlight.delete(frame.id);
1729
+ this.dropUplink(frame.id);
1649
1730
  }
1650
1731
  }
1732
+ /** The stream is over: settle any Host read still waiting on its uplink and stop buffering. */
1733
+ dropUplink(id) {
1734
+ const uplink = this.uplinks.get(id);
1735
+ this.uplinks.delete(id);
1736
+ uplink?.end();
1737
+ }
1651
1738
  sinkFor(id) {
1652
1739
  const send = this.send.bind(this);
1653
1740
  const inFlight = this.inFlight;
@@ -3286,8 +3373,10 @@ function createWorkerHost(options) {
3286
3373
  const require = loader.requireFrom(dirname(configPath));
3287
3374
  const appBoot = require("@deepseek-ai/dsh-app-boot");
3288
3375
  const cmdline = require("@deepseek-ai/dsh-cmdline");
3289
- const { patches, presetOverlay } = bootPatches(loader, mounted, configPath, root);
3290
- const ctx = await appBoot.boot("dsh-webworker", configPath, patches, (hostCtx) => {
3376
+ const { patches, profile } = bootPatches(loader, mounted, configPath, root);
3377
+ const profileConfig = join(profile.dir, "cordis.yml");
3378
+ const ctx = await appBoot.boot("dsh-webworker", profileConfig, patches, (hostCtx) => {
3379
+ hostCtx.provide("profileContext", profile);
3291
3380
  hostCtx.loader.internal = loader.internal;
3292
3381
  installLogSink(hostCtx, require);
3293
3382
  cmdline.provideCmdline(hostCtx, {
@@ -3310,11 +3399,11 @@ function createWorkerHost(options) {
3310
3399
  if (typertGateway === void 0) throw new Error("webworker host: the tree activated without a typertGateway service");
3311
3400
  const handler = connection.createSharedFetchHandler("/api");
3312
3401
  const usage = loader.usage();
3313
- console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, preset root overlay=${presetOverlay ? "applied" : "already in roster"}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
3402
+ console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
3314
3403
  tunnel.serve({
3315
3404
  directFetch: (request) => handler.fetch(request),
3316
3405
  bootPayload: () => readBootPayload(ctx),
3317
- openStream: typertGateway.wireStream.open,
3406
+ openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, void 0, signal),
3318
3407
  streamFailure: typertGateway.wireStream.failure
3319
3408
  });
3320
3409
  } catch (reason) {
@@ -3386,30 +3475,22 @@ function requireLoweredImage(vfs, path) {
3386
3475
  if (lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
3387
3476
  }
3388
3477
  /**
3389
- * The shipped preset root, as the application layer that owns the composition
3390
- * supplies it.
3391
- *
3392
- * A launcher appends this root itself rather than writing it into the roster —
3393
- * `apps/cli` does it in `composeProfile` (`profile-boot.ts:159-166`) because only
3394
- * the application knows where its own presets sit. The worker's presets travel
3395
- * in the image, so the same overlay names their virtual path. Patching replaces
3396
- * a row's whole `config`, so the current one is read and spread, and a roster
3397
- * that already names roots keeps them.
3478
+ * Prepare an editable VFS profile from the packed rows and deployment overlays.
3479
+ * ConfigEditor writes overrides beside the insertion rows; reconciliation starts
3480
+ * at the empty profile root and retains the Worker deployment overlays.
3398
3481
  * @param loader - Module loader, for the image's YAML reader.
3399
3482
  * @param vfs - Filesystem holding the composed configuration.
3400
3483
  * @param configPath - Composed configuration path.
3401
3484
  * @param root - Virtual root.
3402
- * @returns Boot patches (preset root overlay, frontend serving off) and
3403
- * whether the preset overlay was applied.
3485
+ * @returns Profile locations and effective boot patches.
3404
3486
  */
3405
3487
  function bootPatches(loader, vfs, configPath, root) {
3406
3488
  const text = vfs.readFileSync(configPath, "utf8");
3489
+ const include = loader.load(loader.resolve("@deepseek-ai/cordis-plugin-include", root));
3490
+ const yaml = loader.load(loader.resolve("js-yaml", root));
3407
3491
  let rows;
3408
3492
  if (configPath.endsWith(".json")) rows = JSON.parse(text);
3409
- else {
3410
- const include = loader.load(loader.resolve("@deepseek-ai/cordis-plugin-include", root));
3411
- rows = loader.load(loader.resolve("js-yaml", root)).load(text, { schema: include.entryListSchema });
3412
- }
3493
+ else rows = yaml.load(text, { schema: include.entryListSchema });
3413
3494
  const find = (entries, id) => {
3414
3495
  if (!Array.isArray(entries)) return void 0;
3415
3496
  for (const entry of entries) {
@@ -3420,21 +3501,10 @@ function bootPatches(loader, vfs, configPath, root) {
3420
3501
  };
3421
3502
  const configOf = (row) => typeof row.config === "object" && row.config !== null && !Array.isArray(row.config) ? row.config : {};
3422
3503
  const patches = [];
3423
- let presetOverlay = false;
3424
- const presets = find(rows, "agent-presets");
3425
- if (presets !== void 0 && configOf(presets).roots === void 0) {
3426
- presetOverlay = true;
3427
- patches.push({
3428
- id: "agent-presets",
3429
- config: {
3430
- ...configOf(presets),
3431
- roots: [{
3432
- path: join(root, "config/agent-presets"),
3433
- trust: "system"
3434
- }]
3435
- }
3436
- });
3437
- }
3504
+ for (const id of ["hmr", "plugin-manager"]) if (find(rows, id) !== void 0) patches.push({
3505
+ id,
3506
+ disabled: true
3507
+ });
3438
3508
  const jsonl = find(rows, "session-persistence-jsonl");
3439
3509
  if (jsonl !== void 0) patches.push({
3440
3510
  id: "session-persistence-jsonl",
@@ -3443,9 +3513,24 @@ function bootPatches(loader, vfs, configPath, root) {
3443
3513
  compression: "none"
3444
3514
  }
3445
3515
  });
3516
+ const dir = join(root, IMAGE_HOME_DIRECTORY, "profiles/preview");
3517
+ const profile = {
3518
+ name: "preview",
3519
+ dir,
3520
+ patchPath: join(dir, "cordis.patch.yml"),
3521
+ installAnchor: join(dir, "package.json"),
3522
+ cwd: root,
3523
+ home: join(root, IMAGE_HOME_DIRECTORY),
3524
+ startedBundles: [],
3525
+ overlays: patches,
3526
+ telemetryDisabledEnv: void 0
3527
+ };
3528
+ vfs.seed(join(dir, "package.json"), "{\"private\":true,\"dsh\":{\"profile\":{\"bundles\":[]}}}\n");
3529
+ vfs.seed(join(dir, "cordis.yml"), "[]\n");
3530
+ if (!vfs.existsSync(profile.patchPath)) vfs.seed(profile.patchPath, yaml.dump([{ insert: rows }], { schema: include.entryListSchema }));
3446
3531
  return {
3447
- patches,
3448
- presetOverlay
3532
+ patches: loader.load(loader.resolve("@deepseek-ai/dsh-app-boot", root)).readProfilePatches("dsh-webworker", profile),
3533
+ profile
3449
3534
  };
3450
3535
  }
3451
3536
  /**
@@ -48,9 +48,24 @@ export declare class WorkerTunnel {
48
48
  * @param endpoint - canonical Gateway Remote endpoint.
49
49
  * @param payload - decoded endpoint payload.
50
50
  * @param signal - logical-stream cancellation.
51
+ * @param uplink - the stream's uplink, posted as `stream-uplink-item` frames and closed with `stream-uplink-end`.
51
52
  * @returns decoded stream values from the worker Host.
52
53
  */
53
- open(endpoint: string, payload: unknown, signal: AbortSignal): AsyncGenerator;
54
+ open(endpoint: string, payload: unknown, signal: AbortSignal, uplink?: AsyncIterable<unknown>): AsyncGenerator;
55
+ /**
56
+ * Post the caller's uplink items for one logical stream. A failing uplink
57
+ * fails the downlink, and the enclosing `open` then aborts the worker side.
58
+ * `stop()` interrupts a pump blocked on `uplink.next()` and returns the
59
+ * caller's iterator at once, so a handle's queue closes and `send()` throws
60
+ * from then on; a generator blocked in `next()` completes that return only
61
+ * once it yields, so it is not awaited.
62
+ */
63
+ private pumpUplink;
64
+ /**
65
+ * Post items until the caller's iterator ends, the pump is stopped, or the page aborts the stream.
66
+ * @returns whether the caller's iterator was exhausted and the uplink end posted.
67
+ */
68
+ private forwardUplink;
54
69
  /**
55
70
  * Read the pre-cordis boot payload (the injection table).
56
71
  * @returns The payload the page applies before the client tree loads.
@@ -62,7 +77,8 @@ export declare class WorkerTunnel {
62
77
  * The image packs each bundle with a trailing `sourceURL` naming its image
63
78
  * path, so the blob shows under that name in the debugger instead of as an
64
79
  * anonymous blob entry.
65
- * @param url - Graph combo URL (`/plugins/??<id>/client.js&rev=...`).
80
+ * @param url - Graph combo reference (`plugins/??<id>/client.js&rev=...`), which
81
+ * this tunnel resolves against the page origin it maps from.
66
82
  */
67
83
  loadBundle(url: string): Promise<void>;
68
84
  private rejectOnAbort;
@@ -3,7 +3,7 @@
3
3
  * @module @deepseek-ai/dsh-experimental-webworker-runtime
4
4
  */
5
5
  export { createAlsRuntime, type AlsCausality, type AlsRuntime, type AlsSnapshot, type AlsToken, } from './polyfill/async-context/als-runtime.ts';
6
- export { parseInboundFrame, type TunnelAbortFrame, type TunnelInboundFrame, type TunnelOutboundFrame, type TunnelRequestFrame, type TunnelRequestId, type TunnelResponseChunkFrame, type TunnelResponseEndFrame, type TunnelResponseErrorFrame, type TunnelResponseFrame, type TunnelResponseHeadFrame, type TunnelStreamEndFrame, type TunnelStreamErrorFrame, type TunnelStreamItemFrame, type TunnelStreamOpenFrame, } from './transport/frames.ts';
6
+ export { parseInboundFrame, type TunnelAbortFrame, type TunnelInboundFrame, type TunnelOutboundFrame, type TunnelRequestFrame, type TunnelRequestId, type TunnelResponseChunkFrame, type TunnelResponseEndFrame, type TunnelResponseErrorFrame, type TunnelResponseFrame, type TunnelResponseHeadFrame, type TunnelStreamEndFrame, type TunnelStreamErrorFrame, type TunnelStreamUplinkEndFrame, type TunnelStreamUplinkItemFrame, type TunnelStreamItemFrame, type TunnelStreamOpenFrame, } from './transport/frames.ts';
7
7
  export { DEFAULT_CONDITIONS, requireActiveModuleLoader, setActiveModuleLoader, WorkerModuleLoader, type Resolution, type StaticModuleFactory, type WorkerModuleLoaderOptions, type WorkerRequire, } from './module-system/module-loader.ts';
8
8
  export * as posixPath from './module-system/posix-path.ts';
9
9
  export { MODULE_PROXIES, MODULE_PROXY_PREFIXES } from './module-proxies.ts';
@@ -0,0 +1,11 @@
1
+ /** Promise-based stream completion and pipelines from the shared readable-stream implementation. */
2
+ import { promises } from '../stream.ts';
3
+ /** Shared stream completion promise, including abort and listener cleanup options. */
4
+ export declare const finished: typeof import("stream/promises").finished;
5
+ /** Shared stream pipeline promise, including backpressure and error propagation. */
6
+ export declare const pipeline: typeof import("stream/promises").pipeline;
7
+ /** CommonJS interop marker consumed by the worker module loader. */
8
+ export declare const __esModule = true;
9
+ /** CommonJS-compatible promise namespace. */
10
+ export default promises;
11
+ //# sourceMappingURL=promises.d.ts.map
@@ -30,7 +30,7 @@ export declare function inspect(value: unknown): string;
30
30
  */
31
31
  export declare function format(template: unknown, ...args: unknown[]): string;
32
32
  /**
33
- * Structural deep equality, as `isDeepStrictEqual` defines it for plain data.
33
+ * Structural deep equality over own enumerable properties, as `isDeepStrictEqual` defines it for plain data.
34
34
  * @param left - first value.
35
35
  * @param right - second value.
36
36
  * @returns true when both sides are structurally identical.
@@ -9,7 +9,7 @@ export declare const DSH_ROOT = "/dsh";
9
9
  export declare const DSH_HOME = "/dsh/home";
10
10
  /** Flat, symlink-free package tree resolved by the worker module loader. */
11
11
  export declare const DSH_NODE_MODULES = "/dsh/node_modules";
12
- /** Directory holding the composed cordis.yml and the agent-preset tree. */
12
+ /** Directory holding the composed cordis.yml. */
13
13
  export declare const DSH_CONFIG = "/dsh/config";
14
14
  /** Default (empty) workspace directory. */
15
15
  export declare const DSH_WORKSPACE = "/dsh/workspace";
@@ -21,6 +21,17 @@ export interface TunnelStreamOpenFrame {
21
21
  readonly endpoint: string;
22
22
  readonly payload: unknown;
23
23
  }
24
+ /** One uplink item for an open Remote stream; `value` absent and `undefined` are equivalent. */
25
+ export interface TunnelStreamUplinkItemFrame {
26
+ readonly t: 'stream-uplink-item';
27
+ readonly id: TunnelRequestId;
28
+ readonly value?: unknown;
29
+ }
30
+ /** Page-side half-close of a Remote stream's uplink. */
31
+ export interface TunnelStreamUplinkEndFrame {
32
+ readonly t: 'stream-uplink-end';
33
+ readonly id: TunnelRequestId;
34
+ }
24
35
  /** Page-side cancellation of an in-flight request or stream. */
25
36
  export interface TunnelAbortFrame {
26
37
  readonly t: 'abort';
@@ -37,7 +48,7 @@ export interface TunnelInitFrame {
37
48
  readonly overlays: readonly string[];
38
49
  }
39
50
  /** Every frame the page sends the worker. */
40
- export type TunnelInboundFrame = TunnelInitFrame | TunnelRequestFrame | TunnelStreamOpenFrame | TunnelAbortFrame;
51
+ export type TunnelInboundFrame = TunnelInitFrame | TunnelRequestFrame | TunnelStreamOpenFrame | TunnelStreamUplinkItemFrame | TunnelStreamUplinkEndFrame | TunnelAbortFrame;
41
52
  /** Complete response for unary requests and static files. */
42
53
  export interface TunnelResponseFrame {
43
54
  readonly t: 'res';
@@ -47,8 +47,8 @@ export interface TunnelSeams {
47
47
  readonly directFetch: (request: Request) => Promise<Response>;
48
48
  /** Boot payload for `GET /__boot__`: the structured index injection table. */
49
49
  readonly bootPayload: () => unknown;
50
- /** Open one decoded Gateway Remote stream without another network carrier. */
51
- readonly openStream: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<AsyncIterable<unknown>>;
50
+ /** Open one decoded Gateway Remote stream without another network carrier; `uplink` carries the page's items. */
51
+ readonly openStream: (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, signal: AbortSignal) => Promise<AsyncIterable<unknown>>;
52
52
  /** Convert a Gateway stream failure to stable Client fields. */
53
53
  readonly streamFailure: (error: unknown) => {
54
54
  readonly code: string;
@@ -85,6 +85,8 @@ export declare class TunnelServer {
85
85
  private readonly unaryApiLane;
86
86
  private readonly queue;
87
87
  private readonly inFlight;
88
+ /** Uplinks of accepted `stream-open` frames, buffering items that arrive before or while the stream serves. */
89
+ private readonly uplinks;
88
90
  private seams;
89
91
  private failure;
90
92
  private listener;
@@ -109,6 +111,8 @@ export declare class TunnelServer {
109
111
  private refuse;
110
112
  private dispatchFrame;
111
113
  private serveStream;
114
+ /** The stream is over: settle any Host read still waiting on its uplink and stop buffering. */
115
+ private dropUplink;
112
116
  private sinkFor;
113
117
  /** The page sends an absolute URL; route handlers read `req.url` as a path. */
114
118
  private pathFrame;
@@ -17,8 +17,8 @@
17
17
  *
18
18
  * The tree itself boots through the host's own `boot()` glue loaded from the
19
19
  * image, so entry mounting, the activation audit, and its diagnostics are the
20
- * same code the Node deployment runs. Only the module seam and the command line
21
- * are supplied from here.
20
+ * same code the Node deployment runs. The Worker supplies module loading,
21
+ * profile locations, and the command line.
22
22
  * @module @deepseek-ai/dsh-experimental-webworker-runtime/src/worker-host
23
23
  */
24
24
  import { WorkerModuleLoader, type StaticModuleFactory } from './module-system/module-loader.ts';
package/lib/worker.js CHANGED
@@ -717,6 +717,15 @@ function parseInboundFrame(data) {
717
717
  t: "abort",
718
718
  id
719
719
  };
720
+ if (frame.t === "stream-uplink-end") return {
721
+ t: "stream-uplink-end",
722
+ id
723
+ };
724
+ if (frame.t === "stream-uplink-item") return {
725
+ t: "stream-uplink-item",
726
+ id,
727
+ value: frame.value
728
+ };
720
729
  if (frame.t === "stream-open") {
721
730
  if (typeof frame.endpoint !== "string" || frame.endpoint.length === 0) throw new Error(`webworker tunnel: stream ${String(id)} needs a non-empty endpoint`);
722
731
  return {
@@ -962,6 +971,61 @@ var BufferedSink = class {
962
971
  for (const call of this.calls.splice(0)) call();
963
972
  }
964
973
  };
974
+ /** Uplink items of one worker-local logical stream, read once by the Host method as its `uplink`. */
975
+ var TunnelUplink = class {
976
+ items = [];
977
+ ended = false;
978
+ closed = false;
979
+ wake;
980
+ push(value) {
981
+ if (this.ended || this.closed) return;
982
+ this.items.push(value);
983
+ this.signal();
984
+ }
985
+ /** Page half-close or stream end; buffered items still drain. */
986
+ end() {
987
+ this.ended = true;
988
+ this.signal();
989
+ }
990
+ [Symbol.asyncIterator]() {
991
+ return this;
992
+ }
993
+ async next() {
994
+ while (true) {
995
+ if (this.closed) return {
996
+ value: void 0,
997
+ done: true
998
+ };
999
+ if (this.items.length > 0) return {
1000
+ value: this.items.shift(),
1001
+ done: false
1002
+ };
1003
+ if (this.ended) return {
1004
+ value: void 0,
1005
+ done: true
1006
+ };
1007
+ if (this.wake !== void 0) throw new Error("webworker tunnel: stream uplink has one pending read");
1008
+ await new Promise((resolve) => {
1009
+ this.wake = resolve;
1010
+ });
1011
+ }
1012
+ }
1013
+ /** The Host stopped reading: buffered and later items are dropped. */
1014
+ return() {
1015
+ this.closed = true;
1016
+ this.items.length = 0;
1017
+ this.signal();
1018
+ return Promise.resolve({
1019
+ value: void 0,
1020
+ done: true
1021
+ });
1022
+ }
1023
+ signal() {
1024
+ const wake = this.wake;
1025
+ this.wake = void 0;
1026
+ wake?.();
1027
+ }
1028
+ };
965
1029
  /** One tunnel per worker; wire {@link TunnelServer.handleMessage} to `onmessage` first. */
966
1030
  var TunnelServer = class {
967
1031
  port;
@@ -970,6 +1034,8 @@ var TunnelServer = class {
970
1034
  unaryApiLane;
971
1035
  queue = [];
972
1036
  inFlight = /* @__PURE__ */ new Map();
1037
+ /** Uplinks of accepted `stream-open` frames, buffering items that arrive before or while the stream serves. */
1038
+ uplinks = /* @__PURE__ */ new Map();
973
1039
  seams;
974
1040
  failure;
975
1041
  listener;
@@ -986,9 +1052,18 @@ var TunnelServer = class {
986
1052
  handleMessage(data) {
987
1053
  const frame = parseInboundFrame(data);
988
1054
  if (frame.t === "init") throw new Error("webworker tunnel: duplicate init frame; the tunnel is already open");
1055
+ if (frame.t === "stream-uplink-item") {
1056
+ this.uplinks.get(frame.id)?.push(frame.value);
1057
+ return;
1058
+ }
1059
+ if (frame.t === "stream-uplink-end") {
1060
+ this.uplinks.get(frame.id)?.end();
1061
+ return;
1062
+ }
989
1063
  if (frame.t === "abort") {
990
1064
  this.inFlight.get(frame.id)?.abort();
991
1065
  this.inFlight.delete(frame.id);
1066
+ this.dropUplink(frame.id);
992
1067
  const queued = this.queue.findIndex((request) => request.id === frame.id);
993
1068
  if (queued !== -1) this.queue.splice(queued, 1);
994
1069
  return;
@@ -997,6 +1072,7 @@ var TunnelServer = class {
997
1072
  this.refuse(frame, this.failure);
998
1073
  return;
999
1074
  }
1075
+ if (frame.t === "stream-open") this.uplinks.set(frame.id, new TunnelUplink());
1000
1076
  if (this.seams === void 0) {
1001
1077
  this.queue.push(frame);
1002
1078
  return;
@@ -1027,6 +1103,7 @@ var TunnelServer = class {
1027
1103
  }
1028
1104
  refuse(frame, message) {
1029
1105
  if (frame.t === "stream-open") {
1106
+ this.dropUplink(frame.id);
1030
1107
  this.send({
1031
1108
  t: "stream-error",
1032
1109
  id: frame.id,
@@ -1057,12 +1134,13 @@ var TunnelServer = class {
1057
1134
  return;
1058
1135
  }
1059
1136
  const seams = this.seams;
1137
+ const uplink = this.uplinks.get(frame.id) ?? new TunnelUplink();
1060
1138
  const controller = new AbortController();
1061
1139
  this.inFlight.set(frame.id, { abort: () => {
1062
1140
  controller.abort();
1063
1141
  } });
1064
1142
  try {
1065
- const source = await seams.openStream(frame.endpoint, frame.payload, controller.signal);
1143
+ const source = await seams.openStream(frame.endpoint, frame.payload, uplink, controller.signal);
1066
1144
  for await (const value of source) {
1067
1145
  if (controller.signal.aborted) return;
1068
1146
  this.send({
@@ -1089,8 +1167,15 @@ var TunnelServer = class {
1089
1167
  }
1090
1168
  } finally {
1091
1169
  this.inFlight.delete(frame.id);
1170
+ this.dropUplink(frame.id);
1092
1171
  }
1093
1172
  }
1173
+ /** The stream is over: settle any Host read still waiting on its uplink and stop buffering. */
1174
+ dropUplink(id) {
1175
+ const uplink = this.uplinks.get(id);
1176
+ this.uplinks.delete(id);
1177
+ uplink?.end();
1178
+ }
1094
1179
  sinkFor(id) {
1095
1180
  const send = this.send.bind(this);
1096
1181
  const inFlight = this.inFlight;
@@ -2526,8 +2611,10 @@ function createWorkerHost(options) {
2526
2611
  const require = loader.requireFrom(dirname$1(configPath));
2527
2612
  const appBoot = require("@deepseek-ai/dsh-app-boot");
2528
2613
  const cmdline = require("@deepseek-ai/dsh-cmdline");
2529
- const { patches, presetOverlay } = bootPatches(loader, mounted, configPath, root);
2530
- const ctx = await appBoot.boot("dsh-webworker", configPath, patches, (hostCtx) => {
2614
+ const { patches, profile } = bootPatches(loader, mounted, configPath, root);
2615
+ const profileConfig = join$1(profile.dir, "cordis.yml");
2616
+ const ctx = await appBoot.boot("dsh-webworker", profileConfig, patches, (hostCtx) => {
2617
+ hostCtx.provide("profileContext", profile);
2531
2618
  hostCtx.loader.internal = loader.internal;
2532
2619
  installLogSink(hostCtx, require);
2533
2620
  cmdline.provideCmdline(hostCtx, {
@@ -2550,11 +2637,11 @@ function createWorkerHost(options) {
2550
2637
  if (typertGateway === void 0) throw new Error("webworker host: the tree activated without a typertGateway service");
2551
2638
  const handler = connection.createSharedFetchHandler("/api");
2552
2639
  const usage = loader.usage();
2553
- console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, preset root overlay=${presetOverlay ? "applied" : "already in roster"}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
2640
+ console.info(`webworker host: tree active (modules=${String(usage.modules)}, data overlays=${String(overlays.length)}, direct lane=connection.createSharedFetchHandler, als causality=${options.alsCausality === void 0 ? "inert" : "snapshot/restore"}, image lowering=${LOWERING_VERSION})`);
2554
2641
  tunnel.serve({
2555
2642
  directFetch: (request) => handler.fetch(request),
2556
2643
  bootPayload: () => readBootPayload(ctx),
2557
- openStream: typertGateway.wireStream.open,
2644
+ openStream: (endpoint, payload, uplink, signal) => typertGateway.wireStream.open(endpoint, payload, uplink, void 0, signal),
2558
2645
  streamFailure: typertGateway.wireStream.failure
2559
2646
  });
2560
2647
  } catch (reason) {
@@ -2626,30 +2713,22 @@ function requireLoweredImage(vfs, path) {
2626
2713
  if (lowered !== "dsh-worker-transform/1") throw new Error(`webworker host: image was lowered by ${String(lowered)}, this build runs ${LOWERING_VERSION}; rebuild the image`);
2627
2714
  }
2628
2715
  /**
2629
- * The shipped preset root, as the application layer that owns the composition
2630
- * supplies it.
2631
- *
2632
- * A launcher appends this root itself rather than writing it into the roster —
2633
- * `apps/cli` does it in `composeProfile` (`profile-boot.ts:159-166`) because only
2634
- * the application knows where its own presets sit. The worker's presets travel
2635
- * in the image, so the same overlay names their virtual path. Patching replaces
2636
- * a row's whole `config`, so the current one is read and spread, and a roster
2637
- * that already names roots keeps them.
2716
+ * Prepare an editable VFS profile from the packed rows and deployment overlays.
2717
+ * ConfigEditor writes overrides beside the insertion rows; reconciliation starts
2718
+ * at the empty profile root and retains the Worker deployment overlays.
2638
2719
  * @param loader - Module loader, for the image's YAML reader.
2639
2720
  * @param vfs - Filesystem holding the composed configuration.
2640
2721
  * @param configPath - Composed configuration path.
2641
2722
  * @param root - Virtual root.
2642
- * @returns Boot patches (preset root overlay, frontend serving off) and
2643
- * whether the preset overlay was applied.
2723
+ * @returns Profile locations and effective boot patches.
2644
2724
  */
2645
2725
  function bootPatches(loader, vfs, configPath, root) {
2646
2726
  const text = vfs.readFileSync(configPath, "utf8");
2727
+ const include = loader.load(loader.resolve("@deepseek-ai/cordis-plugin-include", root));
2728
+ const yaml = loader.load(loader.resolve("js-yaml", root));
2647
2729
  let rows;
2648
2730
  if (configPath.endsWith(".json")) rows = JSON.parse(text);
2649
- else {
2650
- const include = loader.load(loader.resolve("@deepseek-ai/cordis-plugin-include", root));
2651
- rows = loader.load(loader.resolve("js-yaml", root)).load(text, { schema: include.entryListSchema });
2652
- }
2731
+ else rows = yaml.load(text, { schema: include.entryListSchema });
2653
2732
  const find = (entries, id) => {
2654
2733
  if (!Array.isArray(entries)) return void 0;
2655
2734
  for (const entry of entries) {
@@ -2660,21 +2739,10 @@ function bootPatches(loader, vfs, configPath, root) {
2660
2739
  };
2661
2740
  const configOf = (row) => typeof row.config === "object" && row.config !== null && !Array.isArray(row.config) ? row.config : {};
2662
2741
  const patches = [];
2663
- let presetOverlay = false;
2664
- const presets = find(rows, "agent-presets");
2665
- if (presets !== void 0 && configOf(presets).roots === void 0) {
2666
- presetOverlay = true;
2667
- patches.push({
2668
- id: "agent-presets",
2669
- config: {
2670
- ...configOf(presets),
2671
- roots: [{
2672
- path: join$1(root, "config/agent-presets"),
2673
- trust: "system"
2674
- }]
2675
- }
2676
- });
2677
- }
2742
+ for (const id of ["hmr", "plugin-manager"]) if (find(rows, id) !== void 0) patches.push({
2743
+ id,
2744
+ disabled: true
2745
+ });
2678
2746
  const jsonl = find(rows, "session-persistence-jsonl");
2679
2747
  if (jsonl !== void 0) patches.push({
2680
2748
  id: "session-persistence-jsonl",
@@ -2683,9 +2751,24 @@ function bootPatches(loader, vfs, configPath, root) {
2683
2751
  compression: "none"
2684
2752
  }
2685
2753
  });
2754
+ const dir = join$1(root, IMAGE_HOME_DIRECTORY, "profiles/preview");
2755
+ const profile = {
2756
+ name: "preview",
2757
+ dir,
2758
+ patchPath: join$1(dir, "cordis.patch.yml"),
2759
+ installAnchor: join$1(dir, "package.json"),
2760
+ cwd: root,
2761
+ home: join$1(root, IMAGE_HOME_DIRECTORY),
2762
+ startedBundles: [],
2763
+ overlays: patches,
2764
+ telemetryDisabledEnv: void 0
2765
+ };
2766
+ vfs.seed(join$1(dir, "package.json"), "{\"private\":true,\"dsh\":{\"profile\":{\"bundles\":[]}}}\n");
2767
+ vfs.seed(join$1(dir, "cordis.yml"), "[]\n");
2768
+ if (!vfs.existsSync(profile.patchPath)) vfs.seed(profile.patchPath, yaml.dump([{ insert: rows }], { schema: include.entryListSchema }));
2686
2769
  return {
2687
- patches,
2688
- presetOverlay
2770
+ patches: loader.load(loader.resolve("@deepseek-ai/dsh-app-boot", root)).readProfilePatches("dsh-webworker", profile),
2771
+ profile
2689
2772
  };
2690
2773
  }
2691
2774
  /**
@@ -5782,15 +5865,15 @@ var crypto_default = {
5782
5865
  * reaching public-address preflight must fail loud instead of inventing an
5783
5866
  * address or bypassing the native HTTP provider's SSRF policy.
5784
5867
  */
5785
- var promises_exports$2 = /* @__PURE__ */ __exportAll({
5868
+ var promises_exports$3 = /* @__PURE__ */ __exportAll({
5786
5869
  __esModule: () => true,
5787
- default: () => promises_default$2,
5870
+ default: () => promises_default$3,
5788
5871
  lookup: () => lookup
5789
5872
  });
5790
5873
  /** DNS lookup (unavailable in the worker host). */
5791
5874
  const lookup = notImplementedFail("node:dns/promises", "lookup");
5792
5875
  /** CommonJS default export: the members `require()` hands a caller of this module. */
5793
- var promises_default$2 = { lookup };
5876
+ var promises_default$3 = { lookup };
5794
5877
  //#endregion
5795
5878
  //#region src/node/builtin_modules/implemented/events.ts
5796
5879
  var events_exports = /* @__PURE__ */ __exportAll({
@@ -11044,18 +11127,18 @@ var stream_exports = /* @__PURE__ */ __exportAll({
11044
11127
  compose: () => compose,
11045
11128
  default: () => streamDefault,
11046
11129
  destroy: () => destroy,
11047
- finished: () => finished,
11130
+ finished: () => finished$1,
11048
11131
  getDefaultHighWaterMark: () => getDefaultHighWaterMark,
11049
11132
  isDestroyed: () => isDestroyed,
11050
11133
  isDisturbed: () => isDisturbed,
11051
11134
  isErrored: () => isErrored,
11052
11135
  isReadable: () => isReadable,
11053
11136
  isWritable: () => isWritable,
11054
- pipeline: () => pipeline,
11137
+ pipeline: () => pipeline$1,
11055
11138
  promises: () => promises$1,
11056
11139
  setDefaultHighWaterMark: () => setDefaultHighWaterMark
11057
11140
  });
11058
- const { Duplex, PassThrough, Readable, Stream: StreamBase, Transform, Writable, addAbortSignal, compose, destroy, finished, isDisturbed, isErrored, isReadable, pipeline, promises: promises$1 } = (/* @__PURE__ */ __toESM(require_browser(), 1)).default;
11141
+ const { Duplex, PassThrough, Readable, Stream: StreamBase, Transform, Writable, addAbortSignal, compose, destroy, finished: finished$1, isDisturbed, isErrored, isReadable, pipeline: pipeline$1, promises: promises$1 } = (/* @__PURE__ */ __toESM(require_browser(), 1)).default;
11059
11142
  const { getDefaultHighWaterMark, isDestroyed, isWritable, setDefaultHighWaterMark } = StreamBase;
11060
11143
  if (getDefaultHighWaterMark(false) !== 64 * 1024) setDefaultHighWaterMark(false, 64 * 1024);
11061
11144
  /**
@@ -12609,7 +12692,7 @@ var fs_default = {
12609
12692
  * member set is checked against Node where it is built, on `promises` in
12610
12693
  * `../fs.ts`.
12611
12694
  */
12612
- var promises_exports$1 = /* @__PURE__ */ __exportAll({
12695
+ var promises_exports$2 = /* @__PURE__ */ __exportAll({
12613
12696
  Dirent: () => Dirent,
12614
12697
  __esModule: () => true,
12615
12698
  access: () => access,
@@ -12617,7 +12700,7 @@ var promises_exports$1 = /* @__PURE__ */ __exportAll({
12617
12700
  chmod: () => chmod,
12618
12701
  constants: () => constants$2,
12619
12702
  cp: () => cp$1,
12620
- default: () => promises_default$1,
12703
+ default: () => promises_default$2,
12621
12704
  link: () => link,
12622
12705
  lstat: () => lstat,
12623
12706
  mkdir: () => mkdir$1,
@@ -12637,7 +12720,7 @@ var promises_exports$1 = /* @__PURE__ */ __exportAll({
12637
12720
  });
12638
12721
  /** The promise members of the VFS bridge, as `node:fs/promises` names them. */
12639
12722
  const { readFile, writeFile, appendFile, mkdir: mkdir$1, mkdtemp, readdir, stat: stat$1, lstat, realpath, rm: rm$1, unlink, rename, access, chmod, cp: cp$1, link, open: open$1, opendir, truncate, watch, constants: constants$2 } = promises;
12640
- var promises_default$1 = promises;
12723
+ var promises_default$2 = promises;
12641
12724
  //#endregion
12642
12725
  //#region src/node/builtin_modules/implemented/http.ts
12643
12726
  var http_exports = /* @__PURE__ */ __exportAll({
@@ -13035,6 +13118,21 @@ var perf_hooks_default = {
13035
13118
  PerformanceObserver
13036
13119
  };
13037
13120
  //#endregion
13121
+ //#region src/node/builtin_modules/implemented/stream/promises.ts
13122
+ /** Promise-based stream completion and pipelines from the shared readable-stream implementation. */
13123
+ var promises_exports$1 = /* @__PURE__ */ __exportAll({
13124
+ __esModule: () => true,
13125
+ default: () => promises_default$1,
13126
+ finished: () => finished,
13127
+ pipeline: () => pipeline
13128
+ });
13129
+ /** Shared stream completion promise, including abort and listener cleanup options. */
13130
+ const finished = promises$1.finished;
13131
+ /** Shared stream pipeline promise, including backpressure and error propagation. */
13132
+ const pipeline = promises$1.pipeline;
13133
+ /** CommonJS-compatible promise namespace. */
13134
+ var promises_default$1 = promises$1;
13135
+ //#endregion
13038
13136
  //#region src/node/builtin_modules/implemented/timers/promises.ts
13039
13137
  var promises_exports = /* @__PURE__ */ __exportAll({
13040
13138
  __esModule: () => true,
@@ -13255,7 +13353,7 @@ function format(template, ...args) {
13255
13353
  return rest.length === 0 ? substituted : `${substituted} ${rest.map((value) => inspect(value)).join(" ")}`;
13256
13354
  }
13257
13355
  /**
13258
- * Structural deep equality, as `isDeepStrictEqual` defines it for plain data.
13356
+ * Structural deep equality over own enumerable properties, as `isDeepStrictEqual` defines it for plain data.
13259
13357
  * @param left - first value.
13260
13358
  * @param right - second value.
13261
13359
  * @returns true when both sides are structurally identical.
@@ -13267,7 +13365,7 @@ function isDeepStrictEqual(left, right) {
13267
13365
  const leftKeys = Object.keys(left);
13268
13366
  const rightKeys = Object.keys(right);
13269
13367
  if (leftKeys.length !== rightKeys.length) return false;
13270
- return leftKeys.every((key) => key in right && isDeepStrictEqual(left[key], right[key]));
13368
+ return leftKeys.every((key) => Object.hasOwn(right, key) && isDeepStrictEqual(left[key], right[key]));
13271
13369
  }
13272
13370
  /** Runtime type predicates (`node:util/types`), checked against the Node module of that name. */
13273
13371
  const types$1 = {
@@ -21704,10 +21802,10 @@ const BUILTINS = {
21704
21802
  buffer: () => buffer_exports,
21705
21803
  child_process: () => child_process_exports,
21706
21804
  crypto: () => crypto_exports,
21707
- "dns/promises": () => promises_exports$2,
21805
+ "dns/promises": () => promises_exports$3,
21708
21806
  events: () => events_exports,
21709
21807
  fs: () => fs_exports,
21710
- "fs/promises": () => promises_exports$1,
21808
+ "fs/promises": () => promises_exports$2,
21711
21809
  http: () => http_exports,
21712
21810
  module: () => module_exports,
21713
21811
  net: () => net_exports,
@@ -21717,6 +21815,7 @@ const BUILTINS = {
21717
21815
  perf_hooks: () => perf_hooks_exports,
21718
21816
  sqlite: () => sqlite_exports,
21719
21817
  stream: () => stream_exports,
21818
+ "stream/promises": () => promises_exports$1,
21720
21819
  "timers/promises": () => promises_exports,
21721
21820
  tty: () => tty_exports,
21722
21821
  url: () => url_exports,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-experimental-webworker-runtime",
3
3
  "description": "Browser-only harness runtime: in-memory VFS, module transform and loader, postMessage tunnel, and the dedicated Web Worker assembly, with the Node-compatibility layer that lets the host tree run unchanged",
4
- "version": "0.1.6-alpha.2",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -34,36 +34,37 @@
34
34
  "buffer": "^6.0.3",
35
35
  "picomatch": "^4.0.4",
36
36
  "readable-stream": "^4.7.0",
37
- "@deepseek-ai/dsh-client-web": "^0.1.6-alpha.2",
38
- "@deepseek-ai/dsh-util-crypto": "^0.1.6-alpha.2"
37
+ "@deepseek-ai/dsh-client-web": "^0.1.7-alpha.1",
38
+ "@deepseek-ai/dsh-util-crypto": "^0.1.7-alpha.1"
39
39
  },
40
40
  "peerDependencies": {
41
- "@deepseek-ai/cordis": "^4.0.2",
42
- "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
43
- "@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2",
44
- "@deepseek-ai/dsh-client-modules": "^0.1.6-alpha.2",
45
- "@deepseek-ai/dsh-host-webserver": "^0.1.6-alpha.2"
41
+ "@deepseek-ai/cordis": "^4.0.3",
42
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.4",
43
+ "@deepseek-ai/dsh-client-connection": "^0.1.7-alpha.1",
44
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.1",
45
+ "@deepseek-ai/dsh-client-modules": "^0.1.7-alpha.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/picomatch": "^3.0.2",
49
49
  "@types/readable-stream": "^4.0.24",
50
50
  "chokidar": "^5.0.0",
51
- "@deepseek-ai/cordis": "^4.0.2",
52
- "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
53
- "@deepseek-ai/dsh-api-gateway": "^0.1.6-alpha.2",
54
- "@deepseek-ai/dsh-bash-sandbox": "^0.1.6-alpha.2",
55
- "@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2",
56
- "@deepseek-ai/dsh-client-file-upload": "^0.1.6-alpha.2",
57
- "@deepseek-ai/dsh-client-modules": "^0.1.6-alpha.2",
58
- "@deepseek-ai/dsh-host-webserver": "^0.1.6-alpha.2",
59
- "@deepseek-ai/dsh-sandbox-local": "^0.1.6-alpha.2",
60
- "@deepseek-ai/dsh-sandbox-policy": "^0.1.6-alpha.2",
61
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
62
- "@deepseek-ai/dsh-session-projection": "^0.1.6-alpha.2",
63
- "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.6-alpha.2",
64
- "@deepseek-ai/dsh-subagent": "^0.1.6-alpha.2",
51
+ "@deepseek-ai/cordis": "^4.0.3",
52
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.4",
53
+ "@deepseek-ai/dsh-api-gateway": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/dsh-app-boot": "^0.1.7-alpha.1",
55
+ "@deepseek-ai/dsh-bash-sandbox": "^0.1.7-alpha.1",
56
+ "@deepseek-ai/dsh-client-file-upload": "^0.1.7-alpha.1",
57
+ "@deepseek-ai/dsh-client-connection": "^0.1.7-alpha.1",
58
+ "@deepseek-ai/dsh-client-modules": "^0.1.7-alpha.1",
59
+ "@deepseek-ai/dsh-sandbox-local": "^0.1.7-alpha.1",
60
+ "@deepseek-ai/dsh-sandbox-policy": "^0.1.7-alpha.1",
61
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
62
+ "@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1",
63
+ "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.7-alpha.1",
64
+ "@deepseek-ai/dsh-subagent": "^0.1.7-alpha.1",
65
+ "@deepseek-ai/dsh-subprocess-local": "^0.1.7-alpha.1",
65
66
  "@deepseek-ai/node-addon-system": "^0.1.2",
66
- "@deepseek-ai/dsh-subprocess-local": "^0.1.6-alpha.2"
67
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.1"
67
68
  },
68
69
  "files": [
69
70
  "lib/index.js",