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
|
@@ -21,7 +21,12 @@ export interface RetryHooks {
|
|
|
21
21
|
signal?: AbortSignal;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export const DEFAULT_RETRY_POLICY: RetryPolicy = {
|
|
24
|
+
export const DEFAULT_RETRY_POLICY: RetryPolicy = {
|
|
25
|
+
maxAttempts: 3,
|
|
26
|
+
backoffMs: 1000,
|
|
27
|
+
jitterRatio: 0.3,
|
|
28
|
+
exponentialFactor: 2,
|
|
29
|
+
};
|
|
25
30
|
|
|
26
31
|
function asError(error: unknown): Error {
|
|
27
32
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -45,11 +50,22 @@ export function calculateRetryDelay(attempt: number, policy: RetryPolicy = DEFAU
|
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
function retryAttemptInfo(attempt: number, hooks: RetryHooks): RetryAttemptInfo {
|
|
48
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
attempt,
|
|
55
|
+
attemptId: hooks.attemptId?.(attempt) ?? `retry_attempt_${attempt}`,
|
|
56
|
+
};
|
|
49
57
|
}
|
|
50
58
|
|
|
51
|
-
export async function executeWithRetry<T>(
|
|
52
|
-
|
|
59
|
+
export async function executeWithRetry<T>(
|
|
60
|
+
fn: (attempt: number, info: RetryAttemptInfo) => Promise<T>,
|
|
61
|
+
policy: RetryPolicy = DEFAULT_RETRY_POLICY,
|
|
62
|
+
hooks: RetryHooks = {},
|
|
63
|
+
): Promise<T> {
|
|
64
|
+
const normalized: RetryPolicy = {
|
|
65
|
+
...DEFAULT_RETRY_POLICY,
|
|
66
|
+
...policy,
|
|
67
|
+
maxAttempts: Math.max(1, policy.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts),
|
|
68
|
+
};
|
|
53
69
|
let lastError: Error | undefined;
|
|
54
70
|
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
55
71
|
throwIfCancelled(hooks.signal);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RetryRunner - Execute tasks with retry support and summary accumulation.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Based on pi-boomerang's --rethrow pattern:
|
|
5
5
|
* - Retries failed tasks up to maxAttempts
|
|
6
6
|
* - Generates handoffs between attempts
|
|
7
7
|
* - Accumulates context from previous attempts
|
|
8
8
|
* - Supports exponential backoff
|
|
9
9
|
* - Limits handoff accumulation to prevent memory leaks
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* @see docs/pi-boomerang-integration-plan.md
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type {
|
|
14
|
+
import type { HandoffManager, HandoffSummary, TaskPacket, TaskResult } from "./handoff-manager.ts";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Retry configuration.
|
|
@@ -75,10 +75,7 @@ export class RetryRunner {
|
|
|
75
75
|
private taskRunner: TaskRunnerLike;
|
|
76
76
|
private handoffManager: HandoffManager;
|
|
77
77
|
|
|
78
|
-
constructor(
|
|
79
|
-
taskRunner: TaskRunnerLike,
|
|
80
|
-
handoffManager: HandoffManager,
|
|
81
|
-
) {
|
|
78
|
+
constructor(taskRunner: TaskRunnerLike, handoffManager: HandoffManager) {
|
|
82
79
|
this.taskRunner = taskRunner;
|
|
83
80
|
this.handoffManager = handoffManager;
|
|
84
81
|
}
|
|
@@ -127,15 +124,12 @@ export class RetryRunner {
|
|
|
127
124
|
/**
|
|
128
125
|
* Execute task with retry support.
|
|
129
126
|
* Summaries accumulate between attempts for better context.
|
|
130
|
-
*
|
|
127
|
+
*
|
|
131
128
|
* @param packet - Task packet to execute
|
|
132
129
|
* @param config - Retry configuration (stopOnSuccess defaults to true)
|
|
133
130
|
* @returns Final retry result with all attempts
|
|
134
131
|
*/
|
|
135
|
-
async runWithRetry(
|
|
136
|
-
packet: TaskPacket,
|
|
137
|
-
config: RetryConfig
|
|
138
|
-
): Promise<RetryResult> {
|
|
132
|
+
async runWithRetry(packet: TaskPacket, config: RetryConfig): Promise<RetryResult> {
|
|
139
133
|
if (this._disposed) {
|
|
140
134
|
throw new Error("RetryRunner has been disposed");
|
|
141
135
|
}
|
|
@@ -148,7 +142,6 @@ export class RetryRunner {
|
|
|
148
142
|
// Clear previous handoffs at start of each retry run
|
|
149
143
|
this._handoffs = [];
|
|
150
144
|
|
|
151
|
-
|
|
152
145
|
for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
|
|
153
146
|
if (this._disposed) {
|
|
154
147
|
throw new Error("RetryRunner was disposed during retry");
|
|
@@ -157,11 +150,7 @@ export class RetryRunner {
|
|
|
157
150
|
|
|
158
151
|
try {
|
|
159
152
|
// Inject accumulated handoffs into context
|
|
160
|
-
const enrichedPacket = this.enrichPacketWithHandoffs(
|
|
161
|
-
packet,
|
|
162
|
-
handoffs,
|
|
163
|
-
attempt
|
|
164
|
-
);
|
|
153
|
+
const enrichedPacket = this.enrichPacketWithHandoffs(packet, handoffs, attempt);
|
|
165
154
|
|
|
166
155
|
// Execute task
|
|
167
156
|
const result = await this.taskRunner.runTask(enrichedPacket);
|
|
@@ -175,10 +164,7 @@ export class RetryRunner {
|
|
|
175
164
|
|
|
176
165
|
// Generate summary between attempts
|
|
177
166
|
if (config.summaryBetweenAttempts !== false) {
|
|
178
|
-
const summary = await this.handoffManager.generateSummary(
|
|
179
|
-
packet,
|
|
180
|
-
result
|
|
181
|
-
);
|
|
167
|
+
const summary = await this.handoffManager.generateSummary(packet, result);
|
|
182
168
|
attemptResult.summary = summary;
|
|
183
169
|
handoffs.push(summary);
|
|
184
170
|
|
|
@@ -213,13 +199,12 @@ export class RetryRunner {
|
|
|
213
199
|
config.backoffMs ?? 1000,
|
|
214
200
|
attempt,
|
|
215
201
|
config.backoffMultiplier ?? 1,
|
|
216
|
-
config.maxBackoffMs
|
|
202
|
+
config.maxBackoffMs,
|
|
217
203
|
);
|
|
218
204
|
if (backoffDelay > 0) {
|
|
219
205
|
await this.sleep(backoffDelay);
|
|
220
206
|
}
|
|
221
207
|
}
|
|
222
|
-
|
|
223
208
|
} catch (error) {
|
|
224
209
|
attempts.push({
|
|
225
210
|
attempt,
|
|
@@ -238,7 +223,7 @@ export class RetryRunner {
|
|
|
238
223
|
config.backoffMs,
|
|
239
224
|
attempt,
|
|
240
225
|
config.backoffMultiplier ?? 1,
|
|
241
|
-
config.maxBackoffMs
|
|
226
|
+
config.maxBackoffMs,
|
|
242
227
|
);
|
|
243
228
|
if (backoffDelay > 0) {
|
|
244
229
|
await this.sleep(backoffDelay);
|
|
@@ -260,24 +245,23 @@ export class RetryRunner {
|
|
|
260
245
|
/**
|
|
261
246
|
* Enrich packet with accumulated handoffs from previous attempts.
|
|
262
247
|
*/
|
|
263
|
-
private enrichPacketWithHandoffs(
|
|
264
|
-
packet: TaskPacket,
|
|
265
|
-
handoffs: HandoffSummary[],
|
|
266
|
-
attempt: number
|
|
267
|
-
): TaskPacket {
|
|
248
|
+
private enrichPacketWithHandoffs(packet: TaskPacket, handoffs: HandoffSummary[], attempt: number): TaskPacket {
|
|
268
249
|
if (handoffs.length === 0) {
|
|
269
250
|
return packet;
|
|
270
251
|
}
|
|
271
252
|
|
|
272
253
|
// Build accumulated context from previous attempts
|
|
273
|
-
const accumulatedContext = handoffs
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
254
|
+
const accumulatedContext = handoffs
|
|
255
|
+
.map(
|
|
256
|
+
(h, index) =>
|
|
257
|
+
`## Attempt ${index + 1}: ${h.task}\n` +
|
|
258
|
+
`Outcome: ${h.outcome}\n` +
|
|
259
|
+
`Files: created=${h.filesCreated.join(", ")}, modified=${h.filesModified.join(", ")}\n` +
|
|
260
|
+
`Decisions: ${h.decisions.map((d) => d.rationale).join("; ")}\n` +
|
|
261
|
+
`Blockers: ${h.blockers.join(", ")}\n` +
|
|
262
|
+
`Next Steps: ${h.nextSteps.join(", ")}\n`,
|
|
263
|
+
)
|
|
264
|
+
.join("\n---\n");
|
|
281
265
|
|
|
282
266
|
return {
|
|
283
267
|
...packet,
|
|
@@ -293,12 +277,7 @@ export class RetryRunner {
|
|
|
293
277
|
/**
|
|
294
278
|
* Calculate backoff delay with optional capping.
|
|
295
279
|
*/
|
|
296
|
-
private calculateBackoff(
|
|
297
|
-
baseMs: number,
|
|
298
|
-
attempt: number,
|
|
299
|
-
multiplier: number,
|
|
300
|
-
maxBackoffMs?: number
|
|
301
|
-
): number {
|
|
280
|
+
private calculateBackoff(baseMs: number, attempt: number, multiplier: number, maxBackoffMs?: number): number {
|
|
302
281
|
let delay = baseMs * Math.pow(multiplier, attempt - 1);
|
|
303
282
|
if (maxBackoffMs !== undefined) {
|
|
304
283
|
delay = Math.min(delay, maxBackoffMs);
|
|
@@ -310,17 +289,14 @@ export class RetryRunner {
|
|
|
310
289
|
* Sleep helper.
|
|
311
290
|
*/
|
|
312
291
|
private sleep(ms: number): Promise<void> {
|
|
313
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
292
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
314
293
|
}
|
|
315
294
|
}
|
|
316
295
|
|
|
317
296
|
/**
|
|
318
297
|
* Create a RetryRunner with default dependencies.
|
|
319
298
|
*/
|
|
320
|
-
export function createRetryRunner(
|
|
321
|
-
taskRunner: TaskRunnerLike,
|
|
322
|
-
handoffManager: HandoffManager
|
|
323
|
-
): RetryRunner {
|
|
299
|
+
export function createRetryRunner(taskRunner: TaskRunnerLike, handoffManager: HandoffManager): RetryRunner {
|
|
324
300
|
return new RetryRunner(taskRunner, handoffManager);
|
|
325
301
|
}
|
|
326
302
|
|
|
@@ -357,4 +333,4 @@ export const PERSISTENT_FAILURE_RETRY_CONFIG: RetryConfig = {
|
|
|
357
333
|
backoffMs: 2000,
|
|
358
334
|
backoffMultiplier: 2,
|
|
359
335
|
maxBackoffMs: 60000,
|
|
360
|
-
};
|
|
336
|
+
};
|
|
@@ -24,6 +24,11 @@ export function currentCrewRole(env: NodeJS.ProcessEnv = process.env): string |
|
|
|
24
24
|
export function checkSubagentSpawnPermission(role: string | undefined): PermissionCheckResult {
|
|
25
25
|
if (!role) return { allowed: true, mode: "workspace_write" };
|
|
26
26
|
const mode = permissionForRole(role);
|
|
27
|
-
if (mode === "read_only")
|
|
27
|
+
if (mode === "read_only")
|
|
28
|
+
return {
|
|
29
|
+
allowed: false,
|
|
30
|
+
mode,
|
|
31
|
+
reason: `Role '${role}' is read-only and cannot spawn additional subagents.`,
|
|
32
|
+
};
|
|
28
33
|
return { allowed: true, mode };
|
|
29
34
|
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run-coalesced-task-group.ts — M6 real-dispatch MVP worker spawn.
|
|
3
|
+
*
|
|
4
|
+
* Spawns ONE child Pi process for an entire coalesced group of N tasks
|
|
5
|
+
* (sharing role + cwd per `planCoalescedGroups`). The combined prompt
|
|
6
|
+
* instructs the worker to wrap each task's result in `<<<TASK_RESULT:id>>>`
|
|
7
|
+
* ... `<<<END_TASK_RESULT>>>` delimiters. Output is split back into N
|
|
8
|
+
* per-task results via `splitCoalescedOutput`, each written to its own
|
|
9
|
+
* result artifact + state update.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { writeFile } from "node:fs/promises";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { permissionForRole } from "./role-permission.ts";
|
|
15
|
+
import { runChildPi } from "./child-pi.ts";
|
|
16
|
+
import { splitCoalescedOutput } from "./task-runner/output-splitter.ts";
|
|
17
|
+
import { buildWorkspaceTree } from "./workspace-tree.ts";
|
|
18
|
+
import { saveRunTasks, updateRunStatus } from "../state/state-store.ts";
|
|
19
|
+
import { writeArtifact } from "../state/artifact-store.ts";
|
|
20
|
+
import { appendEventAsync } from "../state/event-log.ts";
|
|
21
|
+
import { createWorkerHeartbeat, touchWorkerHeartbeat } from "./worker-heartbeat.ts";
|
|
22
|
+
import type { AgentConfig } from "../agents/agent-config.ts";
|
|
23
|
+
import type { CrewRuntimeMode } from "./runtime-resolver.ts";
|
|
24
|
+
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
25
|
+
import type { WorkflowStep } from "../workflows/workflow-config.ts";
|
|
26
|
+
import { mergeArtifacts } from "./team-runner-artifacts.ts";
|
|
27
|
+
|
|
28
|
+
export interface CoalescedTaskGroupInput {
|
|
29
|
+
manifest: TeamRunManifest;
|
|
30
|
+
tasks: TeamTaskState[];
|
|
31
|
+
groupTasks: TeamTaskState[];
|
|
32
|
+
step: WorkflowStep;
|
|
33
|
+
agent: AgentConfig;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
executeWorkers: boolean;
|
|
36
|
+
runtimeKind?: CrewRuntimeMode;
|
|
37
|
+
workspaceId: string;
|
|
38
|
+
onJsonEvent?: (taskId: string, runId: string, event: unknown) => void;
|
|
39
|
+
teamRole?: unknown;
|
|
40
|
+
perTaskRuntime?: CrewRuntimeMode;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CoalescedTaskGroupResult {
|
|
44
|
+
manifest: TeamRunManifest;
|
|
45
|
+
tasks: TeamTaskState[];
|
|
46
|
+
taskIds: string[];
|
|
47
|
+
rawOutput: string;
|
|
48
|
+
success: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Promise<CoalescedTaskGroupResult> {
|
|
52
|
+
const { manifest, groupTasks, step, agent, signal, executeWorkers } = input;
|
|
53
|
+
const groupId = groupTasks.map((t) => t.id).join("+");
|
|
54
|
+
const firstTask = groupTasks[0]!;
|
|
55
|
+
const taskIds = groupTasks.map((t) => t.id);
|
|
56
|
+
|
|
57
|
+
// Set ALL N tasks to "running" before spawn so the dashboard reflects
|
|
58
|
+
// the in-flight state.
|
|
59
|
+
let updatedTasks: TeamTaskState[] = input.tasks.map((t) => {
|
|
60
|
+
if (taskIds.includes(t.id) && t.status !== "running") {
|
|
61
|
+
return { ...t, status: "running" as const, startedAt: new Date().toISOString() };
|
|
62
|
+
}
|
|
63
|
+
return t;
|
|
64
|
+
});
|
|
65
|
+
saveRunTasks(manifest, updatedTasks);
|
|
66
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
67
|
+
type: "task.coalesced_dispatch_start",
|
|
68
|
+
runId: manifest.runId,
|
|
69
|
+
message: `Dispatching ${groupTasks.length} coalesced tasks in 1 worker (role=${firstTask.role}, cwd=${firstTask.cwd})`,
|
|
70
|
+
data: { groupId, role: firstTask.role, cwd: firstTask.cwd, taskIds },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const combinedPrompt = await buildCoalescedPrompt(manifest, step, groupTasks, agent);
|
|
74
|
+
|
|
75
|
+
// FIX (M6): write heartbeats for ALL N tasks in the coalesced group so the
|
|
76
|
+
// background watcher doesn't fire heartbeat_dead against the (single)
|
|
77
|
+
// child pi worker. Previously runCoalescedTaskGroup never wrote heartbeats,
|
|
78
|
+
// so the watcher saw `heartbeat.lastSeenAt: undefined` within 2 seconds of
|
|
79
|
+
// spawn and emitted heartbeat_dead even though the worker continued to
|
|
80
|
+
// completion (false-positive stuck-worker alarms). The singleton path
|
|
81
|
+
// (runTeamTask) writes heartbeats via persistHeartbeat — M6 needs the
|
|
82
|
+
// equivalent to avoid the false-positive.
|
|
83
|
+
updatedTasks = updatedTasks.map((t) => {
|
|
84
|
+
if (!taskIds.includes(t.id)) return t;
|
|
85
|
+
return {
|
|
86
|
+
...t,
|
|
87
|
+
heartbeat: t.heartbeat ?? createWorkerHeartbeat(t.id),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
saveRunTasks(manifest, updatedTasks);
|
|
91
|
+
|
|
92
|
+
let rawOutput = "";
|
|
93
|
+
let success = false;
|
|
94
|
+
if (!executeWorkers) {
|
|
95
|
+
rawOutput = buildScaffoldOutput(groupTasks);
|
|
96
|
+
success = true;
|
|
97
|
+
} else {
|
|
98
|
+
// Heartbeat refresher: touch every task's heartbeat every 15s while the
|
|
99
|
+
// worker is alive. Set `alive: true` explicitly so post-completion
|
|
100
|
+
// staleness checks immediately recognize liveness.
|
|
101
|
+
const heartbeatTimer = setInterval(() => {
|
|
102
|
+
const now = new Date().toISOString();
|
|
103
|
+
updatedTasks = updatedTasks.map((t) => {
|
|
104
|
+
if (!taskIds.includes(t.id)) return t;
|
|
105
|
+
return {
|
|
106
|
+
...t,
|
|
107
|
+
heartbeat: touchWorkerHeartbeat(t.heartbeat ?? createWorkerHeartbeat(t.id), { alive: true }),
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
try {
|
|
111
|
+
saveRunTasks(manifest, updatedTasks);
|
|
112
|
+
} catch {
|
|
113
|
+
// Run may have been pruned mid-dispatch — best-effort only.
|
|
114
|
+
}
|
|
115
|
+
}, 15_000);
|
|
116
|
+
try {
|
|
117
|
+
const result = await runChildPi({
|
|
118
|
+
cwd: firstTask.cwd,
|
|
119
|
+
task: combinedPrompt,
|
|
120
|
+
agent,
|
|
121
|
+
signal,
|
|
122
|
+
excludeContextBash: true,
|
|
123
|
+
maxTurns: 5,
|
|
124
|
+
onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e),
|
|
125
|
+
});
|
|
126
|
+
rawOutput = result.rawFinalText ?? result.stdout ?? "";
|
|
127
|
+
success = result.exitStatus?.exitCode === 0;
|
|
128
|
+
} catch (err) {
|
|
129
|
+
rawOutput = `Worker dispatch failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
130
|
+
success = false;
|
|
131
|
+
} finally {
|
|
132
|
+
clearInterval(heartbeatTimer);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const split = splitCoalescedOutput(rawOutput, taskIds);
|
|
137
|
+
|
|
138
|
+
const finishedAt = new Date().toISOString();
|
|
139
|
+
const newArtifacts: TeamRunManifest["artifacts"] = [];
|
|
140
|
+
updatedTasks = updatedTasks.map((t) => {
|
|
141
|
+
if (!taskIds.includes(t.id)) return t;
|
|
142
|
+
const entry = split.find((s) => s.taskId === t.id);
|
|
143
|
+
const ok = success && Boolean(entry?.text);
|
|
144
|
+
const text = entry?.text ?? rawOutput;
|
|
145
|
+
// BUGFIX (M6 real dispatch): write to artifactsRoot via writeArtifact
|
|
146
|
+
// so task.resultArtifact is set and aggregateTaskOutputs can read
|
|
147
|
+
// the per-task text. Previously the coalesced path used a raw
|
|
148
|
+
// writeFile to stateRoot/results/<id>.txt which aggregated task
|
|
149
|
+
// outputs could not locate — they only consult task.resultArtifact.
|
|
150
|
+
// Result: tasks reported "EMPTY OUTPUT" in the batch summary even
|
|
151
|
+
// though on-disk results were correct.
|
|
152
|
+
const resultArtifact = writeArtifact(manifest.artifactsRoot, {
|
|
153
|
+
kind: "result",
|
|
154
|
+
relativePath: `results/${t.id}.txt`,
|
|
155
|
+
content: text,
|
|
156
|
+
producer: t.id,
|
|
157
|
+
});
|
|
158
|
+
newArtifacts.push(resultArtifact);
|
|
159
|
+
return {
|
|
160
|
+
...t,
|
|
161
|
+
status: ok ? ("completed" as const) : ("failed" as const),
|
|
162
|
+
finishedAt,
|
|
163
|
+
result: {
|
|
164
|
+
text,
|
|
165
|
+
producer: groupId,
|
|
166
|
+
strategy: entry?.strategy ?? "broadcast",
|
|
167
|
+
},
|
|
168
|
+
resultArtifact,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
saveRunTasks(manifest, updatedTasks);
|
|
172
|
+
let updatedManifest: TeamRunManifest = {
|
|
173
|
+
...manifest,
|
|
174
|
+
artifacts: mergeArtifacts([...manifest.artifacts, ...newArtifacts]),
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (success) {
|
|
178
|
+
updatedManifest = updateRunStatus(updatedManifest, "running");
|
|
179
|
+
}
|
|
180
|
+
await appendEventAsync(updatedManifest.eventsPath, {
|
|
181
|
+
type: "task.coalesced_dispatch_end",
|
|
182
|
+
runId: manifest.runId,
|
|
183
|
+
message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
|
|
184
|
+
data: { groupId, taskIds, success, strategy: split[0]?.strategy },
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function buildCoalescedPrompt(
|
|
191
|
+
manifest: TeamRunManifest,
|
|
192
|
+
step: WorkflowStep,
|
|
193
|
+
groupTasks: TeamTaskState[],
|
|
194
|
+
agent: AgentConfig,
|
|
195
|
+
): Promise<string> {
|
|
196
|
+
const tree = await buildWorkspaceTree(groupTasks[0]!.cwd);
|
|
197
|
+
const treeBlock = tree.rendered ? `# Workspace Structure\n${tree.rendered}` : "";
|
|
198
|
+
const roleInstructions =
|
|
199
|
+
permissionForRole(groupTasks[0]!.role) === "read_only"
|
|
200
|
+
? `You are running in READ-ONLY mode. Do not create, modify, delete, or move files. Emit your findings as TEXT in your final output.`
|
|
201
|
+
: "";
|
|
202
|
+
|
|
203
|
+
const taskBlocks = groupTasks
|
|
204
|
+
.map((task, idx) => {
|
|
205
|
+
return [
|
|
206
|
+
`### Task ${idx + 1} of ${groupTasks.length} (id: ${task.id})`,
|
|
207
|
+
`Step: ${step.id}`,
|
|
208
|
+
`Role: ${step.role}`,
|
|
209
|
+
`Task: ${step.task.replaceAll("{goal}", manifest.goal)}`,
|
|
210
|
+
].join("\n");
|
|
211
|
+
})
|
|
212
|
+
.join("\n\n---\n\n");
|
|
213
|
+
|
|
214
|
+
const outputInstructions = groupTasks.map((task) => `<<<TASK_RESULT:${task.id}>>>`).join(" ... ");
|
|
215
|
+
|
|
216
|
+
return [
|
|
217
|
+
"# pi-crew Coalesced Worker Prompt",
|
|
218
|
+
`Run ID: ${manifest.runId}`,
|
|
219
|
+
`Team: ${manifest.team}`,
|
|
220
|
+
`Workflow: ${manifest.workflow ?? "(none)"}`,
|
|
221
|
+
`Goal: ${manifest.goal}`,
|
|
222
|
+
`Tasks in this batch: ${groupTasks.length}`,
|
|
223
|
+
``,
|
|
224
|
+
roleInstructions,
|
|
225
|
+
``,
|
|
226
|
+
treeBlock,
|
|
227
|
+
``,
|
|
228
|
+
`# Your Tasks`,
|
|
229
|
+
`Complete ALL ${groupTasks.length} tasks below. For each, structure your final output using the delimiters shown.`,
|
|
230
|
+
``,
|
|
231
|
+
taskBlocks,
|
|
232
|
+
``,
|
|
233
|
+
`# Output Format (CRITICAL)`,
|
|
234
|
+
`After completing all tasks, structure your final output using these delimiters:`,
|
|
235
|
+
``,
|
|
236
|
+
outputInstructions,
|
|
237
|
+
``,
|
|
238
|
+
`Wrap each task's result between the start and end delimiters:`,
|
|
239
|
+
`<<<TASK_RESULT:{taskId}>>>`,
|
|
240
|
+
`...your result for this task...`,
|
|
241
|
+
`<<<END_TASK_RESULT>>>`,
|
|
242
|
+
``,
|
|
243
|
+
`If delimiters don't fit your workflow, use \`### Task N of M\` headings and we'll parse those instead.`,
|
|
244
|
+
]
|
|
245
|
+
.filter(Boolean)
|
|
246
|
+
.join("\n");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildScaffoldOutput(groupTasks: TeamTaskState[]): string {
|
|
250
|
+
return groupTasks
|
|
251
|
+
.map(
|
|
252
|
+
(task, idx) =>
|
|
253
|
+
`<<<TASK_RESULT:${task.id}>>>\nScaffold result for task ${idx + 1} of ${groupTasks.length}: ${task.id}\n<<<END_TASK_RESULT>>>`,
|
|
254
|
+
)
|
|
255
|
+
.join("\n\n");
|
|
256
|
+
}
|
package/src/runtime/run-drift.ts
CHANGED
|
@@ -6,19 +6,19 @@
|
|
|
6
6
|
* Repair handlers are idempotent — safe to run multiple times.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync,
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
12
12
|
|
|
13
13
|
// ── Types ────────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
15
|
export type DriftKind =
|
|
16
|
-
| "stale-process"
|
|
17
|
-
| "orphaned-claim"
|
|
18
|
-
| "orphaned-worktree"
|
|
19
|
-
| "missing-timestamps"
|
|
20
|
-
| "status-divergence"
|
|
21
|
-
| "unregistered-run";
|
|
16
|
+
| "stale-process" // Heartbeat timeout (existing)
|
|
17
|
+
| "orphaned-claim" // Task claim without task
|
|
18
|
+
| "orphaned-worktree" // Worktree dir without active run
|
|
19
|
+
| "missing-timestamps" // State files without timestamps
|
|
20
|
+
| "status-divergence" // Manifest status ≠ status file
|
|
21
|
+
| "unregistered-run"; // State dir but no manifest
|
|
22
22
|
|
|
23
23
|
export interface DriftReport {
|
|
24
24
|
kind: DriftKind;
|
|
@@ -34,7 +34,12 @@ export interface DriftContext {
|
|
|
34
34
|
/** Active run IDs (from registry) */
|
|
35
35
|
activeRunIds: Set<string>;
|
|
36
36
|
/** Manifest content if available */
|
|
37
|
-
manifest?: {
|
|
37
|
+
manifest?: {
|
|
38
|
+
runId: string;
|
|
39
|
+
status: string;
|
|
40
|
+
cwd: string;
|
|
41
|
+
[k: string]: unknown;
|
|
42
|
+
};
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
// ── Detectors ────────────────────────────────────────────────────────────
|
|
@@ -180,13 +185,7 @@ export function detectUnregisteredRun(ctx: DriftContext): DriftReport | null {
|
|
|
180
185
|
|
|
181
186
|
// ── Reconciliation Loop ─────────────────────────────────────────────────
|
|
182
187
|
|
|
183
|
-
const ALL_DETECTORS = [
|
|
184
|
-
detectOrphanedClaim,
|
|
185
|
-
detectOrphanedWorktree,
|
|
186
|
-
detectMissingTimestamps,
|
|
187
|
-
detectStatusDivergence,
|
|
188
|
-
detectUnregisteredRun,
|
|
189
|
-
];
|
|
188
|
+
const ALL_DETECTORS = [detectOrphanedClaim, detectOrphanedWorktree, detectMissingTimestamps, detectStatusDivergence, detectUnregisteredRun];
|
|
190
189
|
|
|
191
190
|
/**
|
|
192
191
|
* Run all drift detectors and collect reports.
|
|
@@ -7,20 +7,14 @@ import { isFinishedRunStatus } from "./process-status.ts";
|
|
|
7
7
|
|
|
8
8
|
export interface ActiveRunPromise {
|
|
9
9
|
promise: Promise<{ manifest: TeamRunManifest; tasks: TeamTaskState[] }>;
|
|
10
|
-
resolve: (value: {
|
|
11
|
-
manifest: TeamRunManifest;
|
|
12
|
-
tasks: TeamTaskState[];
|
|
13
|
-
}) => void;
|
|
10
|
+
resolve: (value: { manifest: TeamRunManifest; tasks: TeamTaskState[] }) => void;
|
|
14
11
|
reject: (reason: unknown) => void;
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
const activeRunPromises = new Map<string, ActiveRunPromise>();
|
|
18
15
|
|
|
19
16
|
export function registerRunPromise(runId: string): ActiveRunPromise {
|
|
20
|
-
let resolve!: (value: {
|
|
21
|
-
manifest: TeamRunManifest;
|
|
22
|
-
tasks: TeamTaskState[];
|
|
23
|
-
}) => void;
|
|
17
|
+
let resolve!: (value: { manifest: TeamRunManifest; tasks: TeamTaskState[] }) => void;
|
|
24
18
|
let reject!: (reason: unknown) => void;
|
|
25
19
|
const promise = new Promise<{
|
|
26
20
|
manifest: TeamRunManifest;
|
|
@@ -34,10 +28,7 @@ export function registerRunPromise(runId: string): ActiveRunPromise {
|
|
|
34
28
|
return entry;
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
export function resolveRunPromise(
|
|
38
|
-
runId: string,
|
|
39
|
-
result: { manifest: TeamRunManifest; tasks: TeamTaskState[] },
|
|
40
|
-
): void {
|
|
31
|
+
export function resolveRunPromise(runId: string, result: { manifest: TeamRunManifest; tasks: TeamTaskState[] }): void {
|
|
41
32
|
const entry = activeRunPromises.get(runId);
|
|
42
33
|
if (entry) {
|
|
43
34
|
entry.resolve(result);
|
|
@@ -78,13 +69,7 @@ export async function waitForRun(
|
|
|
78
69
|
if (entry) {
|
|
79
70
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
80
71
|
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
81
|
-
timer = setTimeout(
|
|
82
|
-
() =>
|
|
83
|
-
reject(
|
|
84
|
-
new Error(`waitForRun timed out after ${timeoutMs}ms`),
|
|
85
|
-
),
|
|
86
|
-
timeoutMs,
|
|
87
|
-
);
|
|
72
|
+
timer = setTimeout(() => reject(new Error(`waitForRun timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
88
73
|
});
|
|
89
74
|
try {
|
|
90
75
|
return await Promise.race([entry.promise, timeoutPromise]);
|
|
@@ -102,16 +87,9 @@ export async function waitForRun(
|
|
|
102
87
|
// projects (see issue #29). Without this, the hardcoded `.crew/state/runs/`
|
|
103
88
|
// path never resolves in projects that use the `.pi/` layout, the throw
|
|
104
89
|
// escapes via subagent-manager.ts:281, and pi crashes with uncaughtException.
|
|
105
|
-
const runDir = path.join(
|
|
106
|
-
projectCrewRoot(cwd),
|
|
107
|
-
"state",
|
|
108
|
-
"runs",
|
|
109
|
-
runId,
|
|
110
|
-
);
|
|
90
|
+
const runDir = path.join(projectCrewRoot(cwd), "state", "runs", runId);
|
|
111
91
|
if (!fs.existsSync(runDir)) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Run ${runId} not found. No run directory at ${runDir}`,
|
|
114
|
-
);
|
|
92
|
+
throw new Error(`Run ${runId} not found. No run directory at ${runDir}`);
|
|
115
93
|
}
|
|
116
94
|
}
|
|
117
95
|
const fresh = loadRunManifestById(cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
|
|
@@ -22,12 +22,7 @@ export function resolveTaskRuntimeKind(
|
|
|
22
22
|
// Exception: when PI_CREW_MOCK_LIVE_SESSION is set, we're in a test harness
|
|
23
23
|
// that mocks the live-session path — forcing child-process would spawn a real
|
|
24
24
|
// pi process and hang the test.
|
|
25
|
-
if (
|
|
26
|
-
globalKind === "live-session" &&
|
|
27
|
-
currentCrewDepth(env) > 0 &&
|
|
28
|
-
env.PI_CREW_MOCK_LIVE_SESSION !== "success"
|
|
29
|
-
)
|
|
30
|
-
return "child-process";
|
|
25
|
+
if (globalKind === "live-session" && currentCrewDepth(env) > 0 && env.PI_CREW_MOCK_LIVE_SESSION !== "success") return "child-process";
|
|
31
26
|
const isolatedRoles = isolationPolicy?.isolatedRoles ?? [];
|
|
32
27
|
if (isolatedRoles.includes(role)) return "child-process";
|
|
33
28
|
return isolationPolicy?.defaultRuntime ?? globalKind;
|