pi-crew 0.11.20 → 0.11.22

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/index.mjs +537 -526
  3. package/package.json +2 -1
  4. package/skills/real-test-pi-crew/SKILL.md +21 -0
  5. package/src/errors.ts +14 -1
  6. package/src/extension/async-notifier.ts +42 -17
  7. package/src/extension/cross-extension-rpc.ts +42 -5
  8. package/src/extension/notification-router.ts +15 -2
  9. package/src/extension/registration/lifecycle-handlers.ts +36 -13
  10. package/src/extension/registration/runtime-cleanup.ts +17 -0
  11. package/src/extension/registration/wire-cross-extension.ts +26 -0
  12. package/src/extension/rpc-hmac.ts +53 -2
  13. package/src/extension/team-tool/cancel.ts +34 -21
  14. package/src/extension/team-tool/dispatch/index.ts +6 -5
  15. package/src/extension/team-tool/dispatch/status.ts +42 -2
  16. package/src/extension/team-tool.ts +24 -2
  17. package/src/prompt/inbox-poll.ts +77 -25
  18. package/src/prompt/message-tool.ts +62 -4
  19. package/src/prompt/prompt-runtime.ts +82 -32
  20. package/src/runtime/broker/crew-broker-client.ts +74 -16
  21. package/src/runtime/broker/crew-broker.ts +111 -77
  22. package/src/runtime/broker/protocol/msg-send-targets.ts +74 -0
  23. package/src/runtime/broker/wait-status-cache.ts +14 -5
  24. package/src/runtime/child-pi/child-pi-spawn.ts +47 -9
  25. package/src/runtime/child-pi/child-pi.ts +20 -2
  26. package/src/runtime/child-pi/mock-fixtures.ts +23 -0
  27. package/src/runtime/compaction/compact-stages/truncation-stage.ts +45 -3
  28. package/src/runtime/compaction/important-line-classifier.ts +85 -11
  29. package/src/runtime/compaction/tool-output-pruner.ts +35 -11
  30. package/src/runtime/crew-agent-records.ts +7 -1
  31. package/src/runtime/delivery-coordinator.ts +30 -0
  32. package/src/runtime/dwf-state-store.ts +41 -7
  33. package/src/runtime/finalize-run.ts +13 -2
  34. package/src/runtime/goal-workflow/dynamic-workflow-context.ts +192 -64
  35. package/src/runtime/goal-workflow/goal-loop-runner.ts +130 -3
  36. package/src/runtime/heartbeat/heartbeat-watcher.ts +7 -0
  37. package/src/runtime/live-session/embedded-tier.ts +3 -1
  38. package/src/runtime/live-session/live-session-runtime.ts +54 -1
  39. package/src/runtime/model/model-fallback.ts +23 -1
  40. package/src/runtime/output/pi-json-output.ts +26 -1
  41. package/src/runtime/policy-engine.ts +18 -0
  42. package/src/runtime/process/zombie-scanner.ts +78 -14
  43. package/src/runtime/process-status.ts +6 -1
  44. package/src/runtime/recovery/crash-recovery.ts +170 -28
  45. package/src/runtime/rpc/rpc-worker.ts +44 -1
  46. package/src/runtime/scheduling/run-coalesced-task-group.ts +64 -3
  47. package/src/runtime/scheduling/scheduler.ts +74 -11
  48. package/src/runtime/scratchpad/engine.ts +26 -3
  49. package/src/runtime/settings-store.ts +22 -2
  50. package/src/runtime/surface/tmux-provider.ts +24 -3
  51. package/src/runtime/task-health.ts +16 -1
  52. package/src/runtime/task-output-context.ts +143 -71
  53. package/src/runtime/task-packet.ts +70 -17
  54. package/src/runtime/task-runner/child-executor.ts +27 -0
  55. package/src/runtime/task-runner/output-splitter.ts +79 -16
  56. package/src/runtime/task-runner/post-execution.ts +75 -15
  57. package/src/runtime/task-runner/state-helpers.ts +92 -2
  58. package/src/runtime/usage-tracker.ts +11 -0
  59. package/src/runtime/verification/green-contract.ts +17 -0
  60. package/src/runtime/verification/verification-gates.ts +24 -6
  61. package/src/runtime/workspace-lock.ts +53 -0
  62. package/src/schema/team-tool-schema.ts +10 -4
  63. package/src/state/coordination/mailbox.ts +133 -38
  64. package/src/state/coordination/process-lock.ts +22 -2
  65. package/src/state/decision-ledger.ts +56 -8
  66. package/src/state/event-log/event-log-rotation.ts +83 -5
  67. package/src/state/event-log/event-log.ts +181 -12
  68. package/src/state/stores/active-run-registry.ts +10 -3
  69. package/src/state/types.ts +9 -1
  70. package/src/ui/agents-jobs-browser.ts +22 -8
  71. package/src/ui/dashboard-panes/agents-pane.ts +5 -2
  72. package/src/ui/events-state-source.ts +67 -28
  73. package/src/ui/inline-panel/agent-pane.ts +13 -6
  74. package/src/ui/inline-panel/agent-transcript.ts +33 -21
  75. package/src/ui/inline-panel/agent-view-overlay.ts +18 -3
  76. package/src/ui/inline-panel/index.ts +21 -1
  77. package/src/ui/live-run-sidebar.ts +14 -3
  78. package/src/ui/overlays/mailbox-compose-overlay.ts +10 -4
  79. package/src/ui/powerbar-publisher.ts +6 -3
  80. package/src/ui/rail.ts +7 -1
  81. package/src/ui/render-scheduler.ts +53 -1
  82. package/src/ui/run-action-dispatcher.ts +68 -20
  83. package/src/ui/status-colors.ts +9 -0
  84. package/src/utils/env-filter.ts +8 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,49 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ### Fixed (rounds 4–7 audit waves — DR-02/DR-03/DR-04, reports `docs/reviews/pi-crew-audit-round{4,5,6,7}-*.md`)
6
+
7
+ - **fix(state): DR-03 C1 — disk-terminal status preserved + delegate allocation max-merge** (R6-1 HIGH). `persistSingleTaskUpdate` reloaded disk inside the lock but `updateTask` replaced by id UNCONDITIONALLY — a detached runner's late settle overwrote a crash-recovery `cancelled` task with `completed` and emitted `task.completed` after `crew.run.orphan_cancelled`. New `mergeTaskUpdateWithDisk()` preserves a differing disk-terminal status (result fields still recorded); post-execution derives the persisted status from the persist return (terminal events via `TERMINAL_TASK_EVENT_TYPES` — never `task.completed` over cancelled). R6-4: delegate budget reserve/roll-up now merges `tokensSpent=max(disk,incoming)` — the 1s heartbeat whole-record replace could no longer clobber a reservation (lost update), and refund over-subtraction self-heals.
8
+ - **fix(scheduling): DR-03 C2 — scheduled jobs survive restart** (R6-2 HIGH). The executor persisted the job snapshot while `lastStatus:'running'` THEN flipped memory-only — the documented `'updated'`-event persist flow did not exist, so after any restart `fire()`'s in-flight guard skipped EVERY occurrence forever, silently. Emit wiring persists updates; the executor updates FIRST (fresh list() read — also fixes I6 stale runCount) then persists; load-time normalization strips stale `running` markers. I3: `parseSchedule` rejects provably-impossible dom/month combos at parse. I4: cron labels state UTC.
9
+ - **fix(goal-workflow): DR-03 C3 — DWF cache key, gate exemptions, retry semantics, checkpoint ordering** (R6-3 HIGH). PERS-1 `computeCallId` hashed 5 of ~12 call-shaping fields — same prompt + different inputs returned iteration-1's cached result WITHIN a live run (and replay dropped structured+artifactPath); now keys all shaping opts (sorted). The DWF empty-settle gate lacked the patches/cancelled exemptions its 3 siblings have (legitimate patch-only steps misreported as 429 exhaustion). `ctx.retry` never retried `ok:false` (maxAttempts decorative) — `DwfRetryFailure` carries the result through `executeWithRetry`. Checkpoint (agentCount++ + onCheckpoint) now runs BEFORE the worktree-cleanup await; hydrated phases don't re-emit phase events; `DwfCheckpointState.version=1` rejects unknown versions.
10
+ - **fix(broker): DR-03 C4 — responses bypass drop cap, overflow resync-close, msg.send idempotency** (R6-5/R6-6). Request RESPONSE acks went through drop-newest — a saturated queue silently dropped wait/msg.send acks after the durable write ("question not delivered" while tasks.json said waiting); responses now queue past the cap (bounded by in-flight requests), fanout overflow closes with typed `protocol.close` reason `outbound-overflow-resync` (the previously write-only `needsResync` bit). `msg.send` gains a stable sha256 `requestId` (idempotent dedup) + 60s timeout. H5 wait-status-cache stamps post-load; H7 single-flight connect; H8 typed teardown rejects. Recipient matrix extracted to `broker/protocol/msg-send-targets.ts` (wc-gate).
11
+ - **fix(model): DR-03 C6 — chain-scoped provider env keys, fault-path usage, E008 wording, order validation** (R6-8/R6-9). Provider env scoping passed ONE model — a bare/undefined id got ZERO provider keys → non-retryable auth failure → chain broke though qualified fallbacks were queued. `modelChain` now threads from the attempt loop; keys scoped to the chain's providers UNION. Embedded-tier usage was captured only on the success return — faulted attempts (watchdog/hung-tool) contributed ZERO usage while the subprocess tier persists on error; `faultPathUsage()` guards all fault returns. E008 distinguishes early break from exhaustion; `PI_CREW_MODEL_FALLBACK_ORDER` validated `{parentFirst,asSet}`.
12
+ - **fix(compaction/output): DR-03 C7 + DR-04 — splitter last-wins, truncation budget, surrogate-safe slicing, digest accuracy** (R6-11/K6/K7/K8). Output-splitter was first-occurrence-wins with no ownership validation — a worker QUOTING another task's delimited block forged attribution with `strategy:'delimiter'` trust; now last-wins + structured warn (residual emit-then-quote documented). TruncationStage two-pass budget keeps ≤ maxChars incl. marker allowance; astral chars survive the 75% seam; pruner digest passes the REAL exit code through + firstErrorLine fallback.
13
+ - **fix(runtime): DR-02 P0 — empty-settle 3-path chain, ack/pickup split, workspace heartbeat, compaction lock** (R4-1/2/3, R5-1/2). `throwIfSettledWithoutAnswer` inside the runOnce per-attempt loop (retry + deadletter work). Worker inbox pickup/durable-ack split (`pollWorkerInbox(pickup,limit)` + `acknowledgeWorkerInboxDelivery` after send + `unmarkSeenInboxIds` on throw) — ack-before-delivery was a permanent silent loss with a false audit record; `INBOX_BATCH_MAX` overflow same class. `refreshWorkspaceLockHeartbeat` (ownership-guarded). events.jsonl compaction holds `.alock` in sync mode (nested mkdirlock→alock). needs_attention finalize surfacing + fail-closed catch; 3 unlocked tasks.json RMWs → withRunLockSync; steer truncate-with-marker at `sanitizeSteerMessage`; `countTokens` CJK-aware.
14
+ - **fix(recovery): DR-04 W1 — purge/cancel liveness: task-heartbeat evidence + under-lock revalidation** (R7-1 HIGH). `purgeStaleActiveRunIndex` step 6 cancelled LIVE dynamic-workflow foreground runs — `hasRecentLifeEvidence` checked only `manifest.updatedAt` + `heartbeat.json`, but DWF foreground never writes heartbeat.json and the manifest stays frozen during a long subagent step → false corpse → cancel → resume double-dispatch. Step 6 now requires NO task-level activity (extracted `hasRecentTaskActivity()`, symmetric with `cancelOrphanedRuns`); all three lock paths re-verify fresh liveness under the lock (resume-adoption race aborts with `crew.run.orphan_skip` reason=fresh_liveness_under_lock). M-4: `applyRecoveryPlan` filters its reset set to fresh non-terminal tasks.
15
+ - **fix(event-log): DR-04 W2 — inline compaction append-guarded at all 5 sites** (R7-2 HIGH — a NEW instance of the DR-02 R4-2 class: that fix only landed in `compactPreparedEventLog`). The 5 inline `prepareCompaction`+`applyCompactionUnlocked` sites had no `.alock` and no `readTailSince` merge — an async-family appender parked between open and appendFile wrote to the orphaned inode (silent event loss incl. terminal events → double-execution). New `applyCompactionAppendGuarded()` (deadlock-aware: acquires `.alock` nested under `.mkdirlock` unless the caller already holds it; batch site is dual-context via a threaded flag); every apply merges the tail first. Live R4-2 repro test: parked contender's events survive, file never renamed.
16
+ - **fix(rpc): DR-04 W3 — live-control op gate, nonce replay cache, channel binding, broker stop wiring** (R7-9/10/11). `pi-crew:live-control` bypassed the read-only RPC policy its own module declares — unauthenticated steer/stop from any in-process extension; operations now gate (all live-control ops mutate → rejected without a valid HMAC). rpc-hmac nonce cache (bounded FIFO 512, anti-poison) kills replay within the validity window; `withHmacVerification` binds the arrival channel. `brokerController.stop()` had zero call sites despite the lifecycle docblock — `cleanupRuntime` now stops the broker (idempotent). Cancel kills the async runner pid + terminates live agents only after the in-lock fresh read (a completed run sent SIGTERM from the pre-lock snapshot).
17
+ - **fix(ui): DR-04 W4 + DR-02 UI cluster — render max-delay cap, paste input, surrogate backspace, grapheme truncation** (R7-12/14). RenderScheduler re-armed the debounce on every event and the fallback refused to render while events were "recent" — sustained bursts starved ALL rendering exactly when users watch; `pendingSince`+maxDelay (≥max(3×debounce,250ms)) force-flushes. Mailbox-compose dropped multi-char paste chunks (`data.length===1` gate); surrogate-safe backspace both editors; ad-hoc slice() → grapheme-aware truncLine; openPane double-mount guard; events-state-source stamps only after a successful resync scan. DR-02: manifest cache, blocked grace, `runId::taskId` keying, rail/status vocab, countdown.
18
+ - **fix(mailbox): DR-04 W5 — archived-message replies, flush containment, EPERM=alive, health stall wiring** (R7-3/4/5/15). `updateMailboxMessageReply` scanned only live files while rotation moves originals to archives — replies to rotated messages silently dropped; archives now scanned and a compensating record (same id + reply metadata) lands in the LIVE file (reader merges by id last-wins, archives immutable). `NotificationRouter.flush()` deliver was unwrapped in a setTimeout (throw = uncaughtException + lost batch) → `deliverSafely`. `replyDeadline` schema description now states advisory truth (broker-clamped `task.waiting.deadline` is the enforced one). active-run-registry EPERM branch returned dead on BOTH paths contradicting its comment — live-but-protected pids were dropped. Stalled-task health derives from `task.heartbeat` age and `finalize-run` now passes heartbeats into the snapshot. `validateMailbox` covers task mailboxes.
19
+ - **fix(verify): DR-04 W6 — checkpoint source of truth, tmux 2-strike, zombie starttime, tolerant ledger, output cap** (R7-8/13). `status domain=checkpoint` always answered "No checkpoint found." (the checkpoint store has zero production writers — real checkpoints live in `task.checkpoint`); the action reads tasks.json PRIMARY + store fallback and reports the source. Any single tmux `list-panes` throw (EAGAIN/EMFILE under load) permanently fired `mux-dead` for every watched pane → revoke + headless re-dispatch while workers lived; strike counter (≥2 consecutive) fires. Zombie rows carry starttime/age + kill-promptly hint (PID-reuse window). decision-ledger readers tolerate a truncated tail; verification gate command output capped while streaming (was unbounded until the post-hoc slice); scratchpad restoreState gates unknown snapshot versions.
20
+
21
+ ### Fixed (DR-01 — kept from previous Unreleased entry)
22
+
23
+ ### Fixed
24
+
25
+ - **fix(runtime): P0 false-green — worker 429-exhaustion (empty settle) now fails on ALL three execution paths** (DR-01, deep review 2026-10-14). Upstream rate-limit exhaustion ends a pi session as a chain of EMPTY assistant `message_end` events (content `[]`, usage 0) between auto-retry cycles → `agent_settled`, exit 0, NO `stopReason:"error"` — the legacy 429 detector never fired, and `finalText` kept an early pre-outage sentence ("finalText lies"). Live evidence: runs `team_20261014082741` / `team_20261014093334` marked 7/7 tasks `completed` with zero findings. Fix per path: (1) task-runner attempt loop — new `ParsedPiJsonOutput.settledWithoutAnswer` (parser tracks the LAST assistant message_end) + an `attemptErrorFor` branch → retryable error → model-fallback advance / task `failed`; (2) RPC transport — no transcript file there, so `runRpcWorker` stamps the same error from live settle tracking (abort-gated; `rpcAgentStarted` keeps the stdio-fallback predicate honest); (3) DWF `ctx.agent()` — calls `runWorker` directly, bypassing the attempt loop; the step gate now also fails on `settledWithoutAnswer`. New mock seam `PI_TEAMS_MOCK_CHILD_PI=json-429-empty-settle` reproduces the live shape; 19 new tests incl. a pre-fix counterfactual pin and replay verification against both historical failed runs.
26
+
27
+ ### Docs
28
+
29
+ - **docs(src): truth fixes + drift gates (F2/F3)**. `crew-broker.ts` header still described the "PHASE 0 skeleton (hello/ping only, NO persistence, NO children)" — rewritten to the real 12-method dispatch surface (+ history note). Action-count comments claimed "54-action"/"9"/"16" while the schema holds 57 (10+17+7+17+6) — fixed, and `team-tool-docs-sync.test.ts` gains a source-comment gate: numeric-claim scan + `ACTION_TO_DOMAIN`↔schema set-compare (comment drift can no longer go undetected). Records: `docs/reviews/pi-crew-deep-review-2026-10-14.md` + `docs/specs/DR-01-deep-review-fixes-2026-10-14.md` (SDD, 4 milestones, all gates green).
30
+
31
+ ### Chore
32
+
33
+ - **chore(test): `npm run test:coverage`** — optional `node --experimental-test-coverage` mirror of `test:unit` (deliberately NOT in `ci` yet). Housekeeping: 487 legacy `docs/perf-report-team_*.md` (gitignored local debris, never tracked) moved to `docs/archive/perf-report-team_/`.
34
+
35
+ ## [0.11.21] — CI matrix fixes: windows child env, macOS takeover probe (2026-10-14)
36
+
37
+ 0.11.19/0.11.20 shipped with red GitHub Actions CI (local `npm run ci` is linux-only and had passed; publish-before-CI-green is now a closed process gap — the real-test skill's Release gate section makes GH-Actions-green mandatory before publish).
38
+
39
+ ### Fixed
40
+
41
+ - **fix(state): macOS transient-refusal takeover — 3-strike corpse probe** (`process-lock.ts`). On file-socket platforms a single ECONNREFUSED flipped a LIVE holder into "corpse": the contender DELETED the live socket file and rebound, stealing the lock (CI-caught: a foreign child reported SOCKET_FREE while the parent held). macOS can return ECONNREFUSED transiently against a live listener (momentarily full backlog). Takeover now requires 3 consecutive refusals 150ms apart; Linux (abstract sockets) and Windows (named pipes) take no probe path — unchanged.
42
+ - **fix(test): windows child env — OS plumbing keys** (`run-lock-socket.test.ts` `childEnv`). A stripped `{PATH,HOME}` env cannot initialize Winsock on Windows — the spawned child died before printing (U21-5/U21-6 failed with empty stdout on windows-latest). Pass through SYSTEMROOT/SYSTEMDRIVE/WINDIR/COMSPEC/PATHEXT/TEMP/TMP/USERPROFILE/USERNAME (OS plumbing, never lock-carriers — the U21-6 env probe filters `^PI_(CREW|TEAMS)_.*LOCK` and stays meaningful).
43
+
44
+ ### Docs
45
+
46
+ - **docs(skill): Release gate section (canonical process)** — battery → version → push (pre-push local unit) → **GitHub Actions 3-OS GREEN** → only then npm publish + GitHub release. Publish-before-green is what shipped two red-CI releases.
47
+
5
48
  ## [0.11.20] — follow-up packets: duplicate-notification idle-gate, 5-field usage ledger, uid-namespaced locks, read-only goal verdict (2026-10-14)
6
49
 
7
50
  Follow-up wave on `v0.11.19` closing the deferred GATES items + one live-caught UX bug. SDD record: `../sdd-pi-durable-followup-2026-10-14/`. 12 commits on `f10da4cc`, merged fast-forward.