@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
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* delegations; goal auto-continue itself only ever asks the session to `continueGoalLoop`, so this
|
|
15
15
|
* controller never touches `prompt()`, the last-assistant-message, retry, or streaming state.
|
|
16
16
|
*/
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
17
18
|
import path from "node:path";
|
|
19
|
+
import { configFile, getWorkRoot, sessionsDir, stateDir } from "./agent-paths.js";
|
|
18
20
|
import { createLaneToolSurface } from "./autonomy/lane-tool-surface.js";
|
|
19
21
|
import { LaneTracker } from "./autonomy/lane-tracker.js";
|
|
20
22
|
import { safeRealpathSync } from "./autonomy/path-scope.js";
|
|
@@ -22,30 +24,89 @@ import { appendLaneRecordSnapshot, getLaneRecordSnapshots } from "./autonomy/ses
|
|
|
22
24
|
import { composeSubagentSystemPrompt } from "./autonomy/subagent-prompt.js";
|
|
23
25
|
import { AUTONOMY_TELEMETRY_EVENT_TYPES } from "./autonomy/telemetry-events.js";
|
|
24
26
|
import { applyWorkerActions } from "./delegation/worker-actions.js";
|
|
27
|
+
import { reviewManagedLaneChangedFiles } from "./delegation/worker-result.js";
|
|
25
28
|
import { runWorker } from "./delegation/worker-runner.js";
|
|
26
29
|
import { deriveModelCapabilityProfile, scaleContinuationBudgetsForCapability, } from "./model-capability.js";
|
|
27
30
|
import { resolveCliModel } from "./model-resolver.js";
|
|
28
31
|
import { FitnessStore } from "./models/fitness-store.js";
|
|
32
|
+
import { registerInFlightWork } from "./reload-blockers.js";
|
|
29
33
|
import { runModelFitnessProbe } from "./research/model-fitness.js";
|
|
30
34
|
import { runResearch } from "./research/research-runner.js";
|
|
31
35
|
const WORKER_HANDOFF_TIMEOUT_MS = 30_000;
|
|
32
36
|
export function clampLaneMaxUsd(settingsMaxUsd, foregroundMaxEstimatedUsd) {
|
|
33
37
|
return Math.min(settingsMaxUsd, foregroundMaxEstimatedUsd ?? Number.POSITIVE_INFINITY);
|
|
34
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Deterministic `addSpawnedUsage` reportId: `kind` + session id + a content hash of `identity`
|
|
41
|
+
* (never `Date.now`/random). Same identity on a retry of the same logical work unit yields the same
|
|
42
|
+
* id, so the ledger's `seenSubagentReportIds` dedupe catches a duplicate report instead of
|
|
43
|
+
* double-counting spend.
|
|
44
|
+
*/
|
|
45
|
+
function deriveSpawnedUsageReportId(kind, sessionId, identity) {
|
|
46
|
+
const digest = createHash("sha256").update(identity).digest("hex").slice(0, 16);
|
|
47
|
+
return `${kind}:${sessionId}:${digest}`;
|
|
48
|
+
}
|
|
35
49
|
export function getPrivateLaneDeniedPaths(cwd, agentDir) {
|
|
36
50
|
return [
|
|
37
|
-
"auth.json",
|
|
38
|
-
"MEMORY.md",
|
|
39
|
-
"USER.md",
|
|
40
|
-
"settings.json",
|
|
41
|
-
"models.json",
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
configFile(agentDir, "auth.json"),
|
|
52
|
+
configFile(agentDir, "MEMORY.md"),
|
|
53
|
+
configFile(agentDir, "USER.md"),
|
|
54
|
+
configFile(agentDir, "settings.json"),
|
|
55
|
+
configFile(agentDir, "models.json"),
|
|
56
|
+
// trust.json now lives under state/ -- covered by the whole-state-dir
|
|
57
|
+
// denial below instead of its own root-level entry.
|
|
58
|
+
sessionsDir(agentDir),
|
|
59
|
+
stateDir(agentDir),
|
|
60
|
+
getWorkRoot(agentDir),
|
|
61
|
+
].concat(path.join(cwd, ".pi", "settings.json"));
|
|
62
|
+
}
|
|
63
|
+
const KNOWN_LANE_TERMINAL_STATUSES = new Set([
|
|
64
|
+
"succeeded",
|
|
65
|
+
"failed",
|
|
66
|
+
"canceled",
|
|
67
|
+
"timeout",
|
|
68
|
+
"budget_exhausted",
|
|
69
|
+
]);
|
|
70
|
+
/**
|
|
71
|
+
* Resolves a managed lane's caller-reported terminal `status` (a free-form CLAIM — e.g. a tmux job's
|
|
72
|
+
* own "done"/"blocked" completion marker, a lifecycle tag like "dismissed", or a raw
|
|
73
|
+
* {@link WorkerResultStatus} spelling) onto the LaneTracker's {@link LaneTerminalStatus} vocabulary.
|
|
74
|
+
* Mirrors the same success/blocked mapping direction `worker-runner.ts` already uses for in-process
|
|
75
|
+
* workers ("completed"/"done" -> succeeded, "blocked" -> failed). An unrecognized or missing status is
|
|
76
|
+
* conservatively reported as `"failed"` rather than silently assumed successful (claims-to-review).
|
|
77
|
+
*/
|
|
78
|
+
export function resolveManagedLaneTerminalStatus(status) {
|
|
79
|
+
if (status !== undefined && KNOWN_LANE_TERMINAL_STATUSES.has(status)) {
|
|
80
|
+
return status;
|
|
81
|
+
}
|
|
82
|
+
switch (status) {
|
|
83
|
+
case "done":
|
|
84
|
+
case "completed":
|
|
85
|
+
return "succeeded";
|
|
86
|
+
case "blocked":
|
|
87
|
+
return "failed";
|
|
88
|
+
case "dismissed":
|
|
89
|
+
case "cancelled":
|
|
90
|
+
return "canceled";
|
|
91
|
+
default:
|
|
92
|
+
return "failed";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Maps a LaneTracker terminal status onto the WorkerResult status vocabulary a managed-lane claim
|
|
96
|
+
* snapshot is persisted under — the two enums use different spellings/values, never interchangeable
|
|
97
|
+
* (e.g. `"canceled"` vs `"cancelled"`); `timeout`/`budget_exhausted` have no dedicated WorkerResult
|
|
98
|
+
* counterpart and are conservatively reported as `"failed"`. */
|
|
99
|
+
export function mapManagedLaneTerminalStatus(status) {
|
|
100
|
+
switch (status) {
|
|
101
|
+
case "succeeded":
|
|
102
|
+
return "completed";
|
|
103
|
+
case "canceled":
|
|
104
|
+
return "cancelled";
|
|
105
|
+
case "failed":
|
|
106
|
+
case "timeout":
|
|
107
|
+
case "budget_exhausted":
|
|
108
|
+
return "failed";
|
|
109
|
+
}
|
|
49
110
|
}
|
|
50
111
|
export function isLocalExecutionModel(model) {
|
|
51
112
|
if (model.provider === "ollama" || model.provider === "transformers" || model.provider === "llama-cpp") {
|
|
@@ -89,7 +150,35 @@ export class BackgroundLaneController {
|
|
|
89
150
|
_workersCompletedSinceFlush = 0;
|
|
90
151
|
_workersFailedSinceFlush = 0;
|
|
91
152
|
_workerTerminalSinceFlush = [];
|
|
153
|
+
/** Live per-lane mutation ledger for a RUNNING worker — the SAME `toolChangedFiles` Set its
|
|
154
|
+
* `afterToolCall` hook mutates, plus a spend getter and the originating request. Read
|
|
155
|
+
* synchronously by `abortInFlightLanes()`'s disposal cutoff, the only provably-safe write window
|
|
156
|
+
* for a mid-flight cancellation (see that method); deleted there (consumed-ledger guard against
|
|
157
|
+
* the post-await disposed branch re-persisting) or in the lane's own `finally` on a normal exit. */
|
|
158
|
+
_inFlightWorkerLedgers = new Map();
|
|
159
|
+
/** Reload-gate deregister function for a worker queued (not yet running) behind a
|
|
160
|
+
* contending local-execution foreground model. Registered at enqueue so `/reload` waits for
|
|
161
|
+
* queued work too, not just running work; deregistered exactly once, either on disposal
|
|
162
|
+
* (`abortInFlightLanes`) or at the running handoff (`drainQueuedWorkerDelegations`). A laneId with
|
|
163
|
+
* no entry here (e.g. seeded directly into `_queuedWorkers` by a test) is simply untracked —
|
|
164
|
+
* deregistration is best-effort by design, never required. */
|
|
165
|
+
_queuedWorkerDeregisters = new Map();
|
|
166
|
+
/** Dispatch -> terminal correlation for out-of-process managed lanes (`pi.reportManagedLane`
|
|
167
|
+
* host bridge), keyed by the CALLER's own `laneId` (e.g. a tmux job id) — distinct from the
|
|
168
|
+
* internal `LaneTracker` id it maps to. Removed on the matching terminal report so a duplicate or
|
|
169
|
+
* unmatched terminal call is a safe no-op instead of a double-deregister or an orphaned entry. */
|
|
170
|
+
_managedLaneDispatches = new Map();
|
|
92
171
|
deps;
|
|
172
|
+
/** Emit a warning without ever throwing — used from disposal-adjacent persistence where a
|
|
173
|
+
* listener failure (or a bare test double missing `emit`) must never block or crash cleanup. */
|
|
174
|
+
_safeWarn(message) {
|
|
175
|
+
try {
|
|
176
|
+
this.deps.emit({ type: "warning", message });
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Dispose must never throw.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
93
182
|
_scheduleWorkerNotification() {
|
|
94
183
|
if (this._disposed || this._workerNotificationScheduled)
|
|
95
184
|
return;
|
|
@@ -174,28 +263,181 @@ export class BackgroundLaneController {
|
|
|
174
263
|
getLaneRecords() {
|
|
175
264
|
return this._laneTracker.getRecords();
|
|
176
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Resolve a tracked managed-lane dispatch's internal `LaneTracker` id from the CALLER's own
|
|
268
|
+
* `laneId` (the id passed to `recordManagedLane`'s `phase: "dispatch"`, e.g. a reconstructed
|
|
269
|
+
* `tmux:jobId:agentId`). A deterministic, non-racy keyed lookup against `_managedLaneDispatches` —
|
|
270
|
+
* NOT a `getLaneRecords()` diff — for a caller (e.g. the goal-to-tmux dispatch adapter) that just
|
|
271
|
+
* minted the dispatch and needs the internal id `Requirement.boundLaneId`/`inFlightGoalLaneIds`
|
|
272
|
+
* actually match. `undefined` once the dispatch has gone terminal (removed from the map) or was
|
|
273
|
+
* never tracked.
|
|
274
|
+
*/
|
|
275
|
+
resolveManagedLaneId(callerLaneId) {
|
|
276
|
+
return this._managedLaneDispatches.get(callerLaneId)?.laneId;
|
|
277
|
+
}
|
|
177
278
|
/** Live count of active lanes — the real source for AutonomyStatusSnapshot.activeLaneCount. */
|
|
178
279
|
getActiveLaneCount() {
|
|
179
280
|
return this._laneTracker.getActiveCount();
|
|
180
281
|
}
|
|
282
|
+
/** Belt-and-braces guard: whether ANY queued/running lane is tagged with this goalId. */
|
|
283
|
+
_hasInFlightLaneForGoal(goalId) {
|
|
284
|
+
return this.getLaneRecords().some((record) => record.goalId === goalId && (record.status === "queued" || record.status === "running"));
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Host-side bridge for `pi.reportManagedLane`: makes an out-of-process managed lane (e.g. a tmux
|
|
288
|
+
* worker) a first-class lane in THIS process's LaneTracker. HONEST cross-process seam — the
|
|
289
|
+
* extension only ever REPORTS a claim; this controller stays the lane-tracking SSOT (no in-process
|
|
290
|
+
* sandboxing is implied by accepting the report).
|
|
291
|
+
*
|
|
292
|
+
* `phase: "dispatch"` mints a `tmux-worker` lane record (goalId-tagged) and registers exactly one
|
|
293
|
+
* reload-quiesce unit for it. `phase: "terminal"` resolves the caller's free-form `status` claim
|
|
294
|
+
* onto {@link LaneTerminalStatus} (see {@link resolveManagedLaneTerminalStatus}), completes that
|
|
295
|
+
* same record, deregisters the quiesce unit (inside a `finally` — never left stuck regardless of
|
|
296
|
+
* what persistence below does), and persists a bounded worker-result CLAIM snapshot from the
|
|
297
|
+
* reported `changedFiles`. Host re-review: the reported `changedFiles` are re-checked against
|
|
298
|
+
* the session's active capability envelope ({@link reviewManagedLaneChangedFiles}, reusing
|
|
299
|
+
* `validateWorkerResult`'s symlink-safe scope check verbatim) and `parentReviewRequired` is
|
|
300
|
+
* stamped on the persisted claim whenever that check does not cleanly "allow" -- an out-of-scope
|
|
301
|
+
* (or no-scope-configured) path is flagged exactly like an in-scope one, since a tmux worker's
|
|
302
|
+
* write never passed through this process's enforcement in the first place. This is the SESSION
|
|
303
|
+
* envelope, not a per-launch tmux standing grant (that is a narrower, launch-specific scope;
|
|
304
|
+
* documented follow-up, not yet implemented). `event.request` stays an unvalidated
|
|
305
|
+
* caller-supplied bag (per its own doc comment) and is deliberately never read for scoping. A
|
|
306
|
+
* terminal report for an unknown `laneId` (no matching dispatch tracked) and a duplicate dispatch
|
|
307
|
+
* for an already-tracked `laneId` are both safe no-ops — never a double registration, a double
|
|
308
|
+
* persisted claim, or a crash.
|
|
309
|
+
*
|
|
310
|
+
* Returns the minted (`phase: "dispatch"`) or completed (`phase: "terminal"`) LaneRecord for an
|
|
311
|
+
* in-process caller that wants the record without a second `getLaneRecords()` read (e.g. a
|
|
312
|
+
* faux-bridge test, or the goal-to-tmux dispatch adapter via `resolveManagedLaneId`); `undefined`
|
|
313
|
+
* on every no-op path (disposed controller, duplicate dispatch, unknown-laneId terminal). The
|
|
314
|
+
* extension-facing `ExtensionActions.reportManagedLane` TYPE stays `=> void` -- that call site is a
|
|
315
|
+
* fire-and-forget statement and is unaffected by this return.
|
|
316
|
+
*/
|
|
317
|
+
recordManagedLane(event) {
|
|
318
|
+
if (this.deps.isDisposed())
|
|
319
|
+
return undefined;
|
|
320
|
+
if (event.phase === "dispatch") {
|
|
321
|
+
if (this._managedLaneDispatches.has(event.laneId))
|
|
322
|
+
return undefined;
|
|
323
|
+
this._seedLaneHistory();
|
|
324
|
+
const record = this._laneTracker.start({
|
|
325
|
+
type: "tmux-worker",
|
|
326
|
+
goalId: event.goalId,
|
|
327
|
+
worktreeLaneKey: event.worktreeLaneKey,
|
|
328
|
+
});
|
|
329
|
+
const deregister = registerInFlightWork(this.deps.getAgentDir(), "lane", `tmux:${record.laneId}`);
|
|
330
|
+
this._managedLaneDispatches.set(event.laneId, { laneId: record.laneId, deregister });
|
|
331
|
+
return record;
|
|
332
|
+
}
|
|
333
|
+
const dispatch = this._managedLaneDispatches.get(event.laneId);
|
|
334
|
+
if (!dispatch)
|
|
335
|
+
return undefined;
|
|
336
|
+
this._managedLaneDispatches.delete(event.laneId);
|
|
337
|
+
try {
|
|
338
|
+
const resolvedStatus = resolveManagedLaneTerminalStatus(event.status);
|
|
339
|
+
const record = this._laneTracker.complete(dispatch.laneId, {
|
|
340
|
+
status: resolvedStatus,
|
|
341
|
+
reasonCode: event.reasonCode,
|
|
342
|
+
costUsd: event.usage?.cost.total,
|
|
343
|
+
});
|
|
344
|
+
if (!record)
|
|
345
|
+
return undefined;
|
|
346
|
+
appendLaneRecordSnapshot(this.deps.getSessionManager(), record);
|
|
347
|
+
const changedFiles = event.changedFiles ? [...event.changedFiles] : [];
|
|
348
|
+
const review = reviewManagedLaneChangedFiles({
|
|
349
|
+
changedFiles,
|
|
350
|
+
envelope: this.deps.getCapabilityEnvelope() ?? {},
|
|
351
|
+
cwd: this.deps.getCwd(),
|
|
352
|
+
});
|
|
353
|
+
const result = {
|
|
354
|
+
requestId: dispatch.laneId,
|
|
355
|
+
status: mapManagedLaneTerminalStatus(resolvedStatus),
|
|
356
|
+
summary: `Managed tmux-worker lane ${dispatch.laneId} reported terminal status "${event.status ?? "unknown"}"${event.reasonCode ? ` (${event.reasonCode})` : ""}.${review.reviewRequired ? ` Changed files require parent review (${review.reasonCode}).` : ""}`,
|
|
357
|
+
changedFiles,
|
|
358
|
+
parentReviewRequired: review.reviewRequired,
|
|
359
|
+
createdAt: new Date().toISOString(),
|
|
360
|
+
};
|
|
361
|
+
this.deps.saveWorkerResultSnapshot(result);
|
|
362
|
+
return record;
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
dispatch.deregister();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
181
368
|
/** Why the last idle research-lane evaluation skipped, for /autonomy diagnostics. */
|
|
182
369
|
getLastResearchLaneSkipReason() {
|
|
183
370
|
return this._lastResearchLaneSkipReason;
|
|
184
371
|
}
|
|
185
|
-
/**
|
|
372
|
+
/**
|
|
373
|
+
* Abort any in-flight research pass or delegated worker (called on session dispose).
|
|
374
|
+
*
|
|
375
|
+
* This synchronous body is the LAST provably-safe write window for canceled/in-flight work.
|
|
376
|
+
* `dispose()` (agent-session.ts) has already set the session's own disposed flag but has not yet
|
|
377
|
+
* returned — no successor session (e.g. a `/reload` adoption) can exist yet, so an append here
|
|
378
|
+
* cannot interleave with one; a post-await continuation resuming AFTER this method returns must
|
|
379
|
+
* not append (see the disposed branch in `runWorkerDelegationOnce`). Persist FIRST, then
|
|
380
|
+
* complete-in-memory, so a throw from one lane's persist cannot skip another's; each persist gets
|
|
381
|
+
* its own try/catch — dispose must never throw.
|
|
382
|
+
*/
|
|
186
383
|
abortInFlightLanes() {
|
|
187
384
|
this._disposed = true;
|
|
188
385
|
this._researchLaneAbort.abort();
|
|
189
386
|
this._workerDelegationAbort.abort();
|
|
190
|
-
this._queuedWorkers.clear();
|
|
191
387
|
for (const record of this._laneTracker.getRecords()) {
|
|
192
|
-
if (record.status
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
388
|
+
if (record.status !== "queued" && record.status !== "running")
|
|
389
|
+
continue;
|
|
390
|
+
const canceled = this._laneTracker.complete(record.laneId, {
|
|
391
|
+
status: "canceled",
|
|
392
|
+
reasonCode: "session_disposed",
|
|
393
|
+
});
|
|
394
|
+
if (!canceled)
|
|
395
|
+
continue;
|
|
396
|
+
try {
|
|
397
|
+
appendLaneRecordSnapshot(this.deps.getSessionManager(), canceled);
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
this._safeWarn(`Failed to persist canceled lane record ${canceled.laneId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
401
|
+
}
|
|
402
|
+
if (canceled.type !== "worker")
|
|
403
|
+
continue;
|
|
404
|
+
// Only a RUNNING worker ever registers a ledger (a queued-never-started worker has none;
|
|
405
|
+
// its cancellation is already fully captured by the lane record above). Consumed
|
|
406
|
+
// (deleted) here so the post-await disposed branch never re-persists it.
|
|
407
|
+
const ledger = this._inFlightWorkerLedgers.get(record.laneId);
|
|
408
|
+
if (!ledger)
|
|
409
|
+
continue;
|
|
410
|
+
this._inFlightWorkerLedgers.delete(record.laneId);
|
|
411
|
+
try {
|
|
412
|
+
const spend = ledger.getSpend();
|
|
413
|
+
const reportId = `worker:${this.deps.getSessionId()}:${record.laneId}`;
|
|
414
|
+
const result = {
|
|
415
|
+
requestId: ledger.request.id,
|
|
416
|
+
status: "cancelled",
|
|
417
|
+
summary: "canceled on session dispose",
|
|
418
|
+
changedFiles: [...ledger.changedFiles],
|
|
419
|
+
usageReportId: reportId,
|
|
420
|
+
createdAt: new Date().toISOString(),
|
|
421
|
+
};
|
|
422
|
+
// Bounded honesty: spend may be incomplete (it lands only when the isolated completion
|
|
423
|
+
// returns, which a mid-flight abort preempts) — record what `getSpend()` knows. Same
|
|
424
|
+
// deterministic reportId scheme as the normal path, so a later duplicate report (there
|
|
425
|
+
// is none in practice here, since the lane is now terminal) stays idempotent.
|
|
426
|
+
this.deps.saveWorkerResultSnapshot(result, ledger.request);
|
|
427
|
+
if (spend && (spend.cost.total > 0 || spend.totalTokens > 0)) {
|
|
428
|
+
this.deps.addSpawnedUsage(spend, { label: "worker-delegation", reportId });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
this._safeWarn(`Failed to persist canceled worker result ${record.laneId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
197
433
|
}
|
|
198
434
|
}
|
|
435
|
+
// A queued (never-started) worker's reload-gate registration ends here on cancellation;
|
|
436
|
+
// the other end is the running handoff in `drainQueuedWorkerDelegations`.
|
|
437
|
+
for (const deregister of this._queuedWorkerDeregisters.values())
|
|
438
|
+
deregister();
|
|
439
|
+
this._queuedWorkerDeregisters.clear();
|
|
440
|
+
this._queuedWorkers.clear();
|
|
199
441
|
this._workerNotificationScheduled = false;
|
|
200
442
|
this._workerTerminalSinceFlush.length = 0;
|
|
201
443
|
}
|
|
@@ -219,6 +461,14 @@ export class BackgroundLaneController {
|
|
|
219
461
|
const snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });
|
|
220
462
|
if (snapshot.continuation.action !== "continue")
|
|
221
463
|
return;
|
|
464
|
+
// Belt-and-braces: the snapshot above is already lane-aware (a bound in-flight worker
|
|
465
|
+
// yields action:"waiting", already caught by the check above), so this direct lane check is
|
|
466
|
+
// redundant in the normal case. It stays as an explicit second gate so a future change that
|
|
467
|
+
// decouples the lane injection from THIS snapshot read can never silently reopen the idle
|
|
468
|
+
// re-dispatch race this guard closes.
|
|
469
|
+
const activeGoalId = snapshot.goalState?.goalId;
|
|
470
|
+
if (activeGoalId !== undefined && this._hasInFlightLaneForGoal(activeGoalId))
|
|
471
|
+
return;
|
|
222
472
|
this.clearGoalAutoContinueTimer();
|
|
223
473
|
this._goalAutoContinueTimer = setTimeout(() => {
|
|
224
474
|
this._goalAutoContinueTimer = undefined;
|
|
@@ -241,14 +491,16 @@ export class BackgroundLaneController {
|
|
|
241
491
|
const snapshot = this.deps.getGoalRuntimeSnapshot({ maxStallTurns });
|
|
242
492
|
if (snapshot.continuation.action !== "continue")
|
|
243
493
|
return;
|
|
244
|
-
//
|
|
494
|
+
// Budget scaling by capability class; note that since the lean class stopped exposing the
|
|
495
|
+
// goal tool at all, sub-full sessions are skipped outright by the goal_tool_unavailable
|
|
496
|
+
// guard in continueGoalLoopExclusive -- the lean budget branch below only matters if that
|
|
497
|
+
// blocklist is ever relaxed. Full passes through unchanged.
|
|
245
498
|
const scaled = scaleContinuationBudgetsForCapability(this.deps.getModelCapabilityProfile(), {
|
|
246
499
|
maxTurns: goalContinueTurns,
|
|
247
500
|
maxWallClockMinutes: goalContinueMaxWallClockMinutes,
|
|
248
501
|
});
|
|
249
|
-
this._isGoalAutoContinuing = true;
|
|
250
502
|
try {
|
|
251
|
-
await this.
|
|
503
|
+
await this.continueGoalLoopExclusive({
|
|
252
504
|
maxTurns: scaled.maxTurns,
|
|
253
505
|
maxStallTurns,
|
|
254
506
|
maxWallClockMinutes: scaled.maxWallClockMinutes,
|
|
@@ -258,10 +510,48 @@ export class BackgroundLaneController {
|
|
|
258
510
|
const message = error instanceof Error ? error.message : String(error);
|
|
259
511
|
this.deps.emit({ type: "warning", message: `Goal auto-continuation failed: ${message}` });
|
|
260
512
|
}
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Single-flight entry point for EVERY goal-continuation loop invocation — idle autosteer
|
|
516
|
+
* ({@link _runScheduledGoalAutoContinue}) AND the manual `/goal start` / `/goal-continue`
|
|
517
|
+
* commands (reached through `AgentSession.continueGoalLoop`). Both paths ultimately submit
|
|
518
|
+
* continuation prompts through the session's single `prompt()` path, so two loops racing throws
|
|
519
|
+
* "Agent is already processing" from whichever submits second. `_isGoalAutoContinuing` is the
|
|
520
|
+
* ONE owner of that mutex; `deps.continueGoalLoop` (the raw {@link GoalLoopController} loop)
|
|
521
|
+
* must never be called directly outside this method, or the guard is bypassed.
|
|
522
|
+
*/
|
|
523
|
+
async continueGoalLoopExclusive(options) {
|
|
524
|
+
if (this._isGoalAutoContinuing)
|
|
525
|
+
return this._skippedGoalResult(options, "already_continuing");
|
|
526
|
+
if (this.deps.isDisposed())
|
|
527
|
+
return this._skippedGoalResult(options, "session_disposed");
|
|
528
|
+
// Adaptative prevails: a session whose active surface lacks the goal tool cannot execute
|
|
529
|
+
// continuation prompts -- driving the loop anyway would ENFORCE complex agentic work on a
|
|
530
|
+
// model/role that cannot perform it. One gate here covers idle autosteer AND manual /goal
|
|
531
|
+
// paths (both funnel through this single-flight guard).
|
|
532
|
+
if (!this.deps.isGoalToolActive())
|
|
533
|
+
return this._skippedGoalResult(options, "goal_tool_unavailable");
|
|
534
|
+
this._isGoalAutoContinuing = true;
|
|
535
|
+
try {
|
|
536
|
+
return await this.deps.continueGoalLoop(options);
|
|
537
|
+
}
|
|
261
538
|
finally {
|
|
262
539
|
this._isGoalAutoContinuing = false;
|
|
263
540
|
}
|
|
264
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* A full {@link GoalContinuationLoopResult} for a continuation request that never ran a pass
|
|
544
|
+
* (another loop already owns the mutex, or the session is disposed). Keeps the skip path
|
|
545
|
+
* type-identical to a real pass — no separate skip union — so every caller keeps reading
|
|
546
|
+
* `result.stopReason`/`result.finalSnapshot` unchanged.
|
|
547
|
+
*/
|
|
548
|
+
_skippedGoalResult(options, stopReason) {
|
|
549
|
+
return {
|
|
550
|
+
turnsSubmitted: 0,
|
|
551
|
+
stopReason,
|
|
552
|
+
finalSnapshot: this.deps.getGoalRuntimeSnapshot({ maxStallTurns: options.maxStallTurns }),
|
|
553
|
+
};
|
|
554
|
+
}
|
|
265
555
|
clearResearchLaneTimer() {
|
|
266
556
|
if (this._researchLaneTimer !== undefined) {
|
|
267
557
|
clearTimeout(this._researchLaneTimer);
|
|
@@ -482,6 +772,9 @@ export class BackgroundLaneController {
|
|
|
482
772
|
this._seedLaneHistory();
|
|
483
773
|
const startedRecord = this._laneTracker.start({ type: "research", goalId: demand.goalId });
|
|
484
774
|
this._persistedResearchRunCount++;
|
|
775
|
+
// Registered for the lane's full run so the reload gate waits it out; deregistered in the
|
|
776
|
+
// finally below no matter how this lane terminates (success, disposal, or a thrown error).
|
|
777
|
+
const deregisterInFlight = registerInFlightWork(this.deps.getAgentDir(), "lane", `research:${startedRecord.laneId}`);
|
|
485
778
|
try {
|
|
486
779
|
let spentUsage;
|
|
487
780
|
// Best-effort, pointer-first workspace evidence. Derives search terms from the goal/requirement
|
|
@@ -527,6 +820,9 @@ export class BackgroundLaneController {
|
|
|
527
820
|
signal,
|
|
528
821
|
// Core/soul/role are all static per configuration — the provider can cache the prefix.
|
|
529
822
|
cacheRetention: "short",
|
|
823
|
+
// Stable per-lane synthetic affinity key so repeat research calls route to the
|
|
824
|
+
// same cache-warm backend without carrying the real session id.
|
|
825
|
+
laneKind: "research",
|
|
530
826
|
});
|
|
531
827
|
spentUsage = completion.usage;
|
|
532
828
|
return {
|
|
@@ -536,7 +832,7 @@ export class BackgroundLaneController {
|
|
|
536
832
|
};
|
|
537
833
|
},
|
|
538
834
|
});
|
|
539
|
-
//
|
|
835
|
+
// If the session was disposed while the completion was in flight, do NOT
|
|
540
836
|
// persist evidence/records/usage against the dead session.
|
|
541
837
|
if (this.deps.isDisposed()) {
|
|
542
838
|
const record = this._laneTracker.complete(startedRecord.laneId, {
|
|
@@ -563,7 +859,7 @@ export class BackgroundLaneController {
|
|
|
563
859
|
});
|
|
564
860
|
if (record) {
|
|
565
861
|
appendLaneRecordSnapshot(this.deps.getSessionManager(), record);
|
|
566
|
-
//
|
|
862
|
+
// A research lane's product is an evidence bundle, so its terminal record maps to
|
|
567
863
|
// the evidence_bundle event. Lane outcome only (status/reasonCode/cost) — no findings text.
|
|
568
864
|
this.deps.emitAutonomyTelemetry({
|
|
569
865
|
type: AUTONOMY_TELEMETRY_EVENT_TYPES.evidenceBundle,
|
|
@@ -606,6 +902,7 @@ export class BackgroundLaneController {
|
|
|
606
902
|
}
|
|
607
903
|
finally {
|
|
608
904
|
this._isResearchLaneRunning = false;
|
|
905
|
+
deregisterInFlight();
|
|
609
906
|
}
|
|
610
907
|
}
|
|
611
908
|
/**
|
|
@@ -637,8 +934,11 @@ export class BackgroundLaneController {
|
|
|
637
934
|
this._laneTracker.getActiveCount("worker") >= settings.maxConcurrent + 8) {
|
|
638
935
|
return { started: false, skipReason: "worker_delegation_queue_full" };
|
|
639
936
|
}
|
|
640
|
-
const record = this._laneTracker.enqueue({ type: "worker" });
|
|
937
|
+
const record = this._laneTracker.enqueue({ type: "worker", goalId: this.deps.getGoalStateSnapshot()?.goalId });
|
|
641
938
|
this._queuedWorkers.set(record.laneId, request);
|
|
939
|
+
// Register the reload-gate quiesce unit at ENQUEUE (not at the later running handoff)
|
|
940
|
+
// so `/reload` waits for queued-but-not-yet-started work too, matching running workers.
|
|
941
|
+
this._queuedWorkerDeregisters.set(record.laneId, registerInFlightWork(this.deps.getAgentDir(), "lane", `worker-queued:${record.laneId}`));
|
|
642
942
|
this._scheduleWorkerNotification();
|
|
643
943
|
return { started: true, record };
|
|
644
944
|
}
|
|
@@ -689,7 +989,8 @@ export class BackgroundLaneController {
|
|
|
689
989
|
return { started: false, skipReason: "model_delegation_unsupported" };
|
|
690
990
|
}
|
|
691
991
|
this._seedLaneHistory();
|
|
692
|
-
const startedRecord = existingRecord ??
|
|
992
|
+
const startedRecord = existingRecord ??
|
|
993
|
+
this._laneTracker.start({ type: "worker", goalId: this.deps.getGoalStateSnapshot()?.goalId });
|
|
693
994
|
if (existingRecord)
|
|
694
995
|
this._laneTracker.markRunning(existingRecord.laneId);
|
|
695
996
|
onStarted?.(startedRecord);
|
|
@@ -737,7 +1038,7 @@ export class BackgroundLaneController {
|
|
|
737
1038
|
maxEstimatedUsd: maxUsd,
|
|
738
1039
|
createdAt: new Date().toISOString(),
|
|
739
1040
|
};
|
|
740
|
-
//
|
|
1041
|
+
// Worker delegation START. Routing/scope codes + budget only — never the instructions text.
|
|
741
1042
|
this.deps.emitAutonomyTelemetry({
|
|
742
1043
|
type: AUTONOMY_TELEMETRY_EVENT_TYPES.workerRequest,
|
|
743
1044
|
timestamp: new Date().toISOString(),
|
|
@@ -749,10 +1050,25 @@ export class BackgroundLaneController {
|
|
|
749
1050
|
},
|
|
750
1051
|
});
|
|
751
1052
|
const usageReportId = `worker:${this.deps.getSessionId()}:${startedRecord.laneId}`;
|
|
1053
|
+
// Registered for the lane's full run so the reload gate waits it out; deregistered in the
|
|
1054
|
+
// finally below no matter how this lane terminates (success, disposal, or a thrown error).
|
|
1055
|
+
// registerInFlightWork is a pure sync map op (cannot throw), so placing it as the last
|
|
1056
|
+
// statement before `try` still guarantees the matching finally always runs.
|
|
1057
|
+
const deregisterInFlight = registerInFlightWork(this.deps.getAgentDir(), "lane", `worker:${startedRecord.laneId}`);
|
|
752
1058
|
try {
|
|
753
1059
|
let spentUsage;
|
|
754
1060
|
const toolChangedFiles = new Set();
|
|
755
1061
|
const toolIssues = new Set();
|
|
1062
|
+
// Register the live mutation ledger BEFORE the suspend point below so a synchronous
|
|
1063
|
+
// disposal cutoff (`abortInFlightLanes`) can read a race-free snapshot of whatever this
|
|
1064
|
+
// worker has already applied — the worker is suspended at the `await runWorker(...)` below
|
|
1065
|
+
// whenever abort runs, and the abort signal stops further tool calls. Deleted in the
|
|
1066
|
+
// `finally` on every exit path (normal completion, throw, or already consumed by abort).
|
|
1067
|
+
this._inFlightWorkerLedgers.set(startedRecord.laneId, {
|
|
1068
|
+
changedFiles: toolChangedFiles,
|
|
1069
|
+
getSpend: () => spentUsage,
|
|
1070
|
+
request: workerRequest,
|
|
1071
|
+
});
|
|
756
1072
|
const outcome = await runWorker({
|
|
757
1073
|
request: workerRequest,
|
|
758
1074
|
maxUsd,
|
|
@@ -762,7 +1078,7 @@ export class BackgroundLaneController {
|
|
|
762
1078
|
signal: this._workerDelegationAbort.signal,
|
|
763
1079
|
// Parent validation must use the same relative-path baseline the runner reports in.
|
|
764
1080
|
cwd: this.deps.getCwd(),
|
|
765
|
-
// Write lane
|
|
1081
|
+
// Write lane: runner-side action application through the envelope path scope.
|
|
766
1082
|
applyActions: workerRequest.envelope.capabilities.includes("write_files")
|
|
767
1083
|
? (actions) => {
|
|
768
1084
|
const permitted = actions.filter((action) => allowedActionOps.has(action.op));
|
|
@@ -827,7 +1143,7 @@ export class BackgroundLaneController {
|
|
|
827
1143
|
catch {
|
|
828
1144
|
// Execution was attempted; preserve conservative accounting with the lexical path.
|
|
829
1145
|
}
|
|
830
|
-
toolChangedFiles.add(path.relative(this.deps.getCwd(), canonicalPath));
|
|
1146
|
+
toolChangedFiles.add(path.relative(this.deps.getCwd(), canonicalPath).split(path.sep).join("/"));
|
|
831
1147
|
}
|
|
832
1148
|
}
|
|
833
1149
|
}
|
|
@@ -840,6 +1156,9 @@ export class BackgroundLaneController {
|
|
|
840
1156
|
signal,
|
|
841
1157
|
// Core/soul/role are all static per configuration — the provider can cache the prefix.
|
|
842
1158
|
cacheRetention: "short",
|
|
1159
|
+
// Stable per-lane synthetic affinity key so repeat worker-delegation calls route to
|
|
1160
|
+
// the same cache-warm backend without carrying the real session id.
|
|
1161
|
+
laneKind: "worker",
|
|
843
1162
|
});
|
|
844
1163
|
spentUsage = completion.usage;
|
|
845
1164
|
return {
|
|
@@ -851,7 +1170,11 @@ export class BackgroundLaneController {
|
|
|
851
1170
|
};
|
|
852
1171
|
},
|
|
853
1172
|
});
|
|
854
|
-
//
|
|
1173
|
+
// Never persist against a disposed session. When disposal raced this
|
|
1174
|
+
// await, `abortInFlightLanes()`'s synchronous cutoff already completed this lane, persisted
|
|
1175
|
+
// its durable lane record + bounded WorkerResult, and consumed (deleted) the ledger —
|
|
1176
|
+
// `.complete()` below is then a no-op (the lane is already terminal, so it returns
|
|
1177
|
+
// undefined) and no double persistence or duplicate terminal notification can happen here.
|
|
855
1178
|
if (this.deps.isDisposed()) {
|
|
856
1179
|
const record = this._laneTracker.complete(startedRecord.laneId, {
|
|
857
1180
|
status: "canceled",
|
|
@@ -873,7 +1196,7 @@ export class BackgroundLaneController {
|
|
|
873
1196
|
if (record) {
|
|
874
1197
|
this._recordWorkerTerminal(record);
|
|
875
1198
|
appendLaneRecordSnapshot(this.deps.getSessionManager(), record);
|
|
876
|
-
//
|
|
1199
|
+
// Worker lane terminal record -> worker_result event. Lane outcome only
|
|
877
1200
|
// (status/reasonCode/cost) — never the worker's summary/changed-file text.
|
|
878
1201
|
this.deps.emitAutonomyTelemetry({
|
|
879
1202
|
type: AUTONOMY_TELEMETRY_EVENT_TYPES.workerResult,
|
|
@@ -915,6 +1238,8 @@ export class BackgroundLaneController {
|
|
|
915
1238
|
return { started: true, record };
|
|
916
1239
|
}
|
|
917
1240
|
finally {
|
|
1241
|
+
this._inFlightWorkerLedgers.delete(startedRecord.laneId);
|
|
1242
|
+
deregisterInFlight();
|
|
918
1243
|
}
|
|
919
1244
|
}
|
|
920
1245
|
/**
|
|
@@ -930,69 +1255,93 @@ export class BackgroundLaneController {
|
|
|
930
1255
|
if (!resolved)
|
|
931
1256
|
return { started: false, skipReason: "model_unresolved_or_unauthenticated" };
|
|
932
1257
|
const capability = this._laneCapabilityProfile(resolved);
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
totalTokens: 0,
|
|
939
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
940
|
-
};
|
|
941
|
-
const report = await runModelFitnessProbe({
|
|
942
|
-
trials: args.trials,
|
|
943
|
-
signal: this._researchLaneAbort.signal,
|
|
944
|
-
capacityProbe: resolved.provider === "ollama" && resolved.contextWindow > 0
|
|
945
|
-
? { registeredContextWindow: resolved.contextWindow }
|
|
946
|
-
: undefined,
|
|
947
|
-
complete: async ({ systemPrompt, userPrompt, signal }) => {
|
|
948
|
-
const callStarted = Date.now();
|
|
949
|
-
const completion = await this.deps.runIsolatedCompletion({
|
|
950
|
-
systemPrompt,
|
|
951
|
-
messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
|
|
952
|
-
model: resolved,
|
|
953
|
-
thinkingLevel: "off",
|
|
954
|
-
maxTokens: capability.laneMaxOutputTokens,
|
|
955
|
-
signal,
|
|
956
|
-
cacheRetention: "short",
|
|
957
|
-
});
|
|
958
|
-
const callMs = Date.now() - callStarted;
|
|
959
|
-
spent.input += completion.usage.input;
|
|
960
|
-
spent.output += completion.usage.output;
|
|
961
|
-
spent.cacheRead += completion.usage.cacheRead;
|
|
962
|
-
spent.cacheWrite += completion.usage.cacheWrite;
|
|
963
|
-
spent.totalTokens += completion.usage.totalTokens;
|
|
964
|
-
spent.cost.input += completion.usage.cost.input;
|
|
965
|
-
spent.cost.output += completion.usage.cost.output;
|
|
966
|
-
spent.cost.cacheRead += completion.usage.cost.cacheRead;
|
|
967
|
-
spent.cost.cacheWrite += completion.usage.cost.cacheWrite;
|
|
968
|
-
spent.cost.total += completion.usage.cost.total;
|
|
969
|
-
return {
|
|
970
|
-
text: completion.text,
|
|
971
|
-
costUsd: completion.usage.cost.total,
|
|
972
|
-
stopReason: String(completion.stopReason),
|
|
973
|
-
// Wall-clock fallback for tok/s: providers don't expose pure eval time, so the
|
|
974
|
-
// measured call time stands in — slightly conservative (includes network/queue).
|
|
975
|
-
outputTokens: completion.usage.output,
|
|
976
|
-
evalMs: callMs,
|
|
977
|
-
};
|
|
978
|
-
},
|
|
979
|
-
});
|
|
980
|
-
if (!this.deps.isDisposed() && (spent.cost.total > 0 || spent.totalTokens > 0)) {
|
|
981
|
-
this.deps.addSpawnedUsage(spent, { label: "model-fitness" });
|
|
982
|
-
}
|
|
983
|
-
const modelRef = `${resolved.provider}/${resolved.id}`;
|
|
984
|
-
// Fitness is a property of a model ON a host — persist the report host-keyed so role
|
|
985
|
-
// assignments stay per-machine (a model can await better hardware without being forgotten).
|
|
986
|
-
// Best-effort: a disk problem must not fail the probe itself.
|
|
1258
|
+
// Registered for the probe's full run (it can execute several isolated-completion trials)
|
|
1259
|
+
// so the reload gate waits it out; deregistered in the finally below on any exit path. Unlike
|
|
1260
|
+
// research/worker, a fitness probe is not tracked in `_laneTracker` (it has no persisted lane
|
|
1261
|
+
// record), so this registry is its ONLY reload-gate visibility.
|
|
1262
|
+
const deregisterInFlight = registerInFlightWork(this.deps.getAgentDir(), "lane", `fitness:${resolved.provider}/${resolved.id}`);
|
|
987
1263
|
try {
|
|
988
|
-
|
|
989
|
-
|
|
1264
|
+
const spent = {
|
|
1265
|
+
input: 0,
|
|
1266
|
+
output: 0,
|
|
1267
|
+
cacheRead: 0,
|
|
1268
|
+
cacheWrite: 0,
|
|
1269
|
+
totalTokens: 0,
|
|
1270
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
1271
|
+
};
|
|
1272
|
+
const report = await runModelFitnessProbe({
|
|
1273
|
+
trials: args.trials,
|
|
1274
|
+
signal: this._researchLaneAbort.signal,
|
|
1275
|
+
capacityProbe: resolved.provider === "ollama" && resolved.contextWindow > 0
|
|
1276
|
+
? { registeredContextWindow: resolved.contextWindow }
|
|
1277
|
+
: undefined,
|
|
1278
|
+
complete: async ({ systemPrompt, userPrompt, signal }) => {
|
|
1279
|
+
const callStarted = Date.now();
|
|
1280
|
+
const completion = await this.deps.runIsolatedCompletion({
|
|
1281
|
+
systemPrompt,
|
|
1282
|
+
messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
|
|
1283
|
+
model: resolved,
|
|
1284
|
+
thinkingLevel: "off",
|
|
1285
|
+
maxTokens: capability.laneMaxOutputTokens,
|
|
1286
|
+
signal,
|
|
1287
|
+
cacheRetention: "short",
|
|
1288
|
+
// Stable per-lane synthetic affinity key so repeat fitness-probe trials against the
|
|
1289
|
+
// same candidate model route to the same cache-warm backend without carrying the real
|
|
1290
|
+
// session id.
|
|
1291
|
+
laneKind: "fitness",
|
|
1292
|
+
});
|
|
1293
|
+
const callMs = Date.now() - callStarted;
|
|
1294
|
+
spent.input += completion.usage.input;
|
|
1295
|
+
spent.output += completion.usage.output;
|
|
1296
|
+
spent.cacheRead += completion.usage.cacheRead;
|
|
1297
|
+
spent.cacheWrite += completion.usage.cacheWrite;
|
|
1298
|
+
spent.totalTokens += completion.usage.totalTokens;
|
|
1299
|
+
spent.cost.input += completion.usage.cost.input;
|
|
1300
|
+
spent.cost.output += completion.usage.cost.output;
|
|
1301
|
+
spent.cost.cacheRead += completion.usage.cost.cacheRead;
|
|
1302
|
+
spent.cost.cacheWrite += completion.usage.cost.cacheWrite;
|
|
1303
|
+
spent.cost.total += completion.usage.cost.total;
|
|
1304
|
+
return {
|
|
1305
|
+
text: completion.text,
|
|
1306
|
+
costUsd: completion.usage.cost.total,
|
|
1307
|
+
stopReason: String(completion.stopReason),
|
|
1308
|
+
// Wall-clock fallback for tok/s: providers don't expose pure eval time, so the
|
|
1309
|
+
// measured call time stands in — slightly conservative (includes network/queue).
|
|
1310
|
+
outputTokens: completion.usage.output,
|
|
1311
|
+
evalMs: callMs,
|
|
1312
|
+
};
|
|
1313
|
+
},
|
|
1314
|
+
});
|
|
1315
|
+
const modelRef = `${resolved.provider}/${resolved.id}`;
|
|
1316
|
+
if (!this.deps.isDisposed() && (spent.cost.total > 0 || spent.totalTokens > 0)) {
|
|
1317
|
+
// Prefer the LLM tool-call id as the idempotency token: it is assigned once per
|
|
1318
|
+
// distinct model_fitness tool call, so two deliberately separate calls on the same
|
|
1319
|
+
// (model, trials) get DISTINCT ids (both count) while a retry of the same tool call
|
|
1320
|
+
// (same toolCallId) keeps the same id (deduped). Callers with no toolCallId (the manual
|
|
1321
|
+
// /fitness command, the auto-probe-on-model-add flows in local-model-commands.ts) fall
|
|
1322
|
+
// back to the (model, trials) identity, unchanged from before.
|
|
1323
|
+
const identity = args.toolCallId
|
|
1324
|
+
? `toolcall:${args.toolCallId}`
|
|
1325
|
+
: `${modelRef} ${args.trials ?? "default"}`;
|
|
1326
|
+
const reportId = deriveSpawnedUsageReportId("model-fitness", this.deps.getSessionId(), identity);
|
|
1327
|
+
this.deps.addSpawnedUsage(spent, { label: "model-fitness", reportId });
|
|
990
1328
|
}
|
|
1329
|
+
// Fitness is a property of a model ON a host — persist the report host-keyed so role
|
|
1330
|
+
// assignments stay per-machine (a model can await better hardware without being forgotten).
|
|
1331
|
+
// Best-effort: a disk problem must not fail the probe itself.
|
|
1332
|
+
try {
|
|
1333
|
+
if (!this.deps.isDisposed()) {
|
|
1334
|
+
FitnessStore.forAgentDir(this.deps.getAgentDir()).save(modelRef, report);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
catch {
|
|
1338
|
+
// best-effort persistence
|
|
1339
|
+
}
|
|
1340
|
+
return { started: true, model: modelRef, report };
|
|
991
1341
|
}
|
|
992
|
-
|
|
993
|
-
|
|
1342
|
+
finally {
|
|
1343
|
+
deregisterInFlight();
|
|
994
1344
|
}
|
|
995
|
-
return { started: true, model: modelRef, report };
|
|
996
1345
|
}
|
|
997
1346
|
/** Start queued local workers at the owner session's foreground-idle boundary. */
|
|
998
1347
|
drainQueuedWorkerDelegations() {
|
|
@@ -1001,11 +1350,19 @@ export class BackgroundLaneController {
|
|
|
1001
1350
|
this.deps.getSettingsManager().getWorkerDelegationSettings().maxConcurrent)
|
|
1002
1351
|
break;
|
|
1003
1352
|
const record = this._laneTracker.getRecords().find((candidate) => candidate.laneId === laneId);
|
|
1353
|
+
// The queued-phase reload-gate registration ends here, at the running handoff, no
|
|
1354
|
+
// matter which branch below runs. `runWorkerDelegationOnce` registers its OWN "running"
|
|
1355
|
+
// unit independently and synchronously (no `await` separates the two calls), so there is
|
|
1356
|
+
// no window where this lane is invisible to the reload gate.
|
|
1357
|
+
const deregisterQueued = this._queuedWorkerDeregisters.get(laneId);
|
|
1358
|
+
this._queuedWorkerDeregisters.delete(laneId);
|
|
1004
1359
|
if (!record) {
|
|
1005
1360
|
this._queuedWorkers.delete(laneId);
|
|
1361
|
+
deregisterQueued?.();
|
|
1006
1362
|
continue;
|
|
1007
1363
|
}
|
|
1008
1364
|
this._queuedWorkers.delete(laneId);
|
|
1365
|
+
deregisterQueued?.();
|
|
1009
1366
|
const promise = this.runWorkerDelegationOnce(request, undefined, record);
|
|
1010
1367
|
this._workerPromises.set(laneId, promise);
|
|
1011
1368
|
void promise.then((outcome) => {
|