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
|
@@ -7,15 +7,7 @@ import { logInternalError } from "../utils/internal-error.ts";
|
|
|
7
7
|
import { projectCrewRoot } from "../utils/paths.ts";
|
|
8
8
|
import { redactSecrets } from "../utils/redaction.ts";
|
|
9
9
|
|
|
10
|
-
export type SubagentStatus =
|
|
11
|
-
| "queued"
|
|
12
|
-
| "running"
|
|
13
|
-
| "completed"
|
|
14
|
-
| "failed"
|
|
15
|
-
| "cancelled"
|
|
16
|
-
| "error"
|
|
17
|
-
| "blocked"
|
|
18
|
-
| "stopped";
|
|
10
|
+
export type SubagentStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "error" | "blocked" | "stopped";
|
|
19
11
|
|
|
20
12
|
export interface SubagentSpawnOptions {
|
|
21
13
|
cwd: string;
|
|
@@ -61,10 +53,7 @@ export interface SubagentRecord {
|
|
|
61
53
|
lifetimeUsage?: { input: number; output: number; cacheWrite: number };
|
|
62
54
|
}
|
|
63
55
|
|
|
64
|
-
type SpawnRunner = (
|
|
65
|
-
options: SubagentSpawnOptions,
|
|
66
|
-
signal?: AbortSignal,
|
|
67
|
-
) => Promise<PiTeamsToolResult>;
|
|
56
|
+
type SpawnRunner = (options: SubagentSpawnOptions, signal?: AbortSignal) => Promise<PiTeamsToolResult>;
|
|
68
57
|
type Notify = (record: SubagentRecord) => void;
|
|
69
58
|
type NotifyEvent = (type: string, data: Record<string, unknown>) => void;
|
|
70
59
|
|
|
@@ -81,11 +70,7 @@ function isValidSubagentId(id: string): boolean {
|
|
|
81
70
|
|
|
82
71
|
function persistedSubagentPath(cwd: string, id: string): string {
|
|
83
72
|
if (!isValidSubagentId(id)) throw new Error(`Invalid subagent id: ${id}`);
|
|
84
|
-
return path.join(
|
|
85
|
-
projectCrewRoot(cwd),
|
|
86
|
-
DEFAULT_PATHS.state.subagentsSubdir,
|
|
87
|
-
`${id}.json`,
|
|
88
|
-
);
|
|
73
|
+
return path.join(projectCrewRoot(cwd), DEFAULT_PATHS.state.subagentsSubdir, `${id}.json`);
|
|
89
74
|
}
|
|
90
75
|
|
|
91
76
|
function serializableRecord(record: SubagentRecord): SubagentRecord {
|
|
@@ -93,18 +78,11 @@ function serializableRecord(record: SubagentRecord): SubagentRecord {
|
|
|
93
78
|
return rest;
|
|
94
79
|
}
|
|
95
80
|
|
|
96
|
-
export function savePersistedSubagentRecord(
|
|
97
|
-
cwd: string,
|
|
98
|
-
record: SubagentRecord,
|
|
99
|
-
): void {
|
|
81
|
+
export function savePersistedSubagentRecord(cwd: string, record: SubagentRecord): void {
|
|
100
82
|
try {
|
|
101
83
|
const filePath = persistedSubagentPath(cwd, record.id);
|
|
102
84
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
103
|
-
fs.writeFileSync(
|
|
104
|
-
filePath,
|
|
105
|
-
`${JSON.stringify(redactSecrets(serializableRecord(record)), null, 2)}\n`,
|
|
106
|
-
"utf-8",
|
|
107
|
-
);
|
|
85
|
+
fs.writeFileSync(filePath, `${JSON.stringify(redactSecrets(serializableRecord(record)), null, 2)}\n`, "utf-8");
|
|
108
86
|
// SECURITY: Restrict permissions to owner-only (rw-------).
|
|
109
87
|
// On multi-user systems, other users must not read task prompts,
|
|
110
88
|
// agent descriptions, and run IDs from subagent record files.
|
|
@@ -186,9 +164,7 @@ function sanitizePersistedRecord(raw: unknown): SubagentRecord | undefined {
|
|
|
186
164
|
for (const key of Object.keys(obj)) {
|
|
187
165
|
if (
|
|
188
166
|
ALLOWED_RECORD_FIELDS.has(key) &&
|
|
189
|
-
(typeof obj[key] === "string" ||
|
|
190
|
-
typeof obj[key] === "number" ||
|
|
191
|
-
typeof obj[key] === "boolean")
|
|
167
|
+
(typeof obj[key] === "string" || typeof obj[key] === "number" || typeof obj[key] === "boolean")
|
|
192
168
|
) {
|
|
193
169
|
clean[key] = obj[key];
|
|
194
170
|
}
|
|
@@ -198,14 +174,9 @@ function sanitizePersistedRecord(raw: unknown): SubagentRecord | undefined {
|
|
|
198
174
|
return clean as unknown as SubagentRecord;
|
|
199
175
|
}
|
|
200
176
|
|
|
201
|
-
export function readPersistedSubagentRecord(
|
|
202
|
-
cwd: string,
|
|
203
|
-
id: string,
|
|
204
|
-
): SubagentRecord | undefined {
|
|
177
|
+
export function readPersistedSubagentRecord(cwd: string, id: string): SubagentRecord | undefined {
|
|
205
178
|
try {
|
|
206
|
-
const raw = JSON.parse(
|
|
207
|
-
fs.readFileSync(persistedSubagentPath(cwd, id), "utf-8"),
|
|
208
|
-
);
|
|
179
|
+
const raw = JSON.parse(fs.readFileSync(persistedSubagentPath(cwd, id), "utf-8"));
|
|
209
180
|
return sanitizePersistedRecord(raw);
|
|
210
181
|
} catch {
|
|
211
182
|
return undefined;
|
|
@@ -226,10 +197,7 @@ function detailsRunId(result: PiTeamsToolResult): string | undefined {
|
|
|
226
197
|
return typeof details?.runId === "string" ? details.runId : undefined;
|
|
227
198
|
}
|
|
228
199
|
|
|
229
|
-
function totalRunTurns(
|
|
230
|
-
cwd: string,
|
|
231
|
-
runId: string | undefined,
|
|
232
|
-
): number | undefined {
|
|
200
|
+
function totalRunTurns(cwd: string, runId: string | undefined): number | undefined {
|
|
233
201
|
if (!runId) return undefined;
|
|
234
202
|
const loaded = loadRunManifestById(cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
|
|
235
203
|
if (!loaded) return undefined;
|
|
@@ -258,33 +226,20 @@ export class SubagentManager {
|
|
|
258
226
|
private readonly onEvent?: NotifyEvent;
|
|
259
227
|
private readonly pollIntervalMs: number;
|
|
260
228
|
|
|
261
|
-
constructor(
|
|
262
|
-
maxConcurrent = 4,
|
|
263
|
-
onComplete?: Notify,
|
|
264
|
-
pollIntervalMs = 1000,
|
|
265
|
-
onEvent?: NotifyEvent,
|
|
266
|
-
) {
|
|
229
|
+
constructor(maxConcurrent = 4, onComplete?: Notify, pollIntervalMs = 1000, onEvent?: NotifyEvent) {
|
|
267
230
|
this.maxConcurrent = maxConcurrent;
|
|
268
231
|
this.onComplete = onComplete;
|
|
269
232
|
this.onEvent = onEvent;
|
|
270
233
|
this.pollIntervalMs = pollIntervalMs;
|
|
271
234
|
}
|
|
272
235
|
|
|
273
|
-
spawn(
|
|
274
|
-
options: SubagentSpawnOptions,
|
|
275
|
-
runner: SpawnRunner,
|
|
276
|
-
signal?: AbortSignal,
|
|
277
|
-
): SubagentRecord {
|
|
236
|
+
spawn(options: SubagentSpawnOptions, runner: SpawnRunner, signal?: AbortSignal): SubagentRecord {
|
|
278
237
|
const record: SubagentRecord = {
|
|
279
238
|
id: `agent_${Date.now().toString(36)}_${(++this.counter).toString(36)}`,
|
|
280
239
|
type: options.type,
|
|
281
240
|
description: options.description,
|
|
282
241
|
prompt: options.prompt,
|
|
283
|
-
status:
|
|
284
|
-
options.background &&
|
|
285
|
-
this.runningBackground >= this.maxConcurrent
|
|
286
|
-
? "queued"
|
|
287
|
-
: "running",
|
|
242
|
+
status: options.background && this.runningBackground >= this.maxConcurrent ? "queued" : "running",
|
|
288
243
|
startedAt: Date.now(),
|
|
289
244
|
model: options.model,
|
|
290
245
|
skill: options.skill,
|
|
@@ -308,9 +263,7 @@ export class SubagentManager {
|
|
|
308
263
|
}
|
|
309
264
|
|
|
310
265
|
listAgents(): SubagentRecord[] {
|
|
311
|
-
return [...this.records.values()].sort(
|
|
312
|
-
(a, b) => b.startedAt - a.startedAt,
|
|
313
|
-
);
|
|
266
|
+
return [...this.records.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
314
267
|
}
|
|
315
268
|
|
|
316
269
|
abort(id: string, reason?: string): boolean {
|
|
@@ -321,8 +274,7 @@ export class SubagentManager {
|
|
|
321
274
|
this.markStopped(record, reason ?? "Aborted by caller.");
|
|
322
275
|
return true;
|
|
323
276
|
}
|
|
324
|
-
if (record.status !== "running" && record.status !== "blocked")
|
|
325
|
-
return false;
|
|
277
|
+
if (record.status !== "running" && record.status !== "blocked") return false;
|
|
326
278
|
this.controllers.get(id)?.abort();
|
|
327
279
|
this.markStopped(record, reason ?? "Aborted by caller.");
|
|
328
280
|
return true;
|
|
@@ -350,15 +302,9 @@ export class SubagentManager {
|
|
|
350
302
|
while (true) {
|
|
351
303
|
this.drainQueue();
|
|
352
304
|
const pending = this.listAgents()
|
|
353
|
-
.filter(
|
|
354
|
-
(record) =>
|
|
355
|
-
record.status === "running" ||
|
|
356
|
-
record.status === "queued",
|
|
357
|
-
)
|
|
305
|
+
.filter((record) => record.status === "running" || record.status === "queued")
|
|
358
306
|
.map((record) => record.promise)
|
|
359
|
-
.filter((promise): promise is Promise<void> =>
|
|
360
|
-
Boolean(promise),
|
|
361
|
-
);
|
|
307
|
+
.filter((promise): promise is Promise<void> => Boolean(promise));
|
|
362
308
|
if (!pending.length) break;
|
|
363
309
|
await Promise.allSettled(pending);
|
|
364
310
|
}
|
|
@@ -368,15 +314,10 @@ export class SubagentManager {
|
|
|
368
314
|
while (true) {
|
|
369
315
|
const record = this.records.get(id);
|
|
370
316
|
if (!record) return undefined;
|
|
371
|
-
if (record.status !== "running" && record.status !== "queued")
|
|
372
|
-
return record;
|
|
317
|
+
if (record.status !== "running" && record.status !== "queued") return record;
|
|
373
318
|
if (record.promise)
|
|
374
319
|
await record.promise.catch((error) => {
|
|
375
|
-
logInternalError(
|
|
376
|
-
"subagent-manager.waitForRecord",
|
|
377
|
-
error,
|
|
378
|
-
`id=${id}`,
|
|
379
|
-
);
|
|
320
|
+
logInternalError("subagent-manager.waitForRecord", error, `id=${id}`);
|
|
380
321
|
});
|
|
381
322
|
else await new Promise((resolve) => setTimeout(resolve, 100));
|
|
382
323
|
}
|
|
@@ -387,12 +328,7 @@ export class SubagentManager {
|
|
|
387
328
|
this.drainQueue();
|
|
388
329
|
}
|
|
389
330
|
|
|
390
|
-
private start(
|
|
391
|
-
record: SubagentRecord,
|
|
392
|
-
options: SubagentSpawnOptions,
|
|
393
|
-
runner: SpawnRunner,
|
|
394
|
-
signal?: AbortSignal,
|
|
395
|
-
): void {
|
|
331
|
+
private start(record: SubagentRecord, options: SubagentSpawnOptions, runner: SpawnRunner, signal?: AbortSignal): void {
|
|
396
332
|
if (options.background) this.runningBackground++;
|
|
397
333
|
record.status = "running";
|
|
398
334
|
record.startedAt = Date.now();
|
|
@@ -411,8 +347,7 @@ export class SubagentManager {
|
|
|
411
347
|
record.error = record.result;
|
|
412
348
|
throw new Error(record.error);
|
|
413
349
|
}
|
|
414
|
-
if (record.runId)
|
|
415
|
-
await this.pollRunToTerminal(options.cwd, record);
|
|
350
|
+
if (record.runId) await this.pollRunToTerminal(options.cwd, record);
|
|
416
351
|
else record.status = "completed";
|
|
417
352
|
} catch (error) {
|
|
418
353
|
if (record.status === "stopped" || runSignal.aborted) {
|
|
@@ -420,27 +355,16 @@ export class SubagentManager {
|
|
|
420
355
|
? "Signal aborted — agent cancelled by parent (session switch, user cancel, or tool timeout)."
|
|
421
356
|
: undefined;
|
|
422
357
|
record.status = "stopped";
|
|
423
|
-
if (!record.error)
|
|
424
|
-
record.error =
|
|
425
|
-
abortReason ??
|
|
426
|
-
(error instanceof Error
|
|
427
|
-
? error.message
|
|
428
|
-
: String(error));
|
|
358
|
+
if (!record.error) record.error = abortReason ?? (error instanceof Error ? error.message : String(error));
|
|
429
359
|
return;
|
|
430
360
|
}
|
|
431
361
|
record.status = "error";
|
|
432
|
-
record.error =
|
|
433
|
-
error instanceof Error ? error.message : String(error);
|
|
362
|
+
record.error = error instanceof Error ? error.message : String(error);
|
|
434
363
|
throw error; // H4: Propagate rejection so callers awaiting record.promise see the error
|
|
435
364
|
} finally {
|
|
436
365
|
this.cleanupRunSignal(record.id);
|
|
437
|
-
if (options.background)
|
|
438
|
-
|
|
439
|
-
0,
|
|
440
|
-
this.runningBackground - 1,
|
|
441
|
-
);
|
|
442
|
-
if (record.status !== "blocked")
|
|
443
|
-
record.completedAt = record.completedAt ?? Date.now();
|
|
366
|
+
if (options.background) this.runningBackground = Math.max(0, this.runningBackground - 1);
|
|
367
|
+
if (record.status !== "blocked") record.completedAt = record.completedAt ?? Date.now();
|
|
444
368
|
savePersistedSubagentRecord(options.cwd, record);
|
|
445
369
|
if (
|
|
446
370
|
record.status === "completed" ||
|
|
@@ -450,12 +374,8 @@ export class SubagentManager {
|
|
|
450
374
|
record.status === "stopped"
|
|
451
375
|
) {
|
|
452
376
|
// Phase 1.6: Populate telemetry fields
|
|
453
|
-
record.turnCount =
|
|
454
|
-
|
|
455
|
-
totalRunTurns(options.cwd, record.runId);
|
|
456
|
-
record.durationMs = record.completedAt
|
|
457
|
-
? Math.max(0, record.completedAt - record.startedAt)
|
|
458
|
-
: undefined;
|
|
377
|
+
record.turnCount = record.turnCount ?? totalRunTurns(options.cwd, record.runId);
|
|
378
|
+
record.durationMs = record.completedAt ? Math.max(0, record.completedAt - record.startedAt) : undefined;
|
|
459
379
|
savePersistedSubagentRecord(options.cwd, record);
|
|
460
380
|
// User policy (v0.9.16): cancelled / stopped subagents leave NO trace.
|
|
461
381
|
// Successful runs (`completed`) and agent errors (`failed`, `error`)
|
|
@@ -477,11 +397,7 @@ export class SubagentManager {
|
|
|
477
397
|
// DO await (they still see the rejection) while preventing the
|
|
478
398
|
// harness-killing failure mode for callers that don't.
|
|
479
399
|
record.promise.catch((error) => {
|
|
480
|
-
logInternalError(
|
|
481
|
-
"subagent-manager.start.unhandled",
|
|
482
|
-
error,
|
|
483
|
-
`id=${record.id}`,
|
|
484
|
-
);
|
|
400
|
+
logInternalError("subagent-manager.start.unhandled", error, `id=${record.id}`);
|
|
485
401
|
});
|
|
486
402
|
}
|
|
487
403
|
|
|
@@ -509,9 +425,7 @@ export class SubagentManager {
|
|
|
509
425
|
if (signal) {
|
|
510
426
|
const abort = (): void => controller.abort();
|
|
511
427
|
signal.addEventListener("abort", abort, { once: true });
|
|
512
|
-
this.controllerCleanup.set(id, () =>
|
|
513
|
-
signal.removeEventListener("abort", abort),
|
|
514
|
-
);
|
|
428
|
+
this.controllerCleanup.set(id, () => signal.removeEventListener("abort", abort));
|
|
515
429
|
}
|
|
516
430
|
return controller.signal;
|
|
517
431
|
}
|
|
@@ -523,48 +437,32 @@ export class SubagentManager {
|
|
|
523
437
|
}
|
|
524
438
|
|
|
525
439
|
private drainQueue(): void {
|
|
526
|
-
while (
|
|
527
|
-
this.queue.length > 0 &&
|
|
528
|
-
this.runningBackground < this.maxConcurrent
|
|
529
|
-
) {
|
|
440
|
+
while (this.queue.length > 0 && this.runningBackground < this.maxConcurrent) {
|
|
530
441
|
const next = this.queue.shift();
|
|
531
442
|
if (!next || next.record.status !== "queued") continue;
|
|
532
443
|
this.start(next.record, next.options, next.runner, next.signal);
|
|
533
444
|
}
|
|
534
445
|
}
|
|
535
446
|
|
|
536
|
-
private async pollRunToTerminal(
|
|
537
|
-
|
|
538
|
-
record: SubagentRecord,
|
|
539
|
-
): Promise<void> {
|
|
540
|
-
while (
|
|
541
|
-
record.runId &&
|
|
542
|
-
(record.status === "running" || record.status === "blocked")
|
|
543
|
-
) {
|
|
447
|
+
private async pollRunToTerminal(cwd: string, record: SubagentRecord): Promise<void> {
|
|
448
|
+
while (record.runId && (record.status === "running" || record.status === "blocked")) {
|
|
544
449
|
const loaded = loadRunManifestById(cwd, record.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
|
|
545
450
|
if (!loaded) {
|
|
546
|
-
await new Promise((resolve) =>
|
|
547
|
-
setTimeout(resolve, this.pollIntervalMs),
|
|
548
|
-
);
|
|
451
|
+
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
549
452
|
continue;
|
|
550
453
|
}
|
|
551
454
|
if (loaded.manifest.status === "completed") {
|
|
552
455
|
record.status = "completed";
|
|
553
456
|
record.error = undefined;
|
|
554
|
-
record.turnCount =
|
|
555
|
-
record.turnCount ?? totalRunTurns(cwd, record.runId);
|
|
457
|
+
record.turnCount = record.turnCount ?? totalRunTurns(cwd, record.runId);
|
|
556
458
|
record.completedAt = Date.now();
|
|
557
459
|
savePersistedSubagentRecord(cwd, record);
|
|
558
460
|
return;
|
|
559
461
|
}
|
|
560
|
-
if (
|
|
561
|
-
loaded.manifest.status === "failed" ||
|
|
562
|
-
loaded.manifest.status === "cancelled"
|
|
563
|
-
) {
|
|
462
|
+
if (loaded.manifest.status === "failed" || loaded.manifest.status === "cancelled") {
|
|
564
463
|
record.status = loaded.manifest.status;
|
|
565
464
|
record.error = loaded.manifest.summary;
|
|
566
|
-
record.turnCount =
|
|
567
|
-
record.turnCount ?? totalRunTurns(cwd, record.runId);
|
|
465
|
+
record.turnCount = record.turnCount ?? totalRunTurns(cwd, record.runId);
|
|
568
466
|
record.completedAt = Date.now();
|
|
569
467
|
savePersistedSubagentRecord(cwd, record);
|
|
570
468
|
return;
|
|
@@ -583,20 +481,14 @@ export class SubagentManager {
|
|
|
583
481
|
savePersistedSubagentRecord(cwd, record);
|
|
584
482
|
return;
|
|
585
483
|
}
|
|
586
|
-
await new Promise((resolve) =>
|
|
587
|
-
setTimeout(resolve, this.pollIntervalMs),
|
|
588
|
-
);
|
|
484
|
+
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
589
485
|
}
|
|
590
486
|
}
|
|
591
487
|
|
|
592
|
-
private scheduleBlockedTerminalPoll(
|
|
593
|
-
cwd: string,
|
|
594
|
-
record: SubagentRecord,
|
|
595
|
-
): void {
|
|
488
|
+
private scheduleBlockedTerminalPoll(cwd: string, record: SubagentRecord): void {
|
|
596
489
|
const poll = (): void => {
|
|
597
490
|
const current = this.records.get(record.id);
|
|
598
|
-
if (!current || current.status !== "blocked" || !current.runId)
|
|
599
|
-
return;
|
|
491
|
+
if (!current || current.status !== "blocked" || !current.runId) return;
|
|
600
492
|
const loaded = loadRunManifestById(cwd, current.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
|
|
601
493
|
if (
|
|
602
494
|
!loaded ||
|
|
@@ -610,25 +502,17 @@ export class SubagentManager {
|
|
|
610
502
|
return;
|
|
611
503
|
}
|
|
612
504
|
const persisted = readPersistedSubagentRecord(cwd, current.id);
|
|
613
|
-
current.resultConsumed =
|
|
614
|
-
current.resultConsumed || persisted?.resultConsumed;
|
|
505
|
+
current.resultConsumed = current.resultConsumed || persisted?.resultConsumed;
|
|
615
506
|
if (loaded.manifest.status === "completed") {
|
|
616
507
|
current.status = "completed";
|
|
617
508
|
current.error = undefined;
|
|
618
|
-
} else if (
|
|
619
|
-
loaded.manifest.status === "failed" ||
|
|
620
|
-
loaded.manifest.status === "cancelled"
|
|
621
|
-
) {
|
|
509
|
+
} else if (loaded.manifest.status === "failed" || loaded.manifest.status === "cancelled") {
|
|
622
510
|
current.status = loaded.manifest.status;
|
|
623
511
|
current.error = loaded.manifest.summary;
|
|
624
512
|
} else return;
|
|
625
513
|
current.completedAt = Date.now();
|
|
626
|
-
current.turnCount =
|
|
627
|
-
|
|
628
|
-
current.durationMs = Math.max(
|
|
629
|
-
0,
|
|
630
|
-
current.completedAt - current.startedAt,
|
|
631
|
-
);
|
|
514
|
+
current.turnCount = current.turnCount ?? totalRunTurns(cwd, current.runId);
|
|
515
|
+
current.durationMs = Math.max(0, current.completedAt - current.startedAt);
|
|
632
516
|
savePersistedSubagentRecord(cwd, current);
|
|
633
517
|
this.onComplete?.(current);
|
|
634
518
|
};
|
|
@@ -636,20 +520,11 @@ export class SubagentManager {
|
|
|
636
520
|
timer.unref();
|
|
637
521
|
}
|
|
638
522
|
|
|
639
|
-
private scheduleStuckBlockedNotify(
|
|
640
|
-
cwd: string,
|
|
641
|
-
record: SubagentRecord,
|
|
642
|
-
): void {
|
|
523
|
+
private scheduleStuckBlockedNotify(cwd: string, record: SubagentRecord): void {
|
|
643
524
|
const threshold = DEFAULT_SUBAGENT.stuckBlockedNotifyMs;
|
|
644
525
|
const fire = (): void => {
|
|
645
526
|
const current = this.records.get(record.id);
|
|
646
|
-
if (
|
|
647
|
-
!current ||
|
|
648
|
-
current.status !== "blocked" ||
|
|
649
|
-
!current.blockedAt ||
|
|
650
|
-
current.stuckNotified
|
|
651
|
-
)
|
|
652
|
-
return;
|
|
527
|
+
if (!current || current.status !== "blocked" || !current.blockedAt || current.stuckNotified) return;
|
|
653
528
|
current.stuckNotified = true;
|
|
654
529
|
this.onEvent?.("subagent.stuck-blocked", {
|
|
655
530
|
event: "subagent.stuck-blocked",
|
|
@@ -2,7 +2,10 @@ export interface SubprocessToolEvent {
|
|
|
2
2
|
toolName: string;
|
|
3
3
|
toolCallId: string;
|
|
4
4
|
args?: Record<string, unknown>;
|
|
5
|
-
result?: {
|
|
5
|
+
result?: {
|
|
6
|
+
content: Array<{ type: string; text?: string }>;
|
|
7
|
+
details?: unknown;
|
|
8
|
+
};
|
|
6
9
|
isError?: boolean;
|
|
7
10
|
}
|
|
8
11
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { TeamRunManifest } from "../state/types.ts";
|
|
2
1
|
import { appendEvent } from "../state/event-log.ts";
|
|
2
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
3
3
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
4
4
|
|
|
5
5
|
export interface SupervisorContactPayload {
|
|
@@ -50,10 +50,15 @@ export function parseSupervisorContactFromLine(line: string): Omit<SupervisorCon
|
|
|
50
50
|
if (record.type !== "supervisor_contact" && record.type !== "crew_supervisor_contact") return undefined;
|
|
51
51
|
return {
|
|
52
52
|
taskId: typeof record.taskId === "string" ? record.taskId : "",
|
|
53
|
-
reason:
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
reason:
|
|
54
|
+
typeof record.reason === "string" &&
|
|
55
|
+
["decision_needed", "clarification", "approval", "error_escalation", "custom"].includes(record.reason)
|
|
56
|
+
? (record.reason as SupervisorContactPayload["reason"])
|
|
57
|
+
: "custom",
|
|
56
58
|
message: typeof record.message === "string" ? record.message : String(record.message ?? ""),
|
|
57
|
-
data:
|
|
59
|
+
data:
|
|
60
|
+
record.data && typeof record.data === "object" && !Array.isArray(record.data)
|
|
61
|
+
? (record.data as Record<string, unknown>)
|
|
62
|
+
: undefined,
|
|
58
63
|
};
|
|
59
64
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import type { TeamTaskState } from "../state/types.ts";
|
|
2
|
-
import type { CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
|
|
1
|
+
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
3
2
|
import { recordFromTask } from "./crew-agent-records.ts";
|
|
4
|
-
import type {
|
|
3
|
+
import type { CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
|
|
5
4
|
|
|
6
5
|
export function shouldMaterializeAgent(task: TeamTaskState): boolean {
|
|
7
6
|
return task.status !== "queued" && task.status !== "skipped";
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
export function recordsForMaterializedTasks(
|
|
9
|
+
export function recordsForMaterializedTasks(
|
|
10
|
+
manifest: TeamRunManifest,
|
|
11
|
+
tasks: TeamTaskState[],
|
|
12
|
+
runtime: CrewRuntimeKind,
|
|
13
|
+
): CrewAgentRecord[] {
|
|
11
14
|
return tasks.filter(shouldMaterializeAgent).map((task) => recordFromTask(manifest, task, runtime));
|
|
12
15
|
}
|
|
13
16
|
|
|
@@ -31,7 +34,18 @@ export function waitingReason(task: TeamTaskState, tasks: TeamTaskState[]): stri
|
|
|
31
34
|
export function formatTaskGraphLines(tasks: TeamTaskState[]): string[] {
|
|
32
35
|
if (tasks.length === 0) return ["- (none)"];
|
|
33
36
|
return tasks.map((task) => {
|
|
34
|
-
const icon =
|
|
37
|
+
const icon =
|
|
38
|
+
task.status === "completed"
|
|
39
|
+
? "✓"
|
|
40
|
+
: task.status === "running"
|
|
41
|
+
? "⠋"
|
|
42
|
+
: task.status === "failed"
|
|
43
|
+
? "✗"
|
|
44
|
+
: task.status === "cancelled" || task.status === "skipped"
|
|
45
|
+
? "■"
|
|
46
|
+
: task.status === "needs_attention"
|
|
47
|
+
? "⚠"
|
|
48
|
+
: "◦";
|
|
35
49
|
const wait = waitingReason(task, tasks);
|
|
36
50
|
return `- ${icon} ${task.id} [${task.status}] ${task.role}->${task.agent}${wait && wait !== "ready" ? ` (${wait})` : ""}`;
|
|
37
51
|
});
|
|
@@ -17,9 +17,16 @@ export interface TaskGraphIndex {
|
|
|
17
17
|
|
|
18
18
|
export function buildTaskGraphIndex(tasks: TeamTaskState[]): TaskGraphIndex {
|
|
19
19
|
return {
|
|
20
|
-
doneSteps: new Set(
|
|
20
|
+
doneSteps: new Set(
|
|
21
|
+
tasks
|
|
22
|
+
.filter((task) => task.status === "completed")
|
|
23
|
+
.map((task) => task.stepId)
|
|
24
|
+
.filter((id): id is string => id !== undefined),
|
|
25
|
+
),
|
|
21
26
|
idMap: new Map(tasks.map((task) => [task.id, task])),
|
|
22
|
-
stepToTaskId: new Map(
|
|
27
|
+
stepToTaskId: new Map(
|
|
28
|
+
tasks.map((task) => [task.stepId, task.id]).filter((entry): entry is [string, string] => entry[0] !== undefined),
|
|
29
|
+
),
|
|
23
30
|
};
|
|
24
31
|
}
|
|
25
32
|
|
|
@@ -27,7 +34,12 @@ function taskById(tasks: TeamTaskState[]): Map<string, TeamTaskState> {
|
|
|
27
34
|
return new Map(tasks.map((task) => [task.id, task]));
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
function dependencySatisfied(
|
|
37
|
+
function dependencySatisfied(
|
|
38
|
+
task: TeamTaskState,
|
|
39
|
+
doneStepIds: Set<string>,
|
|
40
|
+
idMap: Map<string, TeamTaskState>,
|
|
41
|
+
stepMap: Map<string, string>,
|
|
42
|
+
): boolean {
|
|
31
43
|
return task.dependsOn.every((dependency) => {
|
|
32
44
|
if (doneStepIds.has(dependency)) return true;
|
|
33
45
|
const taskId = stepMap.get(dependency) ?? dependency;
|
|
@@ -38,15 +50,27 @@ function dependencySatisfied(task: TeamTaskState, doneStepIds: Set<string>, idMa
|
|
|
38
50
|
function withQueue(task: TeamTaskState, index: TaskGraphIndex): TeamTaskState {
|
|
39
51
|
if (task.status === "queued") {
|
|
40
52
|
const isReady = dependencySatisfied(task, index.doneSteps, index.idMap, index.stepToTaskId);
|
|
41
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
...task,
|
|
55
|
+
graph: task.graph ? { ...task.graph, queue: isReady ? "ready" : "blocked" } : task.graph,
|
|
56
|
+
};
|
|
42
57
|
}
|
|
43
58
|
if (task.status === "running") {
|
|
44
|
-
return {
|
|
59
|
+
return {
|
|
60
|
+
...task,
|
|
61
|
+
graph: task.graph ? { ...task.graph, queue: "running" } : task.graph,
|
|
62
|
+
};
|
|
45
63
|
}
|
|
46
64
|
if (task.status === "completed" || task.status === "skipped" || task.status === "needs_attention") {
|
|
47
|
-
return {
|
|
65
|
+
return {
|
|
66
|
+
...task,
|
|
67
|
+
graph: task.graph ? { ...task.graph, queue: "done" } : task.graph,
|
|
68
|
+
};
|
|
48
69
|
}
|
|
49
|
-
return {
|
|
70
|
+
return {
|
|
71
|
+
...task,
|
|
72
|
+
graph: task.graph ? { ...task.graph, queue: "blocked" } : task.graph,
|
|
73
|
+
};
|
|
50
74
|
}
|
|
51
75
|
|
|
52
76
|
function ensureIndex(tasks: TeamTaskState[], index?: TaskGraphIndex): TaskGraphIndex {
|
|
@@ -59,20 +83,49 @@ export function refreshTaskGraphQueues(tasks: TeamTaskState[], index?: TaskGraph
|
|
|
59
83
|
}
|
|
60
84
|
|
|
61
85
|
export function getReadyTasks(tasks: TeamTaskState[], maxCount = 1, index?: TaskGraphIndex): TeamTaskState[] {
|
|
62
|
-
return refreshTaskGraphQueues(tasks, index)
|
|
86
|
+
return refreshTaskGraphQueues(tasks, index)
|
|
87
|
+
.filter((task) => task.status === "queued" && task.graph?.queue === "ready")
|
|
88
|
+
.slice(0, Math.max(0, maxCount));
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
export function markTaskRunning(tasks: TeamTaskState[], taskId: string, now = new Date(), index?: TaskGraphIndex): TeamTaskState[] {
|
|
66
92
|
const resolved = ensureIndex(tasks, index);
|
|
67
|
-
return refreshTaskGraphQueues(tasks, resolved).map((task) =>
|
|
93
|
+
return refreshTaskGraphQueues(tasks, resolved).map((task) =>
|
|
94
|
+
task.id === taskId
|
|
95
|
+
? withQueue(
|
|
96
|
+
{
|
|
97
|
+
...task,
|
|
98
|
+
status: "running",
|
|
99
|
+
startedAt: task.startedAt ?? now.toISOString(),
|
|
100
|
+
},
|
|
101
|
+
resolved,
|
|
102
|
+
)
|
|
103
|
+
: task,
|
|
104
|
+
);
|
|
68
105
|
}
|
|
69
106
|
|
|
70
107
|
export function markTaskDone(tasks: TeamTaskState[], taskId: string, now = new Date(), index?: TaskGraphIndex): TeamTaskState[] {
|
|
71
108
|
const resolved = ensureIndex(tasks, index);
|
|
72
|
-
return refreshTaskGraphQueues(
|
|
109
|
+
return refreshTaskGraphQueues(
|
|
110
|
+
tasks.map((task) =>
|
|
111
|
+
task.id === taskId
|
|
112
|
+
? {
|
|
113
|
+
...task,
|
|
114
|
+
status: "completed",
|
|
115
|
+
finishedAt: task.finishedAt ?? now.toISOString(),
|
|
116
|
+
}
|
|
117
|
+
: task,
|
|
118
|
+
),
|
|
119
|
+
resolved,
|
|
120
|
+
);
|
|
73
121
|
}
|
|
74
122
|
|
|
75
|
-
export function cancelTaskSubtree(
|
|
123
|
+
export function cancelTaskSubtree(
|
|
124
|
+
tasks: TeamTaskState[],
|
|
125
|
+
rootTaskId: string,
|
|
126
|
+
reason = "Cancelled by task graph scheduler.",
|
|
127
|
+
now = new Date(),
|
|
128
|
+
): TeamTaskState[] {
|
|
76
129
|
const ids = taskById(tasks);
|
|
77
130
|
const toCancel = new Set<string>();
|
|
78
131
|
const stack = [rootTaskId];
|
|
@@ -83,11 +136,18 @@ export function cancelTaskSubtree(tasks: TeamTaskState[], rootTaskId: string, re
|
|
|
83
136
|
const task = ids.get(current);
|
|
84
137
|
for (const child of task?.graph?.children ?? []) stack.push(child);
|
|
85
138
|
}
|
|
86
|
-
return refreshTaskGraphQueues(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
139
|
+
return refreshTaskGraphQueues(
|
|
140
|
+
tasks.map((task) => {
|
|
141
|
+
if (!toCancel.has(task.id)) return task;
|
|
142
|
+
if (task.status === "completed") return task;
|
|
143
|
+
return {
|
|
144
|
+
...task,
|
|
145
|
+
status: "cancelled",
|
|
146
|
+
error: reason,
|
|
147
|
+
finishedAt: task.finishedAt ?? now.toISOString(),
|
|
148
|
+
};
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
91
151
|
}
|
|
92
152
|
|
|
93
153
|
export function failTaskAndBlockChildren(tasks: TeamTaskState[], rootTaskId: string, reason: string, now = new Date()): TeamTaskState[] {
|
|
@@ -102,11 +162,25 @@ export function failTaskAndBlockChildren(tasks: TeamTaskState[], rootTaskId: str
|
|
|
102
162
|
const task = ids.get(current);
|
|
103
163
|
for (const child of task?.graph?.children ?? []) stack.push(child);
|
|
104
164
|
}
|
|
105
|
-
return refreshTaskGraphQueues(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
165
|
+
return refreshTaskGraphQueues(
|
|
166
|
+
tasks.map((task) => {
|
|
167
|
+
if (task.id === rootTaskId)
|
|
168
|
+
return {
|
|
169
|
+
...task,
|
|
170
|
+
status: "failed",
|
|
171
|
+
error: reason,
|
|
172
|
+
finishedAt: task.finishedAt ?? now.toISOString(),
|
|
173
|
+
};
|
|
174
|
+
if (blocked.has(task.id) && task.status === "queued")
|
|
175
|
+
return {
|
|
176
|
+
...task,
|
|
177
|
+
status: "skipped",
|
|
178
|
+
error: `Blocked by failed task '${rootTaskId}'.`,
|
|
179
|
+
finishedAt: task.finishedAt ?? now.toISOString(),
|
|
180
|
+
};
|
|
181
|
+
return task;
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
110
184
|
}
|
|
111
185
|
|
|
112
186
|
export function taskGraphSnapshot(tasks: TeamTaskState[], index?: TaskGraphIndex): TaskGraphSchedulerSnapshot {
|