fix: engines ^22.18.0 || >=24.0.0 — exclude EOL Node 23

This commit is contained in:
imccyu
2026-07-07 17:15:41 +08:00
parent 1c2823c73d
commit 393da2b983
7 changed files with 14 additions and 10 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ Per-package map: the group READMEs, indexed from [packages/README.md](packages/R
## Commands
```sh
pnpm install # pnpm workspaces, node >= 22.18
pnpm install # pnpm workspaces, node ^22.18 || >=24
pnpm run test # vitest unit tests
pnpm run test:coverage # THE gating test run: per-file 100% coverage on packages/*/*/src
pnpm run test:e2e # real-API tests; self-skip without DEEPSEEK_API_KEY
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
development.md: 8f901909264d4405d396782d68c28fbde9b85bfa
development.zh.md: 2b2af080d11b8ea9d9cbf26c62833b4fc00fb6ba
development.md: 3acbff05204e6c7f44c6a8a727052a6abf881fab
development.zh.md: 5a02cce00c26baf5c94d4a20a9138c5af89c27c3
+1 -1
View File
@@ -6,7 +6,7 @@ This guide covers the local setup needed to work on DeepSeek Harness and underst
## Prerequisites
- Node.js 22.18 or newer. The repo declares `node >=22.18`; CI runs the matrix on Node 22.18, 24, and 26.
- Node.js `^22.18.0 || >=24.0.0` (22.18+ on the LTS line, or 24+). The Node 23 line is excluded: `node:sqlite` (until 23.4) and native TS type-stripping (until 23.6) are still flagged there, and 23 is non-LTS/EOL. CI runs the matrix on Node 22.18, 24, and 26.
- Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack.
- Git.
- Optional: a DeepSeek API key for the REPL/ACP agent demos and real-API e2e tests.
+1 -1
View File
@@ -6,7 +6,7 @@
## 前置条件
- Node.js 22.18 或更新版本。仓库声明 `node >=22.18`CI 在 Node 22.18、24 和 26 上跑矩阵。
- Node.js `^22.18.0 || >=24.0.0`(即 LTS 线的 22.18+,或 24+)。排除 Node 23 线:那里 `node:sqlite`(要到 23.4)和原生 TS 类型剥离(要到 23.6)仍需 flag,且 23 是非 LTS、已 EOL。CI 在 Node 22.18、24 和 26 上跑矩阵。
- 启用了 Corepack 的 pnpm。仓库在 `package.json` 中钉住 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,先运行 `corepack enable`
- Git。
- 可选:一个 DeepSeek API key,用于 REPL/ACP agent(智能体)演示和真实 API 的 e2e 测试。
@@ -8,10 +8,12 @@ The root `engines.node` was `>=24`, which excluded the entire Node 22 LTS line f
## Decision
Set `engines.node` to `>=22.18` and treat 22.18 as the tested floor everywhere (CI matrix `['22.18', 24, 26]`, the real-API e2e job on `['22.18', 24]` floor plus primary line, since the keyless matrix exercises only the mock adapter and the live `fetch`/SSE path runs only in e2e). 22.18 is the *later* of the two feature boundaries the code depends on, so it is the earliest Node version where everything the repo ships and tests runs unflagged:
Set `engines.node` to `^22.18.0 || >=24.0.0` (Node 22.18+ on the LTS line, or 24+) and test it on the CI matrix `['22.18', 24, 26]` the real-API e2e job on `['22.18', 24]` (floor plus primary line, since the keyless matrix exercises only the mock adapter and the live `fetch`/SSE path runs only in e2e). Two Node features gate the range, each with its own LTS-line and Current-line unflag point:
- **`node:sqlite` Node 22.13.** `packages/session-persistence/session-persistence-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`. The module dropped its `--experimental-sqlite` flag requirement in Node 22.13 (backport of the 23.4 change), so any floor ≥ 22.13 loads it without a flag.
- **Native TypeScript type-stripping — Node 22.18.** The `packages/ui/stdio-agent/tests/built-bin.e2e.ts` smoke boots the published `lib/bin.js` under plain `node` and loads the example's `.ts` plugins (`mock-llm.ts`, `echo-tool.ts`) with no tsx. Native type-stripping — which makes that work — was unflagged in the 22.x LTS line only in 22.18 (before that it needed `--experimental-strip-types`). This is the binding constraint, so it sets the floor.
- **`node:sqlite`**`packages/session-persistence/session-persistence-sqlite` does a top-level `import { DatabaseSync } from 'node:sqlite'`. 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 `packages/ui/stdio-agent/tests/built-bin.e2e.ts` smoke boots the published `lib/bin.js` under plain `node` (no tsx) and loads the example's `.ts` plugins (`mock-llm.ts`, `echo-tool.ts`). Type-stripping is the default from **22.18** (LTS) and **23.6** (Current); before those it needs `--experimental-strip-types`.
On the 22.x line both features clear at **22.18** (the later of 22.13/22.18), so `^22.18.0` is the LTS floor. The range is **disjoint** rather than an open `>=22.18` because the Node **23.023.5** window still has at least one feature flagged (sqlite until 23.4, stripping until 23.6): `>=22.18` would advertise support there, where the sqlite backend throws `ERR_UNKNOWN_BUILTIN_MODULE` at load. Node 23 is non-LTS and already end-of-life, so rather than carve out `>=23.6` the range skips the whole line and resumes at `>=24.0.0` — the same shape several of the repo's own dependencies already declare (`^22.18.0 || >=24.11.0`).
`@types/node` is pinned to the 22.x line (`^22.20.0`) to match the floor: reaching for a Node 23+/24+/25+ API then fails `tsc` on every machine and in the typecheck gate, rather than compiling clean and surviving to a runtime failure only the 22.18 matrix leg could catch. The whole tree typechecks clean against the Node 22 type surface today, so the pin costs nothing.
@@ -26,5 +28,7 @@ Set `engines.node` to `>=22.18` and treat 22.18 as the tested floor everywhere (
- **Floor `>=22.13` (the `node:sqlite` boundary) plus `--experimental-strip-types` in the built-bin smoke on 22.1322.17.** Rejected: it adds a version-conditional test flag for one narrow range and dresses up an experimental-flag dependency as first-class support. 22.18 clears both boundaries with zero test special-casing, and the five-patch gap below it buys nothing real.
- **Keep `>=24`.** Rejected: it excludes Node 22 LTS with no runtime justification once the two boundaries above are known.
- **Open-ended `>=22.18`.** Rejected: it advertises support for Node 23.023.5, where `node:sqlite` (until 23.4) or type-stripping (until 23.6) is still flagged, so the sqlite backend throws at load. The disjoint `^22.18.0 || >=24.0.0` matches the real runtime boundary.
- **Include Node 23.6+ (`^22.18.0 || >=23.6.0`).** Rejected: 23.6+ does run both features unflagged, but Node 23 is end-of-life — advertising a dead release line adds a range term (and, to back it, a CI leg) for a runtime no deployment should use. 24 is the meaningful resumption point, and the 22.18 and 24 legs already bracket the same unflagged code paths.
- **Matrix `[22, 24, 26]` (latest 22.x) instead of pinning `22.18`.** Rejected: "latest 22.x" drifts upward over time and would silently stop exercising the declared floor. Pinning the floor version is what makes the matrix a proof of the claim rather than a proof of some newer 22.x.
- **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.18 — exactly the "green types, broken product" gap. Pinning `@types/node` to the 22.x line turns that into a compile error everywhere, and the tree already typechecks clean against the Node 22 surface, so the pin is free.
+1 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"packageManager": "pnpm@11.7.0",
"engines": {
"node": ">=22.18"
"node": "^22.18.0 || >=24.0.0"
},
"workspaces": [
"vendor/*",
@@ -8,7 +8,7 @@ A SQLite durable session-persistence backend — a second `SessionPersistence` i
Each `SessionEvent` maps 1:1 onto a row in an `events` table `(session_id, seq, type, time, data, source_event_seqs, surface_op)``data` is the event payload as JSON text, so the row shape is the event verbatim (including `assistant/chunk`, keeping `seq` contiguous). The two `TEXT` columns `source_event_seqs` and `surface_op` are nullable; they store the event's optional surface-metadata fields (see [session surface](../../../docs/rfc/implemented/architecture/2026-06-18-session-surface.md)). Out-of-log metadata (`SessionHeader`) lives in a `sessions` row. A `sessions` row is written only by the first `append` — its existence is the lazy-materialization signal (`list` reports exactly the sessions that have a row), so no separate column is needed.
The repo targets Node ≥ 22.18 (the root `engines` field), which includes `node:sqlite` unflagged — the module has been available without the `--experimental-sqlite` flag since Node 22.13, so this backend's top-level `import { DatabaseSync } from 'node:sqlite'` loads without a flag on every supported version. The database opens with `foreign_keys = ON` (so `ON DELETE CASCADE` drops a session's events with its row) and the configured `journal_mode` (default `wal`; pick a rollback-journal mode like `delete` on filesystems where WAL's shared-memory files do not work, e.g. network mounts). The table-layout version is stored in `PRAGMA user_version` and checked on open: a fresh database is stamped with the current `SCHEMA_VERSION`; a database written by any other, incompatible build (a non-current `user_version`, older or newer) is rejected rather than opened against an unknown layout — there is no migration (unreleased software).
The repo's `engines.node` is `^22.18.0 || >=24.0.0` (Node 22.18+ or 24+). `node:sqlite` ships without the `--experimental-sqlite` flag from Node 22.13 (LTS) and 23.4 / 24 (Current) on; the range deliberately excludes the Node 23.023.3 window, where the module is still flagged and this backend's top-level `import { DatabaseSync } from 'node:sqlite'` would throw at load. The database opens with `foreign_keys = ON` (so `ON DELETE CASCADE` drops a session's events with its row) and the configured `journal_mode` (default `wal`; pick a rollback-journal mode like `delete` on filesystems where WAL's shared-memory files do not work, e.g. network mounts). The table-layout version is stored in `PRAGMA user_version` and checked on open: a fresh database is stamped with the current `SCHEMA_VERSION`; a database written by any other, incompatible build (a non-current `user_version`, older or newer) is rejected rather than opened against an unknown layout — there is no migration (unreleased software).
## Contract semantics over rows