pi-crew 0.9.16 → 0.9.17
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 +81 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21790 -0
- package/dist/index.mjs +83070 -0
- package/dist/index.mjs.map +7 -0
- package/docs/A +358 -0
- package/docs/M-A +357 -0
- package/docs/REVIEW-FINDINGS-2026-06 +357 -0
- package/docs/Y +357 -0
- package/docs/a +357 -0
- package/docs/aA +358 -0
- package/docs/archive/README.md +91 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/skills/REFERENCE.md +0 -17
- package/docs//303/242mmaAAA/303/242 +357 -0
- package/index.bundle.ts +25 -0
- package/index.ts +95 -4
- package/package.json +15 -4
- package/skills/iterative-audit/SKILL.md +0 -1
- package/skills/widget-rendering/SKILL.md +17 -0
- package/src/adapters/claude-adapter.ts +1 -3
- package/src/adapters/export-util.ts +16 -16
- package/src/adapters/index.ts +5 -5
- package/src/agents/agent-config.ts +20 -14
- package/src/agents/agent-serializer.ts +1 -1
- package/src/agents/discover-agents.ts +83 -39
- package/src/benchmark/benchmark-runner.ts +51 -51
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +166 -590
- package/src/config/defaults.ts +28 -2
- package/src/config/drift-detector.ts +11 -14
- package/src/config/markers.ts +201 -203
- package/src/config/resilient-parser.ts +14 -6
- package/src/config/role-tools.ts +3 -3
- package/src/config/suggestions.ts +5 -12
- package/src/config/types.ts +9 -25
- package/src/errors.ts +151 -157
- package/src/extension/action-suggestions.ts +54 -9
- package/src/extension/async-notifier.ts +43 -13
- package/src/extension/autonomous-policy.ts +77 -37
- package/src/extension/command-completions.ts +9 -8
- package/src/extension/context-status-injection.ts +14 -12
- package/src/extension/crew-autocomplete.ts +8 -16
- package/src/extension/crew-cleanup.ts +2 -1
- package/src/extension/crew-shortcuts.ts +9 -6
- package/src/extension/cross-extension-rpc.ts +136 -42
- package/src/extension/help.ts +1 -1
- package/src/extension/import-index.ts +10 -5
- package/src/extension/knowledge-injection.ts +88 -15
- package/src/extension/management.ts +89 -349
- package/src/extension/message-renderers.ts +3 -4
- package/src/extension/notification-router.ts +22 -5
- package/src/extension/notification-sink.ts +6 -3
- package/src/extension/pi-api.ts +17 -8
- package/src/extension/plan-orchestrate.ts +8 -27
- package/src/extension/project-init.ts +21 -6
- package/src/extension/register.ts +329 -975
- package/src/extension/registration/artifact-cleanup.ts +9 -4
- package/src/extension/registration/command-utils.ts +5 -1
- package/src/extension/registration/commands.ts +806 -314
- package/src/extension/registration/compaction-guard.ts +15 -15
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +293 -0
- package/src/extension/registration/subagent-helpers.ts +29 -7
- package/src/extension/registration/subagent-tools.ts +253 -44
- package/src/extension/registration/team-tool.ts +19 -84
- package/src/extension/registration/ui.ts +168 -0
- package/src/extension/registration/viewers.ts +64 -24
- package/src/extension/result-watcher.ts +18 -7
- package/src/extension/run-bundle-schema.ts +21 -6
- package/src/extension/run-export.ts +15 -6
- package/src/extension/run-import.ts +64 -29
- package/src/extension/run-index.ts +28 -9
- package/src/extension/run-maintenance.ts +32 -12
- package/src/extension/session-summary.ts +1 -1
- package/src/extension/team-manager-command.ts +64 -7
- package/src/extension/team-onboard.ts +151 -150
- package/src/extension/team-recommendation.ts +121 -21
- package/src/extension/team-tool/anchor.ts +31 -64
- package/src/extension/team-tool/api.ts +929 -141
- package/src/extension/team-tool/auto-summarize.ts +14 -26
- package/src/extension/team-tool/cache-control.ts +1 -5
- package/src/extension/team-tool/cancel.ts +138 -38
- package/src/extension/team-tool/chain-dispatch.ts +5 -13
- package/src/extension/team-tool/chain-executor.ts +29 -51
- package/src/extension/team-tool/config-patch.ts +1 -1
- package/src/extension/team-tool/context.ts +40 -20
- package/src/extension/team-tool/destructive-gate.ts +10 -5
- package/src/extension/team-tool/doctor.ts +170 -51
- package/src/extension/team-tool/explain.ts +228 -214
- package/src/extension/team-tool/failure-patterns.ts +3 -9
- package/src/extension/team-tool/goal-wrap.ts +71 -29
- package/src/extension/team-tool/goal.ts +185 -35
- package/src/extension/team-tool/handle-schedule.ts +34 -15
- package/src/extension/team-tool/handle-settings.ts +94 -56
- package/src/extension/team-tool/health-monitor.ts +27 -108
- package/src/extension/team-tool/inspect.ts +42 -9
- package/src/extension/team-tool/intent-policy.ts +4 -1
- package/src/extension/team-tool/lifecycle-actions.ts +244 -48
- package/src/extension/team-tool/orchestrate.ts +7 -18
- package/src/extension/team-tool/parallel-dispatch.ts +51 -29
- package/src/extension/team-tool/plan.ts +29 -5
- package/src/extension/team-tool/respond.ts +49 -16
- package/src/extension/team-tool/run-not-found.ts +3 -8
- package/src/extension/team-tool/run.ts +80 -317
- package/src/extension/team-tool/status.ts +135 -43
- package/src/extension/team-tool/workflow-manage.ts +92 -25
- package/src/extension/team-tool-types.ts +8 -1
- package/src/extension/team-tool.ts +142 -551
- package/src/extension/validate-resources.ts +39 -8
- package/src/hooks/registry.ts +66 -17
- package/src/hooks/types.ts +1 -1
- package/src/i18n.ts +25 -11
- package/src/observability/correlation.ts +17 -3
- package/src/observability/event-bus.ts +51 -56
- package/src/observability/event-to-metric.ts +117 -15
- package/src/observability/exporters/otlp-exporter.ts +56 -17
- package/src/observability/exporters/prometheus-exporter.ts +1 -1
- package/src/observability/metric-registry.ts +16 -4
- package/src/observability/metric-retention.ts +8 -2
- package/src/observability/metric-sink.ts +11 -3
- package/src/observability/metrics-primitives.ts +42 -9
- package/src/plugins/plugin-define.ts +2 -2
- package/src/plugins/plugin-registry.ts +25 -25
- package/src/plugins/plugins/index.ts +1 -1
- package/src/plugins/plugins/nextjs.ts +16 -16
- package/src/plugins/plugins/vite.ts +7 -11
- package/src/plugins/plugins/vitest.ts +6 -11
- package/src/runtime/adaptive-plan.ts +225 -40
- package/src/runtime/agent-control.ts +58 -15
- package/src/runtime/agent-memory.ts +15 -9
- package/src/runtime/agent-observability.ts +10 -2
- package/src/runtime/anchor-manager.ts +27 -25
- package/src/runtime/async-marker.ts +7 -1
- package/src/runtime/async-runner.ts +27 -23
- package/src/runtime/auto-summarize.ts +7 -13
- package/src/runtime/background-runner.ts +189 -251
- package/src/runtime/batch-barrier.ts +18 -16
- package/src/runtime/cancellation-token.ts +16 -6
- package/src/runtime/cancellation.ts +46 -8
- package/src/runtime/capability-inventory.ts +6 -4
- package/src/runtime/chain-parser.ts +44 -11
- package/src/runtime/chain-runner.ts +63 -69
- package/src/runtime/checkpoint.ts +12 -56
- package/src/runtime/child-pi.ts +343 -78
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +71 -26
- package/src/runtime/compact-stages/index.ts +15 -5
- package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
- package/src/runtime/compact-stages/truncation-stage.ts +4 -1
- package/src/runtime/compaction-summary.ts +17 -10
- package/src/runtime/completion-guard.ts +33 -16
- package/src/runtime/crash-classification.ts +28 -7
- package/src/runtime/crash-recovery.ts +177 -37
- package/src/runtime/crew-agent-records.ts +145 -47
- package/src/runtime/crew-hooks.ts +9 -24
- package/src/runtime/cross-extension-rpc.ts +25 -23
- package/src/runtime/custom-tools/irc-tool.ts +43 -15
- package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
- package/src/runtime/delivery-coordinator.ts +34 -7
- package/src/runtime/delta-conflict.ts +9 -21
- package/src/runtime/deterministic-ast.ts +1 -1
- package/src/runtime/diagnostic-export.ts +53 -20
- package/src/runtime/direct-run.ts +12 -2
- package/src/runtime/dwf-state-store.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +186 -58
- package/src/runtime/dynamic-workflow-runner.ts +42 -20
- package/src/runtime/effectiveness.ts +16 -8
- package/src/runtime/errors/crew-errors.ts +7 -11
- package/src/runtime/event-stream-bridge.ts +9 -3
- package/src/runtime/foreground-control.ts +54 -14
- package/src/runtime/foreground-watchdog.ts +9 -6
- package/src/runtime/goal-achievement.ts +27 -10
- package/src/runtime/goal-evaluator.ts +54 -19
- package/src/runtime/goal-loop-runner.ts +280 -99
- package/src/runtime/goal-state-store.ts +27 -17
- package/src/runtime/green-contract.ts +11 -2
- package/src/runtime/group-join.ts +64 -31
- package/src/runtime/handoff-manager.ts +37 -42
- package/src/runtime/heartbeat-gradient.ts +10 -2
- package/src/runtime/heartbeat-watcher.ts +32 -6
- package/src/runtime/hidden-handoff.ts +12 -30
- package/src/runtime/important-line-classifier.ts +12 -2
- package/src/runtime/iteration-hooks.ts +25 -15
- package/src/runtime/live-agent-control.ts +55 -7
- package/src/runtime/live-agent-manager.ts +130 -27
- package/src/runtime/live-control-realtime.ts +23 -3
- package/src/runtime/live-irc.ts +4 -1
- package/src/runtime/live-session-health.ts +12 -4
- package/src/runtime/live-session-runtime.ts +379 -117
- package/src/runtime/manifest-cache.ts +28 -12
- package/src/runtime/mcp-proxy.ts +8 -16
- package/src/runtime/metric-parser.ts +1 -5
- package/src/runtime/model-fallback.ts +55 -18
- package/src/runtime/model-resolver.ts +2 -6
- package/src/runtime/model-scope.ts +19 -3
- package/src/runtime/notebook-helpers.ts +60 -62
- package/src/runtime/orphan-worker-registry.ts +37 -26
- package/src/runtime/output-validator.ts +18 -3
- package/src/runtime/overflow-recovery.ts +5 -4
- package/src/runtime/parallel-research.ts +29 -5
- package/src/runtime/parallel-utils.ts +9 -11
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +12 -17
- package/src/runtime/per-write-validator.ts +1 -3
- package/src/runtime/phase-tracker.ts +342 -330
- package/src/runtime/pi-args.ts +26 -8
- package/src/runtime/pi-json-output.ts +1 -1
- package/src/runtime/pi-spawn.ts +30 -19
- package/src/runtime/pipeline-runner.ts +56 -55
- package/src/runtime/plan-templates.ts +5 -6
- package/src/runtime/policy-engine.ts +43 -7
- package/src/runtime/post-checks.ts +12 -4
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-lifecycle.ts +20 -10
- package/src/runtime/process-status.ts +16 -5
- package/src/runtime/progress-event-coalescer.ts +2 -1
- package/src/runtime/progress-tracker.ts +103 -103
- package/src/runtime/prose-compressor.ts +9 -11
- package/src/runtime/recovery-recipes.ts +118 -24
- package/src/runtime/replace.ts +25 -10
- package/src/runtime/resilient-edit.ts +10 -25
- package/src/runtime/result-extractor.ts +1 -2
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +26 -50
- package/src/runtime/role-permission.ts +6 -1
- package/src/runtime/run-coalesced-task-group.ts +256 -0
- package/src/runtime/run-drift.ts +14 -15
- package/src/runtime/run-tracker.ts +6 -28
- package/src/runtime/runtime-policy.ts +1 -6
- package/src/runtime/runtime-resolver.ts +80 -15
- package/src/runtime/scheduler.ts +47 -18
- package/src/runtime/semaphore.ts +5 -7
- package/src/runtime/sensitive-paths.ts +2 -1
- package/src/runtime/session-usage.ts +1 -1
- package/src/runtime/settings-store.ts +16 -12
- package/src/runtime/sidechain-output.ts +6 -2
- package/src/runtime/single-agent-compose.ts +1 -4
- package/src/runtime/skill-effectiveness.ts +29 -97
- package/src/runtime/skill-instructions.ts +40 -83
- package/src/runtime/stale-reconciler.ts +73 -197
- package/src/runtime/stream-preview.ts +1 -1
- package/src/runtime/streaming-output.ts +1 -1
- package/src/runtime/subagent-manager.ts +44 -169
- package/src/runtime/subprocess-tool-registry.ts +4 -1
- package/src/runtime/supervisor-contact.ts +10 -5
- package/src/runtime/task-display.ts +19 -5
- package/src/runtime/task-graph-scheduler.ts +95 -21
- package/src/runtime/task-graph.ts +5 -11
- package/src/runtime/task-health.ts +54 -47
- package/src/runtime/task-id.ts +11 -18
- package/src/runtime/task-output-context.ts +265 -81
- package/src/runtime/task-packet.ts +12 -20
- package/src/runtime/task-quality.ts +6 -14
- package/src/runtime/task-runner/context-retrieval.ts +4 -13
- package/src/runtime/task-runner/live-executor.ts +114 -36
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/progress.ts +50 -12
- package/src/runtime/task-runner/prompt-builder.ts +31 -7
- package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
- package/src/runtime/task-runner/result-utils.ts +3 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +27 -8
- package/src/runtime/task-runner/state-helpers.ts +29 -8
- package/src/runtime/task-runner/tail-read.ts +2 -6
- package/src/runtime/task-runner.ts +180 -326
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +456 -974
- package/src/runtime/tool-output-pruner.ts +6 -9
- package/src/runtime/tool-progress.ts +11 -21
- package/src/runtime/verification-gates.ts +33 -24
- package/src/runtime/verification-integrity.ts +1 -4
- package/src/runtime/verification-worktree.ts +62 -12
- package/src/runtime/worker-heartbeat.ts +5 -1
- package/src/runtime/worker-startup.ts +29 -5
- package/src/runtime/workflow-state.ts +17 -9
- package/src/runtime/workspace-lock.ts +30 -35
- package/src/runtime/workspace-tree.ts +20 -32
- package/src/runtime/yield-handler.ts +43 -9
- package/src/runtime/zombie-scanner.ts +5 -4
- package/src/schema/config-schema.ts +266 -172
- package/src/schema/team-tool-schema.ts +29 -74
- package/src/schema/validation-types.ts +25 -17
- package/src/skills/discover-skills.ts +35 -10
- package/src/skills/skill-templates.ts +109 -27
- package/src/skills/validate.ts +26 -8
- package/src/state/active-run-registry.ts +87 -28
- package/src/state/artifact-store.ts +9 -5
- package/src/state/atomic-write-v2.ts +85 -63
- package/src/state/atomic-write.ts +105 -26
- package/src/state/blob-store.ts +47 -20
- package/src/state/contracts.ts +20 -5
- package/src/state/crew-init.ts +5 -22
- package/src/state/decision-ledger.ts +19 -73
- package/src/state/event-log-rotation.ts +41 -15
- package/src/state/event-log.ts +168 -53
- package/src/state/event-reconstructor.ts +11 -1
- package/src/state/gitignore-manager.ts +2 -8
- package/src/state/health-store.ts +57 -57
- package/src/state/hook-instinct-bridge.ts +1 -1
- package/src/state/hook-integrations.ts +1 -1
- package/src/state/instinct-store.ts +17 -5
- package/src/state/jsonl-writer.ts +1 -1
- package/src/state/locks.ts +23 -9
- package/src/state/mailbox.ts +151 -28
- package/src/state/observation-store.ts +20 -14
- package/src/state/run-cache.ts +142 -135
- package/src/state/run-graph.ts +6 -15
- package/src/state/run-metrics.ts +5 -18
- package/src/state/schedule.ts +15 -9
- package/src/state/state-store.ts +129 -45
- package/src/state/task-claims.ts +12 -2
- package/src/state/tiered-eval.ts +52 -43
- package/src/state/types-eval.ts +2 -2
- package/src/state/types.ts +24 -20
- package/src/state/usage.ts +20 -4
- package/src/state/worker-atomic-writer.ts +6 -3
- package/src/subagents/index.ts +2 -2
- package/src/teams/discover-teams.ts +21 -6
- package/src/tools/safe-bash-extension.ts +5 -6
- package/src/tools/safe-bash.ts +10 -11
- package/src/types/new-api-types.ts +6 -10
- package/src/ui/agent-management-overlay.ts +52 -40
- package/src/ui/card-colors.ts +13 -4
- package/src/ui/crew-footer.ts +8 -7
- package/src/ui/crew-select-list.ts +1 -1
- package/src/ui/dashboard-panes/agents-pane.ts +41 -25
- package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
- package/src/ui/dashboard-panes/capability-pane.ts +32 -15
- package/src/ui/dashboard-panes/health-pane.ts +2 -1
- package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
- package/src/ui/dashboard-panes/progress-pane.ts +12 -9
- package/src/ui/heartbeat-aggregator.ts +15 -4
- package/src/ui/keybinding-map.ts +40 -7
- package/src/ui/live-conversation-overlay.ts +36 -20
- package/src/ui/live-duration.ts +1 -4
- package/src/ui/live-run-sidebar.ts +88 -25
- package/src/ui/loaders.ts +2 -8
- package/src/ui/mascot.ts +20 -36
- package/src/ui/overlays/agent-picker-overlay.ts +11 -3
- package/src/ui/overlays/confirm-overlay.ts +4 -2
- package/src/ui/overlays/help-overlay.ts +25 -14
- package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
- package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
- package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
- package/src/ui/pi-ui-compat.ts +7 -7
- package/src/ui/powerbar-publisher.ts +120 -42
- package/src/ui/render-diff.ts +10 -3
- package/src/ui/render-scheduler.ts +12 -4
- package/src/ui/run-action-dispatcher.ts +81 -18
- package/src/ui/run-dashboard.ts +172 -76
- package/src/ui/run-event-bus.ts +59 -37
- package/src/ui/run-snapshot-cache.ts +276 -86
- package/src/ui/settings-overlay.ts +361 -74
- package/src/ui/status-colors.ts +12 -1
- package/src/ui/syntax-highlight.ts +1 -1
- package/src/ui/theme-adapter.ts +16 -14
- package/src/ui/theme-discovery.ts +13 -2
- package/src/ui/tool-progress-formatter.ts +7 -7
- package/src/ui/tool-render.ts +128 -56
- package/src/ui/tool-renderers/brief-mode.ts +45 -27
- package/src/ui/tool-renderers/index.ts +109 -43
- package/src/ui/transcript-cache.ts +32 -6
- package/src/ui/transcript-entries.ts +25 -23
- package/src/ui/transcript-viewer.ts +78 -29
- package/src/ui/widget/index.ts +99 -40
- package/src/ui/widget/widget-formatters.ts +13 -6
- package/src/ui/widget/widget-model.ts +19 -8
- package/src/ui/widget/widget-renderer.ts +23 -13
- package/src/ui/widget/widget-types.ts +1 -1
- package/src/utils/bm25-search.ts +199 -199
- package/src/utils/conflict-detect.ts +22 -21
- package/src/utils/env-filter.ts +14 -7
- package/src/utils/file-coalescer.ts +5 -1
- package/src/utils/fingerprint.ts +3 -6
- package/src/utils/frontmatter.ts +3 -1
- package/src/utils/fs-watch.ts +2 -6
- package/src/utils/gh-protocol.ts +119 -44
- package/src/utils/git.ts +13 -15
- package/src/utils/guards.ts +2 -5
- package/src/utils/ids.ts +9 -2
- package/src/utils/incremental-reader.ts +14 -3
- package/src/utils/internal-error.ts +2 -1
- package/src/utils/names.ts +12 -3
- package/src/utils/paths.ts +49 -5
- package/src/utils/project-detector.ts +2 -2
- package/src/utils/redaction.ts +29 -19
- package/src/utils/resolve-shell.ts +9 -7
- package/src/utils/run-watcher-registry.ts +19 -31
- package/src/utils/safe-paths.ts +45 -24
- package/src/utils/scan-cache.ts +9 -2
- package/src/utils/session-utils.ts +2 -4
- package/src/utils/sleep.ts +12 -5
- package/src/utils/sse-parser.ts +5 -17
- package/src/utils/visual.ts +52 -32
- package/src/workflows/discover-workflows.ts +41 -109
- package/src/workflows/intermediate-store.ts +5 -21
- package/src/workflows/preflight-validator.ts +6 -33
- package/src/workflows/topology-analyzer.ts +5 -21
- package/src/workflows/workflow-config.ts +7 -6
- package/src/worktree/branch-freshness.ts +66 -8
- package/src/worktree/cleanup.ts +130 -20
- package/src/worktree/worktree-manager.ts +212 -69
- package/skills/artifact-analysis-loop/SKILL.md +0 -303
- package/skills/detection-pipeline-design/SKILL.md +0 -286
- package/skills/hunting-investigation-loop/SKILL.md +0 -402
- package/skills/incident-playbook-construction/SKILL.md +0 -384
- package/skills/security-review/SKILL.md +0 -561
- package/skills/threat-hypothesis-framework/SKILL.md +0 -176
- package/skills/ui-render-performance/SKILL.md +0 -58
- /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
- /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
- /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
- /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
- /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
- /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
- /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
- /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
- /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
- /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
- /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
- /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
- /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
- /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
package/src/runtime/pi-args.ts
CHANGED
|
@@ -4,10 +4,18 @@ import * as path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import type { AgentConfig } from "../agents/agent-config.ts";
|
|
6
6
|
import { resolveToolPolicy } from "../agents/agent-config.ts";
|
|
7
|
-
import { userPiRoot } from "../utils/paths.ts";
|
|
7
|
+
import { packageRoot, userPiRoot } from "../utils/paths.ts";
|
|
8
8
|
|
|
9
9
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
10
|
-
|
|
10
|
+
// FIX (2026-07-02): use packageRoot() instead of import.meta.url-relative path.
|
|
11
|
+
// The previous path.resolve(path.dirname(fileURLToPath(import.meta.url)),
|
|
12
|
+
// "..", "prompt", "prompt-runtime.ts") was correct in source but BROKEN
|
|
13
|
+
// in the bundle: esbuild's __esm helper doesn't preserve per-module
|
|
14
|
+
// import.meta.url, so the resolve lands at <pi-crew>/prompt/... (missing
|
|
15
|
+
// `src/`) instead of <pi-crew>/src/prompt/... packageRoot() walks up to
|
|
16
|
+
// find pi-crew's package.json and works correctly from both src/ and
|
|
17
|
+
// dist/ entry points.
|
|
18
|
+
const PROMPT_RUNTIME_EXTENSION_PATH = path.join(packageRoot(), "src", "prompt", "prompt-runtime.ts");
|
|
11
19
|
const TASK_ARG_LIMIT = 8000;
|
|
12
20
|
const DEFAULT_MAX_CREW_DEPTH = 2;
|
|
13
21
|
|
|
@@ -80,7 +88,10 @@ export function resolveCrewMaxDepth(inputMaxDepth?: number, env: NodeJS.ProcessE
|
|
|
80
88
|
return DEFAULT_MAX_CREW_DEPTH;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
|
-
export function checkCrewDepth(
|
|
91
|
+
export function checkCrewDepth(
|
|
92
|
+
inputMaxDepth?: number,
|
|
93
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
94
|
+
): { blocked: boolean; depth: number; maxDepth: number } {
|
|
84
95
|
const depth = currentCrewDepth(env);
|
|
85
96
|
const maxDepth = resolveCrewMaxDepth(inputMaxDepth, env);
|
|
86
97
|
return { depth, maxDepth, blocked: depth >= maxDepth };
|
|
@@ -218,7 +229,8 @@ export function createSafeTempDir(base: string, prefix: string): string {
|
|
|
218
229
|
resolvedAccumulated = path.join(resolvedAccumulated, resolvedParts[i]);
|
|
219
230
|
try {
|
|
220
231
|
const stat = fs.lstatSync(resolvedAccumulated);
|
|
221
|
-
if (stat.isSymbolicLink())
|
|
232
|
+
if (stat.isSymbolicLink())
|
|
233
|
+
throw new Error("Refusing to create temp dir: resolved path contains symlink ancestor: " + resolvedAccumulated);
|
|
222
234
|
} catch (e) {
|
|
223
235
|
if (e instanceof Error && e.message.includes("symlink")) throw e;
|
|
224
236
|
// Component doesn't exist — OK
|
|
@@ -334,9 +346,9 @@ export function buildPiWorkerArgs(input: BuildPiWorkerArgsInput): BuildPiWorkerA
|
|
|
334
346
|
args,
|
|
335
347
|
env: {
|
|
336
348
|
// PI_CREW_KIND is the authoritative machine-readable sub-agent marker. It is always
|
|
337
|
-
|
|
349
|
+
// present on a child-pi process and NEVER present on a user's interactive main session.
|
|
338
350
|
// doctor --zombies uses it to safely list orphaned sub-agents without ever matching a
|
|
339
|
-
|
|
351
|
+
// main session (the lesson from an accidental `kill` of a live main session).
|
|
340
352
|
PI_CREW_KIND: "subagent",
|
|
341
353
|
PI_CREW_INHERIT_PROJECT_CONTEXT: input.agent.inheritProjectContext ? "1" : "0",
|
|
342
354
|
PI_CREW_INHERIT_SKILLS: input.agent.inheritSkills ? "1" : "0",
|
|
@@ -385,7 +397,10 @@ export function cleanupTempDir(tempDir: string | undefined): void {
|
|
|
385
397
|
* /tmp/pi-crew-* dirs when individual cleanupTempDir calls are missed
|
|
386
398
|
* (e.g. parent process killed before child-pi.ts settles).
|
|
387
399
|
*/
|
|
388
|
-
export function cleanupAllTrackedTempDirs(): {
|
|
400
|
+
export function cleanupAllTrackedTempDirs(): {
|
|
401
|
+
cleaned: number;
|
|
402
|
+
failed: number;
|
|
403
|
+
} {
|
|
389
404
|
let cleaned = 0;
|
|
390
405
|
let failed = 0;
|
|
391
406
|
// Snapshot to avoid mutation during iteration
|
|
@@ -441,7 +456,10 @@ export function __test_getTrackedTempDirs(): readonly string[] {
|
|
|
441
456
|
* Called at startup as a belt-and-suspenders measure alongside the
|
|
442
457
|
* periodic cleanupOrphanTempDirs.
|
|
443
458
|
*/
|
|
444
|
-
export function purgeStaleTrackedTempDirs(): {
|
|
459
|
+
export function purgeStaleTrackedTempDirs(): {
|
|
460
|
+
removed: number;
|
|
461
|
+
remaining: number;
|
|
462
|
+
} {
|
|
445
463
|
let removed = 0;
|
|
446
464
|
for (const dir of [...createdTempDirs]) {
|
|
447
465
|
if (!fs.existsSync(dir)) {
|
|
@@ -21,7 +21,7 @@ export interface ParsedPiJsonOutput {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
24
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
|
|
24
|
+
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function numberField(obj: Record<string, unknown>, keys: string[]): number | undefined {
|
package/src/runtime/pi-spawn.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
1
2
|
import * as fs from "node:fs";
|
|
2
3
|
import * as os from "node:os";
|
|
3
|
-
import { execSync } from "node:child_process";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import * as path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
export interface PiSpawnCommand {
|
|
8
8
|
command: string;
|
|
9
9
|
args: string[];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const PI_PACKAGE_NAMES = [
|
|
13
|
-
"@earendil-works/pi-coding-agent",
|
|
14
|
-
"@mariozechner/pi-coding-agent",
|
|
15
|
-
];
|
|
12
|
+
const PI_PACKAGE_NAMES = ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
|
|
16
13
|
|
|
17
14
|
function isRunnableNodeScript(filePath: string): boolean {
|
|
18
15
|
return fs.existsSync(filePath) && /\.(?:mjs|cjs|js)$/i.test(filePath);
|
|
@@ -33,7 +30,9 @@ function isWithinAllowedPrefixes(resolvedPath: string): boolean {
|
|
|
33
30
|
const execDir = path.dirname(fs.realpathSync.native(process.execPath));
|
|
34
31
|
allowedPrefixes.push(execDir.toLowerCase());
|
|
35
32
|
allowedPrefixes.push(path.join(path.dirname(execDir), "lib", "node_modules").toLowerCase());
|
|
36
|
-
} catch {
|
|
33
|
+
} catch {
|
|
34
|
+
/* ignore */
|
|
35
|
+
}
|
|
37
36
|
|
|
38
37
|
// npm global bin via APPDATA
|
|
39
38
|
if (process.env.APPDATA) {
|
|
@@ -50,19 +49,25 @@ function isWithinAllowedPrefixes(resolvedPath: string): boolean {
|
|
|
50
49
|
try {
|
|
51
50
|
const projectBin = path.resolve("node_modules", ".bin");
|
|
52
51
|
allowedPrefixes.push(projectBin.toLowerCase());
|
|
53
|
-
} catch {
|
|
52
|
+
} catch {
|
|
53
|
+
/* ignore */
|
|
54
|
+
}
|
|
54
55
|
|
|
55
56
|
// User home npm-global
|
|
56
57
|
try {
|
|
57
58
|
const homeNpm = path.join(os.homedir(), ".npm-global", "bin");
|
|
58
59
|
allowedPrefixes.push(homeNpm.toLowerCase());
|
|
59
|
-
} catch {
|
|
60
|
+
} catch {
|
|
61
|
+
/* ignore */
|
|
62
|
+
}
|
|
60
63
|
|
|
61
64
|
// User home .local/bin
|
|
62
65
|
try {
|
|
63
66
|
const homeLocal = path.join(os.homedir(), ".local", "bin");
|
|
64
67
|
allowedPrefixes.push(homeLocal.toLowerCase());
|
|
65
|
-
} catch {
|
|
68
|
+
} catch {
|
|
69
|
+
/* ignore */
|
|
70
|
+
}
|
|
66
71
|
|
|
67
72
|
return allowedPrefixes.some((prefix) => normalized.startsWith(prefix));
|
|
68
73
|
}
|
|
@@ -89,8 +94,10 @@ function resolvePiPackageRoot(): string | undefined {
|
|
|
89
94
|
|
|
90
95
|
function packageBinScript(packageJsonPath: string): string | undefined {
|
|
91
96
|
try {
|
|
92
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
|
|
93
|
-
|
|
97
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
|
|
98
|
+
bin?: string | Record<string, string>;
|
|
99
|
+
};
|
|
100
|
+
const binPath = typeof pkg.bin === "string" ? pkg.bin : (pkg.bin?.pi ?? Object.values(pkg.bin ?? {})[0]);
|
|
94
101
|
if (!binPath) return undefined;
|
|
95
102
|
const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
|
|
96
103
|
return isRunnableNodeScript(candidate) ? candidate : undefined;
|
|
@@ -104,7 +111,9 @@ function findPiPackageJsonFrom(startDir: string): string | undefined {
|
|
|
104
111
|
while (dir !== path.dirname(dir)) {
|
|
105
112
|
const direct = path.join(dir, "package.json");
|
|
106
113
|
try {
|
|
107
|
-
const pkg = JSON.parse(fs.readFileSync(direct, "utf-8")) as {
|
|
114
|
+
const pkg = JSON.parse(fs.readFileSync(direct, "utf-8")) as {
|
|
115
|
+
name?: string;
|
|
116
|
+
};
|
|
108
117
|
if (pkg.name && PI_PACKAGE_NAMES.includes(pkg.name)) return direct;
|
|
109
118
|
} catch {
|
|
110
119
|
// Continue searching upward and in node_modules.
|
|
@@ -200,7 +209,7 @@ function resolvePiCliScript(): string | undefined {
|
|
|
200
209
|
].filter((entry): entry is string => Boolean(entry));
|
|
201
210
|
|
|
202
211
|
for (const root of roots) {
|
|
203
|
-
const packageJsonPath = root.endsWith("package.json") ? root : findPiPackageJsonFrom(root) ?? path.join(root, "package.json");
|
|
212
|
+
const packageJsonPath = root.endsWith("package.json") ? root : (findPiPackageJsonFrom(root) ?? path.join(root, "package.json"));
|
|
204
213
|
const script = packageBinScript(packageJsonPath);
|
|
205
214
|
if (script) return script;
|
|
206
215
|
}
|
|
@@ -214,16 +223,14 @@ function validateExplicitBin(explicit: string): string | undefined {
|
|
|
214
223
|
if (!isWithinAllowedPrefixes(resolved)) {
|
|
215
224
|
throw new Error(
|
|
216
225
|
`PI_TEAMS_PI_BIN path '${resolved}' is outside allowed prefixes. ` +
|
|
217
|
-
|
|
226
|
+
`Allowed: npm global bin, project node_modules/.bin, APPDATA/npm, or process execPath directory.`,
|
|
218
227
|
);
|
|
219
228
|
}
|
|
220
229
|
// Reject if symlink points outside expected directories
|
|
221
230
|
try {
|
|
222
231
|
const real = fs.realpathSync(resolved);
|
|
223
232
|
if (!isWithinAllowedPrefixes(real)) {
|
|
224
|
-
throw new Error(
|
|
225
|
-
`PI_TEAMS_PI_BIN symlink target '${real}' is outside allowed prefixes.`,
|
|
226
|
-
);
|
|
233
|
+
throw new Error(`PI_TEAMS_PI_BIN symlink target '${real}' is outside allowed prefixes.`);
|
|
227
234
|
}
|
|
228
235
|
} catch (e) {
|
|
229
236
|
if (e instanceof Error && e.message.includes("allowed prefixes")) throw e;
|
|
@@ -238,7 +245,11 @@ export function getPiSpawnCommand(args: string[]): PiSpawnCommand {
|
|
|
238
245
|
if (explicit) {
|
|
239
246
|
const validated = validateExplicitBin(explicit);
|
|
240
247
|
if (validated) {
|
|
241
|
-
if (isRunnableNodeScript(validated))
|
|
248
|
+
if (isRunnableNodeScript(validated))
|
|
249
|
+
return {
|
|
250
|
+
command: process.execPath,
|
|
251
|
+
args: [validated, ...args],
|
|
252
|
+
};
|
|
242
253
|
return { command: validated, args };
|
|
243
254
|
}
|
|
244
255
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { TeamTaskState } from "../state/types.ts";
|
|
2
|
-
import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
|
|
3
|
-
import type { TeamConfig } from "../teams/team-config.ts";
|
|
4
1
|
import type { AgentConfig } from "../agents/agent-config.ts";
|
|
5
|
-
import { appendEventAsync } from "../state/event-log.ts";
|
|
6
2
|
import { errors } from "../errors.ts";
|
|
3
|
+
import { appendEventAsync } from "../state/event-log.ts";
|
|
4
|
+
import type { TeamTaskState } from "../state/types.ts";
|
|
5
|
+
import type { TeamConfig } from "../teams/team-config.ts";
|
|
6
|
+
import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
|
|
7
7
|
import { mapConcurrent } from "./parallel-utils.ts";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -80,7 +80,10 @@ export class PipelineRunner {
|
|
|
80
80
|
private stopOnError: boolean;
|
|
81
81
|
private defaultMaxConcurrency: number;
|
|
82
82
|
|
|
83
|
-
constructor(options?: {
|
|
83
|
+
constructor(options?: {
|
|
84
|
+
stopOnError?: boolean;
|
|
85
|
+
defaultMaxConcurrency?: number;
|
|
86
|
+
}) {
|
|
84
87
|
this.stopOnError = options?.stopOnError ?? true;
|
|
85
88
|
this.defaultMaxConcurrency = options?.defaultMaxConcurrency ?? 5;
|
|
86
89
|
}
|
|
@@ -139,12 +142,7 @@ export class PipelineRunner {
|
|
|
139
142
|
const inputs = this.resolveInputs(stage.inputs, previousResults, context);
|
|
140
143
|
|
|
141
144
|
// Execute stage (handle fan-out if enabled)
|
|
142
|
-
const results = await this.executeStageInternal(
|
|
143
|
-
stage,
|
|
144
|
-
inputs,
|
|
145
|
-
stageContext,
|
|
146
|
-
executeStage,
|
|
147
|
-
);
|
|
145
|
+
const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
|
|
148
146
|
|
|
149
147
|
const duration = Date.now() - stageStartTime;
|
|
150
148
|
stages.push({
|
|
@@ -161,7 +159,12 @@ export class PipelineRunner {
|
|
|
161
159
|
type: "pipeline:stage_completed",
|
|
162
160
|
runId,
|
|
163
161
|
message: `Stage '${stage.name}' completed`,
|
|
164
|
-
data: {
|
|
162
|
+
data: {
|
|
163
|
+
stageIndex: i,
|
|
164
|
+
stageName: stage.name,
|
|
165
|
+
duration,
|
|
166
|
+
resultCount: results.length,
|
|
167
|
+
},
|
|
165
168
|
});
|
|
166
169
|
} catch (error) {
|
|
167
170
|
const duration = Date.now() - stageStartTime;
|
|
@@ -180,7 +183,12 @@ export class PipelineRunner {
|
|
|
180
183
|
type: "pipeline:stage_failed",
|
|
181
184
|
runId,
|
|
182
185
|
message: `Stage '${stage.name}' failed: ${errorMessage}`,
|
|
183
|
-
data: {
|
|
186
|
+
data: {
|
|
187
|
+
stageIndex: i,
|
|
188
|
+
stageName: stage.name,
|
|
189
|
+
duration,
|
|
190
|
+
error: errorMessage,
|
|
191
|
+
},
|
|
184
192
|
});
|
|
185
193
|
|
|
186
194
|
await appendEventAsync(eventsPath, {
|
|
@@ -209,7 +217,12 @@ export class PipelineRunner {
|
|
|
209
217
|
type: "pipeline:stage_skipped",
|
|
210
218
|
runId,
|
|
211
219
|
message: `Stage '${stage.name}' skipped due to error`,
|
|
212
|
-
data: {
|
|
220
|
+
data: {
|
|
221
|
+
stageIndex: i,
|
|
222
|
+
stageName: stage.name,
|
|
223
|
+
duration,
|
|
224
|
+
error: errorMessage,
|
|
225
|
+
},
|
|
213
226
|
});
|
|
214
227
|
}
|
|
215
228
|
}
|
|
@@ -219,7 +232,9 @@ export class PipelineRunner {
|
|
|
219
232
|
type: "pipeline:completed",
|
|
220
233
|
runId,
|
|
221
234
|
message: `Pipeline '${workflow.name}' completed`,
|
|
222
|
-
data: {
|
|
235
|
+
data: {
|
|
236
|
+
stages: stages.map((s) => ({ name: s.name, status: s.status })),
|
|
237
|
+
},
|
|
223
238
|
});
|
|
224
239
|
|
|
225
240
|
return {
|
|
@@ -266,18 +281,20 @@ export class PipelineRunner {
|
|
|
266
281
|
}));
|
|
267
282
|
|
|
268
283
|
// Execute with concurrency limit - pass each item to callback
|
|
269
|
-
const results = await mapConcurrent(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
284
|
+
const results = await mapConcurrent(tasks, maxConcurrency, async (task) => {
|
|
285
|
+
// Call the user-provided callback with each item
|
|
286
|
+
const result = await this.executeStageInternal(
|
|
287
|
+
stage,
|
|
288
|
+
task.item,
|
|
289
|
+
{
|
|
275
290
|
...stageContext,
|
|
276
291
|
stageName: task.name,
|
|
277
|
-
},
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
292
|
+
},
|
|
293
|
+
callback,
|
|
294
|
+
nextDepth,
|
|
295
|
+
);
|
|
296
|
+
return result;
|
|
297
|
+
});
|
|
281
298
|
|
|
282
299
|
return results;
|
|
283
300
|
}
|
|
@@ -294,14 +311,10 @@ export class PipelineRunner {
|
|
|
294
311
|
* - ${previous[0]} -> previousResults[0]
|
|
295
312
|
* - ${context.key} -> context.key
|
|
296
313
|
* - ${args.x} -> context.args.x
|
|
297
|
-
*
|
|
314
|
+
*
|
|
298
315
|
* C5: Validates template inputs to prevent injection.
|
|
299
316
|
*/
|
|
300
|
-
private resolveInputs(
|
|
301
|
-
inputs: unknown,
|
|
302
|
-
previousResults: unknown[],
|
|
303
|
-
context: Record<string, unknown>,
|
|
304
|
-
): unknown {
|
|
317
|
+
private resolveInputs(inputs: unknown, previousResults: unknown[], context: Record<string, unknown>): unknown {
|
|
305
318
|
// If inputs is an array, resolve each element
|
|
306
319
|
if (Array.isArray(inputs)) {
|
|
307
320
|
// H4: Type safety - limit array size to prevent memory issues
|
|
@@ -337,13 +350,13 @@ export class PipelineRunner {
|
|
|
337
350
|
private isValidObjectKey(key: string): boolean {
|
|
338
351
|
// Reject dangerous keys
|
|
339
352
|
const dangerousKeys = [
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
353
|
+
"__proto__",
|
|
354
|
+
"constructor",
|
|
355
|
+
"prototype",
|
|
356
|
+
"__defineGetter__",
|
|
357
|
+
"__defineSetter__",
|
|
358
|
+
"__lookupGetter__",
|
|
359
|
+
"__lookupSetter__",
|
|
347
360
|
];
|
|
348
361
|
if (dangerousKeys.includes(key)) {
|
|
349
362
|
return false;
|
|
@@ -373,11 +386,11 @@ export class PipelineRunner {
|
|
|
373
386
|
return false;
|
|
374
387
|
}
|
|
375
388
|
// Reject paths with empty segments
|
|
376
|
-
if (path.includes(
|
|
389
|
+
if (path.includes("..")) {
|
|
377
390
|
return false;
|
|
378
391
|
}
|
|
379
392
|
// Validate each path segment
|
|
380
|
-
const parts = path.split(
|
|
393
|
+
const parts = path.split(".");
|
|
381
394
|
for (const part of parts) {
|
|
382
395
|
if (!this.isValidObjectKey(part)) {
|
|
383
396
|
return false;
|
|
@@ -390,11 +403,7 @@ export class PipelineRunner {
|
|
|
390
403
|
* Resolve a single template string.
|
|
391
404
|
* C5: Validates template inputs to prevent injection.
|
|
392
405
|
*/
|
|
393
|
-
private resolveTemplate(
|
|
394
|
-
template: string,
|
|
395
|
-
previousResults: unknown[],
|
|
396
|
-
context: Record<string, unknown>,
|
|
397
|
-
): unknown {
|
|
406
|
+
private resolveTemplate(template: string, previousResults: unknown[], context: Record<string, unknown>): unknown {
|
|
398
407
|
// C5: Validate template length to prevent DoS
|
|
399
408
|
if (template.length > 10000) {
|
|
400
409
|
return template;
|
|
@@ -473,10 +482,7 @@ export class PipelineRunner {
|
|
|
473
482
|
* Parse a pipeline workflow from a workflow configuration.
|
|
474
483
|
* Converts standard WorkflowConfig to PipelineWorkflow.
|
|
475
484
|
*/
|
|
476
|
-
static fromWorkflowConfig(
|
|
477
|
-
workflow: WorkflowConfig,
|
|
478
|
-
goal: string,
|
|
479
|
-
): PipelineWorkflow {
|
|
485
|
+
static fromWorkflowConfig(workflow: WorkflowConfig, goal: string): PipelineWorkflow {
|
|
480
486
|
const stages: PipelineStage[] = workflow.steps.map((step) => ({
|
|
481
487
|
name: step.id,
|
|
482
488
|
team: step.role, // Using role as team identifier
|
|
@@ -498,12 +504,7 @@ export class PipelineRunner {
|
|
|
498
504
|
/**
|
|
499
505
|
* Create a pipeline workflow from a goal and stage definitions.
|
|
500
506
|
*/
|
|
501
|
-
export function createPipelineWorkflow(
|
|
502
|
-
name: string,
|
|
503
|
-
description: string,
|
|
504
|
-
goal: string,
|
|
505
|
-
stages: PipelineStage[],
|
|
506
|
-
): PipelineWorkflow {
|
|
507
|
+
export function createPipelineWorkflow(name: string, description: string, goal: string, stages: PipelineStage[]): PipelineWorkflow {
|
|
507
508
|
return {
|
|
508
509
|
name,
|
|
509
510
|
description,
|
|
@@ -89,10 +89,7 @@ export function listPlanTemplates(): string[] {
|
|
|
89
89
|
* @param variables - Key-value pairs for substitution
|
|
90
90
|
* @returns Rendered plan, or undefined if template not found
|
|
91
91
|
*/
|
|
92
|
-
export function renderPlanTemplate(
|
|
93
|
-
templateName: string,
|
|
94
|
-
variables: Record<string, string>,
|
|
95
|
-
): RenderedPlan | undefined {
|
|
92
|
+
export function renderPlanTemplate(templateName: string, variables: Record<string, string>): RenderedPlan | undefined {
|
|
96
93
|
const template = templates.get(templateName);
|
|
97
94
|
if (!template) {
|
|
98
95
|
logInternalError("plan-templates", new Error(`Template not found: ${templateName}`));
|
|
@@ -135,7 +132,8 @@ registerPlanTemplate({
|
|
|
135
132
|
{
|
|
136
133
|
name: "review",
|
|
137
134
|
role: "reviewer",
|
|
138
|
-
taskTemplate:
|
|
135
|
+
taskTemplate:
|
|
136
|
+
"Review the code identified in the explore phase for: {{goal}}. Check correctness, maintainability, and security.",
|
|
139
137
|
maxTasks: 1,
|
|
140
138
|
dependsOn: ["explore"],
|
|
141
139
|
},
|
|
@@ -160,7 +158,8 @@ registerPlanTemplate({
|
|
|
160
158
|
{
|
|
161
159
|
name: "explore",
|
|
162
160
|
role: "explorer",
|
|
163
|
-
taskTemplate:
|
|
161
|
+
taskTemplate:
|
|
162
|
+
"Explore the codebase to understand the current state relevant to: {{goal}}. Identify affected files and patterns.",
|
|
164
163
|
maxTasks: 1,
|
|
165
164
|
dependsOn: [],
|
|
166
165
|
},
|
|
@@ -36,34 +36,70 @@ export function evaluateCrewPolicy(input: PolicyEngineInput): PolicyDecision[] {
|
|
|
36
36
|
const decisions: PolicyDecision[] = [];
|
|
37
37
|
const maxTasksPerRun = Number.isFinite(input.limits?.maxTasksPerRun) ? input.limits!.maxTasksPerRun : undefined;
|
|
38
38
|
if (maxTasksPerRun !== undefined && input.tasks.length > maxTasksPerRun) {
|
|
39
|
-
decisions.push(
|
|
39
|
+
decisions.push(
|
|
40
|
+
decision("block", "limit_exceeded", `Run has ${input.tasks.length} tasks, exceeding maxTasksPerRun=${maxTasksPerRun}.`),
|
|
41
|
+
);
|
|
40
42
|
}
|
|
41
43
|
const runningCount = input.tasks.filter((task) => task.status === "running").length;
|
|
42
44
|
const maxConcurrentWorkers = Number.isFinite(input.limits?.maxConcurrentWorkers) ? input.limits!.maxConcurrentWorkers : undefined;
|
|
43
45
|
if (maxConcurrentWorkers !== undefined && runningCount > maxConcurrentWorkers) {
|
|
44
|
-
decisions.push(
|
|
46
|
+
decisions.push(
|
|
47
|
+
decision(
|
|
48
|
+
"block",
|
|
49
|
+
"limit_exceeded",
|
|
50
|
+
`Run has ${runningCount} running workers, exceeding maxConcurrentWorkers=${maxConcurrentWorkers}.`,
|
|
51
|
+
),
|
|
52
|
+
);
|
|
45
53
|
}
|
|
46
54
|
const tasksById = new Map(input.tasks.map((task) => [task.id, task]));
|
|
47
55
|
|
|
48
56
|
for (const task of input.tasks) {
|
|
49
57
|
if (input.limits?.maxChildrenPerTask !== undefined && (task.graph?.children.length ?? 0) > input.limits.maxChildrenPerTask) {
|
|
50
|
-
decisions.push(
|
|
58
|
+
decisions.push(
|
|
59
|
+
decision(
|
|
60
|
+
"block",
|
|
61
|
+
"limit_exceeded",
|
|
62
|
+
`Task has ${task.graph?.children.length ?? 0} children, exceeding maxChildrenPerTask=${input.limits.maxChildrenPerTask}.`,
|
|
63
|
+
task.id,
|
|
64
|
+
),
|
|
65
|
+
);
|
|
51
66
|
}
|
|
52
67
|
if (input.limits?.maxTaskDepth !== undefined && taskDepth(task, tasksById) > input.limits.maxTaskDepth) {
|
|
53
|
-
decisions.push(
|
|
68
|
+
decisions.push(
|
|
69
|
+
decision("block", "limit_exceeded", `Task graph depth exceeds maxTaskDepth=${input.limits.maxTaskDepth}.`, task.id),
|
|
70
|
+
);
|
|
54
71
|
}
|
|
55
72
|
if (task.status === "failed") {
|
|
56
73
|
const retryCount = task.policy?.retryCount ?? 0;
|
|
57
74
|
const maxRetries = input.limits?.maxRetriesPerTask ?? 0;
|
|
58
|
-
decisions.push(
|
|
75
|
+
decisions.push(
|
|
76
|
+
decision(
|
|
77
|
+
retryCount < maxRetries ? "retry" : "escalate",
|
|
78
|
+
"task_failed",
|
|
79
|
+
task.error ? `Task failed: ${task.error}` : "Task failed.",
|
|
80
|
+
task.id,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
59
83
|
}
|
|
60
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
(task.status === "running" || task.status === "queued") &&
|
|
86
|
+
task.heartbeat &&
|
|
87
|
+
task.heartbeat.alive !== false &&
|
|
88
|
+
isWorkerHeartbeatStale(task.heartbeat, input.limits?.heartbeatStaleMs ?? 60_000, input.now)
|
|
89
|
+
) {
|
|
61
90
|
decisions.push(decision("escalate", "worker_stale", "Worker heartbeat is stale.", task.id));
|
|
62
91
|
}
|
|
63
92
|
if (task.taskPacket?.verification) {
|
|
64
93
|
const outcome = evaluateGreenContract(task.taskPacket.verification, task.verification);
|
|
65
94
|
if (!outcome.satisfied && task.status === "completed") {
|
|
66
|
-
decisions.push(
|
|
95
|
+
decisions.push(
|
|
96
|
+
decision(
|
|
97
|
+
"block",
|
|
98
|
+
"green_unsatisfied",
|
|
99
|
+
`Green contract unsatisfied: required=${outcome.requiredGreenLevel}, observed=${outcome.observedGreenLevel}.`,
|
|
100
|
+
task.id,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
67
103
|
}
|
|
68
104
|
}
|
|
69
105
|
}
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import { execFileSync } from "node:child_process";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
|
|
10
|
-
import { resolveShellForScript } from "../utils/resolve-shell.ts";
|
|
11
10
|
import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
|
|
11
|
+
import { resolveShellForScript } from "../utils/resolve-shell.ts";
|
|
12
12
|
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
13
13
|
|
|
14
14
|
/** Default timeout for post-check scripts (5 minutes). */
|
|
@@ -92,12 +92,17 @@ export async function runPostCheck(config: PostCheckConfig, cwd: string): Promis
|
|
|
92
92
|
return new Promise<PostCheckResult>((resolve) => {
|
|
93
93
|
try {
|
|
94
94
|
const { command, args } = resolveShellForScript(scriptPath);
|
|
95
|
-
|
|
95
|
+
const output = execFileSync(command, args, {
|
|
96
96
|
cwd,
|
|
97
97
|
timeout: timeoutMs,
|
|
98
98
|
encoding: "utf-8",
|
|
99
99
|
maxBuffer: 10 * 1024 * 1024, // 10 MB
|
|
100
|
-
env: {
|
|
100
|
+
env: {
|
|
101
|
+
...sanitizeEnvSecrets(process.env, {
|
|
102
|
+
allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"],
|
|
103
|
+
}),
|
|
104
|
+
PI_CREW_POST_CHECK: "1",
|
|
105
|
+
},
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
const durationMs = Date.now() - startTime;
|
|
@@ -118,7 +123,10 @@ export async function runPostCheck(config: PostCheckConfig, cwd: string): Promis
|
|
|
118
123
|
|
|
119
124
|
let output = "";
|
|
120
125
|
if (error instanceof Error) {
|
|
121
|
-
const execError = error as NodeJS.ErrnoException & {
|
|
126
|
+
const execError = error as NodeJS.ErrnoException & {
|
|
127
|
+
stdout?: string;
|
|
128
|
+
stderr?: string;
|
|
129
|
+
};
|
|
122
130
|
output = [execError.stdout ?? "", execError.stderr ?? "", execError.message ?? ""].join("\n").trim();
|
|
123
131
|
} else {
|
|
124
132
|
output = String(error);
|
|
@@ -35,12 +35,12 @@ export function attachPostExitStdioGuard(child: ChildWithPipedStdio, options: Po
|
|
|
35
35
|
if (!stdoutEnded) {
|
|
36
36
|
try {
|
|
37
37
|
child.stdout?.destroy();
|
|
38
|
-
|
|
38
|
+
} catch {}
|
|
39
39
|
}
|
|
40
40
|
if (!stderrEnded) {
|
|
41
41
|
try {
|
|
42
42
|
child.stderr?.destroy();
|
|
43
|
-
|
|
43
|
+
} catch {}
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
46
|
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* `taskkill /F /T /PID` escalation directly (force-kill the whole tree).
|
|
30
30
|
* See `.crew/knowledge.md` gotchas: BSD/Windows signal handling differs.
|
|
31
31
|
*/
|
|
32
|
-
import {
|
|
32
|
+
import { type ChildProcess, type SpawnOptions, spawn } from "node:child_process";
|
|
33
33
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
34
34
|
|
|
35
35
|
// ── tunables ──────────────────────────────────────────────────────────────────
|
|
@@ -125,7 +125,10 @@ export class OwnedProcess {
|
|
|
125
125
|
private deregistered = false;
|
|
126
126
|
/** Terminal once teardown/reconciliation has confirmed the group is gone. */
|
|
127
127
|
private terminated = false;
|
|
128
|
-
private exitPromise: Promise<{
|
|
128
|
+
private exitPromise: Promise<{
|
|
129
|
+
code: number | null;
|
|
130
|
+
signal: NodeJS.Signals | null;
|
|
131
|
+
}>;
|
|
129
132
|
private exitCallbacks = new Set<OwnedExitCallback>();
|
|
130
133
|
private onAbort: (() => void) | undefined;
|
|
131
134
|
private readonly abortSignal: AbortSignal | undefined;
|
|
@@ -187,7 +190,9 @@ export class OwnedProcess {
|
|
|
187
190
|
void this.dispose();
|
|
188
191
|
} else {
|
|
189
192
|
this.onAbort = () => void this.dispose();
|
|
190
|
-
this.abortSignal.addEventListener("abort", this.onAbort, {
|
|
193
|
+
this.abortSignal.addEventListener("abort", this.onAbort, {
|
|
194
|
+
once: true,
|
|
195
|
+
});
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
}
|
|
@@ -230,14 +235,14 @@ export class OwnedProcess {
|
|
|
230
235
|
* With no timeout it resolves only when the child exits. Never rejects.
|
|
231
236
|
*/
|
|
232
237
|
async awaitExit(opts?: { timeoutMs?: number }): Promise<AwaitExitResult> {
|
|
233
|
-
const exitResult = this.exitPromise.then((info) => ({
|
|
238
|
+
const exitResult = this.exitPromise.then((info) => ({
|
|
239
|
+
exited: true as const,
|
|
240
|
+
code: info.code,
|
|
241
|
+
}));
|
|
234
242
|
if (opts?.timeoutMs === undefined) return exitResult;
|
|
235
243
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
236
244
|
const timeout = new Promise<AwaitExitResult>((resolve) => {
|
|
237
|
-
timer = setTimeout(
|
|
238
|
-
() => resolve({ exited: false, code: this.child.exitCode }),
|
|
239
|
-
Math.max(0, opts.timeoutMs!),
|
|
240
|
-
);
|
|
245
|
+
timer = setTimeout(() => resolve({ exited: false, code: this.child.exitCode }), Math.max(0, opts.timeoutMs!));
|
|
241
246
|
});
|
|
242
247
|
try {
|
|
243
248
|
return await Promise.race([exitResult, timeout]);
|
|
@@ -311,7 +316,9 @@ export class OwnedProcess {
|
|
|
311
316
|
if (await pollUntil(() => !groupAlive(this.pgid!), this.gracefulMs)) return;
|
|
312
317
|
this.signalTree("SIGKILL");
|
|
313
318
|
if (!(await pollUntil(() => !groupAlive(this.pgid!), SIGKILL_REAP_CAP_MS))) {
|
|
314
|
-
console.warn(
|
|
319
|
+
console.warn(
|
|
320
|
+
`[pi-crew] owned process group still alive after SIGKILL (name=${this.name ?? "?"}, pgid=${this.pgid})`,
|
|
321
|
+
);
|
|
315
322
|
}
|
|
316
323
|
return;
|
|
317
324
|
}
|
|
@@ -341,7 +348,10 @@ export class OwnedProcess {
|
|
|
341
348
|
const tryTaskkill = (force: boolean): Promise<void> =>
|
|
342
349
|
new Promise((resolve) => {
|
|
343
350
|
const args = ["/T", "/PID", String(pid), ...(force ? ["/F"] : [])];
|
|
344
|
-
const tk = spawn("taskkill", args, {
|
|
351
|
+
const tk = spawn("taskkill", args, {
|
|
352
|
+
stdio: "ignore",
|
|
353
|
+
windowsHide: true,
|
|
354
|
+
});
|
|
345
355
|
tk.on("error", () => resolve());
|
|
346
356
|
tk.on("exit", () => resolve());
|
|
347
357
|
});
|