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
|
@@ -3,26 +3,16 @@ import * as path from "node:path";
|
|
|
3
3
|
import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
4
4
|
import { loadConfig } from "../config/config.ts";
|
|
5
5
|
import { appendEvent } from "../state/event-log.ts";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
} from "../state/
|
|
9
|
-
import {
|
|
10
|
-
createRunPaths,
|
|
11
|
-
loadRunManifestById,
|
|
12
|
-
saveRunManifest,
|
|
13
|
-
updateRunStatus,
|
|
14
|
-
} from "../state/state-store.ts";
|
|
6
|
+
import { withRunLockSync } from "../state/locks.ts";
|
|
7
|
+
import { createRunPaths, loadRunManifestById, saveRunManifest, updateRunStatus } from "../state/state-store.ts";
|
|
8
|
+
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
15
9
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
16
10
|
import { projectCrewRoot } from "../utils/paths.ts";
|
|
17
|
-
import {
|
|
18
|
-
allWorkflows,
|
|
19
|
-
discoverWorkflows,
|
|
20
|
-
} from "../workflows/discover-workflows.ts";
|
|
11
|
+
import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
|
|
21
12
|
// Heavy runtime — lazy-loaded to avoid pulling team-runner into background-runner
|
|
22
13
|
// at module load time. Only needed when a background run actually starts.
|
|
23
14
|
import { primePeerDep } from "./peer-dep.ts";
|
|
24
15
|
import type { executeTeamRun as ExecuteTeamRunFn } from "./team-runner.ts";
|
|
25
|
-
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
26
16
|
|
|
27
17
|
let _cachedExecuteTeamRun: typeof ExecuteTeamRunFn | undefined;
|
|
28
18
|
|
|
@@ -38,9 +28,7 @@ const MAX_BACKGROUND_RUN_MS = (() => {
|
|
|
38
28
|
const env = Number.parseInt(process.env.PI_CREW_MAX_RUN_MS ?? "", 10);
|
|
39
29
|
return Number.isFinite(env) && env > 0 ? env : 2 * 60 * 60 * 1000;
|
|
40
30
|
})();
|
|
41
|
-
async function executeTeamRun(
|
|
42
|
-
...args: Parameters<typeof ExecuteTeamRunFn>
|
|
43
|
-
): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
|
|
31
|
+
async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
|
|
44
32
|
if (!_cachedExecuteTeamRun) {
|
|
45
33
|
// FIX (split-scope install): prime the ESM peer dep BEFORE team-runner is
|
|
46
34
|
// imported, so its transitive skill-instructions.ts can read getAgentDir()
|
|
@@ -56,14 +44,11 @@ async function executeTeamRun(
|
|
|
56
44
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
57
45
|
import { writeAsyncStartMarker } from "./async-marker.ts";
|
|
58
46
|
import { terminateActiveChildPiProcesses } from "./child-pi.ts";
|
|
59
|
-
import { unregisterWorker } from "./orphan-worker-registry.ts";
|
|
60
47
|
import { directTeamAndWorkflowFromRun } from "./direct-run.ts";
|
|
48
|
+
import { unregisterWorker } from "./orphan-worker-registry.ts";
|
|
61
49
|
import { expandParallelResearchWorkflow } from "./parallel-research.ts";
|
|
62
50
|
import { startParentGuard, stopParentGuard } from "./parent-guard.ts";
|
|
63
|
-
import {
|
|
64
|
-
resolveCrewRuntime,
|
|
65
|
-
runtimeResolutionState,
|
|
66
|
-
} from "./runtime-resolver.ts";
|
|
51
|
+
import { resolveCrewRuntime, runtimeResolutionState } from "./runtime-resolver.ts";
|
|
67
52
|
|
|
68
53
|
/**
|
|
69
54
|
* Debug logger gated behind PI_CREW_DEBUG env var. Writes to background.log
|
|
@@ -80,11 +65,7 @@ function debugLog(message: string): void {
|
|
|
80
65
|
* Without this, the reconciler relies solely on process.kill(pid, 0) which can
|
|
81
66
|
* false-positive when a process is SIGKILLed and the PID hasn't been recycled yet.
|
|
82
67
|
*/
|
|
83
|
-
function startHeartbeat(
|
|
84
|
-
stateRoot: string,
|
|
85
|
-
eventsPath: string,
|
|
86
|
-
runId: string,
|
|
87
|
-
): () => void {
|
|
68
|
+
function startHeartbeat(stateRoot: string, eventsPath: string, runId: string): () => void {
|
|
88
69
|
const heartbeatPath = path.join(stateRoot, "heartbeat.json");
|
|
89
70
|
const writeHeartbeat = (): void => {
|
|
90
71
|
try {
|
|
@@ -133,36 +114,29 @@ function startInterruptGuard(
|
|
|
133
114
|
abortController: AbortController,
|
|
134
115
|
stopParentGuard: () => void,
|
|
135
116
|
): () => void {
|
|
136
|
-
const controlPath = path.join(
|
|
137
|
-
manifest.stateRoot,
|
|
138
|
-
"foreground-control.json",
|
|
139
|
-
);
|
|
117
|
+
const controlPath = path.join(manifest.stateRoot, "foreground-control.json");
|
|
140
118
|
// FIX: Made configurable via PI_CREW_INTERRUPT_GUARD_INTERVAL_MS env var.
|
|
141
119
|
// Default 250ms balances fast SIGINT response against filesystem overhead.
|
|
142
|
-
const interruptGuardInterval =
|
|
143
|
-
Number(process.env.PI_CREW_INTERRUPT_GUARD_INTERVAL_MS) || 250;
|
|
120
|
+
const interruptGuardInterval = Number(process.env.PI_CREW_INTERRUPT_GUARD_INTERVAL_MS) || 250;
|
|
144
121
|
const interval = setInterval(() => {
|
|
145
122
|
try {
|
|
146
123
|
if (!fs.existsSync(controlPath)) return;
|
|
147
|
-
const parsed = JSON.parse(
|
|
148
|
-
|
|
149
|
-
|
|
124
|
+
const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as {
|
|
125
|
+
requests?: Array<{ type: string; acknowledged?: boolean }>;
|
|
126
|
+
};
|
|
150
127
|
const last = parsed.requests?.at(-1);
|
|
151
128
|
if (last?.type === "interrupt" && last?.acknowledged !== true) {
|
|
152
129
|
appendEvent(manifest.eventsPath, {
|
|
153
130
|
type: "async.interrupt_detected",
|
|
154
131
|
runId: manifest.runId,
|
|
155
|
-
message:
|
|
156
|
-
"Background runner detected foreground interrupt — killing child processes and exiting.",
|
|
132
|
+
message: "Background runner detected foreground interrupt — killing child processes and exiting.",
|
|
157
133
|
});
|
|
158
134
|
// FIX: Terminate ALL child-pi processes IMMEDIATELY before exiting.
|
|
159
135
|
// Previously this was missing, causing orphaned child processes to run forever
|
|
160
136
|
// after the background-runner exited. terminateActiveChildPiProcesses sends
|
|
161
137
|
// SIGTERM then SIGKILL (after HARD_KILL_MS=3s) to every active child.
|
|
162
138
|
const killed = terminateActiveChildPiProcesses();
|
|
163
|
-
console.log(
|
|
164
|
-
`[background-runner] interrupt: killed ${killed} child processes`,
|
|
165
|
-
);
|
|
139
|
+
console.log(`[background-runner] interrupt: killed ${killed} child processes`);
|
|
166
140
|
// Also abort the run signal so executeTeamRun exits quickly via its signal check.
|
|
167
141
|
abortController.abort();
|
|
168
142
|
// FIX Issue #1: Call stopParentGuard() here since process.exit(130) bypasses
|
|
@@ -201,13 +175,9 @@ function setupUnhandledRejectionGuard(
|
|
|
201
175
|
setExitFlag: () => void,
|
|
202
176
|
): void {
|
|
203
177
|
process.on("unhandledRejection", (reason, promise) => {
|
|
204
|
-
const message =
|
|
205
|
-
reason instanceof Error ? reason.message : String(reason);
|
|
178
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
206
179
|
console.error("[background-runner] UNHANDLED REJECTION:", reason);
|
|
207
|
-
console.error(
|
|
208
|
-
"[background-runner] Stack:",
|
|
209
|
-
reason instanceof Error ? reason.stack : "N/A",
|
|
210
|
-
);
|
|
180
|
+
console.error("[background-runner] Stack:", reason instanceof Error ? reason.stack : "N/A");
|
|
211
181
|
try {
|
|
212
182
|
if (state.eventsPath && state.runId) {
|
|
213
183
|
appendEvent(state.eventsPath, {
|
|
@@ -216,17 +186,13 @@ function setupUnhandledRejectionGuard(
|
|
|
216
186
|
message: `Unhandled rejection: ${message}`,
|
|
217
187
|
data: {
|
|
218
188
|
reason: String(reason),
|
|
219
|
-
stack:
|
|
220
|
-
reason instanceof Error ? reason.stack : undefined,
|
|
189
|
+
stack: reason instanceof Error ? reason.stack : undefined,
|
|
221
190
|
handled: false,
|
|
222
191
|
},
|
|
223
192
|
});
|
|
224
193
|
}
|
|
225
194
|
} catch (appendErr) {
|
|
226
|
-
console.error(
|
|
227
|
-
"[background-runner] Failed to write async.failed event:",
|
|
228
|
-
appendErr,
|
|
229
|
-
);
|
|
195
|
+
console.error("[background-runner] Failed to write async.failed event:", appendErr);
|
|
230
196
|
}
|
|
231
197
|
// FIX Issues #2& #4: Signal child processes to terminate via abortController,
|
|
232
198
|
// set the exit flag so main() exits after the finally block runs cleanup.
|
|
@@ -251,8 +217,7 @@ function runCleanup(
|
|
|
251
217
|
exitDueToRejection: boolean,
|
|
252
218
|
eventsPath?: string,
|
|
253
219
|
): void {
|
|
254
|
-
console.log(`[background-runner] runCleanup, exitDueToRejection=${exitDueToRejection}
|
|
255
|
-
);
|
|
220
|
+
console.log(`[background-runner] runCleanup, exitDueToRejection=${exitDueToRejection}`);
|
|
256
221
|
stopInterruptGuard();
|
|
257
222
|
stopParentGuard();
|
|
258
223
|
stopHeartbeat();
|
|
@@ -280,9 +245,7 @@ function runCleanup(
|
|
|
280
245
|
try {
|
|
281
246
|
unregisterWorker(process.pid);
|
|
282
247
|
} catch (error) {
|
|
283
|
-
console.log(
|
|
284
|
-
`[background-runner] runCleanup: unregisterWorker error: ${error instanceof Error ? error.message : String(error)}`,
|
|
285
|
-
);
|
|
248
|
+
console.log(`[background-runner] runCleanup: unregisterWorker error: ${error instanceof Error ? error.message : String(error)}`);
|
|
286
249
|
if (eventsPath) {
|
|
287
250
|
try {
|
|
288
251
|
appendEvent(eventsPath, {
|
|
@@ -330,13 +293,7 @@ async function main(): Promise<void> {
|
|
|
330
293
|
// Use projectCrewRoot() so the background log lives next to the
|
|
331
294
|
// manifest in either .crew/state/runs/ or .pi/teams/state/runs/
|
|
332
295
|
// depending on the project's chosen layout (issue #29).
|
|
333
|
-
const logPath = path.join(
|
|
334
|
-
projectCrewRoot(_cwd),
|
|
335
|
-
"state",
|
|
336
|
-
"runs",
|
|
337
|
-
_runId,
|
|
338
|
-
"background.log",
|
|
339
|
-
);
|
|
296
|
+
const logPath = path.join(projectCrewRoot(_cwd), "state", "runs", _runId, "background.log");
|
|
340
297
|
logFd = fs.openSync(logPath, "a");
|
|
341
298
|
const origWrite =
|
|
342
299
|
(_prefix: string) =>
|
|
@@ -398,23 +355,14 @@ async function main(): Promise<void> {
|
|
|
398
355
|
const runId = argValue("--run-id");
|
|
399
356
|
if (!cwd || !runId) return undefined;
|
|
400
357
|
// Use projectCrewRoot() to honour the .pi/teams/ fallback (issue #29).
|
|
401
|
-
return path.join(
|
|
402
|
-
projectCrewRoot(cwd),
|
|
403
|
-
"state",
|
|
404
|
-
"runs",
|
|
405
|
-
runId,
|
|
406
|
-
"exit-code.txt",
|
|
407
|
-
);
|
|
358
|
+
return path.join(projectCrewRoot(cwd), "state", "runs", runId, "exit-code.txt");
|
|
408
359
|
})();
|
|
409
360
|
if (exitCodePath) {
|
|
410
361
|
process.on("exit", (code) => {
|
|
411
362
|
// Only log non-zero exit codes to avoid noise in exit-code.txt
|
|
412
363
|
if (code === 0 || code === undefined) return;
|
|
413
364
|
try {
|
|
414
|
-
fs.appendFileSync(
|
|
415
|
-
exitCodePath,
|
|
416
|
-
`${new Date().toISOString()} exit_code=${code} pid=${process.pid}\n`,
|
|
417
|
-
);
|
|
365
|
+
fs.appendFileSync(exitCodePath, `${new Date().toISOString()} exit_code=${code} pid=${process.pid}\n`);
|
|
418
366
|
} catch {}
|
|
419
367
|
});
|
|
420
368
|
}
|
|
@@ -423,10 +371,7 @@ async function main(): Promise<void> {
|
|
|
423
371
|
// are installed, since the handlers reference manifest.eventsPath and abortController.
|
|
424
372
|
const cwd = argValue("--cwd");
|
|
425
373
|
const runId = argValue("--run-id");
|
|
426
|
-
if (!cwd || !runId)
|
|
427
|
-
throw new Error(
|
|
428
|
-
"Usage: background-runner.ts --cwd <cwd> --run-id <runId>",
|
|
429
|
-
);
|
|
374
|
+
if (!cwd || !runId) throw new Error("Usage: background-runner.ts --cwd <cwd> --run-id <runId>");
|
|
430
375
|
// FIX Issue #3: Wrap in withRunLockSync to prevent concurrent background-runners
|
|
431
376
|
// for the same runId from reading stale manifest state. If lock cannot be
|
|
432
377
|
// be acquired within 5s, fail immediately rather than proceeding with stale data.
|
|
@@ -442,11 +387,9 @@ async function main(): Promise<void> {
|
|
|
442
387
|
const bootstrapStateRoot = createRunPaths(cwd, runId).stateRoot;
|
|
443
388
|
let loaded: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
|
|
444
389
|
try {
|
|
445
|
-
loaded = withRunLockSync(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
{ staleMs: 30_000 },
|
|
449
|
-
);
|
|
390
|
+
loaded = withRunLockSync({ stateRoot: bootstrapStateRoot, runId, cwd } as TeamRunManifest, () => loadRunManifestById(cwd, runId), {
|
|
391
|
+
staleMs: 30_000,
|
|
392
|
+
});
|
|
450
393
|
} catch (lockErr) {
|
|
451
394
|
throw new Error(`Failed to acquire lock for run '${runId}': ${lockErr instanceof Error ? lockErr.message : String(lockErr)}`);
|
|
452
395
|
}
|
|
@@ -560,17 +503,12 @@ async function main(): Promise<void> {
|
|
|
560
503
|
runId: manifest.runId,
|
|
561
504
|
data: { pid: process.pid },
|
|
562
505
|
});
|
|
563
|
-
debugLog(`[background-runner] async.started written, pid=${process.pid}
|
|
564
|
-
);
|
|
506
|
+
debugLog(`[background-runner] async.started written, pid=${process.pid}`);
|
|
565
507
|
writeAsyncStartMarker(manifest, {
|
|
566
508
|
pid: process.pid,
|
|
567
509
|
startedAt: new Date().toISOString(),
|
|
568
510
|
});
|
|
569
|
-
const stopHeartbeat = startHeartbeat(
|
|
570
|
-
manifest.stateRoot,
|
|
571
|
-
manifest.eventsPath,
|
|
572
|
-
manifest.runId,
|
|
573
|
-
);
|
|
511
|
+
const stopHeartbeat = startHeartbeat(manifest.stateRoot, manifest.eventsPath, manifest.runId);
|
|
574
512
|
const stopInterruptGuard = startInterruptGuard(manifest, abortController, stopParentGuard);
|
|
575
513
|
debugLog(`[background-runner] heartbeat+interrupt guard started`);
|
|
576
514
|
// NOTE: Keep-alive interval is NOT unref'd (unlike heartbeat and interrupt
|
|
@@ -594,7 +532,9 @@ async function main(): Promise<void> {
|
|
|
594
532
|
message: `Run exceeded ${MAX_BACKGROUND_RUN_MS}ms and was force-aborted to prevent a zombie background-runner process.`,
|
|
595
533
|
data: { maxRunMs: MAX_BACKGROUND_RUN_MS },
|
|
596
534
|
});
|
|
597
|
-
} catch {
|
|
535
|
+
} catch {
|
|
536
|
+
/* best-effort event log */
|
|
537
|
+
}
|
|
598
538
|
// Signal the finally block to exit(1) after cleanup.
|
|
599
539
|
exitDueToRejection = true;
|
|
600
540
|
// Abort the in-flight team run via the shared signal (propagates to
|
|
@@ -605,7 +545,11 @@ async function main(): Promise<void> {
|
|
|
605
545
|
const forceExit = setTimeout(() => {
|
|
606
546
|
console.error(`[background-runner] WATCHDOG: abort did not propagate within grace period — force-exiting`);
|
|
607
547
|
stopParentGuard();
|
|
608
|
-
try {
|
|
548
|
+
try {
|
|
549
|
+
terminateActiveChildPiProcesses();
|
|
550
|
+
} catch {
|
|
551
|
+
/* best-effort */
|
|
552
|
+
}
|
|
609
553
|
process.exit(1);
|
|
610
554
|
}, 15_000);
|
|
611
555
|
forceExit.unref();
|
|
@@ -614,15 +558,24 @@ async function main(): Promise<void> {
|
|
|
614
558
|
try {
|
|
615
559
|
debugLog(`[background-runner] about to call discoverAgents`);
|
|
616
560
|
const agents = allAgents(discoverAgents(cwd));
|
|
617
|
-
debugLog(`[background-runner] discoverAgents done, ${agents.length} agents
|
|
618
|
-
);
|
|
561
|
+
debugLog(`[background-runner] discoverAgents done, ${agents.length} agents`);
|
|
619
562
|
// Round 27 (BUG 2): openSync returned an fd that was never closed → FD
|
|
620
563
|
// leak per background runner startup. Close it in a finally (matches the
|
|
621
564
|
// canonical pattern in checkpoint.ts:83 and event-log.ts:582).
|
|
622
565
|
try {
|
|
623
566
|
const fd = fs.openSync(manifest.eventsPath, "a");
|
|
624
|
-
try {
|
|
625
|
-
|
|
567
|
+
try {
|
|
568
|
+
fs.fsyncSync(fd);
|
|
569
|
+
} finally {
|
|
570
|
+
try {
|
|
571
|
+
fs.closeSync(fd);
|
|
572
|
+
} catch {
|
|
573
|
+
/* best-effort */
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
} catch {
|
|
577
|
+
/* best-effort */
|
|
578
|
+
} // FORCE flush so we see this before death
|
|
626
579
|
// Fix round-4 CRITICAL: goal-loop and dynamic-workflow manifests use SYNTHETIC
|
|
627
580
|
// team/workflow names not in discoverTeams/discoverWorkflows. The team+workflow
|
|
628
581
|
// lookup below would throw "Team not found" BEFORE the runKind switch, making the
|
|
@@ -631,37 +584,64 @@ async function main(): Promise<void> {
|
|
|
631
584
|
let earlyResult: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
|
|
632
585
|
let result: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
|
|
633
586
|
if (manifest.runKind === "goal-loop" || manifest.runKind === "dynamic-workflow") {
|
|
634
|
-
debugLog(`[background-runner] short-circuiting ${manifest.runKind} (synthetic team/workflow)
|
|
635
|
-
);
|
|
587
|
+
debugLog(`[background-runner] short-circuiting ${manifest.runKind} (synthetic team/workflow)`);
|
|
636
588
|
if (manifest.runKind === "goal-loop") {
|
|
637
|
-
|
|
589
|
+
// LAZY: defer dynamic import of ./goal-loop-runner.ts to its call site.
|
|
638
590
|
const { runGoalLoop } = await import("./goal-loop-runner.ts");
|
|
639
|
-
|
|
591
|
+
// LAZY: defer dynamic import of ./goal-state-store.ts to its call site.
|
|
640
592
|
const { GoalStore } = await import("./goal-state-store.ts");
|
|
641
|
-
|
|
593
|
+
// LAZY: defer dynamic import of ../agents/discover-agents.ts to its call site.
|
|
642
594
|
const { discoverAgents, allAgents } = await import("../agents/discover-agents.ts");
|
|
643
595
|
const store = new GoalStore(manifest.cwd);
|
|
644
596
|
const goalState = store.load(manifest.runId);
|
|
645
|
-
if (!goalState)
|
|
646
|
-
|
|
597
|
+
if (!goalState)
|
|
598
|
+
throw new Error(`runKind="goal-loop" but GoalLoopState '${manifest.runId}' not found (cwd=${manifest.cwd})`);
|
|
599
|
+
const goalResult = await runGoalLoop({
|
|
600
|
+
goalState,
|
|
601
|
+
manifest,
|
|
602
|
+
signal: abortController.signal,
|
|
603
|
+
deps: {
|
|
604
|
+
discoverAgents: (c: string) => allAgents(discoverAgents(c)),
|
|
605
|
+
},
|
|
606
|
+
});
|
|
647
607
|
// Fix P1-1 + round-6 #5: persist terminal status reflecting the goal's actual outcome,
|
|
648
608
|
// not a blanket 'completed'. Map goal state → manifest status.
|
|
649
609
|
const goalStatusToRunStatus: Record<string, TeamRunManifest["status"]> = {
|
|
650
|
-
achieved: "completed",
|
|
651
|
-
|
|
610
|
+
achieved: "completed",
|
|
611
|
+
max_turns: "completed",
|
|
612
|
+
budget_exceeded: "completed",
|
|
613
|
+
blocked: "blocked",
|
|
614
|
+
cancelled: "cancelled",
|
|
615
|
+
paused: "blocked",
|
|
616
|
+
running: "running",
|
|
652
617
|
};
|
|
653
618
|
const runStatus = goalStatusToRunStatus[goalResult.goalState.state] ?? "completed";
|
|
654
|
-
const finalGoalManifest: TeamRunManifest = {
|
|
619
|
+
const finalGoalManifest: TeamRunManifest = {
|
|
620
|
+
...goalResult.manifest,
|
|
621
|
+
status: runStatus,
|
|
622
|
+
updatedAt: new Date().toISOString(),
|
|
623
|
+
};
|
|
655
624
|
saveRunManifest(finalGoalManifest);
|
|
656
|
-
earlyResult = {
|
|
625
|
+
earlyResult = {
|
|
626
|
+
manifest: finalGoalManifest,
|
|
627
|
+
tasks: goalResult.tasks,
|
|
628
|
+
};
|
|
657
629
|
} else {
|
|
658
|
-
|
|
630
|
+
// LAZY: defer dynamic import of ./dynamic-workflow-runner.ts to its call site.
|
|
659
631
|
const { runDynamicWorkflow } = await import("./dynamic-workflow-runner.ts");
|
|
660
|
-
|
|
632
|
+
// LAZY: defer dynamic import of ../workflows/discover-workflows.ts to its call site.
|
|
661
633
|
const { allWorkflows, discoverWorkflows } = await import("../workflows/discover-workflows.ts");
|
|
662
634
|
const wf = allWorkflows(discoverWorkflows(manifest.cwd)).find((w) => w.name === manifest.workflow);
|
|
663
|
-
if (!wf || wf.runtime !== "dynamic" || !wf.dynamicScript)
|
|
664
|
-
|
|
635
|
+
if (!wf || wf.runtime !== "dynamic" || !wf.dynamicScript)
|
|
636
|
+
throw new Error(
|
|
637
|
+
`runKind="dynamic-workflow" but workflow '${manifest.workflow}' is not dynamic (runId=${manifest.runId})`,
|
|
638
|
+
);
|
|
639
|
+
const dwfResult = await runDynamicWorkflow({
|
|
640
|
+
manifest,
|
|
641
|
+
workflow: wf as import("../workflows/workflow-config.ts").DynamicWorkflowConfig,
|
|
642
|
+
signal: abortController.signal,
|
|
643
|
+
tokenBudget: wf.maxTokenBudget,
|
|
644
|
+
});
|
|
665
645
|
saveRunManifest(dwfResult.manifest);
|
|
666
646
|
earlyResult = dwfResult;
|
|
667
647
|
}
|
|
@@ -669,115 +649,97 @@ async function main(): Promise<void> {
|
|
|
669
649
|
result = earlyResult;
|
|
670
650
|
}
|
|
671
651
|
if (!earlyResult) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
(candidate) => candidate.name === manifest.
|
|
680
|
-
);
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
data: { runtimeResolution, async: true },
|
|
730
|
-
});
|
|
731
|
-
if (runtime.safety === "blocked")
|
|
732
|
-
throw new Error(
|
|
733
|
-
runtime.reason ??
|
|
734
|
-
"Child worker execution is disabled; refusing to create no-op scaffold subagents.",
|
|
735
|
-
);
|
|
736
|
-
const executeWorkers = runtime.kind !== "scaffold";
|
|
737
|
-
// Use ownerSessionId for workspaceId to ensure agents are only visible to the session that spawned them.
|
|
738
|
-
// manifest.cwd would cause cross-session visibility since all sessions share the same project directory.
|
|
739
|
-
// Mark this as background mode so task-runner writes events to background.log for debugging.
|
|
740
|
-
process.env.PI_CREW_BACKGROUND_MODE = "1";
|
|
741
|
-
// BUG #17: Keep-alive interval (NOT unref'd) prevents event loop from exiting
|
|
742
|
-
// during jiti compilation of team-runner.ts. Without this, the event loop
|
|
743
|
-
// can drain when import() blocks, causing the process to exit prematurely.
|
|
744
|
-
// NOTE: abortController is already created above (before heartbeat/interrupt guard start)
|
|
745
|
-
// so it is available here and its signal is passed through to executeTeamRun → child-pi.
|
|
652
|
+
debugLog(`[background-runner] calling directTeamAndWorkflowFromRun`);
|
|
653
|
+
const direct = directTeamAndWorkflowFromRun(manifest, tasks, agents);
|
|
654
|
+
debugLog(`[background-runner] direct done, finding team`);
|
|
655
|
+
const team = direct?.team ?? allTeams(discoverTeams(cwd)).find((candidate) => candidate.name === manifest.team);
|
|
656
|
+
if (!team) throw new Error(`Team '${manifest.team}' not found.`);
|
|
657
|
+
debugLog(`[background-runner] team=${team.name}, finding workflow`);
|
|
658
|
+
const baseWorkflow =
|
|
659
|
+
direct?.workflow ?? allWorkflows(discoverWorkflows(cwd)).find((candidate) => candidate.name === manifest.workflow);
|
|
660
|
+
if (!baseWorkflow) throw new Error(`Workflow '${manifest.workflow ?? ""}' not found.`);
|
|
661
|
+
debugLog(`[background-runner] workflow=${baseWorkflow.name}`);
|
|
662
|
+
const workflow = expandParallelResearchWorkflow(baseWorkflow, cwd);
|
|
663
|
+
debugLog(`[background-runner] loading config`);
|
|
664
|
+
const loadedConfig = loadConfig(cwd);
|
|
665
|
+
const runConfig =
|
|
666
|
+
manifest.runConfig && typeof manifest.runConfig === "object" && !Array.isArray(manifest.runConfig)
|
|
667
|
+
? (manifest.runConfig as typeof loadedConfig.config)
|
|
668
|
+
: loadedConfig.config;
|
|
669
|
+
const runtime = manifest.runtimeResolution
|
|
670
|
+
? {
|
|
671
|
+
kind: manifest.runtimeResolution.kind,
|
|
672
|
+
requestedMode: manifest.runtimeResolution.requestedMode,
|
|
673
|
+
available: manifest.runtimeResolution.available,
|
|
674
|
+
fallback: manifest.runtimeResolution.fallback,
|
|
675
|
+
steer: manifest.runtimeResolution.kind === "live-session",
|
|
676
|
+
resume: manifest.runtimeResolution.kind === "live-session",
|
|
677
|
+
liveToolActivity: manifest.runtimeResolution.kind === "live-session",
|
|
678
|
+
transcript: manifest.runtimeResolution.kind !== "scaffold",
|
|
679
|
+
reason: manifest.runtimeResolution.reason,
|
|
680
|
+
safety: manifest.runtimeResolution.safety,
|
|
681
|
+
}
|
|
682
|
+
: await resolveCrewRuntime(runConfig);
|
|
683
|
+
const runtimeResolution = manifest.runtimeResolution ?? runtimeResolutionState(runtime);
|
|
684
|
+
manifest = {
|
|
685
|
+
...manifest,
|
|
686
|
+
runtimeResolution,
|
|
687
|
+
runConfig,
|
|
688
|
+
updatedAt: new Date().toISOString(),
|
|
689
|
+
};
|
|
690
|
+
saveRunManifest(manifest);
|
|
691
|
+
appendEvent(manifest.eventsPath, {
|
|
692
|
+
type: "runtime.resolved",
|
|
693
|
+
runId: manifest.runId,
|
|
694
|
+
message: `Runtime resolved: ${runtime.kind} safety=${runtime.safety}`,
|
|
695
|
+
data: { runtimeResolution, async: true },
|
|
696
|
+
});
|
|
697
|
+
if (runtime.safety === "blocked")
|
|
698
|
+
throw new Error(runtime.reason ?? "Child worker execution is disabled; refusing to create no-op scaffold subagents.");
|
|
699
|
+
const executeWorkers = runtime.kind !== "scaffold";
|
|
700
|
+
// Use ownerSessionId for workspaceId to ensure agents are only visible to the session that spawned them.
|
|
701
|
+
// manifest.cwd would cause cross-session visibility since all sessions share the same project directory.
|
|
702
|
+
// Mark this as background mode so task-runner writes events to background.log for debugging.
|
|
703
|
+
process.env.PI_CREW_BACKGROUND_MODE = "1";
|
|
704
|
+
// BUG #17: Keep-alive interval (NOT unref'd) prevents event loop from exiting
|
|
705
|
+
// during jiti compilation of team-runner.ts. Without this, the event loop
|
|
706
|
+
// can drain when import() blocks, causing the process to exit prematurely.
|
|
707
|
+
// NOTE: abortController is already created above (before heartbeat/interrupt guard start)
|
|
708
|
+
// so it is available here and its signal is passed through to executeTeamRun → child-pi.
|
|
746
709
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
710
|
+
debugLog(`[background-runner] dispatching runKind=${manifest.runKind ?? "team-run"}`);
|
|
711
|
+
try {
|
|
712
|
+
// Fix round-4: goal-loop/dynamic-workflow handled by the short-circuit above.
|
|
713
|
+
// This switch now only carries the traditional team-run path.
|
|
714
|
+
switch (manifest.runKind ?? "team-run") {
|
|
715
|
+
default: {
|
|
716
|
+
// Existing "team-run" path — unchanged behavior.
|
|
717
|
+
result = await executeTeamRun({
|
|
718
|
+
manifest,
|
|
719
|
+
tasks,
|
|
720
|
+
team,
|
|
721
|
+
workflow,
|
|
722
|
+
agents,
|
|
723
|
+
executeWorkers,
|
|
724
|
+
limits: runConfig.limits,
|
|
725
|
+
runtime,
|
|
726
|
+
runtimeConfig: runConfig.runtime,
|
|
727
|
+
skillOverride: manifest.skillOverride,
|
|
728
|
+
reliability: runConfig.reliability,
|
|
729
|
+
workspaceId: manifest.ownerSessionId ?? manifest.cwd,
|
|
730
|
+
signal: abortController.signal,
|
|
731
|
+
});
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
770
734
|
}
|
|
735
|
+
console.log(`[background-runner] executeTeamRun returned, status=${result.manifest.status}`);
|
|
736
|
+
} catch (execError) {
|
|
737
|
+
console.log(
|
|
738
|
+
`[background-runner] executeTeamRun THREW: ${execError instanceof Error ? execError.message : String(execError)}`,
|
|
739
|
+
);
|
|
740
|
+
console.log(`[background-runner] stack: ${execError instanceof Error ? execError.stack : "N/A"}`);
|
|
741
|
+
throw execError;
|
|
771
742
|
}
|
|
772
|
-
console.log(`[background-runner] executeTeamRun returned, status=${result.manifest.status}`,
|
|
773
|
-
);
|
|
774
|
-
} catch (execError) {
|
|
775
|
-
console.log(`[background-runner] executeTeamRun THREW: ${execError instanceof Error ? execError.message : String(execError)}`,
|
|
776
|
-
);
|
|
777
|
-
console.log(`[background-runner] stack: ${execError instanceof Error ? execError.stack : "N/A"}`,
|
|
778
|
-
);
|
|
779
|
-
throw execError;
|
|
780
|
-
}
|
|
781
743
|
} // close if (!earlyResult) — team-run setup+execute done; earlyResult path skips to here
|
|
782
744
|
manifest = result!.manifest;
|
|
783
745
|
tasks = result!.tasks;
|
|
@@ -786,40 +748,19 @@ async function main(): Promise<void> {
|
|
|
786
748
|
runId: manifest.runId,
|
|
787
749
|
data: { status: manifest.status, tasks: tasks.length },
|
|
788
750
|
});
|
|
789
|
-
console.log(`[background-runner] async.completed written, status=${manifest.status}
|
|
790
|
-
);
|
|
791
|
-
if (
|
|
792
|
-
manifest.status === "failed" ||
|
|
793
|
-
manifest.status === "cancelled" ||
|
|
794
|
-
manifest.status === "blocked"
|
|
795
|
-
)
|
|
796
|
-
process.exitCode = 1;
|
|
751
|
+
console.log(`[background-runner] async.completed written, status=${manifest.status}`);
|
|
752
|
+
if (manifest.status === "failed" || manifest.status === "cancelled" || manifest.status === "blocked") process.exitCode = 1;
|
|
797
753
|
} catch (error) {
|
|
798
754
|
// Terminate live agents on failure too — agents are done when the run fails
|
|
799
755
|
try {
|
|
800
|
-
const loaded = withRunLockSync(
|
|
801
|
-
manifest,
|
|
802
|
-
() => loadRunManifestById(cwd, runId),
|
|
803
|
-
{ staleMs: 30_000 },
|
|
804
|
-
); // Use withRunLockSync to prevent race with concurrent writers (e.g., stale reconciler)
|
|
756
|
+
const loaded = withRunLockSync(manifest, () => loadRunManifestById(cwd, runId), { staleMs: 30_000 }); // Use withRunLockSync to prevent race with concurrent writers (e.g., stale reconciler)
|
|
805
757
|
// between the read and the subsequent save.
|
|
806
758
|
const manifestToUse = loaded?.manifest ?? manifest;
|
|
807
759
|
if (manifestToUse) {
|
|
808
760
|
// LAZY: live-agent-manager only needed on failure cleanup path; avoid module load at hot path.
|
|
809
|
-
const { terminateLiveAgentsForRun } = await import(
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
void terminateLiveAgentsForRun(
|
|
813
|
-
manifestToUse.runId,
|
|
814
|
-
"failed",
|
|
815
|
-
appendEvent,
|
|
816
|
-
manifestToUse.eventsPath,
|
|
817
|
-
).catch((error) =>
|
|
818
|
-
logInternalError(
|
|
819
|
-
"background-runner.terminate",
|
|
820
|
-
error,
|
|
821
|
-
`runId=${manifestToUse.runId}`,
|
|
822
|
-
),
|
|
761
|
+
const { terminateLiveAgentsForRun } = await import("./live-agent-manager.ts");
|
|
762
|
+
void terminateLiveAgentsForRun(manifestToUse.runId, "failed", appendEvent, manifestToUse.eventsPath).catch((error) =>
|
|
763
|
+
logInternalError("background-runner.terminate", error, `runId=${manifestToUse.runId}`),
|
|
823
764
|
);
|
|
824
765
|
}
|
|
825
766
|
} catch {
|
|
@@ -833,8 +774,7 @@ async function main(): Promise<void> {
|
|
|
833
774
|
message,
|
|
834
775
|
});
|
|
835
776
|
process.exitCode = 1;
|
|
836
|
-
console.log(`[background-runner] catch block, error=${error instanceof Error ? error.message : String(error)}
|
|
837
|
-
);
|
|
777
|
+
console.log(`[background-runner] catch block, error=${error instanceof Error ? error.message : String(error)}`);
|
|
838
778
|
} finally {
|
|
839
779
|
// FIX Issue #4: Use shared runCleanup() function for consistent cleanup
|
|
840
780
|
// across all exit paths (normal, unhandled rejection, main() exception).
|
|
@@ -872,9 +812,7 @@ async function main(): Promise<void> {
|
|
|
872
812
|
try {
|
|
873
813
|
await main();
|
|
874
814
|
} catch (err) {
|
|
875
|
-
console.error(
|
|
876
|
-
`[background-runner] DEBUG: main() uncaught: ${err instanceof Error ? err.message : String(err)}`,
|
|
877
|
-
);
|
|
815
|
+
console.error(`[background-runner] DEBUG: main() uncaught: ${err instanceof Error ? err.message : String(err)}`);
|
|
878
816
|
// FIX Issue #1: Set the flag so the finally block's runCleanup() call
|
|
879
817
|
// will trigger process.exit(1) after cleanup completes. Previously this
|
|
880
818
|
// called process.exit(1) directly, bypassing the finally block and leaving
|