@caupulican/pi-adaptative 0.94.0 → 0.95.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 +55 -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 +173 -293
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +247 -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/skills/pi-harness-learning/SKILL.md +13 -7
- package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +11 -11
- package/dist/core/agent-paths.d.ts +2 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +7 -0
- package/dist/core/agent-paths.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 +98 -161
- 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 +9 -1
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +76 -6
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/background-tool-task-controller.d.ts +23 -2
- package/dist/core/background-tool-task-controller.d.ts.map +1 -1
- package/dist/core/background-tool-task-controller.js +132 -17
- package/dist/core/background-tool-task-controller.js.map +1 -1
- package/dist/core/context/long-term-memory-trigger.d.ts +3 -1
- package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -1
- package/dist/core/context/long-term-memory-trigger.js +1 -1
- package/dist/core/context/long-term-memory-trigger.js.map +1 -1
- package/dist/core/context/okf-memory-provider.d.ts +3 -0
- package/dist/core/context/okf-memory-provider.d.ts.map +1 -1
- package/dist/core/context/okf-memory-provider.js +66 -31
- package/dist/core/context/okf-memory-provider.js.map +1 -1
- package/dist/core/context/okf-memory.d.ts +18 -1
- package/dist/core/context/okf-memory.d.ts.map +1 -1
- package/dist/core/context/okf-memory.js +48 -0
- package/dist/core/context/okf-memory.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-agent-control.d.ts +4 -0
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.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-notification-coordinator.d.ts +10 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.js +69 -3
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
- 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/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 +31 -0
- package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
- package/dist/core/foreground-terminal-handoff-controller.js +239 -55
- 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/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/learning-audit.d.ts +5 -1
- package/dist/core/learning/learning-audit.d.ts.map +1 -1
- package/dist/core/learning/learning-audit.js +32 -1
- package/dist/core/learning/learning-audit.js.map +1 -1
- package/dist/core/learning/reflection-engine.d.ts +24 -0
- package/dist/core/learning/reflection-engine.d.ts.map +1 -1
- package/dist/core/learning/reflection-engine.js +104 -8
- 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/okf-project-memory-store.d.ts +28 -0
- package/dist/core/memory/okf-project-memory-store.d.ts.map +1 -0
- package/dist/core/memory/okf-project-memory-store.js +111 -0
- package/dist/core/memory/okf-project-memory-store.js.map +1 -0
- package/dist/core/memory/providers/file-store.d.ts +44 -1
- package/dist/core/memory/providers/file-store.d.ts.map +1 -1
- package/dist/core/memory/providers/file-store.js +341 -24
- 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/memory-controller.d.ts +10 -1
- package/dist/core/memory-controller.d.ts.map +1 -1
- package/dist/core/memory-controller.js +72 -8
- package/dist/core/memory-controller.js.map +1 -1
- 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/provider-prompt-contracts.d.ts +1 -4
- package/dist/core/provider-prompt-contracts.d.ts.map +1 -1
- package/dist/core/provider-prompt-contracts.js +13 -29
- 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 +63 -27
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +300 -132
- package/dist/core/reflection-controller.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 +13 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +53 -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 +2 -0
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.js +52 -12
- package/dist/core/system-prompt-builder.js.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- 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 +3 -1
- package/dist/core/tools/delegate-status.d.ts.map +1 -1
- package/dist/core/tools/delegate-status.js +5 -1
- package/dist/core/tools/delegate-status.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +4 -13
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +173 -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 +24 -7
- package/dist/core/tools/goal.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.js +1 -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/tools/tool-task.d.ts +1 -0
- package/dist/core/tools/tool-task.d.ts.map +1 -1
- package/dist/core/tools/tool-task.js +13 -8
- package/dist/core/tools/tool-task.js.map +1 -1
- package/dist/core/util/atomic-file.d.ts +2 -0
- package/dist/core/util/atomic-file.d.ts.map +1 -1
- package/dist/core/util/atomic-file.js +4 -0
- package/dist/core/util/atomic-file.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 +32 -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/activity-lane.d.ts.map +1 -1
- package/dist/modes/interactive/components/activity-lane.js +12 -23
- package/dist/modes/interactive/components/activity-lane.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +142 -1
- 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/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 +8 -0
- package/docs/harness-architecture.md +28 -32
- package/docs/index.md +1 -1
- package/docs/process-matrix.md +8 -6
- package/docs/resources.md +1 -1
- package/docs/settings.md +61 -51
- package/docs/tmux-agent-manager.md +47 -40
- package/docs/usage.md +1 -1
- 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
|
@@ -7,34 +7,24 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import {
|
|
8
8
|
type ExtensionAPI,
|
|
9
9
|
type ExtensionContext,
|
|
10
|
+
encodeWorkerSessionAllowedPaths,
|
|
10
11
|
getProcessWorkRun,
|
|
12
|
+
orchestrationThinkingLevelSchema,
|
|
11
13
|
PI_ORCHESTRATION_AGENT_ID_ENV,
|
|
14
|
+
PI_WORKER_ALLOWED_PATHS_ENV,
|
|
12
15
|
} from "@caupulican/pi-adaptative";
|
|
13
|
-
import type { AgentToolResult } from "@caupulican/pi-agent-core";
|
|
16
|
+
import type { AgentToolResult, ThinkingLevel } from "@caupulican/pi-agent-core";
|
|
14
17
|
import type { Usage } from "@caupulican/pi-ai";
|
|
15
18
|
import { Type } from "typebox";
|
|
16
19
|
import {
|
|
17
|
-
buildGrant,
|
|
18
|
-
buildGrantUsageEntry,
|
|
19
20
|
buildLaunchProfileFlags,
|
|
20
|
-
|
|
21
|
-
countGrantUsages,
|
|
22
|
-
DEFAULT_READ_BIASED_TOOLS,
|
|
21
|
+
DEFAULT_MANAGED_WORKER_TOOLS,
|
|
23
22
|
decodeTmuxWorkerUsageClaim,
|
|
24
|
-
|
|
25
|
-
GRANT_CUSTOM_TYPE,
|
|
26
|
-
GRANT_USAGE_CUSTOM_TYPE,
|
|
27
|
-
type GrantDispatchParams,
|
|
28
|
-
grantCovers,
|
|
29
|
-
isTmuxDispatchGrant,
|
|
30
|
-
isTmuxDispatchGrantTombstone,
|
|
23
|
+
deriveWorkerLaunchProfile,
|
|
31
24
|
type LaunchProfileFlag,
|
|
32
|
-
type LaunchProfileSource,
|
|
33
|
-
launchProfileSourceFromGrant,
|
|
34
|
-
ONE_SHOT_LAUNCH_PROFILE_SOURCE,
|
|
35
25
|
type Provider,
|
|
36
|
-
type
|
|
37
|
-
} from "./
|
|
26
|
+
type WorkerLaunchProfile,
|
|
27
|
+
} from "./launch-profile.ts";
|
|
38
28
|
|
|
39
29
|
export const piConfig = { tools: ["tmux_agent_manager"] };
|
|
40
30
|
|
|
@@ -57,22 +47,23 @@ type Action =
|
|
|
57
47
|
| "list_templates"
|
|
58
48
|
| "show_template"
|
|
59
49
|
| "stop_job"
|
|
60
|
-
| "stop_session"
|
|
61
|
-
| "grant_dispatch"
|
|
62
|
-
| "revoke_grant";
|
|
50
|
+
| "stop_session";
|
|
63
51
|
|
|
64
52
|
type AgentSpec = {
|
|
65
53
|
provider?: Provider;
|
|
66
54
|
name?: string;
|
|
67
55
|
command?: string;
|
|
56
|
+
/** Optional workspace. Sets cwd; narrows Pi structured file tools but not external host processes. */
|
|
57
|
+
path?: string;
|
|
58
|
+
/** Legacy process cwd override. Does not narrow the managed profile. */
|
|
68
59
|
cwd?: string;
|
|
69
60
|
tools?: string[];
|
|
61
|
+
resourceProfile?: string;
|
|
62
|
+
thinkingLevel?: ThinkingLevel;
|
|
70
63
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* appended and one extra lane-doctrine sentence in its scoped `--append-system-prompt` -- see
|
|
75
|
-
* `applyLaunchProfile`/`dispatch-grant.ts`'s `buildLaunchProfileFlags`.
|
|
64
|
+
* Pi-only worktree-sync lane binding. Goal dispatch supplies this automatically; an explicit
|
|
65
|
+
* fire_task profile may also narrow a Pi worker to a named lane. The launch adds
|
|
66
|
+
* `--worktree-lane <key>` and the lane doctrine through the immutable profile.
|
|
76
67
|
*/
|
|
77
68
|
worktreeLane?: string;
|
|
78
69
|
};
|
|
@@ -105,26 +96,12 @@ type Params = {
|
|
|
105
96
|
dryRun?: boolean;
|
|
106
97
|
force?: boolean;
|
|
107
98
|
confirm?: string;
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
99
|
+
/** Non-retained launch identity for a fresh fire_task. Unlike jobId, extension identity memory
|
|
100
|
+
* never auto-fills this field from an earlier result. */
|
|
101
|
+
launchKey?: string;
|
|
102
|
+
/** Goal this dispatch is scoped to. Goal-bound dispatch wires this through to managed-lane
|
|
103
|
+
* ownership and the child process's task-recovery fence. */
|
|
110
104
|
goalId?: string;
|
|
111
|
-
/** grant_dispatch: target agent for the standing grant. revoke_grant: unused (the active grant's own
|
|
112
|
-
* agent is used for its tombstone; this field is not required to revoke). */
|
|
113
|
-
agent?: Provider;
|
|
114
|
-
/** grant_dispatch: allowlisted tool names pushed into the child `pi`'s own --tools flag. */
|
|
115
|
-
allowedTools?: string[];
|
|
116
|
-
/** grant_dispatch: resource profile name pushed into the child `pi`'s own --resource-profile flag. */
|
|
117
|
-
resourceProfile?: string;
|
|
118
|
-
/** grant_dispatch: write paths the grant's scoped system prompt tells the child worker to stay within. */
|
|
119
|
-
writePaths?: string[];
|
|
120
|
-
/** grant_dispatch: number of real launches (fire_task/send_followup dispatches) this grant authorizes. */
|
|
121
|
-
maxLaunches?: number;
|
|
122
|
-
/** grant_dispatch: grant validity window from creation time. Omit for a grant that never expires (until revoke_grant). */
|
|
123
|
-
expiresInMinutes?: number;
|
|
124
|
-
/** grant_dispatch: advisory USD ceiling. Never enforced across the process boundary — a claim to review only. */
|
|
125
|
-
maxUsdAdvisory?: number;
|
|
126
|
-
/** revoke_grant: grantId to revoke. Optional — omitted means "revoke whichever grant is currently active". */
|
|
127
|
-
grantId?: string;
|
|
128
105
|
};
|
|
129
106
|
|
|
130
107
|
type RunResult = { ok: boolean; status: number | null; stdout: string; stderr: string; error?: string; args: string[] };
|
|
@@ -158,6 +135,8 @@ type FireAgentPlan = {
|
|
|
158
135
|
resultPath: string;
|
|
159
136
|
paneId?: string;
|
|
160
137
|
result?: AgentResult;
|
|
138
|
+
resourceProfile?: string;
|
|
139
|
+
thinkingLevel?: ThinkingLevel;
|
|
161
140
|
/** Last turn dispatched to this agent's pane via fire_task (1) or send_followup (>=2). Absent means 1. */
|
|
162
141
|
currentTurn?: number;
|
|
163
142
|
/** Last turn whose terminal handoff was delivered to the parent. Absent means 0 (never notified). */
|
|
@@ -169,11 +148,11 @@ type FireAgentPlan = {
|
|
|
169
148
|
* flags) and the dispatch-phase `reportManagedLane` call can both read it after `buildFireTaskPlan`. */
|
|
170
149
|
worktreeLane?: string;
|
|
171
150
|
/** Durable host-lifecycle metadata for the current turn. */
|
|
151
|
+
dispatchProfileId?: string;
|
|
172
152
|
dispatchAuthorizationId?: string;
|
|
173
|
-
dispatchAuthorizationKind?: "
|
|
153
|
+
dispatchAuthorizationKind?: "profile-derived" | "legacy-recovery";
|
|
174
154
|
dispatchAllowedTools?: string[];
|
|
175
155
|
dispatchWritePaths?: string[];
|
|
176
|
-
dispatchMaxCostUsd?: number;
|
|
177
156
|
dispatchInstructions?: string;
|
|
178
157
|
dispatchGoalId?: string;
|
|
179
158
|
dispatchLeaseTtlMs?: number;
|
|
@@ -200,7 +179,7 @@ type FireTaskPlan = {
|
|
|
200
179
|
currentTurn?: number;
|
|
201
180
|
/** Set when session-level reconcile finds the tmux session gone while the job was not terminal. Informational only; nothing is ever killed to produce this state. */
|
|
202
181
|
orphanedAt?: string;
|
|
203
|
-
/** Set by action=dismiss
|
|
182
|
+
/** Set by action=dismiss after its pane watchers are detached. The tmux session is left running. */
|
|
204
183
|
dismissedAt?: string;
|
|
205
184
|
};
|
|
206
185
|
/** The subset of FireTaskPlan the pane watcher generator actually reads — narrow on purpose so a
|
|
@@ -213,10 +192,9 @@ type ManagedLaneDispatchBridge = {
|
|
|
213
192
|
profileId: string;
|
|
214
193
|
provider: string;
|
|
215
194
|
authorizationId: string;
|
|
216
|
-
authorizationKind: "
|
|
195
|
+
authorizationKind: "profile-derived" | "legacy-recovery";
|
|
217
196
|
allowedTools: readonly string[];
|
|
218
197
|
writePaths: readonly string[];
|
|
219
|
-
maxCostUsd?: number;
|
|
220
198
|
leaseTtlMs: number;
|
|
221
199
|
};
|
|
222
200
|
type ManagedLaneBridgeEventBase = {
|
|
@@ -238,19 +216,14 @@ type ManagedLaneBridgeEvent =
|
|
|
238
216
|
* exercises. Production hosts provide `reportManagedLane`, making its pre-launch durable reservation a
|
|
239
217
|
* hard gate. The optional shape only preserves standalone extension-test hosts; without the bridge,
|
|
240
218
|
* lifecycle durability is explicitly unavailable. `reportSpawnedUsage` remains an advisory claim sink.
|
|
241
|
-
* `
|
|
242
|
-
*
|
|
243
|
-
* optional-chained too, while `appendEntry`/`getFlag` are asserted present at the point a grant action
|
|
244
|
-
* actually needs them (a host that can't persist a grant should fail that action clearly, not pretend). */
|
|
219
|
+
* `getActiveTools` supplies the parent's current tool surface so a new worker profile can inherit its
|
|
220
|
+
* policy-owned worker-compatible subset without asking the model to reconstruct it. */
|
|
245
221
|
type HostBridge = {
|
|
246
222
|
reportManagedLane?: (event: ManagedLaneBridgeEvent) => void;
|
|
247
223
|
reportSpawnedUsage?: (usage: Usage, opts?: { label?: string; sourceSessionId?: string; reportId?: string }) => void;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
name: string,
|
|
252
|
-
options: { description?: string; type: "boolean" | "string"; default?: boolean | string },
|
|
253
|
-
) => void;
|
|
224
|
+
getActiveTools?: () => string[];
|
|
225
|
+
getThinkingLevel?: () => ThinkingLevel;
|
|
226
|
+
getEffectiveResourceProfile?: ExtensionAPI["getEffectiveResourceProfile"];
|
|
254
227
|
};
|
|
255
228
|
function agentLaneId(jobId: string, agentId: string): string {
|
|
256
229
|
return `tmux:${jobId}:${agentId}`;
|
|
@@ -263,50 +236,33 @@ function managedDispatch(job: FireTaskPlan, agent: FireAgentPlan): ManagedLaneDi
|
|
|
263
236
|
return {
|
|
264
237
|
sequence,
|
|
265
238
|
instructions,
|
|
266
|
-
profileId: `tmux:${agent.provider}`,
|
|
239
|
+
profileId: agent.dispatchProfileId ?? `tmux:${agent.provider}`,
|
|
267
240
|
provider: agent.provider,
|
|
268
241
|
authorizationId,
|
|
269
242
|
authorizationKind: agent.dispatchAuthorizationKind ?? "legacy-recovery",
|
|
270
|
-
allowedTools: agent.dispatchAllowedTools ??
|
|
243
|
+
allowedTools: agent.dispatchAllowedTools ?? DEFAULT_MANAGED_WORKER_TOOLS,
|
|
271
244
|
writePaths: agent.dispatchWritePaths ?? [],
|
|
272
|
-
...(agent.dispatchMaxCostUsd !== undefined ? { maxCostUsd: agent.dispatchMaxCostUsd } : {}),
|
|
273
245
|
leaseTtlMs: agent.dispatchLeaseTtlMs ?? job.deadlineSeconds * 1_000,
|
|
274
246
|
};
|
|
275
247
|
}
|
|
276
248
|
|
|
277
|
-
function
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const launchSource = grant ? launchProfileSourceFromGrant(grant) : ONE_SHOT_LAUNCH_PROFILE_SOURCE;
|
|
289
|
-
return {
|
|
290
|
-
authorizationId: grant?.grantId ?? fallbackId,
|
|
291
|
-
authorizationKind: grant ? "standing-grant" : "one-shot-owner-approval",
|
|
292
|
-
launchSource,
|
|
293
|
-
allowedTools: [...(launchSource.allowedTools?.length ? launchSource.allowedTools : DEFAULT_READ_BIASED_TOOLS)],
|
|
294
|
-
writePaths: [...(launchSource.writePaths ?? [])],
|
|
295
|
-
...(grant?.budget.maxUsdAdvisory !== undefined ? { maxCostUsd: grant.budget.maxUsdAdvisory } : {}),
|
|
296
|
-
};
|
|
249
|
+
function applyManagedProfile(
|
|
250
|
+
agent: FireAgentPlan,
|
|
251
|
+
profile: WorkerLaunchProfile,
|
|
252
|
+
args: { instructions: string; goalId?: string; leaseTtlMs: number },
|
|
253
|
+
): void {
|
|
254
|
+
agent.dispatchProfileId = profile.identity;
|
|
255
|
+
agent.dispatchAuthorizationId = profile.identity;
|
|
256
|
+
agent.dispatchAuthorizationKind = "profile-derived";
|
|
257
|
+
agent.dispatchAllowedTools = [...profile.allowedTools];
|
|
258
|
+
agent.dispatchWritePaths = [...profile.writePaths];
|
|
259
|
+
applyManagedTurn(agent, args);
|
|
297
260
|
}
|
|
298
261
|
|
|
299
|
-
function
|
|
262
|
+
function applyManagedTurn(
|
|
300
263
|
agent: FireAgentPlan,
|
|
301
|
-
authorization: ReturnType<typeof managedAuthorization>,
|
|
302
264
|
args: { instructions: string; goalId?: string; leaseTtlMs: number },
|
|
303
265
|
): void {
|
|
304
|
-
agent.dispatchAuthorizationId = authorization.authorizationId;
|
|
305
|
-
agent.dispatchAuthorizationKind = authorization.authorizationKind;
|
|
306
|
-
agent.dispatchAllowedTools = [...authorization.allowedTools];
|
|
307
|
-
agent.dispatchWritePaths = [...authorization.writePaths];
|
|
308
|
-
if (authorization.maxCostUsd !== undefined) agent.dispatchMaxCostUsd = authorization.maxCostUsd;
|
|
309
|
-
else delete agent.dispatchMaxCostUsd;
|
|
310
266
|
agent.dispatchInstructions = args.instructions;
|
|
311
267
|
agent.dispatchGoalId = args.goalId;
|
|
312
268
|
agent.dispatchLeaseTtlMs = args.leaseTtlMs;
|
|
@@ -369,13 +325,6 @@ function reportManagedAgentFailure(bridge: HostBridge, jobId: string, agentId: s
|
|
|
369
325
|
}
|
|
370
326
|
}
|
|
371
327
|
|
|
372
|
-
function sameStringValues(left: readonly string[], right: readonly string[]): boolean {
|
|
373
|
-
if (left.length !== right.length) return false;
|
|
374
|
-
const sortedLeft = [...left].sort();
|
|
375
|
-
const sortedRight = [...right].sort();
|
|
376
|
-
return sortedLeft.every((value, index) => value === sortedRight[index]);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
328
|
const EXTENSION_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
380
329
|
export function getTmuxAgentManagerDataRoot(): string {
|
|
381
330
|
const agentDir =
|
|
@@ -433,7 +382,7 @@ const TEAM_TEMPLATES: TeamTemplate[] = [
|
|
|
433
382
|
],
|
|
434
383
|
notes: [
|
|
435
384
|
"Use only when model-token/auth validation is intended.",
|
|
436
|
-
"The manager
|
|
385
|
+
"The manager arms pane capture, starts each CLI with its task, and watches DONE/BLOCKED markers.",
|
|
437
386
|
],
|
|
438
387
|
},
|
|
439
388
|
{
|
|
@@ -464,7 +413,7 @@ const TEAM_TEMPLATES: TeamTemplate[] = [
|
|
|
464
413
|
{ provider: "pi", name: "coordinator" },
|
|
465
414
|
],
|
|
466
415
|
notes: [
|
|
467
|
-
"Use for
|
|
416
|
+
"Use for implementation/QA batches; never claim complete until result files and validation evidence are inspected.",
|
|
468
417
|
],
|
|
469
418
|
},
|
|
470
419
|
];
|
|
@@ -592,7 +541,7 @@ function formatDetection(d: TmuxDetection): string {
|
|
|
592
541
|
}
|
|
593
542
|
function setupHelp(d: TmuxDetection): string {
|
|
594
543
|
if (d.cliAvailable)
|
|
595
|
-
return `tmux CLI available: ${d.tmuxBin || "tmux"}\nUse native delegate for ordinary subagents. Use tmux_agent_manager
|
|
544
|
+
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.`;
|
|
596
545
|
return [
|
|
597
546
|
"tmux CLI not found.",
|
|
598
547
|
"Ordinary subagents remain available through native delegate; do not abort delegation.",
|
|
@@ -614,10 +563,10 @@ async function guardTmux(_ctx: ExtensionContext, detection: TmuxDetection, purpo
|
|
|
614
563
|
}
|
|
615
564
|
function tmuxManagerInstructions(toolName: string, detection: TmuxDetection): string {
|
|
616
565
|
return [
|
|
617
|
-
`tmux is available (${detection.version || detection.tmuxBin || "tmux"}). Native delegate remains the standard subagent route. Use tmux_agent_manager
|
|
566
|
+
`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.`,
|
|
618
567
|
"Suggested next call:",
|
|
619
568
|
'tmux_agent_manager({ action: "fire_task", teamTemplate: "builder-validator", task: "<objective>" })',
|
|
620
|
-
"The launch returns
|
|
569
|
+
"The launch returns after pane capture is armed and each provider command receives its initial task.",
|
|
621
570
|
"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.",
|
|
622
571
|
].join("\n");
|
|
623
572
|
}
|
|
@@ -710,11 +659,26 @@ function normalizeAgents(params: Params): AgentSpec[] {
|
|
|
710
659
|
if (!Object.hasOwn(PROVIDER_COMMANDS, provider)) throw new Error(`unsupported provider: ${provider}`);
|
|
711
660
|
const name =
|
|
712
661
|
firstString(agent.name, provider === "custom" ? `custom-${index + 1}` : provider) || `agent-${index + 1}`;
|
|
713
|
-
const
|
|
714
|
-
if (!
|
|
662
|
+
const command = firstString(agent.command, PROVIDER_COMMANDS[provider]);
|
|
663
|
+
if (!command) throw new Error(`agent ${name} needs command when provider=custom`);
|
|
715
664
|
const tools = Array.isArray(agent.tools) && agent.tools.length > 0 ? agent.tools : undefined;
|
|
716
|
-
const
|
|
717
|
-
|
|
665
|
+
const resourceProfile = firstString(agent.resourceProfile);
|
|
666
|
+
if (provider !== "pi" && (tools || resourceProfile || agent.thinkingLevel || agent.worktreeLane)) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
`agent ${name}: tools, resourceProfile, thinkingLevel, and worktreeLane overrides are Pi-only; configure ${provider}'s native CLI in command instead`,
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
provider,
|
|
673
|
+
name,
|
|
674
|
+
command,
|
|
675
|
+
path: firstString(agent.path),
|
|
676
|
+
cwd: agent.cwd,
|
|
677
|
+
tools,
|
|
678
|
+
resourceProfile,
|
|
679
|
+
thinkingLevel: agent.thinkingLevel,
|
|
680
|
+
worktreeLane: agent.worktreeLane,
|
|
681
|
+
};
|
|
718
682
|
});
|
|
719
683
|
}
|
|
720
684
|
function defaultProviderInvocation(provider: Provider): string {
|
|
@@ -736,6 +700,13 @@ function defaultProviderInvocation(provider: Provider): string {
|
|
|
736
700
|
function interactivePromptText(prompt: string): string {
|
|
737
701
|
return prompt.replace(/\r?\n/g, "\\n");
|
|
738
702
|
}
|
|
703
|
+
function initialPromptInvocation(command: string, provider: Provider, promptPath: string): string {
|
|
704
|
+
const promptArgument = `"$(cat ${quoteShell(promptPath)})"`;
|
|
705
|
+
if (provider === "agy" && !/(?:^|\s)(?:-i|--prompt-interactive)(?:\s|$)/.test(command)) {
|
|
706
|
+
return `${command} --prompt-interactive ${promptArgument}`;
|
|
707
|
+
}
|
|
708
|
+
return `${command} ${promptArgument}`;
|
|
709
|
+
}
|
|
739
710
|
function spellMarker(marker: string): string {
|
|
740
711
|
return marker.split("").join(" ");
|
|
741
712
|
}
|
|
@@ -758,7 +729,7 @@ function managedPrompt(
|
|
|
758
729
|
"Rules:",
|
|
759
730
|
"- Work autonomously until honest PASS or BLOCKED.",
|
|
760
731
|
"- Verify important claims before PASS; include concise evidence.",
|
|
761
|
-
"-
|
|
732
|
+
"- Use the assigned profile without asking for routine permission. Report BLOCKED only when missing external information or an unavailable dependency makes the objective impossible.",
|
|
762
733
|
"- Never print secrets/tokens. Prompts, commands, and logs persist under ~/.pi/agent/work/background/tmux-agent-manager/state/jobs.",
|
|
763
734
|
"- If a task names an external decision variable, read it only at the decision point; never loop or poll the shared JSON file.",
|
|
764
735
|
"",
|
|
@@ -773,7 +744,7 @@ function buildWorkspacePlan(ctx: ExtensionContext, params: Params) {
|
|
|
773
744
|
const cwd = resolveCwd(ctx, params.cwd);
|
|
774
745
|
const agents = normalizeAgents(params).map((agent, index) => ({
|
|
775
746
|
...agent,
|
|
776
|
-
cwd: resolveCwd(ctx, agent.cwd || cwd),
|
|
747
|
+
cwd: resolveCwd(ctx, agent.path || agent.cwd || cwd),
|
|
777
748
|
id: `${safeName(agent.name || agent.provider || "agent")}-${index + 1}`,
|
|
778
749
|
}));
|
|
779
750
|
const workspaceName = firstString(params.workspaceName, `pi-agents-${Date.now().toString(36)}`) || "pi-agents";
|
|
@@ -853,7 +824,12 @@ export function makePaneWatcherScript(job: PaneWatcherJobSpec): string {
|
|
|
853
824
|
" sleep_pid=$!",
|
|
854
825
|
' wait "$sleep_pid" 2>/dev/null || exit 0',
|
|
855
826
|
" sleep_pid=",
|
|
856
|
-
' if [ -e "$stop_request_path" ]; then
|
|
827
|
+
' if [ -e "$stop_request_path" ]; then',
|
|
828
|
+
" finish stopped stop-requested-event",
|
|
829
|
+
" else",
|
|
830
|
+
" finish timeout deadline-event",
|
|
831
|
+
' [ -z "$pane_id" ] || tmux kill-pane -t "$pane_id" >/dev/null 2>&1 || true',
|
|
832
|
+
" fi",
|
|
857
833
|
' kill -TERM "$parent_pid" 2>/dev/null || true',
|
|
858
834
|
") &",
|
|
859
835
|
"timer_pid=$!",
|
|
@@ -897,15 +873,16 @@ function buildFireTaskPlan(ctx: ExtensionContext, params: Params): FireTaskPlan
|
|
|
897
873
|
const task = firstString(params.task, params.body);
|
|
898
874
|
if (!task) throw new Error("fire_task requires task (or body) with the worker objective");
|
|
899
875
|
const cwd = resolveCwd(ctx, params.cwd);
|
|
900
|
-
const id = firstString(params.
|
|
876
|
+
const id = firstString(params.launchKey) || makeJobId();
|
|
901
877
|
if (!/^[a-zA-Z0-9_.:-]{4,80}$/.test(id))
|
|
902
|
-
throw new Error("
|
|
878
|
+
throw new Error("launchKey must be 4-80 chars: letters, numbers, _, ., :, or -");
|
|
903
879
|
const jobDir = path.join(jobsRoot(), id);
|
|
904
880
|
const workspaceName = firstString(params.workspaceName, `Pi job ${id}`) || `Pi job ${id}`;
|
|
905
881
|
const sessionName = safeName(workspaceName, id);
|
|
906
882
|
const rawAgents = normalizeAgents(params);
|
|
907
883
|
const agents: FireAgentPlan[] = rawAgents.map((agent, index) => {
|
|
908
884
|
const provider = agent.provider || "custom";
|
|
885
|
+
const agentCwd = resolveCwd(ctx, agent.path || agent.cwd || cwd);
|
|
909
886
|
const slug = `${safeName(agent.name || provider)}-${index + 1}`;
|
|
910
887
|
const markerBase = `TMUX_${crypto.createHash("sha1").update(`${id}:${slug}`).digest("hex").slice(0, 10).toUpperCase()}`;
|
|
911
888
|
const doneMarker = `${markerBase}_DONE`;
|
|
@@ -915,14 +892,17 @@ function buildFireTaskPlan(ctx: ExtensionContext, params: Params): FireTaskPlan
|
|
|
915
892
|
name: firstString(agent.name, provider) || slug,
|
|
916
893
|
provider,
|
|
917
894
|
command: firstString(agent.command, PROVIDER_COMMANDS[provider]),
|
|
918
|
-
cwd:
|
|
919
|
-
|
|
895
|
+
cwd: agentCwd,
|
|
896
|
+
dispatchAllowedTools: agent.tools ? [...agent.tools] : undefined,
|
|
897
|
+
dispatchWritePaths: agent.path ? [agentCwd] : undefined,
|
|
920
898
|
doneMarker,
|
|
921
899
|
blockedMarker,
|
|
922
900
|
promptPath: path.join(jobDir, `${slug}.prompt.md`),
|
|
923
901
|
logPath: path.join(jobDir, `${slug}.log`),
|
|
924
902
|
resultPath: path.join(jobDir, `${slug}.result.json`),
|
|
925
903
|
worktreeLane: agent.worktreeLane,
|
|
904
|
+
resourceProfile: agent.resourceProfile,
|
|
905
|
+
thinkingLevel: agent.thinkingLevel,
|
|
926
906
|
};
|
|
927
907
|
});
|
|
928
908
|
const job: FireTaskPlan = {
|
|
@@ -949,7 +929,7 @@ function buildFireTaskPlan(ctx: ExtensionContext, params: Params): FireTaskPlan
|
|
|
949
929
|
};
|
|
950
930
|
job.launchCommands = agents.map(
|
|
951
931
|
(agent) =>
|
|
952
|
-
`${agent.command || defaultProviderInvocation(agent.provider)}
|
|
932
|
+
`${agent.command || defaultProviderInvocation(agent.provider)} with initial task from ${path.basename(agent.promptPath)}`,
|
|
953
933
|
);
|
|
954
934
|
return job;
|
|
955
935
|
}
|
|
@@ -964,7 +944,7 @@ function prepareJobDirForLaunch(job: FireTaskPlan, force?: boolean): string | un
|
|
|
964
944
|
if (!fs.existsSync(job.jobDir)) return undefined;
|
|
965
945
|
if (!force)
|
|
966
946
|
throw new Error(
|
|
967
|
-
`tmux job already exists: ${job.id}. Use a new
|
|
947
|
+
`tmux job already exists: ${job.id}. Use a new launchKey, or pass force:true to archive the old job before launching.`,
|
|
968
948
|
);
|
|
969
949
|
return archiveExistingJobDir(job.jobDir);
|
|
970
950
|
}
|
|
@@ -1092,6 +1072,7 @@ function launchTmuxSession(
|
|
|
1092
1072
|
sessionName: string,
|
|
1093
1073
|
cwd: string,
|
|
1094
1074
|
panes: Array<{ title: string; cwd: string; command: string }>,
|
|
1075
|
+
options: { deferCommands?: boolean } = {},
|
|
1095
1076
|
): { runs: RunResult[]; paneIds: string[] } {
|
|
1096
1077
|
if (tmuxSessionExists(sessionName))
|
|
1097
1078
|
throw new Error(
|
|
@@ -1110,7 +1091,7 @@ function launchTmuxSession(
|
|
|
1110
1091
|
try {
|
|
1111
1092
|
const firstPane = create.stdout.trim();
|
|
1112
1093
|
paneIds.push(firstPane);
|
|
1113
|
-
sendCommandToPane(firstPane, first.command);
|
|
1094
|
+
if (!options.deferCommands) sendCommandToPane(firstPane, first.command);
|
|
1114
1095
|
for (const pane of panes.slice(1)) {
|
|
1115
1096
|
const split = runTmux(
|
|
1116
1097
|
["split-window", "-d", "-P", "-F", "#{pane_id}", "-t", `${sessionName}:0`, "-c", pane.cwd || cwd],
|
|
@@ -1121,7 +1102,7 @@ function launchTmuxSession(
|
|
|
1121
1102
|
throw new Error(`tmux split-window failed: ${split.error || split.stderr || `exit ${split.status}`}`);
|
|
1122
1103
|
const paneId = split.stdout.trim();
|
|
1123
1104
|
paneIds.push(paneId);
|
|
1124
|
-
sendCommandToPane(paneId, pane.command);
|
|
1105
|
+
if (!options.deferCommands) sendCommandToPane(paneId, pane.command);
|
|
1125
1106
|
}
|
|
1126
1107
|
runs.push(runTmux(["select-layout", "-t", `${sessionName}:0`, "tiled"], 3_000));
|
|
1127
1108
|
return { runs, paneIds };
|
|
@@ -1411,110 +1392,66 @@ function setVariable(jobId: string, name: string, value: string) {
|
|
|
1411
1392
|
fs.writeFileSync(job.varsPath, `${JSON.stringify(current, null, 2)}\n`, { mode: 0o600 });
|
|
1412
1393
|
return { varsPath: job.varsPath, variables: current.variables, updatedAt: current.updatedAt };
|
|
1413
1394
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
// dispatch-grant.ts; the functions below are the session-access GLUE: read the latest grant custom
|
|
1417
|
-
// entry (skipping past malformed payloads, but stopping DEAD on a tombstone — never resurrecting an
|
|
1418
|
-
// older grant beneath a revocation), count spend, and gate a real launch.
|
|
1419
|
-
// ---------------------------------------------------------------------------
|
|
1420
|
-
|
|
1421
|
-
/** Most recent VALID `tmux-dispatch-grant` entry on the active branch, or undefined when there is
|
|
1422
|
-
* none, it was revoked (a tombstone is a hard stop — never skipped to find an older grant), or every
|
|
1423
|
-
* entry found fails to decode. Mirrors the branch-walk idiom used by goal/task session state. */
|
|
1424
|
-
function resolveLatestGrantEntry(ctx: ExtensionContext): TmuxDispatchGrant | undefined {
|
|
1425
|
-
let fromId: string | undefined;
|
|
1426
|
-
for (;;) {
|
|
1427
|
-
const entry = ctx.sessionManager.getLatestCustomEntryOnBranch(GRANT_CUSTOM_TYPE, fromId);
|
|
1428
|
-
if (!entry) return undefined;
|
|
1429
|
-
if (isTmuxDispatchGrantTombstone(entry.data)) return undefined;
|
|
1430
|
-
if (isTmuxDispatchGrant(entry.data)) return entry.data;
|
|
1431
|
-
if (!entry.parentId) return undefined;
|
|
1432
|
-
fromId = entry.parentId;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
/** Decoded `data` payloads of every custom entry of `customType` on the active branch (root→leaf
|
|
1436
|
-
* order), for spend-counting — a single `getLatestCustomEntryOnBranch` call only ever returns ONE
|
|
1437
|
-
* entry, but budget spend requires ALL of them. */
|
|
1438
|
-
function collectCustomEntryData(ctx: ExtensionContext, customType: string): unknown[] {
|
|
1439
|
-
const result: unknown[] = [];
|
|
1440
|
-
for (const entry of ctx.sessionManager.getBranch()) {
|
|
1441
|
-
const loose = entry as unknown as { type?: string; customType?: string; data?: unknown };
|
|
1442
|
-
if (loose.type === "custom" && loose.customType === customType) result.push(loose.data);
|
|
1443
|
-
}
|
|
1444
|
-
return result;
|
|
1445
|
-
}
|
|
1446
|
-
/** Gate a REAL tmux dispatch (fire_task non-dryRun, send_followup) per the standing-grant doctrine:
|
|
1447
|
-
* a valid covering grant dispatches UNATTENDED (spending one usage); absent that, an
|
|
1448
|
-
* interactive host may approve a ONE-SHOT launch; absent BOTH, this REFUSES — never a silent launch.
|
|
1449
|
-
* Returns the covering grant when one authorized this launch (so the caller can derive launch-profile
|
|
1450
|
-
* flags from its envelope), or `{}` for a one-shot approval (no envelope to derive from — the caller
|
|
1451
|
-
* falls back to the conservative default profile). */
|
|
1452
|
-
async function authorizeLaunch(
|
|
1453
|
-
bridge: HostBridge,
|
|
1454
|
-
ctx: ExtensionContext,
|
|
1455
|
-
request: { agents: readonly Provider[]; goalId?: string; jobId: string; description: string },
|
|
1456
|
-
): Promise<{ grant?: TmuxDispatchGrant }> {
|
|
1457
|
-
if (request.agents.length === 0) throw new Error("tmux dispatch refused: launch has no agents");
|
|
1458
|
-
const grant = resolveLatestGrantEntry(ctx);
|
|
1459
|
-
// A standing grant names one provider. It must cover every child and spend one unit for every
|
|
1460
|
-
// child process; a matching first pane must never authorize an unrestricted mixed team.
|
|
1461
|
-
if (grant && request.agents.every((agent) => grantCovers(grant, { agent, goalId: request.goalId }))) {
|
|
1462
|
-
const used = countGrantUsages(grant.grantId, collectCustomEntryData(ctx, GRANT_USAGE_CUSTOM_TYPE));
|
|
1463
|
-
if (grant.budget.maxLaunches - used >= request.agents.length) {
|
|
1464
|
-
if (!bridge.appendEntry)
|
|
1465
|
-
throw new Error("this host does not support session custom entries; cannot spend the tmux dispatch grant.");
|
|
1466
|
-
for (let index = 0; index < request.agents.length; index++) {
|
|
1467
|
-
bridge.appendEntry(
|
|
1468
|
-
GRANT_USAGE_CUSTOM_TYPE,
|
|
1469
|
-
buildGrantUsageEntry(grant.grantId, `${request.jobId}:${index}`),
|
|
1470
|
-
);
|
|
1471
|
-
}
|
|
1472
|
-
return { grant };
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
if (ctx.hasUI) {
|
|
1476
|
-
const approved = await ctx.ui.confirm(
|
|
1477
|
-
"tmux dispatch approval",
|
|
1478
|
-
[
|
|
1479
|
-
`No standing grant currently authorizes every child of: ${request.description}.`,
|
|
1480
|
-
`Agents: ${request.agents.join(", ")}${request.goalId ? `, goal: ${request.goalId}` : ""}.`,
|
|
1481
|
-
"Approve this ONE-SHOT launch? Run tmux_agent_manager action=grant_dispatch to authorize future launches without this prompt.",
|
|
1482
|
-
].join("\n"),
|
|
1483
|
-
);
|
|
1484
|
-
if (!approved) throw new Error(`tmux dispatch declined by the owner: ${request.description}`);
|
|
1485
|
-
return {};
|
|
1486
|
-
}
|
|
1487
|
-
throw new Error(
|
|
1488
|
-
`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.`,
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
|
-
/** Render grant/one-shot-derived launch-profile flags into a pi child's start command. Values
|
|
1492
|
-
* are shell-quoted; flags are not (they're fixed literals, never user input). */
|
|
1395
|
+
/** Render a host-derived launch profile into a Pi child's start command. Values are shell-quoted;
|
|
1396
|
+
* flags are fixed literals. */
|
|
1493
1397
|
function appendLaunchProfileFlags(command: string, flags: LaunchProfileFlag[]): string {
|
|
1494
1398
|
const rendered = flags.map((flag) =>
|
|
1495
1399
|
flag.value !== undefined ? `${flag.flag} ${quoteShell(flag.value)}` : flag.flag,
|
|
1496
1400
|
);
|
|
1497
1401
|
return [command, ...rendered].join(" ");
|
|
1498
1402
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1403
|
+
function deriveLaunchProfile(
|
|
1404
|
+
bridge: HostBridge,
|
|
1405
|
+
ctx: ExtensionContext,
|
|
1406
|
+
job: FireTaskPlan,
|
|
1407
|
+
agent: FireAgentPlan,
|
|
1408
|
+
goalId?: string,
|
|
1409
|
+
): WorkerLaunchProfile {
|
|
1410
|
+
if (agent.provider !== "pi") {
|
|
1411
|
+
return deriveWorkerLaunchProfile({
|
|
1412
|
+
identity: `tmux-profile:${job.id}:${agent.id}`,
|
|
1413
|
+
// External interactive CLIs expose their own native, unrestricted tool surfaces. Model the
|
|
1414
|
+
// managed boundary honestly as one host-trusted process instead of projecting Pi tool names.
|
|
1415
|
+
allowedTools: ["bash"],
|
|
1416
|
+
writePaths: [],
|
|
1417
|
+
parentPid: process.pid,
|
|
1418
|
+
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1419
|
+
taskRef: goalId,
|
|
1420
|
+
});
|
|
1517
1421
|
}
|
|
1422
|
+
const effectiveResources = agent.resourceProfile ? undefined : bridge.getEffectiveResourceProfile?.();
|
|
1423
|
+
const serializedResources =
|
|
1424
|
+
effectiveResources && Object.keys(effectiveResources).length > 0 ? JSON.stringify(effectiveResources) : undefined;
|
|
1425
|
+
const inheritedResourceProfile = serializedResources
|
|
1426
|
+
? `tmux-inherited-${crypto.createHash("sha256").update(serializedResources).digest("hex").slice(0, 12)}`
|
|
1427
|
+
: undefined;
|
|
1428
|
+
return deriveWorkerLaunchProfile({
|
|
1429
|
+
identity: `tmux-profile:${job.id}:${agent.id}`,
|
|
1430
|
+
inheritedTools: bridge.getActiveTools?.(),
|
|
1431
|
+
allowedTools: agent.dispatchAllowedTools,
|
|
1432
|
+
resourceProfile: agent.resourceProfile ?? inheritedResourceProfile,
|
|
1433
|
+
resourceProfileJson:
|
|
1434
|
+
inheritedResourceProfile && effectiveResources
|
|
1435
|
+
? JSON.stringify({ [inheritedResourceProfile]: effectiveResources })
|
|
1436
|
+
: undefined,
|
|
1437
|
+
writePaths: agent.dispatchWritePaths ?? (agent.worktreeLane ? [agent.cwd] : []),
|
|
1438
|
+
thinkingLevel: agent.thinkingLevel ?? bridge.getThinkingLevel?.(),
|
|
1439
|
+
worktreeLane: agent.worktreeLane,
|
|
1440
|
+
parentPid: process.pid,
|
|
1441
|
+
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1442
|
+
taskRef: goalId,
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/** Apply the already-compiled immutable profile to a Pi child. Other provider CLIs retain their own
|
|
1447
|
+
* native tool surface; the profile remains the host's durable audit contract for every provider. */
|
|
1448
|
+
function applyLaunchProfile(job: FireTaskPlan, agent: FireAgentPlan, profile: WorkerLaunchProfile): void {
|
|
1449
|
+
if (agent.provider !== "pi") return;
|
|
1450
|
+
const profiledCommand = appendLaunchProfileFlags(
|
|
1451
|
+
agent.command || defaultProviderInvocation(agent.provider),
|
|
1452
|
+
buildLaunchProfileFlags(profile),
|
|
1453
|
+
);
|
|
1454
|
+
agent.command = `env ${PI_ORCHESTRATION_AGENT_ID_ENV}=${quoteShell(agentLaneId(job.id, agent.id))} ${PI_WORKER_ALLOWED_PATHS_ENV}=${quoteShell(encodeWorkerSessionAllowedPaths(profile.writePaths))} ${profiledCommand}`;
|
|
1518
1455
|
}
|
|
1519
1456
|
/** Read an OPTIONAL, cooperative worker-reported usage claim for the turn that just went terminal (a
|
|
1520
1457
|
* sibling `<result-path>.usage.json` file next to the watcher's own result file). Absent file ⇒ no
|
|
@@ -1587,62 +1524,6 @@ async function executeTool(
|
|
|
1587
1524
|
details: { action, jobId: params.jobId, variableName: params.variableName, result },
|
|
1588
1525
|
};
|
|
1589
1526
|
}
|
|
1590
|
-
if (action === "grant_dispatch") {
|
|
1591
|
-
if (!params.agent) throw new Error("grant_dispatch requires agent");
|
|
1592
|
-
if (!Object.hasOwn(PROVIDER_COMMANDS, params.agent))
|
|
1593
|
-
throw new Error(`grant_dispatch: unsupported agent: ${params.agent}`);
|
|
1594
|
-
const grantParams: GrantDispatchParams = {
|
|
1595
|
-
agent: params.agent,
|
|
1596
|
-
goalId: params.goalId,
|
|
1597
|
-
allowedTools: params.allowedTools,
|
|
1598
|
-
resourceProfile: params.resourceProfile,
|
|
1599
|
-
writePaths: params.writePaths,
|
|
1600
|
-
maxLaunches: typeof params.maxLaunches === "number" ? params.maxLaunches : Number.NaN,
|
|
1601
|
-
expiresInMinutes: params.expiresInMinutes,
|
|
1602
|
-
maxUsdAdvisory: params.maxUsdAdvisory,
|
|
1603
|
-
};
|
|
1604
|
-
const grant = buildGrant(grantParams);
|
|
1605
|
-
const summary = describeGrant(grant);
|
|
1606
|
-
// OWNER-AUTHORIZED, NEVER SILENT: an interactive host must explicitly confirm the exact
|
|
1607
|
-
// grant details; a non-interactive host (print/rpc, or the unattended goal loop) has no confirm
|
|
1608
|
-
// surface, so it needs an explicit opt-in flag instead — absent either, refuse to create the grant.
|
|
1609
|
-
if (ctx.hasUI) {
|
|
1610
|
-
const approved = await ctx.ui.confirm("Authorize tmux dispatch grant", summary);
|
|
1611
|
-
if (!approved) throw new Error("grant_dispatch was declined by the owner.");
|
|
1612
|
-
} else if (bridge.getFlag?.("allow-tmux-dispatch") !== true) {
|
|
1613
|
-
throw new Error(
|
|
1614
|
-
"grant_dispatch requires interactive approval; no UI is available in this mode. Pass --allow-tmux-dispatch to authorize grant creation non-interactively.",
|
|
1615
|
-
);
|
|
1616
|
-
}
|
|
1617
|
-
if (!bridge.appendEntry)
|
|
1618
|
-
throw new Error("this host does not support session custom entries; cannot persist a tmux dispatch grant.");
|
|
1619
|
-
bridge.appendEntry(GRANT_CUSTOM_TYPE, grant);
|
|
1620
|
-
return {
|
|
1621
|
-
content: [{ type: "text", text: `Created tmux dispatch grant ${grant.grantId}.\n${summary}` }],
|
|
1622
|
-
details: { action, grant },
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
if (action === "revoke_grant") {
|
|
1626
|
-
const activeGrant = resolveLatestGrantEntry(ctx);
|
|
1627
|
-
const targetGrantId = firstString(params.grantId) || activeGrant?.grantId;
|
|
1628
|
-
if (!targetGrantId) throw new Error("revoke_grant: no active tmux dispatch grant to revoke.");
|
|
1629
|
-
if (activeGrant && activeGrant.grantId !== targetGrantId)
|
|
1630
|
-
throw new Error(
|
|
1631
|
-
`revoke_grant: grantId ${targetGrantId} is not the active grant (active: ${activeGrant.grantId}).`,
|
|
1632
|
-
);
|
|
1633
|
-
if (!bridge.appendEntry)
|
|
1634
|
-
throw new Error("this host does not support session custom entries; cannot revoke a tmux dispatch grant.");
|
|
1635
|
-
bridge.appendEntry(GRANT_CUSTOM_TYPE, buildTombstone(targetGrantId));
|
|
1636
|
-
return {
|
|
1637
|
-
content: [
|
|
1638
|
-
{
|
|
1639
|
-
type: "text",
|
|
1640
|
-
text: `Revoked tmux dispatch grant ${targetGrantId}. Future launches need a new grant_dispatch or interactive/opt-in approval.`,
|
|
1641
|
-
},
|
|
1642
|
-
],
|
|
1643
|
-
details: { action, grantId: targetGrantId },
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
1527
|
const guard = await guardTmux(ctx, detection, `tmux ${action}`);
|
|
1647
1528
|
if (!guard.allowed)
|
|
1648
1529
|
return { content: [{ type: "text", text: guard.text }], details: { action, detection, guard, skipped: true } };
|
|
@@ -1707,33 +1588,19 @@ async function executeTool(
|
|
|
1707
1588
|
throw new Error(
|
|
1708
1589
|
`tmux session already exists: ${job.sessionName}. Use stop_job/stop_session first or choose a different workspaceName.`,
|
|
1709
1590
|
);
|
|
1710
|
-
//
|
|
1711
|
-
//
|
|
1712
|
-
const authorization = await authorizeLaunch(bridge, ctx, {
|
|
1713
|
-
agents: job.agents.map((agent) => agent.provider),
|
|
1714
|
-
goalId: params.goalId,
|
|
1715
|
-
jobId: job.id,
|
|
1716
|
-
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(", ")})`,
|
|
1717
|
-
});
|
|
1718
|
-
const managedAuthority = managedAuthorization(authorization.grant, `one-shot:${job.id}:turn:1`);
|
|
1591
|
+
// Compile each immutable worker profile from the parent surface plus per-agent overrides before
|
|
1592
|
+
// any durable reservation, filesystem artifact, pane, watcher, or prompt side effect.
|
|
1719
1593
|
for (const agent of job.agents) {
|
|
1594
|
+
const profile = deriveLaunchProfile(bridge, ctx, job, agent, params.goalId);
|
|
1720
1595
|
agent.currentTurn = 1;
|
|
1721
|
-
|
|
1596
|
+
applyManagedProfile(agent, profile, {
|
|
1722
1597
|
instructions: job.task,
|
|
1723
1598
|
goalId: params.goalId,
|
|
1724
1599
|
leaseTtlMs: job.deadlineSeconds * 1_000,
|
|
1725
1600
|
});
|
|
1601
|
+
applyLaunchProfile(job, agent, profile);
|
|
1726
1602
|
}
|
|
1727
|
-
|
|
1728
|
-
...managedAuthority.launchSource,
|
|
1729
|
-
...(params.goalId ? { taskRef: params.goalId } : {}),
|
|
1730
|
-
// Process-matrix parent identity: the dispatched child self-registers as a worker of THIS
|
|
1731
|
-
// session and winds down gracefully if this session disappears (see dispatch-grant.ts's
|
|
1732
|
-
// `LaunchProfileSource.parentPid`/`parentSession` doc).
|
|
1733
|
-
parentPid: process.pid,
|
|
1734
|
-
parentSession: ctx.sessionManager.getSessionId?.() ?? ctx.sessionManager.getSessionFile(),
|
|
1735
|
-
});
|
|
1736
|
-
// The host commits the full execution grant and starts the durable lease before any child
|
|
1603
|
+
// The host commits the full execution profile and starts the durable lease before any child
|
|
1737
1604
|
// process, job artifact, watcher, or prompt side effect occurs.
|
|
1738
1605
|
reportManagedDispatchReservations(bridge, job, job.agents);
|
|
1739
1606
|
let sessionCreated = false;
|
|
@@ -1744,7 +1611,9 @@ async function executeTool(
|
|
|
1744
1611
|
cwd: agent.cwd,
|
|
1745
1612
|
command: agent.command || defaultProviderInvocation(agent.provider),
|
|
1746
1613
|
}));
|
|
1747
|
-
|
|
1614
|
+
// Create idle panes first. The initial provider task is part of the provider command, but no
|
|
1615
|
+
// command starts until its output watcher owns the pane stream.
|
|
1616
|
+
const launch = launchTmuxSession(job.sessionName, job.cwd, panes, { deferCommands: true });
|
|
1748
1617
|
sessionCreated = true;
|
|
1749
1618
|
job.agents.forEach((agent, index) => {
|
|
1750
1619
|
agent.paneId = launch.paneIds[index];
|
|
@@ -1753,7 +1622,14 @@ async function executeTool(
|
|
|
1753
1622
|
const watcherPanes = startPaneWatchers(job);
|
|
1754
1623
|
for (const agent of job.agents) {
|
|
1755
1624
|
if (!agent.paneId) continue;
|
|
1756
|
-
|
|
1625
|
+
sendCommandToPane(
|
|
1626
|
+
agent.paneId,
|
|
1627
|
+
initialPromptInvocation(
|
|
1628
|
+
agent.command || defaultProviderInvocation(agent.provider),
|
|
1629
|
+
agent.provider,
|
|
1630
|
+
agent.promptPath,
|
|
1631
|
+
),
|
|
1632
|
+
);
|
|
1757
1633
|
}
|
|
1758
1634
|
return {
|
|
1759
1635
|
content: [
|
|
@@ -1831,26 +1707,8 @@ async function executeTool(
|
|
|
1831
1707
|
throw new Error(
|
|
1832
1708
|
`tmux pane ${targetAgent.paneId} for ${targetAgent.name} is gone. The worker cannot receive a follow-up; use fire_task to relaunch.`,
|
|
1833
1709
|
);
|
|
1834
|
-
//
|
|
1835
|
-
//
|
|
1836
|
-
// grant/one-shot authorization is resolved here (no applyLaunchProfile call).
|
|
1837
|
-
const authorization = await authorizeLaunch(bridge, ctx, {
|
|
1838
|
-
agents: [targetAgent.provider],
|
|
1839
|
-
goalId: params.goalId,
|
|
1840
|
-
jobId: `${job.id}:turn:${turn}`,
|
|
1841
|
-
description: `send_followup turn ${turn} to ${targetAgent.name} in job ${job.id}`,
|
|
1842
|
-
});
|
|
1843
|
-
const managedAuthority = managedAuthorization(authorization.grant, `one-shot:${job.id}:turn:${turn}`);
|
|
1844
|
-
const launchAllowedTools = targetAgent.dispatchAllowedTools ?? [...DEFAULT_READ_BIASED_TOOLS];
|
|
1845
|
-
const launchWritePaths = targetAgent.dispatchWritePaths ?? [];
|
|
1846
|
-
if (
|
|
1847
|
-
!sameStringValues(launchAllowedTools, managedAuthority.allowedTools) ||
|
|
1848
|
-
!sameStringValues(launchWritePaths, managedAuthority.writePaths)
|
|
1849
|
-
) {
|
|
1850
|
-
throw new Error(
|
|
1851
|
-
`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.`,
|
|
1852
|
-
);
|
|
1853
|
-
}
|
|
1710
|
+
// The live process keeps the immutable profile persisted at birth. A follow-up only advances
|
|
1711
|
+
// instructions and its fenced sequence; it cannot widen or replace the process profile.
|
|
1854
1712
|
// Reserve before watcher/prompt side effects. Reconcile re-arms a reserved turn but never
|
|
1855
1713
|
// re-injects it, so crash recovery cannot duplicate a provider prompt.
|
|
1856
1714
|
const reserved = persistJobPatch(job.id, (current) => {
|
|
@@ -1859,9 +1717,9 @@ async function executeTool(
|
|
|
1859
1717
|
agent.currentTurn = turn;
|
|
1860
1718
|
agent.pendingTurn = turn;
|
|
1861
1719
|
agent.result = undefined;
|
|
1862
|
-
|
|
1720
|
+
applyManagedTurn(agent, {
|
|
1863
1721
|
instructions: followupTask,
|
|
1864
|
-
goalId: params.goalId,
|
|
1722
|
+
goalId: params.goalId ?? agent.dispatchGoalId,
|
|
1865
1723
|
leaseTtlMs: job.deadlineSeconds * 1_000,
|
|
1866
1724
|
});
|
|
1867
1725
|
current.currentTurn = turn;
|
|
@@ -1914,6 +1772,15 @@ async function executeTool(
|
|
|
1914
1772
|
content: [{ type: "text", text: `tmux job ${job.id} was already dismissed.` }],
|
|
1915
1773
|
details: { action, jobId: job.id, alreadyDismissed: true },
|
|
1916
1774
|
};
|
|
1775
|
+
if (tmuxSessionExists(job.sessionName)) {
|
|
1776
|
+
for (const agent of job.agents) {
|
|
1777
|
+
if (!agent.paneId || !tmuxPaneExists(job.sessionName, agent.paneId)) continue;
|
|
1778
|
+
requireTmuxRun(
|
|
1779
|
+
runTmux(["pipe-pane", "-t", agent.paneId]),
|
|
1780
|
+
`tmux completion watcher detach for ${agent.paneId}`,
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1917
1784
|
persistJobPatch(job.id, (current) => {
|
|
1918
1785
|
current.dismissedAt = new Date().toISOString();
|
|
1919
1786
|
return current;
|
|
@@ -2009,6 +1876,13 @@ function formatFireTaskHandoff(job: FireTaskPlan): string {
|
|
|
2009
1876
|
].join("\n");
|
|
2010
1877
|
}
|
|
2011
1878
|
|
|
1879
|
+
function compactTmuxAgentStatus(status: string | undefined): "succeeded" | "partial" | "failed" | "canceled" {
|
|
1880
|
+
if (status === "done" || status === "succeeded" || status === "completed") return "succeeded";
|
|
1881
|
+
if (status === "blocked" || status === "partial") return "partial";
|
|
1882
|
+
if (status === "canceled" || status === "cancelled" || status === "stopped") return "canceled";
|
|
1883
|
+
return "failed";
|
|
1884
|
+
}
|
|
1885
|
+
|
|
2012
1886
|
/** Diff live tmux sessions against this session's job records. Mirrors the invariant behind
|
|
2013
1887
|
* `LocalRuntimeController.reconcile` (local-runtime-controller.ts): reconcile only ever OBSERVES —
|
|
2014
1888
|
* it never kills a session it did not provably start. A job whose tmux session is gone is marked
|
|
@@ -2067,13 +1941,6 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2067
1941
|
let handoffTail = Promise.resolve();
|
|
2068
1942
|
const jobWatchers = new Map<string, fs.FSWatcher>();
|
|
2069
1943
|
const bridge: HostBridge = pi;
|
|
2070
|
-
// Non-interactive opt-in for grant_dispatch: optional-chained so a lightweight test double
|
|
2071
|
-
// that doesn't implement registerFlag still loads the extension; a real host always has it.
|
|
2072
|
-
bridge.registerFlag?.("allow-tmux-dispatch", {
|
|
2073
|
-
type: "boolean",
|
|
2074
|
-
description:
|
|
2075
|
-
"Authorize tmux_agent_manager grant_dispatch to create a standing tmux dispatch grant without interactive approval (print/rpc/non-interactive mode only).",
|
|
2076
|
-
});
|
|
2077
1944
|
|
|
2078
1945
|
const closeJobWatchers = () => {
|
|
2079
1946
|
for (const watcher of jobWatchers.values()) watcher.close();
|
|
@@ -2114,16 +1981,13 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2114
1981
|
if (!bridge.reportManagedLane) {
|
|
2115
1982
|
pi.sendMessage(
|
|
2116
1983
|
{
|
|
2117
|
-
customType: "
|
|
1984
|
+
customType: "background-worker-completion",
|
|
2118
1985
|
content: formatFireTaskHandoff(job),
|
|
2119
1986
|
display: true,
|
|
2120
1987
|
details: {
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
id: agent.id,
|
|
2125
|
-
name: agent.name,
|
|
2126
|
-
status: agent.result?.status,
|
|
1988
|
+
records: job.agents.map((agent) => ({
|
|
1989
|
+
laneId: agentLaneId(job.id, agent.id),
|
|
1990
|
+
status: compactTmuxAgentStatus(agent.result?.status),
|
|
2127
1991
|
})),
|
|
2128
1992
|
},
|
|
2129
1993
|
},
|
|
@@ -2140,7 +2004,6 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2140
2004
|
}
|
|
2141
2005
|
return current;
|
|
2142
2006
|
});
|
|
2143
|
-
if (ctx.hasUI) ctx.ui.notify(`tmux background task ${job.id} completed.`, "info");
|
|
2144
2007
|
}
|
|
2145
2008
|
|
|
2146
2009
|
const activeIds = new Set(jobs.filter((job) => !isFireTaskTerminal(job)).map((job) => job.id));
|
|
@@ -2199,22 +2062,22 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2199
2062
|
name: "tmux_agent_manager",
|
|
2200
2063
|
label: "tmux Agent Manager",
|
|
2201
2064
|
description:
|
|
2202
|
-
"
|
|
2203
|
-
promptSnippet:
|
|
2065
|
+
"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.",
|
|
2066
|
+
promptSnippet:
|
|
2067
|
+
"Persistent tmux workers with inherited, immutable launch profiles; ordinary agents use native delegate.",
|
|
2204
2068
|
promptGuidelines: [
|
|
2205
|
-
"Ordinary agents: delegate.
|
|
2206
|
-
"
|
|
2207
|
-
"
|
|
2069
|
+
"Ordinary agents: delegate. Tmux availability detection is automatic; guard is diagnostic only. Never use native Windows tmux.",
|
|
2070
|
+
"fire_task derives one immutable profile per agent; omitted fields inherit parent tools, thinking, and machine scope.",
|
|
2071
|
+
"tools, thinkingLevel, resourceProfile, and worktreeLane are Pi-only; configure external CLIs in command.",
|
|
2072
|
+
"Pi path narrows structured file tools; external path sets cwd only and is audited as a machine-wide host process.",
|
|
2073
|
+
"fire_task arms capture before atomically starting each CLI with its task; never wait/poll/peek.",
|
|
2208
2074
|
"Teams: list/show_template, teamTemplate. Layout: workspace_plan, then launch_workspace.",
|
|
2209
2075
|
"send_followup prompts live pane/re-arms watcher; no relaunch. dismiss stops tracking, not session.",
|
|
2210
2076
|
"Cleanup: stop_job/stop_session dryRun, then confirm=yes-tmux-stop.",
|
|
2211
2077
|
"No secrets in task/command; all persist: ~/.pi/agent/work/background/tmux-agent-manager/state/jobs.",
|
|
2212
2078
|
"Display/status metadata only; verify result/log before completion claim.",
|
|
2213
|
-
"
|
|
2214
|
-
"
|
|
2215
|
-
"Grant needs UI confirmation, or --allow-tmux-dispatch without UI; revoke_grant ends it.",
|
|
2216
|
-
"Pi child: restricted tools/resource profile or no extensions/skills, scoped hard-stop prompt; launch config holds grant.",
|
|
2217
|
-
"Non-pi CLI grant applies at launch only; tool loop is CLI-owned.",
|
|
2079
|
+
"Workers continue autonomously inside the assigned profile; no separate approval handshake exists.",
|
|
2080
|
+
"Non-Pi process behavior is CLI-owned; never represent Pi-only profile controls as enforced for an external CLI.",
|
|
2218
2081
|
],
|
|
2219
2082
|
parameters: Type.Object({
|
|
2220
2083
|
action: Type.Optional(
|
|
@@ -2239,12 +2102,10 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2239
2102
|
Type.Literal("show_template"),
|
|
2240
2103
|
Type.Literal("stop_job"),
|
|
2241
2104
|
Type.Literal("stop_session"),
|
|
2242
|
-
Type.Literal("grant_dispatch"),
|
|
2243
|
-
Type.Literal("revoke_grant"),
|
|
2244
2105
|
],
|
|
2245
2106
|
{
|
|
2246
2107
|
description:
|
|
2247
|
-
"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,
|
|
2108
|
+
"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.",
|
|
2248
2109
|
},
|
|
2249
2110
|
),
|
|
2250
2111
|
),
|
|
@@ -2293,7 +2154,13 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2293
2154
|
jobId: Type.Optional(
|
|
2294
2155
|
Type.String({
|
|
2295
2156
|
description:
|
|
2296
|
-
"
|
|
2157
|
+
"Existing job id for job_status/list_variables/set_variable/send_followup/dismiss. Never creates a fresh fire_task job.",
|
|
2158
|
+
}),
|
|
2159
|
+
),
|
|
2160
|
+
launchKey: Type.Optional(
|
|
2161
|
+
Type.String({
|
|
2162
|
+
description:
|
|
2163
|
+
"Optional fresh fire_task identity. Use only for intentional stable naming; omitted creates a unique key. Never auto-retained from results.",
|
|
2297
2164
|
}),
|
|
2298
2165
|
),
|
|
2299
2166
|
agentId: Type.Optional(
|
|
@@ -2314,7 +2181,7 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2314
2181
|
deadlineSeconds: Type.Optional(
|
|
2315
2182
|
Type.Number({
|
|
2316
2183
|
description:
|
|
2317
|
-
"For fire_task: seconds before each event-driven
|
|
2184
|
+
"For fire_task: seconds before each event-driven watcher records timeout and terminates its owned pane. Default 1200.",
|
|
2318
2185
|
minimum: 5,
|
|
2319
2186
|
maximum: MAX_DEADLINE_SECONDS,
|
|
2320
2187
|
}),
|
|
@@ -2327,10 +2194,41 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2327
2194
|
command: Type.Optional(
|
|
2328
2195
|
Type.String({
|
|
2329
2196
|
description:
|
|
2330
|
-
"Interactive CLI start command
|
|
2197
|
+
"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.",
|
|
2198
|
+
}),
|
|
2199
|
+
),
|
|
2200
|
+
cwd: Type.Optional(
|
|
2201
|
+
Type.String({
|
|
2202
|
+
description: "Legacy per-agent working directory; does not narrow filesystem scope.",
|
|
2203
|
+
}),
|
|
2204
|
+
),
|
|
2205
|
+
path: Type.Optional(
|
|
2206
|
+
Type.String({
|
|
2207
|
+
description:
|
|
2208
|
+
"Optional workspace cwd. For Pi it narrows structured file tools; external CLIs remain machine-wide host-trusted processes.",
|
|
2331
2209
|
}),
|
|
2332
2210
|
),
|
|
2333
|
-
|
|
2211
|
+
tools: Type.Optional(
|
|
2212
|
+
Type.Array(Type.String(), {
|
|
2213
|
+
description:
|
|
2214
|
+
"Pi-only policy-owned tool override. Omitted inherits the parent's eligible worker tools; parent-only controls are removed.",
|
|
2215
|
+
minItems: 1,
|
|
2216
|
+
}),
|
|
2217
|
+
),
|
|
2218
|
+
resourceProfile: Type.Optional(
|
|
2219
|
+
Type.String({
|
|
2220
|
+
description:
|
|
2221
|
+
"Pi-only resource-profile override. Omitted inherits the parent's effective resources.",
|
|
2222
|
+
}),
|
|
2223
|
+
),
|
|
2224
|
+
thinkingLevel: Type.Optional(
|
|
2225
|
+
orchestrationThinkingLevelSchema(
|
|
2226
|
+
"Pi-only thinking override. Omitted inherits the orchestrator's current level.",
|
|
2227
|
+
),
|
|
2228
|
+
),
|
|
2229
|
+
worktreeLane: Type.Optional(
|
|
2230
|
+
Type.String({ description: "Pi-only worktree-sync lane binding for this worker." }),
|
|
2231
|
+
),
|
|
2334
2232
|
}),
|
|
2335
2233
|
{ description: "Agents/providers to lay out in tmux. Default: pi, agy, codex." },
|
|
2336
2234
|
),
|
|
@@ -2344,7 +2242,7 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2344
2242
|
force: Type.Optional(
|
|
2345
2243
|
Type.Boolean({
|
|
2346
2244
|
description:
|
|
2347
|
-
"For fire_task only: archive an existing job directory with the same
|
|
2245
|
+
"For fire_task only: archive an existing job directory with the same launchKey before launch. Existing tmux sessions are still refused; stop them first.",
|
|
2348
2246
|
}),
|
|
2349
2247
|
),
|
|
2350
2248
|
confirm: Type.Optional(
|
|
@@ -2352,50 +2250,7 @@ export default function tmuxAgentManagerExtension(pi: ExtensionAPI) {
|
|
|
2352
2250
|
),
|
|
2353
2251
|
goalId: Type.Optional(
|
|
2354
2252
|
Type.String({
|
|
2355
|
-
description:
|
|
2356
|
-
"Goal this dispatch/grant is scoped to. For grant_dispatch: an unscoped grant (omitted) covers any goal; a scoped grant covers only launches naming the same goalId. For fire_task/send_followup: tags the request for grant-coverage matching.",
|
|
2357
|
-
}),
|
|
2358
|
-
),
|
|
2359
|
-
agent: Type.Optional(providerSchema("grant_dispatch: the provider this standing grant authorizes.")),
|
|
2360
|
-
allowedTools: Type.Optional(
|
|
2361
|
-
Type.Array(Type.String(), {
|
|
2362
|
-
description:
|
|
2363
|
-
"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.",
|
|
2364
|
-
}),
|
|
2365
|
-
),
|
|
2366
|
-
resourceProfile: Type.Optional(
|
|
2367
|
-
Type.String({
|
|
2368
|
-
description:
|
|
2369
|
-
"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.",
|
|
2370
|
-
}),
|
|
2371
|
-
),
|
|
2372
|
-
writePaths: Type.Optional(
|
|
2373
|
-
Type.Array(Type.String(), {
|
|
2374
|
-
description: "grant_dispatch: write paths named in the child's scoped --append-system-prompt role text.",
|
|
2375
|
-
}),
|
|
2376
|
-
),
|
|
2377
|
-
maxLaunches: Type.Optional(
|
|
2378
|
-
Type.Number({
|
|
2379
|
-
description: "grant_dispatch: number of real fire_task/send_followup dispatches this grant authorizes.",
|
|
2380
|
-
minimum: 1,
|
|
2381
|
-
}),
|
|
2382
|
-
),
|
|
2383
|
-
expiresInMinutes: Type.Optional(
|
|
2384
|
-
Type.Number({
|
|
2385
|
-
description:
|
|
2386
|
-
"grant_dispatch: grant validity window from creation. Omit for a grant that never expires (until revoke_grant).",
|
|
2387
|
-
minimum: 1,
|
|
2388
|
-
}),
|
|
2389
|
-
),
|
|
2390
|
-
maxUsdAdvisory: Type.Optional(
|
|
2391
|
-
Type.Number({
|
|
2392
|
-
description:
|
|
2393
|
-
"grant_dispatch: advisory USD ceiling. Never enforced across the process boundary (the child bills under its own auth) — a claim to review only.",
|
|
2394
|
-
}),
|
|
2395
|
-
),
|
|
2396
|
-
grantId: Type.Optional(
|
|
2397
|
-
Type.String({
|
|
2398
|
-
description: "revoke_grant: grant id to revoke. Omit to revoke whichever grant is currently active.",
|
|
2253
|
+
description: "Goal this fire_task/send_followup belongs to for durable lane ownership and recovery.",
|
|
2399
2254
|
}),
|
|
2400
2255
|
),
|
|
2401
2256
|
}),
|