@telora/daemon 0.22.132 → 0.22.136
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/build-info.json +6 -3
- package/dist/audit/audit-hooks.d.ts +6 -2
- package/dist/audit/audit-hooks.d.ts.map +1 -1
- package/dist/audit/audit-hooks.js +12 -5
- package/dist/audit/audit-hooks.js.map +1 -1
- package/dist/audit/audit-phase.js +1 -1
- package/dist/audit/audit-phase.js.map +1 -1
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +1 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/completion/completion-phase-registry.d.ts +4 -0
- package/dist/completion/completion-phase-registry.d.ts.map +1 -1
- package/dist/completion/completion-phase-registry.js +63 -18
- package/dist/completion/completion-phase-registry.js.map +1 -1
- package/dist/completion/exit-classification.d.ts.map +1 -1
- package/dist/completion/exit-classification.js +1 -0
- package/dist/completion/exit-classification.js.map +1 -1
- package/dist/completion/review-approved-head-gate.d.ts +53 -0
- package/dist/completion/review-approved-head-gate.d.ts.map +1 -0
- package/dist/completion/review-approved-head-gate.js +124 -0
- package/dist/completion/review-approved-head-gate.js.map +1 -0
- package/dist/completion/review-census-record.d.ts +52 -0
- package/dist/completion/review-census-record.d.ts.map +1 -0
- package/dist/completion/review-census-record.js +145 -0
- package/dist/completion/review-census-record.js.map +1 -0
- package/dist/completion/review-exit-deps.d.ts +5 -1
- package/dist/completion/review-exit-deps.d.ts.map +1 -1
- package/dist/completion/review-exit-deps.js +7 -2
- package/dist/completion/review-exit-deps.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +29 -16
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/review-exit-progress.d.ts +8 -0
- package/dist/completion/review-exit-progress.d.ts.map +1 -1
- package/dist/completion/review-exit-progress.js +14 -0
- package/dist/completion/review-exit-progress.js.map +1 -1
- package/dist/completion/review-exit-shortfall-park.d.ts +14 -0
- package/dist/completion/review-exit-shortfall-park.d.ts.map +1 -0
- package/dist/completion/review-exit-shortfall-park.js +52 -0
- package/dist/completion/review-exit-shortfall-park.js.map +1 -0
- package/dist/completion/review-exit-shortfall.js +1 -1
- package/dist/completion/review-exit-shortfall.js.map +1 -1
- package/dist/completion/review-exit-verdict-gate.d.ts +58 -0
- package/dist/completion/review-exit-verdict-gate.d.ts.map +1 -1
- package/dist/completion/review-exit-verdict-gate.js +68 -8
- package/dist/completion/review-exit-verdict-gate.js.map +1 -1
- package/dist/completion/review-next-step.d.ts +15 -3
- package/dist/completion/review-next-step.d.ts.map +1 -1
- package/dist/completion/review-next-step.js +7 -2
- package/dist/completion/review-next-step.js.map +1 -1
- package/dist/completion/standing-approval-convergence.d.ts +68 -0
- package/dist/completion/standing-approval-convergence.d.ts.map +1 -0
- package/dist/completion/standing-approval-convergence.js +230 -0
- package/dist/completion/standing-approval-convergence.js.map +1 -0
- package/dist/completion/standing-approval-deps.d.ts +5 -0
- package/dist/completion/standing-approval-deps.d.ts.map +1 -0
- package/dist/completion/standing-approval-deps.js +37 -0
- package/dist/completion/standing-approval-deps.js.map +1 -0
- package/dist/completion/state-cascade.d.ts +11 -0
- package/dist/completion/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade.js +40 -1
- package/dist/completion/state-cascade.js.map +1 -1
- package/dist/completion/team-lifecycle.d.ts +14 -2
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +36 -36
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/completion/terminated-work-reconcile.d.ts +3 -1
- package/dist/completion/terminated-work-reconcile.d.ts.map +1 -1
- package/dist/completion/terminated-work-reconcile.js +24 -11
- package/dist/completion/terminated-work-reconcile.js.map +1 -1
- package/dist/delivery/delivery-advance-block.d.ts +6 -0
- package/dist/delivery/delivery-advance-block.d.ts.map +1 -1
- package/dist/delivery/delivery-advance-block.js +29 -3
- package/dist/delivery/delivery-advance-block.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +4 -1
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/delivery/operator-hold.d.ts.map +1 -1
- package/dist/delivery/operator-hold.js +3 -1
- package/dist/delivery/operator-hold.js.map +1 -1
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +6 -0
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/directive/directive-queue.d.ts +8 -0
- package/dist/directive/directive-queue.d.ts.map +1 -1
- package/dist/directive/directive-queue.js +20 -0
- package/dist/directive/directive-queue.js.map +1 -1
- package/dist/directive/phase-sync.d.ts +5 -0
- package/dist/directive/phase-sync.d.ts.map +1 -1
- package/dist/directive/phase-sync.js +11 -1
- package/dist/directive/phase-sync.js.map +1 -1
- package/dist/directive/stranded-review-reroute.d.ts +37 -0
- package/dist/directive/stranded-review-reroute.d.ts.map +1 -0
- package/dist/directive/stranded-review-reroute.js +150 -0
- package/dist/directive/stranded-review-reroute.js.map +1 -0
- package/dist/focus/focus-engine.d.ts.map +1 -1
- package/dist/focus/focus-engine.js +15 -3
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +9 -65
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-lifecycle.d.ts +12 -0
- package/dist/focus/focus-lifecycle.d.ts.map +1 -1
- package/dist/focus/focus-lifecycle.js +66 -37
- package/dist/focus/focus-lifecycle.js.map +1 -1
- package/dist/focus/focus-phase.d.ts.map +1 -1
- package/dist/focus/focus-phase.js +6 -0
- package/dist/focus/focus-phase.js.map +1 -1
- package/dist/focus/focus-spawn-decision.d.ts.map +1 -1
- package/dist/focus/focus-spawn-decision.js +1 -0
- package/dist/focus/focus-spawn-decision.js.map +1 -1
- package/dist/focus/focus-team-state.d.ts +10 -0
- package/dist/focus/focus-team-state.d.ts.map +1 -1
- package/dist/focus/focus-team-state.js +19 -1
- package/dist/focus/focus-team-state.js.map +1 -1
- package/dist/focus/listener-auto-advance.d.ts.map +1 -1
- package/dist/focus/listener-auto-advance.js +1 -0
- package/dist/focus/listener-auto-advance.js.map +1 -1
- package/dist/focus/listener-spawn.d.ts +10 -0
- package/dist/focus/listener-spawn.d.ts.map +1 -1
- package/dist/focus/listener-spawn.js +46 -4
- package/dist/focus/listener-spawn.js.map +1 -1
- package/dist/focus/listener-state.d.ts.map +1 -1
- package/dist/focus/listener-state.js +11 -1
- package/dist/focus/listener-state.js.map +1 -1
- package/dist/focus/listener.d.ts +42 -0
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +62 -33
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/pending-review-approved-head.d.ts +41 -0
- package/dist/focus/pending-review-approved-head.d.ts.map +1 -0
- package/dist/focus/pending-review-approved-head.js +198 -0
- package/dist/focus/pending-review-approved-head.js.map +1 -0
- package/dist/focus/pending-spawn-pass-deps.d.ts +5 -0
- package/dist/focus/pending-spawn-pass-deps.d.ts.map +1 -1
- package/dist/focus/pending-spawn-pass-deps.js +2 -1
- package/dist/focus/pending-spawn-pass-deps.js.map +1 -1
- package/dist/focus/pending-spawn-review-next-step.d.ts.map +1 -1
- package/dist/focus/pending-spawn-review-next-step.js +26 -4
- package/dist/focus/pending-spawn-review-next-step.js.map +1 -1
- package/dist/focus/review-spawn-coverage.d.ts +87 -0
- package/dist/focus/review-spawn-coverage.d.ts.map +1 -0
- package/dist/focus/review-spawn-coverage.js +104 -0
- package/dist/focus/review-spawn-coverage.js.map +1 -0
- package/dist/focus/session-health.d.ts +5 -2
- package/dist/focus/session-health.d.ts.map +1 -1
- package/dist/focus/session-health.js +2 -1
- package/dist/focus/session-health.js.map +1 -1
- package/dist/focus/session-process-reap.d.ts +50 -0
- package/dist/focus/session-process-reap.d.ts.map +1 -0
- package/dist/focus/session-process-reap.js +77 -0
- package/dist/focus/session-process-reap.js.map +1 -0
- package/dist/focus/unchanged-reviewed-head.d.ts +40 -0
- package/dist/focus/unchanged-reviewed-head.d.ts.map +1 -0
- package/dist/focus/unchanged-reviewed-head.js +84 -0
- package/dist/focus/unchanged-reviewed-head.js.map +1 -0
- package/dist/focus/verify-open-issues.d.ts +4 -19
- package/dist/focus/verify-open-issues.d.ts.map +1 -1
- package/dist/focus/verify-open-issues.js +83 -41
- package/dist/focus/verify-open-issues.js.map +1 -1
- package/dist/git/focus-integration-sync.d.ts +61 -0
- package/dist/git/focus-integration-sync.d.ts.map +1 -0
- package/dist/git/focus-integration-sync.js +73 -0
- package/dist/git/focus-integration-sync.js.map +1 -0
- package/dist/git/focus-own-delta.d.ts +38 -0
- package/dist/git/focus-own-delta.d.ts.map +1 -0
- package/dist/git/focus-own-delta.js +193 -0
- package/dist/git/focus-own-delta.js.map +1 -0
- package/dist/git/index.d.ts +2 -0
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +4 -0
- package/dist/git/index.js.map +1 -1
- package/dist/git/merge.d.ts.map +1 -1
- package/dist/git/merge.js +11 -1
- package/dist/git/merge.js.map +1 -1
- package/dist/git/resolution-restore.d.ts +66 -0
- package/dist/git/resolution-restore.d.ts.map +1 -0
- package/dist/git/resolution-restore.js +331 -0
- package/dist/git/resolution-restore.js.map +1 -0
- package/dist/loops/engine.d.ts.map +1 -1
- package/dist/loops/engine.js.map +1 -1
- package/dist/merge/abandoned-merge-recovery.d.ts +72 -0
- package/dist/merge/abandoned-merge-recovery.d.ts.map +1 -0
- package/dist/merge/abandoned-merge-recovery.js +117 -0
- package/dist/merge/abandoned-merge-recovery.js.map +1 -0
- package/dist/merge/focus-merge.d.ts +19 -0
- package/dist/merge/focus-merge.d.ts.map +1 -1
- package/dist/merge/focus-merge.js +136 -99
- package/dist/merge/focus-merge.js.map +1 -1
- package/dist/merge/merge-back-activity.d.ts +11 -0
- package/dist/merge/merge-back-activity.d.ts.map +1 -0
- package/dist/merge/merge-back-activity.js +57 -0
- package/dist/merge/merge-back-activity.js.map +1 -0
- package/dist/merge/merge-back-conflict.d.ts +119 -0
- package/dist/merge/merge-back-conflict.d.ts.map +1 -0
- package/dist/merge/merge-back-conflict.js +178 -0
- package/dist/merge/merge-back-conflict.js.map +1 -0
- package/dist/merge/merge-back-followup.d.ts +109 -0
- package/dist/merge/merge-back-followup.d.ts.map +1 -0
- package/dist/merge/merge-back-followup.js +316 -0
- package/dist/merge/merge-back-followup.js.map +1 -0
- package/dist/merge/merge-back-loop.d.ts +2 -0
- package/dist/merge/merge-back-loop.d.ts.map +1 -1
- package/dist/merge/merge-back-loop.js +14 -15
- package/dist/merge/merge-back-loop.js.map +1 -1
- package/dist/merge/merge-conflict-escalation.d.ts +12 -0
- package/dist/merge/merge-conflict-escalation.d.ts.map +1 -1
- package/dist/merge/merge-conflict-escalation.js +37 -2
- package/dist/merge/merge-conflict-escalation.js.map +1 -1
- package/dist/merge/merge-gate-coverage.d.ts.map +1 -1
- package/dist/merge/merge-gate-coverage.js +1 -0
- package/dist/merge/merge-gate-coverage.js.map +1 -1
- package/dist/prompt/prompt-listing.d.ts +5 -0
- package/dist/prompt/prompt-listing.d.ts.map +1 -1
- package/dist/prompt/prompt-listing.js +35 -0
- package/dist/prompt/prompt-listing.js.map +1 -1
- package/dist/prompt-sections/routing-decider-sections.d.ts.map +1 -1
- package/dist/prompt-sections/routing-decider-sections.js +2 -0
- package/dist/prompt-sections/routing-decider-sections.js.map +1 -1
- package/dist/prompt-sections/shortfall-park-section.d.ts +43 -0
- package/dist/prompt-sections/shortfall-park-section.d.ts.map +1 -0
- package/dist/prompt-sections/shortfall-park-section.js +36 -0
- package/dist/prompt-sections/shortfall-park-section.js.map +1 -0
- package/dist/queries/agent-resolvable-promotion.d.ts +16 -2
- package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
- package/dist/queries/agent-resolvable-promotion.js +30 -3
- package/dist/queries/agent-resolvable-promotion.js.map +1 -1
- package/dist/queries/cause-key.d.ts +17 -22
- package/dist/queries/cause-key.d.ts.map +1 -1
- package/dist/queries/cause-key.js +50 -1
- package/dist/queries/cause-key.js.map +1 -1
- package/dist/queries/commit-attributions.d.ts +18 -0
- package/dist/queries/commit-attributions.d.ts.map +1 -0
- package/dist/queries/commit-attributions.js +39 -0
- package/dist/queries/commit-attributions.js.map +1 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +3 -1
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/delivery-gate-state.d.ts +21 -5
- package/dist/queries/delivery-gate-state.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state.js +35 -9
- package/dist/queries/delivery-gate-state.js.map +1 -1
- package/dist/queries/escalation-consumption.d.ts.map +1 -1
- package/dist/queries/escalation-consumption.js +2 -1
- package/dist/queries/escalation-consumption.js.map +1 -1
- package/dist/queries/escalation-field-bounds.d.ts +48 -0
- package/dist/queries/escalation-field-bounds.d.ts.map +1 -0
- package/dist/queries/escalation-field-bounds.js +83 -0
- package/dist/queries/escalation-field-bounds.js.map +1 -0
- package/dist/queries/escalation-refusal.d.ts +88 -0
- package/dist/queries/escalation-refusal.d.ts.map +1 -0
- package/dist/queries/escalation-refusal.js +217 -0
- package/dist/queries/escalation-refusal.js.map +1 -0
- package/dist/queries/escalation-suppression.d.ts +6 -0
- package/dist/queries/escalation-suppression.d.ts.map +1 -1
- package/dist/queries/escalation-suppression.js +4 -13
- package/dist/queries/escalation-suppression.js.map +1 -1
- package/dist/queries/escalations.d.ts +5 -13
- package/dist/queries/escalations.d.ts.map +1 -1
- package/dist/queries/escalations.js +25 -30
- package/dist/queries/escalations.js.map +1 -1
- package/dist/queries/filing-observation.d.ts.map +1 -1
- package/dist/queries/filing-observation.js +3 -1
- package/dist/queries/filing-observation.js.map +1 -1
- package/dist/queries/focus-review-approval.d.ts +2 -0
- package/dist/queries/focus-review-approval.d.ts.map +1 -1
- package/dist/queries/focus-review-approval.js +7 -2
- package/dist/queries/focus-review-approval.js.map +1 -1
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +2 -1
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/incident-correlation.d.ts.map +1 -1
- package/dist/queries/incident-correlation.js +4 -1
- package/dist/queries/incident-correlation.js.map +1 -1
- package/dist/queries/misattributed-commits-record.d.ts +26 -0
- package/dist/queries/misattributed-commits-record.d.ts.map +1 -0
- package/dist/queries/misattributed-commits-record.js +5 -0
- package/dist/queries/misattributed-commits-record.js.map +1 -0
- package/dist/queries/reality-tree.d.ts.map +1 -1
- package/dist/queries/reality-tree.js +2 -1
- package/dist/queries/reality-tree.js.map +1 -1
- package/dist/queries/schemas.d.ts +3 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +1 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/shared.js +1 -1
- package/dist/queries/shortfall-park-record.d.ts +48 -0
- package/dist/queries/shortfall-park-record.d.ts.map +1 -0
- package/dist/queries/shortfall-park-record.js +31 -0
- package/dist/queries/shortfall-park-record.js.map +1 -0
- package/dist/queries/shortfall-rulings.d.ts +71 -0
- package/dist/queries/shortfall-rulings.d.ts.map +1 -0
- package/dist/queries/shortfall-rulings.js +53 -0
- package/dist/queries/shortfall-rulings.js.map +1 -0
- package/dist/queries/verification.d.ts +20 -1
- package/dist/queries/verification.d.ts.map +1 -1
- package/dist/queries/verification.js +48 -8
- package/dist/queries/verification.js.map +1 -1
- package/dist/resolvers/index.d.ts +2 -2
- package/dist/resolvers/index.d.ts.map +1 -1
- package/dist/resolvers/index.js +1 -1
- package/dist/resolvers/index.js.map +1 -1
- package/dist/resolvers/shared/delivery-trailer.d.ts +99 -0
- package/dist/resolvers/shared/delivery-trailer.d.ts.map +1 -0
- package/dist/resolvers/shared/delivery-trailer.js +103 -0
- package/dist/resolvers/shared/delivery-trailer.js.map +1 -0
- package/dist/resolvers/shared/git-diff.d.ts +11 -84
- package/dist/resolvers/shared/git-diff.d.ts.map +1 -1
- package/dist/resolvers/shared/git-diff.js +17 -84
- package/dist/resolvers/shared/git-diff.js.map +1 -1
- package/dist/resolvers/shared/review-window-commits.d.ts.map +1 -1
- package/dist/resolvers/shared/review-window-commits.js +1 -0
- package/dist/resolvers/shared/review-window-commits.js.map +1 -1
- package/dist/session/session-lineage.d.ts.map +1 -1
- package/dist/session/session-lineage.js +1 -0
- package/dist/session/session-lineage.js.map +1 -1
- package/dist/shell/config.d.ts +2 -1
- package/dist/shell/config.d.ts.map +1 -1
- package/dist/shell/config.js +21 -4
- package/dist/shell/config.js.map +1 -1
- package/dist/shell/crash-recovery.d.ts +3 -7
- package/dist/shell/crash-recovery.d.ts.map +1 -1
- package/dist/shell/crash-recovery.js +28 -31
- package/dist/shell/crash-recovery.js.map +1 -1
- package/dist/shell/orphan-capture.d.ts +42 -0
- package/dist/shell/orphan-capture.d.ts.map +1 -0
- package/dist/shell/orphan-capture.js +53 -0
- package/dist/shell/orphan-capture.js.map +1 -0
- package/dist/shell/worker-pidfile.d.ts +22 -4
- package/dist/shell/worker-pidfile.d.ts.map +1 -1
- package/dist/shell/worker-pidfile.js +51 -13
- package/dist/shell/worker-pidfile.js.map +1 -1
- package/dist/spawn/helpers.d.ts +3 -0
- package/dist/spawn/helpers.d.ts.map +1 -1
- package/dist/spawn/helpers.js +28 -9
- package/dist/spawn/helpers.js.map +1 -1
- package/dist/spawn/lead-launch.d.ts +34 -0
- package/dist/spawn/lead-launch.d.ts.map +1 -0
- package/dist/spawn/lead-launch.js +34 -0
- package/dist/spawn/lead-launch.js.map +1 -0
- package/dist/spawn/lead-process-ownership.d.ts +91 -0
- package/dist/spawn/lead-process-ownership.d.ts.map +1 -0
- package/dist/spawn/lead-process-ownership.js +120 -0
- package/dist/spawn/lead-process-ownership.js.map +1 -0
- package/dist/spawn/lifecycle.d.ts +43 -3
- package/dist/spawn/lifecycle.d.ts.map +1 -1
- package/dist/spawn/lifecycle.js +75 -44
- package/dist/spawn/lifecycle.js.map +1 -1
- package/dist/spawn/liveness.d.ts.map +1 -1
- package/dist/spawn/liveness.js +5 -1
- package/dist/spawn/liveness.js.map +1 -1
- package/dist/spawn/one-shot-process-ownership.d.ts +48 -0
- package/dist/spawn/one-shot-process-ownership.d.ts.map +1 -0
- package/dist/spawn/one-shot-process-ownership.js +44 -0
- package/dist/spawn/one-shot-process-ownership.js.map +1 -0
- package/dist/spawn/process-group-kill.d.ts +79 -0
- package/dist/spawn/process-group-kill.d.ts.map +1 -0
- package/dist/spawn/process-group-kill.js +210 -0
- package/dist/spawn/process-group-kill.js.map +1 -0
- package/dist/spawn/process-spawn.d.ts +18 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +51 -20
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/resolution-budget.d.ts +38 -0
- package/dist/spawn/resolution-budget.d.ts.map +1 -0
- package/dist/spawn/resolution-budget.js +87 -0
- package/dist/spawn/resolution-budget.js.map +1 -0
- package/dist/spawn/review-hold-park.d.ts.map +1 -1
- package/dist/spawn/review-hold-park.js +37 -5
- package/dist/spawn/review-hold-park.js.map +1 -1
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +9 -0
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/untracked-lead-sweep.d.ts +29 -0
- package/dist/spawn/untracked-lead-sweep.d.ts.map +1 -0
- package/dist/spawn/untracked-lead-sweep.js +100 -0
- package/dist/spawn/untracked-lead-sweep.js.map +1 -0
- package/dist/spawn/worker-registry.d.ts.map +1 -1
- package/dist/spawn/worker-registry.js +4 -1
- package/dist/spawn/worker-registry.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +8 -6
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/testing/fixtures/exit-capture-verify.d.ts +41 -0
- package/dist/testing/fixtures/exit-capture-verify.d.ts.map +1 -0
- package/dist/testing/fixtures/exit-capture-verify.js +152 -0
- package/dist/testing/fixtures/exit-capture-verify.js.map +1 -0
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/testing/fixtures/pending-spawn-world.d.ts +7 -0
- package/dist/testing/fixtures/pending-spawn-world.d.ts.map +1 -1
- package/dist/testing/fixtures/pending-spawn-world.js +21 -1
- package/dist/testing/fixtures/pending-spawn-world.js.map +1 -1
- package/dist/testing/fixtures/stubborn-lead-process.d.ts +22 -0
- package/dist/testing/fixtures/stubborn-lead-process.d.ts.map +1 -0
- package/dist/testing/fixtures/stubborn-lead-process.js +68 -0
- package/dist/testing/fixtures/stubborn-lead-process.js.map +1 -0
- package/dist/types/config.d.ts +13 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/focus.d.ts +7 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/verify/commit-verdict-record.d.ts +13 -3
- package/dist/verify/commit-verdict-record.d.ts.map +1 -1
- package/dist/verify/commit-verdict-record.js +45 -25
- package/dist/verify/commit-verdict-record.js.map +1 -1
- package/dist/verify/credit-rearm.d.ts +18 -0
- package/dist/verify/credit-rearm.d.ts.map +1 -0
- package/dist/verify/credit-rearm.js +69 -0
- package/dist/verify/credit-rearm.js.map +1 -0
- package/dist/verify/delivery-commit-set.d.ts +10 -0
- package/dist/verify/delivery-commit-set.d.ts.map +1 -0
- package/dist/verify/delivery-commit-set.js +82 -0
- package/dist/verify/delivery-commit-set.js.map +1 -0
- package/dist/verify/explicit-rearm.d.ts.map +1 -1
- package/dist/verify/explicit-rearm.js +2 -0
- package/dist/verify/explicit-rearm.js.map +1 -1
- package/dist/verify/failure-signature.d.ts.map +1 -1
- package/dist/verify/failure-signature.js +1 -0
- package/dist/verify/failure-signature.js.map +1 -1
- package/dist/verify/gate-run-registry.d.ts.map +1 -1
- package/dist/verify/gate-run-registry.js +3 -0
- package/dist/verify/gate-run-registry.js.map +1 -1
- package/dist/verify/injection-stamp.d.ts +29 -0
- package/dist/verify/injection-stamp.d.ts.map +1 -0
- package/dist/verify/injection-stamp.js +139 -0
- package/dist/verify/injection-stamp.js.map +1 -0
- package/dist/verify/misattributed-commits-read.d.ts +31 -0
- package/dist/verify/misattributed-commits-read.d.ts.map +1 -0
- package/dist/verify/misattributed-commits-read.js +47 -0
- package/dist/verify/misattributed-commits-read.js.map +1 -0
- package/dist/verify/misattributed-commits.d.ts +52 -0
- package/dist/verify/misattributed-commits.d.ts.map +1 -0
- package/dist/verify/misattributed-commits.js +148 -0
- package/dist/verify/misattributed-commits.js.map +1 -0
- package/dist/verify/no-own-delta-verdict.d.ts +47 -0
- package/dist/verify/no-own-delta-verdict.d.ts.map +1 -0
- package/dist/verify/no-own-delta-verdict.js +74 -0
- package/dist/verify/no-own-delta-verdict.js.map +1 -0
- package/dist/verify/shortfall-park-escalation.d.ts +41 -0
- package/dist/verify/shortfall-park-escalation.d.ts.map +1 -0
- package/dist/verify/shortfall-park-escalation.js +177 -0
- package/dist/verify/shortfall-park-escalation.js.map +1 -0
- package/dist/verify/shortfall-park.d.ts +109 -0
- package/dist/verify/shortfall-park.d.ts.map +1 -0
- package/dist/verify/shortfall-park.js +208 -0
- package/dist/verify/shortfall-park.js.map +1 -0
- package/dist/verify/shortfall-repeat.d.ts +47 -16
- package/dist/verify/shortfall-repeat.d.ts.map +1 -1
- package/dist/verify/shortfall-repeat.js +77 -23
- package/dist/verify/shortfall-repeat.js.map +1 -1
- package/dist/verify/shortfall-rulings.d.ts +106 -0
- package/dist/verify/shortfall-rulings.d.ts.map +1 -0
- package/dist/verify/shortfall-rulings.js +118 -0
- package/dist/verify/shortfall-rulings.js.map +1 -0
- package/dist/verify/stall-detectors.d.ts.map +1 -1
- package/dist/verify/stall-detectors.js +10 -4
- package/dist/verify/stall-detectors.js.map +1 -1
- package/dist/verify/verification-attribution-base.d.ts +26 -0
- package/dist/verify/verification-attribution-base.d.ts.map +1 -0
- package/dist/verify/verification-attribution-base.js +127 -0
- package/dist/verify/verification-attribution-base.js.map +1 -0
- package/dist/verify/verification-attribution-deps.d.ts +32 -0
- package/dist/verify/verification-attribution-deps.d.ts.map +1 -0
- package/dist/verify/verification-attribution-deps.js +209 -0
- package/dist/verify/verification-attribution-deps.js.map +1 -0
- package/dist/verify/verification-baseline.d.ts +6 -0
- package/dist/verify/verification-baseline.d.ts.map +1 -1
- package/dist/verify/verification-baseline.js +27 -11
- package/dist/verify/verification-baseline.js.map +1 -1
- package/dist/verify/verification-commit-sharing.d.ts.map +1 -1
- package/dist/verify/verification-commit-sharing.js +30 -8
- package/dist/verify/verification-commit-sharing.js.map +1 -1
- package/dist/verify/verification-config.d.ts.map +1 -1
- package/dist/verify/verification-config.js +2 -0
- package/dist/verify/verification-config.js.map +1 -1
- package/dist/verify/verification-default-on.d.ts +3 -0
- package/dist/verify/verification-default-on.d.ts.map +1 -1
- package/dist/verify/verification-default-on.js +5 -1
- package/dist/verify/verification-default-on.js.map +1 -1
- package/dist/verify/verification-deps-default.d.ts.map +1 -1
- package/dist/verify/verification-deps-default.js +61 -120
- package/dist/verify/verification-deps-default.js.map +1 -1
- package/dist/verify/verification-deps.d.ts +26 -4
- package/dist/verify/verification-deps.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.d.ts +15 -0
- package/dist/verify/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.js +1 -12
- package/dist/verify/verification-deterministic.js.map +1 -1
- package/dist/verify/verification-engine.d.ts +9 -6
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +43 -62
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts +2 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js +19 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
- package/dist/verify/verification-failure-parser.d.ts +57 -1
- package/dist/verify/verification-failure-parser.d.ts.map +1 -1
- package/dist/verify/verification-failure-parser.js +116 -10
- package/dist/verify/verification-failure-parser.js.map +1 -1
- package/dist/verify/verification-feedback.d.ts +60 -43
- package/dist/verify/verification-feedback.d.ts.map +1 -1
- package/dist/verify/verification-feedback.js +115 -120
- package/dist/verify/verification-feedback.js.map +1 -1
- package/dist/verify/verification-history-reset.d.ts +2 -1
- package/dist/verify/verification-history-reset.d.ts.map +1 -1
- package/dist/verify/verification-history-reset.js +4 -2
- package/dist/verify/verification-history-reset.js.map +1 -1
- package/dist/verify/verification-rework-issue.d.ts +87 -0
- package/dist/verify/verification-rework-issue.d.ts.map +1 -0
- package/dist/verify/verification-rework-issue.js +212 -0
- package/dist/verify/verification-rework-issue.js.map +1 -0
- package/dist/verify/verification-rework-messages.d.ts.map +1 -1
- package/dist/verify/verification-rework-messages.js +10 -0
- package/dist/verify/verification-rework-messages.js.map +1 -1
- package/dist/verify/verify-gate-stall.d.ts +2 -0
- package/dist/verify/verify-gate-stall.d.ts.map +1 -1
- package/dist/verify/verify-gate-stall.js +35 -9
- package/dist/verify/verify-gate-stall.js.map +1 -1
- package/dist/verify/verify-restatement.d.ts.map +1 -1
- package/dist/verify/verify-restatement.js +9 -2
- package/dist/verify/verify-restatement.js.map +1 -1
- package/dist/worktree/capture-attribution-read.d.ts +47 -0
- package/dist/worktree/capture-attribution-read.d.ts.map +1 -0
- package/dist/worktree/capture-attribution-read.js +98 -0
- package/dist/worktree/capture-attribution-read.js.map +1 -0
- package/dist/worktree/capture-attribution.d.ts +26 -0
- package/dist/worktree/capture-attribution.d.ts.map +1 -0
- package/dist/worktree/capture-attribution.js +111 -0
- package/dist/worktree/capture-attribution.js.map +1 -0
- package/dist/worktree/index.d.ts.map +1 -1
- package/dist/worktree/index.js +2 -1
- package/dist/worktree/index.js.map +1 -1
- package/dist/worktree/provision.d.ts.map +1 -1
- package/dist/worktree/provision.js +23 -6
- package/dist/worktree/provision.js.map +1 -1
- package/dist/worktree/safety.d.ts +14 -4
- package/dist/worktree/safety.d.ts.map +1 -1
- package/dist/worktree/safety.js +37 -7
- package/dist/worktree/safety.js.map +1 -1
- package/dist/worktree/scratch-guard.d.ts +53 -2
- package/dist/worktree/scratch-guard.d.ts.map +1 -1
- package/dist/worktree/scratch-guard.js +115 -30
- package/dist/worktree/scratch-guard.js.map +1 -1
- package/dist/worktree/unmerged-capture-escalation.d.ts +52 -0
- package/dist/worktree/unmerged-capture-escalation.d.ts.map +1 -0
- package/dist/worktree/unmerged-capture-escalation.js +64 -0
- package/dist/worktree/unmerged-capture-escalation.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-launch.d.ts","sourceRoot":"","sources":["../../src/spawn/lead-launch.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAOxF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,8BAA8B,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,qBAAqB,EAC3B,SAAS,GAAE,iBAAsB,GAChC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAsBhD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/spawn/lead-launch.test.ts
|
|
3
|
+
// PINNED-BY-END
|
|
4
|
+
import { recordLeadProcess } from './lead-process-ownership.js';
|
|
5
|
+
import { endSupersededLeadProcesses } from './untracked-lead-sweep.js';
|
|
6
|
+
import { processGroupEnded } from './process-group-kill.js';
|
|
7
|
+
import { createLogger } from '../shell/log.js';
|
|
8
|
+
const log = createLogger({ module: 'lead-launch' });
|
|
9
|
+
/**
|
|
10
|
+
* End the focus's untracked previous leads, then spawn and record the new lead.
|
|
11
|
+
* Returns the lead, or null when the spawn was refused because a previous lead
|
|
12
|
+
* could not be ended (the caller fails the session and releases the team).
|
|
13
|
+
*/
|
|
14
|
+
export async function launchLeadProcess(args, ownership = {}) {
|
|
15
|
+
const { focusId, focusName, teamState, sessionId } = args;
|
|
16
|
+
const superseded = await endSupersededLeadProcesses(focusId, focusName, ownership);
|
|
17
|
+
const unended = superseded.filter((r) => !processGroupEnded(r));
|
|
18
|
+
if (unended.length > 0) {
|
|
19
|
+
log.error(`Refusing spawn for "${focusName}": previous lead process group(s) ` +
|
|
20
|
+
`${unended.map((r) => `${r.pgid} (${r.outcome}${r.detail ? `: ${r.detail}` : ''})`).join(', ')} could not be ended -- ` +
|
|
21
|
+
'a new lead must not supersede a live one; their ownership records are kept for the untracked-process sweep', { focusId });
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const proc = args.spawn();
|
|
25
|
+
teamState.leadPid = proc.pid ?? null;
|
|
26
|
+
teamState.leadStdin = proc.stdin;
|
|
27
|
+
teamState.phase = 'executing';
|
|
28
|
+
// Record the detached lead (pid, start identity, focus, session) on the team
|
|
29
|
+
// state and in the pidfile: a hard daemon kill group-reaps it from there, and
|
|
30
|
+
// the running daemon's ownership passes read it.
|
|
31
|
+
recordLeadProcess(proc, teamState, sessionId, ownership);
|
|
32
|
+
return proc;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=lead-launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-launch.js","sourceRoot":"","sources":["../../src/spawn/lead-launch.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,2DAA2D;AAC3D,gBAAgB;AAqBhB,OAAO,EAAE,iBAAiB,EAA0B,MAAM,6BAA6B,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;AAWpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAA2B,EAC3B,YAA+B,EAAE;IAEjC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,UAAU,GAAG,MAAM,0BAA0B,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CACP,uBAAuB,SAAS,oCAAoC;YACpE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB;YACvH,4GAA4G,EAC5G,EAAE,OAAO,EAAE,CACZ,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IAC1B,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC;IACrC,SAAS,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;IACjC,SAAS,CAAC,KAAK,GAAG,WAAW,CAAC;IAC9B,6EAA6E;IAC7E,8EAA8E;IAC9E,iDAAiD;IACjD,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which agent processes this daemon owns, and ending the ones no team does.
|
|
3
|
+
*
|
|
4
|
+
* Every team lead the daemon spawns is recorded -- pid, process group (== pid,
|
|
5
|
+
* the lead is spawned detached), start identity, focus and session -- both on
|
|
6
|
+
* its FocusTeamState and in the persisted worker pidfile (shell/worker-pidfile).
|
|
7
|
+
* The running daemon reads that record, not only the boot reap and
|
|
8
|
+
* `stop --force`, so it can:
|
|
9
|
+
*
|
|
10
|
+
* - end a team's whole process group at cleanup (`ensureTeamProcessGroupGone`),
|
|
11
|
+
* - verify a recorded pid still names the process it spawned, and
|
|
12
|
+
* - (spawn/untracked-lead-sweep.ts) end a focus's previous lead before a new
|
|
13
|
+
* spawn supersedes it, and any live lead it spawned that no team tracks.
|
|
14
|
+
*
|
|
15
|
+
* A recorded pid is signalled only when its identity is verified: the live
|
|
16
|
+
* process's start identity equals the one recorded at spawn. A pid that was
|
|
17
|
+
* reused by an unrelated process, or an entry with no recorded identity, is
|
|
18
|
+
* never signalled. A lead that has already exited is safe to group-signal: the
|
|
19
|
+
* kernel does not reuse a pid while a process group of that id still has
|
|
20
|
+
* members, so a live group with that id is the lead's own stragglers.
|
|
21
|
+
*/
|
|
22
|
+
import type { ChildProcess } from 'node:child_process';
|
|
23
|
+
import type { FocusTeamState } from '../types/index.js';
|
|
24
|
+
import { type WorkerPidEntry } from '../shell/worker-pidfile.js';
|
|
25
|
+
import { type ProcessGroupKillOptions, type ProcessGroupKillResult } from './process-group-kill.js';
|
|
26
|
+
/** Seams shared by the ownership passes; defaults touch the real OS and state. */
|
|
27
|
+
export interface LeadOwnershipDeps {
|
|
28
|
+
readEntries?: () => WorkerPidEntry[];
|
|
29
|
+
/** Persist one ownership record. Defaults to the worker pidfile. */
|
|
30
|
+
recordEntry?: (entry: WorkerPidEntry) => void;
|
|
31
|
+
removeEntry?: (pid: number) => void;
|
|
32
|
+
/** Pids a live team or one-shot worker currently owns. */
|
|
33
|
+
trackedPids?: () => Set<number>;
|
|
34
|
+
readStartIdentity?: (pid: number) => string | null;
|
|
35
|
+
/** True while any non-zombie member of the group is alive. */
|
|
36
|
+
isGroupAlive?: (pgid: number) => boolean;
|
|
37
|
+
terminate?: (pgid: number, opts: ProcessGroupKillOptions) => Promise<ProcessGroupKillResult>;
|
|
38
|
+
now?: () => number;
|
|
39
|
+
/** SIGTERM-to-SIGKILL grace for the team-cleanup kill. Defaults to PROCESS_GROUP_GRACE_MS. */
|
|
40
|
+
graceMs?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface ResolvedLeadOwnershipDeps {
|
|
43
|
+
readEntries: () => WorkerPidEntry[];
|
|
44
|
+
removeEntry: (pid: number) => void;
|
|
45
|
+
trackedPids: () => Set<number>;
|
|
46
|
+
readStartIdentity: (pid: number) => string | null;
|
|
47
|
+
isGroupAlive: (pgid: number) => boolean;
|
|
48
|
+
terminate: (pgid: number, opts: ProcessGroupKillOptions) => Promise<ProcessGroupKillResult>;
|
|
49
|
+
now: () => number;
|
|
50
|
+
}
|
|
51
|
+
/** Every pid a live team (lead) or registered one-shot worker owns right now. */
|
|
52
|
+
export declare function currentlyTrackedPids(): Set<number>;
|
|
53
|
+
export declare function resolveLeadOwnershipDeps(deps: LeadOwnershipDeps): ResolvedLeadOwnershipDeps;
|
|
54
|
+
/**
|
|
55
|
+
* Record a freshly spawned lead: its start identity on the team state, and its
|
|
56
|
+
* ownership record in the pidfile. The record is NOT dropped when the lead
|
|
57
|
+
* closes -- the lead's children can outlive it -- only once its whole process
|
|
58
|
+
* group is confirmed gone (`ensureTeamProcessGroupGone`, or the untracked-process
|
|
59
|
+
* sweep, which prunes a record whose group is already gone).
|
|
60
|
+
*/
|
|
61
|
+
export declare function recordLeadProcess(proc: ChildProcess, teamState: FocusTeamState, sessionId: string, deps?: Pick<LeadOwnershipDeps, 'recordEntry' | 'readStartIdentity'>): void;
|
|
62
|
+
/**
|
|
63
|
+
* True when the team's lead pid now names a DIFFERENT live process (pid reuse):
|
|
64
|
+
* a start identity was recorded at spawn and the live one differs. A gone lead
|
|
65
|
+
* (null live identity) or an unrecorded identity is not a mismatch.
|
|
66
|
+
*/
|
|
67
|
+
export declare function leadPidReused(team: Pick<FocusTeamState, 'leadPid' | 'leadStartIdentity'>, readStartIdentity?: (pid: number) => string | null): boolean;
|
|
68
|
+
/** Whether a team's process tree is confirmed gone, and why (or why not). */
|
|
69
|
+
export interface TeamProcessTreeVerdict {
|
|
70
|
+
/** True only when no process of this team's group is observed alive. */
|
|
71
|
+
gone: boolean;
|
|
72
|
+
detail: string;
|
|
73
|
+
/** The termination result, when the group was signalled. */
|
|
74
|
+
result: ProcessGroupKillResult | null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The team-cleanup guarantee: after a team's lead has closed, end anything left
|
|
78
|
+
* in its process group (subagents, MCP children, or a lead that closed its
|
|
79
|
+
* stdio but did not exit) within the grace, SIGKILL stragglers, and CONFIRM the
|
|
80
|
+
* group is gone.
|
|
81
|
+
*
|
|
82
|
+
* Signals only a group whose lead was really spawned (a start identity was
|
|
83
|
+
* recorded), whose pid no other live team now owns, and which was not reused.
|
|
84
|
+
* Absence is confirmed only by observation: a group that survived SIGKILL, could
|
|
85
|
+
* not be signalled, or could not be verified is reported `gone: false` and its
|
|
86
|
+
* ownership record is KEPT -- so the untracked-process sweep keeps acting on it --
|
|
87
|
+
* with the failure logged. Only a confirmed-gone tree discharges the record.
|
|
88
|
+
* Never throws.
|
|
89
|
+
*/
|
|
90
|
+
export declare function ensureTeamProcessGroupGone(teamState: FocusTeamState, context: string, deps?: LeadOwnershipDeps): Promise<TeamProcessTreeVerdict>;
|
|
91
|
+
//# sourceMappingURL=lead-process-ownership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-process-ownership.d.ts","sourceRoot":"","sources":["../../src/spawn/lead-process-ownership.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAML,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC5B,MAAM,yBAAyB,CAAC;AAKjC,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,cAAc,EAAE,CAAC;IACrC,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD,8DAA8D;IAC9D,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC7F,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,cAAc,EAAE,CAAC;IACpC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC5F,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAOlD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,GAAG,yBAAyB,CAU3F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,mBAAmB,CAAM,GACtE,IAAI,CAUN;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,mBAAmB,CAAC,EAC3D,iBAAiB,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAA+B,GAC3E,OAAO,CAIT;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;CACvC;AA4BD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAcjC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/completion/team-teardown-kills-process-group.test.ts,
|
|
3
|
+
// packages/daemon/src/focus/session-process-reap.test.ts, packages/daemon/src/spawn/lead-launch.test.ts,
|
|
4
|
+
// packages/daemon/src/spawn/lead-process-ownership.test.ts,
|
|
5
|
+
// packages/daemon/src/spawn/spawn-team-process-ownership.test.ts,
|
|
6
|
+
// packages/daemon/src/spawn/untracked-lead-sweep.test.ts
|
|
7
|
+
// PINNED-BY-END
|
|
8
|
+
import { getActiveTeams } from '../focus/focus-team-state.js';
|
|
9
|
+
import { listTrackedWorkers } from './worker-registry.js';
|
|
10
|
+
import { readWorkerPidfile, recordWorkerProcess, removeWorkerPid, } from '../shell/worker-pidfile.js';
|
|
11
|
+
import { PROCESS_GROUP_GRACE_MS, isProcessGroupAlive, processGroupEnded, readProcessStartIdentity, terminateProcessGroup, } from './process-group-kill.js';
|
|
12
|
+
import { createLogger } from '../shell/log.js';
|
|
13
|
+
const log = createLogger({ module: 'lead-process-ownership' });
|
|
14
|
+
/** Every pid a live team (lead) or registered one-shot worker owns right now. */
|
|
15
|
+
export function currentlyTrackedPids() {
|
|
16
|
+
const pids = new Set();
|
|
17
|
+
for (const team of getActiveTeams().values()) {
|
|
18
|
+
if (typeof team.leadPid === 'number')
|
|
19
|
+
pids.add(team.leadPid);
|
|
20
|
+
}
|
|
21
|
+
for (const worker of listTrackedWorkers())
|
|
22
|
+
pids.add(worker.pid);
|
|
23
|
+
return pids;
|
|
24
|
+
}
|
|
25
|
+
export function resolveLeadOwnershipDeps(deps) {
|
|
26
|
+
return {
|
|
27
|
+
readEntries: deps.readEntries ?? (() => readWorkerPidfile()),
|
|
28
|
+
removeEntry: deps.removeEntry ?? ((pid) => removeWorkerPid(pid)),
|
|
29
|
+
trackedPids: deps.trackedPids ?? currentlyTrackedPids,
|
|
30
|
+
readStartIdentity: deps.readStartIdentity ?? readProcessStartIdentity,
|
|
31
|
+
isGroupAlive: deps.isGroupAlive ?? isProcessGroupAlive,
|
|
32
|
+
terminate: deps.terminate ?? ((pgid, opts) => terminateProcessGroup(pgid, opts)),
|
|
33
|
+
now: deps.now ?? Date.now,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Record a freshly spawned lead: its start identity on the team state, and its
|
|
38
|
+
* ownership record in the pidfile. The record is NOT dropped when the lead
|
|
39
|
+
* closes -- the lead's children can outlive it -- only once its whole process
|
|
40
|
+
* group is confirmed gone (`ensureTeamProcessGroupGone`, or the untracked-process
|
|
41
|
+
* sweep, which prunes a record whose group is already gone).
|
|
42
|
+
*/
|
|
43
|
+
export function recordLeadProcess(proc, teamState, sessionId, deps = {}) {
|
|
44
|
+
if (typeof proc.pid !== 'number')
|
|
45
|
+
return;
|
|
46
|
+
const leadPid = proc.pid;
|
|
47
|
+
const startIdentity = (deps.readStartIdentity ?? readProcessStartIdentity)(leadPid);
|
|
48
|
+
teamState.leadStartIdentity = startIdentity;
|
|
49
|
+
const entry = {
|
|
50
|
+
pid: leadPid, label: teamState.focusName, focusId: teamState.focusId, sessionId, recordedAt: Date.now(),
|
|
51
|
+
};
|
|
52
|
+
if (startIdentity !== null)
|
|
53
|
+
entry.startIdentity = startIdentity;
|
|
54
|
+
(deps.recordEntry ?? ((e) => recordWorkerProcess(e)))(entry);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* True when the team's lead pid now names a DIFFERENT live process (pid reuse):
|
|
58
|
+
* a start identity was recorded at spawn and the live one differs. A gone lead
|
|
59
|
+
* (null live identity) or an unrecorded identity is not a mismatch.
|
|
60
|
+
*/
|
|
61
|
+
export function leadPidReused(team, readStartIdentity = readProcessStartIdentity) {
|
|
62
|
+
if (team.leadPid == null || team.leadStartIdentity == null)
|
|
63
|
+
return false;
|
|
64
|
+
const live = readStartIdentity(team.leadPid);
|
|
65
|
+
return live !== null && live !== team.leadStartIdentity;
|
|
66
|
+
}
|
|
67
|
+
async function resolveTeamProcessTree(teamState, context, d, graceMs) {
|
|
68
|
+
const pid = teamState.leadPid;
|
|
69
|
+
const verdict = (gone, detail, result = null) => ({ gone, detail, result });
|
|
70
|
+
if (pid == null)
|
|
71
|
+
return verdict(true, 'no lead process was spawned');
|
|
72
|
+
if (!Number.isInteger(pid) || pid <= 1)
|
|
73
|
+
return verdict(false, `lead pid ${pid} is not a signallable process group`);
|
|
74
|
+
// The kernel does not reuse an id a process group still holds, so a pid now
|
|
75
|
+
// owned by another team's lead, or reused by an unrelated process, means
|
|
76
|
+
// nothing of THIS team's group remains.
|
|
77
|
+
const ownedByAnotherTeam = [...getActiveTeams().values()].some((t) => t !== teamState && t.leadPid === pid);
|
|
78
|
+
if (ownedByAnotherTeam || leadPidReused(teamState, d.readStartIdentity)) {
|
|
79
|
+
return verdict(true, `pid ${pid} now belongs to another process, so no process of this team's group remains`);
|
|
80
|
+
}
|
|
81
|
+
if (teamState.leadStartIdentity == null) {
|
|
82
|
+
if (!d.isGroupAlive(pid))
|
|
83
|
+
return verdict(true, `process group ${pid} is already gone`);
|
|
84
|
+
return verdict(false, `no spawn identity was recorded for pid ${pid}, so its live process group cannot be verified as this team's and was not signalled`);
|
|
85
|
+
}
|
|
86
|
+
const result = await d.terminate(pid, { label: teamState.focusName, context, graceMs });
|
|
87
|
+
if (processGroupEnded(result))
|
|
88
|
+
return verdict(true, `process group ${pid} ${result.outcome}`, result);
|
|
89
|
+
return verdict(false, `process group ${pid} ${result.outcome}${result.detail ? ` (${result.detail})` : ''}`, result);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The team-cleanup guarantee: after a team's lead has closed, end anything left
|
|
93
|
+
* in its process group (subagents, MCP children, or a lead that closed its
|
|
94
|
+
* stdio but did not exit) within the grace, SIGKILL stragglers, and CONFIRM the
|
|
95
|
+
* group is gone.
|
|
96
|
+
*
|
|
97
|
+
* Signals only a group whose lead was really spawned (a start identity was
|
|
98
|
+
* recorded), whose pid no other live team now owns, and which was not reused.
|
|
99
|
+
* Absence is confirmed only by observation: a group that survived SIGKILL, could
|
|
100
|
+
* not be signalled, or could not be verified is reported `gone: false` and its
|
|
101
|
+
* ownership record is KEPT -- so the untracked-process sweep keeps acting on it --
|
|
102
|
+
* with the failure logged. Only a confirmed-gone tree discharges the record.
|
|
103
|
+
* Never throws.
|
|
104
|
+
*/
|
|
105
|
+
export async function ensureTeamProcessGroupGone(teamState, context, deps = {}) {
|
|
106
|
+
const d = resolveLeadOwnershipDeps(deps);
|
|
107
|
+
const verdict = await resolveTeamProcessTree(teamState, context, d, deps.graceMs ?? PROCESS_GROUP_GRACE_MS);
|
|
108
|
+
const pid = teamState.leadPid;
|
|
109
|
+
if (verdict.gone) {
|
|
110
|
+
if (pid != null)
|
|
111
|
+
d.removeEntry(pid);
|
|
112
|
+
log.debug(`[process-kill] ${context}: "${teamState.focusName}" process tree confirmed gone -- ${verdict.detail}`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
log.error(`[process-kill] ${context}: "${teamState.focusName}" process tree NOT confirmed gone -- ${verdict.detail}; ` +
|
|
116
|
+
'its ownership record is kept for the untracked-process sweep');
|
|
117
|
+
}
|
|
118
|
+
return verdict;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=lead-process-ownership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lead-process-ownership.js","sourceRoot":"","sources":["../../src/spawn/lead-process-ownership.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent lifecycle management — terminate, query, and resolution spawning.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the former spawner.ts monolith (now ./state.ts) — manages the lifecycle of running agent processes
|
|
5
|
+
* including termination, status queries, and merge conflict resolution agents.
|
|
6
|
+
*/
|
|
7
|
+
import { type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
1
8
|
import type { DaemonConfig } from '../types/index.js';
|
|
2
9
|
import { type TokenUsage } from '@telora/daemon-core';
|
|
10
|
+
import { type OneShotOwnershipDeps } from './one-shot-process-ownership.js';
|
|
3
11
|
import { type TelemetryMetricRow } from '../otlp/telemetry-writer.js';
|
|
12
|
+
import { openSystemSession, closeSystemSession } from '../queries/system-session.js';
|
|
13
|
+
import { type Logger } from '../shell/log.js';
|
|
4
14
|
/** A worker the reap path tracks: a focus team lead and its identity. */
|
|
5
15
|
export interface ReapableWorker {
|
|
6
16
|
/** PID of the team lead `claude --print` process. */
|
|
@@ -54,13 +64,33 @@ export declare function buildResolutionAgentPrompt(p: {
|
|
|
54
64
|
integrationBranch: string;
|
|
55
65
|
conflictFiles: string[];
|
|
56
66
|
focusDescription: string;
|
|
67
|
+
/** A previous run's preserved partial resolution (refs/telora/resolver-partial/...), or null. */
|
|
68
|
+
priorPartialRef?: string | null;
|
|
57
69
|
}): string;
|
|
70
|
+
/**
|
|
71
|
+
* The process, timer, log, session and ownership effects of {@link spawnResolutionAgent}.
|
|
72
|
+
* An optional seam so a test observes the real spawn/timeout log lines, the kill
|
|
73
|
+
* timer's budget and the ownership record without mock.module; production uses
|
|
74
|
+
* the defaults (the real session writes and ownership record).
|
|
75
|
+
*/
|
|
76
|
+
export interface ResolutionAgentEffects {
|
|
77
|
+
spawn: (command: string, args: string[], options: SpawnOptions) => ChildProcess;
|
|
78
|
+
setTimeout: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
|
|
79
|
+
/** Signals the resolver's process GROUP (it is spawned detached) by its leader pid. */
|
|
80
|
+
kill: (pid: number, signal: NodeJS.Signals) => void;
|
|
81
|
+
log: Pick<Logger, 'info' | 'warn' | 'error'>;
|
|
82
|
+
openSession?: typeof openSystemSession;
|
|
83
|
+
closeSession?: typeof closeSystemSession;
|
|
84
|
+
ownership?: OneShotOwnershipDeps;
|
|
85
|
+
}
|
|
58
86
|
/**
|
|
59
87
|
* Spawn a short-lived, single-purpose Claude Code agent for merge conflict resolution.
|
|
60
88
|
*
|
|
61
89
|
* Used when no active team exists for a focus but merge conflicts need resolution.
|
|
62
90
|
* The agent receives a focused prompt with conflict details, resolves in the existing
|
|
63
|
-
* worktree, and exits.
|
|
91
|
+
* worktree, and exits. It runs under a role-less 'merge' agent_sessions row whose
|
|
92
|
+
* process ownership is recorded (spawn/one-shot-process-ownership.ts). No
|
|
93
|
+
* streaming, no team mode.
|
|
64
94
|
*/
|
|
65
95
|
export declare function spawnResolutionAgent(params: {
|
|
66
96
|
config: DaemonConfig;
|
|
@@ -77,7 +107,15 @@ export declare function spawnResolutionAgent(params: {
|
|
|
77
107
|
* row is still created, just unscoped).
|
|
78
108
|
*/
|
|
79
109
|
focusId?: string | null;
|
|
80
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Scales the conflict-sized budget (an escalated retry). Absent => 1. The
|
|
112
|
+
* budget itself is computed from the conflict set or the product's configured
|
|
113
|
+
* `resolutionAgentTimeoutMs` (spawn/resolution-budget.ts).
|
|
114
|
+
*/
|
|
115
|
+
budgetMultiplier?: number;
|
|
116
|
+
/** A previous run's preserved partial resolution, named in the prompt. */
|
|
117
|
+
priorPartialRef?: string | null;
|
|
118
|
+
}, effects?: Partial<ResolutionAgentEffects>): Promise<{
|
|
81
119
|
success: boolean;
|
|
82
120
|
error?: string;
|
|
83
121
|
}>;
|
|
@@ -97,6 +135,8 @@ export interface AuditSpawnOptions {
|
|
|
97
135
|
* cross-cycle memory of the gap's trajectory. Omitted/null -> fresh session.
|
|
98
136
|
*/
|
|
99
137
|
resumeSessionId?: string | null;
|
|
138
|
+
/** The role-less 'audit' agent_sessions row this assessor runs under (its process ownership is recorded against it). */
|
|
139
|
+
agentSessionId?: string | null;
|
|
100
140
|
}
|
|
101
141
|
/** Result of an audit-assessor spawn. */
|
|
102
142
|
export interface AuditSpawnResult {
|
|
@@ -150,5 +190,5 @@ export declare function buildAuditMetricRow(params: {
|
|
|
150
190
|
* reality_tree_* MCP tools so it can mutate the graph but not touch workflow
|
|
151
191
|
* state.
|
|
152
192
|
*/
|
|
153
|
-
export declare function spawnAuditAgent(opts: AuditSpawnOptions): Promise<AuditSpawnResult>;
|
|
193
|
+
export declare function spawnAuditAgent(opts: AuditSpawnOptions, ownership?: OneShotOwnershipDeps): Promise<AuditSpawnResult>;
|
|
154
194
|
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/spawn/lifecycle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/spawn/lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/spawn/lifecycle.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
// packages/daemon/src/spawn/audit-spawn-reaches-spawn.test.ts,
|
|
4
4
|
// packages/daemon/src/spawn/audit-spawn-stream-json.test.ts,
|
|
5
5
|
// packages/daemon/src/spawn/audit-telemetry-capture.test.ts,
|
|
6
|
-
// packages/daemon/src/spawn/
|
|
6
|
+
// packages/daemon/src/spawn/one-shot-process-ownership.test.ts,
|
|
7
|
+
// packages/daemon/src/spawn/reap-worker-fleet.test.ts,
|
|
8
|
+
// packages/daemon/src/spawn/resolution-agent-budget.test.ts
|
|
7
9
|
// PINNED-BY-END
|
|
8
10
|
/**
|
|
9
11
|
* Agent lifecycle management — terminate, query, and resolution spawning.
|
|
@@ -12,11 +14,14 @@
|
|
|
12
14
|
* including termination, status queries, and merge conflict resolution agents.
|
|
13
15
|
*/
|
|
14
16
|
import { spawn } from 'node:child_process';
|
|
15
|
-
import {
|
|
17
|
+
import { readResolutionCompletion } from '../git/index.js';
|
|
18
|
+
import { computeResolutionAgentBudget, formatResolutionBudget, statConflictFiles } from './resolution-budget.js';
|
|
16
19
|
import { stripClaudeCodeEnvVars, StreamJsonParser, } from '@telora/daemon-core';
|
|
17
20
|
import { getActiveTeams } from '../focus/focus-team-state.js';
|
|
18
21
|
import { isProcessAlive } from './liveness.js';
|
|
19
|
-
import {
|
|
22
|
+
import { listTrackedWorkers } from './worker-registry.js';
|
|
23
|
+
import { ownOneShotProcess } from './one-shot-process-ownership.js';
|
|
24
|
+
import { signalProcessGroup } from './process-group-kill.js';
|
|
20
25
|
import { buildLeveredSpawnEnvironment } from './spawn-environment.js';
|
|
21
26
|
import { bufferMetric } from '../otlp/telemetry-writer.js';
|
|
22
27
|
import { estimateLoopCostUsd } from '../loops/index.js';
|
|
@@ -138,8 +143,6 @@ function defaultListWorkers() {
|
|
|
138
143
|
return [...byPid.values()];
|
|
139
144
|
}
|
|
140
145
|
// ── Merge Conflict Resolution Agent ──────────────────────────────────
|
|
141
|
-
/** Timeout for the resolution agent (10 minutes). */
|
|
142
|
-
const RESOLUTION_AGENT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
143
146
|
/**
|
|
144
147
|
* The resolution agent's prompt. Its steps come from the shared merge-resolution
|
|
145
148
|
* wording (the same the team-lead message and the prompt section send). Pure --
|
|
@@ -156,6 +159,17 @@ export function buildResolutionAgentPrompt(p) {
|
|
|
156
159
|
`**Conflicted files:**`,
|
|
157
160
|
...p.conflictFiles.map(f => `- ${f}`),
|
|
158
161
|
``,
|
|
162
|
+
...(p.priorPartialRef
|
|
163
|
+
? [
|
|
164
|
+
`**Earlier partial resolution:** a previous resolver ran out of time on this conflict. Its partial work is`,
|
|
165
|
+
`preserved at \`${p.priorPartialRef}\` (a commit whose tree holds its staged and unstaged edits). Inspect it`,
|
|
166
|
+
`per file with \`git show ${p.priorPartialRef}:<path>\` and reuse what is correct. It may still contain`,
|
|
167
|
+
`conflict markers, so check each file rather than copying the whole tree. Recover per file only: compare with`,
|
|
168
|
+
`\`git diff ${p.priorPartialRef} -- <path>\` and take what is right into the working copy, then \`git add <path>\`.`,
|
|
169
|
+
`Do NOT \`git checkout\`, \`git reset\` or \`git restore\` the ref (or its whole tree) over the in-progress merge.`,
|
|
170
|
+
``,
|
|
171
|
+
]
|
|
172
|
+
: []),
|
|
159
173
|
`**Focus context:** ${p.focusDescription}`,
|
|
160
174
|
``,
|
|
161
175
|
`**Instructions:**`,
|
|
@@ -163,16 +177,32 @@ export function buildResolutionAgentPrompt(p) {
|
|
|
163
177
|
`Follow the coding standards in CLAUDE.md.`,
|
|
164
178
|
].join('\n');
|
|
165
179
|
}
|
|
180
|
+
const DEFAULT_RESOLUTION_AGENT_EFFECTS = {
|
|
181
|
+
spawn: (command, args, options) => spawn(command, args, options),
|
|
182
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
183
|
+
kill: (pid, signal) => { signalProcessGroup(pid, signal); },
|
|
184
|
+
log,
|
|
185
|
+
openSession: openSystemSession,
|
|
186
|
+
closeSession: closeSystemSession,
|
|
187
|
+
};
|
|
166
188
|
/**
|
|
167
189
|
* Spawn a short-lived, single-purpose Claude Code agent for merge conflict resolution.
|
|
168
190
|
*
|
|
169
191
|
* Used when no active team exists for a focus but merge conflicts need resolution.
|
|
170
192
|
* The agent receives a focused prompt with conflict details, resolves in the existing
|
|
171
|
-
* worktree, and exits.
|
|
193
|
+
* worktree, and exits. It runs under a role-less 'merge' agent_sessions row whose
|
|
194
|
+
* process ownership is recorded (spawn/one-shot-process-ownership.ts). No
|
|
195
|
+
* streaming, no team mode.
|
|
172
196
|
*/
|
|
173
|
-
export async function spawnResolutionAgent(params) {
|
|
174
|
-
const
|
|
175
|
-
const
|
|
197
|
+
export async function spawnResolutionAgent(params, effects = {}) {
|
|
198
|
+
const fx = { ...DEFAULT_RESOLUTION_AGENT_EFFECTS, ...effects };
|
|
199
|
+
const { config, worktreePath, branchName, integrationBranch, conflictFiles, focusDescription, model, focusId, priorPartialRef } = params;
|
|
200
|
+
const budget = computeResolutionAgentBudget({
|
|
201
|
+
files: statConflictFiles(worktreePath, conflictFiles),
|
|
202
|
+
configuredMs: config.resolutionAgentTimeoutMs ?? null,
|
|
203
|
+
...(params.budgetMultiplier !== undefined ? { multiplier: params.budgetMultiplier } : {}),
|
|
204
|
+
});
|
|
205
|
+
const prompt = buildResolutionAgentPrompt({ worktreePath, branchName, integrationBranch, conflictFiles, focusDescription, priorPartialRef: priorPartialRef ?? null });
|
|
176
206
|
const args = [
|
|
177
207
|
'--print',
|
|
178
208
|
'--dangerously-skip-permissions',
|
|
@@ -182,64 +212,67 @@ export async function spawnResolutionAgent(params) {
|
|
|
182
212
|
];
|
|
183
213
|
// Build a clean environment (strip Claude Code session vars)
|
|
184
214
|
const spawnEnv = stripClaudeCodeEnvVars(process.env);
|
|
185
|
-
log.info(`Spawning resolution agent for branch ${branchName} (${conflictFiles.length} conflicts, model: ${model ?? 'default'})`);
|
|
215
|
+
fx.log.info(`Spawning resolution agent for branch ${branchName} (${conflictFiles.length} conflicts, model: ${model ?? 'default'}, ${formatResolutionBudget(budget)})`);
|
|
186
216
|
// Open a role-less 'merge' agent_sessions row so this --print one-shot's
|
|
187
217
|
// run-time reaches OEE Availability (merge is production work: landing
|
|
188
218
|
// delivered code). Best-effort -- a null id just means no row for this run.
|
|
189
|
-
const
|
|
219
|
+
const closeSession = fx.closeSession ?? closeSystemSession;
|
|
220
|
+
const mergeSessionId = await (fx.openSession ?? openSystemSession)({
|
|
190
221
|
organizationId: config.organizationId,
|
|
191
222
|
focusId: focusId ?? null,
|
|
192
223
|
branchName,
|
|
193
224
|
sessionType: 'merge',
|
|
194
225
|
});
|
|
195
226
|
return new Promise((resolve) => {
|
|
196
|
-
const proc = spawn(config.claudeCodePath, args, {
|
|
227
|
+
const proc = fx.spawn(config.claudeCodePath, args, {
|
|
197
228
|
cwd: worktreePath,
|
|
198
229
|
env: spawnEnv,
|
|
199
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
230
|
+
stdio: ['pipe', 'pipe', 'pipe'], detached: true, // own group: exit/timeout cleanup ends its MCP children too
|
|
200
231
|
});
|
|
201
|
-
//
|
|
202
|
-
|
|
232
|
+
// Track + record ownership so a graceful stop or the untracked sweep can end its whole tree.
|
|
233
|
+
ownOneShotProcess(proc, { label: `merge-resolution:${branchName}`, focusId: focusId ?? null, sessionId: mergeSessionId }, fx.ownership);
|
|
203
234
|
// Close stdin immediately -- prompt is passed as CLI arg
|
|
204
235
|
proc.stdin?.end();
|
|
205
236
|
// Timeout with escalating shutdown
|
|
206
|
-
const timeout = setTimeout(() => {
|
|
207
|
-
log.warn(`Resolution agent
|
|
208
|
-
|
|
209
|
-
process.kill(proc.pid, 'SIGTERM');
|
|
210
|
-
}
|
|
211
|
-
catch { /* ignore */ }
|
|
212
|
-
setTimeout(() => {
|
|
237
|
+
const timeout = fx.setTimeout(() => {
|
|
238
|
+
fx.log.warn(`Resolution agent for branch ${branchName} timed out, killing (${formatResolutionBudget(budget)})`);
|
|
239
|
+
if (proc.pid) {
|
|
213
240
|
try {
|
|
214
|
-
|
|
241
|
+
fx.kill(proc.pid, 'SIGTERM');
|
|
215
242
|
}
|
|
216
243
|
catch { /* ignore */ }
|
|
244
|
+
}
|
|
245
|
+
fx.setTimeout(() => {
|
|
246
|
+
if (proc.pid) {
|
|
247
|
+
try {
|
|
248
|
+
fx.kill(proc.pid, 'SIGKILL');
|
|
249
|
+
}
|
|
250
|
+
catch { /* ignore */ }
|
|
251
|
+
}
|
|
217
252
|
}, 10000);
|
|
218
|
-
},
|
|
253
|
+
}, budget.budgetMs);
|
|
219
254
|
proc.on('close', (code) => {
|
|
220
255
|
clearTimeout(timeout);
|
|
221
|
-
// Verify resolution
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const success = unmergedFiles.length === 0;
|
|
227
|
-
const msg = success ? undefined : `Conflicts remain in: ${unmergedFiles.join(', ')}`;
|
|
256
|
+
// Verify resolution: a finished merge (no unmerged paths, no MERGE_HEAD,
|
|
257
|
+
// nothing uncommitted), not merely "no unmerged files".
|
|
258
|
+
const completion = readResolutionCompletion(worktreePath);
|
|
259
|
+
const success = completion.finished;
|
|
260
|
+
const msg = success ? undefined : completion.reason;
|
|
228
261
|
if (success) {
|
|
229
|
-
log.info(`Resolution agent succeeded for branch ${branchName} (exit code: ${code})`);
|
|
262
|
+
fx.log.info(`Resolution agent succeeded for branch ${branchName} (exit code: ${code})`);
|
|
230
263
|
}
|
|
231
264
|
else {
|
|
232
|
-
log.warn(`Resolution agent failed for branch ${branchName}: ${msg}`);
|
|
265
|
+
fx.log.warn(`Resolution agent failed for branch ${branchName}: ${msg}`);
|
|
233
266
|
}
|
|
234
267
|
// Close the 'merge' session (stamps ended_at) before resolving, on either
|
|
235
268
|
// outcome. closeSystemSession never throws; .finally guarantees resolve.
|
|
236
|
-
void
|
|
269
|
+
void closeSession(mergeSessionId, { success, exitReason: msg })
|
|
237
270
|
.finally(() => resolve(success ? { success: true } : { success: false, error: msg }));
|
|
238
271
|
});
|
|
239
272
|
proc.on('error', (err) => {
|
|
240
273
|
clearTimeout(timeout);
|
|
241
|
-
log.error(`Resolution agent spawn error: ${err.message}`);
|
|
242
|
-
void
|
|
274
|
+
fx.log.error(`Resolution agent spawn error: ${err.message}`);
|
|
275
|
+
void closeSession(mergeSessionId, { success: false, exitReason: err.message })
|
|
243
276
|
.finally(() => resolve({ success: false, error: `Spawn error: ${err.message}` }));
|
|
244
277
|
});
|
|
245
278
|
});
|
|
@@ -309,7 +342,7 @@ export function buildAuditMetricRow(params) {
|
|
|
309
342
|
* reality_tree_* MCP tools so it can mutate the graph but not touch workflow
|
|
310
343
|
* state.
|
|
311
344
|
*/
|
|
312
|
-
export async function spawnAuditAgent(opts) {
|
|
345
|
+
export async function spawnAuditAgent(opts, ownership = {}) {
|
|
313
346
|
const { config, prompt, worktreePath, focusId, cycleNumber, model, timeoutMs, allowedTools, resumeSessionId } = opts;
|
|
314
347
|
const args = [];
|
|
315
348
|
// --resume must precede the output-format flags.
|
|
@@ -344,15 +377,15 @@ export async function spawnAuditAgent(opts) {
|
|
|
344
377
|
child = spawn(config.claudeCodePath, args, {
|
|
345
378
|
cwd: worktreePath ?? config.repoPath,
|
|
346
379
|
env: stripClaudeCodeEnvVars(spawnEnv, { preserve: ['CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS'] }),
|
|
347
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
380
|
+
stdio: ['pipe', 'pipe', 'pipe'], detached: true, // own group: exit/timeout cleanup ends its MCP children too
|
|
348
381
|
});
|
|
349
382
|
}
|
|
350
383
|
catch (err) {
|
|
351
384
|
resolve({ exitCode: null, stdout: '', stderr: '', errorMessage: `spawn failed: ${err.message}` });
|
|
352
385
|
return;
|
|
353
386
|
}
|
|
354
|
-
//
|
|
355
|
-
|
|
387
|
+
// Track + record ownership so a graceful stop or the untracked sweep can end its whole tree.
|
|
388
|
+
ownOneShotProcess(child, { label: `audit:${focusId} cycle ${cycleNumber}`, focusId, sessionId: opts.agentSessionId ?? null }, ownership);
|
|
356
389
|
child.stdin?.end();
|
|
357
390
|
// Raw stdout is retained as a fallback (e.g. a non-JSON stub). In normal
|
|
358
391
|
// operation stdout is NDJSON stream-json; the parser reconstructs the
|
|
@@ -402,10 +435,8 @@ export async function spawnAuditAgent(opts) {
|
|
|
402
435
|
child.stdout?.on('data', (chunk) => { rawStdout += chunk.toString('utf8'); });
|
|
403
436
|
child.stderr?.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
|
|
404
437
|
const timer = setTimeout(() => {
|
|
405
|
-
|
|
406
|
-
child.
|
|
407
|
-
}
|
|
408
|
-
catch { /* ignore */ }
|
|
438
|
+
if (child.pid)
|
|
439
|
+
signalProcessGroup(child.pid, 'SIGKILL');
|
|
409
440
|
}, timeoutMs);
|
|
410
441
|
// Prefer the reconstructed assistant text (so parseAuditSummary finds the
|
|
411
442
|
// fenced block); fall back to raw stdout when no stream-json was parsed.
|