pi-crew 0.2.3 → 0.2.5
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/AGENTS.md +57 -32
- package/CHANGELOG.md +466 -448
- package/LICENSE +21 -21
- package/NOTICE.md +16 -16
- package/README.md +323 -323
- package/docs/FEATURE_INTAKE.md +126 -0
- package/docs/HARNESS.md +86 -0
- package/docs/HARNESS_BACKLOG.md +41 -0
- package/docs/TEST_MATRIX.md +49 -0
- package/docs/actions-reference.md +595 -595
- package/docs/architecture.md +180 -180
- package/docs/code-review-2026-05-11.md +592 -592
- package/docs/commands-reference.md +347 -347
- package/docs/comparison-pi-subagents-vs-pi-crew.md +303 -0
- package/docs/decisions/0001-durable-state.md +41 -0
- package/docs/decisions/0002-child-process-for-async.md +42 -0
- package/docs/decisions/0003-depth-guard.md +36 -0
- package/docs/decisions/0004-execfile-over-exec.md +34 -0
- package/docs/decisions/0005-no-parameter-properties.md +49 -0
- package/docs/decisions/0006-publish-bundled-esm.md +63 -0
- package/docs/decisions/0007-active-run-binary-index.md +54 -0
- package/docs/decisions/0008-child-pi-warm-pool.md +61 -0
- package/docs/decisions/README.md +23 -0
- package/docs/followup-review-round4-2026-05-13.md +107 -0
- package/docs/implementation-plan-top3.md +333 -0
- package/docs/live-mailbox-runtime.md +36 -36
- package/docs/next-upgrade-roadmap.md +808 -808
- package/docs/oh-my-pi-research.md +509 -0
- package/docs/perf/baseline-2026-05.md +113 -0
- package/docs/perf/final-report-2026-05.md +206 -0
- package/docs/perf/sprint-1-report.md +71 -0
- package/docs/perf/sprint-2-report.md +81 -0
- package/docs/perf/sprint-2.5-report.md +53 -0
- package/docs/perf/sprint-3-report.md +36 -0
- package/docs/perf/sprint-4-report.md +47 -0
- package/docs/perf/sprint-5-report.md +51 -0
- package/docs/perf/sprint-6-report.md +94 -0
- package/docs/perf/sprint-7-report.md +74 -0
- package/docs/perf/upgrade-plan-2026-05.md +147 -0
- package/docs/pi-subagents3-deep-analysis.md +508 -0
- package/docs/product/README.md +31 -0
- package/docs/product/platform.md +27 -0
- package/docs/product/runtime-safety.md +37 -0
- package/docs/product/team-run.md +39 -0
- package/docs/product/team-tool.md +37 -0
- package/docs/publishing.md +65 -65
- package/docs/resource-formats.md +134 -134
- package/docs/runtime-analysis-child-vs-live.md +171 -0
- package/docs/runtime-flow.md +148 -148
- package/docs/runtime-migration-in-process-analysis.md +250 -0
- package/docs/stories/README.md +30 -0
- package/docs/stories/backlog.md +36 -0
- package/docs/templates/decision.md +27 -0
- package/docs/templates/story.md +44 -0
- package/docs/templates/validation-report.md +32 -0
- package/docs/usage.md +238 -238
- package/index.ts +7 -6
- package/install.mjs +65 -65
- package/package.json +107 -100
- package/schema.json +222 -222
- package/skills/child-pi-spawning/SKILL.md +213 -0
- package/skills/context-artifact-hygiene/SKILL.md +32 -0
- package/skills/event-log-tracing/SKILL.md +299 -0
- package/skills/git-master/SKILL.md +225 -24
- package/skills/live-agent-lifecycle/SKILL.md +192 -0
- package/skills/mailbox-interactive/SKILL.md +300 -19
- package/skills/model-routing-context/SKILL.md +94 -0
- package/skills/multi-perspective-review/SKILL.md +88 -0
- package/skills/read-only-explorer/SKILL.md +250 -26
- package/skills/safe-bash/SKILL.md +307 -21
- package/skills/verification-before-done/SKILL.md +11 -2
- package/skills/widget-rendering/SKILL.md +258 -0
- package/skills/workspace-isolation/SKILL.md +202 -0
- package/skills/worktree-isolation/SKILL.md +202 -18
- package/src/adapters/claude-adapter.ts +25 -25
- package/src/adapters/codex-adapter.ts +21 -21
- package/src/adapters/cursor-adapter.ts +17 -17
- package/src/adapters/export-util.ts +137 -137
- package/src/adapters/index.ts +15 -15
- package/src/adapters/registry.ts +18 -18
- package/src/adapters/types.ts +23 -23
- package/src/agents/agent-config.ts +38 -38
- package/src/agents/agent-serializer.ts +38 -38
- package/src/agents/discover-agents.ts +121 -118
- package/src/config/config.ts +740 -858
- package/src/config/defaults.ts +96 -96
- package/src/config/drift-detector.ts +211 -211
- package/src/config/markers.ts +327 -327
- package/src/config/resilient-parser.ts +109 -108
- package/src/config/suggestions.ts +74 -74
- package/src/config/types.ts +199 -0
- package/src/extension/async-notifier.ts +123 -89
- package/src/extension/autonomous-policy.ts +169 -169
- package/src/extension/cross-extension-rpc.ts +104 -104
- package/src/extension/help.ts +47 -47
- package/src/extension/import-index.ts +69 -69
- package/src/extension/management.ts +395 -382
- package/src/extension/notification-router.ts +116 -116
- package/src/extension/notification-sink.ts +51 -51
- package/src/extension/project-init.ts +168 -168
- package/src/extension/register.ts +859 -668
- package/src/extension/registration/artifact-cleanup.ts +15 -15
- package/src/extension/registration/command-utils.ts +54 -54
- package/src/extension/registration/commands.ts +559 -452
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +102 -102
- package/src/extension/registration/subagent-tools.ts +220 -159
- package/src/extension/registration/team-tool.ts +159 -99
- package/src/extension/registration/viewers.ts +29 -0
- package/src/extension/result-watcher.ts +128 -128
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +73 -73
- package/src/extension/run-import.ts +84 -84
- package/src/extension/run-index.ts +94 -94
- package/src/extension/run-maintenance.ts +142 -142
- package/src/extension/session-summary.ts +8 -8
- package/src/extension/team-manager-command.ts +96 -96
- package/src/extension/team-recommendation.ts +188 -188
- package/src/extension/team-tool/api.ts +5 -2
- package/src/extension/team-tool/cancel.ts +224 -209
- package/src/extension/team-tool/config-patch.ts +36 -36
- package/src/extension/team-tool/context.ts +60 -60
- package/src/extension/team-tool/doctor.ts +242 -242
- package/src/extension/team-tool/handle-settings.ts +421 -195
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +139 -139
- package/src/extension/team-tool/parallel-dispatch.ts +156 -156
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +112 -111
- package/src/extension/team-tool/run.ts +246 -229
- package/src/extension/team-tool/status.ts +110 -110
- package/src/extension/team-tool-types.ts +13 -13
- package/src/extension/team-tool.ts +344 -344
- package/src/extension/tool-result.ts +16 -16
- package/src/extension/validate-resources.ts +77 -77
- package/src/hooks/registry.ts +61 -61
- package/src/hooks/types.ts +40 -40
- package/src/i18n.ts +184 -184
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +68 -68
- package/src/observability/exporters/adapter.ts +30 -30
- package/src/observability/exporters/otlp-exporter.ts +106 -92
- package/src/observability/exporters/prometheus-exporter.ts +54 -54
- package/src/observability/metric-registry.ts +87 -87
- package/src/observability/metric-retention.ts +54 -54
- package/src/observability/metric-sink.ts +81 -56
- package/src/observability/metrics-primitives.ts +167 -167
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/adaptive-plan.ts +338 -0
- package/src/runtime/agent-control.ts +169 -169
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/async-runner.ts +153 -153
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -100
- package/src/runtime/background-runner.ts +122 -89
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi.ts +541 -461
- package/src/runtime/code-summary.ts +247 -247
- package/src/runtime/compaction-summary.ts +271 -271
- package/src/runtime/concurrency.ts +58 -58
- package/src/runtime/crash-recovery.ts +317 -301
- package/src/runtime/crew-agent-records.ts +379 -281
- package/src/runtime/crew-agent-runtime.ts +60 -60
- package/src/runtime/cross-extension-rpc.ts +72 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -201
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -90
- package/src/runtime/deadletter.ts +47 -47
- package/src/runtime/delivery-coordinator.ts +176 -176
- package/src/runtime/delta-conflict.ts +360 -360
- package/src/runtime/diagnostic-export.ts +102 -102
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +82 -81
- package/src/runtime/errors/crew-errors.ts +166 -0
- package/src/runtime/event-stream-bridge.ts +92 -92
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +234 -106
- package/src/runtime/heartbeat-watcher.ts +145 -124
- package/src/runtime/iteration-hooks.ts +267 -267
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-agent-manager.ts +377 -179
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +676 -600
- package/src/runtime/loop-gates.ts +129 -129
- package/src/runtime/manifest-cache.ts +263 -263
- package/src/runtime/mcp-proxy.ts +113 -113
- package/src/runtime/metric-parser.ts +40 -40
- package/src/runtime/model-fallback.ts +282 -274
- package/src/runtime/model-resolver.ts +118 -0
- package/src/runtime/output-validator.ts +187 -187
- package/src/runtime/overflow-recovery.ts +175 -175
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +156 -156
- package/src/runtime/parent-guard.ts +80 -80
- package/src/runtime/phase-progress.ts +217 -217
- package/src/runtime/pi-args.ts +165 -165
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +167 -167
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +125 -125
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +97 -73
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +81 -81
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/run-tracker.ts +99 -0
- package/src/runtime/runtime-policy.ts +21 -0
- package/src/runtime/runtime-resolver.ts +94 -91
- package/src/runtime/scheduler.ts +294 -0
- package/src/runtime/semaphore.ts +131 -131
- package/src/runtime/sensitive-paths.ts +92 -92
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/settings-store.ts +103 -0
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/skill-instructions.ts +222 -222
- package/src/runtime/stale-reconciler.ts +198 -189
- package/src/runtime/streaming-output.ts +47 -0
- package/src/runtime/subagent-manager.ts +404 -400
- package/src/runtime/subprocess-tool-registry.ts +67 -67
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph-scheduler.ts +122 -122
- package/src/runtime/task-graph.ts +207 -207
- package/src/runtime/task-output-context.ts +177 -177
- package/src/runtime/task-packet.ts +93 -93
- package/src/runtime/task-quality.ts +207 -207
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +131 -113
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +139 -139
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +103 -103
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +469 -459
- package/src/runtime/team-runner.ts +693 -945
- package/src/runtime/usage-tracker.ts +71 -0
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -187
- package/src/runtime/yield-handler.ts +190 -190
- package/src/schema/config-schema.ts +172 -168
- package/src/schema/team-tool-schema.ts +126 -126
- package/src/schema/validation-types.ts +151 -148
- package/src/skills/discover-skills.ts +67 -67
- package/src/skills/skill-templates.ts +374 -374
- package/src/state/active-run-registry.ts +227 -191
- package/src/state/artifact-store.ts +130 -129
- package/src/state/atomic-write.ts +262 -195
- package/src/state/blob-store.ts +116 -116
- package/src/state/contracts.ts +111 -111
- package/src/state/event-log-rotation.ts +161 -158
- package/src/state/event-log.ts +383 -303
- package/src/state/event-reconstructor.ts +217 -217
- package/src/state/jsonl-writer.ts +82 -82
- package/src/state/locks.ts +146 -146
- package/src/state/mailbox.ts +446 -405
- package/src/state/state-store.ts +364 -351
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +285 -285
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/discover-teams.ts +116 -116
- package/src/teams/team-config.ts +27 -27
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/agent-management-overlay.ts +144 -144
- package/src/ui/crew-widget.ts +487 -370
- package/src/ui/dashboard-panes/agents-pane.ts +109 -28
- package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
- package/src/ui/dashboard-panes/capability-pane.ts +59 -59
- package/src/ui/dashboard-panes/health-pane.ts +30 -30
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/progress-pane.ts +30 -30
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -10
- package/src/ui/heartbeat-aggregator.ts +63 -63
- package/src/ui/keybinding-map.ts +97 -94
- package/src/ui/live-conversation-overlay.ts +152 -0
- package/src/ui/live-run-sidebar.ts +180 -180
- package/src/ui/mascot.ts +442 -442
- package/src/ui/overlays/agent-picker-overlay.ts +57 -57
- package/src/ui/overlays/confirm-overlay.ts +58 -58
- package/src/ui/overlays/mailbox-compose-overlay.ts +144 -144
- package/src/ui/overlays/mailbox-compose-preview.ts +63 -63
- package/src/ui/overlays/mailbox-detail-overlay.ts +122 -122
- package/src/ui/pi-ui-compat.ts +57 -57
- package/src/ui/powerbar-publisher.ts +221 -197
- package/src/ui/render-scheduler.ts +216 -143
- package/src/ui/run-action-dispatcher.ts +118 -118
- package/src/ui/run-dashboard.ts +526 -464
- package/src/ui/run-event-bus.ts +208 -208
- package/src/ui/run-snapshot-cache.ts +826 -777
- package/src/ui/settings-overlay.ts +721 -0
- package/src/ui/snapshot-types.ts +86 -70
- package/src/ui/theme-adapter.ts +190 -190
- package/src/ui/tool-progress-formatter.ts +89 -0
- package/src/ui/transcript-cache.ts +94 -94
- package/src/ui/transcript-viewer.ts +335 -335
- package/src/utils/conflict-detect.ts +662 -0
- package/src/utils/file-coalescer.ts +86 -86
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/fs-watch.ts +88 -31
- package/src/utils/gh-protocol.ts +479 -0
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/internal-error.ts +6 -6
- package/src/utils/names.ts +27 -27
- package/src/utils/paths.ts +102 -63
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- package/src/utils/sse-parser.ts +134 -134
- package/src/utils/task-name-generator.ts +337 -337
- package/src/utils/timings.ts +33 -33
- package/src/utils/visual.ts +243 -198
- package/src/workflows/discover-workflows.ts +139 -139
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/workflows/workflow-config.ts +26 -26
- package/src/workflows/workflow-serializer.ts +32 -32
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/cleanup.ts +75 -75
- package/src/worktree/worktree-manager.ts +188 -188
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/tsconfig.json +19 -19
- package/workflows/default.workflow.md +30 -30
- package/workflows/fast-fix.workflow.md +23 -23
- package/workflows/implementation.workflow.md +43 -43
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/skills/task-packet/SKILL.md +0 -28
- package/skills/verify-evidence/SKILL.md +0 -27
|
@@ -1,301 +1,317 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import type { MetricRegistry } from "../observability/metric-registry.ts";
|
|
4
|
-
import { appendEvent, scanSequence } from "../state/event-log.ts";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
const
|
|
42
|
-
if (!
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (task.
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import type { MetricRegistry } from "../observability/metric-registry.ts";
|
|
4
|
+
import { appendEvent, scanSequence } from "../state/event-log.ts";
|
|
5
|
+
import { recordFromTask, upsertCrewAgent } from "./crew-agent-records.ts";
|
|
6
|
+
import { withRunLockSync } from "../state/locks.ts";
|
|
7
|
+
import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../state/state-store.ts";
|
|
8
|
+
import type { TeamTaskState } from "../state/types.ts";
|
|
9
|
+
import { isWorkerHeartbeatStale } from "./worker-heartbeat.ts";
|
|
10
|
+
import type { ManifestCache } from "./manifest-cache.ts";
|
|
11
|
+
import { checkProcessLiveness } from "./process-status.ts";
|
|
12
|
+
import { reconcileStaleRun, type ReconcileResult } from "./stale-reconciler.ts";
|
|
13
|
+
import { executeHook, appendHookEvent } from "../hooks/registry.ts";
|
|
14
|
+
import { activeRunEntries, unregisterActiveRun, readActiveRunRegistry } from "../state/active-run-registry.ts";
|
|
15
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
16
|
+
import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
17
|
+
import { terminateLiveAgentsForRun } from "./live-agent-manager.ts";
|
|
18
|
+
|
|
19
|
+
export interface RecoveryPlan {
|
|
20
|
+
runId: string;
|
|
21
|
+
resumableTasks: string[];
|
|
22
|
+
preservedTasks: string[];
|
|
23
|
+
lastEventSeq: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isTerminalTask(task: TeamTaskState): boolean {
|
|
27
|
+
return task.status === "completed" || task.status === "failed" || task.status === "cancelled" || task.status === "skipped";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function shouldRecoverTask(task: TeamTaskState, deadMs: number): boolean {
|
|
31
|
+
if (task.status !== "running") return false;
|
|
32
|
+
if (!task.heartbeat) return true;
|
|
33
|
+
return task.heartbeat.alive === false || isWorkerHeartbeatStale(task.heartbeat, deadMs);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function detectInterruptedRuns(cwd: string, manifestCache: ManifestCache, deadMs = 300_000): RecoveryPlan[] {
|
|
37
|
+
const plans: RecoveryPlan[] = [];
|
|
38
|
+
for (const manifest of manifestCache.list(50)) {
|
|
39
|
+
if (manifest.status !== "running" && manifest.status !== "blocked") continue;
|
|
40
|
+
if (manifest.async?.pid !== undefined && checkProcessLiveness(manifest.async.pid).alive) continue;
|
|
41
|
+
const loaded = loadRunManifestById(cwd, manifest.runId);
|
|
42
|
+
if (!loaded) continue;
|
|
43
|
+
const resumableTasks = loaded.tasks.filter((task) => shouldRecoverTask(task, deadMs)).map((task) => task.id);
|
|
44
|
+
if (!resumableTasks.length) continue;
|
|
45
|
+
plans.push({ runId: manifest.runId, resumableTasks, preservedTasks: loaded.tasks.filter(isTerminalTask).map((task) => task.id), lastEventSeq: scanSequence(loaded.manifest.eventsPath) });
|
|
46
|
+
}
|
|
47
|
+
return plans;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function applyRecoveryPlan(plan: RecoveryPlan, ctx: Pick<ExtensionContext, "cwd">, registry?: MetricRegistry): Promise<void> {
|
|
51
|
+
const loaded = loadRunManifestById(ctx.cwd, plan.runId);
|
|
52
|
+
if (!loaded) throw new Error(`Run '${plan.runId}' not found.`);
|
|
53
|
+
|
|
54
|
+
const hookReport = await executeHook("run_recovery", { runId: plan.runId, cwd: ctx.cwd });
|
|
55
|
+
appendHookEvent(loaded.manifest, hookReport);
|
|
56
|
+
if (hookReport.outcome === "block") {
|
|
57
|
+
appendEvent(loaded.manifest.eventsPath, { type: "crew.run.recovery_blocked", runId: plan.runId, message: `Recovery blocked by hook: ${hookReport.reason ?? "run_recovery hook blocked the operation."}`, data: { hookOutcome: "block", reason: hookReport.reason } });
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const reset = new Set(plan.resumableTasks);
|
|
62
|
+
const tasks = loaded.tasks.map((task) => reset.has(task.id) ? { ...task, status: "queued" as const, startedAt: undefined, finishedAt: undefined, error: undefined, heartbeat: undefined } : task);
|
|
63
|
+
saveRunTasks(loaded.manifest, tasks);
|
|
64
|
+
appendEvent(loaded.manifest.eventsPath, { type: "crew.run.resumed", runId: plan.runId, message: `Recovered ${plan.resumableTasks.length} interrupted task(s).`, data: { recoveredFromSeq: plan.lastEventSeq, resumableTasks: plan.resumableTasks } });
|
|
65
|
+
registry?.counter("crew.run.count", "Total runs by status").inc({ status: "resumed" });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function declineRecoveryPlan(plan: RecoveryPlan, ctx: Pick<ExtensionContext, "cwd">): void {
|
|
69
|
+
const loaded = loadRunManifestById(ctx.cwd, plan.runId);
|
|
70
|
+
if (!loaded) throw new Error(`Run '${plan.runId}' not found.`);
|
|
71
|
+
// Log the event first — if appendEvent fails, state remains consistent.
|
|
72
|
+
appendEvent(loaded.manifest.eventsPath, { type: "crew.run.recovery_declined", runId: plan.runId, message: "Interrupted run was not resumed.", data: { recoveredFromSeq: plan.lastEventSeq } });
|
|
73
|
+
updateRunStatus(loaded.manifest, "cancelled", "interrupted-not-resumed");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run 3-phase stale reconciliation on all active runs.
|
|
78
|
+
* Returns results for each reconciled run.
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Auto-cancel orphaned runs whose owner session no longer exists.
|
|
82
|
+
*
|
|
83
|
+
* When a Pi session dies (crash, force-close, Ctrl+C), `session_shutdown`
|
|
84
|
+
* does not fire and child workers are not terminated. The next Pi session
|
|
85
|
+
* must detect these orphaned runs and cancel them.
|
|
86
|
+
*
|
|
87
|
+
* Criteria for orphan detection:
|
|
88
|
+
* 1. Manifest status is "running"
|
|
89
|
+
* 2. Manifest has an `ownerSessionId` that is NOT the current session
|
|
90
|
+
* 3. The owner session's process is no longer alive (PID check)
|
|
91
|
+
* 4. No recent heartbeat activity (task heartbeat or agent progress within threshold)
|
|
92
|
+
*
|
|
93
|
+
* Returns the number of runs cancelled.
|
|
94
|
+
*/
|
|
95
|
+
export function cancelOrphanedRuns(
|
|
96
|
+
cwd: string,
|
|
97
|
+
manifestCache: ManifestCache,
|
|
98
|
+
currentSessionId: string,
|
|
99
|
+
staleThresholdMs = 300_000,
|
|
100
|
+
now = Date.now(),
|
|
101
|
+
): { cancelled: string[]; skipped: string[] } {
|
|
102
|
+
const cancelled: string[] = [];
|
|
103
|
+
const skipped: string[] = [];
|
|
104
|
+
|
|
105
|
+
// Phase 1: Scan project-level manifests via manifestCache
|
|
106
|
+
for (const manifest of manifestCache.list(50)) {
|
|
107
|
+
if (manifest.status !== "running" && manifest.status !== "blocked") continue;
|
|
108
|
+
|
|
109
|
+
// Only consider runs owned by a different session
|
|
110
|
+
const ownerId = manifest.ownerSessionId;
|
|
111
|
+
if (!ownerId || ownerId === currentSessionId) continue;
|
|
112
|
+
|
|
113
|
+
// Check if the owner process is still alive
|
|
114
|
+
const ownerPid = manifest.async?.pid;
|
|
115
|
+
if (ownerPid !== undefined && checkProcessLiveness(ownerPid).alive) {
|
|
116
|
+
skipped.push(manifest.runId);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check for recent heartbeat activity
|
|
121
|
+
const loaded = loadRunManifestById(cwd, manifest.runId);
|
|
122
|
+
if (!loaded) continue;
|
|
123
|
+
|
|
124
|
+
const hasRecentActivity = loaded.tasks.some((task) => {
|
|
125
|
+
if (task.status !== "running" && task.status !== "waiting") return false;
|
|
126
|
+
const heartbeatAt = task.heartbeat?.lastSeenAt ? new Date(task.heartbeat.lastSeenAt).getTime() : Number.NaN;
|
|
127
|
+
if (task.heartbeat?.alive !== false && Number.isFinite(heartbeatAt) && now - heartbeatAt <= staleThresholdMs) return true;
|
|
128
|
+
const activityAt = task.agentProgress?.lastActivityAt ? new Date(task.agentProgress.lastActivityAt).getTime() : Number.NaN;
|
|
129
|
+
return Number.isFinite(activityAt) && now - activityAt <= staleThresholdMs;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (hasRecentActivity) {
|
|
133
|
+
skipped.push(manifest.runId);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Orphan confirmed — mark durable state terminal before best-effort live-agent abort.
|
|
138
|
+
// terminateLiveAgent unregisters handles before awaiting abort(), and live-executor's
|
|
139
|
+
// isCurrent() checks durable terminal state before writing progress.
|
|
140
|
+
|
|
141
|
+
// Orphan confirmed — cancel all running tasks
|
|
142
|
+
let cancelledRun = false;
|
|
143
|
+
withRunLockSync(loaded.manifest, () => {
|
|
144
|
+
const fresh = loadRunManifestById(cwd, manifest.runId);
|
|
145
|
+
if (!fresh || (fresh.manifest.status !== "running" && fresh.manifest.status !== "blocked")) return;
|
|
146
|
+
|
|
147
|
+
const now_iso = new Date(now).toISOString();
|
|
148
|
+
const repairedTasks = fresh.tasks.map((task) => {
|
|
149
|
+
if (task.status === "running" || task.status === "queued" || task.status === "waiting") {
|
|
150
|
+
return { ...task, status: "cancelled" as const, finishedAt: now_iso, error: `Orphaned run: owner session ${ownerId} no longer exists` };
|
|
151
|
+
}
|
|
152
|
+
return task;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
saveRunTasks(fresh.manifest, repairedTasks);
|
|
156
|
+
for (const task of repairedTasks) { try { upsertCrewAgent(fresh.manifest, recordFromTask(fresh.manifest, task, "scaffold")); } catch { /* non-critical */ } }
|
|
157
|
+
updateRunStatus(fresh.manifest, "cancelled", `Orphaned run: owner session ${ownerId} no longer exists`);
|
|
158
|
+
appendEvent(fresh.manifest.eventsPath, { type: "crew.run.orphan_cancelled", runId: manifest.runId, message: `Auto-cancelled orphaned run (owner: ${ownerId})`, data: { ownerSessionId: ownerId, cancelledTasks: repairedTasks.filter((t) => t.status === "cancelled").length } });
|
|
159
|
+
cancelled.push(manifest.runId);
|
|
160
|
+
cancelledRun = true;
|
|
161
|
+
});
|
|
162
|
+
if (cancelledRun) void terminateLiveAgentsForRun(manifest.runId, "cancelled", appendEvent, loaded.manifest.eventsPath).catch(() => {});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { cancelled, skipped };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Purge the global active-run-index of entries whose manifest is no longer active.
|
|
170
|
+
*
|
|
171
|
+
* This scans every entry in active-run-index.json and removes any whose:
|
|
172
|
+
* - manifest file no longer exists, OR
|
|
173
|
+
* - manifest status is terminal (completed/failed/cancelled/blocked), OR
|
|
174
|
+
* - manifest cwd directory no longer exists (e.g. temp test dirs)
|
|
175
|
+
*
|
|
176
|
+
* Also removes entries where the manifest is still "running" but:
|
|
177
|
+
* - The cwd has been deleted (temp dir cleanup)
|
|
178
|
+
* - The async worker PID is dead AND no heartbeat for > threshold
|
|
179
|
+
*
|
|
180
|
+
* This is the **global** cleanup that cancelOrphanedRuns (project-scoped)
|
|
181
|
+
* cannot reach.
|
|
182
|
+
*/
|
|
183
|
+
/**
|
|
184
|
+
* Best-effort removal of stateRoot and artifactsRoot directories for a purged run.
|
|
185
|
+
* Uses resolveRealContainedPath to ensure we only delete paths that are safely
|
|
186
|
+
* contained within a known crew root (project or user level).
|
|
187
|
+
*/
|
|
188
|
+
function tryRemoveRunDirectories(entry: { stateRoot: string; cwd: string }): void {
|
|
189
|
+
const roots = [projectCrewRoot(entry.cwd), userCrewRoot()];
|
|
190
|
+
for (const root of roots) {
|
|
191
|
+
try {
|
|
192
|
+
resolveRealContainedPath(root, entry.stateRoot);
|
|
193
|
+
// If we get here, stateRoot is safely contained — remove it
|
|
194
|
+
fs.rmSync(entry.stateRoot, { recursive: true, force: true });
|
|
195
|
+
break;
|
|
196
|
+
} catch {
|
|
197
|
+
// Not contained in this root, try next
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// NOTE: artifactsRoot is shared across runs and cleaned up by pruneFinishedRuns/pruneUserLevelRuns — not deleted here.
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Purge the global active-run-index of entries whose manifest is no longer active.
|
|
205
|
+
*
|
|
206
|
+
* Note: This function only cleans user-level active run entries.
|
|
207
|
+
* Project-level stale runs are handled by session_start auto-prune triggered during run creation.
|
|
208
|
+
*/
|
|
209
|
+
export function purgeStaleActiveRunIndex(staleThresholdMs = 300_000, now = Date.now()): { purged: string[]; kept: string[] } {
|
|
210
|
+
const purged: string[] = [];
|
|
211
|
+
const kept: string[] = [];
|
|
212
|
+
const entries = readActiveRunRegistry();
|
|
213
|
+
|
|
214
|
+
for (const entry of entries) {
|
|
215
|
+
// 1. Manifest file gone → definitely stale
|
|
216
|
+
if (!fs.existsSync(entry.manifestPath)) {
|
|
217
|
+
unregisterActiveRun(entry.runId);
|
|
218
|
+
tryRemoveRunDirectories(entry);
|
|
219
|
+
purged.push(entry.runId);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 2. CWD gone → temp dir cleaned up
|
|
224
|
+
if (!fs.existsSync(entry.cwd)) {
|
|
225
|
+
unregisterActiveRun(entry.runId);
|
|
226
|
+
tryRemoveRunDirectories(entry);
|
|
227
|
+
purged.push(entry.runId);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 3. Read manifest status
|
|
232
|
+
let manifest: { status?: string; async?: { pid?: number }; ownerSessionId?: string } | undefined;
|
|
233
|
+
try {
|
|
234
|
+
manifest = JSON.parse(fs.readFileSync(entry.manifestPath, "utf-8"));
|
|
235
|
+
} catch {
|
|
236
|
+
unregisterActiveRun(entry.runId);
|
|
237
|
+
tryRemoveRunDirectories(entry);
|
|
238
|
+
purged.push(entry.runId);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 4. Terminal status → no longer active (just unregister, don't delete files)
|
|
243
|
+
const terminalStatuses = new Set(["completed", "failed", "cancelled", "blocked"]);
|
|
244
|
+
if (manifest && terminalStatuses.has(manifest.status ?? "")) {
|
|
245
|
+
unregisterActiveRun(entry.runId);
|
|
246
|
+
purged.push(entry.runId);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 5. Still "running" — check if worker PID is dead and no heartbeat
|
|
251
|
+
if (manifest?.status === "running" && manifest.async?.pid !== undefined) {
|
|
252
|
+
const pidAlive = checkProcessLiveness(manifest.async.pid).alive;
|
|
253
|
+
if (!pidAlive) {
|
|
254
|
+
// Check age — if manifest hasn't been updated in > threshold, it's stale
|
|
255
|
+
const updatedAt = new Date(entry.updatedAt).getTime();
|
|
256
|
+
if (Number.isFinite(updatedAt) && now - updatedAt > staleThresholdMs) {
|
|
257
|
+
// Dead PID + stale update → cancel the manifest and unregister
|
|
258
|
+
try {
|
|
259
|
+
const fullLoaded = loadRunManifestById(entry.cwd, entry.runId);
|
|
260
|
+
if (fullLoaded) {
|
|
261
|
+
const now_iso = new Date(now).toISOString();
|
|
262
|
+
const repairedTasks = fullLoaded.tasks.map((task) => {
|
|
263
|
+
if (task.status === "running" || task.status === "queued" || task.status === "waiting") {
|
|
264
|
+
return { ...task, status: "cancelled" as const, finishedAt: now_iso, error: "Orphaned run: worker process dead and no recent activity" };
|
|
265
|
+
}
|
|
266
|
+
return task;
|
|
267
|
+
});
|
|
268
|
+
saveRunTasks(fullLoaded.manifest, repairedTasks);
|
|
269
|
+
for (const task of repairedTasks) { try { upsertCrewAgent(fullLoaded.manifest, recordFromTask(fullLoaded.manifest, task, "scaffold")); } catch { /* non-critical */ } }
|
|
270
|
+
updateRunStatus(fullLoaded.manifest, "cancelled", "Orphaned run: worker process dead and no recent activity");
|
|
271
|
+
void terminateLiveAgentsForRun(fullLoaded.manifest.runId, "cancelled", appendEvent, fullLoaded.manifest.eventsPath).catch(() => {});
|
|
272
|
+
}
|
|
273
|
+
} catch {
|
|
274
|
+
// Best-effort manifest cleanup
|
|
275
|
+
}
|
|
276
|
+
unregisterActiveRun(entry.runId);
|
|
277
|
+
tryRemoveRunDirectories(entry);
|
|
278
|
+
purged.push(entry.runId);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
kept.push(entry.runId);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { purged, kept };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function reconcileAllStaleRuns(cwd: string, manifestCache: ManifestCache, now = Date.now()): ReconcileResult[] {
|
|
291
|
+
const results: ReconcileResult[] = [];
|
|
292
|
+
for (const manifest of manifestCache.list(50)) {
|
|
293
|
+
if (manifest.status !== "running" && manifest.status !== "blocked") continue;
|
|
294
|
+
const loaded = loadRunManifestById(cwd, manifest.runId);
|
|
295
|
+
if (!loaded) continue;
|
|
296
|
+
// Use lock to prevent race with cancel/status handlers modifying the same run
|
|
297
|
+
withRunLockSync(loaded.manifest, () => {
|
|
298
|
+
// Re-read inside lock to get freshest data
|
|
299
|
+
const fresh = loadRunManifestById(cwd, manifest.runId);
|
|
300
|
+
if (!fresh || (fresh.manifest.status !== "running" && fresh.manifest.status !== "blocked")) return;
|
|
301
|
+
const result = reconcileStaleRun(fresh.manifest, fresh.tasks, now);
|
|
302
|
+
if (result.repaired || result.verdict === "result_exists") {
|
|
303
|
+
if (result.repairedTasks) {
|
|
304
|
+
saveRunTasks(fresh.manifest, result.repairedTasks);
|
|
305
|
+
for (const task of result.repairedTasks) { try { upsertCrewAgent(fresh.manifest, recordFromTask(fresh.manifest, task, "scaffold")); } catch { /* non-critical */ } }
|
|
306
|
+
}
|
|
307
|
+
updateRunStatus(fresh.manifest, "failed", `Stale run reconciled: ${result.detail}`);
|
|
308
|
+
void terminateLiveAgentsForRun(fresh.manifest.runId, "failed", appendEvent, fresh.manifest.eventsPath).catch(() => {});
|
|
309
|
+
appendEvent(fresh.manifest.eventsPath, { type: "crew.run.reconciled_stale", runId: manifest.runId, message: result.detail, data: { verdict: result.verdict } });
|
|
310
|
+
}
|
|
311
|
+
if (result.verdict !== "healthy") {
|
|
312
|
+
results.push(result);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return results;
|
|
317
|
+
}
|