diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml index 18ab194298..9c8b44c4b9 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml @@ -2,18 +2,18 @@ # section, a hash of its English and Chinese blocks outside code blocks and generated regions. # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-06-node-engine-floor.md -/agent-note-raise-the-node-lts-engine-floor-to-22-19: - en: 01f43f2b4c6bd4fa - zh: 7f34e5b3c1c55419 -/agent-note-raise-the-node-lts-engine-floor-to-22-19/problem: - en: d7bf1b1699ce0dc8 - zh: f10dfd0e4787bb44 -/agent-note-raise-the-node-lts-engine-floor-to-22-19/decision: - en: 0051c0d8c1948f63 - zh: cbab7aa0f53f7200 -/agent-note-raise-the-node-lts-engine-floor-to-22-19/consequences: - en: 0b9fbe932d9a8b1f - zh: 642e19d06caecef2 -/agent-note-raise-the-node-lts-engine-floor-to-22-19/alternatives-considered: - en: c433389b2064eaf9 - zh: 57de2af812def1c4 +/agent-note-node-runtime-floor-and-compatibility-coverage: + en: 233253ec88be6880 + zh: 5e1f0db7eea06d67 +/agent-note-node-runtime-floor-and-compatibility-coverage/problem: + en: 1e45546d7452029f + zh: d99966ac42bb193f +/agent-note-node-runtime-floor-and-compatibility-coverage/decision: + en: f7eea7a3e6be6d22 + zh: 976494745decb87b +/agent-note-node-runtime-floor-and-compatibility-coverage/consequences: + en: 224ce4b13f297f8b + zh: 1bb5f8ebdf3243b3 +/agent-note-node-runtime-floor-and-compatibility-coverage/alternatives-considered: + en: f03afb2aaedfba62 + zh: 19ab9a0cd9ec3a5e diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md index e2fe7661f4..45f5335a15 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md @@ -1,4 +1,4 @@ -# Agent Note: Raise the Node LTS engine floor to 22.19 +# Agent Note: Node runtime floor and compatibility coverage Status: implemented @@ -6,34 +6,27 @@ English | [中文](2026-07-06-node-engine-floor.zh.md) ## Problem -The Node 22 branch of the root `engines.node` range is a contract for the installed workspace, not only for the runtime APIs the harness source calls directly. It must be no lower than package `engines.node` declarations for dependencies the workspace installs on that branch; otherwise `pnpm install --engine-strict` fails at an advertised LTS version, and non-strict installs run outside a dependency's supported runtime. +The root `engines.node` range promises that the installed workspace supports each admitted runtime. Its minimum must cover both the runtime APIs DSH uses and the declared requirements of installed dependencies. Otherwise strict installation can reject a supported version before DSH starts. ## Decision -Set `engines.node` to `^22.19.0 || >=24.0.0` and test keyless CI on `['22.19', 24, 26]`. The primary Node 24 jobs own the complete typecheck and unit coverage inventory; every version runs focused source-worker, Zstandard, source-launch, and [jsdom storage](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) smokes without repeating that inventory. The real-API e2e workflow stays on Node 24 because it exercises API integration rather than the runtime floor. +Set `engines.node` to `>=24.0.0`. Node 24 is the minimum for DSH and its development carriers, including dependencies that require Node 24. Migration instructions live in the [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.md). -Two Node features gate the source runtime: +The primary Node 24 jobs own complete typechecking and unit coverage. Focused compatibility jobs run on pinned Node 24.0 and Node 26. Pinning 24.0 exercises the advertised minimum and its v1 private module loader; the primary jobs track the latest Node 24 and exercise its v2 loader. A floating `24` compatibility job would duplicate the primary loader coverage and stop testing the minimum. -- **`node:sqlite`** — `packages/storage/storage-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`, and the optional Session-query provider loads it on first search. The module dropped its `--experimental-sqlite` flag requirement at **22.13** (LTS) and **23.4** (Current); before those, importing it throws at load. -- **Native TypeScript type-stripping** — the built-mode `apps/cli/tests/profiles/headless/tests/keyless-smoke.e2e.ts` smoke boots the test-support `.ts` driver under plain `node` (no tsx) and loads the `.ts` test adapter (`cli-mock-llm.ts`). Type-stripping is the default from **22.18** (LTS) and **23.6** (Current); before those it needs `--experimental-strip-types`. +The Node 24.0 compatibility job also builds artifacts and checks that the shipped Web CLI starts and disposes with full-text Session search disabled. Source-worker, Zstandard, source-launch, profile-resolution, and jsdom-storage checks run on both compatibility versions. Real-API e2e jobs stay on Node 24 because they validate provider integration. -Those source features clear on the 22.x line at **22.18**, but the installed Pi adapter dependency raises the advertised LTS floor. `@deepseek-ai/dsh-llm-pi-ai` depends on `@earendil-works/pi-ai@0.79.3`, whose package declares `engines.node >=22.19.0`, so the LTS floor is **22.19**. The 24.x branch remains `>=24.0.0`. The disjoint range excludes Node 23 entirely: Node 23.0–23.5 still has at least one flagged source feature, and the 23 line is non-LTS/EOL, so advertising `>=23.6` would add a dead release line and a CI leg no deployment should use. - -`@types/node` remains pinned to the 22.x line (`^22.20.0`) to match the LTS support line: reaching for a Node 23+/24+/25+ API fails `tsc` on every machine and in the typecheck gate, rather than compiling clean and surviving to a runtime failure only a floor matrix leg could catch. The whole tree typechecks clean against the Node 22 type API, so the pin costs nothing. +The root, Web, and desktop projects retain `@types/node` on the 22.x line. Their current code needs no Node 24-only declarations. A narrower type API remains valid on the supported runtime and avoids unrelated declaration changes. Raise these declarations when a supported API requires them, and keep their major no newer than the runtime floor. The independently published native addon has its own runtime and type support policy. ## Consequences -- The advertised LTS branch no longer undercuts the Pi adapter dependency floor. -- CI proves the Node 22 LTS floor directly with Node 22.19, keeps primary coverage on `node: 24`, and exercises Node 26 as the next even line; focused compatibility smokes run on all three versions. -- The built-mode smoke needs no version-conditional flag: at 22.19 type-stripping is already the default, so the example-owned TypeScript driver stays a plain `node fixture.ts` path. -- A future dependency or source API that raises the runtime floor must move `engines.node`, the compatibility matrix, and this Agent Note in the same change. +- Node 22 installations must upgrade before running DSH. Packaged applications with an embedded runtime do not require a system Node upgrade. +- Supported runtimes provide `node:sqlite` and native TypeScript stripping without opt-in flags. The source CLI still uses its declared `tsx/esm` launcher. +- The compatibility graph retains both private loader interfaces used by supported Node versions. +- A future runtime-floor change updates `engines.node`, compatibility jobs, current documentation, this note, and an upgrade guide together. ## Alternatives considered -- **Keep `^22.18.0 || >=24.0.0`.** Rejected: it advertises an LTS version lower than the Pi adapter dependency floor. `@earendil-works/pi-ai@0.79.3` requires `>=22.19.0`. -- **Downgrade or pin `@earendil-works/pi-ai` to preserve the 22.18 advertised range.** Rejected: the current Pi adapter dependency is part of the intended workspace, and 22.19 is still inside the Node 22 LTS line. -- **Floor `>=22.13` (the `node:sqlite` boundary) plus `--experimental-strip-types` in the built-bin smoke on 22.13–22.17.** Rejected: it adds a version-conditional test flag for one narrow range and dresses up an experimental-flag dependency as first-class support. The Pi adapter dependency already requires a higher LTS floor. -- **Open-ended `>=22.19`.** Rejected: it advertises support for Node 23.0–23.5, where `node:sqlite` (until 23.4) or type-stripping (until 23.6) is still flagged. -- **Include Node 23.6+ (`^22.19.0 || >=23.6.0`).** Rejected: 23.6+ does run both source features unflagged, but Node 23 is end-of-life; advertising a dead release line adds a range term and a CI leg for a runtime no deployment should use. -- **Matrix `[22, 24, 26]` instead of pinning `22.19`.** Rejected: floating major-version entries drift upward over time and silently stop exercising the declared LTS floor. -- **Keep `@types/node` ahead of the floor (`^25`).** Rejected: types ahead of the runtime floor let a Node 24/25-only API compile clean and fail only at runtime on 22.x. Pinning `@types/node` to the 22.x line turns that into a compile error everywhere. +- **Keep Node 22 support.** It prevents dependencies that declare Node 24 as their minimum from joining the supported installation. +- **Test only the latest Node 24.** It does not exercise the minimum or the older private loader interface still used within the supported 24.x line. +- **Raise Node declarations ahead of the minimum.** It allows code to compile against APIs that a supported runtime can lack. diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md index e7d41e8128..1d5b8e4bbf 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md @@ -1,4 +1,4 @@ -# Agent Note: 将 Node LTS 引擎下限提升至 22.19 +# Agent Note: Node 运行时下限与兼容性覆盖 Status: implemented @@ -6,34 +6,27 @@ Status: implemented ## 问题 -根 `engines.node` 范围中的 Node 22 分支是对安装后工作区的约定,而不仅仅是 harness 源码直接调用的运行时 API 的约定。它不得低于工作区在该分支上安装的依赖包所声明的 `engines.node`;否则 `pnpm install --engine-strict` 会在一个已宣传的 LTS 版本上失败,而非严格模式下的安装结果则会在依赖所支持的运行时范围之外运行。 +根 `engines.node` 范围承诺安装后的工作区支持其允许的每一个运行时。其下限必须覆盖 DSH 使用的运行时 API 和所安装依赖声明的要求。否则,严格安装可能会在 DSH 启动前拒绝一个受支持的版本。 ## 决策 -将 `engines.node` 设为 `^22.19.0 || >=24.0.0`,并在 `['22.19', 24, 26]` 上运行 keyless CI。主要的 Node 24 任务负责整套类型检查和单元测试覆盖率任务;三个版本均运行 source-worker、Zstandard、source-launch 和 [jsdom 存储](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) 专项冒烟测试,不重复这套类型检查和覆盖率任务。真实 API 的 e2e 工作流保持在 Node 24 上,因为它验证的是 API 集成而非运行时下限。 +将 `engines.node` 设为 `>=24.0.0`。Node 24 是 DSH 及其开发载体的下限,也允许使用要求 Node 24 的依赖。迁移说明见[升级指南](../../../../docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.zh.md)。 -两个 Node 特性决定了源码运行时的门槛: +主要的 Node 24 任务负责完整的类型检查和单元测试覆盖率。聚焦的兼容性任务在固定的 Node 24.0 和 Node 26 上运行。固定 24.0 能验证宣传的下限及其 v1 私有模块加载器;主要任务跟随最新的 Node 24,验证其 v2 加载器。浮动的 `24` 兼容性任务会重复主要任务的加载器覆盖,并停止验证下限。 -- **`node:sqlite`**:`packages/storage/storage-sqlite` 在顶层执行 `import { DatabaseSync } from 'node:sqlite'`,可选 Session-query provider 则在首次搜索时加载它。该模块在 **22.13**(LTS)和 **23.4**(Current)取消了 `--experimental-sqlite` 标志要求;在此之前,导入它会在加载时抛出异常。 -- **原生 TypeScript 类型剥离**——构建模式的 `apps/cli/tests/profiles/headless/tests/keyless-smoke.e2e.ts` 冒烟测试使用纯 `node`(无 tsx)启动 test-support 的 `.ts` driver,并加载 `.ts` 测试适配器(`cli-mock-llm.ts`)。类型剥离从 **22.18**(LTS)和 **23.6**(Current)起成为默认行为;更早版本需要 `--experimental-strip-types`。 +Node 24.0 兼容性任务还构建产物,并检查随产品提供的 Web CLI 能在禁用全文 Session 搜索时启动和释放资源。source-worker、Zstandard、source-launch、profile-resolution 和 jsdom-storage 检查在两个兼容性版本上运行。真实 API 的 e2e 任务保留在 Node 24 上,因为它们验证的是提供方集成。 -这些源码特性在 22.x 线上于 **22.18** 全部就绪,但已安装的 Pi 适配器依赖将宣传的 LTS 下限进一步提高。`@deepseek-ai/dsh-llm-pi-ai` 依赖 `@earendil-works/pi-ai@0.79.3`,后者的包声明 `engines.node >=22.19.0`,因此 LTS 下限为 **22.19**。24.x 分支保持 `>=24.0.0`。该不相交范围完全排除了 Node 23:Node 23.0–23.5 至少还有一个源码特性需要标志,而 23 线是非 LTS/已 EOL 的,宣传 `>=23.6` 会增加一条已终止的发布线和一条 CI 分支,而没有任何部署应当使用它。 - -`@types/node` 继续固定在 22.x 线(`^22.20.0`),以匹配 LTS 支持线:使用 Node 23+/24+/25+ 的 API 会在所有机器和类型检查门禁中导致 `tsc` 失败,而不是先编译通过,直到下限矩阵分支运行时才暴露错误。整个代码树针对 Node 22 类型 API 的类型检查全部通过,因此固定该版本不产生任何代价。 +根项目、Web 和桌面项目将 `@types/node` 保留在 22.x 线。它们当前的代码不需要 Node 24 专属声明。较窄的类型 API 仍适用于受支持的运行时,也避免无关的声明变更。当受支持的 API 需要时再提升这些声明,并确保其主版本不高于运行时下限。独立发布的原生 addon 有自己的运行时和类型支持策略。 ## 后果 -- 宣传的 LTS 分支不再低于 Pi 适配器依赖的下限。 -- CI 通过 Node 22.19 直接验证 Node 22 LTS 下限,将主要覆盖率任务保留在 `node: 24`,并用 Node 26 验证下一个偶数线;三个版本均运行聚焦的兼容性冒烟测试。 -- 构建模式冒烟测试无需版本条件标志:在 22.19 上类型剥离已是默认行为,因此示例自有的 TypeScript driver 保持使用纯 `node fixture.ts` 路径。 -- 未来若依赖或源码 API 提高运行时下限,必须在同一变更中同步调整 `engines.node`、兼容性矩阵和本 Agent Note。 +- 使用 Node 22 的安装必须先升级,才能运行 DSH。内嵌运行时的打包应用不要求升级系统 Node。 +- 受支持的运行时无需启用标志即可使用 `node:sqlite` 和原生 TypeScript 类型剥离。源码 CLI 仍使用声明的 `tsx/esm` 启动器。 +- 兼容性任务图保留受支持 Node 版本使用的两种私有加载器接口。 +- 后续提升运行时下限时,必须一起更新 `engines.node`、兼容性任务、当前文档、本文和升级指南。 -## 曾考虑的替代方案 +## 考虑过的替代方案 -- **保持 `^22.18.0 || >=24.0.0`。** 否决:它宣传的 LTS 版本低于 Pi 适配器依赖的下限。`@earendil-works/pi-ai@0.79.3` 要求 `>=22.19.0`。 -- **降级或固定 `@earendil-works/pi-ai` 以保留 22.18 的宣传范围。** 否决:当前 Pi 适配器依赖是预期工作区的一部分,且 22.19 仍在 Node 22 LTS 线内。 -- **下限 `>=22.13`(`node:sqlite` 边界)加上在 22.13–22.17 的 built-bin 冒烟测试中使用 `--experimental-strip-types`。** 否决:它为一个狭窄范围增加了版本条件测试标志,并将实验性标志依赖包装为正式支持。Pi 适配器依赖已经要求更高的 LTS 下限。 -- **使用无上限的 `>=22.19`。** 否决:它宣传支持 Node 23.0–23.5,而在这些版本上 `node:sqlite`(直到 23.4)或类型剥离(直到 23.6)仍需标志。 -- **包含 Node 23.6+(`^22.19.0 || >=23.6.0`)。** 否决:23.6+ 确实能无标志运行两个源码特性,但 Node 23 已结束生命周期(EOL);宣传一条已终止的发布线会增加一个范围项和一条 CI 分支,而没有任何部署应当使用该运行时。 -- **矩阵 `[22, 24, 26]` 而非固定 `22.19`。** 否决:浮动的主版本号条目会随时间上漂,悄然不再验证所声明的 LTS 下限。 -- **保持 `@types/node` 超前于运行时下限(`^25`)。** 否决:类型定义超前于运行时下限会让仅 Node 24/25 才有的 API 编译通过,仅在 22.x 上运行时才失败。将 `@types/node` 固定在 22.x 线上可将此类问题转化为所有环境下的编译错误。 +- **保留 Node 22 支持。** 这会阻止将 Node 24 声明为下限的依赖加入受支持的安装。 +- **只验证最新的 Node 24。** 这既不能验证下限,也不能验证受支持 24.x 系列仍使用的旧私有加载器接口。 +- **将 Node 声明提升至下限之上。** 这会允许代码使用受支持运行时可能缺少的 API 编译通过。 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93c3d5293c..818b4fe32e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -415,15 +415,10 @@ jobs: max-parallel: 1 matrix: include: - - node: '22.19' - name: node 22.19 - runner: ubuntu-latest - gate_concurrency: '1' - # Pinned inside 24.0-24.11.1: those releases carry the v1 internal - # loader while reporting major 24, and every other job tracks the - # latest 24, which is v2. A bare `24` here would retest that same v2. - - node: '24.9' - name: node 24.9 + # Pin the supported floor, whose internal loader is v1. Primary + # jobs track latest 24 and exercise the v2 loader. + - node: '24.0' + name: node 24.0 runner: ubuntu-latest gate_concurrency: '1' - node: 26 diff --git a/AGENTS.md b/AGENTS.md index 7bae0a79c1..ecddbd9209 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,7 +81,7 @@ Package groups: [packages/README.md](packages/README.md). [Experimental status]( ## Commands ```sh -pnpm install # pnpm workspaces, node ^22.19 || >=24 +pnpm install # pnpm workspaces, node >=24 pnpm run clean # remove build outputs and safe residue from deleted packages pnpm run test # unit tests pnpm run test:coverage # CI coverage gate: per-file 100% on packages/*/*/src @@ -129,7 +129,7 @@ Real-API tests/demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and ## Conventions - Packages use `@deepseek-ai/dsh-`; vendor is [rescoped](docs/rescope.md) and `private: true`. Harness packages declare `@deepseek-ai/cordis` in `peerDependencies`/`devDependencies`. Workspace dependency sections use DSH `workspace:*`, vendor/native `workspace:~` ([rules](.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md)). -- ESM everywhere (`"type": "module"`). Use package names across packages and `.ts` in local relative imports. Config subprocesses run built `lib/` under plain Node; source regressions use their declared launcher ([testing policy](docs/testing.md#test-subprocess-launch-modes)). The `dsh` CLI source launch runs through tsx's ESM-only hook (`node --import tsx/esm`); modules it reaches must stay ESM (no CJS-only exports) — Node's native TypeScript modes are unavailable across the engines range ([source-launch contract](.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.md)). Raw/Web `cordis.yml` bare plugins must appear in their resolver manifest's `dependencies`; `verify-cordis-config` enforces it. +- ESM everywhere (`"type": "module"`). Use package names across packages and `.ts` in local relative imports. Config subprocesses run built `lib/` under plain Node; source regressions use their declared launcher ([testing policy](docs/testing.md#test-subprocess-launch-modes)). The `dsh` CLI source launch runs through tsx's ESM-only hook (`node --import tsx/esm`); modules it reaches must stay ESM (no CJS-only exports) ([source-launch contract](.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.md)). Raw/Web `cordis.yml` bare plugins must appear in their resolver manifest's `dependencies`; `verify-cordis-config` enforces it. - **Registrations are effects**: every contribution goes through `ctx.effect()` / `ctx.on()`; a registry's `register()` returns the disposer. - **Typed events use declaration merging** and merge-extensible maps. Event JSDoc needs `@mode` and payload `@param`. Public service methods document parameters and non-void returns. `SessionEventMap` members are required-on-read unless the envelope carries `ignorable: true`. Bump `SESSION_FORMAT_VERSION` only when existing discriminators cannot prevent unsafe interpretation; same-version changes require safe reader behavior ([review](.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md)). - **Switch on discriminant tags.** Closed unions end in `assertNever`; merge-extensible unions fall through a documented default. diff --git a/apps/cli/tests/lazy-search-startup.compat.spec.ts b/apps/cli/tests/lazy-search-startup.compat.spec.ts index d2172ebe57..dfb92dc340 100644 --- a/apps/cli/tests/lazy-search-startup.compat.spec.ts +++ b/apps/cli/tests/lazy-search-startup.compat.spec.ts @@ -1,5 +1,5 @@ /** - * Node 22 startup-output smoke for the shipped Web CLI composition. + * Node 24 startup-output smoke for the shipped Web CLI composition. * * Only the dedicated Node compatibility gate opts this test in after building * both artifacts; ordinary Vitest inventory deterministically skips it. diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml index 9b1fb496ad..ed7b8b7893 100644 --- a/docs/development.i18n.yaml +++ b/docs/development.i18n.yaml @@ -9,8 +9,8 @@ en: 5207d17725c07a27 zh: 258884c4300ac63a /development-guide/setup-tutorial/prerequisites: - en: ae47d95cd48bbb1d - zh: 2378e6ed3305e28d + en: 8b66d0af052e353e + zh: 3864c56cf31ea53d /development-guide/setup-tutorial/windows-and-wsl-2: en: e86be579be6f1813 zh: 14f6a220337d0861 @@ -27,8 +27,8 @@ en: dba2c72ee6ffa930 zh: b65c27fbb9129ec8 /development-guide/contributor-reference/git-integrations: - en: b2767984ba4ad45c - zh: 781307679ed186b2 + en: 2b5d506c14cf4b14 + zh: a1ee5d27996fefa4 /development-guide/contributor-reference/ci-gates: en: b64a914e41b1d285 zh: 6c031b507c3c0e84 diff --git a/docs/development.md b/docs/development.md index da75ae45e5..d3eabb4dec 100644 --- a/docs/development.md +++ b/docs/development.md @@ -8,7 +8,7 @@ The setup tutorial takes a new contributor from prerequisites to a checked check ### Prerequisites -- Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md). +- Node.js requires 24.0 or newer. CI covers 24.0, the latest 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md). - Node.js TypeScript type stripping enabled. The repository build scripts load `tsdown.config.ts` with tsdown's native config loader, so they fail when `--no-experimental-strip-types` is in `NODE_OPTIONS` or the Node.js build lacks TypeScript support; `pnpm run build` checks this first and names the cause. - Corepack-enabled pnpm. The repo pins `pnpm@11.28.5` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack. - Git 2.26 or newer; hook setup enables Git's worktree-specific configuration extension. @@ -144,7 +144,7 @@ The vendor manifest guard checks that changes under `vendor/*/src` are staged wi The pi-ai check regenerates expected metadata in memory. It rejects stale data without rewriting or staging files. After a pi-ai dependency or generator change, follow the package's [provider metadata update steps](../packages/llm/llm-pi-ai/README.md#generated-provider-metadata). -Apart from the scoped pairing and provider metadata checks, the hooks intentionally do not run tests, snapshots, documentation checks, builds, or hygiene. Contributors run the [checks relevant to the changed behavior](../AGENTS.md#run-relevant-checks-locally) once; CI owns exhaustive coverage, built-artifact smokes, and the Node 22.19, 24, and 26 compatibility matrix. +Apart from the scoped pairing and provider metadata checks, the hooks intentionally do not run tests, snapshots, documentation checks, builds, or hygiene. Contributors run the [checks relevant to the changed behavior](../AGENTS.md#run-relevant-checks-locally) once; CI owns exhaustive coverage, built-artifact smokes, and the Node 24.0, latest 24, and 26 compatibility matrix. Contributors can opt into the comprehensive local gate set with `pnpm run check:all`. The command is independent of the Git hooks and is not an agent instruction. diff --git a/docs/development.zh.md b/docs/development.zh.md index ec6ee10f00..3d1ead70c0 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -10,7 +10,7 @@ ### 前置条件 -- Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md)。 +- Node.js 要求 24.0 或更高版本。CI 覆盖 24.0、最新的 24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md)。 - 启用 Node.js TypeScript 类型剥离。仓库构建脚本用 tsdown 的 native 配置加载器加载 `tsdown.config.ts`,因此当 `NODE_OPTIONS` 含 `--no-experimental-strip-types` 或 Node.js 构建缺少 TypeScript 支持时会失败;`pnpm run build` 会先检查这一条件并指出原因。 - 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.28.5`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`。 - Git 2.26 或更高版本;钩子设置会启用 Git 的 worktree 专属配置扩展。 @@ -149,7 +149,7 @@ vendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `v pi-ai 检查会在内存中重新生成预期元数据。数据过期时,检查拒绝提交,不改写或暂存文件。修改 pi-ai 依赖或生成器后,请按包内的[提供方元数据更新步骤](../packages/llm/llm-pi-ai/README.zh.md#generated-provider-metadata)操作。 -除限定范围的配对和提供方元数据检查外,这些钩子有意不运行测试、快照、文档检查、构建或 `hygiene`。贡献者只运行一次[与改动行为相关的检查](../AGENTS.md#run-relevant-checks-locally);CI 负责全量覆盖率门禁、构建产物冒烟测试,以及 Node 22.19、24 和 26 兼容性矩阵。 +除限定范围的配对和提供方元数据检查外,这些钩子有意不运行测试、快照、文档检查、构建或 `hygiene`。贡献者只运行一次[与改动行为相关的检查](../AGENTS.md#run-relevant-checks-locally);CI 负责全量覆盖率门禁、构建产物冒烟测试,以及 Node 24.0、最新的 24 和 26 兼容性矩阵。 贡献者可以选择运行 `pnpm run check:all`,执行全面的本地门禁集。该命令独立于 Git 钩子,也不是对 agent 的指令。 diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.i18n.yaml new file mode 100644 index 0000000000..30834aa349 --- /dev/null +++ b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.i18n.yaml @@ -0,0 +1,16 @@ +# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.md +/: + en: 3e522f1f0e3a5539 + zh: ea94801bb527f209 +/node-js-24-is-required: + en: cc6b7b01b28de18d + zh: 05b7c46ae86fd688 +/node-js-24-is-required/change: + en: 63cadfd5c0c7f662 + zh: 989401b7f33dcdd8 +/node-js-24-is-required/migration: + en: 09189e3b48519ef6 + zh: bd60335c71d71434 diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.md b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.md new file mode 100644 index 0000000000..d9a83f4286 --- /dev/null +++ b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.md @@ -0,0 +1,17 @@ +--- +kind: upgrade-guide +description: "DSH requires Node.js 24.0 or newer and no longer supports Node.js 22." +--- +# Node.js 24 is required + +English | [中文](guide.zh.md) + +## Change + +DSH requires Node.js 24.0 or newer. The previous release also supported Node.js 22.19 and newer releases in the 22.x line. This change affects npm installations, source checkouts, and the Python SDK's explicit `DSH_RUNTIME_MODE=node` development carrier. Packaged desktop applications and the Python SDK's bundled executable provide their own runtime. + +## Migration + +1. Upgrade the Node.js installation used to start DSH to version 24.0 or newer. Update Node version pins in development environments, containers, and CI jobs. +2. Run `node --version` in the same environment and confirm that it reports version 24.0 or newer. +3. For a source checkout, run `pnpm install`. Start DSH with the profile you normally use and confirm that it starts successfully. diff --git a/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.zh.md b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.zh.md new file mode 100644 index 0000000000..250c6ec54f --- /dev/null +++ b/docs/upgrade-guide/v0.2.1-alpha.4/node-24/guide.zh.md @@ -0,0 +1,17 @@ +--- +kind: upgrade-guide +description: "DSH 要求 Node.js 24.0 或更高版本,不再支持 Node.js 22。" +--- +# 必须使用 Node.js 24 + +[English](guide.md) | 中文 + +## 变更 + +DSH 要求 Node.js 24.0 或更高版本。上一版本还支持 Node.js 22.19 及 22.x 系列中的后续版本。此变更影响 npm 安装、源码检出,以及 Python SDK 显式指定 `DSH_RUNTIME_MODE=node` 的开发载体。打包的桌面应用和 Python SDK 的内置可执行文件自带运行时。 + +## 迁移 + +1. 将用于启动 DSH 的 Node.js 安装升级至 24.0 或更高版本。更新开发环境、容器和 CI 任务中固定的 Node 版本。 +2. 在同一环境中运行 `node --version`,确认版本为 24.0 或更高版本。 +3. 对于源码检出,运行 `pnpm install`。使用平常的 profile 启动 DSH,确认它能成功启动。 diff --git a/package.json b/package.json index 042d7eb56b..e1cc4bfcde 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "module", "packageManager": "pnpm@11.28.5", "engines": { - "node": "^22.19.0 || >=24.0.0" + "node": ">=24.0.0" }, "workspaces": [ "vendor/*", diff --git a/packages/boot/app-boot/src/profile-resolution/resolver.ts b/packages/boot/app-boot/src/profile-resolution/resolver.ts index 60fce4f032..468ec82e24 100644 --- a/packages/boot/app-boot/src/profile-resolution/resolver.ts +++ b/packages/boot/app-boot/src/profile-resolution/resolver.ts @@ -751,7 +751,7 @@ function internalModules(): InternalModules { } const esm = esmModule.getOrInitializeCascadedLoader() const modern = 'getOrCreateModuleJob' in esm - /* v8 ignore start -- the supported Node 22/24/26 matrix validates each available Internal interface */ + /* v8 ignore start -- the supported Node 24/26 matrix validates each available Internal interface */ if (typeof esm.resolveSync !== 'function' || typeof Reflect.get(esm, modern ? 'getOrCreateModuleJob' : 'getModuleJobForImport') !== 'function' || (!modern && typeof Reflect.get(esm, 'resolve') !== 'function') @@ -851,7 +851,7 @@ export function installRuntimeInterception( let expected: ResolveResult | Promise try { expected = adapted(target.specifier, target.parentURL, attributes) - /* v8 ignore next -- Node 24+ resolves synchronously; the Node 22 matrix covers its Promise result */ + /* v8 ignore next -- the current loader resolves synchronously; the private interface also permits Promise results */ if (expected instanceof Promise) expected = expected.catch(restoreImporter) } catch (error) { return restoreImporter(error) @@ -877,7 +877,7 @@ export function installRuntimeInterception( } catch (error) { return restoreImporter(error) } - /* v8 ignore next -- Node 24+ resolves synchronously; the Node 22 matrix covers its Promise result */ + /* v8 ignore next -- the current loader resolves synchronously; the private interface also permits Promise results */ if (result instanceof Promise) return result.catch(restoreImporter) if (cacheable) state.esm = result return result @@ -897,13 +897,13 @@ export function installRuntimeInterception( } return (request, parent, attributes) => { const result = adapted(request, parent, attributes) - /* v8 ignore next -- Node 24+ resolves synchronously; the Node 22 matrix covers its Promise result */ + /* v8 ignore next -- the current loader resolves synchronously; the private interface also permits Promise results */ return result instanceof Promise ? result.then(resolved => relocate(resolved, parent)) : relocate(result, parent) } } let restoreEsm: () => void - /* v8 ignore else -- CI coverage runs Node 24 v2; the Node 22 matrix exercises the v1 adapter */ + /* v8 ignore else -- CI coverage runs Node 24 v2; the Node 24.0 matrix exercises the v1 adapter */ if (modern) { const loader = esm as ModuleLoaderV2 const original = Reflect.get(loader, 'resolveSync') diff --git a/packages/boot/hmr/src/package-manifest.ts b/packages/boot/hmr/src/package-manifest.ts index 0c41fbb42c..8bf58746a7 100644 --- a/packages/boot/hmr/src/package-manifest.ts +++ b/packages/boot/hmr/src/package-manifest.ts @@ -111,7 +111,7 @@ function captureEsmResolveCache(native: NativeAccess, parentURL: string): Map { // ZWNJ carries `XID_Continue` and not `XID_Start`, so the predicate splits // on position: bare in a tail, subscripted at a head. Both verdicts are // stable across the supported engines — the property arrives in Unicode - // 15.1 and the floor (Node 22.19.0, Unicode 16.0) is past it. + // 15.1 and the supported engines use Unicode 16.0 or newer. // // The interpreter side is where this one skews, and it is the same skew the // docstring's four other characters record, reached in a tail position @@ -540,10 +540,8 @@ describe('renderToolsSdkPy', () => { // Unlike the other Unicode cases in this file, the table row is recent: // U+A7DC and the U+019B uppercase mapping to it both arrive in Unicode // 16.0 (`DerivedAge.txt`; CPython 3.12.13's 15.0.0 has neither). The - // engines floor sits exactly there with no margin — Node 22.19.0 reports - // Unicode 16.0 (ICU 77.1) and maps U+019B to U+A7DC, measured — so an - // engine below the floor fails here as a renderer regression whose real - // cause is the table version. + // supported engines include that mapping. An older Unicode table fails + // here as a renderer regression whose cause is the table version. const text = renderToolsSdkPy([ { name: 'ƛ', diff --git a/packages/session/session-persistence-jsonl/src/zstd-private-decoder.ts b/packages/session/session-persistence-jsonl/src/zstd-private-decoder.ts index 6cc88aadf7..e9e41bf556 100644 --- a/packages/session/session-persistence-jsonl/src/zstd-private-decoder.ts +++ b/packages/session/session-persistence-jsonl/src/zstd-private-decoder.ts @@ -41,7 +41,7 @@ function privateZstdStream( const errorKey = Reflect.ownKeys(stream).find((key): key is symbol => ( typeof key === 'symbol' && key.description === 'kError' )) - /* v8 ignore next -- one test runtime exposes one Node-private shape; the Node 22/24/26 matrix checks compatibility. */ + /* v8 ignore next -- one test runtime exposes one Node-private shape; the Node 24/26 matrix checks compatibility. */ if ( typeof handle !== 'object' || handle === null || typeof (handle as { writeSync?: unknown }).writeSync !== 'function' diff --git a/packages/session/session-persistence-jsonl/src/zstd.ts b/packages/session/session-persistence-jsonl/src/zstd.ts index 01b4d459f0..c106b6e7c3 100644 --- a/packages/session/session-persistence-jsonl/src/zstd.ts +++ b/packages/session/session-persistence-jsonl/src/zstd.ts @@ -136,7 +136,7 @@ export interface ZstdFrameDecoder { } /** - * Select the shared private decoder when the running Node 22/24/26 shape is + * Select the shared private decoder when the running Node 24/26 shape is * compatible, otherwise preserve correctness with the public one-shot API. * @returns a synchronous decoder with an implementation-independent lifecycle. */ diff --git a/packages/subprocess/subprocess/tests/egress.spec.ts b/packages/subprocess/subprocess/tests/egress.spec.ts index 9c1fd2692b..6b948c183a 100644 --- a/packages/subprocess/subprocess/tests/egress.spec.ts +++ b/packages/subprocess/subprocess/tests/egress.spec.ts @@ -6,16 +6,6 @@ import { clearedProxyEnv, installProxyFromEnvironment } from '@deepseek-ai/dsh-h import { createLaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment' import { scrubbedParentEnv } from '../src/index.ts' -/** - * Whether this runtime honors `NODE_USE_ENV_PROXY`, which is how a child Node receives the policy. - * Added in Node 24.0 and backported to 22.21; the engines range admits 22.19 and 22.20, where a - * child stays direct. - */ -function supportsEnvProxy(): boolean { - const [major = 0, minor = 0] = process.versions.node.split('.').map(Number) - return major >= 24 || (major === 22 && minor >= 21) -} - let seen: string[] = [] let proxy: Server let proxyUrl: string @@ -88,10 +78,7 @@ describe('child process egress', () => { } expect(childEnv.NODE_USE_ENV_PROXY).toBe('1') expect(childEnv.HTTP_PROXY).toBe(proxyUrl) - // The flag is what a child Node acts on; an older runtime ignores it and stays direct, which is - // the documented seam rather than a defect. - if (supportsEnvProxy()) expect(seen.join('|')).toContain('child-probe.invalid') - else expect(seen).toEqual([]) + expect(seen.join('|')).toContain('child-probe.invalid') }) it('a child Node reaches a proxy the user gave only as ALL_PROXY', async () => { @@ -111,8 +98,7 @@ describe('child process egress', () => { // own names connects directly while this process proxies. The resolved value fills that gap. expect(childEnv.ALL_PROXY).toBe(proxyUrl) expect(childEnv.HTTP_PROXY).toBe(proxyUrl) - if (supportsEnvProxy()) expect(seen.join('|')).toContain('all-proxy-probe.invalid') - else expect(seen).toEqual([]) + expect(seen.join('|')).toContain('all-proxy-probe.invalid') }) it('keeps a proxy the user set for another tool, and fills only a scheme they never named', async () => { diff --git a/packages/util/http-proxy/README.i18n.yaml b/packages/util/http-proxy/README.i18n.yaml index ce15b017b0..c792fde61b 100644 --- a/packages/util/http-proxy/README.i18n.yaml +++ b/packages/util/http-proxy/README.i18n.yaml @@ -48,8 +48,8 @@ en: 7f12ca92e7722f98 zh: 97fa75de6a3f6a3f /deepseek-ai-dsh-http-proxy/known-limitations-and-deferred-work: - en: 26e0a7fd9dccf81c - zh: c61ad5d4dfc91cb8 + en: c19acf259afc1b59 + zh: ae48892bc1a89233 /deepseek-ai-dsh-http-proxy/known-limitations-and-deferred-work/dev-note: en: 8f915ac6f4fc1525 zh: 29b06e0aae5a30ec diff --git a/packages/util/http-proxy/README.md b/packages/util/http-proxy/README.md index fd6f1e9603..2f4c6a1948 100644 --- a/packages/util/http-proxy/README.md +++ b/packages/util/http-proxy/README.md @@ -106,7 +106,7 @@ These limits define when the package is a poor fit. They are current package con - **No SOCKS, PAC, or operating-system proxy detection** — only `http(s)://` proxy URLs from the environment. A macOS or Windows system-proxy setting is not read, so a user who only toggled it in a proxy application must still export the variables; a SOCKS URL is reported and that scheme stays direct rather than borrowing another scheme's proxy. - **No custom certificate authority** — a TLS-intercepting corporate proxy needs `NODE_EXTRA_CA_CERTS` set on the process before launch, which this package neither sets nor validates. -- **A spawned child honors the policy only on a new enough runtime, and only when every value it inherits is one Node accepts** — it reads the published environment through Node's `NODE_USE_ENV_PROXY` (22.21+, 24+), and the engines range admits 22.19 and 22.20, where such a child stays direct. A user whose environment also names a SOCKS or otherwise refused proxy leaves every child Node direct: the flag is withheld so the child can start at all. A child also matches bypass entries with Node's own `NO_PROXY` rules, which differ from this package's in their separators and IPv4-range support. Nothing in this process depends on a Node version: every in-process request reaches the global dispatcher. +- **A spawned child honors the policy only on a new enough runtime, and only when every value it inherits is one Node accepts** — it reads the published environment through Node's `NODE_USE_ENV_PROXY` (22.21+, 24+); a child launched with an older Node stays direct. A user whose environment also names a SOCKS or otherwise refused proxy leaves every child Node direct: the flag is withheld so the child can start at all. A child also matches bypass entries with Node's own `NO_PROXY` rules, which differ from this package's in their separators and IPv4-range support. Nothing in this process depends on a Node version: every in-process request reaches the global dispatcher. - **Telemetry is direct by design** — the OTLP exporter posts through `node:http`, which no global dispatcher reaches. Routing it would need either an `http.Agent` whose `proxyEnv` option post-dates the lowest supported Node, or the SDK's `fetch` transport, which has no compression while the shipped profile enables gzip. Telemetry is the one channel whose loss costs the user nothing, so it stays where it was; `DSH_TELEMETRY_MODE=DISABLED` turns it off. - **Model-authored programs receive no proxy settings** — the Node ptc-runtime process and workflow worker do not inherit a proxy URL that may contain `user:password`. Their direct requests need their own configuration and remain subject to the execution sandbox. - **The regression gate sees source, not dependencies** — `verify-no-bare-dispatcher` parses `packages/*/*/src` and `apps/*/src`; tests, scripts, and the internals of a third-party SDK are outside it. That is why every outbound call site also carries an `egress.spec.ts`. diff --git a/packages/util/http-proxy/README.zh.md b/packages/util/http-proxy/README.zh.md index f85f91e2e0..f463daa07e 100644 --- a/packages/util/http-proxy/README.zh.md +++ b/packages/util/http-proxy/README.zh.md @@ -106,7 +106,7 @@ loopback 始终被绕过——`localhost`、整个 `127.0.0.0/8` 段、`::1`、` - **不支持 SOCKS、PAC 或操作系统代理探测**——只接受来自环境的 `http(s)://` 代理 URL。不会读取 macOS 或 Windows 的系统代理设置,因此仅在代理软件里拨了开关的用户仍须导出环境变量;SOCKS URL 会被报告,且该协议保持直连,不会借用另一协议的代理。 - **不支持自定义证书颁发机构**——做 TLS 拦截的企业代理需要在启动前为进程设置 `NODE_EXTRA_CA_CERTS`,本包既不设置也不校验它。 -- **spawn 出的子进程只在足够新的运行时上遵循策略,且仅当它继承的每个值都是 Node 接受的**——它通过 Node 的 `NODE_USE_ENV_PROXY` 读取已发布的环境(22.21+、24+),而 engines 范围允许 22.19 与 22.20,在这两个版本上这样的子进程保持直连。若用户环境里还有 SOCKS 或其他被拒的代理,所有子 Node 都保持直连:不设置该标志,子进程才起得来。子进程还会按 Node 自己的 `NO_PROXY` 规则匹配绕过条目,其分隔符与 IPv4 区间处理与本包不同。本进程内不依赖任何 Node 版本:每一次进程内请求都会落到全局 dispatcher。 +- **spawn 出的子进程只在足够新的运行时上遵循策略,且仅当它继承的每个值都是 Node 接受的**——它通过 Node 的 `NODE_USE_ENV_PROXY` 读取已发布的环境(22.21+、24+);使用更旧 Node 启动的子进程保持直连。若用户环境里还有 SOCKS 或其他被拒的代理,所有子 Node 都保持直连:不设置该标志,子进程才起得来。子进程还会按 Node 自己的 `NO_PROXY` 规则匹配绕过条目,其分隔符与 IPv4 区间处理与本包不同。本进程内不依赖任何 Node 版本:每一次进程内请求都会落到全局 dispatcher。 - **遥测按设计直连**——OTLP 导出器通过 `node:http` 投递,全局 dispatcher 触及不到。要让它走代理,要么依赖 `http.Agent` 的 `proxyEnv`,而该选项晚于本项目支持的最低 Node 版本;要么改用 SDK 的 `fetch` 传输,但它没有压缩能力,而随附配置启用了 gzip。遥测是唯一一条丢失后不会让用户付出任何代价的通道,因此维持原状;`DSH_TELEMETRY_MODE=DISABLED` 可关闭它。 - **模型编写的程序不接收代理配置**——Node ptc-runtime 进程与 workflow worker 不继承可能含有 `user:password` 的代理 URL。其直接请求需要自行配置,并继续受到执行沙箱的约束。 - **防回归门禁只看源码,看不到依赖内部**——`verify-no-bare-dispatcher` 解析 `packages/*/*/src` 与 `apps/*/src`;测试、脚本以及第三方 SDK 的内部都在其之外。这正是每个出网点还各配一份 `egress.spec.ts` 的原因。 diff --git a/python/development.i18n.yaml b/python/development.i18n.yaml index ebc184c7f0..7d26b738c6 100644 --- a/python/development.i18n.yaml +++ b/python/development.i18n.yaml @@ -12,8 +12,8 @@ en: 37884fe76e167a0e zh: 4b6e50768c4bb945 /python-contributor-workflows/run-against-node-source: - en: 4d30c74632781443 - zh: 13191cb05f99dd31 + en: 7ddb8098bcc648b4 + zh: dcc77be69b0a2fef /python-contributor-workflows/build-distributions: en: 4aa941562dba5482 zh: 50543a92b3d0cf04 diff --git a/python/development.md b/python/development.md index 85c5a22142..2ef483692a 100644 --- a/python/development.md +++ b/python/development.md @@ -61,7 +61,7 @@ Alternatively export a non-empty `DSH_HOME`. The SDK rejects a launch that would Repository contributors can select either development route; both execute the normal `dsh --profile sdk` launcher: -- Set `DSH_RUNTIME_MODE=node` to use the built Node carrier on system Node `>=22.19`. The build script refreshes this carrier, but distributions never include or auto-select it. +- Set `DSH_RUNTIME_MODE=node` to use the built Node carrier on system Node `>=24.0`. The build script refreshes this carrier, but distributions never include or auto-select it. - Set `dsh_bin` to the absolute built `apps/cli/lib/bin.js` path to exercise the checkout's CLI directly. Supply an explicit `dsh_home`, plus `profile` and ordered `patches` as needed. `python/sdk/tests/manual_sdk_agent_smoke.py` uses the internal `_launch_args` test adapter to exercise the unbuilt TypeScript CLI under tsx. Arbitrary argv replacement is intentionally absent from the public SDK. diff --git a/python/development.zh.md b/python/development.zh.md index ffdbc55b26..8ad2320270 100644 --- a/python/development.zh.md +++ b/python/development.zh.md @@ -61,7 +61,7 @@ with DeepSeekHarness(dsh_home="/absolute/path/to/test-dsh-home") as harness: 仓库贡献者可以选择以下任一开发路径;两者都执行普通的 `dsh --profile sdk` 启动器: -- 设置 `DSH_RUNTIME_MODE=node`,在系统 Node `>=22.19` 上使用已构建的 Node 载体。构建脚本会刷新该载体,但分发物绝不会包含或自动选择它。 +- 设置 `DSH_RUNTIME_MODE=node`,在系统 Node `>=24.0` 上使用已构建的 Node 载体。构建脚本会刷新该载体,但分发物绝不会包含或自动选择它。 - 将 `dsh_bin` 设置为已构建 `apps/cli/lib/bin.js` 的绝对路径,直接验证当前 checkout 的 CLI。请显式提供 `dsh_home`,并按需提供 `profile` 与有序 `patches`。 `python/sdk/tests/manual_sdk_agent_smoke.py` 使用内部 `_launch_args` 测试适配器,通过 tsx 验证未构建的 TypeScript CLI。公开 SDK 刻意不提供任意 argv 替换。 diff --git a/python/sdk-runtime/README.i18n.yaml b/python/sdk-runtime/README.i18n.yaml index f9edf1d554..48598d8e8e 100644 --- a/python/sdk-runtime/README.i18n.yaml +++ b/python/sdk-runtime/README.i18n.yaml @@ -6,8 +6,8 @@ en: 1ea1d8e6d2c14b02 zh: 8e0b9903158667a1 /deepseek-harness-runtime-bin/installed-commands-and-artifacts: - en: c880421134bfc93e - zh: cf998878378405a8 + en: b5ef4e90c1e707ff + zh: 55e305256b69d6a3 /deepseek-harness-runtime-bin/python-module-api: en: 78a200e328d96637 zh: f3b7a0b69b7a39e0 diff --git a/python/sdk-runtime/README.md b/python/sdk-runtime/README.md index af4ac51be7..f780e25dda 100644 --- a/python/sdk-runtime/README.md +++ b/python/sdk-runtime/README.md @@ -36,7 +36,7 @@ Skill selection is independent of delivery: project, custom-directory, and user Office operation skills invoke the configured Node and Kit CLI through the profile's shell commands. See the [Office provider](../../packages/skill/skill-office/README.md#use-this-package) for command selection and replacing the skill collection. Configuration patches apply at process startup; changing skills does not require rebuilding the runtime wheel or Python environment. -Repository builds also materialize a dev-only `runtime/node/` carrier. It runs `node runtime/node/runtime-bootstrap.mjs` on system Node 22.19 or newer. It is never selected automatically and is excluded from wheels and sdists. +Repository builds also materialize a dev-only `runtime/node/` carrier. It runs `node runtime/node/runtime-bootstrap.mjs` on system Node 24.0 or newer. It is never selected automatically and is excluded from wheels and sdists. Both carriers execute the same `dsh` grammar and shipped profiles, including the standalone `sdk-minimal` tree and the full `web` profile with its frontend assets. The private `dsh-python-runtime-closure` manifest defines the packaged dependency closure; there is no Python-specific Node application or checked-in default `cordis.yml`. diff --git a/python/sdk-runtime/README.zh.md b/python/sdk-runtime/README.zh.md index 991a2a21f9..78d3f8756c 100644 --- a/python/sdk-runtime/README.zh.md +++ b/python/sdk-runtime/README.zh.md @@ -36,7 +36,7 @@ primary_runtime = download_primary_runtime() Office 操作技能通过 profile 的 shell 命令调用配置的 Node 和 Kit CLI。命令选择及替换技能集合的方式见 [Office 提供方](../../packages/skill/skill-office/README.zh.md#use-this-package)。配置 patch 在进程启动时生效;切换技能无需重建 runtime wheel 或 Python 环境。 -仓库构建还会物化仅限开发的 `runtime/node/` 载体。它在系统 Node 22.19 或更高版本上运行 `node runtime/node/runtime-bootstrap.mjs`。系统不会自动选择它,而且 wheel 包与 sdist 均不包含它。 +仓库构建还会物化仅限开发的 `runtime/node/` 载体。它在系统 Node 24.0 或更高版本上运行 `node runtime/node/runtime-bootstrap.mjs`。系统不会自动选择它,而且 wheel 包与 sdist 均不包含它。 两种载体执行相同的 `dsh` 语法与随附 profile,包括独立的 `sdk-minimal` 配置树,以及包含前端产物的完整 `web` profile。私有 `dsh-python-runtime-closure` manifest(元数据清单)定义打包依赖闭包;不存在 Python 专用 Node 应用或检入的默认 `cordis.yml`。 diff --git a/python/sdk-runtime/src/deepseek_harness_runtime/__init__.py b/python/sdk-runtime/src/deepseek_harness_runtime/__init__.py index bd853f73b1..eccec03617 100644 --- a/python/sdk-runtime/src/deepseek_harness_runtime/__init__.py +++ b/python/sdk-runtime/src/deepseek_harness_runtime/__init__.py @@ -11,7 +11,7 @@ Two runtime carriers coexist under ``runtime/``, both injected by the repo's - **node (dev-only)**: the full deploy closure under ``runtime/node/`` (``package.json`` + ``node_modules/``), executed as ``node runtime/node/runtime-bootstrap.mjs`` on a - system Node >= 22.19. It is the current checkout's source build, never + system Node >= 24.0. It is the current checkout's source build, never selected automatically, and excluded from wheel/sdist distributions. Both carriers execute the same dsh command grammar. The Python SDK selects the @@ -147,7 +147,7 @@ def _node_launch_args() -> tuple[str, str]: node = shutil.which("node") if node is None: raise FileNotFoundError( - "the node runtime mode needs a system `node` (>=22.19) on PATH; " + "the node runtime mode needs a system `node` (>=24.0) on PATH; " "install Node.js or use the exe mode" ) return (node, str(bin_js)) diff --git a/scripts/ci-compatible-selfhosted.spec.ts b/scripts/ci-compatible-selfhosted.spec.ts index 151d8c3f07..ab4c2c3666 100644 --- a/scripts/ci-compatible-selfhosted.spec.ts +++ b/scripts/ci-compatible-selfhosted.spec.ts @@ -67,12 +67,11 @@ describe('Node compatibility self-hosted routing', () => { expect(route({ repository: '' })).toBe('ubuntu-latest') }) - it('preserves all three required version jobs and their concurrency', () => { + it('preserves the supported version jobs and their concurrency', () => { expect(job.if).toBe("github.event_name == 'pull_request'") expect(job.strategy['fail-fast']).toBe(false) expect(job.strategy.matrix.include).toEqual([ - { node: '22.19', name: 'node 22.19', runner: 'ubuntu-latest', gate_concurrency: '1' }, - { node: '24.9', name: 'node 24.9', runner: 'ubuntu-latest', gate_concurrency: '1' }, + { node: '24.0', name: 'node 24.0', runner: 'ubuntu-latest', gate_concurrency: '1' }, { node: 26, name: 'node 26', runner: 'ubuntu-latest', gate_concurrency: '1' }, ]) expect(job.env.DSH_GATE_CONCURRENCY).toBe('${{ matrix.gate_concurrency }}') @@ -133,8 +132,8 @@ describe('Node compatibility self-hosted routing', () => { const step = job.steps.find(candidate => candidate.name === 'Verify isolated Node installation')! expect(step.if).toBe("runner.environment == 'self-hosted'") for (const [executable, status] of [ - ['/runner temp/node-compat-toolcache/node/24.9.0/x64/bin/node', 0], - ['/shared/toolcache/node/24.9.0/x64/bin/node', 1], + ['/runner temp/node-compat-toolcache/node/24.0.0/x64/bin/node', 0], + ['/shared/toolcache/node/24.0.0/x64/bin/node', 1], ['/runner temp/node-compat-toolcache-other/node', 1], ] as const) { const child = spawnSync('bash', ['-e', '-u', '-o', 'pipefail', '-c', 'node() { printf "%s" "$TEST_EXECUTABLE"; }; ' + step.run!], { diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 699ffb3acc..25ee0846e7 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -30,7 +30,7 @@ describe('CI workflow', () => { expect(compatibilityLimit).toBeGreaterThan(0) expect(compatibility.strategy).toMatchObject({ 'fail-fast': false, - matrix: { include: [{ node: '22.19' }, { node: '24.9' }, { node: 26 }] }, + matrix: { include: [{ node: '24.0' }, { node: 26 }] }, }) expect(workflowJob(ci, 'all-checks-passed').needs).toContain('node-compat') const ordinaryJobs = Object.entries(ci.jobs) diff --git a/scripts/publint-all.spec.ts b/scripts/publint-all.spec.ts index aea7be74bb..3fdb35e6a5 100644 --- a/scripts/publint-all.spec.ts +++ b/scripts/publint-all.spec.ts @@ -37,7 +37,7 @@ function fixture(options: { version: '0.0.1', type: 'module', license: 'MIT', - engines: { node: '>=22.19' }, + engines: { node: '>=24.0.0' }, sideEffects: false, files: ['lib'], exports: { '.': { default: options.exportPath ?? './lib/index.js' } }, diff --git a/scripts/run-gates.spec.ts b/scripts/run-gates.spec.ts index 19259c8a6d..0f60237059 100644 --- a/scripts/run-gates.spec.ts +++ b/scripts/run-gates.spec.ts @@ -846,7 +846,7 @@ describe('Typert contract preparation', () => { }) describe('Node compatibility graph', () => { - it.each(['22.19.0', '24.9.0', '26.0.0'])('runs all source compatibility smokes serially on Node %s', (version) => { + it.each(['24.0.0', '24.19.0', '26.0.0'])('runs all source compatibility smokes serially on Node %s', (version) => { const subject = withNodeVersion(version, () => withPnpmEntrypoint(() => gatesForMode('node-compat'))) @@ -878,8 +878,16 @@ describe('Node compatibility graph', () => { expect(subject.find(item => item.id === 'source-compat-smokes')?.env).toEqual({ VITEST_MAX_WORKERS: '1' }) }) - it.each([undefined, '1'])('builds the complete Node 22 artifacts once with skip-typecheck=%s', (skipTypecheck) => { - const subject = withNodeVersion('22.19.0', () => + it.each(['24.19.0', '26.0.0'])('keeps Node %s compatibility source-only', (version) => { + const subject = withNodeVersion(version, () => + withPnpmEntrypoint(() => gatesForMode('node-compat'))) + + expect(subject.map(item => item.id)).not.toContain('build') + expect(subject.map(item => item.id)).not.toContain('cli-lazy-search-startup-smoke') + }) + + it.each([undefined, '1'])('builds the complete Node 24.0 artifacts once with skip-typecheck=%s', (skipTypecheck) => { + const subject = withNodeVersion('24.0.0', () => withEnv('DSH_NODE_COMPAT_SKIP_TYPECHECK', skipTypecheck, () => withPnpmEntrypoint(() => gatesForMode('node-compat')))) @@ -894,8 +902,8 @@ describe('Node compatibility graph', () => { }) }) - it.each(['passed', 'failed'] as const)('runs the Node 22 lazy-search smoke only after a passed build (%s)', async (buildStatus) => { - const subject = withNodeVersion('22.19.0', () => + it.each(['passed', 'failed'] as const)('runs the Node 24.0 lazy-search smoke only after a passed build (%s)', async (buildStatus) => { + const subject = withNodeVersion('24.0.0', () => withEnv('DSH_NODE_COMPAT_SKIP_TYPECHECK', '1', () => withPnpmEntrypoint(() => gatesForMode('node-compat')))) let buildCompleted = false diff --git a/scripts/run-gates.ts b/scripts/run-gates.ts index 3e5fd4c418..cc78afeb14 100644 --- a/scripts/run-gates.ts +++ b/scripts/run-gates.ts @@ -429,7 +429,7 @@ function nodeCompatGates(): Gate[] { const typecheck = flagEnabled('DSH_NODE_COMPAT_SKIP_TYPECHECK') ? [] : [pnpmScript('typecheck', 'typecheck')] - if (runningNodeMajor() !== 22) { + if (!process.versions.node.startsWith('24.0.')) { return [...typecheck, ...nodeCompatSmokeGates()] } return [ @@ -475,15 +475,6 @@ function nodeCompatSmokeGates(options: { cliSmoke?: boolean } = {}): Gate[] { return gates } -/** Active Node major used to select version-specific compatibility checks. */ -function runningNodeMajor(): number { - const major = Number.parseInt(process.versions.node.split('.')[0] ?? '', 10) - if (!Number.isSafeInteger(major)) { - throw new Error(`run-gates: cannot parse Node version ${JSON.stringify(process.versions.node)}.`) - } - return major -} - function ciStaticGates(options: { ownsBuild: boolean }): Gate[] { return [ ...ciSharedStaticGates(),