@peakflo/agent-harness 0.2.14-rc.2 → 0.2.14-rc.3

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.
@@ -1,18 +1,15 @@
1
1
  /**
2
2
  * THE NODE.JS FLOOR, CHECKED AT START.
3
3
  *
4
- * `package.json` says `engines.node: ">=22.19"`. npm treats that as advice: on
4
+ * `package.json` declares the patched supported Node lines. npm treats that as advice: on
5
5
  * an older Node it prints EBADENGINE and installs anyway, and `npx` then starts
6
6
  * the runner as if nothing was wrong. So the floor was a line in a manifest
7
7
  * that nothing enforced.
8
8
  *
9
- * WHERE THE FLOOR COMES FROM. Not from this package's own code — that needs
10
- * `node:sqlite` without a flag (Node 22.13). It comes from Pi, the one backend
11
- * this runner runs turns with: `@earendil-works/pi-coding-agent` declares
12
- * `engines.node >=22.19.0`, and Pi's launcher is `#!/usr/bin/env node`, so Pi
13
- * normally runs on the same Node as the runner. Pi 1.1.0 was seen to start on
14
- * 22.14 (`--version`, `--list-models`), and nobody has shown that a full turn
15
- * works there. An upstream requirement that is not proven false is kept.
9
+ * WHERE THE FLOOR COMES FROM. The October 2026 Node.js security release sets
10
+ * the current minimum for each supported line. The prior 22.19 floor came
11
+ * from Pi: `@earendil-works/pi-coding-agent` declares `engines.node >=22.19.0`,
12
+ * and its launcher uses the same Node as the runner.
16
13
  *
17
14
  * WHAT HAPPENS BELOW THE FLOOR. A desktop or self-hosted runner refuses to
18
15
  * start, with one line that says what to do. `PF_ALLOW_UNSUPPORTED_NODE=1`
@@ -27,6 +24,8 @@
27
24
  /** Parse `22.19`, `v22.19.0`, `>=22.19` into numbers. Null when it is none of those. */
28
25
  export declare function parseNodeVersion(text: string): number[] | null;
29
26
  export declare function isNodeVersionAtLeast(current: string, floor: string): boolean;
27
+ /** Match the supported-line range in engines.node without accepting an unpatched major. */
28
+ export declare function isNodeVersionInEngineRange(current: string, range: string): boolean;
30
29
  export type NodeFloorVerdict = {
31
30
  action: 'start';
32
31
  } | {
@@ -2,18 +2,15 @@
2
2
  /**
3
3
  * THE NODE.JS FLOOR, CHECKED AT START.
4
4
  *
5
- * `package.json` says `engines.node: ">=22.19"`. npm treats that as advice: on
5
+ * `package.json` declares the patched supported Node lines. npm treats that as advice: on
6
6
  * an older Node it prints EBADENGINE and installs anyway, and `npx` then starts
7
7
  * the runner as if nothing was wrong. So the floor was a line in a manifest
8
8
  * that nothing enforced.
9
9
  *
10
- * WHERE THE FLOOR COMES FROM. Not from this package's own code — that needs
11
- * `node:sqlite` without a flag (Node 22.13). It comes from Pi, the one backend
12
- * this runner runs turns with: `@earendil-works/pi-coding-agent` declares
13
- * `engines.node >=22.19.0`, and Pi's launcher is `#!/usr/bin/env node`, so Pi
14
- * normally runs on the same Node as the runner. Pi 1.1.0 was seen to start on
15
- * 22.14 (`--version`, `--list-models`), and nobody has shown that a full turn
16
- * works there. An upstream requirement that is not proven false is kept.
10
+ * WHERE THE FLOOR COMES FROM. The October 2026 Node.js security release sets
11
+ * the current minimum for each supported line. The prior 22.19 floor came
12
+ * from Pi: `@earendil-works/pi-coding-agent` declares `engines.node >=22.19.0`,
13
+ * and its launcher uses the same Node as the runner.
17
14
  *
18
15
  * WHAT HAPPENS BELOW THE FLOOR. A desktop or self-hosted runner refuses to
19
16
  * start, with one line that says what to do. `PF_ALLOW_UNSUPPORTED_NODE=1`
@@ -28,6 +25,7 @@
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.parseNodeVersion = parseNodeVersion;
30
27
  exports.isNodeVersionAtLeast = isNodeVersionAtLeast;
28
+ exports.isNodeVersionInEngineRange = isNodeVersionInEngineRange;
31
29
  exports.checkNodeFloor = checkNodeFloor;
32
30
  /** Parse `22.19`, `v22.19.0`, `>=22.19` into numbers. Null when it is none of those. */
33
31
  function parseNodeVersion(text) {
@@ -50,12 +48,41 @@ function isNodeVersionAtLeast(current, floor) {
50
48
  }
51
49
  return true;
52
50
  }
51
+ /** Match the supported-line range in engines.node without accepting an unpatched major. */
52
+ function isNodeVersionInEngineRange(current, range) {
53
+ if (!range.includes('||'))
54
+ return isNodeVersionAtLeast(current, range);
55
+ const have = parseNodeVersion(current);
56
+ if (!have)
57
+ return true;
58
+ const clauses = range.split('||').map((clause) => clause.trim());
59
+ const matches = clauses.map((clause) => {
60
+ const caret = /^\^(\d+\.\d+\.\d+)$/.exec(clause);
61
+ if (caret) {
62
+ const minimum = parseNodeVersion(caret[1]);
63
+ return (minimum !== null &&
64
+ have[0] === minimum[0] &&
65
+ isNodeVersionAtLeast(current, caret[1]));
66
+ }
67
+ const bounded = /^>=(\d+\.\d+\.\d+)\s+<(\d+)$/.exec(clause);
68
+ if (bounded) {
69
+ return (have[0] < Number(bounded[2]) &&
70
+ isNodeVersionAtLeast(current, bounded[1]));
71
+ }
72
+ return null;
73
+ });
74
+ // An unreadable manifest range should not refuse a runner, as with an unreadable version.
75
+ return matches.includes(null) || matches.some(Boolean);
76
+ }
53
77
  function checkNodeFloor(input) {
54
- if (!input.floor || isNodeVersionAtLeast(input.current, input.floor)) {
78
+ if (!input.floor || isNodeVersionInEngineRange(input.current, input.floor)) {
55
79
  return { action: 'start' };
56
80
  }
57
81
  const need = (parseNodeVersion(input.floor) ?? []).slice(0, 2).join('.');
58
- const problem = `pf-agent-harness needs Node.js ${need} or newer; this is Node.js ${input.current}.`;
82
+ const requirement = input.floor.includes('||')
83
+ ? `a patched Node.js release (${input.floor})`
84
+ : `Node.js ${need} or newer`;
85
+ const problem = `pf-agent-harness needs ${requirement}; this is Node.js ${input.current}.`;
59
86
  const allowed = /^(1|true|yes|on)$/i.test((input.env.PF_ALLOW_UNSUPPORTED_NODE ?? '').trim());
60
87
  if (allowed) {
61
88
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peakflo/agent-harness",
3
- "version": "0.2.14-rc.2",
3
+ "version": "0.2.14-rc.3",
4
4
  "description": "Headless Peakflo coding-agent harness. One codebase, three hosting modes: a Daytona cloud sandbox, a customer VM, and the 20x desktop.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -18,7 +18,7 @@
18
18
  "credentialHandoff": "ipc"
19
19
  },
20
20
  "engines": {
21
- "node": ">=22.19"
21
+ "node": "^22.23.4 || ^24.22.1 || >=26.12.1 <27"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public",
@@ -36,7 +36,7 @@
36
36
  "@types/node": "^22.10.0",
37
37
  "tsx": "^4.19.3",
38
38
  "typescript": "^5.7.2",
39
- "undici": "^7.21.0"
39
+ "undici": "^7.30.1"
40
40
  },
41
41
  "dependencies": {
42
42
  "@modelcontextprotocol/client": "2.0.0",