@caupulican/pi-adaptative 0.81.38 → 0.81.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -0
- package/README.md +2 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +133 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +198 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +311 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +31 -2
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +823 -79
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +1043 -86
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
- package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
- package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
- package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +7 -7
- package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +24 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-directory-layout.d.ts +15 -0
- package/dist/core/agent-directory-layout.d.ts.map +1 -0
- package/dist/core/agent-directory-layout.js +72 -0
- package/dist/core/agent-directory-layout.js.map +1 -0
- package/dist/core/agent-paths.d.ts +66 -0
- package/dist/core/agent-paths.d.ts.map +1 -0
- package/dist/core/agent-paths.js +170 -0
- package/dist/core/agent-paths.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +52 -8
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +397 -79
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +4 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +255 -103
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +754 -1173
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/approval-gate.d.ts +3 -2
- package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
- package/dist/core/autonomy/approval-gate.js +6 -28
- package/dist/core/autonomy/approval-gate.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +1 -1
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +26 -9
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/foreground-envelope.d.ts +8 -2
- package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
- package/dist/core/autonomy/foreground-envelope.js +8 -25
- package/dist/core/autonomy/foreground-envelope.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +8 -10
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/autonomy/lane-private-paths.d.ts +2 -0
- package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -0
- package/dist/core/autonomy/lane-private-paths.js +16 -0
- package/dist/core/autonomy/lane-private-paths.js.map +1 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts +10 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +46 -8
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +24 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +43 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/autonomy/risk-assessment.d.ts +1 -2
- package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
- package/dist/core/autonomy/risk-assessment.js +1 -123
- package/dist/core/autonomy/risk-assessment.js.map +1 -1
- package/dist/core/autonomy/session-lane-record.d.ts +4 -4
- package/dist/core/autonomy/session-lane-record.d.ts.map +1 -1
- package/dist/core/autonomy/session-lane-record.js +16 -29
- package/dist/core/autonomy/session-lane-record.js.map +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts +1 -1
- package/dist/core/autonomy/telemetry-events.d.ts.map +1 -1
- package/dist/core/autonomy/telemetry-events.js +1 -1
- package/dist/core/autonomy/telemetry-events.js.map +1 -1
- package/dist/core/autonomy-telemetry.d.ts +7 -7
- package/dist/core/autonomy-telemetry.d.ts.map +1 -1
- package/dist/core/autonomy-telemetry.js +9 -9
- package/dist/core/autonomy-telemetry.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +90 -156
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +192 -968
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +4 -0
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -1
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/capability-contract.d.ts +4 -0
- package/dist/core/capability-contract.d.ts.map +1 -0
- package/dist/core/capability-contract.js +28 -0
- package/dist/core/capability-contract.js.map +1 -0
- package/dist/core/compaction-controller.d.ts +90 -0
- package/dist/core/compaction-controller.d.ts.map +1 -0
- package/dist/core/compaction-controller.js +452 -0
- package/dist/core/compaction-controller.js.map +1 -0
- package/dist/core/compaction-support.d.ts +24 -13
- package/dist/core/compaction-support.d.ts.map +1 -1
- package/dist/core/compaction-support.js +43 -7
- package/dist/core/compaction-support.js.map +1 -1
- package/dist/core/context/context-artifacts.d.ts +6 -2
- package/dist/core/context/context-artifacts.d.ts.map +1 -1
- package/dist/core/context/context-artifacts.js +34 -2
- package/dist/core/context/context-artifacts.js.map +1 -1
- package/dist/core/context/context-audit.d.ts +33 -1
- package/dist/core/context/context-audit.d.ts.map +1 -1
- package/dist/core/context/context-audit.js +31 -5
- package/dist/core/context/context-audit.js.map +1 -1
- package/dist/core/context/context-store-retention.d.ts +8 -2
- package/dist/core/context/context-store-retention.d.ts.map +1 -1
- package/dist/core/context/context-store-retention.js +158 -35
- package/dist/core/context/context-store-retention.js.map +1 -1
- package/dist/core/context/okf-memory.d.ts.map +1 -1
- package/dist/core/context/okf-memory.js +9 -11
- package/dist/core/context/okf-memory.js.map +1 -1
- package/dist/core/context/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +6 -8
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts +3 -0
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +26 -6
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/context-pipeline.d.ts +26 -4
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +146 -18
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/cost-guard.d.ts +19 -3
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +18 -3
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +1 -0
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/delegation/managed-lane-controller.d.ts +34 -0
- package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -0
- package/dist/core/delegation/managed-lane-controller.js +217 -0
- package/dist/core/delegation/managed-lane-controller.js.map +1 -0
- package/dist/core/delegation/session-worker-claim.d.ts +44 -0
- package/dist/core/delegation/session-worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/session-worker-claim.js +98 -0
- package/dist/core/delegation/session-worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-actions.d.ts +7 -5
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +20 -4
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-claim.d.ts +56 -0
- package/dist/core/delegation/worker-claim.d.ts.map +1 -0
- package/dist/core/delegation/worker-claim.js +239 -0
- package/dist/core/delegation/worker-claim.js.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts +110 -0
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-controller.js +861 -0
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -0
- package/dist/core/delegation/worker-delegation-request.d.ts +11 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -0
- package/dist/core/delegation/worker-delegation-request.js +2 -0
- package/dist/core/delegation/worker-delegation-request.js.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +40 -0
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js +93 -0
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -0
- package/dist/core/delegation/worker-execution-policy.d.ts +62 -0
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-execution-policy.js +160 -0
- package/dist/core/delegation/worker-execution-policy.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +14 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -0
- package/dist/core/delegation/worker-lane-projection.js +100 -0
- package/dist/core/delegation/worker-lane-projection.js.map +1 -0
- package/dist/core/delegation/worker-lifecycle.d.ts +97 -0
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -0
- package/dist/core/delegation/worker-lifecycle.js +339 -0
- package/dist/core/delegation/worker-lifecycle.js.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts +36 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-notification-coordinator.js +90 -0
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts +55 -0
- package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -0
- package/dist/core/delegation/worker-profile-resolver.js +113 -0
- package/dist/core/delegation/worker-profile-resolver.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +25 -14
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +122 -53
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-label.d.ts +3 -0
- package/dist/core/delegation/worker-task-label.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-label.js +11 -0
- package/dist/core/delegation/worker-task-label.js.map +1 -0
- package/dist/core/doctor.d.ts +8 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +32 -1
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +5 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +68 -13
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/storage.d.ts +8 -0
- package/dist/core/extensions/storage.d.ts.map +1 -0
- package/dist/core/extensions/storage.js +28 -0
- package/dist/core/extensions/storage.js.map +1 -0
- package/dist/core/extensions/types.d.ts +101 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/failure-corpus.d.ts +1 -15
- package/dist/core/failure-corpus.d.ts.map +1 -1
- package/dist/core/failure-corpus.js +9 -37
- package/dist/core/failure-corpus.js.map +1 -1
- package/dist/core/foreground-recovery-controller.d.ts +52 -0
- package/dist/core/foreground-recovery-controller.d.ts.map +1 -0
- package/dist/core/foreground-recovery-controller.js +111 -0
- package/dist/core/foreground-recovery-controller.js.map +1 -0
- package/dist/core/gateways/channel-provider.d.ts +3 -0
- package/dist/core/gateways/channel-provider.d.ts.map +1 -1
- package/dist/core/gateways/channel-provider.js +31 -6
- package/dist/core/gateways/channel-provider.js.map +1 -1
- package/dist/core/goal-loop-controller.d.ts +17 -1
- package/dist/core/goal-loop-controller.d.ts.map +1 -1
- package/dist/core/goal-loop-controller.js +79 -11
- package/dist/core/goal-loop-controller.js.map +1 -1
- package/dist/core/goals/goal-acceptance.d.ts +11 -0
- package/dist/core/goals/goal-acceptance.d.ts.map +1 -0
- package/dist/core/goals/goal-acceptance.js +25 -0
- package/dist/core/goals/goal-acceptance.js.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts +27 -0
- package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -0
- package/dist/core/goals/goal-auto-continue-controller.js +93 -0
- package/dist/core/goals/goal-auto-continue-controller.js.map +1 -0
- package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
- package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-controller.js +87 -0
- package/dist/core/goals/goal-continuation-controller.js.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
- package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-defaults.js +42 -0
- package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts +5 -1
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +56 -15
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-lifecycle.d.ts +20 -0
- package/dist/core/goals/goal-lifecycle.d.ts.map +1 -0
- package/dist/core/goals/goal-lifecycle.js +17 -0
- package/dist/core/goals/goal-lifecycle.js.map +1 -0
- package/dist/core/goals/goal-runtime-snapshot.d.ts +105 -4
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +91 -6
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +90 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +78 -15
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +58 -2
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +82 -6
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +13 -6
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +20 -28
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/human-input.d.ts +100 -0
- package/dist/core/human-input.d.ts.map +1 -0
- package/dist/core/human-input.js +362 -0
- package/dist/core/human-input.js.map +1 -0
- package/dist/core/index.d.ts +9 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/{telemetry.d.ts → install-telemetry.d.ts} +1 -1
- package/dist/core/install-telemetry.d.ts.map +1 -0
- package/dist/core/{telemetry.js → install-telemetry.js} +1 -1
- package/dist/core/install-telemetry.js.map +1 -0
- package/dist/core/keybindings.d.ts +27 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +34 -8
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/learning-audit.d.ts +2 -4
- package/dist/core/learning/learning-audit.d.ts.map +1 -1
- package/dist/core/learning/learning-audit.js +9 -29
- package/dist/core/learning/learning-audit.js.map +1 -1
- package/dist/core/learning/learning-gate.d.ts.map +1 -1
- package/dist/core/learning/learning-gate.js +1 -6
- package/dist/core/learning/learning-gate.js.map +1 -1
- package/dist/core/learning/observation-store.d.ts +12 -3
- package/dist/core/learning/observation-store.d.ts.map +1 -1
- package/dist/core/learning/observation-store.js +30 -15
- package/dist/core/learning/observation-store.js.map +1 -1
- package/dist/core/learning/session-learning-decision.d.ts +2 -4
- package/dist/core/learning/session-learning-decision.d.ts.map +1 -1
- package/dist/core/learning/session-learning-decision.js +9 -29
- package/dist/core/learning/session-learning-decision.js.map +1 -1
- package/dist/core/learning/skill-curator.d.ts +5 -1
- package/dist/core/learning/skill-curator.d.ts.map +1 -1
- package/dist/core/learning/skill-curator.js +21 -19
- package/dist/core/learning/skill-curator.js.map +1 -1
- package/dist/core/local-runtime-controller.d.ts +65 -3
- package/dist/core/local-runtime-controller.d.ts.map +1 -1
- package/dist/core/local-runtime-controller.js +186 -26
- package/dist/core/local-runtime-controller.js.map +1 -1
- package/dist/core/memory/providers/file-store.d.ts +1 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +6 -6
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/memory-controller.d.ts.map +1 -1
- package/dist/core/memory-controller.js +4 -4
- package/dist/core/memory-controller.js.map +1 -1
- package/dist/core/model-capability.d.ts +34 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +43 -1
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +6 -9
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-router/intent-classifier.d.ts +1 -4
- package/dist/core/model-router/intent-classifier.d.ts.map +1 -1
- package/dist/core/model-router/intent-classifier.js +1 -156
- package/dist/core/model-router/intent-classifier.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts +15 -0
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +23 -1
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +34 -7
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +95 -16
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/model-selection-controller.d.ts +4 -4
- package/dist/core/model-selection-controller.d.ts.map +1 -1
- package/dist/core/model-selection-controller.js.map +1 -1
- package/dist/core/model-tool-protocol.d.ts +23 -0
- package/dist/core/model-tool-protocol.d.ts.map +1 -0
- package/dist/core/model-tool-protocol.js +43 -0
- package/dist/core/model-tool-protocol.js.map +1 -0
- package/dist/core/models/adaptation-store.d.ts +10 -5
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +129 -81
- package/dist/core/models/adaptation-store.js.map +1 -1
- package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
- package/dist/core/models/default-model-suggestions.js +17 -0
- package/dist/core/models/default-model-suggestions.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts +4 -11
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +87 -51
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/host-state-store.d.ts +34 -0
- package/dist/core/models/host-state-store.d.ts.map +1 -0
- package/dist/core/models/host-state-store.js +86 -0
- package/dist/core/models/host-state-store.js.map +1 -0
- package/dist/core/models/llamacpp-runtime.d.ts +180 -0
- package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
- package/dist/core/models/llamacpp-runtime.js +475 -0
- package/dist/core/models/llamacpp-runtime.js.map +1 -0
- package/dist/core/models/local-registration.d.ts +40 -0
- package/dist/core/models/local-registration.d.ts.map +1 -1
- package/dist/core/models/local-registration.js +94 -5
- package/dist/core/models/local-registration.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +6 -5
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/models/model-ref.d.ts +7 -0
- package/dist/core/models/model-ref.d.ts.map +1 -1
- package/dist/core/models/model-ref.js +26 -0
- package/dist/core/models/model-ref.js.map +1 -1
- package/dist/core/models/needle-runtime.d.ts +257 -0
- package/dist/core/models/needle-runtime.d.ts.map +1 -0
- package/dist/core/models/needle-runtime.js +519 -0
- package/dist/core/models/needle-runtime.js.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
- package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
- package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
- package/dist/core/orchestration/agent-resume.d.ts +24 -0
- package/dist/core/orchestration/agent-resume.d.ts.map +1 -0
- package/dist/core/orchestration/agent-resume.js +90 -0
- package/dist/core/orchestration/agent-resume.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +61 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -0
- package/dist/core/orchestration/capability-gateway.js +152 -0
- package/dist/core/orchestration/capability-gateway.js.map +1 -0
- package/dist/core/orchestration/contracts.d.ts +343 -0
- package/dist/core/orchestration/contracts.d.ts.map +1 -0
- package/dist/core/orchestration/contracts.js +135 -0
- package/dist/core/orchestration/contracts.js.map +1 -0
- package/dist/core/orchestration/delegation-ledger.d.ts +56 -0
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -0
- package/dist/core/orchestration/delegation-ledger.js +197 -0
- package/dist/core/orchestration/delegation-ledger.js.map +1 -0
- package/dist/core/orchestration/event-store.d.ts +83 -0
- package/dist/core/orchestration/event-store.d.ts.map +1 -0
- package/dist/core/orchestration/event-store.js +560 -0
- package/dist/core/orchestration/event-store.js.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts +9 -0
- package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -0
- package/dist/core/orchestration/lane-tool-manifests.js +37 -0
- package/dist/core/orchestration/lane-tool-manifests.js.map +1 -0
- package/dist/core/orchestration/model-binding.d.ts +15 -0
- package/dist/core/orchestration/model-binding.d.ts.map +1 -0
- package/dist/core/orchestration/model-binding.js +29 -0
- package/dist/core/orchestration/model-binding.js.map +1 -0
- package/dist/core/orchestration/policy-compiler.d.ts +49 -0
- package/dist/core/orchestration/policy-compiler.d.ts.map +1 -0
- package/dist/core/orchestration/policy-compiler.js +191 -0
- package/dist/core/orchestration/policy-compiler.js.map +1 -0
- package/dist/core/orchestration/policy-gate.d.ts +18 -0
- package/dist/core/orchestration/policy-gate.d.ts.map +1 -0
- package/dist/core/orchestration/policy-gate.js +24 -0
- package/dist/core/orchestration/policy-gate.js.map +1 -0
- package/dist/core/orchestration/profile-registry.d.ts +51 -0
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -0
- package/dist/core/orchestration/profile-registry.js +393 -0
- package/dist/core/orchestration/profile-registry.js.map +1 -0
- package/dist/core/orchestration/profile-store.d.ts +31 -0
- package/dist/core/orchestration/profile-store.d.ts.map +1 -0
- package/dist/core/orchestration/profile-store.js +136 -0
- package/dist/core/orchestration/profile-store.js.map +1 -0
- package/dist/core/orchestration/risk-budget.d.ts +9 -0
- package/dist/core/orchestration/risk-budget.d.ts.map +1 -0
- package/dist/core/orchestration/risk-budget.js +48 -0
- package/dist/core/orchestration/risk-budget.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +168 -0
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime.js +1600 -0
- package/dist/core/orchestration/task-runtime.js.map +1 -0
- package/dist/core/orchestration/work-state-projection.d.ts +32 -0
- package/dist/core/orchestration/work-state-projection.d.ts.map +1 -0
- package/dist/core/orchestration/work-state-projection.js +116 -0
- package/dist/core/orchestration/work-state-projection.js.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts +21 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.js +179 -0
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts +21 -0
- package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -0
- package/dist/core/orchestration/worker-result-adapter.js +109 -0
- package/dist/core/orchestration/worker-result-adapter.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -10
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/process-identity.d.ts +11 -0
- package/dist/core/process-identity.d.ts.map +1 -0
- package/dist/core/process-identity.js +26 -0
- package/dist/core/process-identity.js.map +1 -0
- package/dist/core/process-matrix/codes.d.ts +83 -0
- package/dist/core/process-matrix/codes.d.ts.map +1 -0
- package/dist/core/process-matrix/codes.js +15 -0
- package/dist/core/process-matrix/codes.js.map +1 -0
- package/dist/core/process-matrix/resume-launcher.d.ts +26 -0
- package/dist/core/process-matrix/resume-launcher.d.ts.map +1 -0
- package/dist/core/process-matrix/resume-launcher.js +62 -0
- package/dist/core/process-matrix/resume-launcher.js.map +1 -0
- package/dist/core/process-matrix/runtime.d.ts +78 -0
- package/dist/core/process-matrix/runtime.d.ts.map +1 -0
- package/dist/core/process-matrix/runtime.js +633 -0
- package/dist/core/process-matrix/runtime.js.map +1 -0
- package/dist/core/process-matrix/store.d.ts +25 -0
- package/dist/core/process-matrix/store.d.ts.map +1 -0
- package/dist/core/process-matrix/store.js +189 -0
- package/dist/core/process-matrix/store.js.map +1 -0
- package/dist/core/process-matrix/supervisor.d.ts +75 -0
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
- package/dist/core/process-matrix/supervisor.js +161 -0
- package/dist/core/process-matrix/supervisor.js.map +1 -0
- package/dist/core/profile-registry.d.ts +7 -0
- package/dist/core/profile-registry.d.ts.map +1 -1
- package/dist/core/profile-registry.js +25 -7
- package/dist/core/profile-registry.js.map +1 -1
- package/dist/core/python-runtime.d.ts.map +1 -1
- package/dist/core/python-runtime.js +3 -3
- package/dist/core/python-runtime.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +33 -9
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +217 -127
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +36 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +50 -8
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/research/evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/evidence-bundle.js +1 -6
- package/dist/core/research/evidence-bundle.js.map +1 -1
- package/dist/core/research/lane-model-resolver.d.ts +33 -0
- package/dist/core/research/lane-model-resolver.d.ts.map +1 -0
- package/dist/core/research/lane-model-resolver.js +65 -0
- package/dist/core/research/lane-model-resolver.js.map +1 -0
- package/dist/core/research/model-fitness-controller.d.ts +38 -0
- package/dist/core/research/model-fitness-controller.d.ts.map +1 -0
- package/dist/core/research/model-fitness-controller.js +106 -0
- package/dist/core/research/model-fitness-controller.js.map +1 -0
- package/dist/core/research/model-fitness.d.ts +1 -1
- package/dist/core/research/model-fitness.d.ts.map +1 -1
- package/dist/core/research/model-fitness.js +4 -4
- package/dist/core/research/model-fitness.js.map +1 -1
- package/dist/core/research/research-gate.d.ts.map +1 -1
- package/dist/core/research/research-gate.js +7 -7
- package/dist/core/research/research-gate.js.map +1 -1
- package/dist/core/research/research-lane-controller.d.ts +62 -0
- package/dist/core/research/research-lane-controller.d.ts.map +1 -0
- package/dist/core/research/research-lane-controller.js +292 -0
- package/dist/core/research/research-lane-controller.js.map +1 -0
- package/dist/core/research/session-evidence-bundle.d.ts +2 -4
- package/dist/core/research/session-evidence-bundle.d.ts.map +1 -1
- package/dist/core/research/session-evidence-bundle.js +10 -46
- package/dist/core/research/session-evidence-bundle.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +15 -9
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/risk-classifier.d.ts +6 -0
- package/dist/core/risk-classifier.d.ts.map +1 -0
- package/dist/core/risk-classifier.js +225 -0
- package/dist/core/risk-classifier.js.map +1 -0
- package/dist/core/runtime-builder.d.ts +76 -17
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +367 -124
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +6 -0
- package/dist/core/scout-controller.d.ts.map +1 -1
- package/dist/core/scout-controller.js +66 -52
- package/dist/core/scout-controller.js.map +1 -1
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +64 -27
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-analytics.d.ts.map +1 -1
- package/dist/core/session-analytics.js +28 -1
- package/dist/core/session-analytics.js.map +1 -1
- package/dist/core/session-image-store.d.ts +33 -0
- package/dist/core/session-image-store.d.ts.map +1 -0
- package/dist/core/session-image-store.js +283 -0
- package/dist/core/session-image-store.js.map +1 -0
- package/dist/core/session-role.d.ts +33 -0
- package/dist/core/session-role.d.ts.map +1 -0
- package/dist/core/session-role.js +58 -0
- package/dist/core/session-role.js.map +1 -0
- package/dist/core/session-snapshot.d.ts +34 -0
- package/dist/core/session-snapshot.d.ts.map +1 -0
- package/dist/core/session-snapshot.js +68 -0
- package/dist/core/session-snapshot.js.map +1 -0
- package/dist/core/session-supervision-runtime.d.ts +31 -0
- package/dist/core/session-supervision-runtime.d.ts.map +1 -0
- package/dist/core/session-supervision-runtime.js +97 -0
- package/dist/core/session-supervision-runtime.js.map +1 -0
- package/dist/core/settings-manager.d.ts +51 -7
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +168 -82
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +6 -0
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts +14 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +20 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +13 -6
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +20 -22
- package/dist/core/tasks/session-task-state.js.map +1 -1
- package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
- package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
- package/dist/core/tasks/task-contract-monitor.js +56 -0
- package/dist/core/tasks/task-contract-monitor.js.map +1 -0
- package/dist/core/tasks/task-projection.d.ts +20 -0
- package/dist/core/tasks/task-projection.d.ts.map +1 -0
- package/dist/core/tasks/task-projection.js +47 -0
- package/dist/core/tasks/task-projection.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +12 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +56 -12
- package/dist/core/tasks/task-state.js.map +1 -1
- package/dist/core/tool-capability-policy.d.ts +13 -0
- package/dist/core/tool-capability-policy.d.ts.map +1 -0
- package/dist/core/tool-capability-policy.js +85 -0
- package/dist/core/tool-capability-policy.js.map +1 -0
- package/dist/core/tool-gate-controller.d.ts.map +1 -1
- package/dist/core/tool-gate-controller.js +5 -0
- package/dist/core/tool-gate-controller.js.map +1 -1
- package/dist/core/tool-protocol-controller.d.ts +91 -0
- package/dist/core/tool-protocol-controller.d.ts.map +1 -0
- package/dist/core/tool-protocol-controller.js +512 -0
- package/dist/core/tool-protocol-controller.js.map +1 -0
- package/dist/core/tool-recovery-log-records.d.ts +6 -0
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +13 -31
- package/dist/core/tool-recovery-log-records.js.map +1 -1
- package/dist/core/tool-selection/promotion.d.ts +54 -0
- package/dist/core/tool-selection/promotion.d.ts.map +1 -0
- package/dist/core/tool-selection/promotion.js +81 -0
- package/dist/core/tool-selection/promotion.js.map +1 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts +39 -6
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +153 -110
- package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +96 -0
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/tools/ask-question.d.ts +124 -0
- package/dist/core/tools/ask-question.d.ts.map +1 -0
- package/dist/core/tools/ask-question.js +693 -0
- package/dist/core/tools/ask-question.js.map +1 -0
- package/dist/core/tools/bash.d.ts +18 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +95 -16
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +44 -2
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +214 -19
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +15 -13
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +124 -22
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts.map +1 -1
- package/dist/core/tools/extensionify.js +1 -0
- package/dist/core/tools/extensionify.js.map +1 -1
- package/dist/core/tools/goal.d.ts +94 -3
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +165 -15
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/model-fitness.d.ts +7 -0
- package/dist/core/tools/model-fitness.d.ts.map +1 -1
- package/dist/core/tools/model-fitness.js +4 -4
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +43 -0
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -0
- package/dist/core/tools/orchestration-panel.js +168 -0
- package/dist/core/tools/orchestration-panel.js.map +1 -0
- package/dist/core/tools/run-process.d.ts +26 -0
- package/dist/core/tools/run-process.d.ts.map +1 -0
- package/dist/core/tools/run-process.js +138 -0
- package/dist/core/tools/run-process.js.map +1 -0
- package/dist/core/tools/shell-contract-router.d.ts +6 -1
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
- package/dist/core/tools/shell-contract-router.js +23 -2
- package/dist/core/tools/shell-contract-router.js.map +1 -1
- package/dist/core/tools/shell-session.d.ts +89 -0
- package/dist/core/tools/shell-session.d.ts.map +1 -0
- package/dist/core/tools/shell-session.js +432 -0
- package/dist/core/tools/shell-session.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +6 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +165 -8
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +84 -0
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
- package/dist/core/tools/tmux-dispatch.js +91 -0
- package/dist/core/tools/tmux-dispatch.js.map +1 -0
- package/dist/core/tools/windows-shell-engine.d.ts +42 -0
- package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-engine.js +151 -0
- package/dist/core/tools/windows-shell-engine.js.map +1 -0
- package/dist/core/tools/windows-shell-state.d.ts +40 -0
- package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
- package/dist/core/tools/windows-shell-state.js +59 -0
- package/dist/core/tools/windows-shell-state.js.map +1 -0
- package/dist/core/tools/worktree-sync.d.ts +24 -0
- package/dist/core/tools/worktree-sync.d.ts.map +1 -0
- package/dist/core/tools/worktree-sync.js +338 -0
- package/dist/core/tools/worktree-sync.js.map +1 -0
- package/dist/core/trust-manager.d.ts +4 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +20 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +55 -0
- package/dist/core/util/atomic-file.d.ts.map +1 -0
- package/dist/core/util/atomic-file.js +255 -0
- package/dist/core/util/atomic-file.js.map +1 -0
- package/dist/core/util/bounded-jsonl.d.ts +10 -0
- package/dist/core/util/bounded-jsonl.d.ts.map +1 -0
- package/dist/core/util/bounded-jsonl.js +75 -0
- package/dist/core/util/bounded-jsonl.js.map +1 -0
- package/dist/core/util/minimatch-cache.d.ts +33 -0
- package/dist/core/util/minimatch-cache.d.ts.map +1 -0
- package/dist/core/util/minimatch-cache.js +0 -0
- package/dist/core/util/minimatch-cache.js.map +1 -0
- package/dist/core/util/value-guards.d.ts +5 -0
- package/dist/core/util/value-guards.d.ts.map +1 -0
- package/dist/core/util/value-guards.js +12 -0
- package/dist/core/util/value-guards.js.map +1 -0
- package/dist/core/worktree-sync/codes.d.ts +227 -0
- package/dist/core/worktree-sync/codes.d.ts.map +1 -0
- package/dist/core/worktree-sync/codes.js +14 -0
- package/dist/core/worktree-sync/codes.js.map +1 -0
- package/dist/core/worktree-sync/git-engine.d.ts +156 -0
- package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
- package/dist/core/worktree-sync/git-engine.js +1191 -0
- package/dist/core/worktree-sync/git-engine.js.map +1 -0
- package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
- package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
- package/dist/core/worktree-sync/lane-gate.js +360 -0
- package/dist/core/worktree-sync/lane-gate.js.map +1 -0
- package/dist/core/worktree-sync/runtime.d.ts +47 -0
- package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
- package/dist/core/worktree-sync/runtime.js +96 -0
- package/dist/core/worktree-sync/runtime.js.map +1 -0
- package/dist/core/worktree-sync/store.d.ts +69 -0
- package/dist/core/worktree-sync/store.d.ts.map +1 -0
- package/dist/core/worktree-sync/store.js +252 -0
- package/dist/core/worktree-sync/store.js.map +1 -0
- package/dist/core/worktree-sync/watcher.d.ts +29 -0
- package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
- package/dist/core/worktree-sync/watcher.js +93 -0
- package/dist/core/worktree-sync/watcher.js.map +1 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +179 -1
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +197 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +50 -8
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/clipboard-input.d.ts +7 -1
- package/dist/modes/interactive/clipboard-input.d.ts.map +1 -1
- package/dist/modes/interactive/clipboard-input.js +44 -6
- package/dist/modes/interactive/clipboard-input.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +2 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +5 -4
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/extension-ui-host.d.ts +14 -0
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +76 -15
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +67 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -1
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +43 -0
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +290 -3
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +7 -7
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/report-commands.d.ts.map +1 -1
- package/dist/modes/interactive/report-commands.js +21 -0
- package/dist/modes/interactive/report-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +17 -2
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +74 -21
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-io-commands.js +16 -6
- package/dist/modes/interactive/session-io-commands.js.map +1 -1
- package/dist/modes/interactive/session-replacement-errors.d.ts +7 -0
- package/dist/modes/interactive/session-replacement-errors.d.ts.map +1 -0
- package/dist/modes/interactive/session-replacement-errors.js +15 -0
- package/dist/modes/interactive/session-replacement-errors.js.map +1 -0
- package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
- package/dist/modes/interactive/startup-checks.js +1 -1
- package/dist/modes/interactive/startup-checks.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +82 -23
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +15 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +138 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +2 -13
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +52 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +11 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +20 -2
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/highlight-js-languages.d.ts +4 -0
- package/dist/utils/highlight-js-languages.d.ts.map +1 -0
- package/dist/utils/highlight-js-languages.js +573 -0
- package/dist/utils/highlight-js-languages.js.map +1 -0
- package/dist/utils/platform.d.ts +3 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +8 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/shell.d.ts +7 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +21 -6
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +53 -5
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +112 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/work-directory.d.ts +11 -0
- package/dist/utils/work-directory.d.ts.map +1 -1
- package/dist/utils/work-directory.js +30 -9
- package/dist/utils/work-directory.js.map +1 -1
- package/docs/UI_POLISH_ORCHESTRATION.md +44 -0
- package/docs/UI_QUALITY_ORCHESTRATION.md +30 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +62 -2
- package/docs/harness-architecture.md +156 -0
- package/docs/keybindings.md +11 -1
- package/docs/process-matrix.md +153 -0
- package/docs/rpc.md +45 -2
- package/docs/sessions.md +15 -0
- package/docs/settings.md +128 -11
- package/docs/task-steps.md +8 -0
- package/docs/telemetry.md +19 -0
- package/docs/tmux-agent-manager.md +92 -2
- package/docs/ui-reference/README.md +9 -0
- package/docs/ui-reference/orchestration-after.txt +7 -0
- package/docs/ui-reference/orchestration-before.txt +7 -0
- package/docs/ui-reference/orchestration-reference.txt +6 -0
- package/docs/usage.md +14 -2
- package/docs/windows.md +52 -3
- package/docs/work-directory.md +42 -2
- package/docs/worktree-sync.md +250 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/question.ts +34 -245
- package/examples/extensions/questionnaire.ts +41 -407
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +10 -4
- package/dist/core/delegation/session-worker-result.d.ts +0 -16
- package/dist/core/delegation/session-worker-result.d.ts.map +0 -1
- package/dist/core/delegation/session-worker-result.js +0 -53
- package/dist/core/delegation/session-worker-result.js.map +0 -1
- package/dist/core/delegation/worker-result.d.ts +0 -15
- package/dist/core/delegation/worker-result.d.ts.map +0 -1
- package/dist/core/delegation/worker-result.js +0 -161
- package/dist/core/delegation/worker-result.js.map +0 -1
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js.map +0 -1
- package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
- package/docs/integration-sweep-resume-2026-07-09.md +0 -407
|
@@ -0,0 +1,1191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree-sync git engine: every git-touching operation of the subsystem, built on one injected
|
|
3
|
+
* `exec` seam (the `improvement-loop.ts` pattern) so each function is unit-testable with a
|
|
4
|
+
* scripted exec fake and integration-testable against real git.
|
|
5
|
+
*
|
|
6
|
+
* Doctrine (see docs/worktree-sync.md):
|
|
7
|
+
* - Git is the single source of truth. Freshness ("is current main an ancestor of the lane
|
|
8
|
+
* tip?"), dirtiness, ahead/behind, and rebase-in-progress are RE-DERIVED per call -- the JSON
|
|
9
|
+
* store (`store.ts`) contributes identity/binding only and is rebuildable via {@link reconcile}.
|
|
10
|
+
* - Policy refusals are RETURNED as tagged codes (`codes.ts`), never thrown; git's own stderr
|
|
11
|
+
* rides along as evidence, never as a branch condition.
|
|
12
|
+
* - Nothing here deletes user work silently: releasing a lane with unlanded commits or dirty
|
|
13
|
+
* files requires the explicit discard confirmation, and reconcile only marks orphans.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { hostname as osHostname } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { execCommand } from "../exec.js";
|
|
19
|
+
import { withFileLock } from "../util/atomic-file.js";
|
|
20
|
+
import { EPOCH_CHANGED_PATHS_CAP, LANE_BRANCH_PREFIX, } from "./codes.js";
|
|
21
|
+
import { acquireIntegrationLock, appendAuditEvent, clearLandingTransaction, defaultIsPidAlive, listLanes, readEpoch, readLandingTransaction, readLane, readLockHolder, releaseIntegrationLock, repoSlug, syncStorePaths, writeEpoch, writeLandingTransaction, writeLane, } from "./store.js";
|
|
22
|
+
const GIT_TIMEOUT_MS = 60_000;
|
|
23
|
+
const GIT_MAX_BUFFER = 1024 * 1024;
|
|
24
|
+
const LANE_KEY_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
25
|
+
/** Production exec: `exec.ts`'s bounded `execCommand` (rolling-tail output, timeout, abort). */
|
|
26
|
+
export function createDefaultWorktreeSyncExec() {
|
|
27
|
+
return (command, args, options) => execCommand(command, args, options.cwd, {
|
|
28
|
+
timeout: options.timeout,
|
|
29
|
+
signal: options.signal,
|
|
30
|
+
maxBuffer: options.maxBuffer,
|
|
31
|
+
env: options.env,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function runGit(deps, cwd, args) {
|
|
35
|
+
return deps.exec("git", args, { cwd, timeout: GIT_TIMEOUT_MS, signal: deps.signal, maxBuffer: GIT_MAX_BUFFER });
|
|
36
|
+
}
|
|
37
|
+
function nowIso(deps) {
|
|
38
|
+
return (deps.now ?? (() => new Date().toISOString()))();
|
|
39
|
+
}
|
|
40
|
+
function fileExists(deps, path) {
|
|
41
|
+
return (deps.fileExists ?? existsSync)(path);
|
|
42
|
+
}
|
|
43
|
+
function gitError(message, result) {
|
|
44
|
+
return { code: "git_error", message, gitStderr: (result.stderr || result.stdout).trim().slice(0, 4000) };
|
|
45
|
+
}
|
|
46
|
+
export function laneBranch(laneKey) {
|
|
47
|
+
return `${LANE_BRANCH_PREFIX}${laneKey}`;
|
|
48
|
+
}
|
|
49
|
+
/** Parse `git worktree list --porcelain` blocks. */
|
|
50
|
+
export function parseWorktreeList(output) {
|
|
51
|
+
const entries = [];
|
|
52
|
+
let current;
|
|
53
|
+
for (const line of output.split(/\r?\n/)) {
|
|
54
|
+
if (line.startsWith("worktree ")) {
|
|
55
|
+
if (current)
|
|
56
|
+
entries.push(current);
|
|
57
|
+
current = { path: line.slice("worktree ".length) };
|
|
58
|
+
}
|
|
59
|
+
else if (line.startsWith("HEAD ") && current) {
|
|
60
|
+
current.headSha = line.slice("HEAD ".length);
|
|
61
|
+
}
|
|
62
|
+
else if (line.startsWith("branch ") && current) {
|
|
63
|
+
current.branchRef = line.slice("branch ".length);
|
|
64
|
+
}
|
|
65
|
+
else if (line.trim() === "" && current) {
|
|
66
|
+
entries.push(current);
|
|
67
|
+
current = undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (current)
|
|
71
|
+
entries.push(current);
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
74
|
+
async function revParseBranch(deps, cwd, branch) {
|
|
75
|
+
const result = await runGit(deps, cwd, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
|
|
76
|
+
if (result.code !== 0)
|
|
77
|
+
return undefined;
|
|
78
|
+
const sha = result.stdout.trim();
|
|
79
|
+
return sha || undefined;
|
|
80
|
+
}
|
|
81
|
+
export async function resolveRepoContext(deps) {
|
|
82
|
+
const roots = await runGit(deps, deps.cwd, [
|
|
83
|
+
"rev-parse",
|
|
84
|
+
"--path-format=absolute",
|
|
85
|
+
"--show-toplevel",
|
|
86
|
+
"--git-common-dir",
|
|
87
|
+
]);
|
|
88
|
+
if (roots.code !== 0) {
|
|
89
|
+
return {
|
|
90
|
+
code: "not_a_git_repo",
|
|
91
|
+
message: `worktree-sync requires a git repository; ${deps.cwd} is not inside one`,
|
|
92
|
+
gitStderr: roots.stderr.trim().slice(0, 1000),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const [topLevel, gitCommonDir] = roots.stdout
|
|
96
|
+
.split(/\r?\n/)
|
|
97
|
+
.map((line) => line.trim())
|
|
98
|
+
.filter(Boolean);
|
|
99
|
+
if (!topLevel || !gitCommonDir) {
|
|
100
|
+
return gitError("git rev-parse returned no toplevel/common-dir", roots);
|
|
101
|
+
}
|
|
102
|
+
// D13: explicit resolution order -- override, `main`, `master`. Never guess further.
|
|
103
|
+
const candidates = deps.options.mainBranchOverride ? [deps.options.mainBranchOverride] : ["main", "master"];
|
|
104
|
+
let mainBranch;
|
|
105
|
+
let mainSha;
|
|
106
|
+
for (const candidate of candidates) {
|
|
107
|
+
const sha = await revParseBranch(deps, topLevel, candidate);
|
|
108
|
+
if (sha) {
|
|
109
|
+
mainBranch = candidate;
|
|
110
|
+
mainSha = sha;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!mainBranch || !mainSha) {
|
|
115
|
+
return {
|
|
116
|
+
code: "default_branch_unresolved",
|
|
117
|
+
message: deps.options.mainBranchOverride
|
|
118
|
+
? `configured main branch '${deps.options.mainBranchOverride}' does not exist locally`
|
|
119
|
+
: "no local 'main' or 'master' branch; set the worktree-sync main branch explicitly",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const worktrees = await runGit(deps, topLevel, ["worktree", "list", "--porcelain"]);
|
|
123
|
+
if (worktrees.code !== 0)
|
|
124
|
+
return gitError("git worktree list failed", worktrees);
|
|
125
|
+
const hub = parseWorktreeList(worktrees.stdout).find((entry) => entry.branchRef === `refs/heads/${mainBranch}`);
|
|
126
|
+
return {
|
|
127
|
+
topLevel,
|
|
128
|
+
gitCommonDir,
|
|
129
|
+
mainBranch,
|
|
130
|
+
mainSha,
|
|
131
|
+
paths: syncStorePaths(gitCommonDir),
|
|
132
|
+
slug: repoSlug(topLevel, gitCommonDir),
|
|
133
|
+
hubPath: hub?.path,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Live git facts for one lane. `fresh` is THE freshness definition of the whole subsystem
|
|
138
|
+
* (G3/G9): current main is an ancestor of the lane tip. Everything is re-derived; nothing is
|
|
139
|
+
* read back from a cache that could disagree with git.
|
|
140
|
+
*/
|
|
141
|
+
export async function deriveLaneFacts(deps, ctx, lane) {
|
|
142
|
+
const branchSha = await revParseBranch(deps, ctx.topLevel, lane.branch);
|
|
143
|
+
let fresh = false;
|
|
144
|
+
let aheadOfMain = 0;
|
|
145
|
+
let behindMain = 0;
|
|
146
|
+
if (branchSha) {
|
|
147
|
+
const ancestry = await runGit(deps, ctx.topLevel, ["merge-base", "--is-ancestor", ctx.mainSha, branchSha]);
|
|
148
|
+
fresh = ancestry.code === 0;
|
|
149
|
+
const counts = await runGit(deps, ctx.topLevel, [
|
|
150
|
+
"rev-list",
|
|
151
|
+
"--left-right",
|
|
152
|
+
"--count",
|
|
153
|
+
`${ctx.mainBranch}...${lane.branch}`,
|
|
154
|
+
]);
|
|
155
|
+
if (counts.code === 0) {
|
|
156
|
+
const [left, right] = counts.stdout.trim().split(/\s+/);
|
|
157
|
+
behindMain = Number.parseInt(left ?? "0", 10) || 0;
|
|
158
|
+
aheadOfMain = Number.parseInt(right ?? "0", 10) || 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
let worktreePresent = fileExists(deps, lane.worktreePath);
|
|
162
|
+
let dirty = false;
|
|
163
|
+
let rebaseInProgress = false;
|
|
164
|
+
if (worktreePresent) {
|
|
165
|
+
const status = await runGit(deps, lane.worktreePath, ["status", "--porcelain"]);
|
|
166
|
+
if (status.code === 0) {
|
|
167
|
+
dirty = status.stdout.trim().length > 0;
|
|
168
|
+
rebaseInProgress = await isRebaseActive(deps, lane.worktreePath);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// The directory exists but is not a usable worktree (e.g. pruned metadata): not present.
|
|
172
|
+
worktreePresent = false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const isAlive = deps.isPidAlive ??
|
|
176
|
+
((pid) => defaultIsPidAlive({ pid, hostname: osHostname(), token: "status", acquiredAt: "" }));
|
|
177
|
+
return {
|
|
178
|
+
laneKey: lane.laneKey,
|
|
179
|
+
branch: lane.branch,
|
|
180
|
+
worktreePath: lane.worktreePath,
|
|
181
|
+
registrationStatus: lane.status,
|
|
182
|
+
branchSha,
|
|
183
|
+
fresh,
|
|
184
|
+
dirty,
|
|
185
|
+
rebaseInProgress,
|
|
186
|
+
aheadOfMain,
|
|
187
|
+
behindMain,
|
|
188
|
+
worktreePresent,
|
|
189
|
+
...(lane.ownerPid !== undefined ? { ownerAlive: isAlive(lane.ownerPid) } : {}),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** One-time repo git config (D7): shared rerere + zdiff3 conflict hunks -- the deterministic
|
|
193
|
+
* conflict-resolution substrate for every lane (the rr-cache lives in the common dir, so one
|
|
194
|
+
* resolution replays identically in all worktrees). Idempotent; only actual changes are audited. */
|
|
195
|
+
export async function ensureRepoGitConfig(deps, ctx) {
|
|
196
|
+
const wanted = [
|
|
197
|
+
["rerere.enabled", "true"],
|
|
198
|
+
["rerere.autoUpdate", "true"],
|
|
199
|
+
["merge.conflictStyle", "zdiff3"],
|
|
200
|
+
];
|
|
201
|
+
const changed = [];
|
|
202
|
+
for (const [key, value] of wanted) {
|
|
203
|
+
const current = await runGit(deps, ctx.topLevel, ["config", "--get", key]);
|
|
204
|
+
if (current.code === 0 && current.stdout.trim() === value)
|
|
205
|
+
continue;
|
|
206
|
+
const set = await runGit(deps, ctx.topLevel, ["config", key, value]);
|
|
207
|
+
if (set.code === 0)
|
|
208
|
+
changed.push(`${key}=${value}`);
|
|
209
|
+
}
|
|
210
|
+
if (changed.length > 0) {
|
|
211
|
+
await appendAuditEvent(ctx.paths, { event: "config_set", changed }, nowIso(deps));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function sanitizeScope(scope) {
|
|
215
|
+
const cleaned = scope
|
|
216
|
+
.toLowerCase()
|
|
217
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
218
|
+
.replace(/^-+|-+$/g, "")
|
|
219
|
+
.slice(0, 32);
|
|
220
|
+
return cleaned || "adhoc";
|
|
221
|
+
}
|
|
222
|
+
export async function createLane(deps, args = {}) {
|
|
223
|
+
const ctx = await resolveRepoContext(deps);
|
|
224
|
+
if ("code" in ctx)
|
|
225
|
+
return ctx;
|
|
226
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
227
|
+
if (args.laneKey !== undefined && !LANE_KEY_RE.test(args.laneKey)) {
|
|
228
|
+
return {
|
|
229
|
+
code: "invalid_lane_key",
|
|
230
|
+
message: `laneKey '${args.laneKey}' is invalid: lowercase alphanumerics and inner dashes, max 63 chars`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const lanes = await listLanes(ctx.paths);
|
|
234
|
+
const activeCount = lanes.filter((lane) => lane.status === "active").length;
|
|
235
|
+
if (activeCount >= deps.options.maxLanes) {
|
|
236
|
+
return {
|
|
237
|
+
code: "max_lanes_reached",
|
|
238
|
+
message: `active lane limit reached (${activeCount}/${deps.options.maxLanes}); release a lane first`,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const taken = new Set(lanes.filter((lane) => lane.status !== "released").map((lane) => lane.laneKey));
|
|
242
|
+
const isFree = async (candidate) => {
|
|
243
|
+
if (taken.has(candidate))
|
|
244
|
+
return false;
|
|
245
|
+
if (await revParseBranch(deps, ctx.topLevel, laneBranch(candidate)))
|
|
246
|
+
return false;
|
|
247
|
+
if (fileExists(deps, join(deps.worktreesBaseDir, ctx.slug, candidate)))
|
|
248
|
+
return false;
|
|
249
|
+
return true;
|
|
250
|
+
};
|
|
251
|
+
let laneKey;
|
|
252
|
+
if (args.laneKey !== undefined) {
|
|
253
|
+
if (!(await isFree(args.laneKey))) {
|
|
254
|
+
return {
|
|
255
|
+
code: "lane_exists",
|
|
256
|
+
message: `lane '${args.laneKey}' already exists (registration, branch, or checkout)`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
laneKey = args.laneKey;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Deterministic allocation: lowest free `<scope>-<n>` (scope = goalId or "adhoc").
|
|
263
|
+
const scope = sanitizeScope(args.goalId ?? "adhoc");
|
|
264
|
+
let n = 1;
|
|
265
|
+
while (!(await isFree(`${scope}-${n}`)))
|
|
266
|
+
n++;
|
|
267
|
+
laneKey = `${scope}-${n}`;
|
|
268
|
+
}
|
|
269
|
+
await ensureRepoGitConfig(deps, ctx);
|
|
270
|
+
const branch = laneBranch(laneKey);
|
|
271
|
+
const worktreePath = join(deps.worktreesBaseDir, ctx.slug, laneKey);
|
|
272
|
+
const add = await runGit(deps, ctx.topLevel, ["worktree", "add", "-b", branch, worktreePath, ctx.mainBranch]);
|
|
273
|
+
if (add.code !== 0)
|
|
274
|
+
return gitError(`git worktree add failed for lane '${laneKey}'`, add);
|
|
275
|
+
const at = nowIso(deps);
|
|
276
|
+
const lane = {
|
|
277
|
+
laneKey,
|
|
278
|
+
branch,
|
|
279
|
+
worktreePath,
|
|
280
|
+
status: "active",
|
|
281
|
+
createdAt: at,
|
|
282
|
+
updatedAt: at,
|
|
283
|
+
...(args.goalId !== undefined ? { goalId: args.goalId } : {}),
|
|
284
|
+
...(args.requirementId !== undefined ? { requirementId: args.requirementId } : {}),
|
|
285
|
+
ownerPid: deps.pid ?? process.pid,
|
|
286
|
+
...(deps.sessionId !== undefined ? { ownerSessionId: deps.sessionId } : {}),
|
|
287
|
+
};
|
|
288
|
+
await writeLane(ctx.paths, lane);
|
|
289
|
+
await appendAuditEvent(ctx.paths, { event: "lane_created", laneKey, branch, baseSha: ctx.mainSha }, at);
|
|
290
|
+
return { code: "ok", lane };
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Does `lane` have a DIFFERENT, still-alive session owning it? Same same-host `isAlive` pattern
|
|
295
|
+
* used elsewhere (`deps.isPidAlive`, else {@link defaultIsPidAlive} against this host). A lane
|
|
296
|
+
* with no recorded owner, owned by THIS session, or whose owner is dead never conflicts --
|
|
297
|
+
* `reconcile` already clears dead owners, but a race can still observe one here, and a dead owner
|
|
298
|
+
* must never block land/release (D6, per R1: land/release ONLY -- sync/continue/abort_sync stay
|
|
299
|
+
* ungated at the engine; worker cross-lane containment for those lives at the tool layer, D3).
|
|
300
|
+
*/
|
|
301
|
+
function ownerConflicts(deps, lane) {
|
|
302
|
+
if (lane.ownerSessionId == null || lane.ownerSessionId === deps.sessionId)
|
|
303
|
+
return false;
|
|
304
|
+
if (lane.ownerPid === undefined)
|
|
305
|
+
return false;
|
|
306
|
+
const isAlive = deps.isPidAlive ??
|
|
307
|
+
((pid) => defaultIsPidAlive({ pid, hostname: osHostname(), token: "status", acquiredAt: "" }));
|
|
308
|
+
return isAlive(lane.ownerPid);
|
|
309
|
+
}
|
|
310
|
+
export async function bindLaneWorker(deps, args) {
|
|
311
|
+
const ctx = await resolveRepoContext(deps);
|
|
312
|
+
if ("code" in ctx)
|
|
313
|
+
return { code: "store_error", message: ctx.message };
|
|
314
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
315
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
316
|
+
if (!lane)
|
|
317
|
+
return { code: "lane_not_found", message: `lane '${args.laneKey}' is not registered` };
|
|
318
|
+
if (lane.status !== "active")
|
|
319
|
+
return { code: "lane_not_active", message: `lane '${args.laneKey}' is ${lane.status}` };
|
|
320
|
+
if (lane.boundLaneId === args.laneId)
|
|
321
|
+
return { code: "already_bound", laneKey: args.laneKey, laneId: args.laneId };
|
|
322
|
+
if (lane.boundLaneId) {
|
|
323
|
+
return { code: "binding_conflict", laneKey: args.laneKey, boundLaneId: lane.boundLaneId };
|
|
324
|
+
}
|
|
325
|
+
const updated = { ...lane, boundLaneId: args.laneId, updatedAt: nowIso(deps) };
|
|
326
|
+
try {
|
|
327
|
+
await writeLane(ctx.paths, updated);
|
|
328
|
+
await appendAuditEvent(ctx.paths, { event: "lane_worker_bound", laneKey: args.laneKey, laneId: args.laneId }, nowIso(deps));
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
return { code: "store_error", message: `lane binding persistence failed: ${String(error)}` };
|
|
332
|
+
}
|
|
333
|
+
return { code: "bound", laneKey: args.laneKey, laneId: args.laneId };
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
export async function releaseLane(deps, args) {
|
|
337
|
+
const ctx = await resolveRepoContext(deps);
|
|
338
|
+
if ("code" in ctx)
|
|
339
|
+
return ctx;
|
|
340
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
341
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
342
|
+
if (!lane)
|
|
343
|
+
return { code: "lane_not_found", message: `no registered lane '${args.laneKey}'` };
|
|
344
|
+
if (lane.status === "released")
|
|
345
|
+
return { code: "released", laneKey: lane.laneKey };
|
|
346
|
+
if (ownerConflicts(deps, lane)) {
|
|
347
|
+
return {
|
|
348
|
+
code: "lane_owner_conflict",
|
|
349
|
+
message: `lane '${lane.laneKey}' is owned by another live session (pid ${lane.ownerPid}); only its owner may release it`,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const facts = await deriveLaneFacts(deps, ctx, lane);
|
|
353
|
+
let landed = true;
|
|
354
|
+
if (facts.branchSha) {
|
|
355
|
+
const ancestry = await runGit(deps, ctx.topLevel, [
|
|
356
|
+
"merge-base",
|
|
357
|
+
"--is-ancestor",
|
|
358
|
+
facts.branchSha,
|
|
359
|
+
ctx.mainSha,
|
|
360
|
+
]);
|
|
361
|
+
landed = ancestry.code === 0;
|
|
362
|
+
}
|
|
363
|
+
const unlandedWork = (facts.branchSha !== undefined && !landed) || facts.dirty;
|
|
364
|
+
const discardConfirmed = args.confirm === "yes-discard-lane";
|
|
365
|
+
if (unlandedWork && !discardConfirmed) {
|
|
366
|
+
return {
|
|
367
|
+
code: "lane_unlanded_work",
|
|
368
|
+
message: `lane '${lane.laneKey}' has ${facts.dirty ? "uncommitted changes" : ""}` +
|
|
369
|
+
`${facts.dirty && !landed ? " and " : ""}${!landed ? `${facts.aheadOfMain} unlanded commit(s)` : ""}; ` +
|
|
370
|
+
`land it first, or pass confirm:"yes-discard-lane" to discard`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
if (facts.worktreePresent) {
|
|
374
|
+
const removeArgs = ["worktree", "remove", ...(unlandedWork ? ["--force"] : []), lane.worktreePath];
|
|
375
|
+
const remove = await runGit(deps, ctx.topLevel, removeArgs);
|
|
376
|
+
if (remove.code !== 0)
|
|
377
|
+
return gitError(`git worktree remove failed for lane '${lane.laneKey}'`, remove);
|
|
378
|
+
}
|
|
379
|
+
if (facts.branchSha) {
|
|
380
|
+
const del = await runGit(deps, ctx.topLevel, ["branch", unlandedWork ? "-D" : "-d", lane.branch]);
|
|
381
|
+
if (del.code !== 0)
|
|
382
|
+
return gitError(`git branch delete failed for lane '${lane.laneKey}'`, del);
|
|
383
|
+
}
|
|
384
|
+
await runGit(deps, ctx.topLevel, ["worktree", "prune"]);
|
|
385
|
+
const at = nowIso(deps);
|
|
386
|
+
await writeLane(ctx.paths, { ...lane, status: "released", updatedAt: at });
|
|
387
|
+
await appendAuditEvent(ctx.paths, { event: "lane_released", laneKey: lane.laneKey, ...(unlandedWork ? { discarded: true } : {}) }, at);
|
|
388
|
+
return { code: "released", laneKey: lane.laneKey };
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/** Lane worktrees git knows about that the registry does not (yet) track: a valid `pi/wt/<key>`
|
|
392
|
+
* branch, key not already registered, checkout still present. One derivation shared by the
|
|
393
|
+
* write-free no-op guard below and the re-registration pass, rather than two copies of the same
|
|
394
|
+
* filter. */
|
|
395
|
+
function findUnregisteredLaneWorktrees(deps, worktrees, registered) {
|
|
396
|
+
const found = [];
|
|
397
|
+
for (const entry of worktrees) {
|
|
398
|
+
const ref = entry.branchRef;
|
|
399
|
+
if (!ref?.startsWith(`refs/heads/${LANE_BRANCH_PREFIX}`))
|
|
400
|
+
continue;
|
|
401
|
+
const laneKey = ref.slice(`refs/heads/${LANE_BRANCH_PREFIX}`.length);
|
|
402
|
+
if (registered.has(laneKey) || !LANE_KEY_RE.test(laneKey))
|
|
403
|
+
continue;
|
|
404
|
+
if (!fileExists(deps, entry.path))
|
|
405
|
+
continue;
|
|
406
|
+
found.push({ laneKey, path: entry.path });
|
|
407
|
+
}
|
|
408
|
+
return found;
|
|
409
|
+
}
|
|
410
|
+
async function reconcileLandingTransaction(deps, ctx, transaction) {
|
|
411
|
+
const currentMainSha = await revParseBranch(deps, ctx.topLevel, ctx.mainBranch);
|
|
412
|
+
if (currentMainSha === transaction.priorMainSha) {
|
|
413
|
+
await clearLandingTransaction(ctx.paths);
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
if (currentMainSha !== transaction.testedTipSha) {
|
|
417
|
+
return {
|
|
418
|
+
code: "git_error",
|
|
419
|
+
message: `landing transaction is ambiguous: main is ${currentMainSha?.slice(0, 12) ?? "missing"}, expected prior ${transaction.priorMainSha.slice(0, 12)} or tested ${transaction.testedTipSha.slice(0, 12)}`,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const previous = await readEpoch(ctx.paths);
|
|
423
|
+
let epoch = previous?.epoch ?? 0;
|
|
424
|
+
const at = nowIso(deps);
|
|
425
|
+
if (previous?.mainSha !== transaction.testedTipSha) {
|
|
426
|
+
epoch++;
|
|
427
|
+
await writeEpoch(ctx.paths, {
|
|
428
|
+
epoch,
|
|
429
|
+
mainSha: transaction.testedTipSha,
|
|
430
|
+
previousMainSha: transaction.priorMainSha,
|
|
431
|
+
landedLaneKey: transaction.laneKey,
|
|
432
|
+
landedAt: at,
|
|
433
|
+
changedPaths: transaction.changedPaths,
|
|
434
|
+
changedPathsTruncated: transaction.changedPathsTruncated,
|
|
435
|
+
});
|
|
436
|
+
transaction = { ...transaction, stage: "epoch_written" };
|
|
437
|
+
await writeLandingTransaction(ctx.paths, transaction);
|
|
438
|
+
}
|
|
439
|
+
if (transaction.stage !== "audit_logged") {
|
|
440
|
+
await appendAuditEvent(ctx.paths, {
|
|
441
|
+
event: "epoch_reconciled",
|
|
442
|
+
epoch,
|
|
443
|
+
laneKey: transaction.laneKey,
|
|
444
|
+
mainSha: transaction.testedTipSha,
|
|
445
|
+
gate: transaction.gate,
|
|
446
|
+
}, at);
|
|
447
|
+
await writeLandingTransaction(ctx.paths, { ...transaction, stage: "audit_logged" });
|
|
448
|
+
}
|
|
449
|
+
await clearLandingTransaction(ctx.paths);
|
|
450
|
+
return undefined;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Startup/repair pass (mirrors the tmux session reconcile): diff the registry against git
|
|
454
|
+
* reality, mark orphans (never delete -- G11), re-register lane worktrees git still knows that
|
|
455
|
+
* the registry lost (a deleted store is fully rebuildable), self-heal orphan registrations whose
|
|
456
|
+
* worktree+branch actually exist, clear dead owners, and release a provably-stale lock.
|
|
457
|
+
*/
|
|
458
|
+
export async function reconcile(deps) {
|
|
459
|
+
const ctx = await resolveRepoContext(deps);
|
|
460
|
+
if ("code" in ctx)
|
|
461
|
+
return ctx;
|
|
462
|
+
const landingTransaction = await readLandingTransaction(ctx.paths);
|
|
463
|
+
if (landingTransaction) {
|
|
464
|
+
const recoveryLock = await acquireIntegrationLock(ctx.paths, { pid: deps.pid ?? process.pid, hostname: osHostname(), sessionId: deps.sessionId, laneKey: "reconcile" }, { now: deps.now });
|
|
465
|
+
if (!recoveryLock.acquired) {
|
|
466
|
+
return { code: "git_error", message: "landing transaction recovery is blocked by an active integration lock" };
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const recovery = await reconcileLandingTransaction(deps, ctx, landingTransaction);
|
|
470
|
+
if (recovery)
|
|
471
|
+
return recovery;
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
await releaseIntegrationLock(ctx.paths, recoveryLock.token, { now: deps.now });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const worktreesResult = await runGit(deps, ctx.topLevel, ["worktree", "list", "--porcelain"]);
|
|
478
|
+
if (worktreesResult.code !== 0)
|
|
479
|
+
return gitError("git worktree list failed", worktreesResult);
|
|
480
|
+
const worktrees = parseWorktreeList(worktreesResult.stdout);
|
|
481
|
+
const lanes = await listLanes(ctx.paths);
|
|
482
|
+
const registered = new Set(lanes.map((lane) => lane.laneKey));
|
|
483
|
+
const unregisteredWorktrees = findUnregisteredLaneWorktrees(deps, worktrees, registered);
|
|
484
|
+
// Write-free fast path: worktree-sync now starts in every session, so a mere session open in a
|
|
485
|
+
// git repo must never create `<git-common-dir>/pi-worktree-sync/`. When the store does not exist
|
|
486
|
+
// yet and git shows nothing to recover, there is genuinely nothing to reconcile -- one early
|
|
487
|
+
// determination, then skip every write below (writeLane/appendAuditEvent/releaseIntegrationLock)
|
|
488
|
+
// rather than guarding each call individually. Once the store exists, or findings occur, behavior
|
|
489
|
+
// is unchanged below (including the reconcile_summary audit append).
|
|
490
|
+
if (!fileExists(deps, ctx.paths.root) && unregisteredWorktrees.length === 0) {
|
|
491
|
+
return {
|
|
492
|
+
code: "reconciled",
|
|
493
|
+
orphanedLaneKeys: [],
|
|
494
|
+
reRegisteredLaneKeys: [],
|
|
495
|
+
ownerClearedLaneKeys: [],
|
|
496
|
+
staleLockReleased: false,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const isAlive = deps.isPidAlive ??
|
|
500
|
+
((pid) => defaultIsPidAlive({ pid, hostname: osHostname(), token: "status", acquiredAt: "" }));
|
|
501
|
+
const at = nowIso(deps);
|
|
502
|
+
const orphanedLaneKeys = [];
|
|
503
|
+
const reRegisteredLaneKeys = [];
|
|
504
|
+
const ownerClearedLaneKeys = [];
|
|
505
|
+
for (const lane of lanes) {
|
|
506
|
+
if (lane.status === "released")
|
|
507
|
+
continue;
|
|
508
|
+
let next = lane;
|
|
509
|
+
const branchSha = await revParseBranch(deps, ctx.topLevel, lane.branch);
|
|
510
|
+
const present = branchSha !== undefined && fileExists(deps, lane.worktreePath);
|
|
511
|
+
if (lane.status === "active" && !present) {
|
|
512
|
+
next = { ...next, status: "orphaned", updatedAt: at };
|
|
513
|
+
orphanedLaneKeys.push(lane.laneKey);
|
|
514
|
+
await appendAuditEvent(ctx.paths, { event: "lane_orphaned", laneKey: lane.laneKey }, at);
|
|
515
|
+
}
|
|
516
|
+
else if (lane.status === "orphaned" && present) {
|
|
517
|
+
// Self-heal: the checkout and branch are back (or never really left) -- active again.
|
|
518
|
+
next = { ...next, status: "active", updatedAt: at };
|
|
519
|
+
reRegisteredLaneKeys.push(lane.laneKey);
|
|
520
|
+
await appendAuditEvent(ctx.paths, { event: "lane_reregistered", laneKey: lane.laneKey }, at);
|
|
521
|
+
}
|
|
522
|
+
if (next.ownerPid !== undefined && !isAlive(next.ownerPid)) {
|
|
523
|
+
const { ownerPid: _pid, ownerSessionId: _session, ...rest } = next;
|
|
524
|
+
next = { ...rest, updatedAt: at };
|
|
525
|
+
ownerClearedLaneKeys.push(lane.laneKey);
|
|
526
|
+
}
|
|
527
|
+
if (next !== lane)
|
|
528
|
+
await writeLane(ctx.paths, next);
|
|
529
|
+
}
|
|
530
|
+
// Lane worktrees git knows but the registry does not: rebuild identity from git facts.
|
|
531
|
+
for (const { laneKey, path } of unregisteredWorktrees) {
|
|
532
|
+
await writeLane(ctx.paths, {
|
|
533
|
+
laneKey,
|
|
534
|
+
branch: `${LANE_BRANCH_PREFIX}${laneKey}`,
|
|
535
|
+
worktreePath: path,
|
|
536
|
+
status: "active",
|
|
537
|
+
createdAt: at,
|
|
538
|
+
updatedAt: at,
|
|
539
|
+
});
|
|
540
|
+
reRegisteredLaneKeys.push(laneKey);
|
|
541
|
+
await appendAuditEvent(ctx.paths, { event: "lane_reregistered", laneKey }, at);
|
|
542
|
+
}
|
|
543
|
+
// Provably-stale integration lock: dead same-host owner (never a foreign or live one).
|
|
544
|
+
let staleLockReleased = false;
|
|
545
|
+
const holder = await readLockHolder(ctx.paths);
|
|
546
|
+
if (holder && holder.hostname === osHostname() && !isAlive(holder.pid) && holder.token) {
|
|
547
|
+
staleLockReleased = await releaseIntegrationLock(ctx.paths, holder.token, { now: deps.now });
|
|
548
|
+
}
|
|
549
|
+
await appendAuditEvent(ctx.paths, {
|
|
550
|
+
event: "reconcile_summary",
|
|
551
|
+
orphaned: orphanedLaneKeys,
|
|
552
|
+
reRegistered: reRegisteredLaneKeys,
|
|
553
|
+
ownersCleared: ownerClearedLaneKeys,
|
|
554
|
+
staleLockReleased,
|
|
555
|
+
}, at);
|
|
556
|
+
return { code: "reconciled", orphanedLaneKeys, reRegisteredLaneKeys, ownerClearedLaneKeys, staleLockReleased };
|
|
557
|
+
}
|
|
558
|
+
/** Rebase env for every rebase-driving call: never let git open an editor mid-automation. */
|
|
559
|
+
function rebaseEnv() {
|
|
560
|
+
return { ...process.env, GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" };
|
|
561
|
+
}
|
|
562
|
+
function runGitEnv(deps, cwd, args) {
|
|
563
|
+
return deps.exec("git", args, {
|
|
564
|
+
cwd,
|
|
565
|
+
timeout: GIT_TIMEOUT_MS,
|
|
566
|
+
signal: deps.signal,
|
|
567
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
568
|
+
env: rebaseEnv(),
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function readFileMaybe(deps, path) {
|
|
572
|
+
if (deps.readFile)
|
|
573
|
+
return deps.readFile(path);
|
|
574
|
+
try {
|
|
575
|
+
return readFileSync(path, "utf-8");
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/** A rebase is in progress iff the worktree's rebase-merge or rebase-apply state dir exists. */
|
|
582
|
+
async function isRebaseActive(deps, worktreePath) {
|
|
583
|
+
const gitPaths = await runGit(deps, worktreePath, [
|
|
584
|
+
"rev-parse",
|
|
585
|
+
"--path-format=absolute",
|
|
586
|
+
"--git-path",
|
|
587
|
+
"rebase-merge",
|
|
588
|
+
"--git-path",
|
|
589
|
+
"rebase-apply",
|
|
590
|
+
]);
|
|
591
|
+
if (gitPaths.code !== 0)
|
|
592
|
+
return false;
|
|
593
|
+
return gitPaths.stdout
|
|
594
|
+
.split(/\r?\n/)
|
|
595
|
+
.map((line) => line.trim())
|
|
596
|
+
.filter(Boolean)
|
|
597
|
+
.some((path) => fileExists(deps, path));
|
|
598
|
+
}
|
|
599
|
+
/** Paths with local modifications (`status --porcelain`), rename targets included. Capped. */
|
|
600
|
+
async function dirtyPaths(deps, worktreePath) {
|
|
601
|
+
const status = await runGit(deps, worktreePath, ["status", "--porcelain"]);
|
|
602
|
+
if (status.code !== 0)
|
|
603
|
+
return [];
|
|
604
|
+
return status.stdout
|
|
605
|
+
.split(/\r?\n/)
|
|
606
|
+
.filter((line) => line.length > 3)
|
|
607
|
+
.map((line) => {
|
|
608
|
+
const path = line.slice(3);
|
|
609
|
+
const renameArrow = path.indexOf(" -> ");
|
|
610
|
+
return renameArrow >= 0 ? path.slice(renameArrow + 4) : path;
|
|
611
|
+
})
|
|
612
|
+
.slice(0, 100);
|
|
613
|
+
}
|
|
614
|
+
async function unmergedFiles(deps, worktreePath) {
|
|
615
|
+
const diff = await runGit(deps, worktreePath, ["diff", "--name-only", "--diff-filter=U"]);
|
|
616
|
+
if (diff.code !== 0)
|
|
617
|
+
return [];
|
|
618
|
+
return diff.stdout
|
|
619
|
+
.split(/\r?\n/)
|
|
620
|
+
.map((line) => line.trim())
|
|
621
|
+
.filter(Boolean);
|
|
622
|
+
}
|
|
623
|
+
/** Conflict kind per path from `git ls-files -u` index stages (1=base, 2=ours, 3=theirs). */
|
|
624
|
+
async function unmergedKinds(deps, worktreePath) {
|
|
625
|
+
const lsFiles = await runGit(deps, worktreePath, ["ls-files", "-u"]);
|
|
626
|
+
const stagesByPath = new Map();
|
|
627
|
+
if (lsFiles.code === 0) {
|
|
628
|
+
for (const line of lsFiles.stdout.split(/\r?\n/)) {
|
|
629
|
+
const match = line.match(/^\d+ [0-9a-f]+ ([123])\t(.+)$/);
|
|
630
|
+
if (!match?.[1] || !match[2])
|
|
631
|
+
continue;
|
|
632
|
+
const stages = stagesByPath.get(match[2]) ?? new Set();
|
|
633
|
+
stages.add(match[1]);
|
|
634
|
+
stagesByPath.set(match[2], stages);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const kinds = new Map();
|
|
638
|
+
for (const [path, stages] of stagesByPath) {
|
|
639
|
+
const has = (stage) => stages.has(stage);
|
|
640
|
+
if (has("1") && has("2") && has("3"))
|
|
641
|
+
kinds.set(path, "both_modified");
|
|
642
|
+
else if (!has("1") && has("2") && has("3"))
|
|
643
|
+
kinds.set(path, "both_added");
|
|
644
|
+
else if (has("1") && !has("2") && has("3"))
|
|
645
|
+
kinds.set(path, "deleted_by_us");
|
|
646
|
+
else if (has("1") && has("2") && !has("3"))
|
|
647
|
+
kinds.set(path, "deleted_by_them");
|
|
648
|
+
else
|
|
649
|
+
kinds.set(path, "unknown");
|
|
650
|
+
}
|
|
651
|
+
return kinds;
|
|
652
|
+
}
|
|
653
|
+
/** Rebase progress ("<done>/<total>") plus the commit the rebase stopped at, from the rebase
|
|
654
|
+
* state files (merge backend: msgnum/end/stopped-sha; apply backend: next/last). */
|
|
655
|
+
async function rebaseProgress(deps, worktreePath) {
|
|
656
|
+
const gitPaths = await runGit(deps, worktreePath, [
|
|
657
|
+
"rev-parse",
|
|
658
|
+
"--path-format=absolute",
|
|
659
|
+
"--git-path",
|
|
660
|
+
"rebase-merge",
|
|
661
|
+
"--git-path",
|
|
662
|
+
"rebase-apply",
|
|
663
|
+
]);
|
|
664
|
+
let step = "?";
|
|
665
|
+
let stoppedSha;
|
|
666
|
+
if (gitPaths.code === 0) {
|
|
667
|
+
const [mergeDir, applyDir] = gitPaths.stdout
|
|
668
|
+
.split(/\r?\n/)
|
|
669
|
+
.map((line) => line.trim())
|
|
670
|
+
.filter(Boolean);
|
|
671
|
+
const readCounter = (dir, doneName, totalName) => {
|
|
672
|
+
if (!dir)
|
|
673
|
+
return undefined;
|
|
674
|
+
const done = readFileMaybe(deps, join(dir, doneName))?.trim();
|
|
675
|
+
const total = readFileMaybe(deps, join(dir, totalName))?.trim();
|
|
676
|
+
return done && total ? `${done}/${total}` : undefined;
|
|
677
|
+
};
|
|
678
|
+
step = readCounter(mergeDir, "msgnum", "end") ?? readCounter(applyDir, "next", "last") ?? "?";
|
|
679
|
+
stoppedSha = mergeDir ? readFileMaybe(deps, join(mergeDir, "stopped-sha"))?.trim() : undefined;
|
|
680
|
+
}
|
|
681
|
+
if (!stoppedSha)
|
|
682
|
+
return { step };
|
|
683
|
+
const subject = await runGit(deps, worktreePath, ["log", "-1", "--format=%s", stoppedSha]);
|
|
684
|
+
return {
|
|
685
|
+
step,
|
|
686
|
+
stoppedAtCommit: { sha: stoppedSha, subject: subject.code === 0 ? subject.stdout.trim() : "" },
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Build the structured conflict worklist (G9's input): the agent edits exactly these files, then
|
|
691
|
+
* calls `continue` -- staging, marker verification, and rebase continuation are harness work.
|
|
692
|
+
* Files rerere auto-resolved never appear here (they are staged, not unmerged) -- the mechanical
|
|
693
|
+
* drive loop below continues past fully-replayed stops without involving the agent at all, so
|
|
694
|
+
* `resolvedByRerere` marks nothing today and is reserved for partial-replay surfacing.
|
|
695
|
+
*/
|
|
696
|
+
async function buildWorklist(deps, worktreePath, unmerged) {
|
|
697
|
+
const kinds = await unmergedKinds(deps, worktreePath);
|
|
698
|
+
const progress = await rebaseProgress(deps, worktreePath);
|
|
699
|
+
return {
|
|
700
|
+
step: progress.step,
|
|
701
|
+
...(progress.stoppedAtCommit ? { stoppedAtCommit: progress.stoppedAtCommit } : {}),
|
|
702
|
+
files: unmerged.map((path) => ({
|
|
703
|
+
path,
|
|
704
|
+
kind: kinds.get(path) ?? "unknown",
|
|
705
|
+
resolvedByRerere: false,
|
|
706
|
+
})),
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
const CONFLICT_MARKER_RE = /^(?:<{7}|={7}|>{7}|\|{7})/m;
|
|
710
|
+
/**
|
|
711
|
+
* Mechanically drive an in-progress rebase forward: finish it, stop at the next REAL conflict
|
|
712
|
+
* (returning its worklist), auto-continue stops that rerere fully replayed, and skip steps that
|
|
713
|
+
* provably reduced to nothing (their changes already landed). Bounded; never guesses -- an
|
|
714
|
+
* unrecognized stop state surfaces as git_error with the evidence.
|
|
715
|
+
*/
|
|
716
|
+
async function driveRebase(deps, ctx, lane) {
|
|
717
|
+
const worktreePath = lane.worktreePath;
|
|
718
|
+
let autoContinued = 0;
|
|
719
|
+
for (let iteration = 0; iteration <= 1000; iteration++) {
|
|
720
|
+
if (!(await isRebaseActive(deps, worktreePath))) {
|
|
721
|
+
await appendAuditEvent(ctx.paths, { event: "sync_completed", laneKey: lane.laneKey, autoContinued }, nowIso(deps));
|
|
722
|
+
return { code: "sync_clean", laneKey: lane.laneKey, autoContinued };
|
|
723
|
+
}
|
|
724
|
+
const unmerged = await unmergedFiles(deps, worktreePath);
|
|
725
|
+
if (unmerged.length > 0) {
|
|
726
|
+
const worklist = await buildWorklist(deps, worktreePath, unmerged);
|
|
727
|
+
await appendAuditEvent(ctx.paths, { event: "sync_conflicts", laneKey: lane.laneKey, step: worklist.step, files: worklist.files.length }, nowIso(deps));
|
|
728
|
+
return { code: "sync_conflicts", laneKey: lane.laneKey, worklist };
|
|
729
|
+
}
|
|
730
|
+
// Stopped with nothing unmerged: rerere fully replayed the resolution (auto-staged via
|
|
731
|
+
// rerere.autoUpdate) or the step reduced to nothing. Continue mechanically.
|
|
732
|
+
const cont = await runGitEnv(deps, worktreePath, ["rebase", "--continue"]);
|
|
733
|
+
autoContinued++;
|
|
734
|
+
if (cont.code === 0)
|
|
735
|
+
continue;
|
|
736
|
+
if ((await unmergedFiles(deps, worktreePath)).length > 0)
|
|
737
|
+
continue;
|
|
738
|
+
if (!(await isRebaseActive(deps, worktreePath)))
|
|
739
|
+
continue;
|
|
740
|
+
const staged = await runGit(deps, worktreePath, ["diff", "--cached", "--quiet"]);
|
|
741
|
+
const unstaged = await runGit(deps, worktreePath, ["diff", "--quiet"]);
|
|
742
|
+
if (staged.code === 0 && unstaged.code === 0) {
|
|
743
|
+
// Provably-empty step: its changes are already on main. Skipping is the one correct move.
|
|
744
|
+
const skip = await runGitEnv(deps, worktreePath, ["rebase", "--skip"]);
|
|
745
|
+
if (skip.code !== 0 && (await isRebaseActive(deps, worktreePath))) {
|
|
746
|
+
return gitError("git rebase --skip failed on a provably-empty step", skip);
|
|
747
|
+
}
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
return gitError("git rebase --continue failed in an unrecognized state", cont);
|
|
751
|
+
}
|
|
752
|
+
return {
|
|
753
|
+
code: "git_error",
|
|
754
|
+
message: "rebase drive exceeded 1000 mechanical steps; aborting the drive (rebase left in progress)",
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Rebase current main into the lane branch, locally (the awareness half of "perfect sync"; the
|
|
759
|
+
* land gate is the enforcement half). Pins the main sha it derived so a concurrent land cannot
|
|
760
|
+
* change this sync's meaning mid-flight (D-pinned-sha). Conflicts leave the rebase in progress
|
|
761
|
+
* and return a structured worklist; `continueSync` verifies and drives on.
|
|
762
|
+
*/
|
|
763
|
+
export async function syncLane(deps, args) {
|
|
764
|
+
const ctx = await resolveRepoContext(deps);
|
|
765
|
+
if ("code" in ctx)
|
|
766
|
+
return ctx;
|
|
767
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
768
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
769
|
+
if (!lane || lane.status === "released") {
|
|
770
|
+
return { code: "lane_not_found", message: `no active lane '${args.laneKey}'` };
|
|
771
|
+
}
|
|
772
|
+
const facts = await deriveLaneFacts(deps, ctx, lane);
|
|
773
|
+
if (!facts.worktreePresent) {
|
|
774
|
+
return {
|
|
775
|
+
code: "worktree_missing",
|
|
776
|
+
message: `lane '${lane.laneKey}' checkout missing at ${lane.worktreePath}`,
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
if (facts.rebaseInProgress) {
|
|
780
|
+
return {
|
|
781
|
+
code: "rebase_in_progress",
|
|
782
|
+
message: `lane '${lane.laneKey}' has a rebase in progress; resolve and call continue, or abort_sync`,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
if (facts.fresh) {
|
|
786
|
+
return { code: "sync_clean", laneKey: lane.laneKey, alreadyFresh: true, autoContinued: 0 };
|
|
787
|
+
}
|
|
788
|
+
if (facts.dirty) {
|
|
789
|
+
return {
|
|
790
|
+
code: "lane_dirty",
|
|
791
|
+
message: `lane '${lane.laneKey}' has uncommitted changes; commit them on the lane branch, then sync`,
|
|
792
|
+
paths: await dirtyPaths(deps, lane.worktreePath),
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
await appendAuditEvent(ctx.paths, { event: "sync_started", laneKey: lane.laneKey, ontoSha: ctx.mainSha }, nowIso(deps));
|
|
796
|
+
const rebase = await runGitEnv(deps, lane.worktreePath, ["rebase", ctx.mainSha]);
|
|
797
|
+
if (rebase.code === 0) {
|
|
798
|
+
await appendAuditEvent(ctx.paths, { event: "sync_completed", laneKey: lane.laneKey, autoContinued: 0 }, nowIso(deps));
|
|
799
|
+
return { code: "sync_clean", laneKey: lane.laneKey, alreadyFresh: false, autoContinued: 0 };
|
|
800
|
+
}
|
|
801
|
+
if (!(await isRebaseActive(deps, lane.worktreePath))) {
|
|
802
|
+
return gitError(`git rebase failed to start for lane '${lane.laneKey}'`, rebase);
|
|
803
|
+
}
|
|
804
|
+
const driven = await driveRebase(deps, ctx, lane);
|
|
805
|
+
if (driven.code === "sync_clean")
|
|
806
|
+
return { ...driven, alreadyFresh: false };
|
|
807
|
+
return driven;
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* G9: verify the agent's conflict resolution mechanically (zero conflict markers by byte-scan --
|
|
812
|
+
* the agent never self-certifies), stage it, and drive the rebase on. More conflicts return the
|
|
813
|
+
* next worklist; completion returns sync_clean.
|
|
814
|
+
*/
|
|
815
|
+
export async function continueSync(deps, args) {
|
|
816
|
+
const ctx = await resolveRepoContext(deps);
|
|
817
|
+
if ("code" in ctx)
|
|
818
|
+
return ctx;
|
|
819
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
820
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
821
|
+
if (!lane || lane.status === "released") {
|
|
822
|
+
return { code: "lane_not_found", message: `no active lane '${args.laneKey}'` };
|
|
823
|
+
}
|
|
824
|
+
if (!fileExists(deps, lane.worktreePath)) {
|
|
825
|
+
return {
|
|
826
|
+
code: "worktree_missing",
|
|
827
|
+
message: `lane '${lane.laneKey}' checkout missing at ${lane.worktreePath}`,
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
if (!(await isRebaseActive(deps, lane.worktreePath))) {
|
|
831
|
+
return {
|
|
832
|
+
code: "no_rebase_in_progress",
|
|
833
|
+
message: `lane '${lane.laneKey}' has no rebase in progress; use sync`,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
const unmerged = await unmergedFiles(deps, lane.worktreePath);
|
|
837
|
+
const withMarkers = unmerged.filter((path) => {
|
|
838
|
+
const content = readFileMaybe(deps, join(lane.worktreePath, path));
|
|
839
|
+
return content !== undefined && CONFLICT_MARKER_RE.test(content);
|
|
840
|
+
});
|
|
841
|
+
if (withMarkers.length > 0) {
|
|
842
|
+
return {
|
|
843
|
+
code: "conflict_markers_present",
|
|
844
|
+
message: `${withMarkers.length} file(s) still contain conflict markers; resolve them fully, then continue`,
|
|
845
|
+
paths: withMarkers,
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
if (unmerged.length > 0) {
|
|
849
|
+
const add = await runGit(deps, lane.worktreePath, ["add", "-A"]);
|
|
850
|
+
if (add.code !== 0)
|
|
851
|
+
return gitError("git add -A failed while staging conflict resolutions", add);
|
|
852
|
+
}
|
|
853
|
+
const cont = await runGitEnv(deps, lane.worktreePath, ["rebase", "--continue"]);
|
|
854
|
+
if (cont.code === 0 && !(await isRebaseActive(deps, lane.worktreePath))) {
|
|
855
|
+
await appendAuditEvent(ctx.paths, { event: "sync_completed", laneKey: lane.laneKey, autoContinued: 0 }, nowIso(deps));
|
|
856
|
+
return { code: "sync_clean", laneKey: lane.laneKey, autoContinued: 0 };
|
|
857
|
+
}
|
|
858
|
+
const result = await driveRebase(deps, ctx, lane);
|
|
859
|
+
return result;
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
/** Abort an in-progress sync rebase: the lane returns to its pre-sync tip -- still stale, and
|
|
863
|
+
* honestly reported as such by status. */
|
|
864
|
+
export async function abortSync(deps, args) {
|
|
865
|
+
const ctx = await resolveRepoContext(deps);
|
|
866
|
+
if ("code" in ctx)
|
|
867
|
+
return ctx;
|
|
868
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
869
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
870
|
+
if (!lane || lane.status === "released") {
|
|
871
|
+
return { code: "lane_not_found", message: `no active lane '${args.laneKey}'` };
|
|
872
|
+
}
|
|
873
|
+
if (!(await isRebaseActive(deps, lane.worktreePath))) {
|
|
874
|
+
return { code: "no_rebase_in_progress", message: `lane '${lane.laneKey}' has no rebase in progress` };
|
|
875
|
+
}
|
|
876
|
+
const abort = await runGitEnv(deps, lane.worktreePath, ["rebase", "--abort"]);
|
|
877
|
+
if (abort.code !== 0)
|
|
878
|
+
return gitError("git rebase --abort failed", abort);
|
|
879
|
+
await appendAuditEvent(ctx.paths, { event: "sync_aborted", laneKey: lane.laneKey }, nowIso(deps));
|
|
880
|
+
return { code: "ok", laneKey: lane.laneKey };
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
const DEFAULT_GATE_TIMEOUT_MS = 900_000;
|
|
884
|
+
/**
|
|
885
|
+
* The land gate -- the ONLY door to main (G1-G7). Serialized under the integration lock; every
|
|
886
|
+
* precondition is re-derived INSIDE the lock (the compare-and-swap that makes the whole system
|
|
887
|
+
* race-free: even if every notification failed, a stale lane cannot land). Main only ever moves
|
|
888
|
+
* by ff-only merge of an already-rebased lane branch; a successful land bumps the epoch and
|
|
889
|
+
* broadcasts in the same critical section.
|
|
890
|
+
*/
|
|
891
|
+
export async function landLane(deps, args) {
|
|
892
|
+
const ctx = await resolveRepoContext(deps);
|
|
893
|
+
if ("code" in ctx)
|
|
894
|
+
return ctx;
|
|
895
|
+
return withFileLock(ctx.paths.lifecycleLockFile, async () => {
|
|
896
|
+
const lane = await readLane(ctx.paths, args.laneKey);
|
|
897
|
+
if (!lane || lane.status === "released") {
|
|
898
|
+
return { code: "lane_not_found", message: `no active lane '${args.laneKey}'` };
|
|
899
|
+
}
|
|
900
|
+
if (ownerConflicts(deps, lane)) {
|
|
901
|
+
return {
|
|
902
|
+
code: "lane_owner_conflict",
|
|
903
|
+
message: `lane '${lane.laneKey}' is owned by another live session (pid ${lane.ownerPid}); only its owner may land it`,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
if (args.gate === "on" && !args.gateCommand?.trim()) {
|
|
907
|
+
return {
|
|
908
|
+
code: "gate_command_unset",
|
|
909
|
+
message: 'no gate command configured; set worktreeSync.gateCommand (or worktreeSync.gate: "off" as an owner-level opt-out)',
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
const acquisition = await acquireIntegrationLock(ctx.paths, {
|
|
913
|
+
pid: deps.pid ?? process.pid,
|
|
914
|
+
hostname: osHostname(),
|
|
915
|
+
...(deps.sessionId !== undefined ? { sessionId: deps.sessionId } : {}),
|
|
916
|
+
laneKey: lane.laneKey,
|
|
917
|
+
}, {
|
|
918
|
+
now: deps.now,
|
|
919
|
+
...(deps.isPidAlive
|
|
920
|
+
? { isPidAlive: (owner) => deps.isPidAlive(owner.pid) }
|
|
921
|
+
: {}),
|
|
922
|
+
});
|
|
923
|
+
if (!acquisition.acquired) {
|
|
924
|
+
return {
|
|
925
|
+
code: "lock_busy",
|
|
926
|
+
message: acquisition.holder
|
|
927
|
+
? `integration lock held by pid ${acquisition.holder.pid}${acquisition.holder.laneKey ? ` (lane '${acquisition.holder.laneKey}')` : ""}; retry after it lands`
|
|
928
|
+
: "integration lock held; retry shortly",
|
|
929
|
+
...(acquisition.holder ? { holder: acquisition.holder } : {}),
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
try {
|
|
933
|
+
// Everything below happens INSIDE the lock: re-derive, never trust pre-lock derivations.
|
|
934
|
+
const mainShaNow = await revParseBranch(deps, ctx.topLevel, ctx.mainBranch);
|
|
935
|
+
if (!mainShaNow) {
|
|
936
|
+
return { code: "default_branch_unresolved", message: `main branch '${ctx.mainBranch}' vanished mid-land` };
|
|
937
|
+
}
|
|
938
|
+
if (!fileExists(deps, lane.worktreePath)) {
|
|
939
|
+
return {
|
|
940
|
+
code: "worktree_missing",
|
|
941
|
+
message: `lane '${lane.laneKey}' checkout missing at ${lane.worktreePath}`,
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
if (await isRebaseActive(deps, lane.worktreePath)) {
|
|
945
|
+
return {
|
|
946
|
+
code: "rebase_in_progress",
|
|
947
|
+
message: `lane '${lane.laneKey}' has a rebase in progress; finish (continue) or abort_sync before landing`,
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
const laneDirty = await dirtyPaths(deps, lane.worktreePath);
|
|
951
|
+
if (laneDirty.length > 0) {
|
|
952
|
+
return {
|
|
953
|
+
code: "lane_dirty",
|
|
954
|
+
message: `lane '${lane.laneKey}' has uncommitted changes; commit everything on the lane branch first (G2)`,
|
|
955
|
+
paths: laneDirty,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
const tipSha = await revParseBranch(deps, ctx.topLevel, lane.branch);
|
|
959
|
+
if (!tipSha)
|
|
960
|
+
return { code: "git_error", message: `lane branch '${lane.branch}' has no commits/ref` };
|
|
961
|
+
if (tipSha === mainShaNow) {
|
|
962
|
+
// A no-op land would bump the epoch for nothing (notification churn for every lane).
|
|
963
|
+
return {
|
|
964
|
+
code: "nothing_to_land",
|
|
965
|
+
message: `lane '${lane.laneKey}' has no commits beyond main; nothing to land`,
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
const ancestry = await runGit(deps, ctx.topLevel, ["merge-base", "--is-ancestor", mainShaNow, tipSha]);
|
|
969
|
+
if (ancestry.code !== 0) {
|
|
970
|
+
return {
|
|
971
|
+
code: "stale_lane",
|
|
972
|
+
message: `lane '${lane.laneKey}' does not contain current main (${mainShaNow.slice(0, 12)}); sync first (G3)`,
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
if (!ctx.hubPath) {
|
|
976
|
+
return {
|
|
977
|
+
code: "hub_missing",
|
|
978
|
+
message: `main branch '${ctx.mainBranch}' is not checked out in any worktree; landing needs the hub checkout (G5)`,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
const changedResult = await runGit(deps, ctx.topLevel, ["diff", "--name-only", `${mainShaNow}..${tipSha}`]);
|
|
982
|
+
if (changedResult.code !== 0)
|
|
983
|
+
return gitError("git diff --name-only failed while computing the land set", changedResult);
|
|
984
|
+
const changed = changedResult.stdout
|
|
985
|
+
.split(/\r?\n/)
|
|
986
|
+
.map((line) => line.trim())
|
|
987
|
+
.filter(Boolean);
|
|
988
|
+
// G6, overlap-based: an ff merge only touches the land's changed files, and git itself
|
|
989
|
+
// refuses to clobber local modifications -- so the deterministic pre-check refuses exactly
|
|
990
|
+
// when hub-dirty paths intersect the incoming change set (not on unrelated hub dirt).
|
|
991
|
+
const hubDirty = await dirtyPaths(deps, ctx.hubPath);
|
|
992
|
+
const changedSet = new Set(changed);
|
|
993
|
+
const endangered = hubDirty.filter((path) => changedSet.has(path));
|
|
994
|
+
if (endangered.length > 0) {
|
|
995
|
+
return {
|
|
996
|
+
code: "hub_dirty",
|
|
997
|
+
message: `hub checkout has local modifications to ${endangered.length} file(s) this land would update; commit/stash them first (G6)`,
|
|
998
|
+
paths: endangered.slice(0, 50),
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
if (args.gate === "on") {
|
|
1002
|
+
const gateCommand = (args.gateCommand ?? "").trim();
|
|
1003
|
+
const shell = process.platform === "win32" ? "cmd" : "sh";
|
|
1004
|
+
const shellFlag = process.platform === "win32" ? "/c" : "-c";
|
|
1005
|
+
const gateRun = await deps.exec(shell, [shellFlag, gateCommand], {
|
|
1006
|
+
cwd: lane.worktreePath,
|
|
1007
|
+
timeout: args.gateTimeoutMs ?? DEFAULT_GATE_TIMEOUT_MS,
|
|
1008
|
+
signal: deps.signal,
|
|
1009
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
1010
|
+
});
|
|
1011
|
+
if (gateRun.code !== 0) {
|
|
1012
|
+
const tail = `${gateRun.stdout}\n${gateRun.stderr}`.trim().slice(-4000);
|
|
1013
|
+
return {
|
|
1014
|
+
code: "gate_failed",
|
|
1015
|
+
message: `gate command failed (exit ${gateRun.code}) at lane tip ${tipSha.slice(0, 12)} (G4)`,
|
|
1016
|
+
gitStderr: tail,
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
// The gate is an external process. Re-read the ref after it exits: a worker
|
|
1020
|
+
// may have committed while the gate was running, and the branch name is
|
|
1021
|
+
// mutable. Never merge a tip that was not the one actually tested.
|
|
1022
|
+
const postGateTip = await revParseBranch(deps, ctx.topLevel, lane.branch);
|
|
1023
|
+
if (postGateTip !== tipSha) {
|
|
1024
|
+
return {
|
|
1025
|
+
code: "lane_changed_during_gate",
|
|
1026
|
+
message: `lane '${lane.laneKey}' changed during the gate (tested ${tipSha.slice(0, 12)}, current ${postGateTip?.slice(0, 12) ?? "missing"}); rerun land`,
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
const landingTransaction = {
|
|
1031
|
+
laneKey: lane.laneKey,
|
|
1032
|
+
priorMainSha: mainShaNow,
|
|
1033
|
+
testedTipSha: tipSha,
|
|
1034
|
+
changedPaths: changed.slice(0, EPOCH_CHANGED_PATHS_CAP),
|
|
1035
|
+
changedPathsTruncated: changed.length > EPOCH_CHANGED_PATHS_CAP,
|
|
1036
|
+
lockToken: acquisition.token,
|
|
1037
|
+
gate: args.gate === "on" ? "passed" : "off",
|
|
1038
|
+
stage: "ready_to_merge",
|
|
1039
|
+
};
|
|
1040
|
+
await writeLandingTransaction(ctx.paths, landingTransaction);
|
|
1041
|
+
// Move the named main ref with a compare-and-swap instead of `git merge` in whichever
|
|
1042
|
+
// branch happens to be checked out in the hub. A concurrent branch switch cannot redirect
|
|
1043
|
+
// this land to another branch, and a changed main ref makes update-ref fail without mutation.
|
|
1044
|
+
const update = await runGit(deps, ctx.topLevel, [
|
|
1045
|
+
"update-ref",
|
|
1046
|
+
`refs/heads/${ctx.mainBranch}`,
|
|
1047
|
+
tipSha,
|
|
1048
|
+
mainShaNow,
|
|
1049
|
+
]);
|
|
1050
|
+
if (update.code !== 0) {
|
|
1051
|
+
return gitError(`main ref changed or could not advance for lane '${lane.laneKey}' (G5)`, update);
|
|
1052
|
+
}
|
|
1053
|
+
const landedSha = await revParseBranch(deps, ctx.topLevel, ctx.mainBranch);
|
|
1054
|
+
if (landedSha !== tipSha) {
|
|
1055
|
+
return {
|
|
1056
|
+
code: "git_error",
|
|
1057
|
+
message: `main resolved to ${landedSha?.slice(0, 12) ?? "missing"} after the guarded ref update; expected gated tip ${tipSha.slice(0, 12)}`,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
// `update-ref` deliberately leaves a checked-out worktree untouched. Refresh only when
|
|
1061
|
+
// the hub is still on main, preserving unrelated local changes with reset --merge.
|
|
1062
|
+
const checkedOut = await runGit(deps, ctx.hubPath, ["symbolic-ref", "--short", "HEAD"]);
|
|
1063
|
+
if (checkedOut.code !== 0 || checkedOut.stdout.trim() !== ctx.mainBranch) {
|
|
1064
|
+
return {
|
|
1065
|
+
code: "git_error",
|
|
1066
|
+
message: `main advanced safely, but hub checkout is no longer '${ctx.mainBranch}'; refusing to refresh another branch`,
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
const refresh = await runGit(deps, ctx.hubPath, ["reset", "--merge", tipSha]);
|
|
1070
|
+
if (refresh.code !== 0)
|
|
1071
|
+
return gitError(`could not refresh hub checkout after landing '${lane.laneKey}'`, refresh);
|
|
1072
|
+
await writeLandingTransaction(ctx.paths, { ...landingTransaction, stage: "main_moved" });
|
|
1073
|
+
const previous = await readEpoch(ctx.paths);
|
|
1074
|
+
const epochNumber = (previous?.epoch ?? 0) + 1;
|
|
1075
|
+
const at = nowIso(deps);
|
|
1076
|
+
await writeEpoch(ctx.paths, {
|
|
1077
|
+
epoch: epochNumber,
|
|
1078
|
+
mainSha: tipSha,
|
|
1079
|
+
previousMainSha: mainShaNow,
|
|
1080
|
+
landedLaneKey: lane.laneKey,
|
|
1081
|
+
landedAt: at,
|
|
1082
|
+
changedPaths: changed.slice(0, EPOCH_CHANGED_PATHS_CAP),
|
|
1083
|
+
changedPathsTruncated: changed.length > EPOCH_CHANGED_PATHS_CAP,
|
|
1084
|
+
});
|
|
1085
|
+
await writeLandingTransaction(ctx.paths, { ...landingTransaction, stage: "epoch_written" });
|
|
1086
|
+
await appendAuditEvent(ctx.paths, {
|
|
1087
|
+
event: "epoch_advanced",
|
|
1088
|
+
epoch: epochNumber,
|
|
1089
|
+
laneKey: lane.laneKey,
|
|
1090
|
+
mainSha: tipSha,
|
|
1091
|
+
previousMainSha: mainShaNow,
|
|
1092
|
+
changedFiles: changed.length,
|
|
1093
|
+
gate: args.gate === "on" ? "passed" : "off",
|
|
1094
|
+
}, at);
|
|
1095
|
+
await writeLandingTransaction(ctx.paths, { ...landingTransaction, stage: "audit_logged" });
|
|
1096
|
+
await clearLandingTransaction(ctx.paths);
|
|
1097
|
+
return {
|
|
1098
|
+
code: "ok",
|
|
1099
|
+
laneKey: lane.laneKey,
|
|
1100
|
+
epoch: epochNumber,
|
|
1101
|
+
mainSha: tipSha,
|
|
1102
|
+
gate: args.gate === "on" ? "passed" : "off",
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
finally {
|
|
1106
|
+
await releaseIntegrationLock(ctx.paths, acquisition.token, { now: deps.now });
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* The deterministic full picture (the tool's `status` action): epoch, hub, lock, and per-lane
|
|
1112
|
+
* live facts with the policy derivation (stale / syncRequired / overlap). The `advice` line is
|
|
1113
|
+
* assembled from codes -- never model-generated -- so every agent reads the same situation the
|
|
1114
|
+
* same way.
|
|
1115
|
+
*/
|
|
1116
|
+
export async function buildSyncStatus(deps, args) {
|
|
1117
|
+
const ctx = await resolveRepoContext(deps);
|
|
1118
|
+
if ("code" in ctx)
|
|
1119
|
+
return ctx;
|
|
1120
|
+
const epochRecord = await readEpoch(ctx.paths);
|
|
1121
|
+
const epochNumber = epochRecord?.epoch ?? 0;
|
|
1122
|
+
const changedSet = new Set(epochRecord?.changedPaths ?? []);
|
|
1123
|
+
const changedTruncated = epochRecord?.changedPathsTruncated === true;
|
|
1124
|
+
const holder = await readLockHolder(ctx.paths);
|
|
1125
|
+
const isAlive = deps.isPidAlive ??
|
|
1126
|
+
((pid) => defaultIsPidAlive({ pid, hostname: osHostname(), token: "status", acquiredAt: "" }));
|
|
1127
|
+
let hub;
|
|
1128
|
+
if (ctx.hubPath) {
|
|
1129
|
+
hub = { path: ctx.hubPath, clean: (await dirtyPaths(deps, ctx.hubPath)).length === 0 };
|
|
1130
|
+
}
|
|
1131
|
+
const lanes = [];
|
|
1132
|
+
for (const lane of await listLanes(ctx.paths)) {
|
|
1133
|
+
if (lane.status === "released")
|
|
1134
|
+
continue;
|
|
1135
|
+
const facts = await deriveLaneFacts(deps, ctx, lane);
|
|
1136
|
+
let laneChanged = [];
|
|
1137
|
+
if (facts.branchSha) {
|
|
1138
|
+
const diff = await runGit(deps, ctx.topLevel, ["diff", "--name-only", `${ctx.mainBranch}...${lane.branch}`]);
|
|
1139
|
+
if (diff.code === 0) {
|
|
1140
|
+
laneChanged = diff.stdout
|
|
1141
|
+
.split(/\r?\n/)
|
|
1142
|
+
.map((line) => line.trim())
|
|
1143
|
+
.filter(Boolean)
|
|
1144
|
+
.slice(0, EPOCH_CHANGED_PATHS_CAP);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const overlap = changedTruncated ? laneChanged : laneChanged.filter((path) => changedSet.has(path));
|
|
1148
|
+
const stale = !facts.fresh;
|
|
1149
|
+
const syncRequired = stale &&
|
|
1150
|
+
facts.registrationStatus === "active" &&
|
|
1151
|
+
(args.policy === "on_land_mandatory" || (args.policy === "overlap_mandatory" && overlap.length > 0));
|
|
1152
|
+
lanes.push({
|
|
1153
|
+
...facts,
|
|
1154
|
+
...(lane.goalId !== undefined ? { goalId: lane.goalId } : {}),
|
|
1155
|
+
...(lane.requirementId !== undefined ? { requirementId: lane.requirementId } : {}),
|
|
1156
|
+
...(lane.boundLaneId !== undefined ? { boundLaneId: lane.boundLaneId } : {}),
|
|
1157
|
+
stale,
|
|
1158
|
+
syncRequired,
|
|
1159
|
+
overlapWithLastLand: overlap.slice(0, 50),
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
const inRebase = lanes.filter((lane) => lane.rebaseInProgress).map((lane) => lane.laneKey);
|
|
1163
|
+
const mustSync = lanes.filter((lane) => lane.syncRequired && !lane.rebaseInProgress).map((lane) => lane.laneKey);
|
|
1164
|
+
let advice;
|
|
1165
|
+
if (inRebase.length > 0) {
|
|
1166
|
+
advice = `lane(s) ${inRebase.join(", ")} have a rebase in progress: resolve conflicts and call continue (or abort_sync)`;
|
|
1167
|
+
}
|
|
1168
|
+
else if (mustSync.length > 0) {
|
|
1169
|
+
advice = `lane(s) ${mustSync.join(", ")} must rebase main (epoch ${epochNumber}${epochRecord?.landedLaneKey ? ` landed by ${epochRecord.landedLaneKey}` : ""}): call sync`;
|
|
1170
|
+
}
|
|
1171
|
+
else if (holder) {
|
|
1172
|
+
advice = `integration lock held by pid ${holder.pid}${holder.laneKey ? ` (lane '${holder.laneKey}')` : ""}`;
|
|
1173
|
+
}
|
|
1174
|
+
else if (lanes.length > 0) {
|
|
1175
|
+
advice = "all lanes fresh";
|
|
1176
|
+
}
|
|
1177
|
+
return {
|
|
1178
|
+
code: "ok",
|
|
1179
|
+
mainBranch: ctx.mainBranch,
|
|
1180
|
+
mainSha: ctx.mainSha,
|
|
1181
|
+
epoch: epochNumber,
|
|
1182
|
+
...(hub ? { hub } : {}),
|
|
1183
|
+
lock: {
|
|
1184
|
+
held: holder !== undefined,
|
|
1185
|
+
...(holder ? { holder, holderAlive: isAlive(holder.pid) } : {}),
|
|
1186
|
+
},
|
|
1187
|
+
lanes,
|
|
1188
|
+
...(advice ? { advice } : {}),
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
//# sourceMappingURL=git-engine.js.map
|