@caupulican/pi-adaptative 0.94.1 → 0.96.0
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 +58 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +32 -13
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +7 -4
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +201 -293
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +272 -392
- package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.d.ts +52 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.js +118 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.ts +169 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/builder-validator.json +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/provider-prompt-smoke.json +1 -1
- package/dist/bundled-resources/extensions/tps/index.d.ts +3 -0
- package/dist/bundled-resources/extensions/tps/index.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tps/index.js +44 -0
- package/dist/bundled-resources/extensions/tps/index.js.map +1 -0
- package/dist/bundled-resources/extensions/tps/index.ts +48 -0
- package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +13 -7
- package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +11 -11
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +12 -3
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +11 -41
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +93 -160
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts +6 -1
- package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
- package/dist/core/autonomy/envelope-enforcement.js +26 -25
- package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
- package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -1
- package/dist/core/autonomy/lane-private-paths.js +2 -1
- package/dist/core/autonomy/lane-private-paths.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +8 -8
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +45 -30
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/worker-session-private-scope.d.ts +12 -0
- package/dist/core/autonomy/worker-session-private-scope.d.ts.map +1 -0
- package/dist/core/autonomy/worker-session-private-scope.js +42 -0
- package/dist/core/autonomy/worker-session-private-scope.js.map +1 -0
- package/dist/core/autonomy/worker-tool-adapter-registry.d.ts +65 -0
- package/dist/core/autonomy/worker-tool-adapter-registry.d.ts.map +1 -0
- package/dist/core/autonomy/worker-tool-adapter-registry.js +145 -0
- package/dist/core/autonomy/worker-tool-adapter-registry.js.map +1 -0
- package/dist/core/background-lane-controller.d.ts +1 -1
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +1 -1
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/background-tool-task-controller.d.ts +8 -2
- package/dist/core/background-tool-task-controller.d.ts.map +1 -1
- package/dist/core/background-tool-task-controller.js +10 -2
- package/dist/core/background-tool-task-controller.js.map +1 -1
- package/dist/core/compaction-controller.d.ts +3 -0
- package/dist/core/compaction-controller.d.ts.map +1 -1
- package/dist/core/compaction-controller.js +12 -1
- package/dist/core/compaction-controller.js.map +1 -1
- package/dist/core/cost-guard-controller.d.ts +37 -0
- package/dist/core/cost-guard-controller.d.ts.map +1 -0
- package/dist/core/cost-guard-controller.js +71 -0
- package/dist/core/cost-guard-controller.js.map +1 -0
- package/dist/core/cost-guard.d.ts +9 -4
- package/dist/core/cost-guard.d.ts.map +1 -1
- package/dist/core/cost-guard.js +7 -3
- package/dist/core/cost-guard.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +0 -1
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts +16 -3
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.js +114 -55
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
- package/dist/core/delegation/worker-claim.d.ts +1 -2
- package/dist/core/delegation/worker-claim.d.ts.map +1 -1
- package/dist/core/delegation/worker-claim.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +19 -6
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +125 -40
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +3 -3
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-request.js +13 -19
- package/dist/core/delegation/worker-delegation-request.js.map +1 -1
- package/dist/core/delegation/worker-execution-policy.d.ts +3 -2
- package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
- package/dist/core/delegation/worker-execution-policy.js +54 -39
- package/dist/core/delegation/worker-execution-policy.js.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.d.ts +3 -3
- package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.js +7 -7
- package/dist/core/delegation/worker-fleet-limits.js.map +1 -1
- package/dist/core/delegation/worker-machine-scope.d.ts +12 -0
- package/dist/core/delegation/worker-machine-scope.d.ts.map +1 -0
- package/dist/core/delegation/worker-machine-scope.js +38 -0
- package/dist/core/delegation/worker-machine-scope.js.map +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.js +6 -1
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-runner.d.ts +5 -7
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +1 -4
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +3 -2
- package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-write-reservation-coordinator.js +50 -25
- package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -1
- package/dist/core/durable-custom-message-turn-controller.d.ts +6 -0
- package/dist/core/durable-custom-message-turn-controller.d.ts.map +1 -1
- package/dist/core/durable-custom-message-turn-controller.js +38 -15
- package/dist/core/durable-custom-message-turn-controller.js.map +1 -1
- package/dist/core/extension-binding-controller.d.ts +5 -1
- package/dist/core/extension-binding-controller.d.ts.map +1 -1
- package/dist/core/extension-binding-controller.js +15 -0
- package/dist/core/extension-binding-controller.js.map +1 -1
- package/dist/core/extension-import-authority.d.ts +3 -1
- package/dist/core/extension-import-authority.d.ts.map +1 -1
- package/dist/core/extension-import-authority.js +15 -0
- package/dist/core/extension-import-authority.js.map +1 -1
- package/dist/core/extensions/factory-runtime.d.ts.map +1 -1
- package/dist/core/extensions/factory-runtime.js +5 -0
- package/dist/core/extensions/factory-runtime.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 +8 -4
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.d.ts +14 -0
- package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.js +188 -72
- package/dist/core/foreground-terminal-handoff-controller.js.map +1 -1
- package/dist/core/goals/goal-session-controller.d.ts +2 -0
- package/dist/core/goals/goal-session-controller.d.ts.map +1 -1
- package/dist/core/goals/goal-session-controller.js +8 -0
- package/dist/core/goals/goal-session-controller.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +5 -4
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +23 -19
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/learning/auto-learn-settings.d.ts +84 -0
- package/dist/core/learning/auto-learn-settings.d.ts.map +1 -0
- package/dist/core/learning/auto-learn-settings.js +66 -0
- package/dist/core/learning/auto-learn-settings.js.map +1 -0
- package/dist/core/learning/reflection-engine.d.ts +4 -0
- package/dist/core/learning/reflection-engine.d.ts.map +1 -1
- package/dist/core/learning/reflection-engine.js +48 -10
- package/dist/core/learning/reflection-engine.js.map +1 -1
- package/dist/core/local-prefix-warm-controller.d.ts +24 -0
- package/dist/core/local-prefix-warm-controller.d.ts.map +1 -0
- package/dist/core/local-prefix-warm-controller.js +79 -0
- package/dist/core/local-prefix-warm-controller.js.map +1 -0
- package/dist/core/memory/providers/file-store.d.ts +2 -0
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +71 -15
- package/dist/core/memory/providers/file-store.js.map +1 -1
- package/dist/core/memory/worker-memory-tools.d.ts +5 -0
- package/dist/core/memory/worker-memory-tools.d.ts.map +1 -0
- package/dist/core/memory/worker-memory-tools.js +5 -0
- package/dist/core/memory/worker-memory-tools.js.map +1 -0
- package/dist/core/model-capability.d.ts +7 -0
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +12 -0
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -10
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +3 -2
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +3 -2
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +16 -12
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +2 -2
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/lane-tool-manifests.d.ts +2 -2
- package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
- package/dist/core/orchestration/lane-tool-manifests.js +6 -1
- package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
- package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
- package/dist/core/orchestration/profile-registry.js +14 -0
- package/dist/core/orchestration/profile-registry.js.map +1 -1
- package/dist/core/orchestration/task-profile-writer.d.ts +7 -4
- package/dist/core/orchestration/task-profile-writer.d.ts.map +1 -1
- package/dist/core/orchestration/task-profile-writer.js +71 -59
- package/dist/core/orchestration/task-profile-writer.js.map +1 -1
- package/dist/core/orchestration/thinking-level-schema.d.ts +7 -0
- package/dist/core/orchestration/thinking-level-schema.d.ts.map +1 -0
- package/dist/core/orchestration/thinking-level-schema.js +19 -0
- package/dist/core/orchestration/thinking-level-schema.js.map +1 -0
- package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
- package/dist/core/orchestration/worker-execution-contract.js +12 -5
- package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
- package/dist/core/profile-filter-controller.d.ts.map +1 -1
- package/dist/core/profile-filter-controller.js +5 -2
- package/dist/core/profile-filter-controller.js.map +1 -1
- package/dist/core/provider-prompt-contracts.d.ts +6 -5
- package/dist/core/provider-prompt-contracts.d.ts.map +1 -1
- package/dist/core/provider-prompt-contracts.js +9 -11
- package/dist/core/provider-prompt-contracts.js.map +1 -1
- package/dist/core/provider-request-context-controller.d.ts +2 -0
- package/dist/core/provider-request-context-controller.d.ts.map +1 -1
- package/dist/core/provider-request-context-controller.js +10 -3
- package/dist/core/provider-request-context-controller.js.map +1 -1
- package/dist/core/reflection-controller.d.ts +56 -27
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +206 -117
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +6 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/risk-classifier.d.ts.map +1 -1
- package/dist/core/risk-classifier.js +22 -2
- package/dist/core/risk-classifier.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +1 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +25 -14
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
- package/dist/core/secrets/credential-exposure-guard.js +55 -10
- package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
- package/dist/core/session-role.d.ts +6 -8
- package/dist/core/session-role.d.ts.map +1 -1
- package/dist/core/session-role.js +8 -18
- package/dist/core/session-role.js.map +1 -1
- package/dist/core/settings-manager.d.ts +20 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +75 -50
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skill-vault.d.ts +15 -0
- package/dist/core/skill-vault.d.ts.map +1 -1
- package/dist/core/skill-vault.js +79 -45
- package/dist/core/skill-vault.js.map +1 -1
- package/dist/core/skills.d.ts +3 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +6 -6
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt-builder.d.ts +3 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +73 -13
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +6 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tool-capability-policy.d.ts +3 -0
- package/dist/core/tool-capability-policy.d.ts.map +1 -1
- package/dist/core/tool-capability-policy.js +13 -2
- package/dist/core/tool-capability-policy.js.map +1 -1
- package/dist/core/toolkit/script-runner.d.ts +2 -1
- package/dist/core/toolkit/script-runner.d.ts.map +1 -1
- package/dist/core/toolkit/script-runner.js +10 -2
- package/dist/core/toolkit/script-runner.js.map +1 -1
- package/dist/core/tools/delegate-status.d.ts +1 -1
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +1 -1
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +4 -15
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +111 -73
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/goal.d.ts +3 -4
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +30 -23
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/orchestration-panel.d.ts +7 -0
- package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
- package/dist/core/tools/orchestration-panel.js +58 -9
- package/dist/core/tools/orchestration-panel.js.map +1 -1
- package/dist/core/tools/profile-writer.d.ts +2 -8
- package/dist/core/tools/profile-writer.d.ts.map +1 -1
- package/dist/core/tools/profile-writer.js +6 -18
- package/dist/core/tools/profile-writer.js.map +1 -1
- package/dist/core/tools/run-toolkit-script.d.ts +1 -1
- package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
- package/dist/core/tools/run-toolkit-script.js +2 -2
- package/dist/core/tools/run-toolkit-script.js.map +1 -1
- package/dist/core/tools/skill-audit.d.ts +28 -1
- package/dist/core/tools/skill-audit.d.ts.map +1 -1
- package/dist/core/tools/skill-audit.js +56 -12
- package/dist/core/tools/skill-audit.js.map +1 -1
- package/dist/core/tools/skill.d.ts +12 -1
- package/dist/core/tools/skill.d.ts.map +1 -1
- package/dist/core/tools/skill.js +52 -0
- package/dist/core/tools/skill.js.map +1 -1
- package/dist/core/tools/skillify.d.ts.map +1 -1
- package/dist/core/tools/skillify.js +16 -7
- package/dist/core/tools/skillify.js.map +1 -1
- package/dist/core/tools/task-steps.d.ts.map +1 -1
- package/dist/core/tools/task-steps.js +2 -1
- package/dist/core/tools/task-steps.js.map +1 -1
- package/dist/core/tools/tmux-dispatch.d.ts +4 -16
- package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
- package/dist/core/tools/tmux-dispatch.js +5 -20
- package/dist/core/tools/tmux-dispatch.js.map +1 -1
- package/dist/core/worktree-sync/codes.d.ts +1 -2
- package/dist/core/worktree-sync/codes.d.ts.map +1 -1
- package/dist/core/worktree-sync/codes.js +1 -2
- package/dist/core/worktree-sync/codes.js.map +1 -1
- package/dist/core/worktree-sync/lane-gate.d.ts +4 -8
- package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -1
- package/dist/core/worktree-sync/lane-gate.js +5 -51
- package/dist/core/worktree-sync/lane-gate.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts +6 -7
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +30 -207
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/autonomy-commands.js +2 -2
- package/dist/modes/interactive/autonomy-commands.js.map +1 -1
- package/dist/modes/interactive/components/agents-overlay.d.ts +16 -4
- package/dist/modes/interactive/components/agents-overlay.d.ts.map +1 -1
- package/dist/modes/interactive/components/agents-overlay.js +108 -29
- package/dist/modes/interactive/components/agents-overlay.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +17 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/fitness-role-selector.js +2 -2
- package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +150 -73
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +5 -4
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/report-commands.d.ts.map +1 -1
- package/dist/modes/interactive/report-commands.js +8 -4
- package/dist/modes/interactive/report-commands.js.map +1 -1
- package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
- package/dist/modes/interactive/settings-selector-flow.js +10 -0
- package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
- package/docs/extensions.md +10 -0
- package/docs/harness-architecture.md +28 -32
- package/docs/index.md +2 -1
- package/docs/keybindings.md +2 -0
- package/docs/process-matrix.md +8 -6
- package/docs/resources.md +1 -1
- package/docs/settings.md +61 -51
- package/docs/task-steps.md +2 -0
- package/docs/tmux-agent-manager.md +48 -41
- package/docs/usage.md +1 -1
- package/docs/work-lifecycle.md +137 -0
- package/docs/worker-profiles.md +8 -6
- package/docs/worktree-sync.md +26 -20
- 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 +4 -4
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +0 -133
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +0 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +0 -199
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +0 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +0 -312
|
@@ -4,9 +4,9 @@ import * as fs from "node:fs";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { getProcessWorkRun, PI_ORCHESTRATION_AGENT_ID_ENV, } from "@caupulican/pi-adaptative";
|
|
7
|
+
import { encodeWorkerSessionAllowedPaths, getProcessWorkRun, orchestrationThinkingLevelSchema, PI_ORCHESTRATION_AGENT_ID_ENV, PI_WORKER_ALLOWED_PATHS_ENV, } from "@caupulican/pi-adaptative";
|
|
8
8
|
import { Type } from "typebox";
|
|
9
|
-
import {
|
|
9
|
+
import { buildLaunchProfileFlags, DEFAULT_MANAGED_WORKER_TOOLS, decodeTmuxWorkerUsageClaim, deriveWorkerLaunchProfile, } from "./launch-profile.js";
|
|
10
10
|
export const piConfig = { tools: ["tmux_agent_manager"] };
|
|
11
11
|
function agentLaneId(jobId, agentId) {
|
|
12
12
|
return `tmux:${jobId}:${agentId}`;
|
|
@@ -18,36 +18,24 @@ function managedDispatch(job, agent) {
|
|
|
18
18
|
return {
|
|
19
19
|
sequence,
|
|
20
20
|
instructions,
|
|
21
|
-
profileId: `tmux:${agent.provider}`,
|
|
21
|
+
profileId: agent.dispatchProfileId ?? `tmux:${agent.provider}`,
|
|
22
22
|
provider: agent.provider,
|
|
23
23
|
authorizationId,
|
|
24
24
|
authorizationKind: agent.dispatchAuthorizationKind ?? "legacy-recovery",
|
|
25
|
-
allowedTools: agent.dispatchAllowedTools ??
|
|
25
|
+
allowedTools: agent.dispatchAllowedTools ?? DEFAULT_MANAGED_WORKER_TOOLS,
|
|
26
26
|
writePaths: agent.dispatchWritePaths ?? [],
|
|
27
|
-
...(agent.dispatchMaxCostUsd !== undefined ? { maxCostUsd: agent.dispatchMaxCostUsd } : {}),
|
|
28
27
|
leaseTtlMs: agent.dispatchLeaseTtlMs ?? job.deadlineSeconds * 1_000,
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
writePaths: [...(launchSource.writePaths ?? [])],
|
|
39
|
-
...(grant?.budget.maxUsdAdvisory !== undefined ? { maxCostUsd: grant.budget.maxUsdAdvisory } : {}),
|
|
40
|
-
};
|
|
30
|
+
function applyManagedProfile(agent, profile, args) {
|
|
31
|
+
agent.dispatchProfileId = profile.identity;
|
|
32
|
+
agent.dispatchAuthorizationId = profile.identity;
|
|
33
|
+
agent.dispatchAuthorizationKind = "profile-derived";
|
|
34
|
+
agent.dispatchAllowedTools = [...profile.allowedTools];
|
|
35
|
+
agent.dispatchWritePaths = [...profile.writePaths];
|
|
36
|
+
applyManagedTurn(agent, args);
|
|
41
37
|
}
|
|
42
|
-
function
|
|
43
|
-
agent.dispatchAuthorizationId = authorization.authorizationId;
|
|
44
|
-
agent.dispatchAuthorizationKind = authorization.authorizationKind;
|
|
45
|
-
agent.dispatchAllowedTools = [...authorization.allowedTools];
|
|
46
|
-
agent.dispatchWritePaths = [...authorization.writePaths];
|
|
47
|
-
if (authorization.maxCostUsd !== undefined)
|
|
48
|
-
agent.dispatchMaxCostUsd = authorization.maxCostUsd;
|
|
49
|
-
else
|
|
50
|
-
delete agent.dispatchMaxCostUsd;
|
|
38
|
+
function applyManagedTurn(agent, args) {
|
|
51
39
|
agent.dispatchInstructions = args.instructions;
|
|
52
40
|
agent.dispatchGoalId = args.goalId;
|
|
53
41
|
agent.dispatchLeaseTtlMs = args.leaseTtlMs;
|
|
@@ -108,13 +96,6 @@ function reportManagedAgentFailure(bridge, jobId, agentId, reasonCode) {
|
|
|
108
96
|
// Preserve the launch failure; durable leases fence any terminal-report failure.
|
|
109
97
|
}
|
|
110
98
|
}
|
|
111
|
-
function sameStringValues(left, right) {
|
|
112
|
-
if (left.length !== right.length)
|
|
113
|
-
return false;
|
|
114
|
-
const sortedLeft = [...left].sort();
|
|
115
|
-
const sortedRight = [...right].sort();
|
|
116
|
-
return sortedLeft.every((value, index) => value === sortedRight[index]);
|
|
117
|
-
}
|
|
118
99
|
const EXTENSION_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
119
100
|
export function getTmuxAgentManagerDataRoot() {
|
|
120
101
|
const agentDir = process.env.PI_ADAPTATIVE_CODING_AGENT_DIR ||
|
|
@@ -167,7 +148,7 @@ const TEAM_TEMPLATES = [
|
|
|
167
148
|
],
|
|
168
149
|
notes: [
|
|
169
150
|
"Use only when model-token/auth validation is intended.",
|
|
170
|
-
"The manager
|
|
151
|
+
"The manager arms pane capture, starts each CLI with its task, and watches DONE/BLOCKED markers.",
|
|
171
152
|
],
|
|
172
153
|
},
|
|
173
154
|
{
|
|
@@ -196,7 +177,7 @@ const TEAM_TEMPLATES = [
|
|
|
196
177
|
{ provider: "pi", name: "coordinator" },
|
|
197
178
|
],
|
|
198
179
|
notes: [
|
|
199
|
-
"Use for
|
|
180
|
+
"Use for implementation/QA batches; never claim complete until result files and validation evidence are inspected.",
|
|
200
181
|
],
|
|
201
182
|
},
|
|
202
183
|
];
|
|
@@ -335,7 +316,7 @@ function formatDetection(d) {
|
|
|
335
316
|
}
|
|
336
317
|
function setupHelp(d) {
|
|
337
318
|
if (d.cliAvailable)
|
|
338
|
-
return `tmux CLI available: ${d.tmuxBin || "tmux"}\nUse native delegate for ordinary subagents. Use tmux_agent_manager
|
|
319
|
+
return `tmux CLI available: ${d.tmuxBin || "tmux"}\nUse native delegate for ordinary subagents. Use tmux_agent_manager when a persistent interactive provider pane benefits the task; set dryRun=true only when a preview is useful.`;
|
|
339
320
|
return [
|
|
340
321
|
"tmux CLI not found.",
|
|
341
322
|
"Ordinary subagents remain available through native delegate; do not abort delegation.",
|
|
@@ -358,10 +339,10 @@ async function guardTmux(_ctx, detection, purpose) {
|
|
|
358
339
|
}
|
|
359
340
|
function tmuxManagerInstructions(toolName, detection) {
|
|
360
341
|
return [
|
|
361
|
-
`tmux is available (${detection.version || detection.tmuxBin || "tmux"}). Native delegate remains the standard subagent route. Use tmux_agent_manager
|
|
342
|
+
`tmux is available (${detection.version || detection.tmuxBin || "tmux"}). Native delegate remains the standard subagent route. Use tmux_agent_manager when managed ${toolName} panes need persistent interactive provider processes.`,
|
|
362
343
|
"Suggested next call:",
|
|
363
344
|
'tmux_agent_manager({ action: "fire_task", teamTemplate: "builder-validator", task: "<objective>" })',
|
|
364
|
-
"The launch returns
|
|
345
|
+
"The launch returns after pane capture is armed and each provider command receives its initial task.",
|
|
365
346
|
"Managed mode creates panes, prompt files, result files, event-driven terminal handoffs, tmux display-message notifications, @pi_* status options, shared variables, and one-shot deadlines.",
|
|
366
347
|
].join("\n");
|
|
367
348
|
}
|
|
@@ -464,12 +445,25 @@ function normalizeAgents(params) {
|
|
|
464
445
|
if (!Object.hasOwn(PROVIDER_COMMANDS, provider))
|
|
465
446
|
throw new Error(`unsupported provider: ${provider}`);
|
|
466
447
|
const name = firstString(agent.name, provider === "custom" ? `custom-${index + 1}` : provider) || `agent-${index + 1}`;
|
|
467
|
-
const
|
|
468
|
-
if (!
|
|
448
|
+
const command = firstString(agent.command, PROVIDER_COMMANDS[provider]);
|
|
449
|
+
if (!command)
|
|
469
450
|
throw new Error(`agent ${name} needs command when provider=custom`);
|
|
470
451
|
const tools = Array.isArray(agent.tools) && agent.tools.length > 0 ? agent.tools : undefined;
|
|
471
|
-
const
|
|
472
|
-
|
|
452
|
+
const resourceProfile = firstString(agent.resourceProfile);
|
|
453
|
+
if (provider !== "pi" && (tools || resourceProfile || agent.thinkingLevel || agent.worktreeLane)) {
|
|
454
|
+
throw new Error(`agent ${name}: tools, resourceProfile, thinkingLevel, and worktreeLane overrides are Pi-only; configure ${provider}'s native CLI in command instead`);
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
provider,
|
|
458
|
+
name,
|
|
459
|
+
command,
|
|
460
|
+
path: firstString(agent.path),
|
|
461
|
+
cwd: agent.cwd,
|
|
462
|
+
tools,
|
|
463
|
+
resourceProfile,
|
|
464
|
+
thinkingLevel: agent.thinkingLevel,
|
|
465
|
+
worktreeLane: agent.worktreeLane,
|
|
466
|
+
};
|
|
473
467
|
});
|
|
474
468
|
}
|
|
475
469
|
function defaultProviderInvocation(provider) {
|
|
@@ -491,6 +485,13 @@ function defaultProviderInvocation(provider) {
|
|
|
491
485
|
function interactivePromptText(prompt) {
|
|
492
486
|
return prompt.replace(/\r?\n/g, "\\n");
|
|
493
487
|
}
|
|
488
|
+
function initialPromptInvocation(command, provider, promptPath) {
|
|
489
|
+
const promptArgument = `"$(cat ${quoteShell(promptPath)})"`;
|
|
490
|
+
if (provider === "agy" && !/(?:^|\s)(?:-i|--prompt-interactive)(?:\s|$)/.test(command)) {
|
|
491
|
+
return `${command} --prompt-interactive ${promptArgument}`;
|
|
492
|
+
}
|
|
493
|
+
return `${command} ${promptArgument}`;
|
|
494
|
+
}
|
|
494
495
|
function spellMarker(marker) {
|
|
495
496
|
return marker.split("").join(" ");
|
|
496
497
|
}
|
|
@@ -506,7 +507,7 @@ function managedPrompt(task, jobId, agent, doneMarker, blockedMarker, varsPath)
|
|
|
506
507
|
"Rules:",
|
|
507
508
|
"- Work autonomously until honest PASS or BLOCKED.",
|
|
508
509
|
"- Verify important claims before PASS; include concise evidence.",
|
|
509
|
-
"-
|
|
510
|
+
"- Use the assigned profile without asking for routine permission. Report BLOCKED only when missing external information or an unavailable dependency makes the objective impossible.",
|
|
510
511
|
"- Never print secrets/tokens. Prompts, commands, and logs persist under ~/.pi/agent/work/background/tmux-agent-manager/state/jobs.",
|
|
511
512
|
"- If a task names an external decision variable, read it only at the decision point; never loop or poll the shared JSON file.",
|
|
512
513
|
"",
|
|
@@ -521,7 +522,7 @@ function buildWorkspacePlan(ctx, params) {
|
|
|
521
522
|
const cwd = resolveCwd(ctx, params.cwd);
|
|
522
523
|
const agents = normalizeAgents(params).map((agent, index) => ({
|
|
523
524
|
...agent,
|
|
524
|
-
cwd: resolveCwd(ctx, agent.cwd || cwd),
|
|
525
|
+
cwd: resolveCwd(ctx, agent.path || agent.cwd || cwd),
|
|
525
526
|
id: `${safeName(agent.name || agent.provider || "agent")}-${index + 1}`,
|
|
526
527
|
}));
|
|
527
528
|
const workspaceName = firstString(params.workspaceName, `pi-agents-${Date.now().toString(36)}`) || "pi-agents";
|
|
@@ -599,7 +600,12 @@ export function makePaneWatcherScript(job) {
|
|
|
599
600
|
" sleep_pid=$!",
|
|
600
601
|
' wait "$sleep_pid" 2>/dev/null || exit 0',
|
|
601
602
|
" sleep_pid=",
|
|
602
|
-
' if [ -e "$stop_request_path" ]; then
|
|
603
|
+
' if [ -e "$stop_request_path" ]; then',
|
|
604
|
+
" finish stopped stop-requested-event",
|
|
605
|
+
" else",
|
|
606
|
+
" finish timeout deadline-event",
|
|
607
|
+
' [ -z "$pane_id" ] || tmux kill-pane -t "$pane_id" >/dev/null 2>&1 || true',
|
|
608
|
+
" fi",
|
|
603
609
|
' kill -TERM "$parent_pid" 2>/dev/null || true',
|
|
604
610
|
") &",
|
|
605
611
|
"timer_pid=$!",
|
|
@@ -644,15 +650,16 @@ function buildFireTaskPlan(ctx, params) {
|
|
|
644
650
|
if (!task)
|
|
645
651
|
throw new Error("fire_task requires task (or body) with the worker objective");
|
|
646
652
|
const cwd = resolveCwd(ctx, params.cwd);
|
|
647
|
-
const id = firstString(params.
|
|
653
|
+
const id = firstString(params.launchKey) || makeJobId();
|
|
648
654
|
if (!/^[a-zA-Z0-9_.:-]{4,80}$/.test(id))
|
|
649
|
-
throw new Error("
|
|
655
|
+
throw new Error("launchKey must be 4-80 chars: letters, numbers, _, ., :, or -");
|
|
650
656
|
const jobDir = path.join(jobsRoot(), id);
|
|
651
657
|
const workspaceName = firstString(params.workspaceName, `Pi job ${id}`) || `Pi job ${id}`;
|
|
652
658
|
const sessionName = safeName(workspaceName, id);
|
|
653
659
|
const rawAgents = normalizeAgents(params);
|
|
654
660
|
const agents = rawAgents.map((agent, index) => {
|
|
655
661
|
const provider = agent.provider || "custom";
|
|
662
|
+
const agentCwd = resolveCwd(ctx, agent.path || agent.cwd || cwd);
|
|
656
663
|
const slug = `${safeName(agent.name || provider)}-${index + 1}`;
|
|
657
664
|
const markerBase = `TMUX_${crypto.createHash("sha1").update(`${id}:${slug}`).digest("hex").slice(0, 10).toUpperCase()}`;
|
|
658
665
|
const doneMarker = `${markerBase}_DONE`;
|
|
@@ -662,14 +669,17 @@ function buildFireTaskPlan(ctx, params) {
|
|
|
662
669
|
name: firstString(agent.name, provider) || slug,
|
|
663
670
|
provider,
|
|
664
671
|
command: firstString(agent.command, PROVIDER_COMMANDS[provider]),
|
|
665
|
-
cwd:
|
|
666
|
-
|
|
672
|
+
cwd: agentCwd,
|
|
673
|
+
dispatchAllowedTools: agent.tools ? [...agent.tools] : undefined,
|
|
674
|
+
dispatchWritePaths: agent.path ? [agentCwd] : undefined,
|
|
667
675
|
doneMarker,
|
|
668
676
|
blockedMarker,
|
|
669
677
|
promptPath: path.join(jobDir, `${slug}.prompt.md`),
|
|
670
678
|
logPath: path.join(jobDir, `${slug}.log`),
|
|
671
679
|
resultPath: path.join(jobDir, `${slug}.result.json`),
|
|
672
680
|
worktreeLane: agent.worktreeLane,
|
|
681
|
+
resourceProfile: agent.resourceProfile,
|
|
682
|
+
thinkingLevel: agent.thinkingLevel,
|
|
673
683
|
};
|
|
674
684
|
});
|
|
675
685
|
const job = {
|
|
@@ -689,7 +699,7 @@ function buildFireTaskPlan(ctx, params) {
|
|
|
689
699
|
agents,
|
|
690
700
|
launchCommands: [],
|
|
691
701
|
};
|
|
692
|
-
job.launchCommands = agents.map((agent) => `${agent.command || defaultProviderInvocation(agent.provider)}
|
|
702
|
+
job.launchCommands = agents.map((agent) => `${agent.command || defaultProviderInvocation(agent.provider)} with initial task from ${path.basename(agent.promptPath)}`);
|
|
693
703
|
return job;
|
|
694
704
|
}
|
|
695
705
|
function archiveExistingJobDir(jobDir) {
|
|
@@ -703,7 +713,7 @@ function prepareJobDirForLaunch(job, force) {
|
|
|
703
713
|
if (!fs.existsSync(job.jobDir))
|
|
704
714
|
return undefined;
|
|
705
715
|
if (!force)
|
|
706
|
-
throw new Error(`tmux job already exists: ${job.id}. Use a new
|
|
716
|
+
throw new Error(`tmux job already exists: ${job.id}. Use a new launchKey, or pass force:true to archive the old job before launching.`);
|
|
707
717
|
return archiveExistingJobDir(job.jobDir);
|
|
708
718
|
}
|
|
709
719
|
function writeFireTaskFiles(job) {
|
|
@@ -811,7 +821,7 @@ function sendCommandToPane(paneId, command) {
|
|
|
811
821
|
requireTmuxRun(runTmux(["send-keys", "-t", paneId, "-l", command]), `tmux command injection for ${paneId}`);
|
|
812
822
|
requireTmuxRun(runTmux(["send-keys", "-t", paneId, "Enter"]), `tmux command submit for ${paneId}`);
|
|
813
823
|
}
|
|
814
|
-
function launchTmuxSession(sessionName, cwd, panes) {
|
|
824
|
+
function launchTmuxSession(sessionName, cwd, panes, options = {}) {
|
|
815
825
|
if (tmuxSessionExists(sessionName))
|
|
816
826
|
throw new Error(`tmux session already exists: ${sessionName}. Use stop_session/stop_job first or choose a different workspaceName.`);
|
|
817
827
|
const runs = [];
|
|
@@ -824,7 +834,8 @@ function launchTmuxSession(sessionName, cwd, panes) {
|
|
|
824
834
|
try {
|
|
825
835
|
const firstPane = create.stdout.trim();
|
|
826
836
|
paneIds.push(firstPane);
|
|
827
|
-
|
|
837
|
+
if (!options.deferCommands)
|
|
838
|
+
sendCommandToPane(firstPane, first.command);
|
|
828
839
|
for (const pane of panes.slice(1)) {
|
|
829
840
|
const split = runTmux(["split-window", "-d", "-P", "-F", "#{pane_id}", "-t", `${sessionName}:0`, "-c", pane.cwd || cwd], 5_000);
|
|
830
841
|
runs.push(split);
|
|
@@ -832,7 +843,8 @@ function launchTmuxSession(sessionName, cwd, panes) {
|
|
|
832
843
|
throw new Error(`tmux split-window failed: ${split.error || split.stderr || `exit ${split.status}`}`);
|
|
833
844
|
const paneId = split.stdout.trim();
|
|
834
845
|
paneIds.push(paneId);
|
|
835
|
-
|
|
846
|
+
if (!options.deferCommands)
|
|
847
|
+
sendCommandToPane(paneId, pane.command);
|
|
836
848
|
}
|
|
837
849
|
runs.push(runTmux(["select-layout", "-t", `${sessionName}:0`, "tiled"], 3_000));
|
|
838
850
|
return { runs, paneIds };
|
|
@@ -1125,98 +1137,53 @@ function setVariable(jobId, name, value) {
|
|
|
1125
1137
|
fs.writeFileSync(job.varsPath, `${JSON.stringify(current, null, 2)}\n`, { mode: 0o600 });
|
|
1126
1138
|
return { varsPath: job.varsPath, variables: current.variables, updatedAt: current.updatedAt };
|
|
1127
1139
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
// dispatch-grant.ts; the functions below are the session-access GLUE: read the latest grant custom
|
|
1131
|
-
// entry (skipping past malformed payloads, but stopping DEAD on a tombstone — never resurrecting an
|
|
1132
|
-
// older grant beneath a revocation), count spend, and gate a real launch.
|
|
1133
|
-
// ---------------------------------------------------------------------------
|
|
1134
|
-
/** Most recent VALID `tmux-dispatch-grant` entry on the active branch, or undefined when there is
|
|
1135
|
-
* none, it was revoked (a tombstone is a hard stop — never skipped to find an older grant), or every
|
|
1136
|
-
* entry found fails to decode. Mirrors the branch-walk idiom used by goal/task session state. */
|
|
1137
|
-
function resolveLatestGrantEntry(ctx) {
|
|
1138
|
-
let fromId;
|
|
1139
|
-
for (;;) {
|
|
1140
|
-
const entry = ctx.sessionManager.getLatestCustomEntryOnBranch(GRANT_CUSTOM_TYPE, fromId);
|
|
1141
|
-
if (!entry)
|
|
1142
|
-
return undefined;
|
|
1143
|
-
if (isTmuxDispatchGrantTombstone(entry.data))
|
|
1144
|
-
return undefined;
|
|
1145
|
-
if (isTmuxDispatchGrant(entry.data))
|
|
1146
|
-
return entry.data;
|
|
1147
|
-
if (!entry.parentId)
|
|
1148
|
-
return undefined;
|
|
1149
|
-
fromId = entry.parentId;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
/** Decoded `data` payloads of every custom entry of `customType` on the active branch (root→leaf
|
|
1153
|
-
* order), for spend-counting — a single `getLatestCustomEntryOnBranch` call only ever returns ONE
|
|
1154
|
-
* entry, but budget spend requires ALL of them. */
|
|
1155
|
-
function collectCustomEntryData(ctx, customType) {
|
|
1156
|
-
const result = [];
|
|
1157
|
-
for (const entry of ctx.sessionManager.getBranch()) {
|
|
1158
|
-
const loose = entry;
|
|
1159
|
-
if (loose.type === "custom" && loose.customType === customType)
|
|
1160
|
-
result.push(loose.data);
|
|
1161
|
-
}
|
|
1162
|
-
return result;
|
|
1163
|
-
}
|
|
1164
|
-
/** Gate a REAL tmux dispatch (fire_task non-dryRun, send_followup) per the standing-grant doctrine:
|
|
1165
|
-
* a valid covering grant dispatches UNATTENDED (spending one usage); absent that, an
|
|
1166
|
-
* interactive host may approve a ONE-SHOT launch; absent BOTH, this REFUSES — never a silent launch.
|
|
1167
|
-
* Returns the covering grant when one authorized this launch (so the caller can derive launch-profile
|
|
1168
|
-
* flags from its envelope), or `{}` for a one-shot approval (no envelope to derive from — the caller
|
|
1169
|
-
* falls back to the conservative default profile). */
|
|
1170
|
-
async function authorizeLaunch(bridge, ctx, request) {
|
|
1171
|
-
if (request.agents.length === 0)
|
|
1172
|
-
throw new Error("tmux dispatch refused: launch has no agents");
|
|
1173
|
-
const grant = resolveLatestGrantEntry(ctx);
|
|
1174
|
-
// A standing grant names one provider. It must cover every child and spend one unit for every
|
|
1175
|
-
// child process; a matching first pane must never authorize an unrestricted mixed team.
|
|
1176
|
-
if (grant && request.agents.every((agent) => grantCovers(grant, { agent, goalId: request.goalId }))) {
|
|
1177
|
-
const used = countGrantUsages(grant.grantId, collectCustomEntryData(ctx, GRANT_USAGE_CUSTOM_TYPE));
|
|
1178
|
-
if (grant.budget.maxLaunches - used >= request.agents.length) {
|
|
1179
|
-
if (!bridge.appendEntry)
|
|
1180
|
-
throw new Error("this host does not support session custom entries; cannot spend the tmux dispatch grant.");
|
|
1181
|
-
for (let index = 0; index < request.agents.length; index++) {
|
|
1182
|
-
bridge.appendEntry(GRANT_USAGE_CUSTOM_TYPE, buildGrantUsageEntry(grant.grantId, `${request.jobId}:${index}`));
|
|
1183
|
-
}
|
|
1184
|
-
return { grant };
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
if (ctx.hasUI) {
|
|
1188
|
-
const approved = await ctx.ui.confirm("tmux dispatch approval", [
|
|
1189
|
-
`No standing grant currently authorizes every child of: ${request.description}.`,
|
|
1190
|
-
`Agents: ${request.agents.join(", ")}${request.goalId ? `, goal: ${request.goalId}` : ""}.`,
|
|
1191
|
-
"Approve this ONE-SHOT launch? Run tmux_agent_manager action=grant_dispatch to authorize future launches without this prompt.",
|
|
1192
|
-
].join("\n"));
|
|
1193
|
-
if (!approved)
|
|
1194
|
-
throw new Error(`tmux dispatch declined by the owner: ${request.description}`);
|
|
1195
|
-
return {};
|
|
1196
|
-
}
|
|
1197
|
-
throw new Error(`no standing grant for tmux dispatch; run grant_dispatch first: every child must be covered. ${request.description}. Refusing to launch without a grant or interactive approval.`);
|
|
1198
|
-
}
|
|
1199
|
-
/** Render grant/one-shot-derived launch-profile flags into a pi child's start command. Values
|
|
1200
|
-
* are shell-quoted; flags are not (they're fixed literals, never user input). */
|
|
1140
|
+
/** Render a host-derived launch profile into a Pi child's start command. Values are shell-quoted;
|
|
1141
|
+
* flags are fixed literals. */
|
|
1201
1142
|
function appendLaunchProfileFlags(command, flags) {
|
|
1202
1143
|
const rendered = flags.map((flag) => flag.value !== undefined ? `${flag.flag} ${quoteShell(flag.value)}` : flag.flag);
|
|
1203
1144
|
return [command, ...rendered].join(" ");
|
|
1204
1145
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
const profiledCommand = appendLaunchProfileFlags(agent.command || defaultProviderInvocation(agent.provider), flags);
|
|
1218
|
-
agent.command = `env ${PI_ORCHESTRATION_AGENT_ID_ENV}=${quoteShell(agentLaneId(job.id, agent.id))} ${profiledCommand}`;
|
|
1146
|
+
function deriveLaunchProfile(bridge, ctx, job, agent, goalId) {
|
|
1147
|
+
if (agent.provider !== "pi") {
|
|
1148
|
+
return deriveWorkerLaunchProfile({
|
|
1149
|
+
identity: `tmux-profile:${job.id}:${agent.id}`,
|
|
1150
|
+
// External interactive CLIs expose their own native, unrestricted tool surfaces. Model the
|
|
1151
|
+
// managed boundary honestly as one host-trusted process instead of projecting Pi tool names.
|
|
1152
|
+
allowedTools: ["bash"],
|
|
1153
|
+
writePaths: [],
|
|
1154
|
+
parentPid: process.pid,
|
|
1155
|
+
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1156
|
+
taskRef: goalId,
|
|
1157
|
+
});
|
|
1219
1158
|
}
|
|
1159
|
+
const effectiveResources = agent.resourceProfile ? undefined : bridge.getEffectiveResourceProfile?.();
|
|
1160
|
+
const serializedResources = effectiveResources && Object.keys(effectiveResources).length > 0 ? JSON.stringify(effectiveResources) : undefined;
|
|
1161
|
+
const inheritedResourceProfile = serializedResources
|
|
1162
|
+
? `tmux-inherited-${crypto.createHash("sha256").update(serializedResources).digest("hex").slice(0, 12)}`
|
|
1163
|
+
: undefined;
|
|
1164
|
+
return deriveWorkerLaunchProfile({
|
|
1165
|
+
identity: `tmux-profile:${job.id}:${agent.id}`,
|
|
1166
|
+
inheritedTools: bridge.getActiveTools?.(),
|
|
1167
|
+
allowedTools: agent.dispatchAllowedTools,
|
|
1168
|
+
resourceProfile: agent.resourceProfile ?? inheritedResourceProfile,
|
|
1169
|
+
resourceProfileJson: inheritedResourceProfile && effectiveResources
|
|
1170
|
+
? JSON.stringify({ [inheritedResourceProfile]: effectiveResources })
|
|
1171
|
+
: undefined,
|
|
1172
|
+
writePaths: agent.dispatchWritePaths ?? (agent.worktreeLane ? [agent.cwd] : []),
|
|
1173
|
+
thinkingLevel: agent.thinkingLevel ?? bridge.getThinkingLevel?.(),
|
|
1174
|
+
worktreeLane: agent.worktreeLane,
|
|
1175
|
+
parentPid: process.pid,
|
|
1176
|
+
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1177
|
+
taskRef: goalId,
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
/** Apply the already-compiled immutable profile to a Pi child. Other provider CLIs retain their own
|
|
1181
|
+
* native tool surface; the profile remains the host's durable audit contract for every provider. */
|
|
1182
|
+
function applyLaunchProfile(job, agent, profile) {
|
|
1183
|
+
if (agent.provider !== "pi")
|
|
1184
|
+
return;
|
|
1185
|
+
const profiledCommand = appendLaunchProfileFlags(agent.command || defaultProviderInvocation(agent.provider), buildLaunchProfileFlags(profile));
|
|
1186
|
+
agent.command = `env ${PI_ORCHESTRATION_AGENT_ID_ENV}=${quoteShell(agentLaneId(job.id, agent.id))} ${PI_WORKER_ALLOWED_PATHS_ENV}=${quoteShell(encodeWorkerSessionAllowedPaths(profile.writePaths))} ${profiledCommand}`;
|
|
1220
1187
|
}
|
|
1221
1188
|
/** Read an OPTIONAL, cooperative worker-reported usage claim for the turn that just went terminal (a
|
|
1222
1189
|
* sibling `<result-path>.usage.json` file next to the watcher's own result file). Absent file ⇒ no
|
|
@@ -1286,62 +1253,6 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1286
1253
|
details: { action, jobId: params.jobId, variableName: params.variableName, result },
|
|
1287
1254
|
};
|
|
1288
1255
|
}
|
|
1289
|
-
if (action === "grant_dispatch") {
|
|
1290
|
-
if (!params.agent)
|
|
1291
|
-
throw new Error("grant_dispatch requires agent");
|
|
1292
|
-
if (!Object.hasOwn(PROVIDER_COMMANDS, params.agent))
|
|
1293
|
-
throw new Error(`grant_dispatch: unsupported agent: ${params.agent}`);
|
|
1294
|
-
const grantParams = {
|
|
1295
|
-
agent: params.agent,
|
|
1296
|
-
goalId: params.goalId,
|
|
1297
|
-
allowedTools: params.allowedTools,
|
|
1298
|
-
resourceProfile: params.resourceProfile,
|
|
1299
|
-
writePaths: params.writePaths,
|
|
1300
|
-
maxLaunches: typeof params.maxLaunches === "number" ? params.maxLaunches : Number.NaN,
|
|
1301
|
-
expiresInMinutes: params.expiresInMinutes,
|
|
1302
|
-
maxUsdAdvisory: params.maxUsdAdvisory,
|
|
1303
|
-
};
|
|
1304
|
-
const grant = buildGrant(grantParams);
|
|
1305
|
-
const summary = describeGrant(grant);
|
|
1306
|
-
// OWNER-AUTHORIZED, NEVER SILENT: an interactive host must explicitly confirm the exact
|
|
1307
|
-
// grant details; a non-interactive host (print/rpc, or the unattended goal loop) has no confirm
|
|
1308
|
-
// surface, so it needs an explicit opt-in flag instead — absent either, refuse to create the grant.
|
|
1309
|
-
if (ctx.hasUI) {
|
|
1310
|
-
const approved = await ctx.ui.confirm("Authorize tmux dispatch grant", summary);
|
|
1311
|
-
if (!approved)
|
|
1312
|
-
throw new Error("grant_dispatch was declined by the owner.");
|
|
1313
|
-
}
|
|
1314
|
-
else if (bridge.getFlag?.("allow-tmux-dispatch") !== true) {
|
|
1315
|
-
throw new Error("grant_dispatch requires interactive approval; no UI is available in this mode. Pass --allow-tmux-dispatch to authorize grant creation non-interactively.");
|
|
1316
|
-
}
|
|
1317
|
-
if (!bridge.appendEntry)
|
|
1318
|
-
throw new Error("this host does not support session custom entries; cannot persist a tmux dispatch grant.");
|
|
1319
|
-
bridge.appendEntry(GRANT_CUSTOM_TYPE, grant);
|
|
1320
|
-
return {
|
|
1321
|
-
content: [{ type: "text", text: `Created tmux dispatch grant ${grant.grantId}.\n${summary}` }],
|
|
1322
|
-
details: { action, grant },
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
if (action === "revoke_grant") {
|
|
1326
|
-
const activeGrant = resolveLatestGrantEntry(ctx);
|
|
1327
|
-
const targetGrantId = firstString(params.grantId) || activeGrant?.grantId;
|
|
1328
|
-
if (!targetGrantId)
|
|
1329
|
-
throw new Error("revoke_grant: no active tmux dispatch grant to revoke.");
|
|
1330
|
-
if (activeGrant && activeGrant.grantId !== targetGrantId)
|
|
1331
|
-
throw new Error(`revoke_grant: grantId ${targetGrantId} is not the active grant (active: ${activeGrant.grantId}).`);
|
|
1332
|
-
if (!bridge.appendEntry)
|
|
1333
|
-
throw new Error("this host does not support session custom entries; cannot revoke a tmux dispatch grant.");
|
|
1334
|
-
bridge.appendEntry(GRANT_CUSTOM_TYPE, buildTombstone(targetGrantId));
|
|
1335
|
-
return {
|
|
1336
|
-
content: [
|
|
1337
|
-
{
|
|
1338
|
-
type: "text",
|
|
1339
|
-
text: `Revoked tmux dispatch grant ${targetGrantId}. Future launches need a new grant_dispatch or interactive/opt-in approval.`,
|
|
1340
|
-
},
|
|
1341
|
-
],
|
|
1342
|
-
details: { action, grantId: targetGrantId },
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
1256
|
const guard = await guardTmux(ctx, detection, `tmux ${action}`);
|
|
1346
1257
|
if (!guard.allowed)
|
|
1347
1258
|
return { content: [{ type: "text", text: guard.text }], details: { action, detection, guard, skipped: true } };
|
|
@@ -1406,33 +1317,19 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1406
1317
|
};
|
|
1407
1318
|
if (tmuxSessionExists(job.sessionName))
|
|
1408
1319
|
throw new Error(`tmux session already exists: ${job.sessionName}. Use stop_job/stop_session first or choose a different workspaceName.`);
|
|
1409
|
-
//
|
|
1410
|
-
//
|
|
1411
|
-
const authorization = await authorizeLaunch(bridge, ctx, {
|
|
1412
|
-
agents: job.agents.map((agent) => agent.provider),
|
|
1413
|
-
goalId: params.goalId,
|
|
1414
|
-
jobId: job.id,
|
|
1415
|
-
description: `fire_task launch of job ${job.id} (${job.agents.length} child process${job.agents.length === 1 ? "" : "es"}: ${job.agents.map((agent) => `${agent.name}/${agent.provider}`).join(", ")})`,
|
|
1416
|
-
});
|
|
1417
|
-
const managedAuthority = managedAuthorization(authorization.grant, `one-shot:${job.id}:turn:1`);
|
|
1320
|
+
// Compile each immutable worker profile from the parent surface plus per-agent overrides before
|
|
1321
|
+
// any durable reservation, filesystem artifact, pane, watcher, or prompt side effect.
|
|
1418
1322
|
for (const agent of job.agents) {
|
|
1323
|
+
const profile = deriveLaunchProfile(bridge, ctx, job, agent, params.goalId);
|
|
1419
1324
|
agent.currentTurn = 1;
|
|
1420
|
-
|
|
1325
|
+
applyManagedProfile(agent, profile, {
|
|
1421
1326
|
instructions: job.task,
|
|
1422
1327
|
goalId: params.goalId,
|
|
1423
1328
|
leaseTtlMs: job.deadlineSeconds * 1_000,
|
|
1424
1329
|
});
|
|
1330
|
+
applyLaunchProfile(job, agent, profile);
|
|
1425
1331
|
}
|
|
1426
|
-
|
|
1427
|
-
...managedAuthority.launchSource,
|
|
1428
|
-
...(params.goalId ? { taskRef: params.goalId } : {}),
|
|
1429
|
-
// Process-matrix parent identity: the dispatched child self-registers as a worker of THIS
|
|
1430
|
-
// session and winds down gracefully if this session disappears (see dispatch-grant.ts's
|
|
1431
|
-
// `LaunchProfileSource.parentPid`/`parentSession` doc).
|
|
1432
|
-
parentPid: process.pid,
|
|
1433
|
-
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1434
|
-
});
|
|
1435
|
-
// The host commits the full execution grant and starts the durable lease before any child
|
|
1332
|
+
// The host commits the full execution profile and starts the durable lease before any child
|
|
1436
1333
|
// process, job artifact, watcher, or prompt side effect occurs.
|
|
1437
1334
|
reportManagedDispatchReservations(bridge, job, job.agents);
|
|
1438
1335
|
let sessionCreated = false;
|
|
@@ -1443,7 +1340,9 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1443
1340
|
cwd: agent.cwd,
|
|
1444
1341
|
command: agent.command || defaultProviderInvocation(agent.provider),
|
|
1445
1342
|
}));
|
|
1446
|
-
|
|
1343
|
+
// Create idle panes first. The initial provider task is part of the provider command, but no
|
|
1344
|
+
// command starts until its output watcher owns the pane stream.
|
|
1345
|
+
const launch = launchTmuxSession(job.sessionName, job.cwd, panes, { deferCommands: true });
|
|
1447
1346
|
sessionCreated = true;
|
|
1448
1347
|
job.agents.forEach((agent, index) => {
|
|
1449
1348
|
agent.paneId = launch.paneIds[index];
|
|
@@ -1453,7 +1352,7 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1453
1352
|
for (const agent of job.agents) {
|
|
1454
1353
|
if (!agent.paneId)
|
|
1455
1354
|
continue;
|
|
1456
|
-
|
|
1355
|
+
sendCommandToPane(agent.paneId, initialPromptInvocation(agent.command || defaultProviderInvocation(agent.provider), agent.provider, agent.promptPath));
|
|
1457
1356
|
}
|
|
1458
1357
|
return {
|
|
1459
1358
|
content: [
|
|
@@ -1524,22 +1423,8 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1524
1423
|
throw new Error(`tmux session is gone: ${job.sessionName}. The worker pane cannot receive a follow-up; use fire_task to relaunch.`);
|
|
1525
1424
|
if (!tmuxPaneExists(job.sessionName, targetAgent.paneId))
|
|
1526
1425
|
throw new Error(`tmux pane ${targetAgent.paneId} for ${targetAgent.name} is gone. The worker cannot receive a follow-up; use fire_task to relaunch.`);
|
|
1527
|
-
//
|
|
1528
|
-
//
|
|
1529
|
-
// grant/one-shot authorization is resolved here (no applyLaunchProfile call).
|
|
1530
|
-
const authorization = await authorizeLaunch(bridge, ctx, {
|
|
1531
|
-
agents: [targetAgent.provider],
|
|
1532
|
-
goalId: params.goalId,
|
|
1533
|
-
jobId: `${job.id}:turn:${turn}`,
|
|
1534
|
-
description: `send_followup turn ${turn} to ${targetAgent.name} in job ${job.id}`,
|
|
1535
|
-
});
|
|
1536
|
-
const managedAuthority = managedAuthorization(authorization.grant, `one-shot:${job.id}:turn:${turn}`);
|
|
1537
|
-
const launchAllowedTools = targetAgent.dispatchAllowedTools ?? [...DEFAULT_READ_BIASED_TOOLS];
|
|
1538
|
-
const launchWritePaths = targetAgent.dispatchWritePaths ?? [];
|
|
1539
|
-
if (!sameStringValues(launchAllowedTools, managedAuthority.allowedTools) ||
|
|
1540
|
-
!sameStringValues(launchWritePaths, managedAuthority.writePaths)) {
|
|
1541
|
-
throw new Error(`send_followup refused: authorization scope differs from the already-running process for ${targetAgent.name}; relaunch the worker to apply a new tool or write-path scope.`);
|
|
1542
|
-
}
|
|
1426
|
+
// The live process keeps the immutable profile persisted at birth. A follow-up only advances
|
|
1427
|
+
// instructions and its fenced sequence; it cannot widen or replace the process profile.
|
|
1543
1428
|
// Reserve before watcher/prompt side effects. Reconcile re-arms a reserved turn but never
|
|
1544
1429
|
// re-injects it, so crash recovery cannot duplicate a provider prompt.
|
|
1545
1430
|
const reserved = persistJobPatch(job.id, (current) => {
|
|
@@ -1549,9 +1434,9 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1549
1434
|
agent.currentTurn = turn;
|
|
1550
1435
|
agent.pendingTurn = turn;
|
|
1551
1436
|
agent.result = undefined;
|
|
1552
|
-
|
|
1437
|
+
applyManagedTurn(agent, {
|
|
1553
1438
|
instructions: followupTask,
|
|
1554
|
-
goalId: params.goalId,
|
|
1439
|
+
goalId: params.goalId ?? agent.dispatchGoalId,
|
|
1555
1440
|
leaseTtlMs: job.deadlineSeconds * 1_000,
|
|
1556
1441
|
});
|
|
1557
1442
|
current.currentTurn = turn;
|
|
@@ -1608,6 +1493,13 @@ async function executeTool(_toolCallId, params, _signal, _onUpdate, ctx, bridge)
|
|
|
1608
1493
|
content: [{ type: "text", text: `tmux job ${job.id} was already dismissed.` }],
|
|
1609
1494
|
details: { action, jobId: job.id, alreadyDismissed: true },
|
|
1610
1495
|
};
|
|
1496
|
+
if (tmuxSessionExists(job.sessionName)) {
|
|
1497
|
+
for (const agent of job.agents) {
|
|
1498
|
+
if (!agent.paneId || !tmuxPaneExists(job.sessionName, agent.paneId))
|
|
1499
|
+
continue;
|
|
1500
|
+
requireTmuxRun(runTmux(["pipe-pane", "-t", agent.paneId]), `tmux completion watcher detach for ${agent.paneId}`);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1611
1503
|
persistJobPatch(job.id, (current) => {
|
|
1612
1504
|
current.dismissedAt = new Date().toISOString();
|
|
1613
1505
|
return current;
|
|
@@ -1705,6 +1597,15 @@ function formatFireTaskHandoff(job) {
|
|
|
1705
1597
|
"This event woke the parent; do not poll. Continue safe scoped work from the bounded handoff and inspect terminal artifacts only if needed for a material claim.",
|
|
1706
1598
|
].join("\n");
|
|
1707
1599
|
}
|
|
1600
|
+
function compactTmuxAgentStatus(status) {
|
|
1601
|
+
if (status === "done" || status === "succeeded" || status === "completed")
|
|
1602
|
+
return "succeeded";
|
|
1603
|
+
if (status === "blocked" || status === "partial")
|
|
1604
|
+
return "partial";
|
|
1605
|
+
if (status === "canceled" || status === "cancelled" || status === "stopped")
|
|
1606
|
+
return "canceled";
|
|
1607
|
+
return "failed";
|
|
1608
|
+
}
|
|
1708
1609
|
/** Diff live tmux sessions against this session's job records. Mirrors the invariant behind
|
|
1709
1610
|
* `LocalRuntimeController.reconcile` (local-runtime-controller.ts): reconcile only ever OBSERVES —
|
|
1710
1611
|
* it never kills a session it did not provably start. A job whose tmux session is gone is marked
|
|
@@ -1765,13 +1666,8 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1765
1666
|
let handoffContext;
|
|
1766
1667
|
let handoffTail = Promise.resolve();
|
|
1767
1668
|
const jobWatchers = new Map();
|
|
1669
|
+
const reconciledTerminalTurns = new Set();
|
|
1768
1670
|
const bridge = pi;
|
|
1769
|
-
// Non-interactive opt-in for grant_dispatch: optional-chained so a lightweight test double
|
|
1770
|
-
// that doesn't implement registerFlag still loads the extension; a real host always has it.
|
|
1771
|
-
bridge.registerFlag?.("allow-tmux-dispatch", {
|
|
1772
|
-
type: "boolean",
|
|
1773
|
-
description: "Authorize tmux_agent_manager grant_dispatch to create a standing tmux dispatch grant without interactive approval (print/rpc/non-interactive mode only).",
|
|
1774
|
-
});
|
|
1775
1671
|
const closeJobWatchers = () => {
|
|
1776
1672
|
for (const watcher of jobWatchers.values())
|
|
1777
1673
|
watcher.close();
|
|
@@ -1783,6 +1679,31 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1783
1679
|
for (const job of jobs) {
|
|
1784
1680
|
if (!isFireTaskTerminal(job))
|
|
1785
1681
|
continue;
|
|
1682
|
+
// A fresh host may rehydrate a legacy running lane after this external job already persisted
|
|
1683
|
+
// its terminal result. Reconcile that terminal truth once per process/turn even when the
|
|
1684
|
+
// completion handoff was delivered earlier; ManagedLaneController makes the replay inert
|
|
1685
|
+
// when its own lifecycle is already terminal.
|
|
1686
|
+
for (const agent of job.agents) {
|
|
1687
|
+
const turn = agent.currentTurn ?? 1;
|
|
1688
|
+
if ((agent.notifiedTurn ?? 0) < turn)
|
|
1689
|
+
continue;
|
|
1690
|
+
const reconciliationKey = `${job.id}:${agent.id}:${turn}:${agent.result?.status ?? "failed"}`;
|
|
1691
|
+
if (reconciledTerminalTurns.has(reconciliationKey))
|
|
1692
|
+
continue;
|
|
1693
|
+
bridge.reportManagedLane?.({
|
|
1694
|
+
laneId: agentLaneId(job.id, agent.id),
|
|
1695
|
+
phase: "terminal",
|
|
1696
|
+
status: agent.result?.status,
|
|
1697
|
+
reasonCode: "tmux_terminal_reconciled",
|
|
1698
|
+
});
|
|
1699
|
+
reconciledTerminalTurns.add(reconciliationKey);
|
|
1700
|
+
while (reconciledTerminalTurns.size > 1_024) {
|
|
1701
|
+
const oldest = reconciledTerminalTurns.values().next().value;
|
|
1702
|
+
if (oldest === undefined)
|
|
1703
|
+
break;
|
|
1704
|
+
reconciledTerminalTurns.delete(oldest);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1786
1707
|
// Per-turn notify: a job is not permanently "closed" after its first terminal turn — a
|
|
1787
1708
|
// follow-up reopens it, so the gate is per-agent (notifiedTurn < currentTurn), not a
|
|
1788
1709
|
// single whole-job notifiedAt flag.
|
|
@@ -1790,6 +1711,8 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1790
1711
|
if (pendingAgents.length === 0)
|
|
1791
1712
|
continue;
|
|
1792
1713
|
for (const agent of pendingAgents) {
|
|
1714
|
+
const reconciliationKey = `${job.id}:${agent.id}:${agent.currentTurn ?? 1}:${agent.result?.status ?? "failed"}`;
|
|
1715
|
+
reconciledTerminalTurns.add(reconciliationKey);
|
|
1793
1716
|
// Advisory-only, and only ever reported when the worker itself chose to write the
|
|
1794
1717
|
// claim file — never fabricated, never a hard cross-process cap.
|
|
1795
1718
|
const usage = readWorkerUsageClaim(job, agent);
|
|
@@ -1810,16 +1733,13 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1810
1733
|
// message remains only for lightweight/standalone hosts that omit that bridge entirely.
|
|
1811
1734
|
if (!bridge.reportManagedLane) {
|
|
1812
1735
|
pi.sendMessage({
|
|
1813
|
-
customType: "
|
|
1736
|
+
customType: "background-worker-completion",
|
|
1814
1737
|
content: formatFireTaskHandoff(job),
|
|
1815
1738
|
display: true,
|
|
1816
1739
|
details: {
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
id: agent.id,
|
|
1821
|
-
name: agent.name,
|
|
1822
|
-
status: agent.result?.status,
|
|
1740
|
+
records: job.agents.map((agent) => ({
|
|
1741
|
+
laneId: agentLaneId(job.id, agent.id),
|
|
1742
|
+
status: compactTmuxAgentStatus(agent.result?.status),
|
|
1823
1743
|
})),
|
|
1824
1744
|
},
|
|
1825
1745
|
}, { triggerTurn: true, deliverAs: "followUp" });
|
|
@@ -1834,8 +1754,6 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1834
1754
|
}
|
|
1835
1755
|
return current;
|
|
1836
1756
|
});
|
|
1837
|
-
if (ctx.hasUI)
|
|
1838
|
-
ctx.ui.notify(`tmux background task ${job.id} completed.`, "info");
|
|
1839
1757
|
}
|
|
1840
1758
|
const activeIds = new Set(jobs.filter((job) => !isFireTaskTerminal(job)).map((job) => job.id));
|
|
1841
1759
|
for (const [jobId, watcher] of jobWatchers) {
|
|
@@ -1892,22 +1810,21 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1892
1810
|
pi.registerTool({
|
|
1893
1811
|
name: "tmux_agent_manager",
|
|
1894
1812
|
label: "tmux Agent Manager",
|
|
1895
|
-
description: "
|
|
1896
|
-
promptSnippet: "tmux
|
|
1813
|
+
description: "Tmux-backed persistent interactive provider/workspace manager. Native delegate remains the standard cross-platform route; use this when a task benefits from persistent CLI panes.",
|
|
1814
|
+
promptSnippet: "Persistent tmux workers with inherited, immutable launch profiles; ordinary agents use native delegate.",
|
|
1897
1815
|
promptGuidelines: [
|
|
1898
|
-
"Ordinary agents: delegate.
|
|
1899
|
-
"
|
|
1900
|
-
"
|
|
1816
|
+
"Ordinary agents: delegate. Tmux availability detection is automatic; guard is diagnostic only. Never use native Windows tmux.",
|
|
1817
|
+
"fire_task derives one immutable profile per agent; omitted fields inherit parent tools, thinking, and machine scope.",
|
|
1818
|
+
"tools, thinkingLevel, resourceProfile, and worktreeLane are Pi-only; configure external CLIs in command.",
|
|
1819
|
+
"Pi path narrows structured file tools; external path sets cwd only and is audited as a machine-wide host process.",
|
|
1820
|
+
"fire_task arms capture before atomically starting each CLI with its task; never wait/poll/peek.",
|
|
1901
1821
|
"Teams: list/show_template, teamTemplate. Layout: workspace_plan, then launch_workspace.",
|
|
1902
1822
|
"send_followup prompts live pane/re-arms watcher; no relaunch. dismiss stops tracking, not session.",
|
|
1903
1823
|
"Cleanup: stop_job/stop_session dryRun, then confirm=yes-tmux-stop.",
|
|
1904
1824
|
"No secrets in task/command; all persist: ~/.pi/agent/work/background/tmux-agent-manager/state/jobs.",
|
|
1905
1825
|
"Display/status metadata only; verify result/log before completion claim.",
|
|
1906
|
-
"
|
|
1907
|
-
"
|
|
1908
|
-
"Grant needs UI confirmation, or --allow-tmux-dispatch without UI; revoke_grant ends it.",
|
|
1909
|
-
"Pi child: restricted tools/resource profile or no extensions/skills, scoped hard-stop prompt; launch config holds grant.",
|
|
1910
|
-
"Non-pi CLI grant applies at launch only; tool loop is CLI-owned.",
|
|
1826
|
+
"Workers continue autonomously inside the assigned profile; no separate approval handshake exists.",
|
|
1827
|
+
"Non-Pi process behavior is CLI-owned; never represent Pi-only profile controls as enforced for an external CLI.",
|
|
1911
1828
|
],
|
|
1912
1829
|
parameters: Type.Object({
|
|
1913
1830
|
action: Type.Optional(Type.Union([
|
|
@@ -1930,10 +1847,8 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1930
1847
|
Type.Literal("show_template"),
|
|
1931
1848
|
Type.Literal("stop_job"),
|
|
1932
1849
|
Type.Literal("stop_session"),
|
|
1933
|
-
Type.Literal("grant_dispatch"),
|
|
1934
|
-
Type.Literal("revoke_grant"),
|
|
1935
1850
|
], {
|
|
1936
|
-
description: "status, setup_help, guard, notify, set_status, clear_status, workspace_plan, launch_workspace, fire_task, send_followup, dismiss, job_status, list_jobs, set_variable, list_variables, list_templates, show_template, stop_job,
|
|
1851
|
+
description: "status, setup_help, guard, notify, set_status, clear_status, workspace_plan, launch_workspace, fire_task, send_followup, dismiss, job_status, list_jobs, set_variable, list_variables, list_templates, show_template, stop_job, or stop_session. Default status.",
|
|
1937
1852
|
})),
|
|
1938
1853
|
title: Type.Optional(Type.String({ description: "Notification title or workspace title context." })),
|
|
1939
1854
|
body: Type.Optional(Type.String({ description: "Notification body/status text, or fallback task text for fire_task." })),
|
|
@@ -1957,7 +1872,10 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1957
1872
|
description: "Fire-and-forget worker objective. Required for fire_task unless body is provided. Also used as the follow-up objective for send_followup.",
|
|
1958
1873
|
})),
|
|
1959
1874
|
jobId: Type.Optional(Type.String({
|
|
1960
|
-
description: "
|
|
1875
|
+
description: "Existing job id for job_status/list_variables/set_variable/send_followup/dismiss. Never creates a fresh fire_task job.",
|
|
1876
|
+
})),
|
|
1877
|
+
launchKey: Type.Optional(Type.String({
|
|
1878
|
+
description: "Optional fresh fire_task identity. Use only for intentional stable naming; omitted creates a unique key. Never auto-retained from results.",
|
|
1961
1879
|
})),
|
|
1962
1880
|
agentId: Type.Optional(Type.String({
|
|
1963
1881
|
description: "Target agent id for send_followup. Defaults to the job's first (primary) agent. Multi-agent-per-turn follow-up is not supported; only one agent's pane receives each follow-up.",
|
|
@@ -1967,7 +1885,7 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1967
1885
|
})),
|
|
1968
1886
|
variableValue: Type.Optional(Type.String({ description: "For set_variable: value to write. body/status can also supply the value." })),
|
|
1969
1887
|
deadlineSeconds: Type.Optional(Type.Number({
|
|
1970
|
-
description: "For fire_task: seconds before each event-driven
|
|
1888
|
+
description: "For fire_task: seconds before each event-driven watcher records timeout and terminates its owned pane. Default 1200.",
|
|
1971
1889
|
minimum: 5,
|
|
1972
1890
|
maximum: MAX_DEADLINE_SECONDS,
|
|
1973
1891
|
})),
|
|
@@ -1975,43 +1893,33 @@ export default function tmuxAgentManagerExtension(pi) {
|
|
|
1975
1893
|
provider: Type.Optional(providerSchema()),
|
|
1976
1894
|
name: Type.Optional(Type.String({ description: "Pane/worker name." })),
|
|
1977
1895
|
command: Type.Optional(Type.String({
|
|
1978
|
-
description: "Interactive CLI start command
|
|
1896
|
+
description: "Interactive CLI start command. Capture is armed first, then the initial prompt file is passed as the final CLI argument; Agy also gets --prompt-interactive.",
|
|
1897
|
+
})),
|
|
1898
|
+
cwd: Type.Optional(Type.String({
|
|
1899
|
+
description: "Legacy per-agent working directory; does not narrow filesystem scope.",
|
|
1900
|
+
})),
|
|
1901
|
+
path: Type.Optional(Type.String({
|
|
1902
|
+
description: "Optional workspace cwd. For Pi it narrows structured file tools; external CLIs remain machine-wide host-trusted processes.",
|
|
1979
1903
|
})),
|
|
1980
|
-
|
|
1904
|
+
tools: Type.Optional(Type.Array(Type.String(), {
|
|
1905
|
+
description: "Pi-only policy-owned tool override. Omitted inherits the parent's eligible worker tools; parent-only controls are removed.",
|
|
1906
|
+
minItems: 1,
|
|
1907
|
+
})),
|
|
1908
|
+
resourceProfile: Type.Optional(Type.String({
|
|
1909
|
+
description: "Pi-only resource-profile override. Omitted inherits the parent's effective resources.",
|
|
1910
|
+
})),
|
|
1911
|
+
thinkingLevel: Type.Optional(orchestrationThinkingLevelSchema("Pi-only thinking override. Omitted inherits the orchestrator's current level.")),
|
|
1912
|
+
worktreeLane: Type.Optional(Type.String({ description: "Pi-only worktree-sync lane binding for this worker." })),
|
|
1981
1913
|
}), { description: "Agents/providers to lay out in tmux. Default: pi, agy, codex." })),
|
|
1982
1914
|
dryRun: Type.Optional(Type.Boolean({
|
|
1983
1915
|
description: "Preview only when true. Launch actions run by default; stop actions remain previews by default.",
|
|
1984
1916
|
})),
|
|
1985
1917
|
force: Type.Optional(Type.Boolean({
|
|
1986
|
-
description: "For fire_task only: archive an existing job directory with the same
|
|
1918
|
+
description: "For fire_task only: archive an existing job directory with the same launchKey before launch. Existing tmux sessions are still refused; stop them first.",
|
|
1987
1919
|
})),
|
|
1988
1920
|
confirm: Type.Optional(Type.String({ description: "Required for real stop_job/stop_session: yes-tmux-stop." })),
|
|
1989
1921
|
goalId: Type.Optional(Type.String({
|
|
1990
|
-
description: "Goal this
|
|
1991
|
-
})),
|
|
1992
|
-
agent: Type.Optional(providerSchema("grant_dispatch: the provider this standing grant authorizes.")),
|
|
1993
|
-
allowedTools: Type.Optional(Type.Array(Type.String(), {
|
|
1994
|
-
description: "grant_dispatch: tool allowlist pushed into the child pi's own --tools flag. Defaults to a read-biased safe set (read, grep, find, ls) when omitted.",
|
|
1995
|
-
})),
|
|
1996
|
-
resourceProfile: Type.Optional(Type.String({
|
|
1997
|
-
description: "grant_dispatch: resource profile name pushed into the child pi's own --resource-profile flag. When omitted, the child launches with --no-extensions --no-skills instead.",
|
|
1998
|
-
})),
|
|
1999
|
-
writePaths: Type.Optional(Type.Array(Type.String(), {
|
|
2000
|
-
description: "grant_dispatch: write paths named in the child's scoped --append-system-prompt role text.",
|
|
2001
|
-
})),
|
|
2002
|
-
maxLaunches: Type.Optional(Type.Number({
|
|
2003
|
-
description: "grant_dispatch: number of real fire_task/send_followup dispatches this grant authorizes.",
|
|
2004
|
-
minimum: 1,
|
|
2005
|
-
})),
|
|
2006
|
-
expiresInMinutes: Type.Optional(Type.Number({
|
|
2007
|
-
description: "grant_dispatch: grant validity window from creation. Omit for a grant that never expires (until revoke_grant).",
|
|
2008
|
-
minimum: 1,
|
|
2009
|
-
})),
|
|
2010
|
-
maxUsdAdvisory: Type.Optional(Type.Number({
|
|
2011
|
-
description: "grant_dispatch: advisory USD ceiling. Never enforced across the process boundary (the child bills under its own auth) — a claim to review only.",
|
|
2012
|
-
})),
|
|
2013
|
-
grantId: Type.Optional(Type.String({
|
|
2014
|
-
description: "revoke_grant: grant id to revoke. Omit to revoke whichever grant is currently active.",
|
|
1922
|
+
description: "Goal this fire_task/send_followup belongs to for durable lane ownership and recovery.",
|
|
2015
1923
|
})),
|
|
2016
1924
|
}),
|
|
2017
1925
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|