pi-crew 0.11.49 → 0.11.50

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.50] — LOW-1: PI_CREW_BROKER_STDIN fd3 handshake DEFAULT-ON ('0' opts out) (2026-10-28)
4
+
5
+ **Changed (LOW-1 gate flip).** The broker-credential fd3 handshake route is now **DEFAULT-ON**: both gates
6
+ (`child-pi-spawn.resolveBrokerStdinHandshakeEnabled`, `crew-broker-child.resolveBrokerStdinFallbackEnabled`) flipped
7
+ from `=== "1"` (opt-in, v0.11.48–0.11.49) to `!== "0"` — only the exact value `0` opts out (rollback to the
8
+ bit-for-bit legacy env route, unchanged contract). Safe now because the v0.11.49 fd3 dedicated-pipe transport removed
9
+ the fd0/readPipedStdin race that caused the 0.11.46/47 silent-worker-death incident. The FIX-1 spawner write of
10
+ `"1"` stays (pins the child route deterministically + keeps a default-ON parent compatible with pre-flip workers
11
+ whose mirror gate still reads `=== "1"`); an opted-out parent keeps the token in the child env, which independently
12
+ disarms the worker reader. New live-worker integration pin: DEFAULT (gate unset) + fd3 payload → alive + no leak
13
+ (unit seams are structurally blind to this failure class — run the real-test battery T7 before publishing).
14
+
3
15
  ## [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
16
 
5
17
  **Root cause (dynamically confirmed 5/5 + on-disk leak proof).** pi's `readPipedStdin` (main.js:52-65) consumes ALL
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ const require = __piCrewCreateRequire(import.meta.url);
5
5
  const module = { exports: {} };
6
6
  const exports = module.exports;
7
7
 
8
- var Kge=Object.defineProperty;var v=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Re=(e,t)=>{for(var n in t)Kge(e,n,{get:t[n],enumerable:!0})};function z(e,t=process.env){let n=pg[e];if(!n?.mirror||n.mirrorPrecedence==="or")return t[e];let r=e.startsWith("PI_TEAMS_")?n.mirror:e,o=e.startsWith("PI_TEAMS_")?e:n.mirror;return n.mirrorPrecedence==="teams"?t[o]??t[r]:t[r]??t[o]}function uq(e){let t=pg[e],n=z(e),r=()=>t?.default;if(n===void 0)return r();if(!t?.parser||t.parser==="string")return n;switch(t.parser){case"int":{let o=Number.parseInt(n,10);return Number.isNaN(o)?r():o}case"number":{let o=Number(n);return Number.isNaN(o)?r():o}case"boolean":{let o=n.trim().toLowerCase();return o==="1"||o==="true"||o==="yes"?!0:o==="0"||o==="false"||o==="no"?!1:r()}case"json":try{return JSON.parse(n)}catch{return r()}}return n}function dr(e){let t=uq(e);return typeof t=="boolean"?t:void 0}function Yc(e){let t=uq(e);return typeof t=="number"?t:void 0}function dq(e){return z(e)}var pg,Pe=v(()=>{"use strict";pg={PI_CREW_HOME:{name:"PI_CREW_HOME",mirror:"PI_TEAMS_HOME",mirrorPrecedence:"teams",doc:"pi-crew home dir; PI_TEAMS_HOME wins (config.ts:174, paths.ts:58, crew-vibes/config.ts:16)"},PI_TEAMS_HOME:{name:"PI_TEAMS_HOME",mirror:"PI_CREW_HOME",mirrorPrecedence:"teams",doc:"pi-crew home dir \u2014 primary of the TEAMS??CREW pair"},PI_CREW_DEPTH:{name:"PI_CREW_DEPTH",mirror:"PI_TEAMS_DEPTH",mirrorPrecedence:"crew",default:0,parser:"int",doc:"current crew depth (pi-args.ts:72 reads CREW ?? TEAMS ?? '0')"},PI_TEAMS_DEPTH:{name:"PI_TEAMS_DEPTH",mirror:"PI_CREW_DEPTH",mirrorPrecedence:"crew",default:0,parser:"int",doc:"crew depth mirror (fallback of PI_CREW_DEPTH)"},PI_CREW_MAX_DEPTH:{name:"PI_CREW_MAX_DEPTH",mirror:"PI_TEAMS_MAX_DEPTH",mirrorPrecedence:"crew",parser:"int",doc:"max crew depth, clamped 1..10 (pi-args.ts:78)"},PI_TEAMS_MAX_DEPTH:{name:"PI_TEAMS_MAX_DEPTH",mirror:"PI_CREW_MAX_DEPTH",mirrorPrecedence:"crew",parser:"int",doc:"max crew depth mirror (fallback of PI_CREW_MAX_DEPTH)"},PI_CREW_ROLE:{name:"PI_CREW_ROLE",mirror:"PI_TEAMS_ROLE",mirrorPrecedence:"crew",doc:"crew role name (role-permission.ts:46 reads CREW?.trim() || TEAMS?.trim())"},PI_TEAMS_ROLE:{name:"PI_TEAMS_ROLE",mirror:"PI_CREW_ROLE",mirrorPrecedence:"crew",doc:"crew role mirror (fallback of PI_CREW_ROLE)"},PI_CREW_VERIFICATION_PRESERVE_ENV:{name:"PI_CREW_VERIFICATION_PRESERVE_ENV",mirror:"PI_TEAMS_VERIFICATION_PRESERVE_ENV",mirrorPrecedence:"crew",doc:"extra env keys to preserve in verification commands (CREW ?? TEAMS ?? '')"},PI_TEAMS_VERIFICATION_PRESERVE_ENV:{name:"PI_TEAMS_VERIFICATION_PRESERVE_ENV",mirror:"PI_CREW_VERIFICATION_PRESERVE_ENV",mirrorPrecedence:"crew",doc:"verification preserve-env mirror (fallback of PI_CREW_...)"},PI_CREW_VERIFICATION_WORKTREE:{name:"PI_CREW_VERIFICATION_WORKTREE",mirror:"PI_TEAMS_VERIFICATION_WORKTREE",mirrorPrecedence:"crew",doc:"enable verification worktree sandbox; '1'/'true' (CREW ?? TEAMS)"},PI_TEAMS_VERIFICATION_WORKTREE:{name:"PI_TEAMS_VERIFICATION_WORKTREE",mirror:"PI_CREW_VERIFICATION_WORKTREE",mirrorPrecedence:"crew",doc:"verification worktree mirror (fallback of PI_CREW_...)"},PI_CREW_INHERIT_PROJECT_CONTEXT:{name:"PI_CREW_INHERIT_PROJECT_CONTEXT",mirror:"PI_TEAMS_INHERIT_PROJECT_CONTEXT",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit project context in worker prompts; CREW checked first (prompt-runtime.ts readBooleanEnvAny)"},PI_TEAMS_INHERIT_PROJECT_CONTEXT:{name:"PI_TEAMS_INHERIT_PROJECT_CONTEXT",mirror:"PI_CREW_INHERIT_PROJECT_CONTEXT",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit project context mirror (fallback of PI_CREW_...)"},PI_CREW_INHERIT_SKILLS:{name:"PI_CREW_INHERIT_SKILLS",mirror:"PI_TEAMS_INHERIT_SKILLS",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit skills in worker prompts; CREW checked first (prompt-runtime.ts readBooleanEnvAny)"},PI_TEAMS_INHERIT_SKILLS:{name:"PI_TEAMS_INHERIT_SKILLS",mirror:"PI_CREW_INHERIT_SKILLS",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit skills mirror (fallback of PI_CREW_...)"},PI_CREW_ADAPTIVE_REPAIR:{name:"PI_CREW_ADAPTIVE_REPAIR",mirror:"PI_TEAMS_ADAPTIVE_REPAIR",mirrorPrecedence:"or",doc:"'0' disables adaptive-plan repair; either name may disable (adaptive-plan.ts:434)"},PI_TEAMS_ADAPTIVE_REPAIR:{name:"PI_TEAMS_ADAPTIVE_REPAIR",mirror:"PI_CREW_ADAPTIVE_REPAIR",mirrorPrecedence:"or",doc:"adaptive-repair mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_BG_REPORT_ON_FATAL:{name:"PI_CREW_BG_REPORT_ON_FATAL",mirror:"PI_TEAMS_BG_REPORT_ON_FATAL",mirrorPrecedence:"or",doc:"'0' disables --report-on-fatalerror; either name may disable (async-runner.ts:127)"},PI_TEAMS_BG_REPORT_ON_FATAL:{name:"PI_TEAMS_BG_REPORT_ON_FATAL",mirror:"PI_CREW_BG_REPORT_ON_FATAL",mirrorPrecedence:"or",doc:"bg-report mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_EXECUTE_WORKERS:{name:"PI_CREW_EXECUTE_WORKERS",mirror:"PI_TEAMS_EXECUTE_WORKERS",mirrorPrecedence:"or",doc:"'0' disables real worker execution; either name disables (team-tool.ts:376, run.ts, runtime-resolver.ts:91)"},PI_TEAMS_EXECUTE_WORKERS:{name:"PI_TEAMS_EXECUTE_WORKERS",mirror:"PI_CREW_EXECUTE_WORKERS",mirrorPrecedence:"or",doc:"execute-workers mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_VERIFICATION_SANITIZE_ENV:{name:"PI_CREW_VERIFICATION_SANITIZE_ENV",mirror:"PI_TEAMS_VERIFICATION_SANITIZE_ENV",mirrorPrecedence:"or",doc:"'0' disables verification env sanitization; either name disables (verification-gates.ts:69)"},PI_TEAMS_VERIFICATION_SANITIZE_ENV:{name:"PI_TEAMS_VERIFICATION_SANITIZE_ENV",mirror:"PI_CREW_VERIFICATION_SANITIZE_ENV",mirrorPrecedence:"or",doc:"verification sanitize mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_WORKER_ATOMIC_WRITER:{name:"PI_CREW_WORKER_ATOMIC_WRITER",mirror:"PI_TEAMS_WORKER_ATOMIC_WRITER",mirrorPrecedence:"or",doc:"'1' enables the worker-thread atomic writer; either name enables (worker-atomic-writer.ts:166)"},PI_TEAMS_WORKER_ATOMIC_WRITER:{name:"PI_TEAMS_WORKER_ATOMIC_WRITER",mirror:"PI_CREW_WORKER_ATOMIC_WRITER",mirrorPrecedence:"or",doc:"worker-atomic-writer mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_ALLOW_MOCK:{name:"PI_CREW_ALLOW_MOCK",parser:"boolean",doc:"'1'/'true' allows PI_TEAMS_MOCK_CHILD_PI mock mode (mock-fixtures.ts:40)"},PI_CREW_TEST_ASYNC_INLINE:{name:"PI_CREW_TEST_ASYNC_INLINE",parser:"boolean",doc:"'1' runs async team runs IN-PROCESS instead of spawning a detached background-runner (test seam; requires PI_CREW_ALLOW_MOCK=1 \u2014 see async-runner.ts spawnBackgroundTeamRun)"},PI_CREW_ASYNC_RUN:{name:"PI_CREW_ASYNC_RUN",doc:"async telemetry marker STAMPED onto the detached runner env by buildBackgroundRunnerEnv (async-runner.ts), read by resolve-surface.ts SurfaceGateEnvSnapshot \u2014 NOT a surface gate (registry entry added with the C-1 env-allowlist registry 2026-10-11 \u2014 was a read/write site missing from this registry)"},PI_CREW_BACKGROUND_RUNNER_ENTRY:{name:"PI_CREW_BACKGROUND_RUNNER_ENTRY",parser:"boolean",doc:"'1' marks this process as a directly-invoked background-runner entry (set by async-runner spawn; module import without it skips main() \u2014 background-runner.ts entry guard)"},PI_CREW_PROMPT_BREAKDOWN:{name:"PI_CREW_PROMPT_BREAKDOWN",parser:"boolean",doc:"'1' writes a per-section prompt token breakdown artifact metadata/<task>.prompt-breakdown.json (SR-02 phase 1, prompt-builder.ts)"},PI_CREW_PROMPT_SKILLS:{name:"PI_CREW_PROMPT_SKILLS",doc:"'full' inlines complete skill bodies in worker prompts (SR-02 escape hatch); default injects compact index entries (name+description+path, read-on-demand)"},PI_CREW_ASYNC_EARLY_EXIT_GUARD:{name:"PI_CREW_ASYNC_EARLY_EXIT_GUARD",doc:"'0' skips the async-run early-exit guard (team-tool/run.ts:103)"},PI_CREW_BACKGROUND_MODE:{name:"PI_CREW_BACKGROUND_MODE",doc:"background-runner marker; READ at child-executor.ts:616 ('1'), WRITTEN at background-runner.ts:713/833 (= '1')"},PI_CREW_BROKER:{name:"PI_CREW_BROKER",doc:"'1'/'0' force broker enabled/disabled (defaults.ts:187, lifecycle-handlers.ts:908)"},PI_CREW_DEBUG:{name:"PI_CREW_DEBUG",doc:"truthiness enables background debug logging (background-runner.ts:63); RAW truthiness \u2014 '0'/'false' still ENABLE, unset to disable (R45-F4)"},PI_CREW_DEBUG_BUDGET:{name:"PI_CREW_DEBUG_BUDGET",doc:"'1' logs token budget (team-tool/run.ts:498)"},PI_CREW_DEBUG_STALE:{name:"PI_CREW_DEBUG_STALE",doc:"'1' writes a forensic sidecar log of every STALE verdict from the stale reconciler (stale-reconciler.ts:287) \u2014 the reconciler may run in ANY host process, hence a fixed env gate"},PI_CREW_DWF_SCRIPT_TIMEOUT_MS:{name:"PI_CREW_DWF_SCRIPT_TIMEOUT_MS",parser:"int",default:18e5,doc:"dynamic-workflow script timeout; default 30 min (dynamic-workflow-runner.ts:226)"},PI_CREW_DWF_SKIP_DETERMINISM_CHECK:{name:"PI_CREW_DWF_SKIP_DETERMINISM_CHECK",doc:"'1' disables the DWF determinism check (deterministic-ast.ts:60)"},PI_CREW_HEARTBEAT_PULSE_MS:{name:"PI_CREW_HEARTBEAT_PULSE_MS",parser:"int",default:15e3,doc:"child-executor liveness-pulse interval (ms): touches the worker heartbeat while the attempt's child process is alive even with no stdout events (NEW-4/G25 silent-turn freeze); \u22640 disables (child-executor.ts)"},PI_CREW_INTERRUPT_GUARD_INTERVAL_MS:{name:"PI_CREW_INTERRUPT_GUARD_INTERVAL_MS",parser:"int",default:1e3,doc:"interrupt-guard poll interval; default 1000ms (background-runner.ts:217)"},PI_CREW_WATCHDOG_INTERVAL_MS:{name:"PI_CREW_WATCHDOG_INTERVAL_MS",parser:"int",default:3e5,doc:"WD-INTERVAL (2026-10-23): foreground-run watchdog poll interval (ms), default 300000 = DEFAULT_CHECK_INTERVAL_MS (foreground-watchdog.ts resolveCheckIntervalMs). Lower it (e.g. 20000) to live-repro watchdog behavior without waiting 5 min per cycle; \u22640/garbage falls back to the default. An explicit checkIntervalMs argument still wins."},PI_CREW_MAX_RUN_MS:{name:"PI_CREW_MAX_RUN_MS",parser:"int",default:72e5,doc:"max background run duration; default 2h (background-runner.ts:31)"},PI_CREW_REAP_GROUP:{name:"PI_CREW_REAP_GROUP",parser:"boolean",default:!0,doc:"wave 11a kill switch for identity-proven PROCESS-GROUP reaping: '0'/'false' degrades child-pi-kill + orphan-reaper to their pre-wave pid-only paths (resolveReapGroupEnabled, reap-group.ts). FORWARDED to the detached background runner (env-allowlist-registry: PI_CREW_REAP_GROUP \u2192 background-runner) because the runner/worker process is the one that reaps its own children \u2014 an unforwarded switch would be silently inert there"},PI_CREW_REAP_RUN_DESCENDANTS:{name:"PI_CREW_REAP_RUN_DESCENDANTS",parser:"boolean",default:!0,doc:"wave 11b kill switch for the RUN-SCOPED descendant sweep: '0'/'false' makes collectRunDescendants/reapRunDescendants (run-descendant-sweep.ts) a complete no-op. FORWARDED to the detached background runner (env-allowlist-registry: \u2192 background-runner) for the same reason as PI_CREW_REAP_GROUP: the reconciler that drives the sweep also runs there. Never forwarded to a worker"},PI_CREW_MAX_WORKERS:{name:"PI_CREW_MAX_WORKERS",parser:"int",doc:"max concurrent workers; unset/empty/invalid \u2192 default max(2, cpus-2) (global-worker-cap.ts:33)"},PI_CREW_DISABLE_RESULT_READ_CACHE:{name:"PI_CREW_DISABLE_RESULT_READ_CACHE",doc:"'1' bypasses the per-run result-artifact read cache (R10-1 bypass control; task-output-context.ts createResultArtifactReadCache)"},PI_CREW_HANDOFF_BUDGET_TOKENS:{name:"PI_CREW_HANDOFF_BUDGET_TOKENS",parser:"int",doc:"est-token (chars/4) budget for the dynamic.dependencyContext prompt layer; beats runtime.handoffBudgetTokens, default 1800; resolved \u22640 or >1_000_000 disables the trim (task-output-context.ts renderDependencyOutputContext)"},PI_CREW_MOCK_LIVE_SESSION:{name:"PI_CREW_MOCK_LIVE_SESSION",doc:"'success' mocks a successful live-session (live-session-runtime.ts:558, runtime-resolver.ts:38/116)"},PI_CREW_PARENT_GUARD_INTERVAL_MS:{name:"PI_CREW_PARENT_GUARD_INTERVAL_MS",parser:"int",default:500,doc:"parent-guard poll interval; default 500ms (parent-guard.ts:73)"},PI_CREW_PARENT_PID:{name:"PI_CREW_PARENT_PID",parser:"int",doc:"parent process pid; >0 starts the parent guard (background-runner.ts:626)"},PI_CREW_SCRATCHPAD_DEMOTE_BASH:{name:"PI_CREW_SCRATCHPAD_DEMOTE_BASH",doc:"'1' demotes bash for scratchpad roles (agent-config.ts:199)"},PI_CREW_SKIP_HOME_CHECK:{name:"PI_CREW_SKIP_HOME_CHECK",doc:"'1' skips the PI_TEAMS_HOME escape check (config.ts:185)"},PI_CREW_SUPPRESS_RPC_WARNING:{name:"PI_CREW_SUPPRESS_RPC_WARNING",doc:"'1' suppresses the missing-RPC-secret warning (rpc-hmac.ts:145)"},PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS:{name:"PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS",doc:"'1' trusts project/.pi agent extensions (discover-agents.ts:445/544)"},PI_CREW_PLAN_UI:{name:"PI_CREW_PLAN_UI",doc:"'1' enables the Plan dashboard pane (7) + plans snapshot slice (WP-7/R7)"},PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC:{name:"PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC",parser:"boolean",doc:"'1'/'true' writes non-terminal tasks checkpoints best-effort (no fsync; 50ms coalesce kept, only durability skipped); terminal transitions stay full (defaults.ts, config.ts persistence.skipTasksFsync)"},PI_CREW_TRUST_PROJECT_DWF:{name:"PI_CREW_TRUST_PROJECT_DWF",doc:"'1' allows project-sourced .dwf.ts workflows (dynamic-workflow-runner.ts:189)"},PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS:{name:"PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS",parser:"int",doc:"child Pi response timeout, bounded 1s..3.6M ms (child-pi.ts:356)"},PI_TEAMS_DEBUG:{name:"PI_TEAMS_DEBUG",doc:"truthiness gates debug-severity internal-error logging (internal-error.ts:10); RAW truthiness \u2014 '0'/'false' still ENABLE, unset to disable (R45-F4)"},PI_TEAMS_MOCK_CHILD_PI:{name:"PI_TEAMS_MOCK_CHILD_PI",doc:"mock child-pi mode; values 'success'/'json-success'/'adaptive-plan' (mock-fixtures.ts:35)"},PI_TEAMS_PI_BIN:{name:"PI_TEAMS_PI_BIN",doc:"explicit pi binary path for spawned children (pi-spawn.ts:262)"},PI_CREW_MAX_OUTPUT:{name:"PI_CREW_MAX_OUTPUT",parser:"int",doc:"max output tokens cap for background workers (prompt-runtime.ts:228)"},PI_CREW_STEERING_FILE:{name:"PI_CREW_STEERING_FILE",doc:"steering JSONL path polled by the worker (prompt-runtime.ts:260)"},PI_CREW_KEYBINDINGS:{name:"PI_CREW_KEYBINDINGS",parser:"json",doc:"JSON keybinding override map (keybinding-map.ts:314/347)"},PI_CREW_RPC_SECRET:{name:"PI_CREW_RPC_SECRET",doc:"shared HMAC secret; READ at rpc-hmac.ts:26/41, WRITTEN/cleared at :31/:36"},PI_CREW_PEER_DEP_DIR:{name:"PI_CREW_PEER_DEP_DIR",doc:"parent-provided pi-coding-agent package dir hint (peer-dep.ts:81)"},PI_CREW_SCRATCHPAD:{name:"PI_CREW_SCRATCHPAD",doc:"'1' enables the scratchpad engine (scratchpad-lifecycle.ts:486/679, written by child-pi-spawn.ts)"},PI_CREW_KIND:{name:"PI_CREW_KIND",doc:"'subagent' marks child workers (scratchpad-lifecycle.ts:679, lifecycle-handlers.ts:892, zombie-scanner.ts:183)"},PI_CREW_TASK_ID:{name:"PI_CREW_TASK_ID",doc:"current task id for scratchpad/events (scratchpad-lifecycle.ts:90/227/280)"},PI_CREW_EVENTS_PATH:{name:"PI_CREW_EVENTS_PATH",doc:"events JSONL path for fire-and-forget metric events (scratchpad-lifecycle.ts:80)"},PI_CREW_ASK_ENABLED:{name:"PI_CREW_ASK_ENABLED",doc:"'1' enables the worker-side ask tool \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts per ADR-0 WP-2 item 2)"},PI_CREW_MSG_ENABLED:{name:"PI_CREW_MSG_ENABLED",doc:"'1' enables the worker-side message tool (D9/\xA715.2) \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts)"},PI_CREW_DELEGATE_ENABLED:{name:"PI_CREW_DELEGATE_ENABLED",doc:"'1' enables the worker-side delegate tool \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts; registry entry added with the C-1 env-allowlist registry 2026-10-11 \u2014 was a read/write site missing from this registry)"},PI_CREW_STATE_ROOT:{name:"PI_CREW_STATE_ROOT",doc:"run stateRoot for the ask-tool mailbox poll (<stateRoot>/mailbox; written by child-pi-spawn.ts, read by prompt-runtime.ts per ADR-0 WP-2 item 2)"},PI_CREW_BROKER_RUN_ID:{name:"PI_CREW_BROKER_RUN_ID",doc:"broker run id; aliases PI_CREW_RUN_ID (scratchpad-lifecycle.ts:81, crew-broker-child.ts:51)"},PI_CREW_AGENT_EVENTS_PATH:{name:"PI_CREW_AGENT_EVENTS_PATH",doc:"per-agent events log (<stateRoot>/agents/<taskId>/events.jsonl) written by the surface worker recorder (surface-worker.ts; derived by prepareSurfaceSpawn)"},PI_CREW_SESSION_RECOVERY:{name:"PI_CREW_SESSION_RECOVERY",parser:"boolean",doc:"W2 session-file recovery override for killed workers (exitCode null / killed): '0' disables, '1' forces on \u2014 beats ChildPiRunInput.sessionRecovery / runtime.sessionRecovery; default ON (session-recovery.ts:resolveSessionRecoveryEnabled)"},PI_CREW_HERMETIC_WORKERS:{name:"PI_CREW_HERMETIC_WORKERS",parser:"boolean",doc:"R3-19/D5 hermetic worker spawn override: '1' forces --no-extensions on worker argv, '0' restores the old ambient-stack parity (discovered/configured/built-in extensions load); beats BuildPiWorkerArgsInput.hermeticWorkers / runtime.hermeticWorkers; default ON (pi-args.ts:resolveHermeticWorkers). Explicit -e prompt-runtime + agent extensions + --skill flags are unaffected (cli.md:186-191)"},PI_CREW_TOOLS_MODIFIERS:{name:"PI_CREW_TOOLS_MODIFIERS",parser:"boolean",doc:"L2-3 (pi 1.1.0 `+name`/`-name` --tools modifier syntax) OPT-IN, default OFF: '1' emits a declared agent tool list as --tools +a,+b (default toolset PLUS the declarations) instead of the legacy replacement allowlist, but ONLY when the probed host piVersion >= 1.1.0 (PI_TOOLS_MODIFIER_FLOOR, pi-args.ts); below the floor or unknown version falls back to the legacy syntax (pi's strict parser + getToolListError must never see modifiers on a pre-1.1.0 host). Additive semantics: the worker keeps the FULL default tool surface (incl. runtime-registered control tools), so CONTROL_TOOLS are not merged in modifier mode (pi-args.ts)"},PI_CREW_STATE_BACKEND:{name:"PI_CREW_STATE_BACKEND",doc:"U8 (upgrade-spec 2026-10-09) run-state store backend select: 'sqlite' stores manifest+tasks+worker-status in <runId>/state.sqlite via node:sqlite with ONE transaction per batch write (all-or-nothing; manifest.json/tasks.json stay written as read-side mirrors for legacy consumers \u2014 loads read the db); unset or any other value keeps the default JSON file backend. events.jsonl audit spine unchanged in both modes (state-store.ts / sqlite-run-state.ts isSqliteStateBackend)"},PI_CREW_RUN_LOCK_SOCKET:{name:"PI_CREW_RUN_LOCK_SOCKET",parser:"boolean",default:!0,doc:"U21 (upgrade 2026-10-13) run-lock dual-guard toggle: '0'/'false'/'no' disables the kernel process-socket SUPPLEMENT on the ASYNC withRunLock path (pre-U21 file-lock-only behaviour, bit-for-bit); default ON. Host-process read only \u2014 never passed to children (the socket name is derived from the run dir path). The SYNC withRunLockSync path is always file-only by design (locks.ts acquireRunLockGuarded)"},PI_CREW_CHILD_PI_MODE:{name:"PI_CREW_CHILD_PI_MODE",doc:"U14 (upgrade-spec 2026-10-09) child-pi protocol v2 transport select: 'rpc' spawns task workers over pi --mode rpc (SDK RpcClient wire \u2014 steer receipts, clear_queue hygiene, multi-prompt reuse; child-pi-rpc.ts), 'json' / unset keeps the --mode json -p stdio path (phase-1 default; both modes fully regression-tested). Surface TUI panes are unaffected"},PI_CREW_CHILD_PI_POOL:{name:"PI_CREW_CHILD_PI_POOL",parser:"boolean",doc:"U14 rpc worker process pool (phase 1, default OFF): '1' lets consecutive tasks of the SAME agent reuse one live idle rpc process (warm context \u2014 1 process = 1 session, many prompts). Phase-1 limitation: per-run env control vars (broker token, steering file, scratchpad gates) and argv flags apply only to the FIRST task on the process; entries evict on exit/abort (child-pi-rpc.ts)"},PI_CREW_WORKER_TRANSPORT:{name:"PI_CREW_WORKER_TRANSPORT",doc:"W7 EXPERIMENTAL worker transport override: 'rpc' selects the prototype RPC frame-client (src/runtime/rpc/) at the run-worker seam (wired e2728f68; D1/DR2 also plumbs config runtime.workerTransport to the same seam \u2014 env WINS over config when set, invalid env fails safe to stdio); anything else/unset \u2192 config value, default stdio child-pi path (rpc-worker.ts resolveWorkerTransport)"},PI_CREW_INFERENCE_BROKER:{name:"PI_CREW_INFERENCE_BROKER",parser:"boolean",doc:"v0.11.31 (S-broker PoC) opt-in loopback inference broker: '1' starts a 127.0.0.1 HTTP broker in the parent; child workers get ONLY a per-worker token (PI_CREW_INFERENCE_TOKEN) + a scoped agent dir (PI_CODING_AGENT_DIR containing models.json that reroutes the built-in anthropic provider to the broker) instead of any real provider key. Real key (ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN from the PARENT env only) is substituted after requests leave the worker. FAIL-CLOSED: flag set without an upstream credential throws at first spawn (inference-broker.ts). PoC: anthropic upstream only, env-credential users only (auth.json users unsupported yet)"},PI_CREW_INFERENCE_TOKEN:{name:"PI_CREW_INFERENCE_TOKEN",doc:"v0.11.31 child-side per-worker inference broker token (256-bit random, heap-only on the parent, injected via built.env by prepareSpawnContext when the broker is active; interpolated into models.json apiKey as $PI_CREW_INFERENCE_TOKEN by the child at request time). NEVER user-set; workerKey-scoped \u2014 re-registering a task id replaces (kills) the old token"},PI_CREW_INFERENCE_BUDGET_TOKENS:{name:"PI_CREW_INFERENCE_BUDGET_TOKENS",parser:"int",doc:"v0.11.31 per-worker inference token budget counted from streamed Anthropic usage events; 0/unset = unlimited. Requests past the budget get 429 (inference-broker.ts registerInferenceWorker)"},PI_CREW_INFERENCE_UPSTREAM:{name:"PI_CREW_INFERENCE_UPSTREAM",doc:"v0.11.31 inference broker upstream override applied to EVERY provider (defaults: anthropic https://api.anthropic.com, openai https://api.openai.com/v1). TESTS ONLY \u2014 points the broker at a local mock upstream; production callers must never set it"},PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC:{name:"PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC",doc:"v0.11.34 anthropic-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_INFERENCE_UPSTREAM_OPENAI:{name:"PI_CREW_INFERENCE_UPSTREAM_OPENAI",doc:"v0.11.34 openai-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_INFERENCE_UPSTREAM_GOOGLE:{name:"PI_CREW_INFERENCE_UPSTREAM_GOOGLE",doc:"v0.11.36 google-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM; default https://generativelanguage.googleapis.com, native google-generative-ai wire, x-goog-api-key auth). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_SANDBOX:{name:"PI_CREW_SANDBOX",doc:"v0.11.36 OS-boundary sandbox, Linux opt-in: 'bwrap' wraps every headless child-pi worker in bubblewrap \u2014 the worker sees ONLY the project cwd (rw), the scoped inference agent dir, explicitly handed-in temp/scratch paths, and read-only OS runtime; ~/.pi (auth.json!), ~/.ssh and every other host path are NOT MOUNTED (fail-closed: forgotten paths yield ENOENT inside, never host access). Fail-closed knob: unsupported value / non-Linux / missing bwrap binary / net=off+broker all THROW before any spawn (never silently unsandboxed). Pass-1 boundary: TUI surface panes and the rpc transport are SKIPPED while on (fail-closed branch selection); PI_CREW_SANDBOX_NET=share keeps host networking \u2014 egress is NOT isolated unless net=off. Sandbox+broker-off = workers have no credentials at all (by isolation) \u2014 pair with PI_CREW_INFERENCE_BROKER=1 for sandboxed inference (sandbox.ts)"},PI_CREW_SANDBOX_NET:{name:"PI_CREW_SANDBOX_NET",doc:"v0.11.36 sandbox network profile: 'share' (default \u2014 host networking, loopback inference broker reachable, egress NOT isolated) or 'off' (unshare the net stack entirely: no egress, no loopback \u2014 REJECTED while the inference broker is active because broker-token workers would be keyless AND netless) (sandbox.ts)"},PI_CREW_CLASSIFIER_ENABLED:{name:"PI_CREW_CLASSIFIER_ENABLED",parser:"boolean",doc:"P2-1 (pi 1.0.4 adoption) host-process retry-triage classifier override: '1' enables transient-vs-permanent classification before a queued re-attempt in the child-executor retry loop, '0' forces it off \u2014 beats runtime.classifierEnabled; default OFF / dormant (classifier-service.ts:resolveClassifierEnabled). Fallback decision is used on every classifier failure mode, never an error"},PI_CREW_CLASSIFIER_MODEL:{name:"PI_CREW_CLASSIFIER_MODEL",doc:"P2-1 classifier model id override ('provider/id', catalog ids only \u2014 availability depends on provider credentials at runtime): beats runtime.classifierModel; unset/blank \u2192 config value, default 'opencode/jev-1.13-free' (classifier-service.ts:resolveClassifierModel)"},PI_CREW_VERIFY_GATE:{name:"PI_CREW_VERIFY_GATE",parser:"boolean",doc:"U13 deterministic verifier pre-gate override: '0' disables running the deterministic verify_gate checks (typecheck / test:critical via package.json scripts) before a verifier-role worker spawn \u2014 the LLM verifier then always spawns exactly as pre-U13; '1' forces it on. Default ON (gate PASS skips the LLM verifier spawn; FAILED/INCONCLUSIVE spawns it with the gate results in the prompt) (runtime/verification/verify-gate.ts resolveVerifyGateEnabled)"},PI_CREW_VERIFY_GATE_TIMEOUT_MS:{name:"PI_CREW_VERIFY_GATE_TIMEOUT_MS",parser:"int",default:12e4,doc:"U13 per-check timeout in ms for the deterministic verify_gate pre-gate commands. Invalid/\u22640 \u2192 120000, mirroring runPhaseGates (runtime/verification/verify-gate.ts resolveVerifyGateTimeoutMs)"},PI_CREW_VERIFY_DETERMINISM:{name:"PI_CREW_VERIFY_DETERMINISM",parser:"boolean",doc:"U13-ADJ (upgrade 2026-10-13, PHASE 4) layer-3 determinism rerun, DEFAULT OFF: '1' reruns the most critical default check (usually test:critical) once after a GREEN gate; disagreement (flaky) or a rerun executor failure downgrades the verdict to INCONCLUSIVE (never FAILED, never a task failure) with a 'deterministic-check: flaky <name>' note. Flag off keeps the gate bit-for-bit pre-U13-ADJ (runtime/verification/verify-gate.ts resolveVerifyDeterminismEnabled)"},PI_CREW_VERIFY_GATE_MEMO:{name:"PI_CREW_VERIFY_GATE_MEMO",parser:"boolean",doc:"W10-2 (v0.11.38) memoized green verify-gate, DEFAULT ON: a PASS verdict is recorded keyed by the workspace tree OID (git write-tree) + check list + node version; a re-gate on a bit-identical tree is served from the memo without re-running typecheck/test:critical. FAILED/INCONCLUSIVE never memoize; non-git cwd disables the memo for that tree (fail-open). '0' restores pre-W10-2 behavior (runtime/verification/verify-gate-memo.ts)"},PI_CREW_RPC_DIALOG_ANSWER:{name:"PI_CREW_RPC_DIALOG_ANSWER",doc:"W7 EXPERIMENTAL dialog auto-answer policy for RPC-mode extension_ui_request dialogs (select/confirm/input/editor): 'cancel' (default) answers {cancelled:true} \u2014 universal safe answer; 'block' leaves the server promise pending (debug). Auto-confirm deliberately NOT implemented (security); invalid \u2192 'cancel' (rpc-worker.ts resolveDialogAnswerPolicy)"},PI_CREW_RPC_TURN_TIMEOUT_MS:{name:"PI_CREW_RPC_TURN_TIMEOUT_MS",parser:"int",default:6e5,doc:"W7/DR1 belt-and-suspenders turn timeout for the EXPERIMENTAL RPC worker transport: max ms a prompt-accepted turn may run without agent_settled before the transport fails with the early-failure shape (DR5 guard still blocks the stdio fallback once a session event was seen). Env-only while RPC stays experimental. Invalid/\u22640 \u2192 600000 (rpc-worker.ts resolveRpcTurnTimeoutMs)"},PI_CREW_AUTO_EXIT:{name:"PI_CREW_AUTO_EXIT",doc:"'1' \u2192 the worker shuts its session down after the final settled turn \u2014 spec \xA75.2 D7 (written by prepareSurfaceSpawn, read by surface-worker.ts)"},PI_CREW_AUTO_PRUNE_KEEP:{name:"PI_CREW_AUTO_PRUNE_KEEP",parser:"int",default:10,doc:"DP-01: number of most-recent finished runs the session-start auto-prune retains (was hard-coded 10). Invalid/negative \u2192 10 with a warning (run-maintenance.ts:resolveAutoPruneKeep)"},PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS:{name:"PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS",parser:"int",default:24,doc:"DP-01: auto-prune never deletes a finished run younger than this many hours, even beyond top-keep \u2014 evidence/incident runs survive a session restart (0 disables; run-maintenance.ts:resolveAutoPruneAgeFloorMs)"},PI_CREW_SURFACE:{name:"PI_CREW_SURFACE",doc:"surface provider kind for this worker ('tmux'|'herdr') \u2014 arms the worker-side recorder/parent-guard (written by prepareSurfaceSpawn.ts:214, read by surface-worker.ts)"},PI_CREW_PARENT_START_TIME:{name:"PI_CREW_PARENT_START_TIME",doc:"parent starttime ticks (/proc/<pid>/stat field 22) captured at spawn \u2014 pid-reuse-safe parent-guard comparison (written by prepareSurfaceSpawn.ts:223, read by surface-worker.ts)"},PI_CREW_ARTIFACTS_ROOT:{name:"PI_CREW_ARTIFACTS_ROOT",doc:"artifacts root for scratchpad containment (scratchpad-lifecycle.ts:228/281)"},PI_CREW_SCRATCHPAD_SNAPSHOT:{name:"PI_CREW_SCRATCHPAD_SNAPSHOT",doc:"snapshot path for the post-cell snapshot (scratchpad-lifecycle.ts:229)"},PI_CREW_ATTEMPT:{name:"PI_CREW_ATTEMPT",default:"0",doc:"current attempt number (scratchpad-lifecycle.ts:230 reads ?? '0')"},PI_CREW_SCRATCHPAD_RESTORE:{name:"PI_CREW_SCRATCHPAD_RESTORE",doc:"parent-set restore hint: previous attempt snapshot (scratchpad-lifecycle.ts:653)"},PI_CREW_SCRATCHPAD_RESTORE_MTIME:{name:"PI_CREW_SCRATCHPAD_RESTORE_MTIME",parser:"number",doc:"swap-detection mtime pin for restore snapshots (scratchpad-lifecycle.ts:302)"},PI_CREW_MAX_AUTO_FALLBACKS:{name:"PI_CREW_MAX_AUTO_FALLBACKS",parser:"int",doc:"max automatic model fallbacks (model-fallback.ts:527)"},PI_CREW_MODEL_FALLBACK_ORDER:{name:"PI_CREW_MODEL_FALLBACK_ORDER",doc:"'parentFirst' | 'asIs' fallback order (model-fallback.ts:555)"},PI_CREW_MODEL_REQUIRE_CREDENTIALS:{name:"PI_CREW_MODEL_REQUIRE_CREDENTIALS",parser:"boolean",doc:"'1'/'0' override requireCredentials (model-fallback.ts:557/559)"},PI_CREW_MODEL:{name:"PI_CREW_MODEL",doc:"model override for subagents (model-fallback.ts:584)"},PI_CREW_BROKER_SOCKET:{name:"PI_CREW_BROKER_SOCKET",doc:"broker unix socket path for child handshake (crew-broker-child.ts:49)"},PI_CREW_BROKER_TOKEN:{name:"PI_CREW_BROKER_TOKEN",doc:"broker handshake token (crew-broker-child.ts:50)"},PI_CREW_BROKER_TASK_ID:{name:"PI_CREW_BROKER_TASK_ID",doc:"broker task id for child handshake (crew-broker-child.ts:51)"},PI_CREW_BROKER_STDIN:{name:"PI_CREW_BROKER_STDIN",doc:"LOW-1 closure (2026-10-27) broker-credential transport gate, OPT-IN since v0.11.48 (2026-10-28): '1' sends the per-task compound PI_CREW_BROKER_TOKEN to the headless JSON worker over a DEDICATED fd3 spawn pipe (one buildBrokerStdinLine v2 payload on stdio entry 4; heap \u2192 fd3 \u2192 heap \u2014 child-pi-spawn.ts writer, crew-broker-child.ts startChildBrokerClientAsync reader; fd0 stays 'ignore') so the token never appears in the child env; unset/any-other-value (default) keeps the LEGACY ENV ROUTE (token in built.env) on BOTH sides \u2014 the original fd0 variant was caught by the live battery (real-test-pi-crew T7) killing real headless workers with a silent exit 0 (root cause: pi@1.0.4 readPipedStdin consumes all piped fd0 as prompt content); the fd3 transport removes that race. RPC-mode workers and surface TUI panes are unaffected (they keep the env route; the worker-side fd3 read skips --mode rpc argv by design)"},PI_CREW_GUEST:{name:"PI_CREW_GUEST",doc:"'1' marks the guest scratchpad process (written by scratchpad-lifecycle.ts:167)"},PI_CREW_USE_BUNDLE:{name:"PI_CREW_USE_BUNDLE",doc:"read at root index.ts:78 (outside src/ gate scope): force strip-types loading when 0"},PI_CREW_SMOKE:{name:"PI_CREW_SMOKE",doc:"test-only: smoke-test helper flag (test/smoke/_helpers.ts)"},PI_CREW_RUN_PLATFORM_TESTS:{name:"PI_CREW_RUN_PLATFORM_TESTS",doc:"test-only: enables platform tests (test/platform)"},PI_CREW_HARD_KILL_GRACE_MS:{name:"PI_CREW_HARD_KILL_GRACE_MS",doc:"scripts-only: watchdog hard-kill grace (scripts/watchdog-harness.ts)"},PI_CREW_LIVE_MODEL:{name:"PI_CREW_LIVE_MODEL",doc:"docs/test-only: live-session model selection"},PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION:{name:"PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION",doc:"docs/test-only: experimental live-session opt-in"},PI_CREW_RUN_ID:{name:"PI_CREW_RUN_ID",doc:"test fixture name; src uses PI_CREW_BROKER_RUN_ID instead"},PI_TEAMS_CONFIG_KEYS:{name:"PI_TEAMS_CONFIG_KEYS",doc:"docs/test-only"},PI_TEAMS_MOCK:{name:"PI_TEAMS_MOCK",doc:"docs/test-only"},PI_CREW_VERIFICATION:{name:"PI_CREW_VERIFICATION",deprecated:"partial fixture name; real vars are PI_CREW_VERIFICATION_*",doc:"docs/test-only"},PI_TEAMS_VERIFICATION:{name:"PI_TEAMS_VERIFICATION",deprecated:"partial fixture name; real vars are PI_TEAMS_VERIFICATION_*",doc:"docs/test-only"},PI_CREW_ATOMIC_WRITER:{name:"PI_CREW_ATOMIC_WRITER",deprecated:"superseded by PI_CREW_WORKER_ATOMIC_WRITER",doc:"docs/test-only"},PI_CREW_BROKER_DIAG_UI:{name:"PI_CREW_BROKER_DIAG_UI",deprecated:"removed",doc:"docs/test-only"},PI_CREW_DEBUG_KILL:{name:"PI_CREW_DEBUG_KILL",doc:"docs/test-only"},PI_CREW_DWF_SANDBOX_DRY_RUN:{name:"PI_CREW_DWF_SANDBOX_DRY_RUN",doc:"docs/test-only"},PI_CREW_HOOK:{name:"PI_CREW_HOOK",doc:"docs/test-only"},PI_CREW_POOL_HEALTH:{name:"PI_CREW_POOL_HEALTH",doc:"docs/test-only"},PI_CREW_QUIET_PREFLIGHT:{name:"PI_CREW_QUIET_PREFLIGHT",doc:"docs/test-only"},PI_CREW_SAFE_BASH:{name:"PI_CREW_SAFE_BASH",deprecated:"dead",doc:"docs/test-only"},PI_CREW_SCRATCHPAD_EXPERIMENT:{name:"PI_CREW_SCRATCHPAD_EXPERIMENT",doc:"docs/test-only"},PI_CREW_SESSION_DEPTH:{name:"PI_CREW_SESSION_DEPTH",deprecated:"legacy name; code uses PI_CREW_DEPTH (ADR drift history 2026-08-09)",doc:"docs/test-only"},PI_CREW_SIG:{name:"PI_CREW_SIG",doc:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_KEY:{name:"PI_CREW_SNAPSHOT_HMAC_KEY",deprecated:"reverted (2026-08-13)",doc:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_STRICT:{name:"PI_CREW_SNAPSHOT_HMAC_STRICT",deprecated:"reverted (2026-08-13)",doc:"docs/test-only"},PI_CREW_TEST_REAL_MODEL:{name:"PI_CREW_TEST_REAL_MODEL",doc:"docs/test-only"},PI_CREW_TOOLING_429_NOTE:{name:"PI_CREW_TOOLING_429_NOTE",doc:"docs/test-only"},PI_CREW_MAX_OUTPUT_TOKENS:{name:"PI_CREW_MAX_OUTPUT_TOKENS",deprecated:"legacy name; actual var is PI_CREW_MAX_OUTPUT (comment-only in src)",doc:"docs/test-only"},PI_CREW_GOAL_OSCILLATION_EMBEDDINGS:{name:"PI_CREW_GOAL_OSCILLATION_EMBEDDINGS",doc:"docs/test-only"},PI_CREW_OPENAI_API_KEY:{name:"PI_CREW_OPENAI_API_KEY",doc:"comment-only in src (env-filter secret-name example)"},PI_CREW_SECRET_TOKEN:{name:"PI_CREW_SECRET_TOKEN",doc:"comment-only in src (env-filter secret-name example)"},PI_CREW_REFUSE_GATE_PATTERNS:{name:"PI_CREW_REFUSE_GATE_PATTERNS",doc:"docs-only"},PI_CREW_PROCEED_PATTERNS:{name:"PI_CREW_PROCEED_PATTERNS",doc:"docs-only"},PI_CREW_ENFORCEMENT_PATTERNS:{name:"PI_CREW_ENFORCEMENT_PATTERNS",doc:"docs-only"},PI_CREW_DIR:{name:"PI_CREW_DIR",doc:"test fixture"},PI_CREW_ROOT:{name:"PI_CREW_ROOT",doc:"test fixture"},PI_CREW_FOO:{name:"PI_CREW_FOO",doc:"test fixture"},PI_CREW_VAR:{name:"PI_CREW_VAR",doc:"test fixture"},PI_CREW_X:{name:"PI_CREW_X",doc:"test fixture"},PI_CREW_TEST_SECRET_VALUE:{name:"PI_CREW_TEST_SECRET_VALUE",doc:"test fixture"},PI_TEAMS_BAR:{name:"PI_TEAMS_BAR",doc:"test fixture"}}});function b(e,t,n,r){if((!r||r==="debug")&&!z("PI_TEAMS_DEBUG"))return;let o=t instanceof Error?t.message:typeof t=="object"&&t!==null?JSON.stringify(t):String(t),s=n?`: ${n}`:"";console.error(`[pi-crew:${e}] ${o}${s}`)}var H=v(()=>{"use strict";Pe()});import{execFileSync as Vge}from"node:child_process";function wo(e){try{let t=new SharedArrayBuffer(4);Atomics.wait(new Int32Array(t),0,0,e)}catch{if(process.platform!=="win32"&&e>=10)try{Vge("sleep",[(e/1e3).toFixed(3)],{timeout:e+1e3,stdio:"pipe"});return}catch{}let t=Date.now()+e;for(;Date.now()<t;);}}function mg(e,t){return t?.aborted?Promise.reject(new Error("aborted")):new Promise((n,r)=>{let o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new Error("aborted"))},{once:!0})})}var Ba=v(()=>{"use strict"});import*as gl from"node:fs";import{createRequire as zge}from"node:module";import*as uM from"node:path";import{Worker as Jge}from"node:worker_threads";function Qge(){let e=z("PI_CREW_WORKER_ATOMIC_TIMEOUT_MS");if(e===void 0)return lq;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<=0?lq:t}function dM(){return fq??=new cM(Qge()),fq}function ehe(){return(!cp||!gl.existsSync(cp))&&(cp=gl.mkdtempSync(uM.join(Yge("node:os").tmpdir(),"pi-crew-waw-"))),cp}function pq(){if(cp){try{gl.rmSync(cp,{recursive:!0,force:!0})}catch{}cp=void 0}}function the(){if(Si)return Si;let e=uM.join(ehe(),"worker.cjs");gl.writeFileSync(e,Zge,"utf-8"),Si=new Jge(e);let t=Si;return dM().attach(t,()=>Si===t),t.on("error",()=>{Si===t&&(Si=void 0)}),t.on("exit",()=>{Si===t&&(Si=void 0)}),t.unref(),Xge&&t.ref(),Si}function mq(e,t){return new Promise((n,r)=>{let o=dM(),s=o.register(n,r);try{the().postMessage({kind:e,id:s,...t})}catch(i){o.cancel(s),r(i instanceof Error?i:new Error(String(i)))}})}function NR(){return z("PI_CREW_WORKER_ATOMIC_WRITER")==="1"||z("PI_TEAMS_WORKER_ATOMIC_WRITER")==="1"}function gq(e,t,n){return mq("write",{filePath:e,content:t,mode:n})}async function hq(e,t){if(!hl(e))throw new Error(`Refusing to append: target is a symlink or inside untrusted directory: ${e}`);return mq("append",{filePath:e,content:t})}function nhe(){if(Si){let e=Si;Si=void 0,e.terminate().catch(()=>{})}dM().drain(new Error("worker terminated")),pq()}var Yge,Si,cp,Xge,Zge,lq,cM,fq,lM=v(()=>{"use strict";Pe();Be();Yge=zge(import.meta.url),Xge=!1,Zge=`
8
+ var Kge=Object.defineProperty;var v=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(r){throw n=[r],r}};var Re=(e,t)=>{for(var n in t)Kge(e,n,{get:t[n],enumerable:!0})};function z(e,t=process.env){let n=pg[e];if(!n?.mirror||n.mirrorPrecedence==="or")return t[e];let r=e.startsWith("PI_TEAMS_")?n.mirror:e,o=e.startsWith("PI_TEAMS_")?e:n.mirror;return n.mirrorPrecedence==="teams"?t[o]??t[r]:t[r]??t[o]}function uq(e){let t=pg[e],n=z(e),r=()=>t?.default;if(n===void 0)return r();if(!t?.parser||t.parser==="string")return n;switch(t.parser){case"int":{let o=Number.parseInt(n,10);return Number.isNaN(o)?r():o}case"number":{let o=Number(n);return Number.isNaN(o)?r():o}case"boolean":{let o=n.trim().toLowerCase();return o==="1"||o==="true"||o==="yes"?!0:o==="0"||o==="false"||o==="no"?!1:r()}case"json":try{return JSON.parse(n)}catch{return r()}}return n}function dr(e){let t=uq(e);return typeof t=="boolean"?t:void 0}function Yc(e){let t=uq(e);return typeof t=="number"?t:void 0}function dq(e){return z(e)}var pg,Pe=v(()=>{"use strict";pg={PI_CREW_HOME:{name:"PI_CREW_HOME",mirror:"PI_TEAMS_HOME",mirrorPrecedence:"teams",doc:"pi-crew home dir; PI_TEAMS_HOME wins (config.ts:174, paths.ts:58, crew-vibes/config.ts:16)"},PI_TEAMS_HOME:{name:"PI_TEAMS_HOME",mirror:"PI_CREW_HOME",mirrorPrecedence:"teams",doc:"pi-crew home dir \u2014 primary of the TEAMS??CREW pair"},PI_CREW_DEPTH:{name:"PI_CREW_DEPTH",mirror:"PI_TEAMS_DEPTH",mirrorPrecedence:"crew",default:0,parser:"int",doc:"current crew depth (pi-args.ts:72 reads CREW ?? TEAMS ?? '0')"},PI_TEAMS_DEPTH:{name:"PI_TEAMS_DEPTH",mirror:"PI_CREW_DEPTH",mirrorPrecedence:"crew",default:0,parser:"int",doc:"crew depth mirror (fallback of PI_CREW_DEPTH)"},PI_CREW_MAX_DEPTH:{name:"PI_CREW_MAX_DEPTH",mirror:"PI_TEAMS_MAX_DEPTH",mirrorPrecedence:"crew",parser:"int",doc:"max crew depth, clamped 1..10 (pi-args.ts:78)"},PI_TEAMS_MAX_DEPTH:{name:"PI_TEAMS_MAX_DEPTH",mirror:"PI_CREW_MAX_DEPTH",mirrorPrecedence:"crew",parser:"int",doc:"max crew depth mirror (fallback of PI_CREW_MAX_DEPTH)"},PI_CREW_ROLE:{name:"PI_CREW_ROLE",mirror:"PI_TEAMS_ROLE",mirrorPrecedence:"crew",doc:"crew role name (role-permission.ts:46 reads CREW?.trim() || TEAMS?.trim())"},PI_TEAMS_ROLE:{name:"PI_TEAMS_ROLE",mirror:"PI_CREW_ROLE",mirrorPrecedence:"crew",doc:"crew role mirror (fallback of PI_CREW_ROLE)"},PI_CREW_VERIFICATION_PRESERVE_ENV:{name:"PI_CREW_VERIFICATION_PRESERVE_ENV",mirror:"PI_TEAMS_VERIFICATION_PRESERVE_ENV",mirrorPrecedence:"crew",doc:"extra env keys to preserve in verification commands (CREW ?? TEAMS ?? '')"},PI_TEAMS_VERIFICATION_PRESERVE_ENV:{name:"PI_TEAMS_VERIFICATION_PRESERVE_ENV",mirror:"PI_CREW_VERIFICATION_PRESERVE_ENV",mirrorPrecedence:"crew",doc:"verification preserve-env mirror (fallback of PI_CREW_...)"},PI_CREW_VERIFICATION_WORKTREE:{name:"PI_CREW_VERIFICATION_WORKTREE",mirror:"PI_TEAMS_VERIFICATION_WORKTREE",mirrorPrecedence:"crew",doc:"enable verification worktree sandbox; '1'/'true' (CREW ?? TEAMS)"},PI_TEAMS_VERIFICATION_WORKTREE:{name:"PI_TEAMS_VERIFICATION_WORKTREE",mirror:"PI_CREW_VERIFICATION_WORKTREE",mirrorPrecedence:"crew",doc:"verification worktree mirror (fallback of PI_CREW_...)"},PI_CREW_INHERIT_PROJECT_CONTEXT:{name:"PI_CREW_INHERIT_PROJECT_CONTEXT",mirror:"PI_TEAMS_INHERIT_PROJECT_CONTEXT",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit project context in worker prompts; CREW checked first (prompt-runtime.ts readBooleanEnvAny)"},PI_TEAMS_INHERIT_PROJECT_CONTEXT:{name:"PI_TEAMS_INHERIT_PROJECT_CONTEXT",mirror:"PI_CREW_INHERIT_PROJECT_CONTEXT",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit project context mirror (fallback of PI_CREW_...)"},PI_CREW_INHERIT_SKILLS:{name:"PI_CREW_INHERIT_SKILLS",mirror:"PI_TEAMS_INHERIT_SKILLS",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit skills in worker prompts; CREW checked first (prompt-runtime.ts readBooleanEnvAny)"},PI_TEAMS_INHERIT_SKILLS:{name:"PI_TEAMS_INHERIT_SKILLS",mirror:"PI_CREW_INHERIT_SKILLS",mirrorPrecedence:"crew",parser:"boolean",doc:"inherit skills mirror (fallback of PI_CREW_...)"},PI_CREW_ADAPTIVE_REPAIR:{name:"PI_CREW_ADAPTIVE_REPAIR",mirror:"PI_TEAMS_ADAPTIVE_REPAIR",mirrorPrecedence:"or",doc:"'0' disables adaptive-plan repair; either name may disable (adaptive-plan.ts:434)"},PI_TEAMS_ADAPTIVE_REPAIR:{name:"PI_TEAMS_ADAPTIVE_REPAIR",mirror:"PI_CREW_ADAPTIVE_REPAIR",mirrorPrecedence:"or",doc:"adaptive-repair mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_BG_REPORT_ON_FATAL:{name:"PI_CREW_BG_REPORT_ON_FATAL",mirror:"PI_TEAMS_BG_REPORT_ON_FATAL",mirrorPrecedence:"or",doc:"'0' disables --report-on-fatalerror; either name may disable (async-runner.ts:127)"},PI_TEAMS_BG_REPORT_ON_FATAL:{name:"PI_TEAMS_BG_REPORT_ON_FATAL",mirror:"PI_CREW_BG_REPORT_ON_FATAL",mirrorPrecedence:"or",doc:"bg-report mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_EXECUTE_WORKERS:{name:"PI_CREW_EXECUTE_WORKERS",mirror:"PI_TEAMS_EXECUTE_WORKERS",mirrorPrecedence:"or",doc:"'0' disables real worker execution; either name disables (team-tool.ts:376, run.ts, runtime-resolver.ts:91)"},PI_TEAMS_EXECUTE_WORKERS:{name:"PI_TEAMS_EXECUTE_WORKERS",mirror:"PI_CREW_EXECUTE_WORKERS",mirrorPrecedence:"or",doc:"execute-workers mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_VERIFICATION_SANITIZE_ENV:{name:"PI_CREW_VERIFICATION_SANITIZE_ENV",mirror:"PI_TEAMS_VERIFICATION_SANITIZE_ENV",mirrorPrecedence:"or",doc:"'0' disables verification env sanitization; either name disables (verification-gates.ts:69)"},PI_TEAMS_VERIFICATION_SANITIZE_ENV:{name:"PI_TEAMS_VERIFICATION_SANITIZE_ENV",mirror:"PI_CREW_VERIFICATION_SANITIZE_ENV",mirrorPrecedence:"or",doc:"verification sanitize mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_WORKER_ATOMIC_WRITER:{name:"PI_CREW_WORKER_ATOMIC_WRITER",mirror:"PI_TEAMS_WORKER_ATOMIC_WRITER",mirrorPrecedence:"or",doc:"'1' enables the worker-thread atomic writer; either name enables (worker-atomic-writer.ts:166)"},PI_TEAMS_WORKER_ATOMIC_WRITER:{name:"PI_TEAMS_WORKER_ATOMIC_WRITER",mirror:"PI_CREW_WORKER_ATOMIC_WRITER",mirrorPrecedence:"or",doc:"worker-atomic-writer mirror (read independently, OR'd with PI_CREW_...)"},PI_CREW_ALLOW_MOCK:{name:"PI_CREW_ALLOW_MOCK",parser:"boolean",doc:"'1'/'true' allows PI_TEAMS_MOCK_CHILD_PI mock mode (mock-fixtures.ts:40)"},PI_CREW_TEST_ASYNC_INLINE:{name:"PI_CREW_TEST_ASYNC_INLINE",parser:"boolean",doc:"'1' runs async team runs IN-PROCESS instead of spawning a detached background-runner (test seam; requires PI_CREW_ALLOW_MOCK=1 \u2014 see async-runner.ts spawnBackgroundTeamRun)"},PI_CREW_ASYNC_RUN:{name:"PI_CREW_ASYNC_RUN",doc:"async telemetry marker STAMPED onto the detached runner env by buildBackgroundRunnerEnv (async-runner.ts), read by resolve-surface.ts SurfaceGateEnvSnapshot \u2014 NOT a surface gate (registry entry added with the C-1 env-allowlist registry 2026-10-11 \u2014 was a read/write site missing from this registry)"},PI_CREW_BACKGROUND_RUNNER_ENTRY:{name:"PI_CREW_BACKGROUND_RUNNER_ENTRY",parser:"boolean",doc:"'1' marks this process as a directly-invoked background-runner entry (set by async-runner spawn; module import without it skips main() \u2014 background-runner.ts entry guard)"},PI_CREW_PROMPT_BREAKDOWN:{name:"PI_CREW_PROMPT_BREAKDOWN",parser:"boolean",doc:"'1' writes a per-section prompt token breakdown artifact metadata/<task>.prompt-breakdown.json (SR-02 phase 1, prompt-builder.ts)"},PI_CREW_PROMPT_SKILLS:{name:"PI_CREW_PROMPT_SKILLS",doc:"'full' inlines complete skill bodies in worker prompts (SR-02 escape hatch); default injects compact index entries (name+description+path, read-on-demand)"},PI_CREW_ASYNC_EARLY_EXIT_GUARD:{name:"PI_CREW_ASYNC_EARLY_EXIT_GUARD",doc:"'0' skips the async-run early-exit guard (team-tool/run.ts:103)"},PI_CREW_BACKGROUND_MODE:{name:"PI_CREW_BACKGROUND_MODE",doc:"background-runner marker; READ at child-executor.ts:616 ('1'), WRITTEN at background-runner.ts:713/833 (= '1')"},PI_CREW_BROKER:{name:"PI_CREW_BROKER",doc:"'1'/'0' force broker enabled/disabled (defaults.ts:187, lifecycle-handlers.ts:908)"},PI_CREW_DEBUG:{name:"PI_CREW_DEBUG",doc:"truthiness enables background debug logging (background-runner.ts:63); RAW truthiness \u2014 '0'/'false' still ENABLE, unset to disable (R45-F4)"},PI_CREW_DEBUG_BUDGET:{name:"PI_CREW_DEBUG_BUDGET",doc:"'1' logs token budget (team-tool/run.ts:498)"},PI_CREW_DEBUG_STALE:{name:"PI_CREW_DEBUG_STALE",doc:"'1' writes a forensic sidecar log of every STALE verdict from the stale reconciler (stale-reconciler.ts:287) \u2014 the reconciler may run in ANY host process, hence a fixed env gate"},PI_CREW_DWF_SCRIPT_TIMEOUT_MS:{name:"PI_CREW_DWF_SCRIPT_TIMEOUT_MS",parser:"int",default:18e5,doc:"dynamic-workflow script timeout; default 30 min (dynamic-workflow-runner.ts:226)"},PI_CREW_DWF_SKIP_DETERMINISM_CHECK:{name:"PI_CREW_DWF_SKIP_DETERMINISM_CHECK",doc:"'1' disables the DWF determinism check (deterministic-ast.ts:60)"},PI_CREW_HEARTBEAT_PULSE_MS:{name:"PI_CREW_HEARTBEAT_PULSE_MS",parser:"int",default:15e3,doc:"child-executor liveness-pulse interval (ms): touches the worker heartbeat while the attempt's child process is alive even with no stdout events (NEW-4/G25 silent-turn freeze); \u22640 disables (child-executor.ts)"},PI_CREW_INTERRUPT_GUARD_INTERVAL_MS:{name:"PI_CREW_INTERRUPT_GUARD_INTERVAL_MS",parser:"int",default:1e3,doc:"interrupt-guard poll interval; default 1000ms (background-runner.ts:217)"},PI_CREW_WATCHDOG_INTERVAL_MS:{name:"PI_CREW_WATCHDOG_INTERVAL_MS",parser:"int",default:3e5,doc:"WD-INTERVAL (2026-10-23): foreground-run watchdog poll interval (ms), default 300000 = DEFAULT_CHECK_INTERVAL_MS (foreground-watchdog.ts resolveCheckIntervalMs). Lower it (e.g. 20000) to live-repro watchdog behavior without waiting 5 min per cycle; \u22640/garbage falls back to the default. An explicit checkIntervalMs argument still wins."},PI_CREW_MAX_RUN_MS:{name:"PI_CREW_MAX_RUN_MS",parser:"int",default:72e5,doc:"max background run duration; default 2h (background-runner.ts:31)"},PI_CREW_REAP_GROUP:{name:"PI_CREW_REAP_GROUP",parser:"boolean",default:!0,doc:"wave 11a kill switch for identity-proven PROCESS-GROUP reaping: '0'/'false' degrades child-pi-kill + orphan-reaper to their pre-wave pid-only paths (resolveReapGroupEnabled, reap-group.ts). FORWARDED to the detached background runner (env-allowlist-registry: PI_CREW_REAP_GROUP \u2192 background-runner) because the runner/worker process is the one that reaps its own children \u2014 an unforwarded switch would be silently inert there"},PI_CREW_REAP_RUN_DESCENDANTS:{name:"PI_CREW_REAP_RUN_DESCENDANTS",parser:"boolean",default:!0,doc:"wave 11b kill switch for the RUN-SCOPED descendant sweep: '0'/'false' makes collectRunDescendants/reapRunDescendants (run-descendant-sweep.ts) a complete no-op. FORWARDED to the detached background runner (env-allowlist-registry: \u2192 background-runner) for the same reason as PI_CREW_REAP_GROUP: the reconciler that drives the sweep also runs there. Never forwarded to a worker"},PI_CREW_MAX_WORKERS:{name:"PI_CREW_MAX_WORKERS",parser:"int",doc:"max concurrent workers; unset/empty/invalid \u2192 default max(2, cpus-2) (global-worker-cap.ts:33)"},PI_CREW_DISABLE_RESULT_READ_CACHE:{name:"PI_CREW_DISABLE_RESULT_READ_CACHE",doc:"'1' bypasses the per-run result-artifact read cache (R10-1 bypass control; task-output-context.ts createResultArtifactReadCache)"},PI_CREW_HANDOFF_BUDGET_TOKENS:{name:"PI_CREW_HANDOFF_BUDGET_TOKENS",parser:"int",doc:"est-token (chars/4) budget for the dynamic.dependencyContext prompt layer; beats runtime.handoffBudgetTokens, default 1800; resolved \u22640 or >1_000_000 disables the trim (task-output-context.ts renderDependencyOutputContext)"},PI_CREW_MOCK_LIVE_SESSION:{name:"PI_CREW_MOCK_LIVE_SESSION",doc:"'success' mocks a successful live-session (live-session-runtime.ts:558, runtime-resolver.ts:38/116)"},PI_CREW_PARENT_GUARD_INTERVAL_MS:{name:"PI_CREW_PARENT_GUARD_INTERVAL_MS",parser:"int",default:500,doc:"parent-guard poll interval; default 500ms (parent-guard.ts:73)"},PI_CREW_PARENT_PID:{name:"PI_CREW_PARENT_PID",parser:"int",doc:"parent process pid; >0 starts the parent guard (background-runner.ts:626)"},PI_CREW_SCRATCHPAD_DEMOTE_BASH:{name:"PI_CREW_SCRATCHPAD_DEMOTE_BASH",doc:"'1' demotes bash for scratchpad roles (agent-config.ts:199)"},PI_CREW_SKIP_HOME_CHECK:{name:"PI_CREW_SKIP_HOME_CHECK",doc:"'1' skips the PI_TEAMS_HOME escape check (config.ts:185)"},PI_CREW_SUPPRESS_RPC_WARNING:{name:"PI_CREW_SUPPRESS_RPC_WARNING",doc:"'1' suppresses the missing-RPC-secret warning (rpc-hmac.ts:145)"},PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS:{name:"PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS",doc:"'1' trusts project/.pi agent extensions (discover-agents.ts:445/544)"},PI_CREW_PLAN_UI:{name:"PI_CREW_PLAN_UI",doc:"'1' enables the Plan dashboard pane (7) + plans snapshot slice (WP-7/R7)"},PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC:{name:"PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC",parser:"boolean",doc:"'1'/'true' writes non-terminal tasks checkpoints best-effort (no fsync; 50ms coalesce kept, only durability skipped); terminal transitions stay full (defaults.ts, config.ts persistence.skipTasksFsync)"},PI_CREW_TRUST_PROJECT_DWF:{name:"PI_CREW_TRUST_PROJECT_DWF",doc:"'1' allows project-sourced .dwf.ts workflows (dynamic-workflow-runner.ts:189)"},PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS:{name:"PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS",parser:"int",doc:"child Pi response timeout, bounded 1s..3.6M ms (child-pi.ts:356)"},PI_TEAMS_DEBUG:{name:"PI_TEAMS_DEBUG",doc:"truthiness gates debug-severity internal-error logging (internal-error.ts:10); RAW truthiness \u2014 '0'/'false' still ENABLE, unset to disable (R45-F4)"},PI_TEAMS_MOCK_CHILD_PI:{name:"PI_TEAMS_MOCK_CHILD_PI",doc:"mock child-pi mode; values 'success'/'json-success'/'adaptive-plan' (mock-fixtures.ts:35)"},PI_TEAMS_PI_BIN:{name:"PI_TEAMS_PI_BIN",doc:"explicit pi binary path for spawned children (pi-spawn.ts:262)"},PI_CREW_MAX_OUTPUT:{name:"PI_CREW_MAX_OUTPUT",parser:"int",doc:"max output tokens cap for background workers (prompt-runtime.ts:228)"},PI_CREW_STEERING_FILE:{name:"PI_CREW_STEERING_FILE",doc:"steering JSONL path polled by the worker (prompt-runtime.ts:260)"},PI_CREW_KEYBINDINGS:{name:"PI_CREW_KEYBINDINGS",parser:"json",doc:"JSON keybinding override map (keybinding-map.ts:314/347)"},PI_CREW_RPC_SECRET:{name:"PI_CREW_RPC_SECRET",doc:"shared HMAC secret; READ at rpc-hmac.ts:26/41, WRITTEN/cleared at :31/:36"},PI_CREW_PEER_DEP_DIR:{name:"PI_CREW_PEER_DEP_DIR",doc:"parent-provided pi-coding-agent package dir hint (peer-dep.ts:81)"},PI_CREW_SCRATCHPAD:{name:"PI_CREW_SCRATCHPAD",doc:"'1' enables the scratchpad engine (scratchpad-lifecycle.ts:486/679, written by child-pi-spawn.ts)"},PI_CREW_KIND:{name:"PI_CREW_KIND",doc:"'subagent' marks child workers (scratchpad-lifecycle.ts:679, lifecycle-handlers.ts:892, zombie-scanner.ts:183)"},PI_CREW_TASK_ID:{name:"PI_CREW_TASK_ID",doc:"current task id for scratchpad/events (scratchpad-lifecycle.ts:90/227/280)"},PI_CREW_EVENTS_PATH:{name:"PI_CREW_EVENTS_PATH",doc:"events JSONL path for fire-and-forget metric events (scratchpad-lifecycle.ts:80)"},PI_CREW_ASK_ENABLED:{name:"PI_CREW_ASK_ENABLED",doc:"'1' enables the worker-side ask tool \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts per ADR-0 WP-2 item 2)"},PI_CREW_MSG_ENABLED:{name:"PI_CREW_MSG_ENABLED",doc:"'1' enables the worker-side message tool (D9/\xA715.2) \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts)"},PI_CREW_DELEGATE_ENABLED:{name:"PI_CREW_DELEGATE_ENABLED",doc:"'1' enables the worker-side delegate tool \u2014 dormant-until-env gate (written UNCONDITIONALLY by child-pi-spawn.ts, read by prompt-runtime.ts; registry entry added with the C-1 env-allowlist registry 2026-10-11 \u2014 was a read/write site missing from this registry)"},PI_CREW_STATE_ROOT:{name:"PI_CREW_STATE_ROOT",doc:"run stateRoot for the ask-tool mailbox poll (<stateRoot>/mailbox; written by child-pi-spawn.ts, read by prompt-runtime.ts per ADR-0 WP-2 item 2)"},PI_CREW_BROKER_RUN_ID:{name:"PI_CREW_BROKER_RUN_ID",doc:"broker run id; aliases PI_CREW_RUN_ID (scratchpad-lifecycle.ts:81, crew-broker-child.ts:51)"},PI_CREW_AGENT_EVENTS_PATH:{name:"PI_CREW_AGENT_EVENTS_PATH",doc:"per-agent events log (<stateRoot>/agents/<taskId>/events.jsonl) written by the surface worker recorder (surface-worker.ts; derived by prepareSurfaceSpawn)"},PI_CREW_SESSION_RECOVERY:{name:"PI_CREW_SESSION_RECOVERY",parser:"boolean",doc:"W2 session-file recovery override for killed workers (exitCode null / killed): '0' disables, '1' forces on \u2014 beats ChildPiRunInput.sessionRecovery / runtime.sessionRecovery; default ON (session-recovery.ts:resolveSessionRecoveryEnabled)"},PI_CREW_HERMETIC_WORKERS:{name:"PI_CREW_HERMETIC_WORKERS",parser:"boolean",doc:"R3-19/D5 hermetic worker spawn override: '1' forces --no-extensions on worker argv, '0' restores the old ambient-stack parity (discovered/configured/built-in extensions load); beats BuildPiWorkerArgsInput.hermeticWorkers / runtime.hermeticWorkers; default ON (pi-args.ts:resolveHermeticWorkers). Explicit -e prompt-runtime + agent extensions + --skill flags are unaffected (cli.md:186-191)"},PI_CREW_TOOLS_MODIFIERS:{name:"PI_CREW_TOOLS_MODIFIERS",parser:"boolean",doc:"L2-3 (pi 1.1.0 `+name`/`-name` --tools modifier syntax) OPT-IN, default OFF: '1' emits a declared agent tool list as --tools +a,+b (default toolset PLUS the declarations) instead of the legacy replacement allowlist, but ONLY when the probed host piVersion >= 1.1.0 (PI_TOOLS_MODIFIER_FLOOR, pi-args.ts); below the floor or unknown version falls back to the legacy syntax (pi's strict parser + getToolListError must never see modifiers on a pre-1.1.0 host). Additive semantics: the worker keeps the FULL default tool surface (incl. runtime-registered control tools), so CONTROL_TOOLS are not merged in modifier mode (pi-args.ts)"},PI_CREW_STATE_BACKEND:{name:"PI_CREW_STATE_BACKEND",doc:"U8 (upgrade-spec 2026-10-09) run-state store backend select: 'sqlite' stores manifest+tasks+worker-status in <runId>/state.sqlite via node:sqlite with ONE transaction per batch write (all-or-nothing; manifest.json/tasks.json stay written as read-side mirrors for legacy consumers \u2014 loads read the db); unset or any other value keeps the default JSON file backend. events.jsonl audit spine unchanged in both modes (state-store.ts / sqlite-run-state.ts isSqliteStateBackend)"},PI_CREW_RUN_LOCK_SOCKET:{name:"PI_CREW_RUN_LOCK_SOCKET",parser:"boolean",default:!0,doc:"U21 (upgrade 2026-10-13) run-lock dual-guard toggle: '0'/'false'/'no' disables the kernel process-socket SUPPLEMENT on the ASYNC withRunLock path (pre-U21 file-lock-only behaviour, bit-for-bit); default ON. Host-process read only \u2014 never passed to children (the socket name is derived from the run dir path). The SYNC withRunLockSync path is always file-only by design (locks.ts acquireRunLockGuarded)"},PI_CREW_CHILD_PI_MODE:{name:"PI_CREW_CHILD_PI_MODE",doc:"U14 (upgrade-spec 2026-10-09) child-pi protocol v2 transport select: 'rpc' spawns task workers over pi --mode rpc (SDK RpcClient wire \u2014 steer receipts, clear_queue hygiene, multi-prompt reuse; child-pi-rpc.ts), 'json' / unset keeps the --mode json -p stdio path (phase-1 default; both modes fully regression-tested). Surface TUI panes are unaffected"},PI_CREW_CHILD_PI_POOL:{name:"PI_CREW_CHILD_PI_POOL",parser:"boolean",doc:"U14 rpc worker process pool (phase 1, default OFF): '1' lets consecutive tasks of the SAME agent reuse one live idle rpc process (warm context \u2014 1 process = 1 session, many prompts). Phase-1 limitation: per-run env control vars (broker token, steering file, scratchpad gates) and argv flags apply only to the FIRST task on the process; entries evict on exit/abort (child-pi-rpc.ts)"},PI_CREW_WORKER_TRANSPORT:{name:"PI_CREW_WORKER_TRANSPORT",doc:"W7 EXPERIMENTAL worker transport override: 'rpc' selects the prototype RPC frame-client (src/runtime/rpc/) at the run-worker seam (wired e2728f68; D1/DR2 also plumbs config runtime.workerTransport to the same seam \u2014 env WINS over config when set, invalid env fails safe to stdio); anything else/unset \u2192 config value, default stdio child-pi path (rpc-worker.ts resolveWorkerTransport)"},PI_CREW_INFERENCE_BROKER:{name:"PI_CREW_INFERENCE_BROKER",parser:"boolean",doc:"v0.11.31 (S-broker PoC) opt-in loopback inference broker: '1' starts a 127.0.0.1 HTTP broker in the parent; child workers get ONLY a per-worker token (PI_CREW_INFERENCE_TOKEN) + a scoped agent dir (PI_CODING_AGENT_DIR containing models.json that reroutes the built-in anthropic provider to the broker) instead of any real provider key. Real key (ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN from the PARENT env only) is substituted after requests leave the worker. FAIL-CLOSED: flag set without an upstream credential throws at first spawn (inference-broker.ts). PoC: anthropic upstream only, env-credential users only (auth.json users unsupported yet)"},PI_CREW_INFERENCE_TOKEN:{name:"PI_CREW_INFERENCE_TOKEN",doc:"v0.11.31 child-side per-worker inference broker token (256-bit random, heap-only on the parent, injected via built.env by prepareSpawnContext when the broker is active; interpolated into models.json apiKey as $PI_CREW_INFERENCE_TOKEN by the child at request time). NEVER user-set; workerKey-scoped \u2014 re-registering a task id replaces (kills) the old token"},PI_CREW_INFERENCE_BUDGET_TOKENS:{name:"PI_CREW_INFERENCE_BUDGET_TOKENS",parser:"int",doc:"v0.11.31 per-worker inference token budget counted from streamed Anthropic usage events; 0/unset = unlimited. Requests past the budget get 429 (inference-broker.ts registerInferenceWorker)"},PI_CREW_INFERENCE_UPSTREAM:{name:"PI_CREW_INFERENCE_UPSTREAM",doc:"v0.11.31 inference broker upstream override applied to EVERY provider (defaults: anthropic https://api.anthropic.com, openai https://api.openai.com/v1). TESTS ONLY \u2014 points the broker at a local mock upstream; production callers must never set it"},PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC:{name:"PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC",doc:"v0.11.34 anthropic-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_INFERENCE_UPSTREAM_OPENAI:{name:"PI_CREW_INFERENCE_UPSTREAM_OPENAI",doc:"v0.11.34 openai-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_INFERENCE_UPSTREAM_GOOGLE:{name:"PI_CREW_INFERENCE_UPSTREAM_GOOGLE",doc:"v0.11.36 google-only broker upstream override (beats PI_CREW_INFERENCE_UPSTREAM; default https://generativelanguage.googleapis.com, native google-generative-ai wire, x-goog-api-key auth). TESTS ONLY \u2014 local mock; production must never set it"},PI_CREW_SANDBOX:{name:"PI_CREW_SANDBOX",doc:"v0.11.36 OS-boundary sandbox, Linux opt-in: 'bwrap' wraps every headless child-pi worker in bubblewrap \u2014 the worker sees ONLY the project cwd (rw), the scoped inference agent dir, explicitly handed-in temp/scratch paths, and read-only OS runtime; ~/.pi (auth.json!), ~/.ssh and every other host path are NOT MOUNTED (fail-closed: forgotten paths yield ENOENT inside, never host access). Fail-closed knob: unsupported value / non-Linux / missing bwrap binary / net=off+broker all THROW before any spawn (never silently unsandboxed). Pass-1 boundary: TUI surface panes and the rpc transport are SKIPPED while on (fail-closed branch selection); PI_CREW_SANDBOX_NET=share keeps host networking \u2014 egress is NOT isolated unless net=off. Sandbox+broker-off = workers have no credentials at all (by isolation) \u2014 pair with PI_CREW_INFERENCE_BROKER=1 for sandboxed inference (sandbox.ts)"},PI_CREW_SANDBOX_NET:{name:"PI_CREW_SANDBOX_NET",doc:"v0.11.36 sandbox network profile: 'share' (default \u2014 host networking, loopback inference broker reachable, egress NOT isolated) or 'off' (unshare the net stack entirely: no egress, no loopback \u2014 REJECTED while the inference broker is active because broker-token workers would be keyless AND netless) (sandbox.ts)"},PI_CREW_CLASSIFIER_ENABLED:{name:"PI_CREW_CLASSIFIER_ENABLED",parser:"boolean",doc:"P2-1 (pi 1.0.4 adoption) host-process retry-triage classifier override: '1' enables transient-vs-permanent classification before a queued re-attempt in the child-executor retry loop, '0' forces it off \u2014 beats runtime.classifierEnabled; default OFF / dormant (classifier-service.ts:resolveClassifierEnabled). Fallback decision is used on every classifier failure mode, never an error"},PI_CREW_CLASSIFIER_MODEL:{name:"PI_CREW_CLASSIFIER_MODEL",doc:"P2-1 classifier model id override ('provider/id', catalog ids only \u2014 availability depends on provider credentials at runtime): beats runtime.classifierModel; unset/blank \u2192 config value, default 'opencode/jev-1.13-free' (classifier-service.ts:resolveClassifierModel)"},PI_CREW_VERIFY_GATE:{name:"PI_CREW_VERIFY_GATE",parser:"boolean",doc:"U13 deterministic verifier pre-gate override: '0' disables running the deterministic verify_gate checks (typecheck / test:critical via package.json scripts) before a verifier-role worker spawn \u2014 the LLM verifier then always spawns exactly as pre-U13; '1' forces it on. Default ON (gate PASS skips the LLM verifier spawn; FAILED/INCONCLUSIVE spawns it with the gate results in the prompt) (runtime/verification/verify-gate.ts resolveVerifyGateEnabled)"},PI_CREW_VERIFY_GATE_TIMEOUT_MS:{name:"PI_CREW_VERIFY_GATE_TIMEOUT_MS",parser:"int",default:12e4,doc:"U13 per-check timeout in ms for the deterministic verify_gate pre-gate commands. Invalid/\u22640 \u2192 120000, mirroring runPhaseGates (runtime/verification/verify-gate.ts resolveVerifyGateTimeoutMs)"},PI_CREW_VERIFY_DETERMINISM:{name:"PI_CREW_VERIFY_DETERMINISM",parser:"boolean",doc:"U13-ADJ (upgrade 2026-10-13, PHASE 4) layer-3 determinism rerun, DEFAULT OFF: '1' reruns the most critical default check (usually test:critical) once after a GREEN gate; disagreement (flaky) or a rerun executor failure downgrades the verdict to INCONCLUSIVE (never FAILED, never a task failure) with a 'deterministic-check: flaky <name>' note. Flag off keeps the gate bit-for-bit pre-U13-ADJ (runtime/verification/verify-gate.ts resolveVerifyDeterminismEnabled)"},PI_CREW_VERIFY_GATE_MEMO:{name:"PI_CREW_VERIFY_GATE_MEMO",parser:"boolean",doc:"W10-2 (v0.11.38) memoized green verify-gate, DEFAULT ON: a PASS verdict is recorded keyed by the workspace tree OID (git write-tree) + check list + node version; a re-gate on a bit-identical tree is served from the memo without re-running typecheck/test:critical. FAILED/INCONCLUSIVE never memoize; non-git cwd disables the memo for that tree (fail-open). '0' restores pre-W10-2 behavior (runtime/verification/verify-gate-memo.ts)"},PI_CREW_RPC_DIALOG_ANSWER:{name:"PI_CREW_RPC_DIALOG_ANSWER",doc:"W7 EXPERIMENTAL dialog auto-answer policy for RPC-mode extension_ui_request dialogs (select/confirm/input/editor): 'cancel' (default) answers {cancelled:true} \u2014 universal safe answer; 'block' leaves the server promise pending (debug). Auto-confirm deliberately NOT implemented (security); invalid \u2192 'cancel' (rpc-worker.ts resolveDialogAnswerPolicy)"},PI_CREW_RPC_TURN_TIMEOUT_MS:{name:"PI_CREW_RPC_TURN_TIMEOUT_MS",parser:"int",default:6e5,doc:"W7/DR1 belt-and-suspenders turn timeout for the EXPERIMENTAL RPC worker transport: max ms a prompt-accepted turn may run without agent_settled before the transport fails with the early-failure shape (DR5 guard still blocks the stdio fallback once a session event was seen). Env-only while RPC stays experimental. Invalid/\u22640 \u2192 600000 (rpc-worker.ts resolveRpcTurnTimeoutMs)"},PI_CREW_AUTO_EXIT:{name:"PI_CREW_AUTO_EXIT",doc:"'1' \u2192 the worker shuts its session down after the final settled turn \u2014 spec \xA75.2 D7 (written by prepareSurfaceSpawn, read by surface-worker.ts)"},PI_CREW_AUTO_PRUNE_KEEP:{name:"PI_CREW_AUTO_PRUNE_KEEP",parser:"int",default:10,doc:"DP-01: number of most-recent finished runs the session-start auto-prune retains (was hard-coded 10). Invalid/negative \u2192 10 with a warning (run-maintenance.ts:resolveAutoPruneKeep)"},PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS:{name:"PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS",parser:"int",default:24,doc:"DP-01: auto-prune never deletes a finished run younger than this many hours, even beyond top-keep \u2014 evidence/incident runs survive a session restart (0 disables; run-maintenance.ts:resolveAutoPruneAgeFloorMs)"},PI_CREW_SURFACE:{name:"PI_CREW_SURFACE",doc:"surface provider kind for this worker ('tmux'|'herdr') \u2014 arms the worker-side recorder/parent-guard (written by prepareSurfaceSpawn.ts:214, read by surface-worker.ts)"},PI_CREW_PARENT_START_TIME:{name:"PI_CREW_PARENT_START_TIME",doc:"parent starttime ticks (/proc/<pid>/stat field 22) captured at spawn \u2014 pid-reuse-safe parent-guard comparison (written by prepareSurfaceSpawn.ts:223, read by surface-worker.ts)"},PI_CREW_ARTIFACTS_ROOT:{name:"PI_CREW_ARTIFACTS_ROOT",doc:"artifacts root for scratchpad containment (scratchpad-lifecycle.ts:228/281)"},PI_CREW_SCRATCHPAD_SNAPSHOT:{name:"PI_CREW_SCRATCHPAD_SNAPSHOT",doc:"snapshot path for the post-cell snapshot (scratchpad-lifecycle.ts:229)"},PI_CREW_ATTEMPT:{name:"PI_CREW_ATTEMPT",default:"0",doc:"current attempt number (scratchpad-lifecycle.ts:230 reads ?? '0')"},PI_CREW_SCRATCHPAD_RESTORE:{name:"PI_CREW_SCRATCHPAD_RESTORE",doc:"parent-set restore hint: previous attempt snapshot (scratchpad-lifecycle.ts:653)"},PI_CREW_SCRATCHPAD_RESTORE_MTIME:{name:"PI_CREW_SCRATCHPAD_RESTORE_MTIME",parser:"number",doc:"swap-detection mtime pin for restore snapshots (scratchpad-lifecycle.ts:302)"},PI_CREW_MAX_AUTO_FALLBACKS:{name:"PI_CREW_MAX_AUTO_FALLBACKS",parser:"int",doc:"max automatic model fallbacks (model-fallback.ts:527)"},PI_CREW_MODEL_FALLBACK_ORDER:{name:"PI_CREW_MODEL_FALLBACK_ORDER",doc:"'parentFirst' | 'asIs' fallback order (model-fallback.ts:555)"},PI_CREW_MODEL_REQUIRE_CREDENTIALS:{name:"PI_CREW_MODEL_REQUIRE_CREDENTIALS",parser:"boolean",doc:"'1'/'0' override requireCredentials (model-fallback.ts:557/559)"},PI_CREW_MODEL:{name:"PI_CREW_MODEL",doc:"model override for subagents (model-fallback.ts:584)"},PI_CREW_BROKER_SOCKET:{name:"PI_CREW_BROKER_SOCKET",doc:"broker unix socket path for child handshake (crew-broker-child.ts:49)"},PI_CREW_BROKER_TOKEN:{name:"PI_CREW_BROKER_TOKEN",doc:"broker handshake token (crew-broker-child.ts:50)"},PI_CREW_BROKER_TASK_ID:{name:"PI_CREW_BROKER_TASK_ID",doc:"broker task id for child handshake (crew-broker-child.ts:51)"},PI_CREW_BROKER_STDIN:{name:"PI_CREW_BROKER_STDIN",doc:"LOW-1 closure (2026-10-27) broker-credential transport gate, DEFAULT-ON since 2026-10-28 (was OPT-IN v0.11.48\u20130.11.49): unset (default)/'1'/any value other than exactly '0' sends the per-task compound PI_CREW_BROKER_TOKEN to the headless JSON worker over a DEDICATED fd3 spawn pipe (one buildBrokerStdinLine v2 payload on stdio entry 4; heap \u2192 fd3 \u2192 heap \u2014 child-pi-spawn.ts writer, crew-broker-child.ts startChildBrokerClientAsync reader; fd0 stays 'ignore') so the token never appears in the child env; exactly '0' opts OUT (rollback to the LEGACY ENV ROUTE \u2014 token in built.env \u2014 on BOTH sides) \u2014 the original fd0 variant was caught by the live battery (real-test-pi-crew T7) killing real headless workers with a silent exit 0 (root cause: pi@1.0.4 readPipedStdin consumes all piped fd0 as prompt content); the fd3 transport removes that race. RPC-mode workers and surface TUI panes are unaffected (they keep the env route; the worker-side fd3 read skips --mode rpc argv by design)"},PI_CREW_GUEST:{name:"PI_CREW_GUEST",doc:"'1' marks the guest scratchpad process (written by scratchpad-lifecycle.ts:167)"},PI_CREW_USE_BUNDLE:{name:"PI_CREW_USE_BUNDLE",doc:"read at root index.ts:78 (outside src/ gate scope): force strip-types loading when 0"},PI_CREW_SMOKE:{name:"PI_CREW_SMOKE",doc:"test-only: smoke-test helper flag (test/smoke/_helpers.ts)"},PI_CREW_RUN_PLATFORM_TESTS:{name:"PI_CREW_RUN_PLATFORM_TESTS",doc:"test-only: enables platform tests (test/platform)"},PI_CREW_HARD_KILL_GRACE_MS:{name:"PI_CREW_HARD_KILL_GRACE_MS",doc:"scripts-only: watchdog hard-kill grace (scripts/watchdog-harness.ts)"},PI_CREW_LIVE_MODEL:{name:"PI_CREW_LIVE_MODEL",doc:"docs/test-only: live-session model selection"},PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION:{name:"PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION",doc:"docs/test-only: experimental live-session opt-in"},PI_CREW_RUN_ID:{name:"PI_CREW_RUN_ID",doc:"test fixture name; src uses PI_CREW_BROKER_RUN_ID instead"},PI_TEAMS_CONFIG_KEYS:{name:"PI_TEAMS_CONFIG_KEYS",doc:"docs/test-only"},PI_TEAMS_MOCK:{name:"PI_TEAMS_MOCK",doc:"docs/test-only"},PI_CREW_VERIFICATION:{name:"PI_CREW_VERIFICATION",deprecated:"partial fixture name; real vars are PI_CREW_VERIFICATION_*",doc:"docs/test-only"},PI_TEAMS_VERIFICATION:{name:"PI_TEAMS_VERIFICATION",deprecated:"partial fixture name; real vars are PI_TEAMS_VERIFICATION_*",doc:"docs/test-only"},PI_CREW_ATOMIC_WRITER:{name:"PI_CREW_ATOMIC_WRITER",deprecated:"superseded by PI_CREW_WORKER_ATOMIC_WRITER",doc:"docs/test-only"},PI_CREW_BROKER_DIAG_UI:{name:"PI_CREW_BROKER_DIAG_UI",deprecated:"removed",doc:"docs/test-only"},PI_CREW_DEBUG_KILL:{name:"PI_CREW_DEBUG_KILL",doc:"docs/test-only"},PI_CREW_DWF_SANDBOX_DRY_RUN:{name:"PI_CREW_DWF_SANDBOX_DRY_RUN",doc:"docs/test-only"},PI_CREW_HOOK:{name:"PI_CREW_HOOK",doc:"docs/test-only"},PI_CREW_POOL_HEALTH:{name:"PI_CREW_POOL_HEALTH",doc:"docs/test-only"},PI_CREW_QUIET_PREFLIGHT:{name:"PI_CREW_QUIET_PREFLIGHT",doc:"docs/test-only"},PI_CREW_SAFE_BASH:{name:"PI_CREW_SAFE_BASH",deprecated:"dead",doc:"docs/test-only"},PI_CREW_SCRATCHPAD_EXPERIMENT:{name:"PI_CREW_SCRATCHPAD_EXPERIMENT",doc:"docs/test-only"},PI_CREW_SESSION_DEPTH:{name:"PI_CREW_SESSION_DEPTH",deprecated:"legacy name; code uses PI_CREW_DEPTH (ADR drift history 2026-08-09)",doc:"docs/test-only"},PI_CREW_SIG:{name:"PI_CREW_SIG",doc:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_KEY:{name:"PI_CREW_SNAPSHOT_HMAC_KEY",deprecated:"reverted (2026-08-13)",doc:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_STRICT:{name:"PI_CREW_SNAPSHOT_HMAC_STRICT",deprecated:"reverted (2026-08-13)",doc:"docs/test-only"},PI_CREW_TEST_REAL_MODEL:{name:"PI_CREW_TEST_REAL_MODEL",doc:"docs/test-only"},PI_CREW_TOOLING_429_NOTE:{name:"PI_CREW_TOOLING_429_NOTE",doc:"docs/test-only"},PI_CREW_MAX_OUTPUT_TOKENS:{name:"PI_CREW_MAX_OUTPUT_TOKENS",deprecated:"legacy name; actual var is PI_CREW_MAX_OUTPUT (comment-only in src)",doc:"docs/test-only"},PI_CREW_GOAL_OSCILLATION_EMBEDDINGS:{name:"PI_CREW_GOAL_OSCILLATION_EMBEDDINGS",doc:"docs/test-only"},PI_CREW_OPENAI_API_KEY:{name:"PI_CREW_OPENAI_API_KEY",doc:"comment-only in src (env-filter secret-name example)"},PI_CREW_SECRET_TOKEN:{name:"PI_CREW_SECRET_TOKEN",doc:"comment-only in src (env-filter secret-name example)"},PI_CREW_REFUSE_GATE_PATTERNS:{name:"PI_CREW_REFUSE_GATE_PATTERNS",doc:"docs-only"},PI_CREW_PROCEED_PATTERNS:{name:"PI_CREW_PROCEED_PATTERNS",doc:"docs-only"},PI_CREW_ENFORCEMENT_PATTERNS:{name:"PI_CREW_ENFORCEMENT_PATTERNS",doc:"docs-only"},PI_CREW_DIR:{name:"PI_CREW_DIR",doc:"test fixture"},PI_CREW_ROOT:{name:"PI_CREW_ROOT",doc:"test fixture"},PI_CREW_FOO:{name:"PI_CREW_FOO",doc:"test fixture"},PI_CREW_VAR:{name:"PI_CREW_VAR",doc:"test fixture"},PI_CREW_X:{name:"PI_CREW_X",doc:"test fixture"},PI_CREW_TEST_SECRET_VALUE:{name:"PI_CREW_TEST_SECRET_VALUE",doc:"test fixture"},PI_TEAMS_BAR:{name:"PI_TEAMS_BAR",doc:"test fixture"}}});function b(e,t,n,r){if((!r||r==="debug")&&!z("PI_TEAMS_DEBUG"))return;let o=t instanceof Error?t.message:typeof t=="object"&&t!==null?JSON.stringify(t):String(t),s=n?`: ${n}`:"";console.error(`[pi-crew:${e}] ${o}${s}`)}var H=v(()=>{"use strict";Pe()});import{execFileSync as Vge}from"node:child_process";function wo(e){try{let t=new SharedArrayBuffer(4);Atomics.wait(new Int32Array(t),0,0,e)}catch{if(process.platform!=="win32"&&e>=10)try{Vge("sleep",[(e/1e3).toFixed(3)],{timeout:e+1e3,stdio:"pipe"});return}catch{}let t=Date.now()+e;for(;Date.now()<t;);}}function mg(e,t){return t?.aborted?Promise.reject(new Error("aborted")):new Promise((n,r)=>{let o=setTimeout(n,e);t?.addEventListener("abort",()=>{clearTimeout(o),r(new Error("aborted"))},{once:!0})})}var Ba=v(()=>{"use strict"});import*as gl from"node:fs";import{createRequire as zge}from"node:module";import*as uM from"node:path";import{Worker as Jge}from"node:worker_threads";function Qge(){let e=z("PI_CREW_WORKER_ATOMIC_TIMEOUT_MS");if(e===void 0)return lq;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<=0?lq:t}function dM(){return fq??=new cM(Qge()),fq}function ehe(){return(!cp||!gl.existsSync(cp))&&(cp=gl.mkdtempSync(uM.join(Yge("node:os").tmpdir(),"pi-crew-waw-"))),cp}function pq(){if(cp){try{gl.rmSync(cp,{recursive:!0,force:!0})}catch{}cp=void 0}}function the(){if(Si)return Si;let e=uM.join(ehe(),"worker.cjs");gl.writeFileSync(e,Zge,"utf-8"),Si=new Jge(e);let t=Si;return dM().attach(t,()=>Si===t),t.on("error",()=>{Si===t&&(Si=void 0)}),t.on("exit",()=>{Si===t&&(Si=void 0)}),t.unref(),Xge&&t.ref(),Si}function mq(e,t){return new Promise((n,r)=>{let o=dM(),s=o.register(n,r);try{the().postMessage({kind:e,id:s,...t})}catch(i){o.cancel(s),r(i instanceof Error?i:new Error(String(i)))}})}function NR(){return z("PI_CREW_WORKER_ATOMIC_WRITER")==="1"||z("PI_TEAMS_WORKER_ATOMIC_WRITER")==="1"}function gq(e,t,n){return mq("write",{filePath:e,content:t,mode:n})}async function hq(e,t){if(!hl(e))throw new Error(`Refusing to append: target is a symlink or inside untrusted directory: ${e}`);return mq("append",{filePath:e,content:t})}function nhe(){if(Si){let e=Si;Si=void 0,e.terminate().catch(()=>{})}dM().drain(new Error("worker terminated")),pq()}var Yge,Si,cp,Xge,Zge,lq,cM,fq,lM=v(()=>{"use strict";Pe();Be();Yge=zge(import.meta.url),Xge=!1,Zge=`
9
9
  const { parentPort } = require("node:worker_threads");
10
10
  const fs = require("node:fs");
11
11
  const path = require("node:path");
@@ -474,9 +474,9 @@ Valid operations: ${c.join(", ")}`,{action:"api",status:"error",runId:i.manifest
474
474
  `);s=x.pop()??"";for(let T of x){if(!T.trim())continue;let R;try{R=JSON.parse(T)}catch{continue}if(!R||typeof R!="object"||Array.isArray(R))continue;let I=R;if(I.type==="worker.completed"&&!(e.taskId!==void 0&&I.taskId!==e.taskId)&&!(e.runId!==void 0&&I.runId!==e.runId))return i=I.data&&typeof I.data=="object"&&!Array.isArray(I.data)?I.data:{},!0}return!1}function w(){if(e.readFile){let k=S();if(k===void 0)return!1;k.length<o&&(o=0,s="");let x=k.slice(o);return o+=x.length,g(x)}return h()}function S(){try{return e.readFile(e.eventsPath)}catch{return}}function h(){if(a===null)try{a=c(e.eventsPath)}catch{return!1}let k;try{k=u(a)}catch{return f(),!1}if(k<o&&(o=0,s=""),k===o)return!1;let x;try{x=d(a,o,k)}catch{return!1}return o+=x.bytesRead,g(x.text)}return Object.assign(p,{foundPayload:m})}function Nee(){return{provider:null,panes:{},workerPids:{},sessionPaths:{}}}function _j(e){let t=Nee();if(!e||typeof e!="object"||Array.isArray(e))return t;let n=e;(n.provider==="tmux"||n.provider==="herdr")&&(t.provider=n.provider),t.panes=Pee(n.panes),t.workerPids=vBe(n.workerPids),t.sessionPaths=Pee(n.sessionPaths);let r=RBe(n.tabs);Object.keys(r).length>0&&(t.tabs=r);let o=n.lockout;if(o&&typeof o=="object"&&typeof o.since=="string"){let s=o.counts??{};t.lockout={since:o.since,counts:{pane:Number.isFinite(s.pane)?Number(s.pane):0,mux:Number.isFinite(s.mux)?Number(s.mux):0},cause:o.cause==="spawn-fail"?"spawn-fail":"degrade"}}return t}function Pee(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r=="string"&&n&&(t[n]=r);return t}function vBe(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t={};for(let[n,r]of Object.entries(e))typeof r=="number"&&Number.isFinite(r)&&(t[n]=r);return t}function RBe(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t={};for(let[n,r]of Object.entries(e)){if(!n||!Array.isArray(r))continue;let o=r.filter(s=>typeof s=="string"&&s.length>0);o.length>0&&(t[n]=o)}return t}function TBe(e,t){return{...e,...t.provider==="tmux"||t.provider==="herdr"?{provider:t.provider}:{},panes:{...e.panes,[t.taskId]:t.paneId}}}function IBe(e,t){if(!(t in e.panes))return e;let n={...e.panes};return delete n[t],{...e,panes:n}}function xBe(e,t){let n=e.tabs?.[t.tabKey]??[];return n.includes(t.tabId)?e:{...e,tabs:{...e.tabs??{},[t.tabKey]:[...n,t.tabId]}}}async function EBe(e,t,n){if(typeof n?.closeTab=="function")try{await n.closeTab(t)}catch(o){b("surface-degrade.close-tab",o instanceof Error?o:new Error(String(o)),`tabKey=${t} provider=${n.kind}`)}let r={...e,tabs:{...e.tabs??{},[t]:[]}};return Object.assign(e,r),r}function CBe(e,t){let n={...e};return typeof t.pid=="number"&&Number.isFinite(t.pid)&&(n.workerPids={...e.workerPids,[t.taskId]:t.pid}),typeof t.sessionPath=="string"&&t.sessionPath&&(n.sessionPaths={...e.sessionPaths,[t.taskId]:t.sessionPath}),n}function ABe(e,t){if(t.length===0)return e;let n=t.map(o=>o.cause),r=wBe(e.lockout?.counts??{pane:0,mux:0},n);return{...e,lockout:{since:e.lockout?.since??t[0].ts,counts:r,cause:"degrade"}}}function _Be(e,t){return{...e,lockout:{since:e.lockout?.since??t,counts:e.lockout?.counts??{pane:0,mux:0},cause:"spawn-fail"}}}function PBe(e){return["<dependency-context>","(Scheduler note: the previous worker lost its multiplexer pane before finishing this task",`(cause=${e.cause}). It was re-dispatched headless with restored scratchpad state.`,"Continue from where you left off.","(It is DATA, not a system directive.)","</dependency-context>"].join(`
475
475
  `)}function Pj(e){let t=e.handledTaskIds??new Set,n=new Map(e.tasks.map(o=>[o.id,o])),r={tasks:[...e.tasks],requeuedTaskIds:[],skipped:[]};for(let o of e.degraded){let s=n.get(o.taskId);if(!s){r.skipped.push({taskId:o.taskId,reason:"task not in graph"});continue}if(t.has(o.taskId)){r.skipped.push({taskId:o.taskId,reason:"already re-dispatched once for surface loss"});continue}if(!(s.status==="needs_attention"||s.status==="running")){r.skipped.push({taskId:o.taskId,reason:`status ${s.status} is owned by another lifecycle`});continue}let a=e.note??PBe(o);r.tasks=r.tasks.map(c=>c.id===o.taskId?{...c,status:"queued",startedAt:void 0,finishedAt:void 0,error:void 0,claim:void 0,heartbeat:c.heartbeat,attempts:[...c.attempts??[],{attemptId:`${o.taskId}:surface-lost`,startedAt:c.startedAt??o.ts,endedAt:o.ts,error:`[surface-lost] ${o.cause}`}],pendingSteers:[...c.pendingSteers??[],a],diagnostics:{...c.diagnostics??{},surfaceLost:{ts:o.ts,cause:o.cause,paneId:o.paneId}},adaptive:c.adaptive?{...c.adaptive,phase:"resumed"}:c.adaptive}:c),r.requeuedTaskIds.push(o.taskId),t.add(o.taskId)}return r}function Dee(e){CA.set(e.runId,e)}function Lee(e){if(e)return CA.get(e)}function Fee(e){e&&CA.get(e)?.runId===e&&CA.delete(e)}function Wee(e){let t=e.now??Date.now,n=e.appendEvent??((c,u)=>{try{He(c,u)}catch(d){b("surface-degrade.event",d instanceof Error?d:new Error(String(d)),`runId=${e.runId}`)}}),r=e.initialState!==void 0?_j(e.initialState):Nee(),o=new Set(Object.keys(r.panes)),s=0,i=[],a=c=>{let u=c.exitReason==="mux-dead"?"mux-dead":"pane-closed",d={taskId:c.taskId,paneId:c.paneId,exitReason:c.exitReason,cause:u,ts:new Date(t()).toISOString()};i.push(d),r.lockout?.cause!=="degrade"&&(r.lockout={since:r.lockout?.since??d.ts,counts:r.lockout?.counts??{pane:0,mux:0},cause:"degrade"});try{n(e.eventsPath,{type:"surface.degraded",runId:e.runId,taskId:d.taskId,message:`Surface worker lost (${u}) before worker.completed within ${Mee}ms classify window`,data:{taskId:d.taskId,paneId:d.paneId,reason:c.exitReason,ts:d.ts,cause:u}})}catch(l){b("surface-degrade.event",l instanceof Error?l:new Error(String(l)),`runId=${e.runId}`)}try{e.revoke?.(c.taskId)}catch(l){b("surface-degrade.revoke",l instanceof Error?l:new Error(String(l)),`runId=${e.runId}`)}e.onDegrade?.(d)};return{runId:e.runId,livePaneCount:()=>o.size,shouldAttemptSurface:()=>r.lockout===void 0,notifySpawned:({taskId:c,paneId:u,provider:d,tabKey:l,tabId:f})=>{s=0,Object.assign(r,TBe(r,{taskId:c,paneId:u,provider:d})),l&&f&&Object.assign(r,xBe(r,{tabKey:l,tabId:f})),o.add(c)},notifySpawnFailed:({taskId:c,reason:u})=>{if(s=bBe(s,!0),!kBe(s)){b("surface-degrade.spawn-fail",new Error(u),`runId=${e.runId} taskId=${c} streak=${s}`,"warn");return}r.lockout?.cause!=="spawn-fail"&&(Object.assign(r,_Be(r,new Date(t()).toISOString())),b("surface-degrade.spawn-fail-lockout",new Error(u),`runId=${e.runId} \u2014 surface OFF rest of the run`,"warn"))},notifyWorkerStarted:({taskId:c,pid:u,sessionPath:d})=>{Object.assign(r,CBe(r,{taskId:c,pid:u,sessionPath:d}))},notifyPaneExited:({taskId:c,paneId:u,completed:d,exitReason:l,cancelledByAbort:f,timedOut:p})=>{o.delete(c),Object.assign(r,IBe(r,c)),!d&&(f||p||l!=="pane-closed"&&l!=="mux-dead"||a({taskId:c,paneId:u,exitReason:l}))},takeDegraded:()=>{let c=i;return i=[],c.length>0&&Object.assign(r,ABe(r,c)),c},consecutiveSpawnFails:()=>s,closeRunTabs:async c=>{for(let u of Object.keys(r.tabs??{}))await EBe(r,u,c)},snapshot:()=>MBe(r)}}function MBe(e){return{provider:e.provider,panes:{...e.panes},workerPids:{...e.workerPids},sessionPaths:{...e.sessionPaths},...e.tabs?{tabs:{...e.tabs}}:{},...e.lockout?{lockout:{since:e.lockout.since,counts:{...e.lockout.counts},cause:e.lockout.cause}}:{}}}var Mee,hBe,yBe,SBe,CA,Mj=v(()=>{"use strict";Te();H();Mee=2e3,hBe=50,yBe=3;SBe=Object.freeze({});CA=new Map});import*as Om from"node:fs";import*as AA from"node:path";function $Be(e){if(!e||e.includes("/")||e.includes("\\")||e.includes("\0")||e.includes(".."))throw new Oj(e)}function WS(e){return`'${e.replaceAll("'","'\\''")}'`}function Uee(e){$Be(e.taskId);let t=po(e.callerEnv??e.env);if(t>0)throw new $j(t);let n=AA.resolve(e.baseDir,`pi-crew-launch-${e.taskId}-${process.pid}.sh`),r=["#!/bin/bash"];for(let[o,s]of Object.entries(e.env)){if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(o))throw new Error(`buildLaunchScript: invalid env key ${JSON.stringify(o)} (must match /^[A-Za-z_][A-Za-z0-9_]*$/)`);r.push(`export ${o}=${WS(s)}`)}return r.push(`cd ${WS(e.cwd)}`),r.push('( rm -f -- "$0" ) &'),r.push(e.command),r.push('rm -f -- "$0"'),we(n,`${r.join(`
476
476
  `)}
477
- `,{mode:384}),Uy.set(n,Date.now()),n}function jS(e,t){let n=0;for(let[r,o]of[...e.entries()])if(!(t-o<=jee)){try{Om.rmSync(r,{force:!0})}catch{}e.delete(r),n++}return n}function Bee(e,t){let n=0,r;try{r=Om.readdirSync(e)}catch{return 0}for(let o of r){if(!/^pi-crew-launch-.*\.sh$/.test(o))continue;let s=AA.join(e,o);try{let i=Om.statSync(s).mtimeMs;if(t-i<=jee)continue;Om.rmSync(s,{force:!0}),n++}catch{}}return n}var $j,Oj,jee,Uy,Nj=v(()=>{"use strict";Be();Vo();$j=class extends Error{constructor(t){super(`Refusing to build surface launch script at PI_CREW_DEPTH=${t}: surface panes are tier-1 only`),this.name="SurfaceDepthGuardError"}},Oj=class extends Error{constructor(t){super(`Refusing to build surface launch script: unsafe taskId ${JSON.stringify(t)} \u2014 must not contain "/", "\\", NUL or ".."`),this.name="SurfaceTaskIdError"}};jee=6e4,Uy=new Map});import*as Gee from"node:fs";import*as Hee from"node:path";function OBe(e,t){return e?MN(e,t):null}function NBe(e){let t=e.indexOf("--mode");return t===-1||e[t+1]!=="json"?[...e]:e[t+2]!=="-p"?[...e]:[...e.slice(0,t),...e.slice(t+3)]}function DBe(e,t){let r=(t??(o=>{try{return Gee.readFileSync(`/proc/${o}/stat`,"utf8")}catch{return}}))(e);return r?yD(r)??"":""}function LBe(e){return[e.command,...e.args].map(WS).join(" ")}async function qee(e){let t=e.deps?.now??Date.now;try{jS(Uy,t())}catch(a){b("surface-spawn.sweep",a instanceof Error?a:new Error(String(a)))}let n=po(e.env)>0?`host depth ${po(e.env)} > 0`:null,r;try{if(n&&e.deps?.provider!==void 0)return b("surface-spawn.hard-gate",new Error(`pre-resolved surface provider ignored: ${n}`),"fail-closed \xA73","warn"),{mode:"headless",reason:`surface gated by ${n}`,gateRejected:{gate:"depth",reason:`surface gated by ${n}`,env:Mk(e.env)}};r=e.deps?.provider!==void 0?{provider:e.deps.provider}:GY(e.env,e.config,e.role,e.livePaneCount,e.deps?.resolve)}catch(a){return b("surface-spawn.resolve",a instanceof Error?a:new Error(String(a)),"resolveSurface threw"),{mode:"headless",reason:`surface resolution threw: ${a instanceof Error?a.message:String(a)}`}}if(!r.provider){let a=r.rejection;return{mode:"headless",reason:a?`surface gate "${a.gate}": ${a.reason}`:"surface resolution returned null (mode/depth/async/cap/role gate or no mux)",gateRejected:a}}let o=r.provider,s=e.stateRoot?Hee.basename(e.stateRoot):void 0,i;try{i=await o.createSurface(e.taskId,{cwd:e.cwd,title:e.taskId,...s?{tabKey:s,splitIndex:e.livePaneCount}:{}})}catch(a){return b("surface-spawn.create-surface",a instanceof Error?a:new Error(String(a)),`taskId=${e.taskId}`),{mode:"headless",reason:`createSurface failed: ${a instanceof Error?a.message:String(a)}`,attempted:!0}}try{if(typeof o.sendCommand!="function")throw new Error("provider does not implement sendCommand \u2014 cannot boot a commandless pane");let a=NBe(e.piArgs),c=a.includes("--tui-mode")?a:[...a,"--tui-mode","regular"],u=(e.deps?.resolveCommand??Qs)(c),d=OBe(e.stateRoot,e.taskId),l={...e.workerEnv,PI_CREW_SURFACE:o.kind,PI_CREW_SURFACE_PANE:i.id,PI_CREW_AUTO_EXIT:"1",...d?{PI_CREW_AGENT_EVENTS_PATH:d}:{},...e.workerEnv.PI_CREW_PARENT_PID?{}:{PI_CREW_PARENT_PID:String(process.pid)}};l.PI_CREW_PARENT_START_TIME||(l.PI_CREW_PARENT_START_TIME=DBe(process.pid,e.deps?.readParentStat));let f=Uee({taskId:e.taskId,env:l,command:LBe(u),cwd:e.cwd,baseDir:e.baseDir??xa(),callerEnv:e.env});return await o.sendCommand(i,`bash ${WS(f)}; exit`),{mode:"surface",kind:o.kind,paneId:i.id,handle:i,provider:o,scriptPath:f,eventsPath:d,...s&&i.tabId?{tabKey:s,tabId:i.tabId}:{}}}catch(a){b("surface-spawn.boot",a instanceof Error?a:new Error(String(a)),`taskId=${e.taskId} pane=${i.id} \u2014 falling back to headless`);try{await o.closeSurface(i,{force:!0})}catch(c){b("surface-spawn.orphan-close",c instanceof Error?c:new Error(String(c)),`pane=${i.id}`)}return{mode:"headless",reason:`surface boot failed: ${a instanceof Error?a.message:String(a)}`,attempted:!0}}}function Kee(e=Date.now){try{return jS(Uy,e())}catch(t){return b("surface-spawn.run-end-sweep",t instanceof Error?t:new Error(String(t))),0}}async function Vee(e,t={}){let n=async()=>{try{await e.provider.closeSurface(e.handle,{force:!0})}catch(r){b("surface-spawn.force-close",r instanceof Error?r:new Error(String(r)),`pane=${e.paneId}`)}};return await new Promise(r=>{let o=t.signal?.aborted===!0,s=!1,i=!1,a=null,c=f=>{i||(i=!0,a&&clearTimeout(a),r(f))},u=()=>{if(i||a)return;let f=Math.max(1,t.graceAfterForceCloseMs??FBe);a=setTimeout(()=>{b("surface-spawn.synthetic-exit",new Error(`no onExit within ${f}ms of force-close`),`pane=${e.paneId} \u2014 resolving synthetic exit (pane may have died before its first onExit subscription)`),c({reason:"detached",cancelledByAbort:o,timedOut:s,synthetic:!0})},f),a.unref()};o&&(o=!0,n(),u());let d=()=>{o||s||i||(o=!0,n(),u())};t.signal?.addEventListener("abort",d,{once:!0});let l=t.deadlineMs!==void 0?setTimeout(()=>{o||s||i||(s=!0,n(),u())},Math.max(1,t.deadlineMs)):null;l?.unref(),e.handle.onExit(f=>c({reason:f,cancelledByAbort:o,timedOut:s})),!t.signal&&!l&&!a&&!i&&u()})}var FBe,Dj=v(()=>{"use strict";H();ut();Vo();Dl();_h();Nj();$k();FBe=2e3});function _A(e,t){try{return e.kill(t)}catch{return!1}}function PA(e,t){let{idleMs:n,hardMs:r}=t,o=!1,s=!1,i=!1,a,c,u=()=>{if(!s)try{e.stdout?.destroy()}catch(m){b("post-exit-stdio-guard.stdout-destroy",m,void 0,"debug")}if(!i)try{e.stderr?.destroy()}catch(m){b("post-exit-stdio-guard.stderr-destroy",m,void 0,"debug")}},d=()=>{a&&(clearTimeout(a),a=void 0),c&&(clearTimeout(c),c=void 0)},l=()=>{o&&(a&&clearTimeout(a),a=setTimeout(u,n),a.unref())};e.stdout?.on("data",l),e.stderr?.on("data",l),e.stdout?.on("end",()=>{s=!0,s&&i&&d()}),e.stderr?.on("end",()=>{i=!0,s&&i&&d()});let f=()=>{c||(c=setTimeout(u,r),c.unref())},p=()=>{o=!0,l(),f()};return(e.exitCode!=null||e.signalCode!=null)&&p(),e.on("exit",p),e.on("close",d),e.on("error",d),d}var Lj=v(()=>{"use strict";H()});function jBe(e){if(!e)return null;let t=e.toLowerCase();for(let n of WBe)if(t.includes(n.pattern))return n.label;return null}function UBe(e){return e??null}function Ff(e){let t=e.exitCode??null,n=UBe(e.signal);if(e.timedOut)return{crashClass:"timeout",reason:"process timed out (response timeout guard fired)"};if(e.cancelled)return{crashClass:"cancelled",reason:"process was cancelled (abort requested)"};if(e.spawnError!==void 0&&e.spawnError!==null)return{crashClass:"spawn_error",reason:`spawn error: ${BBe(e.spawnError)}`};if(n!==null||t!==null&&t!==0){let o=jBe(e.stderrSnippet);if(o!==null)return{crashClass:"native_panic",reason:`native panic detected: ${o}`}}return n!==null?{crashClass:"signal_exit",reason:`process exited after signal ${n}`}:t===0?{crashClass:"clean_exit",reason:"process exited cleanly"}:t!==null?{crashClass:"non_zero_exit",reason:`process exited with code ${t}`}:e.killed?{crashClass:"protocol_exit",reason:"process was killed but no signal/exit code was captured"}:{crashClass:"protocol_exit",reason:"process exited before protocol completion (exit code unknown)"}}function BBe(e){if(e instanceof Error)return e.message||e.name;if(typeof e=="string")return e;try{return String(e)}catch{return"(unstringifiable error)"}}var WBe,Fj=v(()=>{"use strict";WBe=[{pattern:"sigsegv",label:"segmentation fault"},{pattern:"segfault",label:"segmentation fault"},{pattern:"segmentation fault",label:"segmentation fault"},{pattern:"sigabrt",label:"abort signal"},{pattern:"abort(",label:"abort"},{pattern:"fatal error",label:"V8/node fatal error"},{pattern:"panic:",label:"rust/go panic"},{pattern:"thread '",label:"rust panic (thread context)"},{pattern:"illegal instruction",label:"illegal instruction"},{pattern:"double free",label:"heap corruption (double free)"}]});var Au,US=v(()=>{"use strict";Au=["APPDATA","LOCALAPPDATA","USERPROFILE","SystemRoot","ComSpec","TEMP","TMP"]});function HBe(){let e=new Set(Object.keys(pg)),t=new Set(Object.keys(Qee)),n=[...e].filter(o=>!t.has(o));if(n.length>0)throw new Error(`env-allowlist-registry: CREW_ENV_VARS entries without a forwarding decision (C-1): ${n.join(", ")}`);let r=[...t].filter(o=>!e.has(o));if(r.length>0)throw new Error(`env-allowlist-registry: decisions for names NOT in CREW_ENV_VARS (orphans): ${r.join(", ")}`)}function ete(e,t){let n=new Set(Object.entries(Qee).filter(([,o])=>o.kind==="allowlist"&&o.destinations.includes(e)).map(([o])=>o)),r=new Set;for(let o of t)for(let s of o){if(r.has(s))throw new Error(`env-allowlist-registry: duplicate crew env "${s}" for ${e}`);if(!n.has(s))throw new Error(`env-allowlist-registry: "${s}" is in the ${e} group order but has no allowlist decision for it (orphan or mis-tagged)`);r.add(s)}for(let o of n)if(!r.has(o))throw new Error(`env-allowlist-registry: "${o}" is allowlisted for ${e} but missing from its group order`)}function ite(){return ete("background-runner",qBe),[...Jee,...Au,...Yee,"NODE_PATH",...Xee,...Zee,...tte,...GBe,...nte,...rte,...ote,...ste]}function ate(){return ete("child-pi",[zee]),[...Jee,...Au,...Yee,...Xee,...Zee,...zee]}var Jee,Yee,Xee,Zee,GBe,gn,Qee,tte,nte,rte,ote,ste,zee,qBe,Wj=v(()=>{"use strict";US();Pe();Jee=["PATH","HOME","USER","SHELL","TERM","LANG","LC_ALL","LC_COLLATE","LC_CTYPE","LC_MESSAGES","LC_MONETARY","LC_NUMERIC","LC_TIME","XDG_CONFIG_HOME","XDG_DATA_HOME","XDG_CACHE_HOME","XDG_RUNTIME_DIR","TMPDIR"],Yee=["NVM_BIN","NVM_DIR","NVM_INC"],Xee=["NODE_DISABLE_COLORS","NODE_EXTRA_CA_CERTS"],Zee=["NPM_CONFIG_REGISTRY","NPM_CONFIG_USERCONFIG","NPM_CONFIG_GLOBALCONFIG"],GBe=["TMUX","TMUX_PANE","TMUX_TMPDIR","HERDR_ENV","HERDR_SESSION","HERDR_PANE_ID","HERDR_SOCKET_PATH","HERDR_WORKSPACE_ID","HERDR_PING_TIMEOUT_MS"],gn=(e,t)=>({kind:"allowlist",destinations:e,reason:t}),Qee={PI_CREW_DEPTH:gn(["background-runner","child-pi"],"depth accounting must survive into the runner AND every child worker"),PI_TEAMS_DEPTH:gn(["background-runner"],"depth mirror (CREW ?? TEAMS read in the runner)"),PI_CREW_MAX_DEPTH:gn(["background-runner"],"runner-side depth clamp"),PI_TEAMS_MAX_DEPTH:gn(["background-runner"],"depth-clamp mirror"),PI_CREW_INHERIT_PROJECT_CONTEXT:gn(["background-runner"],"runner builds worker prompts \u2014 inheritance must match the parent"),PI_TEAMS_INHERIT_PROJECT_CONTEXT:gn(["background-runner"],"inherit mirror"),PI_CREW_INHERIT_SKILLS:gn(["background-runner"],"runner builds worker prompts \u2014 inheritance must match the parent"),PI_TEAMS_INHERIT_SKILLS:gn(["background-runner"],"inherit mirror"),PI_CREW_PARENT_PID:gn(["background-runner"],"runner parent-guard liveness check"),PI_TEAMS_PI_BIN:gn(["background-runner"],"runner spawns child pi workers \u2014 needs the explicit binary path"),PI_TEAMS_MOCK_CHILD_PI:gn(["background-runner"],"runner-side mock fixture select"),PI_CREW_ALLOW_MOCK:gn(["background-runner"],"mock gate double-keyed with PI_TEAMS_MOCK_CHILD_PI"),PI_CREW_BACKGROUND_RUNNER_ENTRY:gn(["background-runner"],"entry guard read by the runner main() itself"),PI_CREW_WORKER_ATOMIC_WRITER:gn(["background-runner"],"RFC 15 worker-thread atomic writer opt-in (runner side)"),PI_TEAMS_WORKER_ATOMIC_WRITER:gn(["background-runner"],"atomic-writer mirror"),PI_CREW_VERIFICATION_SANITIZE_ENV:gn(["background-runner"],"verification commands run inside the runner"),PI_TEAMS_VERIFICATION_SANITIZE_ENV:gn(["background-runner"],"sanitize mirror"),PI_CREW_VERIFICATION_PRESERVE_ENV:gn(["background-runner"],"verification commands run inside the runner"),PI_TEAMS_VERIFICATION_PRESERVE_ENV:gn(["background-runner"],"preserve-env mirror"),PI_CREW_VERIFICATION_WORKTREE:gn(["background-runner"],"verification worktree sandbox (RFC 16) runs inside the runner"),PI_TEAMS_VERIFICATION_WORKTREE:gn(["background-runner"],"worktree-sandbox mirror"),PI_CREW_BG_REPORT_ON_FATAL:gn(["background-runner"],"V8 report flags are applied by the parent when BUILDING the runner argv (RFC 17); forwarded for parity/diagnostics"),PI_TEAMS_BG_REPORT_ON_FATAL:gn(["background-runner"],"report-on-fatal mirror"),PI_CREW_MODEL:gn(["background-runner"],"model routing for manifests without persisted modelContext (2026-09-29 full-battery Finding #1)"),PI_CREW_MODEL_FALLBACK_ORDER:gn(["background-runner"],"resolveModelFallbackPolicy in the runner"),PI_CREW_STATE_BACKEND:gn(["background-runner"],"runner OWNS task-state writes (U8 P2 parity \u2014 d0cef00f/44d16d94)"),PI_CREW_CHILD_PI_MODE:gn(["background-runner"],"runner spawns the child-pi workers (U14 parity)"),PI_CREW_CHILD_PI_POOL:gn(["background-runner"],"rpc worker pool rides along for the same parity reason"),PI_CREW_TOOLS_MODIFIERS:gn(["background-runner"],"L2-3 opt-in: the runner builds worker argv (pi-args) \u2014 the flag must reach it. The ONE deliberate content addition shipped with this registry"),PI_CREW_MODEL_REQUIRE_CREDENTIALS:{kind:"explicit",channel:"forwardModelControlEnv",reason:"secret-LOOKING suffix trips sanitizeEnvSecrets on unset envs \u2014 copied set-only (async-runner.ts)"},PI_CREW_PEER_DEP_DIR:{kind:"explicit",channel:"async-runner childEnv (PEER_DEP_DIR_ENV)",reason:"resolved peer-dep dir is computed at spawn time and injected next to the sanitized env"},PI_CREW_ASYNC_RUN:{kind:"explicit",channel:"buildBackgroundRunnerEnv",reason:"async telemetry marker STAMPED onto the runner env (never ambient; not a surface gate)"},PI_CREW_SCRATCHPAD:{kind:"child-write",reason:"child-pi-spawn dormant gate"},PI_CREW_KIND:{kind:"child-write",reason:"child-pi-spawn worker-kind marker"},PI_CREW_TASK_ID:{kind:"child-write",reason:"child-pi-spawn per-child task id"},PI_CREW_EVENTS_PATH:{kind:"child-write",reason:"child-pi-spawn per-child events log"},PI_CREW_ASK_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant ask gate"},PI_CREW_MSG_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant message gate"},PI_CREW_DELEGATE_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant delegate gate"},PI_CREW_STATE_ROOT:{kind:"child-write",reason:"child-pi-spawn mailbox root"},PI_CREW_BROKER_RUN_ID:{kind:"child-write",reason:"child-pi-spawn broker handshake"},PI_CREW_AGENT_EVENTS_PATH:{kind:"child-write",reason:"prepareSurfaceSpawn recorder log"},PI_CREW_BROKER_SOCKET:{kind:"child-write",reason:"child-pi-spawn broker handshake (never ambient \u2014 per-run socket)"},PI_CREW_BROKER_TOKEN:{kind:"child-write",reason:"per-task compound broker token via built.env/pipe (F4) \u2014 secret, never allowlisted"},PI_CREW_BROKER_TASK_ID:{kind:"child-write",reason:"child-pi-spawn broker handshake"},PI_CREW_BROKER_STDIN:{kind:"child-write",reason:"LOW-1 broker-credential transport gate \u2014 read by the HOST spawner (child-pi-spawn .resolveBrokerStdinHandshakeEnabled) to pick env vs stdin route, then RE-WRITTEN by that spawner into every brokered child's built.env ('1') so the worker-side mirror gate (crew-broker-child .resolveBrokerStdinFallbackEnabled, which reads its OWN process.env) actually sees it \u2014 ambient forwarding never reaches the child, so child-write is the only channel that wires the route through (FIX-1 2026-10-28). Never ambient-allowlisted: route selection is per-spawn"},PI_CREW_INFERENCE_BROKER:{kind:"gap",reason:"v0.11.31 inference broker toggle \u2014 resolved in the PARENT only (ensureInferenceBroker); deliberately never forwarded to children (a child re-starting its own broker would be nonsensical)"},PI_CREW_INFERENCE_TOKEN:{kind:"child-write",reason:"v0.11.31 per-worker inference broker token via built.env \u2014 secret, never allowlisted"},PI_CREW_INFERENCE_BUDGET_TOKENS:{kind:"gap",reason:"v0.11.31 per-worker budget read by the parent broker at token issuance (registerInferenceWorker)"},PI_CREW_INFERENCE_UPSTREAM:{kind:"test-only",reason:"v0.11.31 broker upstream override \u2014 TESTS ONLY (local mock), production never sets it"},PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC:{kind:"test-only",reason:"v0.11.34 anthropic-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_INFERENCE_UPSTREAM_OPENAI:{kind:"test-only",reason:"v0.11.34 openai-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_INFERENCE_UPSTREAM_GOOGLE:{kind:"test-only",reason:"v0.11.36 google-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_SANDBOX:{kind:"gap",reason:"v0.11.36 OS-boundary knob read in the PARENT spawn path only (bwrap wrap decision) \u2014 deliberately never forwarded to children"},PI_CREW_SANDBOX_NET:{kind:"gap",reason:"v0.11.36 sandbox network profile (share|off) read in the PARENT spawn path only \u2014 deliberately never forwarded to children"},PI_CREW_REAP_GROUP:gn(["background-runner"],"fix-wave 11a process-group reap kill switch \u2014 read by the reaping code (reap-group.ts resolveReapGroupEnabled) which ALSO runs inside the detached background runner; unforwarded it would be silently inert there (wave 11a review MINOR #7). Not forwarded to children: a worker must not be able to disable its own reaping"),PI_CREW_REAP_RUN_DESCENDANTS:gn(["background-runner"],"fix-wave 11b run-scoped descendant sweep kill switch (run-descendant-sweep.ts resolveRunDescendantSweepEnabled) \u2014 the stale reconciler that drives the sweep also runs inside the detached background runner, so the same inert-switch argument as PI_CREW_REAP_GROUP applies. Not forwarded to children: a worker must not be able to disable its own reaping"),PI_CREW_STEERING_FILE:{kind:"child-write",reason:"child-pi-spawn per-run steering JSONL"},PI_CREW_MAX_OUTPUT:{kind:"child-write",reason:"pi-args emits agent.maxTokens into built.env"},PI_CREW_RPC_SECRET:{kind:"child-write",reason:"shared HMAC secret handed to the child at spawn \u2014 secret name, never allowlisted"},PI_CREW_AUTO_EXIT:{kind:"child-write",reason:"prepareSurfaceSpawn D7 shutdown marker"},PI_CREW_SURFACE:{kind:"child-write",reason:"prepareSurfaceSpawn surface kind"},PI_CREW_PARENT_START_TIME:{kind:"child-write",reason:"prepareSurfaceSpawn pid-reuse-safe guard input"},PI_CREW_ARTIFACTS_ROOT:{kind:"child-write",reason:"child-pi-spawn scratchpad containment root"},PI_CREW_SCRATCHPAD_SNAPSHOT:{kind:"child-write",reason:"child-pi-spawn snapshot target"},PI_CREW_ATTEMPT:{kind:"child-write",reason:"child-pi-spawn attempt number"},PI_CREW_SCRATCHPAD_RESTORE:{kind:"child-write",reason:"child-pi-spawn restore hint"},PI_CREW_SCRATCHPAD_RESTORE_MTIME:{kind:"child-write",reason:"child-pi-spawn swap-detection pin"},PI_CREW_GUEST:{kind:"child-write",reason:"guest scratchpad self-marker"},PI_CREW_HOME:{kind:"host-only",reason:"host config/paths resolution"},PI_TEAMS_HOME:{kind:"host-only",reason:"host config/paths resolution (mirror)"},PI_CREW_ROLE:{kind:"host-only",reason:"role-permission host-side check"},PI_TEAMS_ROLE:{kind:"host-only",reason:"role mirror"},PI_CREW_ADAPTIVE_REPAIR:{kind:"host-only",reason:"adaptive-plan repair runs pre-spawn in the host"},PI_TEAMS_ADAPTIVE_REPAIR:{kind:"host-only",reason:"adaptive-repair mirror"},PI_CREW_EXECUTE_WORKERS:{kind:"host-only",reason:"spawn-mode decision made by the host"},PI_TEAMS_EXECUTE_WORKERS:{kind:"host-only",reason:"execute-workers mirror"},PI_CREW_TEST_ASYNC_INLINE:{kind:"host-only",reason:"async-runner spawn seam decision (test-only, host side)"},PI_CREW_ASYNC_EARLY_EXIT_GUARD:{kind:"host-only",reason:"team-tool/run.ts early-exit guard (host spawn path)"},PI_CREW_BROKER:{kind:"host-only",reason:"defaults/lifecycle broker enable (host session)"},PI_CREW_DEBUG_BUDGET:{kind:"host-only",reason:"token-budget debug log at run submit"},PI_CREW_DEBUG_STALE:{kind:"host-only",reason:"stale reconciler forensic gate \u2014 may run in any HOST process"},PI_CREW_MOCK_LIVE_SESSION:{kind:"host-only",reason:"runtime-resolver mode pick (host)"},PI_CREW_SKIP_HOME_CHECK:{kind:"host-only",reason:"config load escape check"},PI_CREW_SUPPRESS_RPC_WARNING:{kind:"host-only",reason:"host extension rpc-hmac warning"},PI_CREW_PLAN_UI:{kind:"host-only",reason:"host UI pane toggle"},PI_CREW_KEYBINDINGS:{kind:"host-only",reason:"host keybinding map"},PI_CREW_AUTO_PRUNE_KEEP:{kind:"host-only",reason:"session-start auto-prune (run-maintenance, host)"},PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS:{kind:"host-only",reason:"session-start auto-prune (host)"},PI_CREW_USE_BUNDLE:{kind:"host-only",reason:"root index.ts loader choice (outside src/ gate scope)"},PI_CREW_PROMPT_BREAKDOWN:{kind:"gap",reason:"prompt-builder runs in the runner for async runs"},PI_CREW_PROMPT_SKILLS:{kind:"gap",reason:"prompt-builder runs in the runner for async runs"},PI_CREW_BACKGROUND_MODE:{kind:"gap",reason:"child-executor read inside the runner"},PI_CREW_DEBUG:{kind:"gap",reason:"background-runner debug logging"},PI_TEAMS_DEBUG:{kind:"gap",reason:"internal-error severity gate \u2014 every process incl. the runner"},PI_CREW_DWF_SCRIPT_TIMEOUT_MS:{kind:"gap",reason:"dynamic-workflow runner executes in-process"},PI_CREW_DWF_SKIP_DETERMINISM_CHECK:{kind:"gap",reason:"deterministic-ast check in the DWF path"},PI_CREW_TRUST_PROJECT_DWF:{kind:"gap",reason:"project DWF trust gate on the runner path"},PI_CREW_HEARTBEAT_PULSE_MS:{kind:"gap",reason:"child-executor liveness pulse (runner)"},PI_CREW_INTERRUPT_GUARD_INTERVAL_MS:{kind:"gap",reason:"background-runner interrupt guard"},PI_CREW_WATCHDOG_INTERVAL_MS:{kind:"gap",reason:"WD-INTERVAL foreground-watchdog poll interval \u2014 read by the HOST session only; registered as a gap (deliberately not forwarded) so the decision table stays complete and a future runner-side watchdog is auditable"},PI_CREW_MAX_RUN_MS:{kind:"gap",reason:"background-runner run ceiling"},PI_CREW_MAX_WORKERS:{kind:"gap",reason:"global worker cap \u2014 enforced in the runner too"},PI_CREW_DISABLE_RESULT_READ_CACHE:{kind:"gap",reason:"task-output-context read cache (runner)"},PI_CREW_HANDOFF_BUDGET_TOKENS:{kind:"gap",reason:"dependency-context trim (runner)"},PI_CREW_PARENT_GUARD_INTERVAL_MS:{kind:"gap",reason:"parent-guard poll (runner + surface workers)"},PI_CREW_SCRATCHPAD_DEMOTE_BASH:{kind:"gap",reason:"agent-config parse on the runner prompt path"},PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS:{kind:"gap",reason:"pi-args reads input.env when the runner builds worker argv"},PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC:{kind:"gap",reason:"config load happens in the runner too"},PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS:{kind:"gap",reason:"child-pi response timeout resolved in the runner"},PI_CREW_SESSION_RECOVERY:{kind:"gap",reason:"session-recovery resolve on the runner path"},PI_CREW_HERMETIC_WORKERS:{kind:"gap",reason:"pi-args hermetic resolve when the runner builds argv"},PI_CREW_WORKER_TRANSPORT:{kind:"gap",reason:"rpc-worker transport seam (runner)"},PI_CREW_CLASSIFIER_ENABLED:{kind:"gap",reason:"child-executor retry classifier (runner)"},PI_CREW_CLASSIFIER_MODEL:{kind:"gap",reason:"child-executor retry classifier (runner)"},PI_CREW_VERIFY_GATE:{kind:"gap",reason:"verify-gate pre-gate before verifier spawns (runner)"},PI_CREW_VERIFY_GATE_TIMEOUT_MS:{kind:"gap",reason:"verify-gate per-check timeout (runner)"},PI_CREW_VERIFY_GATE_MEMO:{kind:"gap",reason:"W10-2 memoized green verify-gate (host-only tree-hash memo; never forwarded to children \u2014 each process gates itself, exactly like PI_CREW_VERIFY_GATE)"},PI_CREW_VERIFY_DETERMINISM:{kind:"gap",reason:"U13-ADJ layer-3 determinism rerun toggle \u2014 resolved in the runner before the verifier spawns; deliberately unforwarded (each process decides its own gate determinism tier, exactly like PI_CREW_VERIFY_GATE above)"},PI_CREW_RUN_LOCK_SOCKET:{kind:"gap",reason:"U21 run-lock dual-guard toggle \u2014 withRunLock also runs in the runner; deliberately unforwarded this wave (socket dies with each process, so a host-vs-runner toggle mismatch is harmless: each process just picks its own admission tier)"},PI_CREW_RPC_DIALOG_ANSWER:{kind:"gap",reason:"rpc-worker dialog policy (runner)"},PI_CREW_RPC_TURN_TIMEOUT_MS:{kind:"gap",reason:"rpc-worker turn timeout (runner)"},PI_CREW_MAX_AUTO_FALLBACKS:{kind:"gap",reason:"model-fallback cap resolved in the runner"},PI_CREW_SMOKE:{kind:"test-only",reason:"smoke helper"},PI_CREW_RUN_PLATFORM_TESTS:{kind:"test-only",reason:"platform test gate"},PI_CREW_HARD_KILL_GRACE_MS:{kind:"test-only",reason:"watchdog harness script"},PI_CREW_LIVE_MODEL:{kind:"test-only",reason:"live-session test model"},PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION:{kind:"test-only",reason:"experimental opt-in (docs)"},PI_CREW_RUN_ID:{kind:"test-only",reason:"fixture name; src uses PI_CREW_BROKER_RUN_ID"},PI_TEAMS_CONFIG_KEYS:{kind:"test-only",reason:"fixture"},PI_TEAMS_MOCK:{kind:"test-only",reason:"fixture"},PI_CREW_VERIFICATION:{kind:"test-only",reason:"partial fixture name"},PI_TEAMS_VERIFICATION:{kind:"test-only",reason:"partial fixture name"},PI_CREW_ATOMIC_WRITER:{kind:"test-only",reason:"superseded by PI_CREW_WORKER_ATOMIC_WRITER"},PI_CREW_BROKER_DIAG_UI:{kind:"test-only",reason:"removed"},PI_CREW_DEBUG_KILL:{kind:"test-only",reason:"docs/test-only"},PI_CREW_DWF_SANDBOX_DRY_RUN:{kind:"test-only",reason:"docs/test-only"},PI_CREW_HOOK:{kind:"test-only",reason:"docs/test-only"},PI_CREW_POOL_HEALTH:{kind:"test-only",reason:"docs/test-only"},PI_CREW_QUIET_PREFLIGHT:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SAFE_BASH:{kind:"test-only",reason:"dead"},PI_CREW_SCRATCHPAD_EXPERIMENT:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SESSION_DEPTH:{kind:"test-only",reason:"legacy name (ADR drift 2026-08-09)"},PI_CREW_SIG:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_KEY:{kind:"test-only",reason:"reverted 2026-08-13"},PI_CREW_SNAPSHOT_HMAC_STRICT:{kind:"test-only",reason:"reverted 2026-08-13"},PI_CREW_TEST_REAL_MODEL:{kind:"test-only",reason:"docs/test-only"},PI_CREW_TOOLING_429_NOTE:{kind:"test-only",reason:"docs/test-only"},PI_CREW_MAX_OUTPUT_TOKENS:{kind:"test-only",reason:"legacy name; real var PI_CREW_MAX_OUTPUT"},PI_CREW_GOAL_OSCILLATION_EMBEDDINGS:{kind:"test-only",reason:"docs/test-only"},PI_CREW_OPENAI_API_KEY:{kind:"test-only",reason:"env-filter secret-name EXAMPLE (comment-only)"},PI_CREW_SECRET_TOKEN:{kind:"test-only",reason:"env-filter secret-name EXAMPLE (comment-only)"},PI_CREW_REFUSE_GATE_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_PROCEED_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_ENFORCEMENT_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_DIR:{kind:"test-only",reason:"isolation fixture"},PI_CREW_ROOT:{kind:"test-only",reason:"isolation fixture"},PI_CREW_FOO:{kind:"test-only",reason:"isolation fixture"},PI_CREW_VAR:{kind:"test-only",reason:"isolation fixture"},PI_CREW_X:{kind:"test-only",reason:"isolation fixture"},PI_CREW_TEST_SECRET_VALUE:{kind:"test-only",reason:"isolation fixture"},PI_TEAMS_BAR:{kind:"test-only",reason:"isolation fixture"}};HBe();tte=["PI_CREW_DEPTH","PI_CREW_MAX_DEPTH","PI_CREW_INHERIT_PROJECT_CONTEXT","PI_CREW_INHERIT_SKILLS","PI_CREW_PARENT_PID","PI_TEAMS_DEPTH","PI_TEAMS_MAX_DEPTH","PI_TEAMS_INHERIT_PROJECT_CONTEXT","PI_TEAMS_INHERIT_SKILLS","PI_TEAMS_PI_BIN","PI_TEAMS_MOCK_CHILD_PI","PI_CREW_ALLOW_MOCK","PI_CREW_BACKGROUND_RUNNER_ENTRY","PI_CREW_WORKER_ATOMIC_WRITER","PI_TEAMS_WORKER_ATOMIC_WRITER","PI_CREW_VERIFICATION_SANITIZE_ENV","PI_TEAMS_VERIFICATION_SANITIZE_ENV","PI_CREW_VERIFICATION_PRESERVE_ENV","PI_TEAMS_VERIFICATION_PRESERVE_ENV","PI_CREW_VERIFICATION_WORKTREE","PI_TEAMS_VERIFICATION_WORKTREE","PI_CREW_BG_REPORT_ON_FATAL","PI_TEAMS_BG_REPORT_ON_FATAL"],nte=["PI_CREW_MODEL","PI_CREW_MODEL_FALLBACK_ORDER"],rte=["PI_CREW_STATE_BACKEND","PI_CREW_CHILD_PI_MODE","PI_CREW_CHILD_PI_POOL"],ote=["PI_CREW_TOOLS_MODIFIERS"],ste=["PI_CREW_REAP_GROUP","PI_CREW_REAP_RUN_DESCENDANTS"],zee=["PI_CREW_DEPTH"],qBe=[tte,nte,rte,ote,ste]});function zBe(e){return KBe.has(e)}function jj(e){if(!e)return[];let t=e.indexOf("/");if(t<=0)return[];let n=e.substring(0,t).toLowerCase();return VBe[n]??[]}function cte(e,t){let n=new Set;for(let r of t)for(let o of jj(r))n.add(o);return[...e,...n]}function YBe(e){if(!e.endsWith("*"))return!1;let t=e.slice(0,-1);if(t==="")return!0;if(t.startsWith("PI_CREW_")||t==="PI_CREW")return!1;for(let n of JBe)if(au(t+n))return!0;return!1}function Vi(e,t){let n={};if(t?.allowList&&t.allowList.length>0){for(let s of t.allowList){if(YBe(s))throw new Error(`Allowlist pattern "${s}" could match secret env vars. Use a more specific pattern.`);if(!s.endsWith("*")&&au(s)&&!(s in e)&&!zBe(s))throw new Error(`Allowlist entry "${s}" looks like a secret key. Use a more specific pattern.`)}let r=new Set,o=[];for(let s of t.allowList)s.endsWith("*")?o.push(s.slice(0,-1)):r.add(s);for(let[s,i]of Object.entries(e))if(i!==void 0){if(r.has(s)){n[s]=i;continue}o.some(a=>s.startsWith(a)&&s.length>a.length)&&!au(s)&&(n[s]=i)}return n}for(let[r,o]of Object.entries(e))o!==void 0&&!au(r)&&(n[r]=o);return n}var KBe,VBe,JBe,Nm=v(()=>{"use strict";$n();KBe=new Set(["MINIMAX_API_KEY","MINIMAX_GROUP_ID","OPENAI_API_KEY","OPENAI_ORG_ID","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY","AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION","ZEU_API_KEY","ZERODEV_API_KEY"]),VBe={minimax:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"],openai:["OPENAI_API_KEY","OPENAI_ORG_ID"],anthropic:["ANTHROPIC_API_KEY"],google:["GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],gemini:["GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],azure:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],"azure-openai":["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],aws:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],bedrock:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],zai:["ZEU_API_KEY"],zerodev:["ZERODEV_API_KEY"]};JBe=["token","api","key","password","passwd","secret","credential","authorization","private"]});function Uj(e,t){try{let n=JSON.parse(e.trim());if(!n||typeof n!="object")return;let r=n;if(r.v!==2||r.runId!==t||typeof r.socketPath!="string"||r.socketPath.length===0)return;let o=r.tasks;if(!o||typeof o!="object"||Array.isArray(o))return;for(let[s,i]of Object.entries(o))if(s.length===0||typeof i!="string"||i.length===0)return;return{v:2,runId:r.runId,socketPath:r.socketPath,tasks:o}}catch{return}}function BS(e,t,n){let r=`${JSON.stringify({v:2,runId:e,socketPath:t,tasks:n})}
477
+ `,{mode:384}),Uy.set(n,Date.now()),n}function jS(e,t){let n=0;for(let[r,o]of[...e.entries()])if(!(t-o<=jee)){try{Om.rmSync(r,{force:!0})}catch{}e.delete(r),n++}return n}function Bee(e,t){let n=0,r;try{r=Om.readdirSync(e)}catch{return 0}for(let o of r){if(!/^pi-crew-launch-.*\.sh$/.test(o))continue;let s=AA.join(e,o);try{let i=Om.statSync(s).mtimeMs;if(t-i<=jee)continue;Om.rmSync(s,{force:!0}),n++}catch{}}return n}var $j,Oj,jee,Uy,Nj=v(()=>{"use strict";Be();Vo();$j=class extends Error{constructor(t){super(`Refusing to build surface launch script at PI_CREW_DEPTH=${t}: surface panes are tier-1 only`),this.name="SurfaceDepthGuardError"}},Oj=class extends Error{constructor(t){super(`Refusing to build surface launch script: unsafe taskId ${JSON.stringify(t)} \u2014 must not contain "/", "\\", NUL or ".."`),this.name="SurfaceTaskIdError"}};jee=6e4,Uy=new Map});import*as Gee from"node:fs";import*as Hee from"node:path";function OBe(e,t){return e?MN(e,t):null}function NBe(e){let t=e.indexOf("--mode");return t===-1||e[t+1]!=="json"?[...e]:e[t+2]!=="-p"?[...e]:[...e.slice(0,t),...e.slice(t+3)]}function DBe(e,t){let r=(t??(o=>{try{return Gee.readFileSync(`/proc/${o}/stat`,"utf8")}catch{return}}))(e);return r?yD(r)??"":""}function LBe(e){return[e.command,...e.args].map(WS).join(" ")}async function qee(e){let t=e.deps?.now??Date.now;try{jS(Uy,t())}catch(a){b("surface-spawn.sweep",a instanceof Error?a:new Error(String(a)))}let n=po(e.env)>0?`host depth ${po(e.env)} > 0`:null,r;try{if(n&&e.deps?.provider!==void 0)return b("surface-spawn.hard-gate",new Error(`pre-resolved surface provider ignored: ${n}`),"fail-closed \xA73","warn"),{mode:"headless",reason:`surface gated by ${n}`,gateRejected:{gate:"depth",reason:`surface gated by ${n}`,env:Mk(e.env)}};r=e.deps?.provider!==void 0?{provider:e.deps.provider}:GY(e.env,e.config,e.role,e.livePaneCount,e.deps?.resolve)}catch(a){return b("surface-spawn.resolve",a instanceof Error?a:new Error(String(a)),"resolveSurface threw"),{mode:"headless",reason:`surface resolution threw: ${a instanceof Error?a.message:String(a)}`}}if(!r.provider){let a=r.rejection;return{mode:"headless",reason:a?`surface gate "${a.gate}": ${a.reason}`:"surface resolution returned null (mode/depth/async/cap/role gate or no mux)",gateRejected:a}}let o=r.provider,s=e.stateRoot?Hee.basename(e.stateRoot):void 0,i;try{i=await o.createSurface(e.taskId,{cwd:e.cwd,title:e.taskId,...s?{tabKey:s,splitIndex:e.livePaneCount}:{}})}catch(a){return b("surface-spawn.create-surface",a instanceof Error?a:new Error(String(a)),`taskId=${e.taskId}`),{mode:"headless",reason:`createSurface failed: ${a instanceof Error?a.message:String(a)}`,attempted:!0}}try{if(typeof o.sendCommand!="function")throw new Error("provider does not implement sendCommand \u2014 cannot boot a commandless pane");let a=NBe(e.piArgs),c=a.includes("--tui-mode")?a:[...a,"--tui-mode","regular"],u=(e.deps?.resolveCommand??Qs)(c),d=OBe(e.stateRoot,e.taskId),l={...e.workerEnv,PI_CREW_SURFACE:o.kind,PI_CREW_SURFACE_PANE:i.id,PI_CREW_AUTO_EXIT:"1",...d?{PI_CREW_AGENT_EVENTS_PATH:d}:{},...e.workerEnv.PI_CREW_PARENT_PID?{}:{PI_CREW_PARENT_PID:String(process.pid)}};l.PI_CREW_PARENT_START_TIME||(l.PI_CREW_PARENT_START_TIME=DBe(process.pid,e.deps?.readParentStat));let f=Uee({taskId:e.taskId,env:l,command:LBe(u),cwd:e.cwd,baseDir:e.baseDir??xa(),callerEnv:e.env});return await o.sendCommand(i,`bash ${WS(f)}; exit`),{mode:"surface",kind:o.kind,paneId:i.id,handle:i,provider:o,scriptPath:f,eventsPath:d,...s&&i.tabId?{tabKey:s,tabId:i.tabId}:{}}}catch(a){b("surface-spawn.boot",a instanceof Error?a:new Error(String(a)),`taskId=${e.taskId} pane=${i.id} \u2014 falling back to headless`);try{await o.closeSurface(i,{force:!0})}catch(c){b("surface-spawn.orphan-close",c instanceof Error?c:new Error(String(c)),`pane=${i.id}`)}return{mode:"headless",reason:`surface boot failed: ${a instanceof Error?a.message:String(a)}`,attempted:!0}}}function Kee(e=Date.now){try{return jS(Uy,e())}catch(t){return b("surface-spawn.run-end-sweep",t instanceof Error?t:new Error(String(t))),0}}async function Vee(e,t={}){let n=async()=>{try{await e.provider.closeSurface(e.handle,{force:!0})}catch(r){b("surface-spawn.force-close",r instanceof Error?r:new Error(String(r)),`pane=${e.paneId}`)}};return await new Promise(r=>{let o=t.signal?.aborted===!0,s=!1,i=!1,a=null,c=f=>{i||(i=!0,a&&clearTimeout(a),r(f))},u=()=>{if(i||a)return;let f=Math.max(1,t.graceAfterForceCloseMs??FBe);a=setTimeout(()=>{b("surface-spawn.synthetic-exit",new Error(`no onExit within ${f}ms of force-close`),`pane=${e.paneId} \u2014 resolving synthetic exit (pane may have died before its first onExit subscription)`),c({reason:"detached",cancelledByAbort:o,timedOut:s,synthetic:!0})},f),a.unref()};o&&(o=!0,n(),u());let d=()=>{o||s||i||(o=!0,n(),u())};t.signal?.addEventListener("abort",d,{once:!0});let l=t.deadlineMs!==void 0?setTimeout(()=>{o||s||i||(s=!0,n(),u())},Math.max(1,t.deadlineMs)):null;l?.unref(),e.handle.onExit(f=>c({reason:f,cancelledByAbort:o,timedOut:s})),!t.signal&&!l&&!a&&!i&&u()})}var FBe,Dj=v(()=>{"use strict";H();ut();Vo();Dl();_h();Nj();$k();FBe=2e3});function _A(e,t){try{return e.kill(t)}catch{return!1}}function PA(e,t){let{idleMs:n,hardMs:r}=t,o=!1,s=!1,i=!1,a,c,u=()=>{if(!s)try{e.stdout?.destroy()}catch(m){b("post-exit-stdio-guard.stdout-destroy",m,void 0,"debug")}if(!i)try{e.stderr?.destroy()}catch(m){b("post-exit-stdio-guard.stderr-destroy",m,void 0,"debug")}},d=()=>{a&&(clearTimeout(a),a=void 0),c&&(clearTimeout(c),c=void 0)},l=()=>{o&&(a&&clearTimeout(a),a=setTimeout(u,n),a.unref())};e.stdout?.on("data",l),e.stderr?.on("data",l),e.stdout?.on("end",()=>{s=!0,s&&i&&d()}),e.stderr?.on("end",()=>{i=!0,s&&i&&d()});let f=()=>{c||(c=setTimeout(u,r),c.unref())},p=()=>{o=!0,l(),f()};return(e.exitCode!=null||e.signalCode!=null)&&p(),e.on("exit",p),e.on("close",d),e.on("error",d),d}var Lj=v(()=>{"use strict";H()});function jBe(e){if(!e)return null;let t=e.toLowerCase();for(let n of WBe)if(t.includes(n.pattern))return n.label;return null}function UBe(e){return e??null}function Ff(e){let t=e.exitCode??null,n=UBe(e.signal);if(e.timedOut)return{crashClass:"timeout",reason:"process timed out (response timeout guard fired)"};if(e.cancelled)return{crashClass:"cancelled",reason:"process was cancelled (abort requested)"};if(e.spawnError!==void 0&&e.spawnError!==null)return{crashClass:"spawn_error",reason:`spawn error: ${BBe(e.spawnError)}`};if(n!==null||t!==null&&t!==0){let o=jBe(e.stderrSnippet);if(o!==null)return{crashClass:"native_panic",reason:`native panic detected: ${o}`}}return n!==null?{crashClass:"signal_exit",reason:`process exited after signal ${n}`}:t===0?{crashClass:"clean_exit",reason:"process exited cleanly"}:t!==null?{crashClass:"non_zero_exit",reason:`process exited with code ${t}`}:e.killed?{crashClass:"protocol_exit",reason:"process was killed but no signal/exit code was captured"}:{crashClass:"protocol_exit",reason:"process exited before protocol completion (exit code unknown)"}}function BBe(e){if(e instanceof Error)return e.message||e.name;if(typeof e=="string")return e;try{return String(e)}catch{return"(unstringifiable error)"}}var WBe,Fj=v(()=>{"use strict";WBe=[{pattern:"sigsegv",label:"segmentation fault"},{pattern:"segfault",label:"segmentation fault"},{pattern:"segmentation fault",label:"segmentation fault"},{pattern:"sigabrt",label:"abort signal"},{pattern:"abort(",label:"abort"},{pattern:"fatal error",label:"V8/node fatal error"},{pattern:"panic:",label:"rust/go panic"},{pattern:"thread '",label:"rust panic (thread context)"},{pattern:"illegal instruction",label:"illegal instruction"},{pattern:"double free",label:"heap corruption (double free)"}]});var Au,US=v(()=>{"use strict";Au=["APPDATA","LOCALAPPDATA","USERPROFILE","SystemRoot","ComSpec","TEMP","TMP"]});function HBe(){let e=new Set(Object.keys(pg)),t=new Set(Object.keys(Qee)),n=[...e].filter(o=>!t.has(o));if(n.length>0)throw new Error(`env-allowlist-registry: CREW_ENV_VARS entries without a forwarding decision (C-1): ${n.join(", ")}`);let r=[...t].filter(o=>!e.has(o));if(r.length>0)throw new Error(`env-allowlist-registry: decisions for names NOT in CREW_ENV_VARS (orphans): ${r.join(", ")}`)}function ete(e,t){let n=new Set(Object.entries(Qee).filter(([,o])=>o.kind==="allowlist"&&o.destinations.includes(e)).map(([o])=>o)),r=new Set;for(let o of t)for(let s of o){if(r.has(s))throw new Error(`env-allowlist-registry: duplicate crew env "${s}" for ${e}`);if(!n.has(s))throw new Error(`env-allowlist-registry: "${s}" is in the ${e} group order but has no allowlist decision for it (orphan or mis-tagged)`);r.add(s)}for(let o of n)if(!r.has(o))throw new Error(`env-allowlist-registry: "${o}" is allowlisted for ${e} but missing from its group order`)}function ite(){return ete("background-runner",qBe),[...Jee,...Au,...Yee,"NODE_PATH",...Xee,...Zee,...tte,...GBe,...nte,...rte,...ote,...ste]}function ate(){return ete("child-pi",[zee]),[...Jee,...Au,...Yee,...Xee,...Zee,...zee]}var Jee,Yee,Xee,Zee,GBe,gn,Qee,tte,nte,rte,ote,ste,zee,qBe,Wj=v(()=>{"use strict";US();Pe();Jee=["PATH","HOME","USER","SHELL","TERM","LANG","LC_ALL","LC_COLLATE","LC_CTYPE","LC_MESSAGES","LC_MONETARY","LC_NUMERIC","LC_TIME","XDG_CONFIG_HOME","XDG_DATA_HOME","XDG_CACHE_HOME","XDG_RUNTIME_DIR","TMPDIR"],Yee=["NVM_BIN","NVM_DIR","NVM_INC"],Xee=["NODE_DISABLE_COLORS","NODE_EXTRA_CA_CERTS"],Zee=["NPM_CONFIG_REGISTRY","NPM_CONFIG_USERCONFIG","NPM_CONFIG_GLOBALCONFIG"],GBe=["TMUX","TMUX_PANE","TMUX_TMPDIR","HERDR_ENV","HERDR_SESSION","HERDR_PANE_ID","HERDR_SOCKET_PATH","HERDR_WORKSPACE_ID","HERDR_PING_TIMEOUT_MS"],gn=(e,t)=>({kind:"allowlist",destinations:e,reason:t}),Qee={PI_CREW_DEPTH:gn(["background-runner","child-pi"],"depth accounting must survive into the runner AND every child worker"),PI_TEAMS_DEPTH:gn(["background-runner"],"depth mirror (CREW ?? TEAMS read in the runner)"),PI_CREW_MAX_DEPTH:gn(["background-runner"],"runner-side depth clamp"),PI_TEAMS_MAX_DEPTH:gn(["background-runner"],"depth-clamp mirror"),PI_CREW_INHERIT_PROJECT_CONTEXT:gn(["background-runner"],"runner builds worker prompts \u2014 inheritance must match the parent"),PI_TEAMS_INHERIT_PROJECT_CONTEXT:gn(["background-runner"],"inherit mirror"),PI_CREW_INHERIT_SKILLS:gn(["background-runner"],"runner builds worker prompts \u2014 inheritance must match the parent"),PI_TEAMS_INHERIT_SKILLS:gn(["background-runner"],"inherit mirror"),PI_CREW_PARENT_PID:gn(["background-runner"],"runner parent-guard liveness check"),PI_TEAMS_PI_BIN:gn(["background-runner"],"runner spawns child pi workers \u2014 needs the explicit binary path"),PI_TEAMS_MOCK_CHILD_PI:gn(["background-runner"],"runner-side mock fixture select"),PI_CREW_ALLOW_MOCK:gn(["background-runner"],"mock gate double-keyed with PI_TEAMS_MOCK_CHILD_PI"),PI_CREW_BACKGROUND_RUNNER_ENTRY:gn(["background-runner"],"entry guard read by the runner main() itself"),PI_CREW_WORKER_ATOMIC_WRITER:gn(["background-runner"],"RFC 15 worker-thread atomic writer opt-in (runner side)"),PI_TEAMS_WORKER_ATOMIC_WRITER:gn(["background-runner"],"atomic-writer mirror"),PI_CREW_VERIFICATION_SANITIZE_ENV:gn(["background-runner"],"verification commands run inside the runner"),PI_TEAMS_VERIFICATION_SANITIZE_ENV:gn(["background-runner"],"sanitize mirror"),PI_CREW_VERIFICATION_PRESERVE_ENV:gn(["background-runner"],"verification commands run inside the runner"),PI_TEAMS_VERIFICATION_PRESERVE_ENV:gn(["background-runner"],"preserve-env mirror"),PI_CREW_VERIFICATION_WORKTREE:gn(["background-runner"],"verification worktree sandbox (RFC 16) runs inside the runner"),PI_TEAMS_VERIFICATION_WORKTREE:gn(["background-runner"],"worktree-sandbox mirror"),PI_CREW_BG_REPORT_ON_FATAL:gn(["background-runner"],"V8 report flags are applied by the parent when BUILDING the runner argv (RFC 17); forwarded for parity/diagnostics"),PI_TEAMS_BG_REPORT_ON_FATAL:gn(["background-runner"],"report-on-fatal mirror"),PI_CREW_MODEL:gn(["background-runner"],"model routing for manifests without persisted modelContext (2026-09-29 full-battery Finding #1)"),PI_CREW_MODEL_FALLBACK_ORDER:gn(["background-runner"],"resolveModelFallbackPolicy in the runner"),PI_CREW_STATE_BACKEND:gn(["background-runner"],"runner OWNS task-state writes (U8 P2 parity \u2014 d0cef00f/44d16d94)"),PI_CREW_CHILD_PI_MODE:gn(["background-runner"],"runner spawns the child-pi workers (U14 parity)"),PI_CREW_CHILD_PI_POOL:gn(["background-runner"],"rpc worker pool rides along for the same parity reason"),PI_CREW_TOOLS_MODIFIERS:gn(["background-runner"],"L2-3 opt-in: the runner builds worker argv (pi-args) \u2014 the flag must reach it. The ONE deliberate content addition shipped with this registry"),PI_CREW_MODEL_REQUIRE_CREDENTIALS:{kind:"explicit",channel:"forwardModelControlEnv",reason:"secret-LOOKING suffix trips sanitizeEnvSecrets on unset envs \u2014 copied set-only (async-runner.ts)"},PI_CREW_PEER_DEP_DIR:{kind:"explicit",channel:"async-runner childEnv (PEER_DEP_DIR_ENV)",reason:"resolved peer-dep dir is computed at spawn time and injected next to the sanitized env"},PI_CREW_ASYNC_RUN:{kind:"explicit",channel:"buildBackgroundRunnerEnv",reason:"async telemetry marker STAMPED onto the runner env (never ambient; not a surface gate)"},PI_CREW_SCRATCHPAD:{kind:"child-write",reason:"child-pi-spawn dormant gate"},PI_CREW_KIND:{kind:"child-write",reason:"child-pi-spawn worker-kind marker"},PI_CREW_TASK_ID:{kind:"child-write",reason:"child-pi-spawn per-child task id"},PI_CREW_EVENTS_PATH:{kind:"child-write",reason:"child-pi-spawn per-child events log"},PI_CREW_ASK_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant ask gate"},PI_CREW_MSG_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant message gate"},PI_CREW_DELEGATE_ENABLED:{kind:"child-write",reason:"child-pi-spawn dormant delegate gate"},PI_CREW_STATE_ROOT:{kind:"child-write",reason:"child-pi-spawn mailbox root"},PI_CREW_BROKER_RUN_ID:{kind:"child-write",reason:"child-pi-spawn broker handshake"},PI_CREW_AGENT_EVENTS_PATH:{kind:"child-write",reason:"prepareSurfaceSpawn recorder log"},PI_CREW_BROKER_SOCKET:{kind:"child-write",reason:"child-pi-spawn broker handshake (never ambient \u2014 per-run socket)"},PI_CREW_BROKER_TOKEN:{kind:"child-write",reason:"per-task compound broker token via built.env/pipe (F4) \u2014 secret, never allowlisted"},PI_CREW_BROKER_TASK_ID:{kind:"child-write",reason:"child-pi-spawn broker handshake"},PI_CREW_BROKER_STDIN:{kind:"child-write",reason:"LOW-1 broker-credential transport gate (DEFAULT-ON since 2026-10-28 \u2014 only exact '0' opts out) \u2014 read by the HOST spawner (child-pi-spawn .resolveBrokerStdinHandshakeEnabled) to pick env vs stdin route, then RE-WRITTEN by that spawner into every gate-ON brokered child's built.env ('1') so the worker-side mirror gate (crew-broker-child .resolveBrokerStdinFallbackEnabled, which reads its OWN process.env) is armed deterministically and pre-flip workers (mirror === '1') stay compatible \u2014 ambient forwarding never reaches the child, so child-write is the only channel that wires the route through (FIX-1 2026-10-28). Never ambient-allowlisted: route selection is per-spawn"},PI_CREW_INFERENCE_BROKER:{kind:"gap",reason:"v0.11.31 inference broker toggle \u2014 resolved in the PARENT only (ensureInferenceBroker); deliberately never forwarded to children (a child re-starting its own broker would be nonsensical)"},PI_CREW_INFERENCE_TOKEN:{kind:"child-write",reason:"v0.11.31 per-worker inference broker token via built.env \u2014 secret, never allowlisted"},PI_CREW_INFERENCE_BUDGET_TOKENS:{kind:"gap",reason:"v0.11.31 per-worker budget read by the parent broker at token issuance (registerInferenceWorker)"},PI_CREW_INFERENCE_UPSTREAM:{kind:"test-only",reason:"v0.11.31 broker upstream override \u2014 TESTS ONLY (local mock), production never sets it"},PI_CREW_INFERENCE_UPSTREAM_ANTHROPIC:{kind:"test-only",reason:"v0.11.34 anthropic-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_INFERENCE_UPSTREAM_OPENAI:{kind:"test-only",reason:"v0.11.34 openai-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_INFERENCE_UPSTREAM_GOOGLE:{kind:"test-only",reason:"v0.11.36 google-only broker upstream override \u2014 TESTS ONLY"},PI_CREW_SANDBOX:{kind:"gap",reason:"v0.11.36 OS-boundary knob read in the PARENT spawn path only (bwrap wrap decision) \u2014 deliberately never forwarded to children"},PI_CREW_SANDBOX_NET:{kind:"gap",reason:"v0.11.36 sandbox network profile (share|off) read in the PARENT spawn path only \u2014 deliberately never forwarded to children"},PI_CREW_REAP_GROUP:gn(["background-runner"],"fix-wave 11a process-group reap kill switch \u2014 read by the reaping code (reap-group.ts resolveReapGroupEnabled) which ALSO runs inside the detached background runner; unforwarded it would be silently inert there (wave 11a review MINOR #7). Not forwarded to children: a worker must not be able to disable its own reaping"),PI_CREW_REAP_RUN_DESCENDANTS:gn(["background-runner"],"fix-wave 11b run-scoped descendant sweep kill switch (run-descendant-sweep.ts resolveRunDescendantSweepEnabled) \u2014 the stale reconciler that drives the sweep also runs inside the detached background runner, so the same inert-switch argument as PI_CREW_REAP_GROUP applies. Not forwarded to children: a worker must not be able to disable its own reaping"),PI_CREW_STEERING_FILE:{kind:"child-write",reason:"child-pi-spawn per-run steering JSONL"},PI_CREW_MAX_OUTPUT:{kind:"child-write",reason:"pi-args emits agent.maxTokens into built.env"},PI_CREW_RPC_SECRET:{kind:"child-write",reason:"shared HMAC secret handed to the child at spawn \u2014 secret name, never allowlisted"},PI_CREW_AUTO_EXIT:{kind:"child-write",reason:"prepareSurfaceSpawn D7 shutdown marker"},PI_CREW_SURFACE:{kind:"child-write",reason:"prepareSurfaceSpawn surface kind"},PI_CREW_PARENT_START_TIME:{kind:"child-write",reason:"prepareSurfaceSpawn pid-reuse-safe guard input"},PI_CREW_ARTIFACTS_ROOT:{kind:"child-write",reason:"child-pi-spawn scratchpad containment root"},PI_CREW_SCRATCHPAD_SNAPSHOT:{kind:"child-write",reason:"child-pi-spawn snapshot target"},PI_CREW_ATTEMPT:{kind:"child-write",reason:"child-pi-spawn attempt number"},PI_CREW_SCRATCHPAD_RESTORE:{kind:"child-write",reason:"child-pi-spawn restore hint"},PI_CREW_SCRATCHPAD_RESTORE_MTIME:{kind:"child-write",reason:"child-pi-spawn swap-detection pin"},PI_CREW_GUEST:{kind:"child-write",reason:"guest scratchpad self-marker"},PI_CREW_HOME:{kind:"host-only",reason:"host config/paths resolution"},PI_TEAMS_HOME:{kind:"host-only",reason:"host config/paths resolution (mirror)"},PI_CREW_ROLE:{kind:"host-only",reason:"role-permission host-side check"},PI_TEAMS_ROLE:{kind:"host-only",reason:"role mirror"},PI_CREW_ADAPTIVE_REPAIR:{kind:"host-only",reason:"adaptive-plan repair runs pre-spawn in the host"},PI_TEAMS_ADAPTIVE_REPAIR:{kind:"host-only",reason:"adaptive-repair mirror"},PI_CREW_EXECUTE_WORKERS:{kind:"host-only",reason:"spawn-mode decision made by the host"},PI_TEAMS_EXECUTE_WORKERS:{kind:"host-only",reason:"execute-workers mirror"},PI_CREW_TEST_ASYNC_INLINE:{kind:"host-only",reason:"async-runner spawn seam decision (test-only, host side)"},PI_CREW_ASYNC_EARLY_EXIT_GUARD:{kind:"host-only",reason:"team-tool/run.ts early-exit guard (host spawn path)"},PI_CREW_BROKER:{kind:"host-only",reason:"defaults/lifecycle broker enable (host session)"},PI_CREW_DEBUG_BUDGET:{kind:"host-only",reason:"token-budget debug log at run submit"},PI_CREW_DEBUG_STALE:{kind:"host-only",reason:"stale reconciler forensic gate \u2014 may run in any HOST process"},PI_CREW_MOCK_LIVE_SESSION:{kind:"host-only",reason:"runtime-resolver mode pick (host)"},PI_CREW_SKIP_HOME_CHECK:{kind:"host-only",reason:"config load escape check"},PI_CREW_SUPPRESS_RPC_WARNING:{kind:"host-only",reason:"host extension rpc-hmac warning"},PI_CREW_PLAN_UI:{kind:"host-only",reason:"host UI pane toggle"},PI_CREW_KEYBINDINGS:{kind:"host-only",reason:"host keybinding map"},PI_CREW_AUTO_PRUNE_KEEP:{kind:"host-only",reason:"session-start auto-prune (run-maintenance, host)"},PI_CREW_AUTO_PRUNE_AGE_FLOOR_HOURS:{kind:"host-only",reason:"session-start auto-prune (host)"},PI_CREW_USE_BUNDLE:{kind:"host-only",reason:"root index.ts loader choice (outside src/ gate scope)"},PI_CREW_PROMPT_BREAKDOWN:{kind:"gap",reason:"prompt-builder runs in the runner for async runs"},PI_CREW_PROMPT_SKILLS:{kind:"gap",reason:"prompt-builder runs in the runner for async runs"},PI_CREW_BACKGROUND_MODE:{kind:"gap",reason:"child-executor read inside the runner"},PI_CREW_DEBUG:{kind:"gap",reason:"background-runner debug logging"},PI_TEAMS_DEBUG:{kind:"gap",reason:"internal-error severity gate \u2014 every process incl. the runner"},PI_CREW_DWF_SCRIPT_TIMEOUT_MS:{kind:"gap",reason:"dynamic-workflow runner executes in-process"},PI_CREW_DWF_SKIP_DETERMINISM_CHECK:{kind:"gap",reason:"deterministic-ast check in the DWF path"},PI_CREW_TRUST_PROJECT_DWF:{kind:"gap",reason:"project DWF trust gate on the runner path"},PI_CREW_HEARTBEAT_PULSE_MS:{kind:"gap",reason:"child-executor liveness pulse (runner)"},PI_CREW_INTERRUPT_GUARD_INTERVAL_MS:{kind:"gap",reason:"background-runner interrupt guard"},PI_CREW_WATCHDOG_INTERVAL_MS:{kind:"gap",reason:"WD-INTERVAL foreground-watchdog poll interval \u2014 read by the HOST session only; registered as a gap (deliberately not forwarded) so the decision table stays complete and a future runner-side watchdog is auditable"},PI_CREW_MAX_RUN_MS:{kind:"gap",reason:"background-runner run ceiling"},PI_CREW_MAX_WORKERS:{kind:"gap",reason:"global worker cap \u2014 enforced in the runner too"},PI_CREW_DISABLE_RESULT_READ_CACHE:{kind:"gap",reason:"task-output-context read cache (runner)"},PI_CREW_HANDOFF_BUDGET_TOKENS:{kind:"gap",reason:"dependency-context trim (runner)"},PI_CREW_PARENT_GUARD_INTERVAL_MS:{kind:"gap",reason:"parent-guard poll (runner + surface workers)"},PI_CREW_SCRATCHPAD_DEMOTE_BASH:{kind:"gap",reason:"agent-config parse on the runner prompt path"},PI_CREW_TRUST_PROJECT_AGENT_EXTENSIONS:{kind:"gap",reason:"pi-args reads input.env when the runner builds worker argv"},PI_CREW_PERSISTENCE_SKIP_TASKS_FSYNC:{kind:"gap",reason:"config load happens in the runner too"},PI_TEAMS_CHILD_RESPONSE_TIMEOUT_MS:{kind:"gap",reason:"child-pi response timeout resolved in the runner"},PI_CREW_SESSION_RECOVERY:{kind:"gap",reason:"session-recovery resolve on the runner path"},PI_CREW_HERMETIC_WORKERS:{kind:"gap",reason:"pi-args hermetic resolve when the runner builds argv"},PI_CREW_WORKER_TRANSPORT:{kind:"gap",reason:"rpc-worker transport seam (runner)"},PI_CREW_CLASSIFIER_ENABLED:{kind:"gap",reason:"child-executor retry classifier (runner)"},PI_CREW_CLASSIFIER_MODEL:{kind:"gap",reason:"child-executor retry classifier (runner)"},PI_CREW_VERIFY_GATE:{kind:"gap",reason:"verify-gate pre-gate before verifier spawns (runner)"},PI_CREW_VERIFY_GATE_TIMEOUT_MS:{kind:"gap",reason:"verify-gate per-check timeout (runner)"},PI_CREW_VERIFY_GATE_MEMO:{kind:"gap",reason:"W10-2 memoized green verify-gate (host-only tree-hash memo; never forwarded to children \u2014 each process gates itself, exactly like PI_CREW_VERIFY_GATE)"},PI_CREW_VERIFY_DETERMINISM:{kind:"gap",reason:"U13-ADJ layer-3 determinism rerun toggle \u2014 resolved in the runner before the verifier spawns; deliberately unforwarded (each process decides its own gate determinism tier, exactly like PI_CREW_VERIFY_GATE above)"},PI_CREW_RUN_LOCK_SOCKET:{kind:"gap",reason:"U21 run-lock dual-guard toggle \u2014 withRunLock also runs in the runner; deliberately unforwarded this wave (socket dies with each process, so a host-vs-runner toggle mismatch is harmless: each process just picks its own admission tier)"},PI_CREW_RPC_DIALOG_ANSWER:{kind:"gap",reason:"rpc-worker dialog policy (runner)"},PI_CREW_RPC_TURN_TIMEOUT_MS:{kind:"gap",reason:"rpc-worker turn timeout (runner)"},PI_CREW_MAX_AUTO_FALLBACKS:{kind:"gap",reason:"model-fallback cap resolved in the runner"},PI_CREW_SMOKE:{kind:"test-only",reason:"smoke helper"},PI_CREW_RUN_PLATFORM_TESTS:{kind:"test-only",reason:"platform test gate"},PI_CREW_HARD_KILL_GRACE_MS:{kind:"test-only",reason:"watchdog harness script"},PI_CREW_LIVE_MODEL:{kind:"test-only",reason:"live-session test model"},PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION:{kind:"test-only",reason:"experimental opt-in (docs)"},PI_CREW_RUN_ID:{kind:"test-only",reason:"fixture name; src uses PI_CREW_BROKER_RUN_ID"},PI_TEAMS_CONFIG_KEYS:{kind:"test-only",reason:"fixture"},PI_TEAMS_MOCK:{kind:"test-only",reason:"fixture"},PI_CREW_VERIFICATION:{kind:"test-only",reason:"partial fixture name"},PI_TEAMS_VERIFICATION:{kind:"test-only",reason:"partial fixture name"},PI_CREW_ATOMIC_WRITER:{kind:"test-only",reason:"superseded by PI_CREW_WORKER_ATOMIC_WRITER"},PI_CREW_BROKER_DIAG_UI:{kind:"test-only",reason:"removed"},PI_CREW_DEBUG_KILL:{kind:"test-only",reason:"docs/test-only"},PI_CREW_DWF_SANDBOX_DRY_RUN:{kind:"test-only",reason:"docs/test-only"},PI_CREW_HOOK:{kind:"test-only",reason:"docs/test-only"},PI_CREW_POOL_HEALTH:{kind:"test-only",reason:"docs/test-only"},PI_CREW_QUIET_PREFLIGHT:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SAFE_BASH:{kind:"test-only",reason:"dead"},PI_CREW_SCRATCHPAD_EXPERIMENT:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SESSION_DEPTH:{kind:"test-only",reason:"legacy name (ADR drift 2026-08-09)"},PI_CREW_SIG:{kind:"test-only",reason:"docs/test-only"},PI_CREW_SNAPSHOT_HMAC_KEY:{kind:"test-only",reason:"reverted 2026-08-13"},PI_CREW_SNAPSHOT_HMAC_STRICT:{kind:"test-only",reason:"reverted 2026-08-13"},PI_CREW_TEST_REAL_MODEL:{kind:"test-only",reason:"docs/test-only"},PI_CREW_TOOLING_429_NOTE:{kind:"test-only",reason:"docs/test-only"},PI_CREW_MAX_OUTPUT_TOKENS:{kind:"test-only",reason:"legacy name; real var PI_CREW_MAX_OUTPUT"},PI_CREW_GOAL_OSCILLATION_EMBEDDINGS:{kind:"test-only",reason:"docs/test-only"},PI_CREW_OPENAI_API_KEY:{kind:"test-only",reason:"env-filter secret-name EXAMPLE (comment-only)"},PI_CREW_SECRET_TOKEN:{kind:"test-only",reason:"env-filter secret-name EXAMPLE (comment-only)"},PI_CREW_REFUSE_GATE_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_PROCEED_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_ENFORCEMENT_PATTERNS:{kind:"test-only",reason:"docs-only"},PI_CREW_DIR:{kind:"test-only",reason:"isolation fixture"},PI_CREW_ROOT:{kind:"test-only",reason:"isolation fixture"},PI_CREW_FOO:{kind:"test-only",reason:"isolation fixture"},PI_CREW_VAR:{kind:"test-only",reason:"isolation fixture"},PI_CREW_X:{kind:"test-only",reason:"isolation fixture"},PI_CREW_TEST_SECRET_VALUE:{kind:"test-only",reason:"isolation fixture"},PI_TEAMS_BAR:{kind:"test-only",reason:"isolation fixture"}};HBe();tte=["PI_CREW_DEPTH","PI_CREW_MAX_DEPTH","PI_CREW_INHERIT_PROJECT_CONTEXT","PI_CREW_INHERIT_SKILLS","PI_CREW_PARENT_PID","PI_TEAMS_DEPTH","PI_TEAMS_MAX_DEPTH","PI_TEAMS_INHERIT_PROJECT_CONTEXT","PI_TEAMS_INHERIT_SKILLS","PI_TEAMS_PI_BIN","PI_TEAMS_MOCK_CHILD_PI","PI_CREW_ALLOW_MOCK","PI_CREW_BACKGROUND_RUNNER_ENTRY","PI_CREW_WORKER_ATOMIC_WRITER","PI_TEAMS_WORKER_ATOMIC_WRITER","PI_CREW_VERIFICATION_SANITIZE_ENV","PI_TEAMS_VERIFICATION_SANITIZE_ENV","PI_CREW_VERIFICATION_PRESERVE_ENV","PI_TEAMS_VERIFICATION_PRESERVE_ENV","PI_CREW_VERIFICATION_WORKTREE","PI_TEAMS_VERIFICATION_WORKTREE","PI_CREW_BG_REPORT_ON_FATAL","PI_TEAMS_BG_REPORT_ON_FATAL"],nte=["PI_CREW_MODEL","PI_CREW_MODEL_FALLBACK_ORDER"],rte=["PI_CREW_STATE_BACKEND","PI_CREW_CHILD_PI_MODE","PI_CREW_CHILD_PI_POOL"],ote=["PI_CREW_TOOLS_MODIFIERS"],ste=["PI_CREW_REAP_GROUP","PI_CREW_REAP_RUN_DESCENDANTS"],zee=["PI_CREW_DEPTH"],qBe=[tte,nte,rte,ote,ste]});function zBe(e){return KBe.has(e)}function jj(e){if(!e)return[];let t=e.indexOf("/");if(t<=0)return[];let n=e.substring(0,t).toLowerCase();return VBe[n]??[]}function cte(e,t){let n=new Set;for(let r of t)for(let o of jj(r))n.add(o);return[...e,...n]}function YBe(e){if(!e.endsWith("*"))return!1;let t=e.slice(0,-1);if(t==="")return!0;if(t.startsWith("PI_CREW_")||t==="PI_CREW")return!1;for(let n of JBe)if(au(t+n))return!0;return!1}function Vi(e,t){let n={};if(t?.allowList&&t.allowList.length>0){for(let s of t.allowList){if(YBe(s))throw new Error(`Allowlist pattern "${s}" could match secret env vars. Use a more specific pattern.`);if(!s.endsWith("*")&&au(s)&&!(s in e)&&!zBe(s))throw new Error(`Allowlist entry "${s}" looks like a secret key. Use a more specific pattern.`)}let r=new Set,o=[];for(let s of t.allowList)s.endsWith("*")?o.push(s.slice(0,-1)):r.add(s);for(let[s,i]of Object.entries(e))if(i!==void 0){if(r.has(s)){n[s]=i;continue}o.some(a=>s.startsWith(a)&&s.length>a.length)&&!au(s)&&(n[s]=i)}return n}for(let[r,o]of Object.entries(e))o!==void 0&&!au(r)&&(n[r]=o);return n}var KBe,VBe,JBe,Nm=v(()=>{"use strict";$n();KBe=new Set(["MINIMAX_API_KEY","MINIMAX_GROUP_ID","OPENAI_API_KEY","OPENAI_ORG_ID","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY","AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION","ZEU_API_KEY","ZERODEV_API_KEY"]),VBe={minimax:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"],openai:["OPENAI_API_KEY","OPENAI_ORG_ID"],anthropic:["ANTHROPIC_API_KEY"],google:["GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],gemini:["GOOGLE_API_KEY","GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],azure:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],"azure-openai":["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"],aws:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],bedrock:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],zai:["ZEU_API_KEY"],zerodev:["ZERODEV_API_KEY"]};JBe=["token","api","key","password","passwd","secret","credential","authorization","private"]});function Uj(e,t){try{let n=JSON.parse(e.trim());if(!n||typeof n!="object")return;let r=n;if(r.v!==2||r.runId!==t||typeof r.socketPath!="string"||r.socketPath.length===0)return;let o=r.tasks;if(!o||typeof o!="object"||Array.isArray(o))return;for(let[s,i]of Object.entries(o))if(s.length===0||typeof i!="string"||i.length===0)return;return{v:2,runId:r.runId,socketPath:r.socketPath,tasks:o}}catch{return}}function BS(e,t,n){let r=`${JSON.stringify({v:2,runId:e,socketPath:t,tasks:n})}
478
478
  `;return r.length>XBe&&b("stdin-handshake.oversize-payload",new Error(`broker stdin handshake line is ${r.length} bytes \u2014 the worker reader aborts past 64 KiB, so EVERY task loses its broker creds (ask/message/mailbox/steer fall back to file-poll)`),`${Object.keys(n).length} tasks on run ${e}; shrink the task fanout or split the run`,"warn"),r}function ZBe(e,t){return new Promise(n=>{let r=!1,o=c=>{r||(r=!0,clearTimeout(s),e.removeListener("data",a),n(c))},s=setTimeout(()=>o(void 0),t),i="",a=c=>{i+=typeof c=="string"?c:c.toString("utf-8");let u=i.indexOf(`
479
- `);u>=0?(e.pause(),o(i.slice(0,u))):i.length>64*1024&&o(void 0)};e.on("data",a),e.on("end",()=>o(i.length>0?i:void 0)),e.on("error",()=>o(void 0))})}function ute(e=2e3){let t=process.stdin;return!t||t.isTTY||t.destroyed||!t.readable?Promise.resolve(void 0):ZBe(t,e)}var XBe,By=v(()=>{"use strict";H();XBe=60*1024});import{lstatSync as dte,readdirSync as QBe}from"node:fs";import{join as Bj}from"node:path";function fte(e,t){let n=Bj(e,"scratchpad"),r;try{r=dte(n)}catch{return null}if(r.isSymbolicLink()||!r.isDirectory())return null;let o;try{o=QBe(n,{withFileTypes:!0})}catch{return null}let s=`${t}.attempt-`,i=null;for(let a of o){if(a.isSymbolicLink()||!a.isFile())continue;let c=a.name;if(!c.startsWith(s)||!c.endsWith(lte))continue;let u=c.slice(s.length,c.length-lte.length);if(!/^\d+$/.test(u))continue;let d=Number.parseInt(u,10),l;try{l=dte(Bj(n,c))}catch{continue}if(!l.isFile())continue;let f={path:Bj(n,c),attempt:d,mtimeMs:l.mtimeMs};(i===null||f.mtimeMs>i.mtimeMs||f.mtimeMs===i.mtimeMs&&f.attempt<i.attempt)&&(i=f)}return i}var lte,pte=v(()=>{"use strict";lte=".snapshot.json"});import*as $A from"node:fs";import*as OA from"node:path";function tGe(){return z("PI_CREW_BROKER_STDIN")==="1"}function Gj(e,t,n,r){let o;try{if(o=$A.realpathSync(e),!$A.statSync(o).isDirectory())throw new Error(`cwd is not a directory: ${e}`)}catch(l){if(l.code==="ENOENT"&&l instanceof Error&&l.message.includes("ENOENT"))o=OA.resolve(e);else throw new Error(`Invalid cwd: ${e} \u2014 ${l instanceof Error?l.message:String(l)}`)}let s=r&&r.length>0?r:n!==void 0?[n]:[],a=!Pm().active&&s.length>0?cte(MA,s):MA,c=s.some(l=>jj(l).length>0),u=s.some(l=>typeof l=="string"&&!l.includes("/"));s.length>0&&u&&!c&&b("child-pi-spawn.bare-model-no-provider-keys",void 0,`model "${n??s.find(l=>typeof l=="string")}" is a bare id (no provider prefix) and resolved zero provider env keys \u2014 the child worker gets no provider API key via env; env-key users should qualify the model id (e.g. "openai/<id>") or configure a fallback chain (auth.json users are unaffected)`,"warn");let d=Vi(t,{allowList:a});if(d.NODE_PATH){let l=["/opt/","/lib/","/usr/local/","/usr/","/home/"],f=d.NODE_PATH.split(":").filter(p=>l.some(m=>p.startsWith(m)));f.length>0?d.NODE_PATH=f.join(":"):delete d.NODE_PATH}return{cwd:o,env:{...d,PI_CREW_PARENT_PID:String(process.pid)},stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32",setsid:!0,windowsHide:!0}}function nGe(e){for(let t of Object.keys(e))if(!t.startsWith("PI_CREW_")&&!t.startsWith("PI_TEAMS_"))throw new Error(`SECURITY: built.env contains unexpected key "${t}"; expected only PI_CREW_* or PI_TEAMS_* execution-control vars`)}function Dm(e,t,n,r,o,s,i){nGe(n);let a=Gj(e,t,r,o);a.env={...a.env,...n};let c=Pm();return c.active&&n[hA]&&(a.env[uee]=c.scopedAgentDir??""),s&&(a.env[eGe]="short"),i!==void 0&&(a.stdio=["ignore","pipe","pipe","pipe"],delete a.env.PI_CREW_BROKER_TOKEN),a}function mte(e,t,n){let r=iL({task:t,agent:e.agent,model:e.model,sessionEnabled:!0,taskId:e.agentId,sessionId:e.sessionId,sessionDir:e.sessionDir,maxDepth:e.maxDepth,skillPaths:e.skillPaths,role:e.role,thinkingOverride:e.thinkingOverride,systemPromptAppend:e.systemPromptAppend,hermeticWorkers:e.hermeticWorkers,piVersion:e.piVersion,env:n}),o=fee(),s,i;if(o.active&&(s=e.agentId?vj(e.agentId):`inference:spawn:${process.pid}:${Date.now().toString(36)}`,r.env[hA]=pee(s)),e.steeringFile&&(r.env.PI_CREW_STEERING_FILE=e.steeringFile),e.runId&&(r.env.PI_CREW_BROKER_RUN_ID=e.runId),e.agentId&&(r.env.PI_CREW_BROKER_TASK_ID=e.agentId),r.env.PI_CREW_ASK_ENABLED="1",r.env.PI_CREW_MSG_ENABLED="1",r.env.PI_CREW_DELEGATE_ENABLED="1",e.eventsPath&&(r.env.PI_CREW_STATE_ROOT=OA.dirname(e.eventsPath)),e.brokerSpawn?.socketPath&&e.brokerSpawn.token){r.env.PI_CREW_BROKER_SOCKET=e.brokerSpawn.socketPath;let c=tGe();c&&(r.env.PI_CREW_BROKER_STDIN="1"),r.env.PI_CREW_BROKER_TOKEN=e.brokerSpawn.token,c&&e.runId&&e.agentId&&(i=BS(e.runId,e.brokerSpawn.socketPath,{[e.agentId]:e.brokerSpawn.token}))}if(e.eventsPath&&(r.env.PI_CREW_EVENTS_PATH=e.eventsPath,e.agentId&&!r.env.PI_CREW_TASK_ID&&(r.env.PI_CREW_TASK_ID=e.agentId)),e.agentId&&$I(e.role??e.agent.name,e.agent)){r.env.PI_CREW_SCRATCHPAD="1",r.env.PI_CREW_TASK_ID=e.agentId,r.env.PI_CREW_ATTEMPT=String(e.attempt??0),e.artifactsRoot&&(r.env.PI_CREW_ARTIFACTS_ROOT=e.artifactsRoot);let c=r.tempDir??df(xa(),"pi-crew-scratchpad-");r.env.PI_CREW_SCRATCHPAD_SNAPSHOT=pe(c,`${e.agentId}.snapshot.json`);let u=e.artifactsRoot?fte(e.artifactsRoot,e.agentId):null;u&&(r.env.PI_CREW_SCRATCHPAD_RESTORE=u.path,r.env.PI_CREW_SCRATCHPAD_RESTORE_MTIME=String(u.mtimeMs))}return e.signal?.aborted?{kind:"aborted",result:{exitCode:null,stdout:"",stderr:"",error:"Aborted before spawn (parent AbortSignal already aborted)",aborted:!0}}:{kind:"ready",ctx:{spawnSpec:Qs(r.args),builtArgs:r.args,mergedEnv:{...process.env,...r.env},tempDir:r.tempDir,builtEnv:r.env,inferenceWorkerKey:s,...i!==void 0?{brokerStdinPayload:i}:{}}}}var eGe,MA,GS=v(()=>{"use strict";Wj();Pe();OI();Nm();H();Xe();By();Vo();Dl();pte();yA();eGe="PI_CACHE_RETENTION";MA=ate()});import*as gte from"node:fs";var Hj,Gy,qj=v(()=>{"use strict";H();tf();Hj="You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now.",Gy=class{turnCount=0;softLimitReached=!1;hardAbortInitiatedFlag=!1;maxTurns;graceTurns;constructor(t,n){this.maxTurns=t,this.graceTurns=n!==void 0&&n>1e3?1e3:n}onTurnEnd(t,n,r){if(this.turnCount+=1,this.maxTurns!==void 0&&!this.softLimitReached&&this.turnCount>=this.maxTurns){if(this.softLimitReached=!0,r)try{gte.appendFileSync(r,JSON.stringify({type:"steer",message:Hj})+`
479
+ `);u>=0?(e.pause(),o(i.slice(0,u))):i.length>64*1024&&o(void 0)};e.on("data",a),e.on("end",()=>o(i.length>0?i:void 0)),e.on("error",()=>o(void 0))})}function ute(e=2e3){let t=process.stdin;return!t||t.isTTY||t.destroyed||!t.readable?Promise.resolve(void 0):ZBe(t,e)}var XBe,By=v(()=>{"use strict";H();XBe=60*1024});import{lstatSync as dte,readdirSync as QBe}from"node:fs";import{join as Bj}from"node:path";function fte(e,t){let n=Bj(e,"scratchpad"),r;try{r=dte(n)}catch{return null}if(r.isSymbolicLink()||!r.isDirectory())return null;let o;try{o=QBe(n,{withFileTypes:!0})}catch{return null}let s=`${t}.attempt-`,i=null;for(let a of o){if(a.isSymbolicLink()||!a.isFile())continue;let c=a.name;if(!c.startsWith(s)||!c.endsWith(lte))continue;let u=c.slice(s.length,c.length-lte.length);if(!/^\d+$/.test(u))continue;let d=Number.parseInt(u,10),l;try{l=dte(Bj(n,c))}catch{continue}if(!l.isFile())continue;let f={path:Bj(n,c),attempt:d,mtimeMs:l.mtimeMs};(i===null||f.mtimeMs>i.mtimeMs||f.mtimeMs===i.mtimeMs&&f.attempt<i.attempt)&&(i=f)}return i}var lte,pte=v(()=>{"use strict";lte=".snapshot.json"});import*as $A from"node:fs";import*as OA from"node:path";function tGe(){return z("PI_CREW_BROKER_STDIN")!=="0"}function Gj(e,t,n,r){let o;try{if(o=$A.realpathSync(e),!$A.statSync(o).isDirectory())throw new Error(`cwd is not a directory: ${e}`)}catch(l){if(l.code==="ENOENT"&&l instanceof Error&&l.message.includes("ENOENT"))o=OA.resolve(e);else throw new Error(`Invalid cwd: ${e} \u2014 ${l instanceof Error?l.message:String(l)}`)}let s=r&&r.length>0?r:n!==void 0?[n]:[],a=!Pm().active&&s.length>0?cte(MA,s):MA,c=s.some(l=>jj(l).length>0),u=s.some(l=>typeof l=="string"&&!l.includes("/"));s.length>0&&u&&!c&&b("child-pi-spawn.bare-model-no-provider-keys",void 0,`model "${n??s.find(l=>typeof l=="string")}" is a bare id (no provider prefix) and resolved zero provider env keys \u2014 the child worker gets no provider API key via env; env-key users should qualify the model id (e.g. "openai/<id>") or configure a fallback chain (auth.json users are unaffected)`,"warn");let d=Vi(t,{allowList:a});if(d.NODE_PATH){let l=["/opt/","/lib/","/usr/local/","/usr/","/home/"],f=d.NODE_PATH.split(":").filter(p=>l.some(m=>p.startsWith(m)));f.length>0?d.NODE_PATH=f.join(":"):delete d.NODE_PATH}return{cwd:o,env:{...d,PI_CREW_PARENT_PID:String(process.pid)},stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32",setsid:!0,windowsHide:!0}}function nGe(e){for(let t of Object.keys(e))if(!t.startsWith("PI_CREW_")&&!t.startsWith("PI_TEAMS_"))throw new Error(`SECURITY: built.env contains unexpected key "${t}"; expected only PI_CREW_* or PI_TEAMS_* execution-control vars`)}function Dm(e,t,n,r,o,s,i){nGe(n);let a=Gj(e,t,r,o);a.env={...a.env,...n};let c=Pm();return c.active&&n[hA]&&(a.env[uee]=c.scopedAgentDir??""),s&&(a.env[eGe]="short"),i!==void 0&&(a.stdio=["ignore","pipe","pipe","pipe"],delete a.env.PI_CREW_BROKER_TOKEN),a}function mte(e,t,n){let r=iL({task:t,agent:e.agent,model:e.model,sessionEnabled:!0,taskId:e.agentId,sessionId:e.sessionId,sessionDir:e.sessionDir,maxDepth:e.maxDepth,skillPaths:e.skillPaths,role:e.role,thinkingOverride:e.thinkingOverride,systemPromptAppend:e.systemPromptAppend,hermeticWorkers:e.hermeticWorkers,piVersion:e.piVersion,env:n}),o=fee(),s,i;if(o.active&&(s=e.agentId?vj(e.agentId):`inference:spawn:${process.pid}:${Date.now().toString(36)}`,r.env[hA]=pee(s)),e.steeringFile&&(r.env.PI_CREW_STEERING_FILE=e.steeringFile),e.runId&&(r.env.PI_CREW_BROKER_RUN_ID=e.runId),e.agentId&&(r.env.PI_CREW_BROKER_TASK_ID=e.agentId),r.env.PI_CREW_ASK_ENABLED="1",r.env.PI_CREW_MSG_ENABLED="1",r.env.PI_CREW_DELEGATE_ENABLED="1",e.eventsPath&&(r.env.PI_CREW_STATE_ROOT=OA.dirname(e.eventsPath)),e.brokerSpawn?.socketPath&&e.brokerSpawn.token){r.env.PI_CREW_BROKER_SOCKET=e.brokerSpawn.socketPath;let c=tGe();c&&(r.env.PI_CREW_BROKER_STDIN="1"),r.env.PI_CREW_BROKER_TOKEN=e.brokerSpawn.token,c&&e.runId&&e.agentId&&(i=BS(e.runId,e.brokerSpawn.socketPath,{[e.agentId]:e.brokerSpawn.token}))}if(e.eventsPath&&(r.env.PI_CREW_EVENTS_PATH=e.eventsPath,e.agentId&&!r.env.PI_CREW_TASK_ID&&(r.env.PI_CREW_TASK_ID=e.agentId)),e.agentId&&$I(e.role??e.agent.name,e.agent)){r.env.PI_CREW_SCRATCHPAD="1",r.env.PI_CREW_TASK_ID=e.agentId,r.env.PI_CREW_ATTEMPT=String(e.attempt??0),e.artifactsRoot&&(r.env.PI_CREW_ARTIFACTS_ROOT=e.artifactsRoot);let c=r.tempDir??df(xa(),"pi-crew-scratchpad-");r.env.PI_CREW_SCRATCHPAD_SNAPSHOT=pe(c,`${e.agentId}.snapshot.json`);let u=e.artifactsRoot?fte(e.artifactsRoot,e.agentId):null;u&&(r.env.PI_CREW_SCRATCHPAD_RESTORE=u.path,r.env.PI_CREW_SCRATCHPAD_RESTORE_MTIME=String(u.mtimeMs))}return e.signal?.aborted?{kind:"aborted",result:{exitCode:null,stdout:"",stderr:"",error:"Aborted before spawn (parent AbortSignal already aborted)",aborted:!0}}:{kind:"ready",ctx:{spawnSpec:Qs(r.args),builtArgs:r.args,mergedEnv:{...process.env,...r.env},tempDir:r.tempDir,builtEnv:r.env,inferenceWorkerKey:s,...i!==void 0?{brokerStdinPayload:i}:{}}}}var eGe,MA,GS=v(()=>{"use strict";Wj();Pe();OI();Nm();H();Xe();By();Vo();Dl();pte();yA();eGe="PI_CACHE_RETENTION";MA=ate()});import*as gte from"node:fs";var Hj,Gy,qj=v(()=>{"use strict";H();tf();Hj="You have reached your turn limit. Wrap up immediately \u2014 provide your final answer now.",Gy=class{turnCount=0;softLimitReached=!1;hardAbortInitiatedFlag=!1;maxTurns;graceTurns;constructor(t,n){this.maxTurns=t,this.graceTurns=n!==void 0&&n>1e3?1e3:n}onTurnEnd(t,n,r){if(this.turnCount+=1,this.maxTurns!==void 0&&!this.softLimitReached&&this.turnCount>=this.maxTurns){if(this.softLimitReached=!0,r)try{gte.appendFileSync(r,JSON.stringify({type:"steer",message:Hj})+`
480
480
  `,"utf-8")}catch(o){b("child-pi.steer-write-failed",o instanceof Error?o:new Error(String(o)),`pid=${t}`)}return{kind:"steer"}}return this.maxTurns!==void 0&&this.softLimitReached&&this.turnCount>=this.maxTurns+(this.graceTurns??5)?(this.hardAbortInitiatedFlag=!0,t!==void 0&&n?(As(t,n),{kind:"hardAbort",pid:t,child:n}):{kind:"none"}):{kind:"none"}}isHardAbortInitiated(){return this.hardAbortInitiatedFlag}isSoftLimitReached(){return this.softLimitReached}getTurnCount(){return this.turnCount}getMaxTurns(){return this.maxTurns}getGraceTurns(){return this.graceTurns}}});function NA(e){let t,n,r,o,s,i,a=()=>{e.responseTimeoutMs<=0||(r&&clearTimeout(r),r=setTimeout(()=>{e.state.setResponseTimeoutHit(!0);let d=e.stderrTail.value(),l=e.redactStderrExcerpt(d,1024);e.input.onLifecycleEvent?.({type:"response_timeout",pid:e.child.pid,error:`No output for ${e.responseTimeoutMs}ms`,ts:new Date().toISOString(),stderr:l||void 0}),As(e.child.pid,e.child);try{e.child.kill(process.platform==="win32"?void 0:"SIGTERM")}catch(p){b("child-pi.response-timeout-term",p,`pid=${e.child.pid}`)}let f=mu+2e3;o=setTimeout(()=>{if(!(e.state.getSettled()||e.state.getChildExited())){b("child-pi.settle-safety-fired",new Error(`Child did not exit within ${f}ms of kill; forcing settle`),`pid=${e.child.pid}, responseTimeoutMs=${e.responseTimeoutMs}`);try{process.kill(e.child.pid,0);let p=`Child Pi produced no new output for ${e.responseTimeoutMs}ms; killed but did not exit within ${f}ms (possible zombie).`;e.getSettle()({exitCode:null,stdout:e.stdoutTail.value(),stderr:e.stderrTail.value(),error:p,exitStatus:{exitCode:null,cancelled:e.state.getAbortRequested(),timedOut:!0,killed:e.state.getHardKilled(),cleanupErrors:e.cleanupErrors,finalDrainMs:e.finalDrainMs,crashClass:"timeout"}})}catch{}}},f),o.unref()},e.responseTimeoutMs),r.unref())},c=()=>{r&&clearTimeout(r),r=void 0},u=()=>{t&&clearTimeout(t),n&&clearTimeout(n),t=void 0,n=void 0};return{restartNoResponseTimer:a,clearNoResponseTimer:c,clearFinalDrainTimers:u,armFinalDrain(){let d=e.input.finalDrainQuietMs??Po.finalDrainQuietMs;d<(e.input.finalDrainMs??Po.finalDrainMs)&&(i=setInterval(()=>{if(e.state.getSettled()||e.state.getChildExited()){i&&(clearInterval(i),i.unref()),i=void 0;return}if(performance.now()-e.state.getLastStdoutActivityMonotonicMs()>=d){i&&(clearInterval(i),i.unref()),i=void 0,e.state.setForcedFinalDrain(!0),e.state.setFinalDrainFiredMonotonicMs(performance.now()),e.input.onLifecycleEvent?.({type:"final_drain",pid:e.child.pid,ts:new Date().toISOString(),reason:"stdout-quiet"});try{e.child.kill(process.platform==="win32"?void 0:"SIGTERM")}catch(f){b("child-pi.quiet-drain-term",f,`pid=${e.child.pid}`)}n=setTimeout(()=>{if(!(e.state.getSettled()||e.state.getChildExited()))try{e.state.setHardKilled(!0),e.input.onLifecycleEvent?.({type:"hard_kill",pid:e.child.pid,ts:new Date().toISOString()}),e.child.kill(process.platform==="win32"?void 0:"SIGKILL")}catch(f){b("child-pi.quiet-drain-hard-kill",f,`pid=${e.child.pid}`)}},e.hardKillMs),n.unref(),t&&(clearTimeout(t),t=void 0)}},200),i.unref()),t=setTimeout(()=>{if(!(e.state.getSettled()||e.state.getChildExited())){e.state.setForcedFinalDrain(!0),e.state.setFinalDrainFiredMonotonicMs(performance.now()),e.input.onLifecycleEvent?.({type:"final_drain",pid:e.child.pid,ts:new Date().toISOString()});try{e.child.kill(process.platform==="win32"?void 0:"SIGTERM")}catch(l){b("child-pi.final-drain-term",l,`pid=${e.child.pid}`)}n=setTimeout(()=>{if(!(e.state.getSettled()||e.state.getChildExited()))try{e.state.setHardKilled(!0),e.input.onLifecycleEvent?.({type:"hard_kill",pid:e.child.pid,ts:new Date().toISOString()}),e.child.kill(process.platform==="win32"?void 0:"SIGKILL")}catch(l){b("child-pi.final-drain-kill",l,`pid=${e.child.pid}`)}},e.hardKillMs),n.unref()}},e.finalDrainMs),t.unref()},hasFinalDrainTimer(){return t!==void 0},armCancelHardKill(){s=setTimeout(()=>{if(!(e.state.getSettled()||e.state.getChildExited()))try{e.state.setHardKilled(!0),e.child.kill(process.platform==="win32"?void 0:"SIGKILL")}catch(d){b("child-pi.cancel-fast-kill",d,`pid=${e.child.pid}`)}},200),s.unref()},clearAll(){c(),o&&clearTimeout(o),o=void 0,u(),s&&clearTimeout(s),s=void 0,i&&(clearInterval(i),i.unref()),i=void 0}}}var Kj=v(()=>{"use strict";It();H();Mh();tf()});import*as Lm from"node:fs";import*as Hy from"node:path";function hte(e){let t=e.includes(":")?e.split(":").pop()??"":e;return Gn(t)?t:null}function wte(e){let t=e.agentId?hte(e.agentId):null,n=e.sessionId?hte(e.sessionId):null,r=n??t,o=t??n;return!r||!o?null:e.sessionDir?{sessionId:r,sessionDir:e.sessionDir}:e.artifactsRoot?{sessionId:r,sessionDir:Hy.join(e.artifactsRoot,"sessions",o)}:null}function bte(e,t,n){let r=!1,o=[];t.includes("--session-id")||o.push("--session-id",n.sessionId),t.includes("--session-dir")||o.push("--session-dir",n.sessionDir),o.length>0&&(yte(t,o),r=!0);let s=[];return e.includes("--session-id")||s.push("--session-id",n.sessionId),e.includes("--session-dir")||s.push("--session-dir",n.sessionDir),s.length>0&&(yte(e,s),r=!0),r}function yte(e,t){let n=e.indexOf("--mode");n!==-1&&e[n+1]==="json"&&e[n+2]==="-p"?e.splice(n+3,0,...t):e.push(...t)}function kte(e){let t=dr("PI_CREW_SESSION_RECOVERY");return t!==void 0?t:e??!0}function DA(e,t){let n=e.exitStatus,r=n&&n.exitCode!==void 0?n.exitCode:e.exitCode,o=n&&n.killed!==void 0?n.killed:t;return r===null||o===!0}async function iGe(){if(HS!==void 0)return HS;try{let e=await import("@earendil-works/pi-coding-agent");HS=typeof e.parseSessionEntries=="function"&&typeof e.migrateSessionEntries=="function"?e:null}catch{HS=null}return HS}function aGe(e,t){let n;try{n=Lm.readdirSync(e)}catch{return[]}let r=n.filter(c=>c.endsWith(".jsonl")),o=c=>{let u=[];for(let d of c)try{u.push({name:d,mtimeMs:Lm.statSync(Hy.join(e,d)).mtimeMs})}catch{}return u.sort((d,l)=>l.mtimeMs-d.mtimeMs)},s=t?o(r.filter(c=>c.endsWith(`_${t}.jsonl`))):[],i=new Set(s.map(c=>c.name)),a=o(r.filter(c=>!i.has(c)));return[...s,...a].map(c=>Hy.join(e,c.name))}function cGe(e){if(!Array.isArray(e))return typeof e=="string"?e:"";let t=[];for(let n of e)if(n&&typeof n=="object"&&n.type==="text"){let r=n.text;typeof r=="string"&&r.length>0&&t.push(r)}return t.join(`
481
481
  `)}async function uGe(e,t){let n;try{n=Lm.statSync(e).size}catch{return null}if(n<=0||n>sGe)return null;let r;try{r=Lm.readFileSync(e,"utf8")}catch{return null}r.length>0&&!r.endsWith(`
482
482
  `)&&(r=r.slice(0,r.lastIndexOf(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.11.49",
3
+ "version": "0.11.50",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -202,6 +202,8 @@ End-to-end verification discipline for pi-crew changes. Distilled from the broke
202
202
 
203
203
  > **2026-10-28-b update (ROOT-CAUSE CLOSED + fd3 transport + review-wave fixes, commits `38d5ed58`..`5190e66b`, cold-verified review + 3-agent verification)**: (1) **ROOT-CAUSE incident 0.11.46/47 (đóng)** — pi's `readPipedStdin` (pi@1.0.4 `dist/main.js:52-65`) đọc TOÀN BỘ piped stdin làm PROMPT CONTENT trong mọi mode trừ `--mode=rpc` ⇒ handshake fd0 race 3 cách: token JSON thành literal prompt text (SECRET LEAK — chứng minh trên transcript thật), stream-ended creds-less câm lặng, reader `pause()` trước EOF drain → pi boot await deadlock → **TỰ exit 0** (KHÔNG phải parent SIGTERM — strace cũ misread) + takeOverStdout buffer ⇒ ~0 bytes. **Fix: handshake dời sang fd3** (stdio entry 4 `['ignore','pipe','pipe','pipe']`, `readFirstLineFromFd(BROKER_HANDSHAKE_FD=3)`; fd0 về `"ignore"` — shape pre-0.11.46); detached runner GIỮ fd0 (node process, không race). Gate `PI_CREW_BROKER_STDIN` vẫn OPT-IN. Evidence: pre-fix pi@1.0.4 gate-ON 5/5 chết (~1s, 0B) → post-fix 2/2 sống (106-110KB, ~3.2s, 0 leak); integration live-worker giờ assert CẢ gate-ON liveness (bỏ comment "DO NOT assert liveness") + leak-check `v":2` không xuất hiện trong output. **Contract nền: fd0 KHÔNG BAO GIỜ là kênh sideband của extension khi spawn pi binary — dùng stdio entry 4+ (fd3).** (2) **T7 judge mechanics ĐỔI** — dispatch goal giờ mang nonce `[t7-nonce:<uuid>]`, judge CHỈ dirs có nonce trong events.jsonl (hết race newest-mtime với run đồng thời); non-terminal run = FAIL (check TRƯỚC killed-branch); stdout/stderr của dispatch được capture (cap 64KB) vào failure reason + audit. (3) **T7 triggers mở rộng** — thêm `src/(state|utils|agents|teams|workflows|hooks|observability)/` + `scripts/` (gate changes live-test the gate). (4) diff-base tag filter `^v\d+\.\d+\.\d+$` + porcelain rename both-sides; `PI_CREW_BATTERY_SKIP` giờ sống qua prepublishOnly path. (5) maxTasksPerRun admission cap tại createRunManifest (pre-dispatch); seam fix gate đọc `options.env ?? process.env` (test inject được — hết mutate process.env); `buildBrokerStdinLine` warn >60KiB (reader abort ~131KiB thực tế Linux — pipe chunks). (6) **CI 'Checks' step có gates NGOÀI battery list** (`check:env-vars`, `check:decision-drift`) — thêm vào leader local gate list sau mọi src edit.
204
204
 
205
+ > **2026-10-28-c update (LOW-1 DEFAULT-ON flip — commit riêng ngay sau `deb09c5e`/v0.11.49)**: `PI_CREW_BROKER_STDIN` gate lật **DEFAULT-ON** — cả 2 gate (`resolveBrokerStdinHandshakeEnabled` child-pi-spawn.ts + `resolveBrokerStdinFallbackEnabled` crew-broker-child.ts) đổi `=== "1"` → `!== "0"` (chỉ đúng `0` opt-out; unset/`1`/bất kỳ giá trị khác = ON — kể cả garbage như `true`). Fix-1 spawner vẫn ghi `"1"` vào built.env khi gate ON (pin route + tương thích worker pre-flip); parent opt-out ⇒ token ở env child (worker reader tự disarm). T7 live-worker test có thêm pin DEFAULT (gate unset + fd3 payload → alive + no-leak). **Meta-rule của update 2026-10-28 (1) vẫn nguyên: flip này là ĐÚNG lớp incident 0.11.46/47 — battery T7 REAL-TEST BẮT BUỘC trước publish, không chỉ CI.** Skew case mới: gate unset + KHÔNG fd3 pipe (old-parent shape) → `readFirstLineFromFd(3)` missing-fd contract = undefined (bounded, không treo) — pin bởi test đầu của `broker-stdin-reader-liveworker.test.ts`.
206
+
205
207
  ## Core principle: disk ≠ live Pi
206
208
 
207
209
  Two locations hold pi-crew state:
@@ -200,7 +200,7 @@ const CREW_ENV_FORWARDING_DECL: Record<string, CrewEnvForwardDecision> = {
200
200
  PI_CREW_BROKER_TASK_ID: { kind: "child-write", reason: "child-pi-spawn broker handshake" },
201
201
  PI_CREW_BROKER_STDIN: {
202
202
  kind: "child-write",
203
- reason: "LOW-1 broker-credential transport gate — read by the HOST spawner (child-pi-spawn .resolveBrokerStdinHandshakeEnabled) to pick env vs stdin route, then RE-WRITTEN by that spawner into every brokered child's built.env ('1') so the worker-side mirror gate (crew-broker-child .resolveBrokerStdinFallbackEnabled, which reads its OWN process.env) actually sees it — ambient forwarding never reaches the child, so child-write is the only channel that wires the route through (FIX-1 2026-10-28). Never ambient-allowlisted: route selection is per-spawn",
203
+ reason: "LOW-1 broker-credential transport gate (DEFAULT-ON since 2026-10-28 — only exact '0' opts out) — read by the HOST spawner (child-pi-spawn .resolveBrokerStdinHandshakeEnabled) to pick env vs stdin route, then RE-WRITTEN by that spawner into every gate-ON brokered child's built.env ('1') so the worker-side mirror gate (crew-broker-child .resolveBrokerStdinFallbackEnabled, which reads its OWN process.env) is armed deterministically and pre-flip workers (mirror === '1') stay compatible — ambient forwarding never reaches the child, so child-write is the only channel that wires the route through (FIX-1 2026-10-28). Never ambient-allowlisted: route selection is per-spawn",
204
204
  },
205
205
  PI_CREW_INFERENCE_BROKER: {
206
206
  kind: "gap",
@@ -668,7 +668,7 @@ export const CREW_ENV_VARS: Record<string, CrewEnvVarSpec> = {
668
668
  },
669
669
  PI_CREW_BROKER_STDIN: {
670
670
  name: "PI_CREW_BROKER_STDIN",
671
- doc: "LOW-1 closure (2026-10-27) broker-credential transport gate, OPT-IN since v0.11.48 (2026-10-28): '1' sends the per-task compound PI_CREW_BROKER_TOKEN to the headless JSON worker over a DEDICATED fd3 spawn pipe (one buildBrokerStdinLine v2 payload on stdio entry 4; heap → fd3 → heap — child-pi-spawn.ts writer, crew-broker-child.ts startChildBrokerClientAsync reader; fd0 stays 'ignore') so the token never appears in the child env; unset/any-other-value (default) keeps the LEGACY ENV ROUTE (token in built.env) on BOTH sides — the original fd0 variant was caught by the live battery (real-test-pi-crew T7) killing real headless workers with a silent exit 0 (root cause: pi@1.0.4 readPipedStdin consumes all piped fd0 as prompt content); the fd3 transport removes that race. RPC-mode workers and surface TUI panes are unaffected (they keep the env route; the worker-side fd3 read skips --mode rpc argv by design)",
671
+ doc: "LOW-1 closure (2026-10-27) broker-credential transport gate, DEFAULT-ON since 2026-10-28 (was OPT-IN v0.11.48–0.11.49): unset (default)/'1'/any value other than exactly '0' sends the per-task compound PI_CREW_BROKER_TOKEN to the headless JSON worker over a DEDICATED fd3 spawn pipe (one buildBrokerStdinLine v2 payload on stdio entry 4; heap → fd3 → heap — child-pi-spawn.ts writer, crew-broker-child.ts startChildBrokerClientAsync reader; fd0 stays 'ignore') so the token never appears in the child env; exactly '0' opts OUT (rollback to the LEGACY ENV ROUTE — token in built.env — on BOTH sides) — the original fd0 variant was caught by the live battery (real-test-pi-crew T7) killing real headless workers with a silent exit 0 (root cause: pi@1.0.4 readPipedStdin consumes all piped fd0 as prompt content); the fd3 transport removes that race. RPC-mode workers and surface TUI panes are unaffected (they keep the env route; the worker-side fd3 read skips --mode rpc argv by design)",
672
672
  },
673
673
  PI_CREW_GUEST: {
674
674
  name: "PI_CREW_GUEST",
@@ -109,7 +109,7 @@ function argvIndicatesRpcMode(): boolean {
109
109
  /** LOW-1 residual closure (2026-10-27): async wrapper used by the worker
110
110
  * bootstrap (prompt-runtime Feature 2b). Identical contract to
111
111
  * {@link startChildBrokerClient}, plus: when the env token is ABSENT but the
112
- * socket/run/task identity env IS present (opt-in parent on the fd3 route —
112
+ * socket/run/task identity env IS present (gate-ON parent on the fd3 route —
113
113
  * FIX-1 re-writes the flag into the child's built.env, without which this
114
114
  * fallback was dead-wired), read the single buildBrokerStdinLine payload off
115
115
  * the DEDICATED fd3 handshake pipe (bounded, never throws) and merge the
@@ -120,14 +120,17 @@ function argvIndicatesRpcMode(): boolean {
120
120
  * prompt; creds-less silence; pause() deadlock → SIGTERM exit 0 with ~0
121
121
  * stdout — the 0.11.46/47 live incident), so the spawner now writes the
122
122
  * payload to a dedicated stdio[3] pipe and this reader reads fd 3, never
123
- * fd0. Ordering matrix (post FIX-1, 2026-10-28 — the gate is OPT-IN and the
124
- * spawner writes flag + token into built.env for every brokered spawn; only
125
- * the headless JSON compose strips the token):
126
- * - opt-in parent (PI_CREW_BROKER_STDIN=1) + headless JSON worker → flag
127
- * set, token ABSENT from env → token via fd3 (env stays secret-free);
128
- * - opt-in parent + RPC / surface worker → flag set, token PRESENT in env
123
+ * fd0. Ordering matrix (post FIX-1, 2026-10-28; DEFAULT-ON flip same day —
124
+ * the gate is ON unless the exact value '0' opts out, and the spawner writes
125
+ * flag + token into built.env for every brokered spawn; only the headless
126
+ * JSON compose strips the token):
127
+ * - default parent (gate ON — unset/'1'/any non-'0') + headless JSON worker
128
+ * → flag set, token ABSENT from env → token via fd3 (env stays secret-free);
129
+ * - gate-ON parent + RPC / surface worker → flag set, token PRESENT in env
129
130
  * → env route, fd3 untouched (their stdin is the protocol channel/TTY);
130
- * - legacy parent (gate off/unset) → env token present → env route;
131
+ * - opt-out parent (PI_CREW_BROKER_STDIN=0) → no flag write, env token
132
+ * present → env route (and even if the token were missing, the mirror
133
+ * gate's '0' check disarms the fd3 read);
131
134
  * - payload missing/invalid/wrong-run/socket-mismatch/unreadable fd3 →
132
135
  * creds-less no-op handle (same as today), never a throw.
133
136
  * The sync {@link startChildBrokerClient} stays untouched (env-only) so every
@@ -163,19 +166,22 @@ export async function startChildBrokerClientAsync(options: StartChildBrokerClien
163
166
  }
164
167
 
165
168
  /** Worker-side mirror of the parent gate (child-pi-spawn
166
- * .resolveBrokerStdinHandshakeEnabled). **OPT-IN since v0.11.48** (live
167
- * battery caught the reader path killing real headless workers — see the
168
- * parent gate's doc block): the default is OFF, so a legacy parent that set
169
- * the env token travels the legacy env route. PI_CREW_BROKER_STDIN=1 opts
170
- * BOTH sides into the stdin route — FIX-1 (2026-10-28): the spawner now
171
- * re-writes the flag into the child's built.env ("child-write" registry
172
- * kind), so an opt-in parent actually reaches this mirror gate (previously
173
- * the flag died with the ambient-env strip and this fallback was dead-wired).
169
+ * .resolveBrokerStdinHandshakeEnabled). **DEFAULT-ON since 2026-10-28** (was
170
+ * OPT-IN v0.11.48–0.11.49, after the live battery caught the original fd0
171
+ * reader path killing real headless workers — see the parent gate's doc
172
+ * block; the v0.11.49 fd3 transport removed that race): the default is ON,
173
+ * so an unset flag arms the fd3 fallback whenever the identity env is
174
+ * present and the env token is absent. PI_CREW_BROKER_STDIN=0 opts BOTH
175
+ * sides out (the spawner then writes the env token instead — legacy env
176
+ * route). FIX-1 (2026-10-28): the spawner re-writes the flag "1" into the
177
+ * child's built.env ("child-write" registry kind), so a gate-ON parent
178
+ * reaches pre-flip workers too (their mirror still reads === "1") and the
179
+ * child route is pinned deterministically.
174
180
  * Seam (2026-10-28): the gate reads the RESOLVED env — the same source as
175
181
  * the identity vars above (`options.env ?? process.env`, threaded through
176
182
  * getCrewEnv's env param) — so spawn-env delivery (FIX-1 writes the flag
177
183
  * into the child's built.env) is pinnable in tests via options.env without
178
184
  * mutating process.env. */
179
185
  function resolveBrokerStdinFallbackEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
180
- return getCrewEnv("PI_CREW_BROKER_STDIN", env) === "1";
186
+ return getCrewEnv("PI_CREW_BROKER_STDIN", env) !== "0";
181
187
  }
@@ -46,11 +46,12 @@ import {
46
46
  // NOT set this (their sessions persist and benefit from long-lived cache).
47
47
  export const PI_CACHE_RETENTION_ENV = "PI_CACHE_RETENTION";
48
48
 
49
- /** LOW-1 residual closure (2026-10-27, DISABLED-DEFAULT 2026-10-28 v0.11.48):
49
+ /** LOW-1 residual closure (2026-10-27, DISABLED-DEFAULT v0.11.48 2026-10-28,
50
+ * DEFAULT-ON 2026-10-28):
50
51
  * when the inter-pi broker credentials are present, the per-task compound TOKEN
51
52
  * can travel over a DEDICATED fd3 spawn pipe (one `buildBrokerStdinLine`
52
53
  * payload, heap → stdio[3] → heap — fd0 stays "ignore") instead of the child
53
- * env. **EXPERIMENTAL / OPT-IN since v0.11.48**: the first attempt (v0.11.46/47)
54
+ * env. **DEFAULT-ON since 2026-10-28 (was OPT-IN v0.11.48–0.11.49)**: the first attempt (v0.11.46/47)
54
55
  * rode fd0 and was caught by the live battery (real-test-pi-crew T7) making
55
56
  * REAL headless JSON workers exit 0 silently with zero stdout. Root cause
56
57
  * (2026-10-28): pi@1.0.4 `readPipedStdin()` (dist/main.js:52-65) consumes ALL
@@ -62,8 +63,10 @@ export const PI_CACHE_RETENTION_ENV = "PI_CACHE_RETENTION";
62
63
  * (stdio entry 4); fd0 returns to "ignore" (pre-0.11.46 shape), killing every
63
64
  * race ordering. Unit tests missed the original because they inject the
64
65
  * `readHandshakeLine` seam instead of running the fd reader in a real pi
65
- * process. The gate stays OPT-IN (legacy env route bit-for-bit pre-0.11.46);
66
- * set PI_CREW_BROKER_STDIN=1 to opt the parent into the fd3 route.
66
+ * process. DEFAULT-ON FLIP (2026-10-28): with the fd3 transport proven live
67
+ * (v0.11.49), the gate flipped from opt-in to DEFAULT-ON — only exact
68
+ * PI_CREW_BROKER_STDIN=0 opts the parent out (legacy env route bit-for-bit
69
+ * pre-0.11.46); unset/'1'/any other value takes the fd3 route.
67
70
  *
68
71
  * FIX-1 dead-wire repair (2026-10-28): when this gate is ON and broker
69
72
  * credentials exist, prepareSpawnContext (1) WRITES PI_CREW_BROKER_STDIN="1"
@@ -79,9 +82,12 @@ export const PI_CACHE_RETENTION_ENV = "PI_CACHE_RETENTION";
79
82
  * Only the headless JSON branch composes WITH ctx.brokerStdinPayload, and
80
83
  * buildFinalChildPiSpawnOptions strips the token from the FINAL env there
81
84
  * (defense-in-depth) — on that branch alone the token rides the fd3 pipe.
82
- * Gate OFF: no flag, no payload, token via env — bit-for-bit legacy. */
85
+ * Gate OFF ('0'): no flag, no payload, token via env — bit-for-bit legacy.
86
+ * The FIX-1 "1" write stays even under the DEFAULT-ON flip: it keeps a NEW
87
+ * default-ON parent compatible with PRE-FLIP workers (their mirror gate still
88
+ * reads === "1"), and pins the child's route deterministically. */
83
89
  export function resolveBrokerStdinHandshakeEnabled(): boolean {
84
- return getCrewEnv("PI_CREW_BROKER_STDIN") === "1";
90
+ return getCrewEnv("PI_CREW_BROKER_STDIN") !== "0";
85
91
  }
86
92
 
87
93
  // ── Env allowlist (base set always passed to children) ──────────────────
@@ -489,16 +495,17 @@ export function prepareSpawnContext(
489
495
  // safe under assertOnlyControlEnvKeys). Only when the parent broker issued
490
496
  // credentials for this run — i.e. the broker is enabled AND this run is
491
497
  // eligible. NEVER persisted to disk.
492
- // LOW-1 closure (OPT-IN since v0.11.48; FIX-1 dead-wire repair 2026-10-28;
493
- // FD-3 transport 2026-10-28): when PI_CREW_BROKER_STDIN=1 the headless
494
- // JSON spawn branch delivers the token over a DEDICATED fd3 pipe (4th
498
+ // LOW-1 closure (OPT-IN v0.11.48–0.11.49, DEFAULT-ON since 2026-10-28;
499
+ // FIX-1 dead-wire repair 2026-10-28; FD-3 transport 2026-10-28): when the
500
+ // gate is ON (default — only exact PI_CREW_BROKER_STDIN=0 opts out) the
501
+ // headless JSON spawn branch delivers the token over a DEDICATED fd3 pipe (4th
495
502
  // stdio entry; ctx.brokerStdinPayload — fd0 stays "ignore") as a single
496
503
  // buildBrokerStdinLine payload, and buildFinalChildPiSpawnOptions strips
497
504
  // it from the FINAL env there. The token still lands in built.env for
498
505
  // every brokered spawn — the RPC/surface call sites compose without the
499
506
  // payload and need the env credential. SOCKET/RUN_ID/TASK_ID stay in env
500
507
  // (non-secret, needed by the worker bootstrap to bind and validate the
501
- // fd3 payload). Default (gate off) keeps the env route bit-for-bit.
508
+ // fd3 payload). Opt-out (gate '0') keeps the env route bit-for-bit.
502
509
  if (input.brokerSpawn?.socketPath && input.brokerSpawn.token) {
503
510
  built.env.PI_CREW_BROKER_SOCKET = input.brokerSpawn.socketPath;
504
511
  const brokerStdinRoute = resolveBrokerStdinHandshakeEnabled();
@@ -508,6 +515,8 @@ export function prepareSpawnContext(
508
515
  // "child-write" (ambient forwarding never carries it), so the spawner
509
516
  // MUST write it here — before the fix the flag never reached the
510
517
  // child and the stdin fallback NEVER activated (creds-less NOOP).
518
+ // Kept under the DEFAULT-ON flip for pre-flip-worker compatibility
519
+ // (their mirror gate still reads === "1").
511
520
  built.env.PI_CREW_BROKER_STDIN = "1";
512
521
  }
513
522
  // FIX-1: the token is written into built.env for BOTH branches. The RPC
@@ -281,7 +281,8 @@ export interface ChildPiRunInput {
281
281
  * in the parent's in-memory `Map<runId, token>` and in the child's env.
282
282
  * NEVER persisted to manifest, events, mailbox, or any run-dir file.
283
283
  *
284
- * OPT-IN fd3 route (PI_CREW_BROKER_STDIN=1, FIX-1 2026-10-28): the token
284
+ * DEFAULT-ON fd3 route (PI_CREW_BROKER_STDIN — '0' opts out; DEFAULT-ON
285
+ * flip 2026-10-28, FIX-1 2026-10-28): the token
285
286
  * is still written into built.env for every brokered spawn — RPC/surface
286
287
  * branches compose WITHOUT the payload and need the env credential, but
287
288
  * the headless JSON branch composes WITH ctx.brokerStdinPayload and