pi-crew 0.11.47 → 0.11.49
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/.githooks/pre-push +15 -0
- package/CHANGELOG.md +92 -0
- package/dist/index.mjs +440 -440
- package/package.json +1 -1
- package/skills/real-test-pi-crew/SKILL.md +5 -0
- package/src/config/env-allowlist-registry.ts +2 -2
- package/src/config/env-vars.ts +1 -1
- package/src/extension/autonomous-policy.ts +8 -1
- package/src/extension/knowledge-injection.ts +5 -1
- package/src/prompt/prompt-runtime.ts +11 -3
- package/src/prompt/surface-worker.ts +1 -1
- package/src/runtime/broker/crew-broker-child.ts +63 -28
- package/src/runtime/broker/crew-broker-delegate-request.ts +5 -5
- package/src/runtime/broker/stdin-handshake.ts +113 -17
- package/src/runtime/child-pi/child-pi-spawn.ts +88 -32
- package/src/runtime/child-pi/child-pi.ts +32 -13
- package/src/state/stores/state-store.ts +34 -0
package/.githooks/pre-push
CHANGED
|
@@ -51,6 +51,21 @@ if [ "$pushes_main" = "1" ]; then
|
|
|
51
51
|
echo "[pre-push] Fix the failures above, or bypass with 'git push --no-verify' (CI still gates)."
|
|
52
52
|
exit 1
|
|
53
53
|
fi
|
|
54
|
+
|
|
55
|
+
# Pre-publish battery (2026-10-28, born from the v0.11.46/0.11.47
|
|
56
|
+
# silent-worker incident): the LIVE real-Pi gate. One command, tiers
|
|
57
|
+
# path-triggered from the diff (T1/T3/T11j always; T7 spawns a real
|
|
58
|
+
# pi -p fast-fix smoke when spawn-affecting paths changed). Evidence lands
|
|
59
|
+
# in .crew/audit/battery.jsonl. There is NO tier selection here on purpose —
|
|
60
|
+
# a gate the agent may choose to skip is a gate that will be skipped.
|
|
61
|
+
# No pi binary (CI-like hosts): PI_CREW_BATTERY_SKIP=1 records the skip in
|
|
62
|
+
# the audit trail; a silent skip fails.
|
|
63
|
+
echo "[pre-push] push to main: running the pre-publish battery (live real-Pi gate)…"
|
|
64
|
+
if ! node scripts/pre-publish-battery.mjs; then
|
|
65
|
+
echo "[pre-push] ✋ push blocked: pre-publish battery RED (see .crew/audit/battery.jsonl)."
|
|
66
|
+
echo "[pre-push] Gate KHÔNG CHẠY = KHÔNG PUBLISH — fix, or bypass with 'git push --no-verify' (CI still gates) and record WHY."
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
54
69
|
fi
|
|
55
70
|
|
|
56
71
|
echo "[pre-push] gates green."
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,97 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.49] — 0.11.46/47 incident ROOT-CAUSED & CLOSED: fd0 handshake race → fd3 dedicated pipe; T7 nonce binding; review fix wave (2026-10-28)
|
|
4
|
+
|
|
5
|
+
**Root cause (dynamically confirmed 5/5 + on-disk leak proof).** pi's `readPipedStdin` (main.js:52-65) consumes ALL
|
|
6
|
+
piped stdin as PROMPT CONTENT in every mode except `--mode rpc`. The v0.11.46 stdin handshake rode fd0 and raced it
|
|
7
|
+
three ways: (1) token JSON merged into the worker prompt — **secret leak, proven in a live transcript** (the model
|
|
8
|
+
reasoned about the credential line); (2) stream-ended creds-less silence; (3) our reader's `pause()` before EOF
|
|
9
|
+
drain deadlocking pi's boot await → **self-initiated exit 0** (not parent SIGTERM as first modeled) with
|
|
10
|
+
`takeOverStdout`-buffered ~0-byte stdout — the exact incident signature (~1.2s, 0 stdout, ×3 retry).
|
|
11
|
+
|
|
12
|
+
**Fix:** brokered child-pi workers now get a DEDICATED pipe as stdio entry 4 (fd3); the payload is written there
|
|
13
|
+
and read by `readFirstLineFromFd`. fd0 returns to `"ignore"` (pre-0.11.46 shape), killing every race ordering. The
|
|
14
|
+
detached background RUNNER keeps fd0 (plain node process, no race). Gate stays OPT-IN (`PI_CREW_BROKER_STDIN=1`);
|
|
15
|
+
default route bit-for-bit unchanged. **Evidence:** pre-fix gate-ON matrix on pi@1.0.4: 5/5 silent death; post-fix
|
|
16
|
+
2/2 alive (106-110 KB stdout, ~3.2 s, zero leak) — full inversion. Live-worker integration 2/2 on pi 1.4.0 with
|
|
17
|
+
REPO-SRC extension proven via argv capture; security review 9/9 invariants.
|
|
18
|
+
|
|
19
|
+
**Also:** `maxTasksPerRun` is now enforced at manifest admission (pre-dispatch) instead of only at finalize —
|
|
20
|
+
oversized manifests were fully dispatched and >~2.5k-task handshake lines silently lose broker creds (writer now
|
|
21
|
+
warns >60 KiB). Worker-side fallback gate reads the RESOLVED env (`options.env ?? process.env` via `getCrewEnv`)
|
|
22
|
+
— closing the test seam that masked the earlier dead-wire. `check:env-vars` registry compliance for all 8 broker
|
|
23
|
+
identity reads; fd-mechanics tests skip on win32 (reader short-circuits there by design; brokered spawn is
|
|
24
|
+
auto-disabled on Windows).
|
|
25
|
+
|
|
26
|
+
### Also in this release (earlier 2026-10-28) — cold-verified review fix wave (2026-10-28)
|
|
27
|
+
|
|
28
|
+
**Fixed (MAJOR) — the `PI_CREW_BROKER_STDIN=1` opt-in route was dead-wired end-to-end (review + 3-agent cold-verification, dynamically proven).**
|
|
29
|
+
Two compounding defects: (1) the parent never re-wrote the flag into the child's `built.env` (registry kind `host-only` strips
|
|
30
|
+
the ambient value), so the worker-side mirror gate (`resolveBrokerStdinFallbackEnabled`, which reads the child's OWN
|
|
31
|
+
`process.env`) could never activate — the stdin payload was written and never read, leaving every opt-in worker creds-less
|
|
32
|
+
(`NOOP_HANDLE`: no ask/message/mailbox/steer, no broker audit). (2) the token was omitted from `built.env` for ALL
|
|
33
|
+
branches, but only the headless-JSON branch delivers the payload — RPC (`child-pi-rpc.ts`) and surface-pane spawns compose
|
|
34
|
+
without it, so their children got neither token nor payload. Unit tests masked both: they flip the gate by mutating
|
|
35
|
+
`process.env` in-process, bypassing the spawn-env plumbing (structural seam: `startChildBrokerClientAsync` honors
|
|
36
|
+
`options.env` for identity vars but the gate reads ambient `process.env`).
|
|
37
|
+
|
|
38
|
+
Repair: `prepareSpawnContext` now writes `PI_CREW_BROKER_STDIN="1"` into `built.env` when the host gate is ON (registry
|
|
39
|
+
kind → `child-write`) and puts the token in `built.env` for every brokered spawn; only the headless compose (payload
|
|
40
|
+
present) strips it from the FINAL env (`buildFinalChildPiSpawnOptions`), so RPC/surface keep the env route — a token in
|
|
41
|
+
their env also disarms their stdin fallback (their stdin is the JSONL protocol / a TTY). Gate OFF: bit-for-bit legacy.
|
|
42
|
+
New composed-path tests assert the flag/token/payload matrix across all three call sites (no live-reader assertions — the
|
|
43
|
+
0.11.46 reader exit-0 death is still open, default stays OPT-IN). Misleading docblocks + the factually-wrong registry
|
|
44
|
+
rationale ("parent decision fully determines the route") corrected. `argvIndicatesRpcMode` now also matches the
|
|
45
|
+
`--mode=rpc` equals-form.
|
|
46
|
+
|
|
47
|
+
**Fixed (MAJOR) — T7 live-smoke judged the WRONG run (newest-mtime race → false PASS).** The battery's T7 tier picked
|
|
48
|
+
`candidates[0]` (newest run dir ≥ start−60s) with zero binding to its own dispatch — a concurrent team run created in the
|
|
49
|
+
window got judged instead (two real run dirs on this host measured 25 ms apart), and the `pi -p` dispatch stdout/stderr
|
|
50
|
+
was piped with zero listeners, so a silent smoke death left no forensics. Now the dispatched goal carries a per-invocation
|
|
51
|
+
nonce (`[t7-nonce:<uuid>]`); only run dirs whose `events.jsonl` contain that nonce are judged, zero nonce dirs = FAIL, a
|
|
52
|
+
non-terminal run is a FAIL checked before the killed/hung diagnosis, and a capped tail (64 KB) of the dispatch's own
|
|
53
|
+
stdout/stderr is recorded in the failure reason, audit entry, and log output.
|
|
54
|
+
|
|
55
|
+
**Fixed (minor) — battery gate fail-open trigger paths.** `changedPaths()` now filters diff-base tags to release semver
|
|
56
|
+
(`^v\d+\.\d+\.\d+$` — a near-HEAD non-release tag previously shrank the diff window and silently un-triggered T7) and
|
|
57
|
+
parses `git status --porcelain` renames (`R old -> new`) into BOTH paths instead of one mangled string.
|
|
58
|
+
`prepublish-ci.mjs` now preserves `PI_CREW_BATTERY_SKIP` through the npm `prepublishOnly` path (the documented skip
|
|
59
|
+
hatch was dead there — the battery's own failure message recommended a var its caller stripped).
|
|
60
|
+
|
|
61
|
+
**Changed — T7 trigger coverage gaps closed** (`src/state|utils|agents|teams|workflows|hooks|observability`, `scripts/`):
|
|
62
|
+
cold-verification found the workflow-engine src subtrees and the gate's own scripts/ were invisible to the live gate; gate
|
|
63
|
+
changes now live-test the gate (this commit deliberately triggers T7 on its own pre-push).
|
|
64
|
+
|
|
65
|
+
**Changed — `before_agent_start` `systemPrompt` hardening (defense-in-depth).** Cold-verification refuted reachability
|
|
66
|
+
(`systemPrompt` is required in every host type def and synthesized by a runner getter, unlike `prompt` which arrives via
|
|
67
|
+
caller args — the GH #64 root cause), but the asymmetry was real: `autonomous-policy.ts` and `knowledge-injection.ts`
|
|
68
|
+
now coerce non-strings to `""` (append-only output, safe); `prompt-runtime.ts` SKIPS the rewrite instead (coercing `""`
|
|
69
|
+
there would blank a real system prompt — it strips sections). No behavior change on any current host.
|
|
70
|
+
|
|
71
|
+
**Added — LOUD oversize diagnostic in `buildBrokerStdinLine`.** The reader aborts past 64 KiB without a newline; a
|
|
72
|
+
~2.5k-task manifest (~131 KiB measured live — pipe chunks make the effective Linux boundary ~2× the coded cap)
|
|
73
|
+
silently stripped EVERY task's broker creds. The writer now warns at 60 KiB via `logInternalError` (not PI_CREW_DEBUG-
|
|
74
|
+
gated); the line is still written, never blocked.
|
|
75
|
+
|
|
76
|
+
## [0.11.48] — HOTFIX: stdin broker-credential gate flipped OPT-IN — fixes silent worker death (2026-10-28)
|
|
77
|
+
|
|
78
|
+
**Fixed — v0.11.46/0.11.47 regression: every headless JSON worker on the default route exited 0 with zero stdout.**
|
|
79
|
+
The LOW-1 stdin handshake shipped in 0.11.46 with the gate default-ON. On real Pi hosts, the moment the worker-side
|
|
80
|
+
stdin reader activated (`startChildBrokerClientAsync` → `readStdinFirstLine`: env broker identity present, env token
|
|
81
|
+
absent, gate on — the default shape of every team-run worker), the whole `pi --mode json -p` process exited 0 silently
|
|
82
|
+
(~1.2s, 0 stdout bytes, retry ×3, run failed). CI never caught it: unit tests inject the `readStdinLine` seam and never
|
|
83
|
+
run `process.stdin` inside a real pi process; the live battery (real-test-pi-crew T7) caught it — which is also why this
|
|
84
|
+
release ships with that battery re-run green (3/3 tasks + `message` round-trip through the legacy env route).
|
|
85
|
+
|
|
86
|
+
`PI_CREW_BROKER_STDIN` is now **OPT-IN** (`"1"` enables the stdin route; unset/`0`/anything else keeps the legacy env
|
|
87
|
+
route on BOTH sides, bit-for-bit pre-0.11.46 behavior). The LOW-1 closure code (writer, reader, payload v2) stays for
|
|
88
|
+
re-enabling after the reader path is root-caused.
|
|
89
|
+
|
|
90
|
+
Also adds `test/integration/broker-stdin-reader-liveworker.test.ts` — a LIVE regression test that spawns a real `pi`
|
|
91
|
+
worker with the exact env shape that died and asserts non-zero stdout (skips where no `pi` binary exists, e.g. CI
|
|
92
|
+
containers that run the fake-pi fixtures), plus incidental biome fixes (`useImportType` ×5, `noUselessTernary`) in
|
|
93
|
+
crew-broker-delegate-request.ts / surface-worker.ts.
|
|
94
|
+
|
|
3
95
|
## [0.11.47] — live-session split: model/prompt clusters out of live-session-runtime.ts (2026-10-27)
|
|
4
96
|
|
|
5
97
|
**Changed — `live-session-runtime.ts` (1971 → 1634 lines).** The file sat 29 lines under the 2000-line wc-gate LIMIT,
|