@caupulican/pi-adaptative 0.81.37 → 0.81.39
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 +91 -0
- package/README.md +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -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 +196 -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 +308 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -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 +585 -24
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
- 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 +5 -5
- package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-paths.d.ts +49 -0
- package/dist/core/agent-paths.d.ts.map +1 -0
- package/dist/core/agent-paths.js +107 -0
- package/dist/core/agent-paths.js.map +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +3 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +234 -17
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +484 -62
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/contracts.d.ts +9 -0
- package/dist/core/autonomy/contracts.d.ts.map +1 -1
- package/dist/core/autonomy/contracts.js.map +1 -1
- package/dist/core/autonomy/lane-tracker.d.ts +10 -1
- package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tracker.js +5 -1
- package/dist/core/autonomy/lane-tracker.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +122 -6
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +447 -90
- 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/compaction-support.d.ts +13 -3
- 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-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/sqlite-runtime-index.d.ts.map +1 -1
- package/dist/core/context/sqlite-runtime-index.js +13 -7
- package/dist/core/context/sqlite-runtime-index.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +16 -1
- 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 +137 -14
- 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/delegation/session-worker-result.d.ts +34 -4
- package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
- package/dist/core/delegation/session-worker-result.js +64 -4
- package/dist/core/delegation/session-worker-result.js.map +1 -1
- package/dist/core/delegation/worker-actions.d.ts.map +1 -1
- package/dist/core/delegation/worker-actions.js +1 -1
- package/dist/core/delegation/worker-actions.js.map +1 -1
- package/dist/core/delegation/worker-result.d.ts +42 -1
- package/dist/core/delegation/worker-result.d.ts.map +1 -1
- package/dist/core/delegation/worker-result.js +68 -0
- package/dist/core/delegation/worker-result.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +23 -7
- 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 +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +51 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.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-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 +77 -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 +4 -0
- package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
- package/dist/core/goals/goal-continuation-prompt.js +51 -10
- package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
- package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
- package/dist/core/goals/goal-runtime-snapshot.js +87 -4
- package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +89 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +71 -5
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +56 -2
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +111 -5
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/goals/session-goal-state.d.ts +11 -2
- package/dist/core/goals/session-goal-state.d.ts.map +1 -1
- package/dist/core/goals/session-goal-state.js +32 -17
- package/dist/core/goals/session-goal-state.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +10 -2
- package/dist/core/keybindings.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/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/model-capability.d.ts +34 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +42 -1
- package/dist/core/model-capability.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/models/adaptation-store.d.ts +7 -0
- package/dist/core/models/adaptation-store.d.ts.map +1 -1
- package/dist/core/models/adaptation-store.js +47 -11
- 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 +3 -0
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +11 -2
- package/dist/core/models/fitness-store.js.map +1 -1
- 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 +9 -7
- 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/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-matrix/codes.d.ts +72 -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/runtime.d.ts +63 -0
- package/dist/core/process-matrix/runtime.d.ts.map +1 -0
- package/dist/core/process-matrix/runtime.js +310 -0
- package/dist/core/process-matrix/runtime.js.map +1 -0
- package/dist/core/process-matrix/store.d.ts +22 -0
- package/dist/core/process-matrix/store.d.ts.map +1 -0
- package/dist/core/process-matrix/store.js +80 -0
- package/dist/core/process-matrix/store.js.map +1 -0
- package/dist/core/process-matrix/supervisor.d.ts +72 -0
- package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
- package/dist/core/process-matrix/supervisor.js +130 -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 +20 -0
- 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 +29 -5
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +215 -126
- 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 +44 -0
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +58 -5
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +209 -25
- 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.map +1 -1
- package/dist/core/sdk.js +3 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-role.d.ts +31 -0
- package/dist/core/session-role.d.ts.map +1 -0
- package/dist/core/session-role.js +52 -0
- package/dist/core/session-role.js.map +1 -0
- package/dist/core/settings-manager.d.ts +42 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +94 -60
- 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 +11 -2
- package/dist/core/tasks/session-task-state.d.ts.map +1 -1
- package/dist/core/tasks/session-task-state.js +27 -11
- 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-state.d.ts +8 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -1
- package/dist/core/tasks/task-state.js +28 -1
- package/dist/core/tasks/task-state.js.map +1 -1
- 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-recovery-log-records.d.ts +7 -0
- package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
- package/dist/core/tool-recovery-log-records.js +14 -6
- 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 +37 -0
- package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-performance-store.js +87 -3
- 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/bash.d.ts +18 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -18
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +14 -0
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +82 -7
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +25 -8
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +52 -44
- package/dist/core/tools/find.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/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +5 -4
- package/dist/core/tools/grep.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 +2 -2
- package/dist/core/tools/model-fitness.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- 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 +69 -13
- 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 +4 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +63 -8
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +86 -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 +153 -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/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/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 +247 -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/main.d.ts.map +1 -1
- package/dist/main.js +94 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts +9 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +38 -0
- 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/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/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +4 -0
- package/dist/modes/interactive/interactive-mode.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/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +24 -4
- package/dist/modes/interactive/session-flow-commands.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/shell.d.ts +7 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +39 -9
- 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/docs/development.md +2 -0
- package/docs/packages.md +1 -1
- package/docs/process-matrix.md +120 -0
- package/docs/settings.md +5 -2
- package/docs/tmux-agent-manager.md +85 -2
- package/docs/windows.md +52 -3
- package/docs/work-directory.md +29 -0
- 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/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/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,338 @@
|
|
|
1
|
+
import { isAbsolute, relative } from "node:path";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { abortSync, buildSyncStatus, continueSync, createLane, landLane, reconcile, releaseLane, resolveRepoContext, syncLane, } from "../worktree-sync/git-engine.js";
|
|
4
|
+
import { resolveLaneMutationPath } from "../worktree-sync/lane-gate.js";
|
|
5
|
+
import { readLane } from "../worktree-sync/store.js";
|
|
6
|
+
/**
|
|
7
|
+
* `worktree_sync` -- the ENTIRE agent-facing surface of the worktree-per-lane workflow: a closed
|
|
8
|
+
* action set over the engine (`core/worktree-sync/git-engine.ts`) so agents never improvise git
|
|
9
|
+
* ceremony. Every outcome carries a tagged code in `details`; refusal text always names the exact
|
|
10
|
+
* recovery step. See docs/worktree-sync.md for the workflow and the G1-G11 gate table.
|
|
11
|
+
*/
|
|
12
|
+
function createWorktreeSyncSchema() {
|
|
13
|
+
return Type.Object({
|
|
14
|
+
action: Type.Union([
|
|
15
|
+
Type.Literal("status"),
|
|
16
|
+
Type.Literal("git_status"),
|
|
17
|
+
Type.Literal("git_diff"),
|
|
18
|
+
Type.Literal("git_add"),
|
|
19
|
+
Type.Literal("git_commit"),
|
|
20
|
+
Type.Literal("check"),
|
|
21
|
+
Type.Literal("create_lane"),
|
|
22
|
+
Type.Literal("sync"),
|
|
23
|
+
Type.Literal("continue"),
|
|
24
|
+
Type.Literal("abort_sync"),
|
|
25
|
+
Type.Literal("land"),
|
|
26
|
+
Type.Literal("release_lane"),
|
|
27
|
+
Type.Literal("reconcile"),
|
|
28
|
+
], {
|
|
29
|
+
description: "status: the deterministic full picture (epoch, hub, lock, per-lane freshness/staleness, advice). git_status/git_diff: read the bound lane with fixed argv. git_add: stage only explicitly validated paths inside the lane. git_commit: commit with a literal message. check: run only the owner-configured trusted check command. create_lane: new worktree+branch off main. sync: rebase current main into a lane (conflicts return a worklist and leave the rebase in progress). continue: verify resolved conflicts (zero markers, mechanically checked), stage, drive on. abort_sync: abort the in-progress rebase. land: the ONLY door to main -- serialized, freshness-checked, gate-command-verified, ff-only. release_lane: remove a fully-landed lane (discarding unlanded work needs confirm). reconcile: re-sync the registry with git reality.",
|
|
30
|
+
}),
|
|
31
|
+
laneKey: Type.Optional(Type.String({
|
|
32
|
+
description: "Target lane. Defaults to this session's bound lane (PI_WORKTREE_LANE) for sync/continue/abort_sync/land.",
|
|
33
|
+
})),
|
|
34
|
+
goalId: Type.Optional(Type.String({ description: "create_lane: goal to scope the auto-allocated lane key by." })),
|
|
35
|
+
requirementId: Type.Optional(Type.String({ description: "create_lane: requirement this lane will work." })),
|
|
36
|
+
paths: Type.Optional(Type.Array(Type.String(), {
|
|
37
|
+
description: "git_diff/git_add: explicit relative paths inside the bound lane.",
|
|
38
|
+
})),
|
|
39
|
+
message: Type.Optional(Type.String({ description: "git_commit: literal commit message." })),
|
|
40
|
+
confirm: Type.Optional(Type.String({
|
|
41
|
+
description: 'release_lane: pass exactly "yes-discard-lane" to release a lane that still has unlanded commits or dirty files (G11: never silent).',
|
|
42
|
+
})),
|
|
43
|
+
}, { additionalProperties: false });
|
|
44
|
+
}
|
|
45
|
+
const worktreeSyncSchema = createWorktreeSyncSchema();
|
|
46
|
+
/** Actions a worker session may run unconditionally -- read-only status plus the sync/conflict
|
|
47
|
+
* cycle on its own bound lane. Everything else is refused as `role_forbidden`, EXCEPT `land`,
|
|
48
|
+
* which is instead gated by the `worktreeSync.workerLand` setting (still subject to normal
|
|
49
|
+
* ownership/freshness gating downstream when allowed). */
|
|
50
|
+
const WORKER_ALLOWED_ACTIONS = new Set([
|
|
51
|
+
"status",
|
|
52
|
+
"git_status",
|
|
53
|
+
"git_diff",
|
|
54
|
+
"git_add",
|
|
55
|
+
"git_commit",
|
|
56
|
+
"check",
|
|
57
|
+
"sync",
|
|
58
|
+
"continue",
|
|
59
|
+
"abort_sync",
|
|
60
|
+
]);
|
|
61
|
+
function formatWorklist(worklist) {
|
|
62
|
+
const lines = [
|
|
63
|
+
`Conflicts at rebase step ${worklist.step}${worklist.stoppedAtCommit ? ` (replaying ${worklist.stoppedAtCommit.sha.slice(0, 12)}: ${worklist.stoppedAtCommit.subject})` : ""}:`,
|
|
64
|
+
];
|
|
65
|
+
for (const file of worklist.files) {
|
|
66
|
+
lines.push(`- ${file.path} [${file.kind}]`);
|
|
67
|
+
}
|
|
68
|
+
lines.push('Resolve the zdiff3 hunks in exactly these files (base section included), save, then call {"action":"continue"}. Staging and marker verification are automatic.');
|
|
69
|
+
return lines;
|
|
70
|
+
}
|
|
71
|
+
function formatStatus(status) {
|
|
72
|
+
const lines = [
|
|
73
|
+
`epoch ${status.epoch} | main ${status.mainBranch}@${status.mainSha.slice(0, 12)}` +
|
|
74
|
+
`${status.hub ? ` | hub ${status.hub.clean ? "clean" : "has local changes"}` : " | hub: main not checked out"}` +
|
|
75
|
+
`${status.lock.held ? ` | LOCK held by pid ${status.lock.holder?.pid ?? "?"}` : ""}`,
|
|
76
|
+
];
|
|
77
|
+
for (const lane of status.lanes) {
|
|
78
|
+
const flags = [
|
|
79
|
+
lane.registrationStatus !== "active" ? lane.registrationStatus : undefined,
|
|
80
|
+
lane.rebaseInProgress ? "REBASE-IN-PROGRESS" : undefined,
|
|
81
|
+
lane.syncRequired ? "SYNC-REQUIRED" : lane.stale ? "stale" : "fresh",
|
|
82
|
+
lane.dirty ? "dirty" : undefined,
|
|
83
|
+
]
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.join(", ");
|
|
86
|
+
lines.push(`- ${lane.laneKey} (${lane.branch}) +${lane.aheadOfMain}/-${lane.behindMain} [${flags}]` +
|
|
87
|
+
`${lane.overlapWithLastLand.length > 0 ? ` overlap: ${lane.overlapWithLastLand.slice(0, 5).join(", ")}` : ""}`);
|
|
88
|
+
}
|
|
89
|
+
if (status.lanes.length === 0)
|
|
90
|
+
lines.push("(no lanes)");
|
|
91
|
+
if (status.advice)
|
|
92
|
+
lines.push(`advice: ${status.advice}`);
|
|
93
|
+
return lines;
|
|
94
|
+
}
|
|
95
|
+
const WORKTREE_SYNC_PROMPT_GUIDELINES = [
|
|
96
|
+
"Work ONLY inside your lane worktree on your lane branch; never edit the hub checkout or touch main directly -- main moves exclusively through the land action.",
|
|
97
|
+
"Commit your work on the lane branch, then land. Landing is refused while the lane is stale (G3): call sync first, resolve conflicts locally, then land.",
|
|
98
|
+
"When a sync stops on conflicts, edit exactly the listed files, then call continue -- staging, marker verification, and rebase continuation are mechanical.",
|
|
99
|
+
"After any other lane lands, your lane becomes stale; under the mandatory policy your file mutations are refused until you sync. git add/commit stay available to save WIP first.",
|
|
100
|
+
"Check status when unsure -- it is the deterministic full picture; never infer sync state from raw git output.",
|
|
101
|
+
];
|
|
102
|
+
export function createWorktreeSyncToolDefinition(deps) {
|
|
103
|
+
return {
|
|
104
|
+
name: "worktree_sync",
|
|
105
|
+
label: "worktree_sync",
|
|
106
|
+
description: "Hard-gated worktree-per-lane parallel work: each agent works in its own git worktree/branch; integration is always rebase-onto-main + ff-only land, serialized under one integration lock with the gate command verified at the exact landing tip. status/create_lane/sync/continue/abort_sync/land/release_lane/reconcile. Every outcome is a tagged code with the exact recovery step; landing while stale is structurally impossible.",
|
|
107
|
+
promptSnippet: "Coordinate parallel work: lane worktrees, rebase-onto-main sync, serialized gated landing.",
|
|
108
|
+
promptGuidelines: WORKTREE_SYNC_PROMPT_GUIDELINES,
|
|
109
|
+
parameters: worktreeSyncSchema,
|
|
110
|
+
executionMode: "sequential",
|
|
111
|
+
async execute(_toolCallId, input, signal) {
|
|
112
|
+
const engineDeps = { ...deps.engineDeps(), ...(signal ? { signal } : {}) };
|
|
113
|
+
const settings = deps.settings();
|
|
114
|
+
const laneKey = input.laneKey ?? deps.boundLaneKey();
|
|
115
|
+
const respond = (lines, details) => ({
|
|
116
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
117
|
+
details: details,
|
|
118
|
+
});
|
|
119
|
+
const needLane = () => {
|
|
120
|
+
if (laneKey)
|
|
121
|
+
return undefined;
|
|
122
|
+
return "laneKey required: pass laneKey, or run inside a lane-bound session (PI_WORKTREE_LANE).";
|
|
123
|
+
};
|
|
124
|
+
const runTypedLaneAction = async () => {
|
|
125
|
+
const missingLane = needLane();
|
|
126
|
+
if (missingLane)
|
|
127
|
+
return respond([missingLane], { code: "lane_not_found", message: missingLane });
|
|
128
|
+
const context = await resolveRepoContext(engineDeps);
|
|
129
|
+
if ("code" in context)
|
|
130
|
+
return respond([`[${context.code}] ${context.message}`], context);
|
|
131
|
+
const lane = await readLane(context.paths, laneKey);
|
|
132
|
+
if (!lane || lane.status !== "active") {
|
|
133
|
+
const message = `active lane '${laneKey}' is not available`;
|
|
134
|
+
return respond([`[lane_not_found] ${message}`], { code: "lane_not_found", message });
|
|
135
|
+
}
|
|
136
|
+
const requestedPaths = input.paths ?? [];
|
|
137
|
+
const gitPaths = [];
|
|
138
|
+
for (const requestedPath of requestedPaths) {
|
|
139
|
+
if (!requestedPath || isAbsolute(requestedPath) || requestedPath.includes("\0")) {
|
|
140
|
+
const message = "git path must be a non-empty relative path without NUL";
|
|
141
|
+
return respond([`[path_outside_lane] ${message}`], { code: "path_outside_lane", message });
|
|
142
|
+
}
|
|
143
|
+
const resolved = resolveLaneMutationPath(lane.worktreePath, requestedPath);
|
|
144
|
+
if (!resolved) {
|
|
145
|
+
const message = `path '${requestedPath}' escapes the lane worktree`;
|
|
146
|
+
return respond([`[path_outside_lane] ${message}`], { code: "path_outside_lane", message });
|
|
147
|
+
}
|
|
148
|
+
gitPaths.push(relative(lane.worktreePath, resolved) || ".");
|
|
149
|
+
}
|
|
150
|
+
let command;
|
|
151
|
+
let args;
|
|
152
|
+
switch (input.action) {
|
|
153
|
+
case "git_status":
|
|
154
|
+
command = "git";
|
|
155
|
+
args = ["status", "--short", "--branch"];
|
|
156
|
+
break;
|
|
157
|
+
case "git_diff":
|
|
158
|
+
command = "git";
|
|
159
|
+
args = ["diff", "--", ...gitPaths];
|
|
160
|
+
break;
|
|
161
|
+
case "git_add":
|
|
162
|
+
if (gitPaths.length === 0) {
|
|
163
|
+
const message = "git_add requires one or more explicit in-lane paths";
|
|
164
|
+
return respond([`[path_outside_lane] ${message}`], { code: "path_outside_lane", message });
|
|
165
|
+
}
|
|
166
|
+
command = "git";
|
|
167
|
+
args = ["add", "--", ...gitPaths];
|
|
168
|
+
break;
|
|
169
|
+
case "git_commit":
|
|
170
|
+
if (!input.message?.trim() || input.message.includes("\0")) {
|
|
171
|
+
const message = "git_commit requires a non-empty literal message";
|
|
172
|
+
return respond([`[git_failed] ${message}`], { code: "git_failed", message });
|
|
173
|
+
}
|
|
174
|
+
command = "git";
|
|
175
|
+
args = ["commit", "-m", input.message];
|
|
176
|
+
break;
|
|
177
|
+
case "check": {
|
|
178
|
+
const trustedCommand = settings.gateCommand?.trim();
|
|
179
|
+
if (!trustedCommand) {
|
|
180
|
+
const message = "no trusted worktree check is configured";
|
|
181
|
+
return respond([`[gate_command_unset] ${message}`], { code: "gate_command_unset", message });
|
|
182
|
+
}
|
|
183
|
+
const shell = process.platform === "win32" ? "cmd" : "sh";
|
|
184
|
+
const shellFlag = process.platform === "win32" ? "/c" : "-c";
|
|
185
|
+
const result = await engineDeps.exec(shell, [shellFlag, trustedCommand], {
|
|
186
|
+
cwd: lane.worktreePath,
|
|
187
|
+
timeout: 900_000,
|
|
188
|
+
signal: engineDeps.signal,
|
|
189
|
+
maxBuffer: 1024 * 1024,
|
|
190
|
+
});
|
|
191
|
+
const output = `${result.stdout}\n${result.stderr}`.trim().slice(-8000);
|
|
192
|
+
if (result.code !== 0) {
|
|
193
|
+
return respond([`[gate_failed] ${output || `trusted check exited ${result.code}`}`], {
|
|
194
|
+
code: "gate_failed",
|
|
195
|
+
exitCode: result.code,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return respond([output || "trusted check passed"], { code: "ok", action: input.action });
|
|
199
|
+
}
|
|
200
|
+
default:
|
|
201
|
+
return respond(["typed lane action is unavailable for this request"], { code: "role_forbidden" });
|
|
202
|
+
}
|
|
203
|
+
const result = await engineDeps.exec(command, args, {
|
|
204
|
+
cwd: lane.worktreePath,
|
|
205
|
+
timeout: 60_000,
|
|
206
|
+
signal: engineDeps.signal,
|
|
207
|
+
maxBuffer: 1024 * 1024,
|
|
208
|
+
});
|
|
209
|
+
const output = `${result.stdout}\n${result.stderr}`.trim().slice(-8000);
|
|
210
|
+
if (result.code !== 0) {
|
|
211
|
+
return respond([`[git_failed] ${output || `${command} exited ${result.code}`}`], {
|
|
212
|
+
code: "git_failed",
|
|
213
|
+
action: input.action,
|
|
214
|
+
exitCode: result.code,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return respond([output || `${input.action} completed`], { code: "ok", action: input.action });
|
|
218
|
+
};
|
|
219
|
+
if (deps.isWorker()) {
|
|
220
|
+
const bound = deps.boundLaneKey();
|
|
221
|
+
if (input.laneKey !== undefined && input.laneKey !== bound) {
|
|
222
|
+
const message = "workers may only target their bound lane";
|
|
223
|
+
return respond([message], { code: "role_forbidden", message });
|
|
224
|
+
}
|
|
225
|
+
const landAllowed = input.action === "land" && settings.workerLand === "allow";
|
|
226
|
+
if (!WORKER_ALLOWED_ACTIONS.has(input.action) && !landAllowed) {
|
|
227
|
+
const message = input.action === "land"
|
|
228
|
+
? 'workers may not land (worktreeSync.workerLand is "deny")'
|
|
229
|
+
: `workers may not run action "${input.action}"`;
|
|
230
|
+
return respond([message], { code: "role_forbidden", message });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
switch (input.action) {
|
|
234
|
+
case "git_status":
|
|
235
|
+
case "git_diff":
|
|
236
|
+
case "git_add":
|
|
237
|
+
case "git_commit":
|
|
238
|
+
case "check":
|
|
239
|
+
return runTypedLaneAction();
|
|
240
|
+
case "status": {
|
|
241
|
+
const status = await buildSyncStatus(engineDeps, { policy: settings.syncPolicy });
|
|
242
|
+
if (status.code !== "ok")
|
|
243
|
+
return respond([`[${status.code}] ${status.message}`], status);
|
|
244
|
+
return respond(formatStatus(status), status);
|
|
245
|
+
}
|
|
246
|
+
case "create_lane": {
|
|
247
|
+
const created = await createLane(engineDeps, {
|
|
248
|
+
...(input.laneKey !== undefined ? { laneKey: input.laneKey } : {}),
|
|
249
|
+
...(input.goalId !== undefined ? { goalId: input.goalId } : {}),
|
|
250
|
+
...(input.requirementId !== undefined ? { requirementId: input.requirementId } : {}),
|
|
251
|
+
});
|
|
252
|
+
if (created.code !== "ok")
|
|
253
|
+
return respond([`[${created.code}] ${created.message}`], created);
|
|
254
|
+
return respond([
|
|
255
|
+
`lane '${created.lane.laneKey}' created: branch ${created.lane.branch}, checkout ${created.lane.worktreePath}`,
|
|
256
|
+
], created);
|
|
257
|
+
}
|
|
258
|
+
case "sync": {
|
|
259
|
+
const missing = needLane();
|
|
260
|
+
if (missing)
|
|
261
|
+
return respond([missing], { code: "lane_not_found", message: missing });
|
|
262
|
+
const synced = await syncLane(engineDeps, { laneKey: laneKey });
|
|
263
|
+
if (synced.code === "sync_clean") {
|
|
264
|
+
return respond([
|
|
265
|
+
synced.alreadyFresh
|
|
266
|
+
? `lane '${synced.laneKey}' already contains current main -- nothing to do`
|
|
267
|
+
: `lane '${synced.laneKey}' rebased onto current main (auto-continued ${synced.autoContinued} step(s))`,
|
|
268
|
+
], synced);
|
|
269
|
+
}
|
|
270
|
+
if (synced.code === "sync_conflicts")
|
|
271
|
+
return respond(formatWorklist(synced.worklist), synced);
|
|
272
|
+
return respond([`[${synced.code}] ${synced.message}${synced.paths ? `\n${synced.paths.join("\n")}` : ""}`], synced);
|
|
273
|
+
}
|
|
274
|
+
case "continue": {
|
|
275
|
+
const missing = needLane();
|
|
276
|
+
if (missing)
|
|
277
|
+
return respond([missing], { code: "lane_not_found", message: missing });
|
|
278
|
+
const continued = await continueSync(engineDeps, { laneKey: laneKey });
|
|
279
|
+
if (continued.code === "sync_clean") {
|
|
280
|
+
return respond([`lane '${continued.laneKey}' rebase completed -- lane is fresh relative to the synced main`], continued);
|
|
281
|
+
}
|
|
282
|
+
if (continued.code === "sync_conflicts")
|
|
283
|
+
return respond(formatWorklist(continued.worklist), continued);
|
|
284
|
+
return respond([
|
|
285
|
+
`[${continued.code}] ${continued.message}${continued.paths ? `\n${continued.paths.join("\n")}` : ""}`,
|
|
286
|
+
], continued);
|
|
287
|
+
}
|
|
288
|
+
case "abort_sync": {
|
|
289
|
+
const missing = needLane();
|
|
290
|
+
if (missing)
|
|
291
|
+
return respond([missing], { code: "lane_not_found", message: missing });
|
|
292
|
+
const aborted = await abortSync(engineDeps, { laneKey: laneKey });
|
|
293
|
+
if (aborted.code !== "ok")
|
|
294
|
+
return respond([`[${aborted.code}] ${aborted.message}`], aborted);
|
|
295
|
+
return respond([`lane '${aborted.laneKey}' rebase aborted -- back to its pre-sync tip (still stale)`], aborted);
|
|
296
|
+
}
|
|
297
|
+
case "land": {
|
|
298
|
+
const missing = needLane();
|
|
299
|
+
if (missing)
|
|
300
|
+
return respond([missing], { code: "lane_not_found", message: missing });
|
|
301
|
+
const landed = await landLane(engineDeps, {
|
|
302
|
+
laneKey: laneKey,
|
|
303
|
+
gate: settings.gate,
|
|
304
|
+
...(settings.gateCommand !== undefined ? { gateCommand: settings.gateCommand } : {}),
|
|
305
|
+
gateTimeoutMs: settings.gateTimeoutMs,
|
|
306
|
+
});
|
|
307
|
+
if (landed.code !== "ok") {
|
|
308
|
+
return respond([`[${landed.code}] ${landed.message}${landed.paths ? `\n${landed.paths.join("\n")}` : ""}`], landed);
|
|
309
|
+
}
|
|
310
|
+
return respond([
|
|
311
|
+
`lane '${landed.laneKey}' LANDED: epoch ${landed.epoch}, main is now ${landed.mainSha.slice(0, 12)} (gate: ${landed.gate}). All other lanes must sync before their next land.`,
|
|
312
|
+
], landed);
|
|
313
|
+
}
|
|
314
|
+
case "release_lane": {
|
|
315
|
+
const missing = needLane();
|
|
316
|
+
if (missing)
|
|
317
|
+
return respond([missing], { code: "lane_not_found", message: missing });
|
|
318
|
+
const released = await releaseLane(engineDeps, {
|
|
319
|
+
laneKey: laneKey,
|
|
320
|
+
...(input.confirm !== undefined ? { confirm: input.confirm } : {}),
|
|
321
|
+
});
|
|
322
|
+
if (released.code !== "released")
|
|
323
|
+
return respond([`[${released.code}] ${released.message}`], released);
|
|
324
|
+
return respond([`lane '${released.laneKey}' released`], released);
|
|
325
|
+
}
|
|
326
|
+
case "reconcile": {
|
|
327
|
+
const reconciled = await reconcile(engineDeps);
|
|
328
|
+
if (reconciled.code !== "reconciled")
|
|
329
|
+
return respond([`[${reconciled.code}] ${reconciled.message}`], reconciled);
|
|
330
|
+
return respond([
|
|
331
|
+
`reconciled: ${reconciled.orphanedLaneKeys.length} orphaned, ${reconciled.reRegisteredLaneKeys.length} re-registered, ${reconciled.ownerClearedLaneKeys.length} owners cleared${reconciled.staleLockReleased ? ", stale lock released" : ""}`,
|
|
332
|
+
], reconciled);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=worktree-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-sync.js","sourceRoot":"","sources":["../../../src/core/tools/worktree-sync.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative } from \"node:path\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport type { ResolvedWorktreeSyncSettings } from \"../settings-manager.ts\";\nimport type { ConflictWorklist, SyncStatus } from \"../worktree-sync/codes.ts\";\nimport {\n\tabortSync,\n\tbuildSyncStatus,\n\tcontinueSync,\n\tcreateLane,\n\tlandLane,\n\treconcile,\n\treleaseLane,\n\tresolveRepoContext,\n\tsyncLane,\n\ttype WorktreeSyncEngineDeps,\n} from \"../worktree-sync/git-engine.ts\";\nimport { resolveLaneMutationPath } from \"../worktree-sync/lane-gate.ts\";\nimport { readLane } from \"../worktree-sync/store.ts\";\n\n/**\n * `worktree_sync` -- the ENTIRE agent-facing surface of the worktree-per-lane workflow: a closed\n * action set over the engine (`core/worktree-sync/git-engine.ts`) so agents never improvise git\n * ceremony. Every outcome carries a tagged code in `details`; refusal text always names the exact\n * recovery step. See docs/worktree-sync.md for the workflow and the G1-G11 gate table.\n */\n\nfunction createWorktreeSyncSchema() {\n\treturn Type.Object(\n\t\t{\n\t\t\taction: Type.Union(\n\t\t\t\t[\n\t\t\t\t\tType.Literal(\"status\"),\n\t\t\t\t\tType.Literal(\"git_status\"),\n\t\t\t\t\tType.Literal(\"git_diff\"),\n\t\t\t\t\tType.Literal(\"git_add\"),\n\t\t\t\t\tType.Literal(\"git_commit\"),\n\t\t\t\t\tType.Literal(\"check\"),\n\t\t\t\t\tType.Literal(\"create_lane\"),\n\t\t\t\t\tType.Literal(\"sync\"),\n\t\t\t\t\tType.Literal(\"continue\"),\n\t\t\t\t\tType.Literal(\"abort_sync\"),\n\t\t\t\t\tType.Literal(\"land\"),\n\t\t\t\t\tType.Literal(\"release_lane\"),\n\t\t\t\t\tType.Literal(\"reconcile\"),\n\t\t\t\t],\n\t\t\t\t{\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"status: the deterministic full picture (epoch, hub, lock, per-lane freshness/staleness, advice). git_status/git_diff: read the bound lane with fixed argv. git_add: stage only explicitly validated paths inside the lane. git_commit: commit with a literal message. check: run only the owner-configured trusted check command. create_lane: new worktree+branch off main. sync: rebase current main into a lane (conflicts return a worklist and leave the rebase in progress). continue: verify resolved conflicts (zero markers, mechanically checked), stage, drive on. abort_sync: abort the in-progress rebase. land: the ONLY door to main -- serialized, freshness-checked, gate-command-verified, ff-only. release_lane: remove a fully-landed lane (discarding unlanded work needs confirm). reconcile: re-sync the registry with git reality.\",\n\t\t\t\t},\n\t\t\t),\n\t\t\tlaneKey: Type.Optional(\n\t\t\t\tType.String({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Target lane. Defaults to this session's bound lane (PI_WORKTREE_LANE) for sync/continue/abort_sync/land.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t\tgoalId: Type.Optional(\n\t\t\t\tType.String({ description: \"create_lane: goal to scope the auto-allocated lane key by.\" }),\n\t\t\t),\n\t\t\trequirementId: Type.Optional(Type.String({ description: \"create_lane: requirement this lane will work.\" })),\n\t\t\tpaths: Type.Optional(\n\t\t\t\tType.Array(Type.String(), {\n\t\t\t\t\tdescription: \"git_diff/git_add: explicit relative paths inside the bound lane.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t\tmessage: Type.Optional(Type.String({ description: \"git_commit: literal commit message.\" })),\n\t\t\tconfirm: Type.Optional(\n\t\t\t\tType.String({\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'release_lane: pass exactly \"yes-discard-lane\" to release a lane that still has unlanded commits or dirty files (G11: never silent).',\n\t\t\t\t}),\n\t\t\t),\n\t\t},\n\t\t{ additionalProperties: false },\n\t);\n}\n\nconst worktreeSyncSchema = createWorktreeSyncSchema();\n\nexport type WorktreeSyncToolInput = Static<typeof worktreeSyncSchema>;\n\nexport interface WorktreeSyncToolDeps {\n\tengineDeps: () => WorktreeSyncEngineDeps;\n\tsettings: () => ResolvedWorktreeSyncSettings;\n\tboundLaneKey: () => string | undefined;\n\t/** True iff this is a worker session (see session-role.ts) -- narrows the action surface below. */\n\tisWorker: () => boolean;\n}\n\n/** Actions a worker session may run unconditionally -- read-only status plus the sync/conflict\n * cycle on its own bound lane. Everything else is refused as `role_forbidden`, EXCEPT `land`,\n * which is instead gated by the `worktreeSync.workerLand` setting (still subject to normal\n * ownership/freshness gating downstream when allowed). */\nconst WORKER_ALLOWED_ACTIONS: ReadonlySet<WorktreeSyncToolInput[\"action\"]> = new Set([\n\t\"status\",\n\t\"git_status\",\n\t\"git_diff\",\n\t\"git_add\",\n\t\"git_commit\",\n\t\"check\",\n\t\"sync\",\n\t\"continue\",\n\t\"abort_sync\",\n]);\n\nfunction formatWorklist(worklist: ConflictWorklist): string[] {\n\tconst lines = [\n\t\t`Conflicts at rebase step ${worklist.step}${worklist.stoppedAtCommit ? ` (replaying ${worklist.stoppedAtCommit.sha.slice(0, 12)}: ${worklist.stoppedAtCommit.subject})` : \"\"}:`,\n\t];\n\tfor (const file of worklist.files) {\n\t\tlines.push(`- ${file.path} [${file.kind}]`);\n\t}\n\tlines.push(\n\t\t'Resolve the zdiff3 hunks in exactly these files (base section included), save, then call {\"action\":\"continue\"}. Staging and marker verification are automatic.',\n\t);\n\treturn lines;\n}\n\nfunction formatStatus(status: SyncStatus): string[] {\n\tconst lines = [\n\t\t`epoch ${status.epoch} | main ${status.mainBranch}@${status.mainSha.slice(0, 12)}` +\n\t\t\t`${status.hub ? ` | hub ${status.hub.clean ? \"clean\" : \"has local changes\"}` : \" | hub: main not checked out\"}` +\n\t\t\t`${status.lock.held ? ` | LOCK held by pid ${status.lock.holder?.pid ?? \"?\"}` : \"\"}`,\n\t];\n\tfor (const lane of status.lanes) {\n\t\tconst flags = [\n\t\t\tlane.registrationStatus !== \"active\" ? lane.registrationStatus : undefined,\n\t\t\tlane.rebaseInProgress ? \"REBASE-IN-PROGRESS\" : undefined,\n\t\t\tlane.syncRequired ? \"SYNC-REQUIRED\" : lane.stale ? \"stale\" : \"fresh\",\n\t\t\tlane.dirty ? \"dirty\" : undefined,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\", \");\n\t\tlines.push(\n\t\t\t`- ${lane.laneKey} (${lane.branch}) +${lane.aheadOfMain}/-${lane.behindMain} [${flags}]` +\n\t\t\t\t`${lane.overlapWithLastLand.length > 0 ? ` overlap: ${lane.overlapWithLastLand.slice(0, 5).join(\", \")}` : \"\"}`,\n\t\t);\n\t}\n\tif (status.lanes.length === 0) lines.push(\"(no lanes)\");\n\tif (status.advice) lines.push(`advice: ${status.advice}`);\n\treturn lines;\n}\n\nconst WORKTREE_SYNC_PROMPT_GUIDELINES = [\n\t\"Work ONLY inside your lane worktree on your lane branch; never edit the hub checkout or touch main directly -- main moves exclusively through the land action.\",\n\t\"Commit your work on the lane branch, then land. Landing is refused while the lane is stale (G3): call sync first, resolve conflicts locally, then land.\",\n\t\"When a sync stops on conflicts, edit exactly the listed files, then call continue -- staging, marker verification, and rebase continuation are mechanical.\",\n\t\"After any other lane lands, your lane becomes stale; under the mandatory policy your file mutations are refused until you sync. git add/commit stay available to save WIP first.\",\n\t\"Check status when unsure -- it is the deterministic full picture; never infer sync state from raw git output.\",\n];\n\nexport function createWorktreeSyncToolDefinition(deps: WorktreeSyncToolDeps): ToolDefinition {\n\treturn {\n\t\tname: \"worktree_sync\",\n\t\tlabel: \"worktree_sync\",\n\t\tdescription:\n\t\t\t\"Hard-gated worktree-per-lane parallel work: each agent works in its own git worktree/branch; integration is always rebase-onto-main + ff-only land, serialized under one integration lock with the gate command verified at the exact landing tip. status/create_lane/sync/continue/abort_sync/land/release_lane/reconcile. Every outcome is a tagged code with the exact recovery step; landing while stale is structurally impossible.\",\n\t\tpromptSnippet: \"Coordinate parallel work: lane worktrees, rebase-onto-main sync, serialized gated landing.\",\n\t\tpromptGuidelines: WORKTREE_SYNC_PROMPT_GUIDELINES,\n\t\tparameters: worktreeSyncSchema,\n\t\texecutionMode: \"sequential\",\n\t\tasync execute(_toolCallId, input: WorktreeSyncToolInput, signal) {\n\t\t\tconst engineDeps: WorktreeSyncEngineDeps = { ...deps.engineDeps(), ...(signal ? { signal } : {}) };\n\t\t\tconst settings = deps.settings();\n\t\t\tconst laneKey = input.laneKey ?? deps.boundLaneKey();\n\n\t\t\tconst respond = (lines: string[], details: unknown) => ({\n\t\t\t\tcontent: [{ type: \"text\" as const, text: lines.join(\"\\n\") }],\n\t\t\t\tdetails: details as Record<string, unknown>,\n\t\t\t});\n\t\t\tconst needLane = (): string | undefined => {\n\t\t\t\tif (laneKey) return undefined;\n\t\t\t\treturn \"laneKey required: pass laneKey, or run inside a lane-bound session (PI_WORKTREE_LANE).\";\n\t\t\t};\n\n\t\t\tconst runTypedLaneAction = async (): Promise<ReturnType<typeof respond>> => {\n\t\t\t\tconst missingLane = needLane();\n\t\t\t\tif (missingLane) return respond([missingLane], { code: \"lane_not_found\", message: missingLane });\n\t\t\t\tconst context = await resolveRepoContext(engineDeps);\n\t\t\t\tif (\"code\" in context) return respond([`[${context.code}] ${context.message}`], context);\n\t\t\t\tconst lane = await readLane(context.paths, laneKey!);\n\t\t\t\tif (!lane || lane.status !== \"active\") {\n\t\t\t\t\tconst message = `active lane '${laneKey}' is not available`;\n\t\t\t\t\treturn respond([`[lane_not_found] ${message}`], { code: \"lane_not_found\", message });\n\t\t\t\t}\n\n\t\t\t\tconst requestedPaths = input.paths ?? [];\n\t\t\t\tconst gitPaths: string[] = [];\n\t\t\t\tfor (const requestedPath of requestedPaths) {\n\t\t\t\t\tif (!requestedPath || isAbsolute(requestedPath) || requestedPath.includes(\"\\0\")) {\n\t\t\t\t\t\tconst message = \"git path must be a non-empty relative path without NUL\";\n\t\t\t\t\t\treturn respond([`[path_outside_lane] ${message}`], { code: \"path_outside_lane\", message });\n\t\t\t\t\t}\n\t\t\t\t\tconst resolved = resolveLaneMutationPath(lane.worktreePath, requestedPath);\n\t\t\t\t\tif (!resolved) {\n\t\t\t\t\t\tconst message = `path '${requestedPath}' escapes the lane worktree`;\n\t\t\t\t\t\treturn respond([`[path_outside_lane] ${message}`], { code: \"path_outside_lane\", message });\n\t\t\t\t\t}\n\t\t\t\t\tgitPaths.push(relative(lane.worktreePath, resolved) || \".\");\n\t\t\t\t}\n\n\t\t\t\tlet command: string;\n\t\t\t\tlet args: string[];\n\t\t\t\tswitch (input.action) {\n\t\t\t\t\tcase \"git_status\":\n\t\t\t\t\t\tcommand = \"git\";\n\t\t\t\t\t\targs = [\"status\", \"--short\", \"--branch\"];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"git_diff\":\n\t\t\t\t\t\tcommand = \"git\";\n\t\t\t\t\t\targs = [\"diff\", \"--\", ...gitPaths];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"git_add\":\n\t\t\t\t\t\tif (gitPaths.length === 0) {\n\t\t\t\t\t\t\tconst message = \"git_add requires one or more explicit in-lane paths\";\n\t\t\t\t\t\t\treturn respond([`[path_outside_lane] ${message}`], { code: \"path_outside_lane\", message });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcommand = \"git\";\n\t\t\t\t\t\targs = [\"add\", \"--\", ...gitPaths];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"git_commit\":\n\t\t\t\t\t\tif (!input.message?.trim() || input.message.includes(\"\\0\")) {\n\t\t\t\t\t\t\tconst message = \"git_commit requires a non-empty literal message\";\n\t\t\t\t\t\t\treturn respond([`[git_failed] ${message}`], { code: \"git_failed\", message });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcommand = \"git\";\n\t\t\t\t\t\targs = [\"commit\", \"-m\", input.message];\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"check\": {\n\t\t\t\t\t\tconst trustedCommand = settings.gateCommand?.trim();\n\t\t\t\t\t\tif (!trustedCommand) {\n\t\t\t\t\t\t\tconst message = \"no trusted worktree check is configured\";\n\t\t\t\t\t\t\treturn respond([`[gate_command_unset] ${message}`], { code: \"gate_command_unset\", message });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n\t\t\t\t\t\tconst shellFlag = process.platform === \"win32\" ? \"/c\" : \"-c\";\n\t\t\t\t\t\tconst result = await engineDeps.exec(shell, [shellFlag, trustedCommand], {\n\t\t\t\t\t\t\tcwd: lane.worktreePath,\n\t\t\t\t\t\t\ttimeout: 900_000,\n\t\t\t\t\t\t\tsignal: engineDeps.signal,\n\t\t\t\t\t\t\tmaxBuffer: 1024 * 1024,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst output = `${result.stdout}\\n${result.stderr}`.trim().slice(-8000);\n\t\t\t\t\t\tif (result.code !== 0) {\n\t\t\t\t\t\t\treturn respond([`[gate_failed] ${output || `trusted check exited ${result.code}`}`], {\n\t\t\t\t\t\t\t\tcode: \"gate_failed\",\n\t\t\t\t\t\t\t\texitCode: result.code,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn respond([output || \"trusted check passed\"], { code: \"ok\", action: input.action });\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn respond([\"typed lane action is unavailable for this request\"], { code: \"role_forbidden\" });\n\t\t\t\t}\n\n\t\t\t\tconst result = await engineDeps.exec(command, args, {\n\t\t\t\t\tcwd: lane.worktreePath,\n\t\t\t\t\ttimeout: 60_000,\n\t\t\t\t\tsignal: engineDeps.signal,\n\t\t\t\t\tmaxBuffer: 1024 * 1024,\n\t\t\t\t});\n\t\t\t\tconst output = `${result.stdout}\\n${result.stderr}`.trim().slice(-8000);\n\t\t\t\tif (result.code !== 0) {\n\t\t\t\t\treturn respond([`[git_failed] ${output || `${command} exited ${result.code}`}`], {\n\t\t\t\t\t\tcode: \"git_failed\",\n\t\t\t\t\t\taction: input.action,\n\t\t\t\t\t\texitCode: result.code,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn respond([output || `${input.action} completed`], { code: \"ok\", action: input.action });\n\t\t\t};\n\n\t\t\tif (deps.isWorker()) {\n\t\t\t\tconst bound = deps.boundLaneKey();\n\t\t\t\tif (input.laneKey !== undefined && input.laneKey !== bound) {\n\t\t\t\t\tconst message = \"workers may only target their bound lane\";\n\t\t\t\t\treturn respond([message], { code: \"role_forbidden\", message });\n\t\t\t\t}\n\t\t\t\tconst landAllowed = input.action === \"land\" && settings.workerLand === \"allow\";\n\t\t\t\tif (!WORKER_ALLOWED_ACTIONS.has(input.action) && !landAllowed) {\n\t\t\t\t\tconst message =\n\t\t\t\t\t\tinput.action === \"land\"\n\t\t\t\t\t\t\t? 'workers may not land (worktreeSync.workerLand is \"deny\")'\n\t\t\t\t\t\t\t: `workers may not run action \"${input.action}\"`;\n\t\t\t\t\treturn respond([message], { code: \"role_forbidden\", message });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (input.action) {\n\t\t\t\tcase \"git_status\":\n\t\t\t\tcase \"git_diff\":\n\t\t\t\tcase \"git_add\":\n\t\t\t\tcase \"git_commit\":\n\t\t\t\tcase \"check\":\n\t\t\t\t\treturn runTypedLaneAction();\n\t\t\t\tcase \"status\": {\n\t\t\t\t\tconst status = await buildSyncStatus(engineDeps, { policy: settings.syncPolicy });\n\t\t\t\t\tif (status.code !== \"ok\") return respond([`[${status.code}] ${status.message}`], status);\n\t\t\t\t\treturn respond(formatStatus(status), status);\n\t\t\t\t}\n\t\t\t\tcase \"create_lane\": {\n\t\t\t\t\tconst created = await createLane(engineDeps, {\n\t\t\t\t\t\t...(input.laneKey !== undefined ? { laneKey: input.laneKey } : {}),\n\t\t\t\t\t\t...(input.goalId !== undefined ? { goalId: input.goalId } : {}),\n\t\t\t\t\t\t...(input.requirementId !== undefined ? { requirementId: input.requirementId } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tif (created.code !== \"ok\") return respond([`[${created.code}] ${created.message}`], created);\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t`lane '${created.lane.laneKey}' created: branch ${created.lane.branch}, checkout ${created.lane.worktreePath}`,\n\t\t\t\t\t\t],\n\t\t\t\t\t\tcreated,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcase \"sync\": {\n\t\t\t\t\tconst missing = needLane();\n\t\t\t\t\tif (missing) return respond([missing], { code: \"lane_not_found\", message: missing });\n\t\t\t\t\tconst synced = await syncLane(engineDeps, { laneKey: laneKey as string });\n\t\t\t\t\tif (synced.code === \"sync_clean\") {\n\t\t\t\t\t\treturn respond(\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\tsynced.alreadyFresh\n\t\t\t\t\t\t\t\t\t? `lane '${synced.laneKey}' already contains current main -- nothing to do`\n\t\t\t\t\t\t\t\t\t: `lane '${synced.laneKey}' rebased onto current main (auto-continued ${synced.autoContinued} step(s))`,\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tsynced,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (synced.code === \"sync_conflicts\") return respond(formatWorklist(synced.worklist), synced);\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[`[${synced.code}] ${synced.message}${synced.paths ? `\\n${synced.paths.join(\"\\n\")}` : \"\"}`],\n\t\t\t\t\t\tsynced,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcase \"continue\": {\n\t\t\t\t\tconst missing = needLane();\n\t\t\t\t\tif (missing) return respond([missing], { code: \"lane_not_found\", message: missing });\n\t\t\t\t\tconst continued = await continueSync(engineDeps, { laneKey: laneKey as string });\n\t\t\t\t\tif (continued.code === \"sync_clean\") {\n\t\t\t\t\t\treturn respond(\n\t\t\t\t\t\t\t[`lane '${continued.laneKey}' rebase completed -- lane is fresh relative to the synced main`],\n\t\t\t\t\t\t\tcontinued,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (continued.code === \"sync_conflicts\") return respond(formatWorklist(continued.worklist), continued);\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t`[${continued.code}] ${continued.message}${continued.paths ? `\\n${continued.paths.join(\"\\n\")}` : \"\"}`,\n\t\t\t\t\t\t],\n\t\t\t\t\t\tcontinued,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcase \"abort_sync\": {\n\t\t\t\t\tconst missing = needLane();\n\t\t\t\t\tif (missing) return respond([missing], { code: \"lane_not_found\", message: missing });\n\t\t\t\t\tconst aborted = await abortSync(engineDeps, { laneKey: laneKey as string });\n\t\t\t\t\tif (aborted.code !== \"ok\") return respond([`[${aborted.code}] ${aborted.message}`], aborted);\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[`lane '${aborted.laneKey}' rebase aborted -- back to its pre-sync tip (still stale)`],\n\t\t\t\t\t\taborted,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcase \"land\": {\n\t\t\t\t\tconst missing = needLane();\n\t\t\t\t\tif (missing) return respond([missing], { code: \"lane_not_found\", message: missing });\n\t\t\t\t\tconst landed = await landLane(engineDeps, {\n\t\t\t\t\t\tlaneKey: laneKey as string,\n\t\t\t\t\t\tgate: settings.gate,\n\t\t\t\t\t\t...(settings.gateCommand !== undefined ? { gateCommand: settings.gateCommand } : {}),\n\t\t\t\t\t\tgateTimeoutMs: settings.gateTimeoutMs,\n\t\t\t\t\t});\n\t\t\t\t\tif (landed.code !== \"ok\") {\n\t\t\t\t\t\treturn respond(\n\t\t\t\t\t\t\t[`[${landed.code}] ${landed.message}${landed.paths ? `\\n${landed.paths.join(\"\\n\")}` : \"\"}`],\n\t\t\t\t\t\t\tlanded,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t`lane '${landed.laneKey}' LANDED: epoch ${landed.epoch}, main is now ${landed.mainSha.slice(0, 12)} (gate: ${landed.gate}). All other lanes must sync before their next land.`,\n\t\t\t\t\t\t],\n\t\t\t\t\t\tlanded,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcase \"release_lane\": {\n\t\t\t\t\tconst missing = needLane();\n\t\t\t\t\tif (missing) return respond([missing], { code: \"lane_not_found\", message: missing });\n\t\t\t\t\tconst released = await releaseLane(engineDeps, {\n\t\t\t\t\t\tlaneKey: laneKey as string,\n\t\t\t\t\t\t...(input.confirm !== undefined ? { confirm: input.confirm } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tif (released.code !== \"released\") return respond([`[${released.code}] ${released.message}`], released);\n\t\t\t\t\treturn respond([`lane '${released.laneKey}' released`], released);\n\t\t\t\t}\n\t\t\t\tcase \"reconcile\": {\n\t\t\t\t\tconst reconciled = await reconcile(engineDeps);\n\t\t\t\t\tif (reconciled.code !== \"reconciled\")\n\t\t\t\t\t\treturn respond([`[${reconciled.code}] ${reconciled.message}`], reconciled);\n\t\t\t\t\treturn respond(\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t`reconciled: ${reconciled.orphanedLaneKeys.length} orphaned, ${reconciled.reRegisteredLaneKeys.length} re-registered, ${reconciled.ownerClearedLaneKeys.length} owners cleared${reconciled.staleLockReleased ? \", stale lock released\" : \"\"}`,\n\t\t\t\t\t\t],\n\t\t\t\t\t\treconciled,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
|
|
@@ -2,7 +2,10 @@ export type ProjectTrustDecision = boolean | null;
|
|
|
2
2
|
export declare function hasProjectTrustInputs(cwd: string): boolean;
|
|
3
3
|
export declare class ProjectTrustStore {
|
|
4
4
|
private trustPath;
|
|
5
|
-
|
|
5
|
+
private readonly readOnly;
|
|
6
|
+
constructor(agentDir: string, options?: {
|
|
7
|
+
readOnly?: boolean;
|
|
8
|
+
});
|
|
6
9
|
get(cwd: string): ProjectTrustDecision;
|
|
7
10
|
set(cwd: string, decision: ProjectTrustDecision): void;
|
|
8
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trust-manager.d.ts","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,IAAI,CAAC;AAyFlD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA2B1D;AAED,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IAEnC,YAAY,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,EAI7D;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAiBrC;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAarD;CACD","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { stateFile } from \"./agent-paths.ts\";\nimport { isWorkerSession } from \"./session-role.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst CONTEXT_FILE_NAMES = [\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\", \"GEMINI.md\", \"GEMINI.MD\"];\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction readTrustFile(path: string): TrustFile | undefined {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\nexport function hasProjectTrustInputs(cwd: string): boolean {\n\tlet currentDir = resolvePath(cwd);\n\tif (existsSync(join(currentDir, CONFIG_DIR_NAME))) {\n\t\treturn true;\n\t}\n\n\tconst root = resolve(\"/\");\n\twhile (true) {\n\t\tfor (const filename of CONTEXT_FILE_NAMES) {\n\t\t\tif (existsSync(join(currentDir, filename))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (existsSync(join(currentDir, \".agents\", \"skills\"))) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (currentDir === root) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\tprivate readonly readOnly: boolean;\n\n\tconstructor(agentDir: string, options?: { readOnly?: boolean }) {\n\t\t// Machine-persisted trust decisions -- state/, not the agentDir root.\n\t\tthis.trustPath = stateFile(resolvePath(agentDir), \"trust.json\");\n\t\tthis.readOnly = options?.readOnly ?? isWorkerSession();\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\tif (this.readOnly) {\n\t\t\t// Zero-footprint (worker session): a lock-free read -- no lockfile, no state dir ever\n\t\t\t// created just from reading trust. A torn read (concurrent writer mid-rewrite) reads as\n\t\t\t// invalid JSON, which `readTrustFile` reports as `undefined`, and fails safe to untrusted\n\t\t\t// (null) below, never a crash.\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return null;\n\t\t\tconst value = data[normalizeCwd(cwd)];\n\t\t\treturn value === true || value === false ? value : null;\n\t\t}\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return null;\n\t\t\tconst value = data[normalizeCwd(cwd)];\n\t\t\treturn value === true || value === false ? value : null;\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tif (this.readOnly) return;\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return;\n\t\t\tconst key = normalizeCwd(cwd);\n\t\t\tif (decision === null) {\n\t\t\t\tdelete data[key];\n\t\t\t} else {\n\t\t\t\tdata[key] = decision;\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
|
|
@@ -3,6 +3,8 @@ import { dirname, join, resolve } from "node:path";
|
|
|
3
3
|
import lockfile from "proper-lockfile";
|
|
4
4
|
import { CONFIG_DIR_NAME } from "../config.js";
|
|
5
5
|
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
6
|
+
import { stateFile } from "./agent-paths.js";
|
|
7
|
+
import { isWorkerSession } from "./session-role.js";
|
|
6
8
|
const CONTEXT_FILE_NAMES = ["AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD", "GEMINI.md", "GEMINI.MD"];
|
|
7
9
|
function normalizeCwd(cwd) {
|
|
8
10
|
return canonicalizePath(resolvePath(cwd));
|
|
@@ -106,10 +108,24 @@ export function hasProjectTrustInputs(cwd) {
|
|
|
106
108
|
}
|
|
107
109
|
export class ProjectTrustStore {
|
|
108
110
|
trustPath;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
readOnly;
|
|
112
|
+
constructor(agentDir, options) {
|
|
113
|
+
// Machine-persisted trust decisions -- state/, not the agentDir root.
|
|
114
|
+
this.trustPath = stateFile(resolvePath(agentDir), "trust.json");
|
|
115
|
+
this.readOnly = options?.readOnly ?? isWorkerSession();
|
|
111
116
|
}
|
|
112
117
|
get(cwd) {
|
|
118
|
+
if (this.readOnly) {
|
|
119
|
+
// Zero-footprint (worker session): a lock-free read -- no lockfile, no state dir ever
|
|
120
|
+
// created just from reading trust. A torn read (concurrent writer mid-rewrite) reads as
|
|
121
|
+
// invalid JSON, which `readTrustFile` reports as `undefined`, and fails safe to untrusted
|
|
122
|
+
// (null) below, never a crash.
|
|
123
|
+
const data = readTrustFile(this.trustPath);
|
|
124
|
+
if (!data)
|
|
125
|
+
return null;
|
|
126
|
+
const value = data[normalizeCwd(cwd)];
|
|
127
|
+
return value === true || value === false ? value : null;
|
|
128
|
+
}
|
|
113
129
|
return withTrustFileLock(this.trustPath, () => {
|
|
114
130
|
const data = readTrustFile(this.trustPath);
|
|
115
131
|
if (!data)
|
|
@@ -119,6 +135,8 @@ export class ProjectTrustStore {
|
|
|
119
135
|
});
|
|
120
136
|
}
|
|
121
137
|
set(cwd, decision) {
|
|
138
|
+
if (this.readOnly)
|
|
139
|
+
return;
|
|
122
140
|
withTrustFileLock(this.trustPath, () => {
|
|
123
141
|
const data = readTrustFile(this.trustPath);
|
|
124
142
|
if (!data)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trust-manager.js","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst CONTEXT_FILE_NAMES = [\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\", \"GEMINI.md\", \"GEMINI.MD\"];\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction readTrustFile(path: string): TrustFile | undefined {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\nexport function hasProjectTrustInputs(cwd: string): boolean {\n\tlet currentDir = resolvePath(cwd);\n\tif (existsSync(join(currentDir, CONFIG_DIR_NAME))) {\n\t\treturn true;\n\t}\n\n\tconst root = resolve(\"/\");\n\twhile (true) {\n\t\tfor (const filename of CONTEXT_FILE_NAMES) {\n\t\t\tif (existsSync(join(currentDir, filename))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (existsSync(join(currentDir, \".agents\", \"skills\"))) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (currentDir === root) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\n\tconstructor(agentDir: string) {\n\t\tthis.trustPath = join(resolvePath(agentDir), \"trust.json\");\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return null;\n\t\t\tconst value = data[normalizeCwd(cwd)];\n\t\t\treturn value === true || value === false ? value : null;\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return;\n\t\t\tconst key = normalizeCwd(cwd);\n\t\t\tif (decision === null) {\n\t\t\t\tdelete data[key];\n\t\t\t} else {\n\t\t\t\tdata[key] = decision;\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"trust-manager.js","sourceRoot":"","sources":["../../src/core/trust-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport { CONFIG_DIR_NAME } from \"../config.ts\";\nimport { canonicalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { stateFile } from \"./agent-paths.ts\";\nimport { isWorkerSession } from \"./session-role.ts\";\n\nexport type ProjectTrustDecision = boolean | null;\n\ntype TrustFile = Record<string, boolean | null | undefined>;\n\nconst CONTEXT_FILE_NAMES = [\"AGENTS.md\", \"AGENTS.MD\", \"CLAUDE.md\", \"CLAUDE.MD\", \"GEMINI.md\", \"GEMINI.MD\"];\n\nfunction normalizeCwd(cwd: string): string {\n\treturn canonicalizePath(resolvePath(cwd));\n}\n\nfunction readTrustFile(path: string): TrustFile | undefined {\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, \"utf-8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid trust store ${path}: expected an object`);\n\t}\n\n\tconst data: TrustFile = {};\n\tfor (const [key, value] of Object.entries(parsed)) {\n\t\tif (value !== true && value !== false && value !== null) {\n\t\t\tthrow new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);\n\t\t}\n\t\tdata[key] = value;\n\t}\n\treturn data;\n}\n\nfunction writeTrustFile(path: string, data: TrustFile): void {\n\tconst sorted: TrustFile = {};\n\tfor (const key of Object.keys(data).sort()) {\n\t\tconst value = data[key];\n\t\tif (value === true || value === false || value === null) {\n\t\t\tsorted[key] = value;\n\t\t}\n\t}\n\tmkdirSync(dirname(path), { recursive: true });\n\twriteFileSync(path, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction acquireTrustLockSync(path: string): () => void {\n\tconst trustDir = dirname(path);\n\tmkdirSync(trustDir, { recursive: true });\n\tconst maxAttempts = 10;\n\tconst delayMs = 20;\n\tlet lastError: unknown;\n\n\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\ttry {\n\t\t\treturn lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t: undefined;\n\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlastError = error;\n\t\t\tconst start = Date.now();\n\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t// Sleep synchronously to avoid changing trust store callers to async.\n\t\t\t}\n\t\t}\n\t}\n\n\tif (lastError instanceof Error) {\n\t\tthrow lastError;\n\t}\n\tthrow new Error(\"Failed to acquire trust store lock\");\n}\n\nfunction withTrustFileLock<T>(path: string, fn: () => T): T {\n\tconst release = acquireTrustLockSync(path);\n\ttry {\n\t\treturn fn();\n\t} finally {\n\t\trelease();\n\t}\n}\n\nexport function hasProjectTrustInputs(cwd: string): boolean {\n\tlet currentDir = resolvePath(cwd);\n\tif (existsSync(join(currentDir, CONFIG_DIR_NAME))) {\n\t\treturn true;\n\t}\n\n\tconst root = resolve(\"/\");\n\twhile (true) {\n\t\tfor (const filename of CONTEXT_FILE_NAMES) {\n\t\t\tif (existsSync(join(currentDir, filename))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\tif (existsSync(join(currentDir, \".agents\", \"skills\"))) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (currentDir === root) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) {\n\t\t\treturn false;\n\t\t}\n\t\tcurrentDir = parentDir;\n\t}\n}\n\nexport class ProjectTrustStore {\n\tprivate trustPath: string;\n\tprivate readonly readOnly: boolean;\n\n\tconstructor(agentDir: string, options?: { readOnly?: boolean }) {\n\t\t// Machine-persisted trust decisions -- state/, not the agentDir root.\n\t\tthis.trustPath = stateFile(resolvePath(agentDir), \"trust.json\");\n\t\tthis.readOnly = options?.readOnly ?? isWorkerSession();\n\t}\n\n\tget(cwd: string): ProjectTrustDecision {\n\t\tif (this.readOnly) {\n\t\t\t// Zero-footprint (worker session): a lock-free read -- no lockfile, no state dir ever\n\t\t\t// created just from reading trust. A torn read (concurrent writer mid-rewrite) reads as\n\t\t\t// invalid JSON, which `readTrustFile` reports as `undefined`, and fails safe to untrusted\n\t\t\t// (null) below, never a crash.\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return null;\n\t\t\tconst value = data[normalizeCwd(cwd)];\n\t\t\treturn value === true || value === false ? value : null;\n\t\t}\n\t\treturn withTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return null;\n\t\t\tconst value = data[normalizeCwd(cwd)];\n\t\t\treturn value === true || value === false ? value : null;\n\t\t});\n\t}\n\n\tset(cwd: string, decision: ProjectTrustDecision): void {\n\t\tif (this.readOnly) return;\n\t\twithTrustFileLock(this.trustPath, () => {\n\t\t\tconst data = readTrustFile(this.trustPath);\n\t\t\tif (!data) return;\n\t\t\tconst key = normalizeCwd(cwd);\n\t\t\tif (decision === null) {\n\t\t\t\tdelete data[key];\n\t\t\t} else {\n\t\t\t\tdata[key] = decision;\n\t\t\t}\n\t\t\twriteTrustFile(this.trustPath, data);\n\t\t});\n\t}\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared atomic-file helper: a single lock+tmp+rename implementation reused by every
|
|
3
|
+
* on-disk store that does a read-modify-write against a JSON/text file. Modeled on the pattern
|
|
4
|
+
* already proven in `memory/providers/file-store.ts` (proper-lockfile advisory lock + write-tmp-
|
|
5
|
+
* then-rename); before this helper existed the pattern was copy-pasted per store, and most copies
|
|
6
|
+
* were missing either the lock, the atomic write, or both.
|
|
7
|
+
*
|
|
8
|
+
* Two call shapes:
|
|
9
|
+
* - `withFileLock(Sync)` — hold an exclusive advisory lock across an arbitrary read-modify-write
|
|
10
|
+
* callback. The lock spans BOTH the read and the write, closing the classic RMW race where two
|
|
11
|
+
* writers each read the old content before either writes back.
|
|
12
|
+
* - `writeFileAtomic(Sync)` — write-tmp-then-rename. Used INSIDE a `withFileLock` callback (or
|
|
13
|
+
* standalone, for a pure overwrite that has no read step to race).
|
|
14
|
+
*
|
|
15
|
+
* Sync and async variants are both exported. Most existing stores expose a synchronous public API
|
|
16
|
+
* called from hot, non-async paths (e.g. a per-token-stream perf sample), so the sync variant lets
|
|
17
|
+
* them gain locking without forcing an async ripple through their callers; the async variant is for
|
|
18
|
+
* call sites that are already async.
|
|
19
|
+
*/
|
|
20
|
+
export interface AtomicFileLockOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Bounded retry attempts while waiting for a lock already held elsewhere. Both variants use a
|
|
23
|
+
* SHORT, capped backoff (see {@link RETRY_MIN_TIMEOUT_MS}/{@link RETRY_MAX_TIMEOUT_MS}) — these
|
|
24
|
+
* stores' critical sections are sub-millisecond reads+writes of small JSON/text files, so
|
|
25
|
+
* contention should clear in milliseconds, not the multi-second-to-31-second worst case
|
|
26
|
+
* proper-lockfile's OWN default backoff produces for a bare numeric `retries` (its default
|
|
27
|
+
* `minTimeout` is 1000ms with factor 2 — see node_modules/retry/lib/retry.js). Passing a bare
|
|
28
|
+
* number straight through would turn brief contention into a multi-second stall on a hot path
|
|
29
|
+
* (e.g. a per-token-stream perf sample), so both variants instead build an explicit short-backoff
|
|
30
|
+
* `retry` options object.
|
|
31
|
+
* - Async (`withFileLock`): forwarded as `{retries, minTimeout, maxTimeout}` to proper-lockfile.
|
|
32
|
+
* - Sync (`withFileLockSync`): proper-lockfile's sync API REJECTS `retries > 0` outright (it
|
|
33
|
+
* requires the whole acquire flow to be synchronous — see proper-lockfile/lib/adapter.js
|
|
34
|
+
* `toSyncOptions`, which throws `ESYNC`). So the sync path implements its own bounded retry
|
|
35
|
+
* around single `lockfile.lockSync` attempts, blocking briefly between them (Atomics.wait) —
|
|
36
|
+
* callers are already fully synchronous fs code, so a short blocking wait on contention matches
|
|
37
|
+
* the existing execution model rather than introducing a new one.
|
|
38
|
+
*/
|
|
39
|
+
retries?: number;
|
|
40
|
+
/** Resolve symlinks before locking (proper-lockfile `realpath`); false matches file-store.ts. */
|
|
41
|
+
realpath?: boolean;
|
|
42
|
+
/** Lock staleness window in ms (proper-lockfile `stale`); omitted = proper-lockfile's own default. */
|
|
43
|
+
stale?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Hold an exclusive advisory lock on `filePath` for the duration of `fn` (sync). Always releases,
|
|
47
|
+
* including when `fn` throws.
|
|
48
|
+
*/
|
|
49
|
+
export declare function withFileLockSync<T>(filePath: string, fn: () => T, options?: AtomicFileLockOptions): T;
|
|
50
|
+
/** Async counterpart of {@link withFileLockSync}. Always releases, including when `fn` throws/rejects. */
|
|
51
|
+
export declare function withFileLock<T>(filePath: string, fn: () => Promise<T> | T, options?: AtomicFileLockOptions): Promise<T>;
|
|
52
|
+
export declare function writeFileAtomicSync(filePath: string, content: string): void;
|
|
53
|
+
/** Async counterpart of {@link writeFileAtomicSync}. */
|
|
54
|
+
export declare function writeFileAtomic(filePath: string, content: string): Promise<void>;
|
|
55
|
+
//# sourceMappingURL=atomic-file.d.ts.map
|