@erichll/pi-sandbox 0.25.0 → 0.26.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/CHANGELOG.md +42 -0
- package/README.md +7 -6
- package/package.json +10 -11
- package/src/pi-subagents-native.ts +17 -2
- package/src/subagent.ts +20 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.1 - 2026-10-13
|
|
4
|
+
|
|
5
|
+
- Upgrade the Pi 1.2.0 development baseline (`pi-coding-agent`, `pi-ai`,
|
|
6
|
+
typebox `>=1.3.37`) and drop the unused
|
|
7
|
+
`@earendil-works/pi-server` development dependency: nothing in this package
|
|
8
|
+
imports it and pi-subagents 0.77.0 no longer references it, while upstream
|
|
9
|
+
removed the package in 1.2.0.
|
|
10
|
+
- Revalidate the compatibility gate against pi-subagents 0.78.0 on Pi 1.2.0.
|
|
11
|
+
pi-subagents 0.77.0 required chord for every host from Pi 0.85 onward,
|
|
12
|
+
which broke background children on Pi 1.2.0 (chord removed upstream);
|
|
13
|
+
0.78.0 requires chord only when the host manifest declares it, so the
|
|
14
|
+
package needs no workaround. The deterministic gate and the model-backed
|
|
15
|
+
native baseline / protected child acceptance pass. Raise the pi-subagents
|
|
16
|
+
peer floor to `>=0.78.0` because 0.77.x background children fail on
|
|
17
|
+
Pi 1.2.0; 0.78.0 also works on Pi 1.1.0.
|
|
18
|
+
- Keep the pi-coding-agent peer floor at `>=1.1.0`: both 1.1.0 and 1.2.0 are
|
|
19
|
+
verified, so 1.1.0 users are not forced onto the newer Pi.
|
|
20
|
+
|
|
21
|
+
## 0.26.0 - 2026-10-08
|
|
22
|
+
|
|
23
|
+
- Require `@erichll/pi-auto-review ^0.26.0` so sandbox approvals use the
|
|
24
|
+
coordinated Pi 1.1.0 reviewer and cancellation fixes.
|
|
25
|
+
- Upgrade Pi development dependencies and the coding-agent peer floor to
|
|
26
|
+
`^1.1.0`; move pi-subagents to development `^0.76.1` / peer `>=0.76.1` and
|
|
27
|
+
typebox to development `^1.3.36` / peer `>=1.3.36`. Keep pi-subagents optional
|
|
28
|
+
and leave the sandbox-runtime and jiti dependency declarations unchanged.
|
|
29
|
+
- Revalidate the deterministic 0.76.1 compatibility gate and isolated Node,
|
|
30
|
+
Bun, and compiled-Bun host-peer loading on Pi 1.1.0. Fix the copied-package
|
|
31
|
+
test for dependencies that do not export package.json, including the new
|
|
32
|
+
Temporal polyfill. Revalidate model-backed acceptance with the native host
|
|
33
|
+
read baseline and the protected child blocking that read, including the
|
|
34
|
+
required sandbox runtime acknowledgement.
|
|
35
|
+
- Reject settlement waiters for RPC runs cancelled with Pi 1.1.0
|
|
36
|
+
`agent_settled.aborted: true`
|
|
37
|
+
([#10607](https://github.com/earendil-works/pi/issues/10607)), including
|
|
38
|
+
waiters registered after the event or after subsequent successful runs.
|
|
39
|
+
Keep the persistent child idle and available for follow-up instead of
|
|
40
|
+
reporting a successful result or terminating the session. Legacy events
|
|
41
|
+
without the field retain their existing behavior.
|
|
42
|
+
- Ignore buffered lifecycle events after an RPC session stops or fails so
|
|
43
|
+
cancellation and shutdown cannot resurrect a terminal session.
|
|
44
|
+
|
|
3
45
|
## 0.25.0 - 2026-10-07
|
|
4
46
|
|
|
5
47
|
- Align the `@erichll/pi-auto-review` dependency with `^0.25.0` so the
|
package/README.md
CHANGED
|
@@ -91,14 +91,14 @@ Supported modes:
|
|
|
91
91
|
|
|
92
92
|
- `builtin` (default): register the process-backed `subagent` tool and sandbox
|
|
93
93
|
each complete worker process tree.
|
|
94
|
-
- `pi-subagents`: let `pi-subagents >=0.
|
|
94
|
+
- `pi-subagents`: let `pi-subagents >=0.76.1` own orchestration under the required
|
|
95
95
|
native-background tool boundary described below.
|
|
96
96
|
- `off`: protect Bash only.
|
|
97
97
|
|
|
98
98
|
### `pi-subagents` native-background tool boundary
|
|
99
99
|
|
|
100
|
-
This provider
|
|
101
|
-
0.
|
|
100
|
+
This provider requires `pi-subagents >=0.76.1` (deterministically validated on
|
|
101
|
+
0.76.1) and fails closed if its public ceiling API, module layout, or internal
|
|
102
102
|
discovery layout drifts. The peer dependency is a floor with no upper pin; the
|
|
103
103
|
loader's explicit export and layout checks are the real gate. On Node, the
|
|
104
104
|
loader aliases the `@earendil-works/pi-tui` host peer to the running Pi package.
|
|
@@ -342,9 +342,10 @@ discovery behavior,
|
|
|
342
342
|
capability ceiling, and protected launch policy in an isolated temporary agent
|
|
343
343
|
directory. It never reads or updates production Pi configuration. Model-backed
|
|
344
344
|
acceptance requires `PI_SUBAGENTS_GATE_MODEL` and an already-exported matching
|
|
345
|
-
credential; missing prerequisites are reported as `SKIP`.
|
|
346
|
-
|
|
347
|
-
|
|
345
|
+
credential; missing prerequisites are reported as `SKIP`. The current peer
|
|
346
|
+
floor and development baseline are Pi 1.1.0, with pi-subagents 0.76.1 and
|
|
347
|
+
`typebox >=1.3.36`. The deterministic and model-backed native-baseline /
|
|
348
|
+
protected-child gates pass on this baseline.
|
|
348
349
|
|
|
349
350
|
The test suite covers real Linux Sandbox Runtime enforcement when its native
|
|
350
351
|
dependencies are installed, plus deterministic broker, network approval,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erichll/pi-sandbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Linux (bubblewrap) and macOS (Seatbelt) process sandbox for Pi backed by Anthropic's @anthropic-ai/sandbox-runtime. Fail-closed static filesystem policy plus per-connection network approval from pi-auto-review; sandboxes main-agent Bash and built-in subagent process trees.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@anthropic-ai/sandbox-runtime": "^0.0.78",
|
|
54
|
-
"@erichll/pi-auto-review": "^0.
|
|
54
|
+
"@erichll/pi-auto-review": "^0.26.0",
|
|
55
55
|
"jiti": "2.7.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@earendil-works/pi-coding-agent": "
|
|
59
|
-
"pi-subagents": ">=0.
|
|
60
|
-
"typebox": ">=1.
|
|
58
|
+
"@earendil-works/pi-coding-agent": ">=1.1.0",
|
|
59
|
+
"pi-subagents": ">=0.78.0",
|
|
60
|
+
"typebox": ">=1.3.37"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"pi-subagents": {
|
|
@@ -65,12 +65,11 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@earendil-works/pi-ai": "^1.
|
|
69
|
-
"@earendil-works/pi-coding-agent": "^1.
|
|
70
|
-
"@
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"typebox": "^1.3.34",
|
|
68
|
+
"@earendil-works/pi-ai": "^1.2.0",
|
|
69
|
+
"@earendil-works/pi-coding-agent": "^1.2.0",
|
|
70
|
+
"@types/node": "^26.6.5",
|
|
71
|
+
"pi-subagents": "^0.78.0",
|
|
72
|
+
"typebox": "^1.3.37",
|
|
74
73
|
"typescript": "^7.0.2"
|
|
75
74
|
}
|
|
76
75
|
}
|
|
@@ -7,7 +7,8 @@ import * as hostPiTui from "@earendil-works/pi-tui";
|
|
|
7
7
|
import * as hostTypebox from "typebox";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Structural loader compatibility: 0.66.0 and above; the package peer floor
|
|
11
|
+
* is >=0.76.1 (no upper pin). Any new minor
|
|
11
12
|
* is accepted, so the exports/ceiling checks below are the real compatibility
|
|
12
13
|
* gate; this range only rejects old lines and guards against blind
|
|
13
14
|
* accept-on-drift. Revalidated against 0.70.0: ceiling registry
|
|
@@ -35,7 +36,21 @@ import * as hostTypebox from "typebox";
|
|
|
35
36
|
* mode needs Pi 0.86.0 or newer
|
|
36
37
|
* (the pi-subagents 0.68.0+ floor): 0.85.0 does not ship
|
|
37
38
|
* `@earendil-works/pi-server`, so background children fail to launch there.
|
|
38
|
-
*
|
|
39
|
+
* Revalidated 2026-10-08 against 0.76.1 / Pi 1.1.0: capability-ceiling and
|
|
40
|
+
* config modules are byte-identical to 0.75.0; discovery changes add settings
|
|
41
|
+
* write leases without changing the loader seams. The deterministic gate and
|
|
42
|
+
* isolated Node/Bun/compiled-Bun host-peer tests pass. Model-backed native
|
|
43
|
+
* baseline / protected child acceptance also passes, with the host read
|
|
44
|
+
* blocked by sandboxed Bash and the runtime acknowledgement verified. This
|
|
45
|
+
* package's own peer floor is Pi 1.1.0.
|
|
46
|
+
* Revalidated 2026-10-13 against 0.78.0 / Pi 1.2.0: pi-subagents 0.77.0
|
|
47
|
+
* required chord aliases for every host from Pi 0.85 onward, which broke
|
|
48
|
+
* background children on Pi 1.2.0 (chord removed upstream); 0.78.0 requires
|
|
49
|
+
* chord only when the host manifest declares it, so no host-side workaround
|
|
50
|
+
* is needed. The subagent tool schema moved management fields into `options`
|
|
51
|
+
* in 0.77.0 (top-level aliases keep working) and the loader seams are
|
|
52
|
+
* unchanged. The deterministic gate and model-backed native baseline /
|
|
53
|
+
* protected child acceptance pass.
|
|
39
54
|
*/
|
|
40
55
|
export const PI_SUBAGENTS_COMPAT_RANGE = ">=0.66.0";
|
|
41
56
|
export const NATIVE_CHILD_TOOLS = ["bash", "read", "grep", "find", "ls"] as const;
|
package/src/subagent.ts
CHANGED
|
@@ -294,6 +294,9 @@ export class ProcessBackedSubagentSession {
|
|
|
294
294
|
private stdin: Writable | undefined;
|
|
295
295
|
private rpcSequence = 0;
|
|
296
296
|
private settledCount = 0;
|
|
297
|
+
// Keep cancellation tied to its run even when settlement beats the RPC
|
|
298
|
+
// response/waiter registration or later follow-ups complete successfully.
|
|
299
|
+
private readonly abortedSettlements = new Set<number>();
|
|
297
300
|
private requestedSettlements = 0;
|
|
298
301
|
private activePrompt:
|
|
299
302
|
| {
|
|
@@ -421,6 +424,8 @@ export class ProcessBackedSubagentSession {
|
|
|
421
424
|
if (this.stateValue === "stopped") {
|
|
422
425
|
return Promise.reject(new Error(`subagent session ${this.id} is stopped`));
|
|
423
426
|
}
|
|
427
|
+
const aborted = this.settlementAbortError(target);
|
|
428
|
+
if (aborted) return Promise.reject(aborted);
|
|
424
429
|
if (this.settledCount >= target) {
|
|
425
430
|
return Promise.resolve(sessionResult(this.textValue, this.rawOutput));
|
|
426
431
|
}
|
|
@@ -531,12 +536,20 @@ export class ProcessBackedSubagentSession {
|
|
|
531
536
|
this.startReady = undefined;
|
|
532
537
|
}
|
|
533
538
|
|
|
539
|
+
private settlementAbortError(target: number): Error | undefined {
|
|
540
|
+
return this.abortedSettlements.has(target)
|
|
541
|
+
? new Error(`subagent session ${this.id} run ${target} aborted`)
|
|
542
|
+
: undefined;
|
|
543
|
+
}
|
|
544
|
+
|
|
534
545
|
private resolveSettledWaiters(): void {
|
|
535
546
|
const result = sessionResult(this.textValue, this.rawOutput);
|
|
536
547
|
for (const waiter of [...this.waiters]) {
|
|
537
548
|
if (waiter.target <= this.settledCount) {
|
|
538
549
|
waiter.cleanup();
|
|
539
|
-
waiter.
|
|
550
|
+
const aborted = this.settlementAbortError(waiter.target);
|
|
551
|
+
if (aborted) waiter.reject(aborted);
|
|
552
|
+
else waiter.resolve(result);
|
|
540
553
|
}
|
|
541
554
|
}
|
|
542
555
|
}
|
|
@@ -573,6 +586,9 @@ export class ProcessBackedSubagentSession {
|
|
|
573
586
|
pending.resolve(event as RpcResponse);
|
|
574
587
|
return;
|
|
575
588
|
}
|
|
589
|
+
// Buffered lifecycle records after shutdown/failure must not resurrect a
|
|
590
|
+
// terminal session or resolve a waiter already rejected by that path.
|
|
591
|
+
if (this.stateValue === "stopped" || this.stateValue === "failed") return;
|
|
576
592
|
const text = rpcAssistantText(event);
|
|
577
593
|
if (text) {
|
|
578
594
|
this.textValue = text;
|
|
@@ -584,6 +600,9 @@ export class ProcessBackedSubagentSession {
|
|
|
584
600
|
if (event.type === "agent_settled") {
|
|
585
601
|
this.stateValue = "idle";
|
|
586
602
|
this.settledCount++;
|
|
603
|
+
// Pi 1.1.0 distinguishes an aborted run from a completed one. The child
|
|
604
|
+
// process is still alive/idle; cancel its result, not the whole session.
|
|
605
|
+
if (event.aborted === true) this.abortedSettlements.add(this.settledCount);
|
|
587
606
|
if (
|
|
588
607
|
this.activePrompt &&
|
|
589
608
|
this.activePrompt.target <= this.settledCount
|