pi-crew 0.9.16 → 0.9.18
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 +130 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21784 -0
- package/dist/index.mjs +83169 -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/migration/atomic-write-v2-migration.md +297 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/perf/performance-review-2026-07.md +287 -0
- 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 +303 -591
- 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 +149 -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 +39 -24
- 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 +344 -79
- 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 +187 -59
- 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 +13 -31
- 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 +10 -18
- 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 +2 -6
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +27 -51
- 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 +12 -19
- 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 +50 -14
- 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 +2 -7
- 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 +106 -27
- 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 +137 -47
- 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/terminal-status.ts +1 -3
- 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 +46 -29
- 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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
5
4
|
import { errors } from "../errors.ts";
|
|
5
|
+
import { saveRunManifest } from "../state/state-store.ts";
|
|
6
|
+
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
6
7
|
import { recordFromTask, upsertCrewAgent } from "./crew-agent-records.ts";
|
|
7
8
|
import { checkProcessLiveness } from "./process-status.ts";
|
|
8
|
-
import { saveRunManifest } from "../state/state-store.ts";
|
|
9
9
|
|
|
10
10
|
/** Age threshold for orphaned temp directory cleanup: 1 hour. */
|
|
11
11
|
const ORPHAN_TEMP_DIR_AGE_THRESHOLD_MS = 60 * 60 * 1000;
|
|
@@ -22,13 +22,7 @@ const ORPHAN_TEMP_SCAN_BATCH_SIZE = 50;
|
|
|
22
22
|
export interface ReconcileResult {
|
|
23
23
|
runId: string;
|
|
24
24
|
/** What was found and what action was taken */
|
|
25
|
-
verdict:
|
|
26
|
-
| "healthy"
|
|
27
|
-
| "blocked_awaiting_approval"
|
|
28
|
-
| "result_exists"
|
|
29
|
-
| "pid_dead"
|
|
30
|
-
| "pid_alive_stale"
|
|
31
|
-
| "no_status";
|
|
25
|
+
verdict: "healthy" | "blocked_awaiting_approval" | "result_exists" | "pid_dead" | "pid_alive_stale" | "no_status";
|
|
32
26
|
/** Whether repair was applied */
|
|
33
27
|
repaired: boolean;
|
|
34
28
|
/** Human-readable detail */
|
|
@@ -47,11 +41,7 @@ export interface ReconcileResult {
|
|
|
47
41
|
* original analysis of this failure mode.
|
|
48
42
|
*/
|
|
49
43
|
export function isPlanApprovalPending(manifest: TeamRunManifest): boolean {
|
|
50
|
-
return
|
|
51
|
-
manifest.status === "blocked" &&
|
|
52
|
-
manifest.planApproval?.required === true &&
|
|
53
|
-
manifest.planApproval.status === "pending"
|
|
54
|
-
);
|
|
44
|
+
return manifest.status === "blocked" && manifest.planApproval?.required === true && manifest.planApproval.status === "pending";
|
|
55
45
|
}
|
|
56
46
|
|
|
57
47
|
const STALE_ALIVE_PID_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
@@ -63,10 +53,7 @@ const NO_PID_HEARTBEAT_STALE_MS = 5 * 60 * 1000; // 5 minutes — same as heartb
|
|
|
63
53
|
* Phase 1: Check if a result file already exists for the run.
|
|
64
54
|
* If so, the run completed but status wasn't updated — repair it.
|
|
65
55
|
*/
|
|
66
|
-
function checkResultFile(
|
|
67
|
-
manifest: TeamRunManifest,
|
|
68
|
-
tasks: TeamTaskState[],
|
|
69
|
-
): { found: boolean; repaired: boolean } {
|
|
56
|
+
function checkResultFile(manifest: TeamRunManifest, tasks: TeamTaskState[]): { found: boolean; repaired: boolean } {
|
|
70
57
|
// Check if all tasks already have terminal status (result was written but manifest wasn't updated)
|
|
71
58
|
const allTerminal =
|
|
72
59
|
tasks.length > 0 &&
|
|
@@ -87,10 +74,7 @@ function checkResultFile(
|
|
|
87
74
|
// (e.g., a previous reconcile fixed tasks but crashed before updating agents)
|
|
88
75
|
for (const task of tasks) {
|
|
89
76
|
try {
|
|
90
|
-
upsertCrewAgent(
|
|
91
|
-
manifest,
|
|
92
|
-
recordFromTask(manifest, task, "scaffold"),
|
|
93
|
-
);
|
|
77
|
+
upsertCrewAgent(manifest, recordFromTask(manifest, task, "scaffold"));
|
|
94
78
|
} catch {
|
|
95
79
|
/* non-critical */
|
|
96
80
|
}
|
|
@@ -114,7 +98,10 @@ function getProcessStartTime(pid: number): number | undefined {
|
|
|
114
98
|
const stat = fs.readFileSync(`/proc/${pid}/stat`, "utf-8");
|
|
115
99
|
const lastParen = stat.lastIndexOf(")");
|
|
116
100
|
if (lastParen === -1) return undefined;
|
|
117
|
-
const fieldsAfterComm = stat
|
|
101
|
+
const fieldsAfterComm = stat
|
|
102
|
+
.slice(lastParen + 1)
|
|
103
|
+
.trim()
|
|
104
|
+
.split(/\s+/);
|
|
118
105
|
// starttime is at index 19 (the 20th field after comm)
|
|
119
106
|
const startTimeClockTicks = Number(fieldsAfterComm[19]);
|
|
120
107
|
if (!Number.isFinite(startTimeClockTicks)) return undefined;
|
|
@@ -151,11 +138,7 @@ function checkPidLiveness(
|
|
|
151
138
|
// Re-verify startTime after kill(0) to close the TOCTOU window.
|
|
152
139
|
// If startTime changed, the PID was recycled to a different process.
|
|
153
140
|
const startTimeAfter = getProcessStartTime(pid);
|
|
154
|
-
if (
|
|
155
|
-
startTimeBefore !== undefined &&
|
|
156
|
-
startTimeAfter !== undefined &&
|
|
157
|
-
startTimeBefore !== startTimeAfter
|
|
158
|
-
) {
|
|
141
|
+
if (startTimeBefore !== undefined && startTimeAfter !== undefined && startTimeBefore !== startTimeAfter) {
|
|
159
142
|
// PID was recycled — treat as dead to avoid acting on wrong process.
|
|
160
143
|
return { alive: false, detail: "pid_recycled" };
|
|
161
144
|
}
|
|
@@ -166,9 +149,7 @@ function checkPidLiveness(
|
|
|
166
149
|
const heartbeatPath = path.join(stateRoot, "heartbeat.json");
|
|
167
150
|
try {
|
|
168
151
|
if (fs.existsSync(heartbeatPath)) {
|
|
169
|
-
const hb = JSON.parse(
|
|
170
|
-
fs.readFileSync(heartbeatPath, "utf-8"),
|
|
171
|
-
) as { pid?: number; at?: number };
|
|
152
|
+
const hb = JSON.parse(fs.readFileSync(heartbeatPath, "utf-8")) as { pid?: number; at?: number };
|
|
172
153
|
if (hb?.pid === pid && hb?.at) {
|
|
173
154
|
const ageMs = Date.now() - hb.at;
|
|
174
155
|
// Heartbeat written < 5 min ago → process was alive recently.
|
|
@@ -192,11 +173,7 @@ function checkPidLiveness(
|
|
|
192
173
|
* Phase 3: For dead PIDs, repair immediately.
|
|
193
174
|
* For alive PIDs, only mark stale if status hasn't updated in STALE_ALIVE_PID_MS.
|
|
194
175
|
*/
|
|
195
|
-
function evaluateStaleness(
|
|
196
|
-
manifest: TeamRunManifest,
|
|
197
|
-
pidAlive: boolean,
|
|
198
|
-
now: number,
|
|
199
|
-
): { stale: boolean; reason: string } {
|
|
176
|
+
function evaluateStaleness(manifest: TeamRunManifest, pidAlive: boolean, now: number): { stale: boolean; reason: string } {
|
|
200
177
|
if (!pidAlive) {
|
|
201
178
|
return { stale: true, reason: "pid_dead" };
|
|
202
179
|
}
|
|
@@ -215,24 +192,11 @@ function evaluateStaleness(
|
|
|
215
192
|
|
|
216
193
|
function hasRecentActiveEvidence(tasks: TeamTaskState[], now: number): boolean {
|
|
217
194
|
return tasks.some((task) => {
|
|
218
|
-
if (task.status !== "running" && task.status !== "waiting")
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (
|
|
224
|
-
task.heartbeat?.alive !== false &&
|
|
225
|
-
Number.isFinite(heartbeatAt) &&
|
|
226
|
-
now - heartbeatAt <= ACTIVE_EVIDENCE_TTL_MS
|
|
227
|
-
)
|
|
228
|
-
return true;
|
|
229
|
-
const activityAt = task.agentProgress?.lastActivityAt
|
|
230
|
-
? new Date(task.agentProgress.lastActivityAt).getTime()
|
|
231
|
-
: Number.NaN;
|
|
232
|
-
return (
|
|
233
|
-
Number.isFinite(activityAt) &&
|
|
234
|
-
now - activityAt <= ACTIVE_EVIDENCE_TTL_MS
|
|
235
|
-
);
|
|
195
|
+
if (task.status !== "running" && task.status !== "waiting") return false;
|
|
196
|
+
const heartbeatAt = task.heartbeat?.lastSeenAt ? new Date(task.heartbeat.lastSeenAt).getTime() : Number.NaN;
|
|
197
|
+
if (task.heartbeat?.alive !== false && Number.isFinite(heartbeatAt) && now - heartbeatAt <= ACTIVE_EVIDENCE_TTL_MS) return true;
|
|
198
|
+
const activityAt = task.agentProgress?.lastActivityAt ? new Date(task.agentProgress.lastActivityAt).getTime() : Number.NaN;
|
|
199
|
+
return Number.isFinite(activityAt) && now - activityAt <= ACTIVE_EVIDENCE_TTL_MS;
|
|
236
200
|
});
|
|
237
201
|
}
|
|
238
202
|
|
|
@@ -242,15 +206,10 @@ function hasRecentActiveEvidence(tasks: TeamTaskState[], now: number): boolean {
|
|
|
242
206
|
* are considered NOT stale (they may be newly spawned and haven't reported yet).
|
|
243
207
|
*/
|
|
244
208
|
function isTaskHeartbeatStale(task: TeamTaskState, now: number): boolean {
|
|
245
|
-
const heartbeatAt = task.heartbeat?.lastSeenAt
|
|
246
|
-
|
|
247
|
-
: Number.NaN;
|
|
248
|
-
const activityAt = task.agentProgress?.lastActivityAt
|
|
249
|
-
? new Date(task.agentProgress.lastActivityAt).getTime()
|
|
250
|
-
: Number.NaN;
|
|
209
|
+
const heartbeatAt = task.heartbeat?.lastSeenAt ? new Date(task.heartbeat.lastSeenAt).getTime() : Number.NaN;
|
|
210
|
+
const activityAt = task.agentProgress?.lastActivityAt ? new Date(task.agentProgress.lastActivityAt).getTime() : Number.NaN;
|
|
251
211
|
// If no heartbeat AND no activity, we can't determine staleness — not stale
|
|
252
|
-
if (!Number.isFinite(heartbeatAt) && !Number.isFinite(activityAt))
|
|
253
|
-
return false;
|
|
212
|
+
if (!Number.isFinite(heartbeatAt) && !Number.isFinite(activityAt)) return false;
|
|
254
213
|
// Compute elapsed from both sources and use the fresher (minimum) one,
|
|
255
214
|
// mirroring heartbeat-watcher logic: if either source has recent activity,
|
|
256
215
|
// the task is not stale even if the other is stale.
|
|
@@ -268,13 +227,8 @@ function isTaskHeartbeatStale(task: TeamTaskState, now: number): boolean {
|
|
|
268
227
|
* Merged from the former allRunningTasksHeartbeatStale and
|
|
269
228
|
* findIndividuallyStaleTaskIds to avoid redundant duplicate checks.
|
|
270
229
|
*/
|
|
271
|
-
function getRunningTaskStaleness(
|
|
272
|
-
tasks
|
|
273
|
-
now: number,
|
|
274
|
-
): { allStale: boolean; staleTaskIds: string[] } {
|
|
275
|
-
const runningTasks = tasks.filter(
|
|
276
|
-
(t) => t.status === "running" || t.status === "waiting",
|
|
277
|
-
);
|
|
230
|
+
function getRunningTaskStaleness(tasks: TeamTaskState[], now: number): { allStale: boolean; staleTaskIds: string[] } {
|
|
231
|
+
const runningTasks = tasks.filter((t) => t.status === "running" || t.status === "waiting");
|
|
278
232
|
if (runningTasks.length === 0) return { allStale: false, staleTaskIds: [] };
|
|
279
233
|
const staleTaskIds: string[] = [];
|
|
280
234
|
for (const task of runningTasks) {
|
|
@@ -299,7 +253,9 @@ function getRunningTaskStaleness(
|
|
|
299
253
|
* readable code + help hint; `.message` carries the same rich text as before.
|
|
300
254
|
*/
|
|
301
255
|
function buildStaleReconcileError(task: TeamTaskState, reason: string): Error {
|
|
302
|
-
const heartbeatAgeSeconds = task.heartbeat?.lastSeenAt
|
|
256
|
+
const heartbeatAgeSeconds = task.heartbeat?.lastSeenAt
|
|
257
|
+
? Math.round((Date.now() - new Date(task.heartbeat.lastSeenAt).getTime()) / 1000)
|
|
258
|
+
: undefined;
|
|
303
259
|
return errors.runStale(reason, heartbeatAgeSeconds);
|
|
304
260
|
}
|
|
305
261
|
|
|
@@ -308,18 +264,10 @@ function formatStaleReconcileError(task: TeamTaskState, reason: string): string
|
|
|
308
264
|
return buildStaleReconcileError(task, reason).message;
|
|
309
265
|
}
|
|
310
266
|
|
|
311
|
-
function repairStaleRun(
|
|
312
|
-
manifest: TeamRunManifest,
|
|
313
|
-
tasks: TeamTaskState[],
|
|
314
|
-
reason: string,
|
|
315
|
-
): TeamTaskState[] {
|
|
267
|
+
function repairStaleRun(manifest: TeamRunManifest, tasks: TeamTaskState[], reason: string): TeamTaskState[] {
|
|
316
268
|
const now = new Date().toISOString();
|
|
317
269
|
const repairedTasks = tasks.map((task) => {
|
|
318
|
-
if (
|
|
319
|
-
task.status === "running" ||
|
|
320
|
-
task.status === "queued" ||
|
|
321
|
-
task.status === "waiting"
|
|
322
|
-
) {
|
|
270
|
+
if (task.status === "running" || task.status === "queued" || task.status === "waiting") {
|
|
323
271
|
return {
|
|
324
272
|
...task,
|
|
325
273
|
status: "cancelled" as const,
|
|
@@ -333,10 +281,7 @@ function repairStaleRun(
|
|
|
333
281
|
// Update agent records so widget sees cancelled status immediately
|
|
334
282
|
for (const task of repairedTasks) {
|
|
335
283
|
try {
|
|
336
|
-
upsertCrewAgent(
|
|
337
|
-
manifest,
|
|
338
|
-
recordFromTask(manifest, task, "scaffold"),
|
|
339
|
-
);
|
|
284
|
+
upsertCrewAgent(manifest, recordFromTask(manifest, task, "scaffold"));
|
|
340
285
|
} catch {
|
|
341
286
|
/* non-critical */
|
|
342
287
|
}
|
|
@@ -358,11 +303,7 @@ function repairStaleRun(
|
|
|
358
303
|
* /tmp workspaces where concurrent access is a known benign race (separate
|
|
359
304
|
* dirs, low consequence of redundant repair).
|
|
360
305
|
*/
|
|
361
|
-
export function reconcileStaleRun(
|
|
362
|
-
manifest: TeamRunManifest,
|
|
363
|
-
tasks: TeamTaskState[],
|
|
364
|
-
now = Date.now(),
|
|
365
|
-
): ReconcileResult {
|
|
306
|
+
export function reconcileStaleRun(manifest: TeamRunManifest, tasks: TeamTaskState[], now = Date.now()): ReconcileResult {
|
|
366
307
|
const runId = manifest.runId;
|
|
367
308
|
|
|
368
309
|
// Preserve runs intentionally blocked on human plan approval. These are not
|
|
@@ -414,11 +355,7 @@ export function reconcileStaleRun(
|
|
|
414
355
|
// Merged with individual stale task check to avoid redundant duplicate checks.
|
|
415
356
|
const { allStale, staleTaskIds } = getRunningTaskStaleness(tasks, now);
|
|
416
357
|
if (allStale) {
|
|
417
|
-
const repaired = repairStaleRun(
|
|
418
|
-
manifest,
|
|
419
|
-
tasks,
|
|
420
|
-
"no_pid_heartbeat_stale",
|
|
421
|
-
);
|
|
358
|
+
const repaired = repairStaleRun(manifest, tasks, "no_pid_heartbeat_stale");
|
|
422
359
|
return {
|
|
423
360
|
runId,
|
|
424
361
|
verdict: "no_status",
|
|
@@ -449,10 +386,7 @@ export function reconcileStaleRun(
|
|
|
449
386
|
// Fall through: no recent activity but not all tasks stale enough yet.
|
|
450
387
|
// Check the longer STALE_ALIVE_PID_MS threshold for very old runs.
|
|
451
388
|
const updatedAt = new Date(manifest.updatedAt).getTime();
|
|
452
|
-
if (
|
|
453
|
-
Number.isFinite(updatedAt) &&
|
|
454
|
-
now - updatedAt > STALE_ALIVE_PID_MS
|
|
455
|
-
) {
|
|
389
|
+
if (Number.isFinite(updatedAt) && now - updatedAt > STALE_ALIVE_PID_MS) {
|
|
456
390
|
const repaired = repairStaleRun(manifest, tasks, "no_pid_stale");
|
|
457
391
|
return {
|
|
458
392
|
runId,
|
|
@@ -515,7 +449,11 @@ export interface OrphanReconcileResult {
|
|
|
515
449
|
*/
|
|
516
450
|
export function reconcileOrphanedTempWorkspaces(
|
|
517
451
|
now = Date.now(),
|
|
518
|
-
options?: {
|
|
452
|
+
options?: {
|
|
453
|
+
cleanupOrphanedTempDirs?: boolean;
|
|
454
|
+
tmpDir?: string;
|
|
455
|
+
scanBatchSize?: number;
|
|
456
|
+
},
|
|
519
457
|
): OrphanReconcileResult {
|
|
520
458
|
// Injectable tmpDir + scanBatchSize for deterministic unit testing
|
|
521
459
|
// (Round 19: tests must not depend on global /tmp cleanliness; the
|
|
@@ -536,8 +474,7 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
536
474
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
537
475
|
.slice(0, scanBatch);
|
|
538
476
|
for (const entry of candidates) {
|
|
539
|
-
if (!entry.isDirectory() || !entry.name.startsWith("pi-crew-"))
|
|
540
|
-
continue;
|
|
477
|
+
if (!entry.isDirectory() || !entry.name.startsWith("pi-crew-")) continue;
|
|
541
478
|
const workspaceDir = path.join(tmpDir, entry.name);
|
|
542
479
|
const crewDir = path.join(workspaceDir, ".crew");
|
|
543
480
|
if (!fs.existsSync(crewDir)) continue;
|
|
@@ -546,36 +483,17 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
546
483
|
let hasRunning = false;
|
|
547
484
|
try {
|
|
548
485
|
for (const runDir of fs.readdirSync(stateRunsDir)) {
|
|
549
|
-
const manifestPath = path.join(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
"manifest.json",
|
|
553
|
-
);
|
|
554
|
-
const tasksPath = path.join(
|
|
555
|
-
stateRunsDir,
|
|
556
|
-
runDir,
|
|
557
|
-
"tasks.json",
|
|
558
|
-
);
|
|
559
|
-
if (
|
|
560
|
-
!fs.existsSync(manifestPath) ||
|
|
561
|
-
!fs.existsSync(tasksPath)
|
|
562
|
-
)
|
|
563
|
-
continue;
|
|
486
|
+
const manifestPath = path.join(stateRunsDir, runDir, "manifest.json");
|
|
487
|
+
const tasksPath = path.join(stateRunsDir, runDir, "tasks.json");
|
|
488
|
+
if (!fs.existsSync(manifestPath) || !fs.existsSync(tasksPath)) continue;
|
|
564
489
|
try {
|
|
565
|
-
const manifest: TeamRunManifest = JSON.parse(
|
|
566
|
-
fs.readFileSync(manifestPath, "utf-8"),
|
|
567
|
-
);
|
|
490
|
+
const manifest: TeamRunManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
568
491
|
if (manifest.status !== "running") continue;
|
|
569
|
-
const tasks: TeamTaskState[] = JSON.parse(
|
|
570
|
-
fs.readFileSync(tasksPath, "utf-8"),
|
|
571
|
-
);
|
|
492
|
+
const tasks: TeamTaskState[] = JSON.parse(fs.readFileSync(tasksPath, "utf-8"));
|
|
572
493
|
const result = reconcileStaleRun(manifest, tasks, now);
|
|
573
494
|
if (result.repaired && result.repairedTasks) {
|
|
574
495
|
// Persist repaired tasks
|
|
575
|
-
fs.writeFileSync(
|
|
576
|
-
tasksPath,
|
|
577
|
-
JSON.stringify(result.repairedTasks, null, 2),
|
|
578
|
-
);
|
|
496
|
+
fs.writeFileSync(tasksPath, JSON.stringify(result.repairedTasks, null, 2));
|
|
579
497
|
// Update manifest status
|
|
580
498
|
const updated = {
|
|
581
499
|
...manifest,
|
|
@@ -583,21 +501,11 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
583
501
|
updatedAt: new Date(now).toISOString(),
|
|
584
502
|
summary: `Stale run reconciled: ${result.detail}`,
|
|
585
503
|
};
|
|
586
|
-
fs.writeFileSync(
|
|
587
|
-
manifestPath,
|
|
588
|
-
JSON.stringify(updated, null, 2),
|
|
589
|
-
);
|
|
504
|
+
fs.writeFileSync(manifestPath, JSON.stringify(updated, null, 2));
|
|
590
505
|
// Update agent records
|
|
591
506
|
for (const task of result.repairedTasks) {
|
|
592
507
|
try {
|
|
593
|
-
upsertCrewAgent(
|
|
594
|
-
updated,
|
|
595
|
-
recordFromTask(
|
|
596
|
-
updated,
|
|
597
|
-
task,
|
|
598
|
-
"scaffold",
|
|
599
|
-
),
|
|
600
|
-
);
|
|
508
|
+
upsertCrewAgent(updated, recordFromTask(updated, task, "scaffold"));
|
|
601
509
|
} catch {
|
|
602
510
|
/* non-critical */
|
|
603
511
|
}
|
|
@@ -605,48 +513,38 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
605
513
|
repaired++;
|
|
606
514
|
}
|
|
607
515
|
// Persist result_exists manifest status update
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
);
|
|
621
|
-
} catch {
|
|
622
|
-
/* non-critical */
|
|
623
|
-
}
|
|
516
|
+
if (result.verdict === "result_exists") {
|
|
517
|
+
// checkResultFile (called by reconcileStaleRun at line 518)
|
|
518
|
+
// already saved manifest.status='completed' at line 66.
|
|
519
|
+
// No additional manifest write needed here — avoids TOCTOU
|
|
520
|
+
// window between checkResultFile save and re-write.
|
|
521
|
+
// Sync agent records (checkResultFile also does this but
|
|
522
|
+
// we sync here for consistency)
|
|
523
|
+
for (const task of tasks) {
|
|
524
|
+
try {
|
|
525
|
+
upsertCrewAgent(manifest, recordFromTask(manifest, task, "scaffold"));
|
|
526
|
+
} catch {
|
|
527
|
+
/* non-critical */
|
|
624
528
|
}
|
|
625
529
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
hasRunning = true;
|
|
632
|
-
}
|
|
530
|
+
}
|
|
531
|
+
// If still running after reconciliation attempt, mark for dir-preserving
|
|
532
|
+
if (result.verdict === "healthy" || (result.verdict === "no_status" && !result.repaired)) {
|
|
533
|
+
hasRunning = true;
|
|
534
|
+
}
|
|
633
535
|
} catch (err) {
|
|
634
536
|
// Log warning when skipping a directory due to error.
|
|
635
537
|
// Note: manifestPath here refers to the variable defined in the
|
|
636
538
|
// for loop at line 442 (outer scope of this catch block).
|
|
637
539
|
const scanManifestPath = manifestPath;
|
|
638
|
-
console.warn(
|
|
639
|
-
`[stale-reconciler] Skipping manifest due to parse error: ${scanManifestPath}: ${err}`,
|
|
640
|
-
);
|
|
540
|
+
console.warn(`[stale-reconciler] Skipping manifest due to parse error: ${scanManifestPath}: ${err}`);
|
|
641
541
|
}
|
|
642
542
|
}
|
|
643
543
|
} catch (err) {
|
|
644
544
|
// Cannot determine running state — treat as if running to prevent
|
|
645
545
|
// premature cleanup of a potentially active workspace.
|
|
646
546
|
hasRunning = true;
|
|
647
|
-
console.warn(
|
|
648
|
-
`[stale-reconciler] Skipping unreadable runs dir: ${stateRunsDir}: ${err}`,
|
|
649
|
-
);
|
|
547
|
+
console.warn(`[stale-reconciler] Skipping unreadable runs dir: ${stateRunsDir}: ${err}`);
|
|
650
548
|
}
|
|
651
549
|
|
|
652
550
|
// Post-loop: check if this workspace dir can be cleaned up.
|
|
@@ -696,22 +594,14 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
696
594
|
if (fs.existsSync(stateRunsDir)) {
|
|
697
595
|
try {
|
|
698
596
|
for (const runDir of fs.readdirSync(stateRunsDir)) {
|
|
699
|
-
const manifestPath = path.join(
|
|
700
|
-
stateRunsDir,
|
|
701
|
-
runDir,
|
|
702
|
-
"manifest.json",
|
|
703
|
-
);
|
|
597
|
+
const manifestPath = path.join(stateRunsDir, runDir, "manifest.json");
|
|
704
598
|
if (!fs.existsSync(manifestPath)) continue;
|
|
705
599
|
let manifest: TeamRunManifest | undefined;
|
|
706
600
|
try {
|
|
707
|
-
manifest = JSON.parse(
|
|
708
|
-
fs.readFileSync(manifestPath, "utf-8"),
|
|
709
|
-
);
|
|
601
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
710
602
|
} catch (err) {
|
|
711
603
|
// Log warning when skipping a directory due to error
|
|
712
|
-
console.warn(
|
|
713
|
-
`[stale-reconciler] Skipping manifest due to parse error: ${manifestPath}: ${err}`,
|
|
714
|
-
);
|
|
604
|
+
console.warn(`[stale-reconciler] Skipping manifest due to parse error: ${manifestPath}: ${err}`);
|
|
715
605
|
continue;
|
|
716
606
|
}
|
|
717
607
|
if (manifest?.status === "running") {
|
|
@@ -720,9 +610,7 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
720
610
|
}
|
|
721
611
|
}
|
|
722
612
|
} catch (err) {
|
|
723
|
-
console.warn(
|
|
724
|
-
`[stale-reconciler] Skipping unreadable runs dir: ${stateRunsDir}: ${err}`,
|
|
725
|
-
);
|
|
613
|
+
console.warn(`[stale-reconciler] Skipping unreadable runs dir: ${stateRunsDir}: ${err}`);
|
|
726
614
|
}
|
|
727
615
|
}
|
|
728
616
|
}
|
|
@@ -733,16 +621,10 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
733
621
|
if (fs.existsSync(stateRunsDir)) {
|
|
734
622
|
try {
|
|
735
623
|
for (const runDir of fs.readdirSync(stateRunsDir)) {
|
|
736
|
-
const manifestPath = path.join(
|
|
737
|
-
stateRunsDir,
|
|
738
|
-
runDir,
|
|
739
|
-
"manifest.json",
|
|
740
|
-
);
|
|
624
|
+
const manifestPath = path.join(stateRunsDir, runDir, "manifest.json");
|
|
741
625
|
if (!fs.existsSync(manifestPath)) continue;
|
|
742
626
|
try {
|
|
743
|
-
const manifest: TeamRunManifest = JSON.parse(
|
|
744
|
-
fs.readFileSync(manifestPath, "utf-8"),
|
|
745
|
-
);
|
|
627
|
+
const manifest: TeamRunManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
746
628
|
if (manifest.status === "running") {
|
|
747
629
|
stillClean = false;
|
|
748
630
|
break;
|
|
@@ -777,16 +659,10 @@ export function reconcileOrphanedTempWorkspaces(
|
|
|
777
659
|
if (fs.existsSync(stateRunsDir)) {
|
|
778
660
|
try {
|
|
779
661
|
for (const runDir of fs.readdirSync(stateRunsDir)) {
|
|
780
|
-
const manifestPath = path.join(
|
|
781
|
-
stateRunsDir,
|
|
782
|
-
runDir,
|
|
783
|
-
"manifest.json",
|
|
784
|
-
);
|
|
662
|
+
const manifestPath = path.join(stateRunsDir, runDir, "manifest.json");
|
|
785
663
|
if (!fs.existsSync(manifestPath)) continue;
|
|
786
664
|
try {
|
|
787
|
-
const m = JSON.parse(
|
|
788
|
-
fs.readFileSync(manifestPath, "utf-8"),
|
|
789
|
-
) as TeamRunManifest;
|
|
665
|
+
const m = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as TeamRunManifest;
|
|
790
666
|
if (m.status === "running") {
|
|
791
667
|
hasRunningManifest = true;
|
|
792
668
|
break;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Captures incremental worker output during task execution for live preview.
|
|
3
3
|
// Used by the UI layer to show partial results before task completion.
|
|
4
4
|
|
|
5
|
-
import type { ParsedPiUsage } from "./pi-json-output.ts";
|
|
6
5
|
import { TAIL_CAPTURE_STREAM_STAGE } from "./compact-stages/index.ts";
|
|
6
|
+
import type { ParsedPiUsage } from "./pi-json-output.ts";
|
|
7
7
|
|
|
8
8
|
export interface ToolCallPreview {
|
|
9
9
|
toolName: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { isSafePathId } from "../utils/safe-paths.ts";
|
|
4
3
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
import { isSafePathId } from "../utils/safe-paths.ts";
|
|
5
5
|
|
|
6
6
|
export interface StreamingOutputHandle {
|
|
7
7
|
write(text: string): void;
|