@caupulican/pi-adaptative 0.94.1 → 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.
Files changed (324) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +32 -13
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +7 -4
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +173 -293
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +247 -392
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.d.ts +52 -0
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.d.ts.map +1 -0
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.js +118 -0
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.js.map +1 -0
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/launch-profile.ts +169 -0
  13. package/dist/bundled-resources/extensions/tmux-agent-manager/templates/builder-validator.json +1 -1
  14. package/dist/bundled-resources/extensions/tmux-agent-manager/templates/provider-prompt-smoke.json +1 -1
  15. package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +13 -7
  16. package/dist/bundled-resources/skills/worker-profile-writer/SKILL.md +11 -11
  17. package/dist/core/agent-session.d.ts +11 -41
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +93 -160
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/autonomy/envelope-enforcement.d.ts +6 -1
  22. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  23. package/dist/core/autonomy/envelope-enforcement.js +26 -25
  24. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  25. package/dist/core/autonomy/lane-private-paths.d.ts.map +1 -1
  26. package/dist/core/autonomy/lane-private-paths.js +2 -1
  27. package/dist/core/autonomy/lane-private-paths.js.map +1 -1
  28. package/dist/core/autonomy/lane-tool-surface.d.ts +8 -8
  29. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  30. package/dist/core/autonomy/lane-tool-surface.js +45 -30
  31. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  32. package/dist/core/autonomy/worker-session-private-scope.d.ts +12 -0
  33. package/dist/core/autonomy/worker-session-private-scope.d.ts.map +1 -0
  34. package/dist/core/autonomy/worker-session-private-scope.js +42 -0
  35. package/dist/core/autonomy/worker-session-private-scope.js.map +1 -0
  36. package/dist/core/autonomy/worker-tool-adapter-registry.d.ts +65 -0
  37. package/dist/core/autonomy/worker-tool-adapter-registry.d.ts.map +1 -0
  38. package/dist/core/autonomy/worker-tool-adapter-registry.js +145 -0
  39. package/dist/core/autonomy/worker-tool-adapter-registry.js.map +1 -0
  40. package/dist/core/background-lane-controller.d.ts +1 -1
  41. package/dist/core/background-lane-controller.d.ts.map +1 -1
  42. package/dist/core/background-lane-controller.js +1 -1
  43. package/dist/core/background-lane-controller.js.map +1 -1
  44. package/dist/core/background-tool-task-controller.d.ts +8 -2
  45. package/dist/core/background-tool-task-controller.d.ts.map +1 -1
  46. package/dist/core/background-tool-task-controller.js +10 -2
  47. package/dist/core/background-tool-task-controller.js.map +1 -1
  48. package/dist/core/cost-guard-controller.d.ts +37 -0
  49. package/dist/core/cost-guard-controller.d.ts.map +1 -0
  50. package/dist/core/cost-guard-controller.js +71 -0
  51. package/dist/core/cost-guard-controller.js.map +1 -0
  52. package/dist/core/cost-guard.d.ts +9 -4
  53. package/dist/core/cost-guard.d.ts.map +1 -1
  54. package/dist/core/cost-guard.js +7 -3
  55. package/dist/core/cost-guard.js.map +1 -1
  56. package/dist/core/delegation/worker-attempt-executor.d.ts +1 -1
  57. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  58. package/dist/core/delegation/worker-attempt-executor.js +0 -1
  59. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  60. package/dist/core/delegation/worker-authority-resolver.d.ts +16 -3
  61. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
  62. package/dist/core/delegation/worker-authority-resolver.js +114 -55
  63. package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
  64. package/dist/core/delegation/worker-claim.d.ts +1 -2
  65. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  66. package/dist/core/delegation/worker-claim.js.map +1 -1
  67. package/dist/core/delegation/worker-delegation-controller.d.ts +19 -6
  68. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  69. package/dist/core/delegation/worker-delegation-controller.js +125 -40
  70. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  71. package/dist/core/delegation/worker-delegation-request.d.ts +3 -3
  72. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  73. package/dist/core/delegation/worker-delegation-request.js +13 -19
  74. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  75. package/dist/core/delegation/worker-execution-policy.d.ts +3 -2
  76. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  77. package/dist/core/delegation/worker-execution-policy.js +54 -39
  78. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  79. package/dist/core/delegation/worker-fleet-limits.d.ts +3 -3
  80. package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -1
  81. package/dist/core/delegation/worker-fleet-limits.js +7 -7
  82. package/dist/core/delegation/worker-fleet-limits.js.map +1 -1
  83. package/dist/core/delegation/worker-machine-scope.d.ts +12 -0
  84. package/dist/core/delegation/worker-machine-scope.d.ts.map +1 -0
  85. package/dist/core/delegation/worker-machine-scope.js +38 -0
  86. package/dist/core/delegation/worker-machine-scope.js.map +1 -0
  87. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
  88. package/dist/core/delegation/worker-recovery-coordinator.js +6 -1
  89. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
  90. package/dist/core/delegation/worker-runner.d.ts +5 -7
  91. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  92. package/dist/core/delegation/worker-runner.js +1 -4
  93. package/dist/core/delegation/worker-runner.js.map +1 -1
  94. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +3 -2
  95. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -1
  96. package/dist/core/delegation/worker-write-reservation-coordinator.js +50 -25
  97. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -1
  98. package/dist/core/durable-custom-message-turn-controller.d.ts +6 -0
  99. package/dist/core/durable-custom-message-turn-controller.d.ts.map +1 -1
  100. package/dist/core/durable-custom-message-turn-controller.js +38 -15
  101. package/dist/core/durable-custom-message-turn-controller.js.map +1 -1
  102. package/dist/core/extension-binding-controller.d.ts +5 -1
  103. package/dist/core/extension-binding-controller.d.ts.map +1 -1
  104. package/dist/core/extension-binding-controller.js +15 -0
  105. package/dist/core/extension-binding-controller.js.map +1 -1
  106. package/dist/core/extensions/factory-runtime.d.ts.map +1 -1
  107. package/dist/core/extensions/factory-runtime.js +5 -0
  108. package/dist/core/extensions/factory-runtime.js.map +1 -1
  109. package/dist/core/extensions/runner.d.ts.map +1 -1
  110. package/dist/core/extensions/runner.js +1 -0
  111. package/dist/core/extensions/runner.js.map +1 -1
  112. package/dist/core/extensions/types.d.ts +8 -4
  113. package/dist/core/extensions/types.d.ts.map +1 -1
  114. package/dist/core/extensions/types.js.map +1 -1
  115. package/dist/core/foreground-terminal-handoff-controller.d.ts +14 -0
  116. package/dist/core/foreground-terminal-handoff-controller.d.ts.map +1 -1
  117. package/dist/core/foreground-terminal-handoff-controller.js +188 -72
  118. package/dist/core/foreground-terminal-handoff-controller.js.map +1 -1
  119. package/dist/core/goals/goal-session-controller.d.ts +2 -0
  120. package/dist/core/goals/goal-session-controller.d.ts.map +1 -1
  121. package/dist/core/goals/goal-session-controller.js +8 -0
  122. package/dist/core/goals/goal-session-controller.js.map +1 -1
  123. package/dist/core/goals/goal-tool-core.d.ts +5 -4
  124. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  125. package/dist/core/goals/goal-tool-core.js +23 -19
  126. package/dist/core/goals/goal-tool-core.js.map +1 -1
  127. package/dist/core/learning/auto-learn-settings.d.ts +84 -0
  128. package/dist/core/learning/auto-learn-settings.d.ts.map +1 -0
  129. package/dist/core/learning/auto-learn-settings.js +66 -0
  130. package/dist/core/learning/auto-learn-settings.js.map +1 -0
  131. package/dist/core/learning/reflection-engine.d.ts +4 -0
  132. package/dist/core/learning/reflection-engine.d.ts.map +1 -1
  133. package/dist/core/learning/reflection-engine.js +48 -10
  134. package/dist/core/learning/reflection-engine.js.map +1 -1
  135. package/dist/core/local-prefix-warm-controller.d.ts +24 -0
  136. package/dist/core/local-prefix-warm-controller.d.ts.map +1 -0
  137. package/dist/core/local-prefix-warm-controller.js +79 -0
  138. package/dist/core/local-prefix-warm-controller.js.map +1 -0
  139. package/dist/core/memory/providers/file-store.d.ts +2 -0
  140. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  141. package/dist/core/memory/providers/file-store.js +71 -15
  142. package/dist/core/memory/providers/file-store.js.map +1 -1
  143. package/dist/core/memory/worker-memory-tools.d.ts +5 -0
  144. package/dist/core/memory/worker-memory-tools.d.ts.map +1 -0
  145. package/dist/core/memory/worker-memory-tools.js +5 -0
  146. package/dist/core/memory/worker-memory-tools.js.map +1 -0
  147. package/dist/core/model-capability.d.ts +7 -0
  148. package/dist/core/model-capability.d.ts.map +1 -1
  149. package/dist/core/model-capability.js +12 -0
  150. package/dist/core/model-capability.js.map +1 -1
  151. package/dist/core/model-registry.d.ts.map +1 -1
  152. package/dist/core/model-registry.js +2 -10
  153. package/dist/core/model-registry.js.map +1 -1
  154. package/dist/core/model-router-controller.d.ts +3 -2
  155. package/dist/core/model-router-controller.d.ts.map +1 -1
  156. package/dist/core/model-router-controller.js +3 -2
  157. package/dist/core/model-router-controller.js.map +1 -1
  158. package/dist/core/orchestration/contracts.d.ts +16 -12
  159. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  160. package/dist/core/orchestration/contracts.js +2 -2
  161. package/dist/core/orchestration/contracts.js.map +1 -1
  162. package/dist/core/orchestration/lane-tool-manifests.d.ts +2 -2
  163. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
  164. package/dist/core/orchestration/lane-tool-manifests.js +6 -1
  165. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  166. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  167. package/dist/core/orchestration/profile-registry.js +14 -0
  168. package/dist/core/orchestration/profile-registry.js.map +1 -1
  169. package/dist/core/orchestration/task-profile-writer.d.ts +7 -4
  170. package/dist/core/orchestration/task-profile-writer.d.ts.map +1 -1
  171. package/dist/core/orchestration/task-profile-writer.js +71 -59
  172. package/dist/core/orchestration/task-profile-writer.js.map +1 -1
  173. package/dist/core/orchestration/thinking-level-schema.d.ts +7 -0
  174. package/dist/core/orchestration/thinking-level-schema.d.ts.map +1 -0
  175. package/dist/core/orchestration/thinking-level-schema.js +19 -0
  176. package/dist/core/orchestration/thinking-level-schema.js.map +1 -0
  177. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  178. package/dist/core/orchestration/worker-execution-contract.js +12 -5
  179. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  180. package/dist/core/provider-prompt-contracts.d.ts +1 -4
  181. package/dist/core/provider-prompt-contracts.d.ts.map +1 -1
  182. package/dist/core/provider-prompt-contracts.js +4 -10
  183. package/dist/core/provider-prompt-contracts.js.map +1 -1
  184. package/dist/core/provider-request-context-controller.d.ts +2 -0
  185. package/dist/core/provider-request-context-controller.d.ts.map +1 -1
  186. package/dist/core/provider-request-context-controller.js +10 -3
  187. package/dist/core/provider-request-context-controller.js.map +1 -1
  188. package/dist/core/reflection-controller.d.ts +56 -27
  189. package/dist/core/reflection-controller.d.ts.map +1 -1
  190. package/dist/core/reflection-controller.js +206 -117
  191. package/dist/core/reflection-controller.js.map +1 -1
  192. package/dist/core/runtime-builder.d.ts +1 -0
  193. package/dist/core/runtime-builder.d.ts.map +1 -1
  194. package/dist/core/runtime-builder.js +25 -14
  195. package/dist/core/runtime-builder.js.map +1 -1
  196. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
  197. package/dist/core/secrets/credential-exposure-guard.js +55 -10
  198. package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
  199. package/dist/core/session-role.d.ts +6 -8
  200. package/dist/core/session-role.d.ts.map +1 -1
  201. package/dist/core/session-role.js +8 -18
  202. package/dist/core/session-role.js.map +1 -1
  203. package/dist/core/settings-manager.d.ts +13 -16
  204. package/dist/core/settings-manager.d.ts.map +1 -1
  205. package/dist/core/settings-manager.js +53 -50
  206. package/dist/core/settings-manager.js.map +1 -1
  207. package/dist/core/skill-vault.d.ts +15 -0
  208. package/dist/core/skill-vault.d.ts.map +1 -1
  209. package/dist/core/skill-vault.js +79 -45
  210. package/dist/core/skill-vault.js.map +1 -1
  211. package/dist/core/skills.d.ts +3 -0
  212. package/dist/core/skills.d.ts.map +1 -1
  213. package/dist/core/skills.js +6 -6
  214. package/dist/core/skills.js.map +1 -1
  215. package/dist/core/system-prompt-builder.d.ts +2 -0
  216. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  217. package/dist/core/system-prompt-builder.js +52 -12
  218. package/dist/core/system-prompt-builder.js.map +1 -1
  219. package/dist/core/system-prompt.js +1 -1
  220. package/dist/core/system-prompt.js.map +1 -1
  221. package/dist/core/tool-capability-policy.d.ts +3 -0
  222. package/dist/core/tool-capability-policy.d.ts.map +1 -1
  223. package/dist/core/tool-capability-policy.js +13 -2
  224. package/dist/core/tool-capability-policy.js.map +1 -1
  225. package/dist/core/toolkit/script-runner.d.ts +2 -1
  226. package/dist/core/toolkit/script-runner.d.ts.map +1 -1
  227. package/dist/core/toolkit/script-runner.js +10 -2
  228. package/dist/core/toolkit/script-runner.js.map +1 -1
  229. package/dist/core/tools/delegate-status.d.ts +1 -1
  230. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  231. package/dist/core/tools/delegate-status.js +1 -1
  232. package/dist/core/tools/delegate-status.js.map +1 -1
  233. package/dist/core/tools/delegate.d.ts +4 -15
  234. package/dist/core/tools/delegate.d.ts.map +1 -1
  235. package/dist/core/tools/delegate.js +111 -73
  236. package/dist/core/tools/delegate.js.map +1 -1
  237. package/dist/core/tools/goal.d.ts +3 -4
  238. package/dist/core/tools/goal.d.ts.map +1 -1
  239. package/dist/core/tools/goal.js +24 -7
  240. package/dist/core/tools/goal.js.map +1 -1
  241. package/dist/core/tools/profile-writer.d.ts +2 -8
  242. package/dist/core/tools/profile-writer.d.ts.map +1 -1
  243. package/dist/core/tools/profile-writer.js +6 -18
  244. package/dist/core/tools/profile-writer.js.map +1 -1
  245. package/dist/core/tools/run-toolkit-script.d.ts +1 -1
  246. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
  247. package/dist/core/tools/run-toolkit-script.js +2 -2
  248. package/dist/core/tools/run-toolkit-script.js.map +1 -1
  249. package/dist/core/tools/skill-audit.d.ts +28 -1
  250. package/dist/core/tools/skill-audit.d.ts.map +1 -1
  251. package/dist/core/tools/skill-audit.js +56 -12
  252. package/dist/core/tools/skill-audit.js.map +1 -1
  253. package/dist/core/tools/skill.d.ts +12 -1
  254. package/dist/core/tools/skill.d.ts.map +1 -1
  255. package/dist/core/tools/skill.js +52 -0
  256. package/dist/core/tools/skill.js.map +1 -1
  257. package/dist/core/tools/skillify.d.ts.map +1 -1
  258. package/dist/core/tools/skillify.js +16 -7
  259. package/dist/core/tools/skillify.js.map +1 -1
  260. package/dist/core/tools/task-steps.js +1 -1
  261. package/dist/core/tools/task-steps.js.map +1 -1
  262. package/dist/core/tools/tmux-dispatch.d.ts +4 -16
  263. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -1
  264. package/dist/core/tools/tmux-dispatch.js +5 -20
  265. package/dist/core/tools/tmux-dispatch.js.map +1 -1
  266. package/dist/core/worktree-sync/codes.d.ts +1 -2
  267. package/dist/core/worktree-sync/codes.d.ts.map +1 -1
  268. package/dist/core/worktree-sync/codes.js +1 -2
  269. package/dist/core/worktree-sync/codes.js.map +1 -1
  270. package/dist/core/worktree-sync/lane-gate.d.ts +4 -8
  271. package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -1
  272. package/dist/core/worktree-sync/lane-gate.js +5 -51
  273. package/dist/core/worktree-sync/lane-gate.js.map +1 -1
  274. package/dist/index.d.ts +5 -0
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +7 -0
  277. package/dist/index.js.map +1 -1
  278. package/dist/modes/interactive/auto-learn-controller.d.ts +6 -7
  279. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
  280. package/dist/modes/interactive/auto-learn-controller.js +30 -207
  281. package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
  282. package/dist/modes/interactive/autonomy-commands.js +2 -2
  283. package/dist/modes/interactive/autonomy-commands.js.map +1 -1
  284. package/dist/modes/interactive/components/custom-message.js +17 -0
  285. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  286. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
  287. package/dist/modes/interactive/components/fitness-role-selector.js +2 -2
  288. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
  289. package/dist/modes/interactive/components/footer.js +1 -1
  290. package/dist/modes/interactive/components/footer.js.map +1 -1
  291. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  292. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  293. package/dist/modes/interactive/components/settings-selector.js +150 -73
  294. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  295. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  296. package/dist/modes/interactive/report-commands.js +8 -4
  297. package/dist/modes/interactive/report-commands.js.map +1 -1
  298. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  299. package/dist/modes/interactive/settings-selector-flow.js +10 -0
  300. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  301. package/docs/extensions.md +8 -0
  302. package/docs/harness-architecture.md +28 -32
  303. package/docs/index.md +1 -1
  304. package/docs/process-matrix.md +8 -6
  305. package/docs/resources.md +1 -1
  306. package/docs/settings.md +61 -51
  307. package/docs/tmux-agent-manager.md +47 -40
  308. package/docs/usage.md +1 -1
  309. package/docs/worker-profiles.md +8 -6
  310. package/docs/worktree-sync.md +26 -20
  311. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  312. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  313. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  314. package/examples/extensions/sandbox/package-lock.json +2 -2
  315. package/examples/extensions/sandbox/package.json +1 -1
  316. package/examples/extensions/with-deps/package-lock.json +2 -2
  317. package/examples/extensions/with-deps/package.json +1 -1
  318. package/npm-shrinkwrap.json +12 -12
  319. package/package.json +4 -4
  320. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +0 -133
  321. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +0 -1
  322. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +0 -199
  323. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +0 -1
  324. 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 { buildGrant, buildGrantUsageEntry, buildLaunchProfileFlags, buildTombstone, countGrantUsages, DEFAULT_READ_BIASED_TOOLS, decodeTmuxWorkerUsageClaim, describeGrant, GRANT_CUSTOM_TYPE, GRANT_USAGE_CUSTOM_TYPE, grantCovers, isTmuxDispatchGrant, isTmuxDispatchGrantTombstone, launchProfileSourceFromGrant, ONE_SHOT_LAUNCH_PROFILE_SOURCE, } from "./dispatch-grant.js";
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 ?? DEFAULT_READ_BIASED_TOOLS,
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 managedAuthorization(grant, fallbackId) {
32
- const launchSource = grant ? launchProfileSourceFromGrant(grant) : ONE_SHOT_LAUNCH_PROFILE_SOURCE;
33
- return {
34
- authorizationId: grant?.grantId ?? fallbackId,
35
- authorizationKind: grant ? "standing-grant" : "one-shot-owner-approval",
36
- launchSource,
37
- allowedTools: [...(launchSource.allowedTools?.length ? launchSource.allowedTools : DEFAULT_READ_BIASED_TOOLS)],
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 applyManagedAuthorization(agent, authorization, args) {
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 opens each CLI in tmux, injects the prompt, captures pane output, and watches DONE/BLOCKED markers.",
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 owner-approved implementation/QA batches; never claim complete until result files and validation evidence are inspected.",
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 only for an owner-requested persistent interactive pane; set dryRun=true only when a preview is useful.`;
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 for managed ${toolName} panes only when the owner explicitly requested persistent interactive provider processes.`,
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 immediately after panes, event watchers, and prompt handoffs are armed.",
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 baseCommand = firstString(agent.command, PROVIDER_COMMANDS[provider]);
468
- if (!baseCommand)
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 command = tools ? `${baseCommand} --tools ${tools.join(",")}` : baseCommand;
472
- return { provider, name, command, cwd: agent.cwd, tools, worktreeLane: agent.worktreeLane };
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
- "- If user input, credentials, destructive approval, publishing, or unavailable dependency blocks you, report BLOCKED instead of waiting silently.",
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 finish stopped stop-requested-event; else finish timeout deadline-event; fi',
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.jobId) || makeJobId();
653
+ const id = firstString(params.launchKey) || makeJobId();
648
654
  if (!/^[a-zA-Z0-9_.:-]{4,80}$/.test(id))
649
- throw new Error("jobId must be 4-80 chars: letters, numbers, _, ., :, or -");
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: resolveCwd(ctx, agent.cwd || cwd),
666
- // tools already baked into command by normalizeAgents
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)} then inject ${path.basename(agent.promptPath)}`);
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 jobId, or pass force:true to archive the old job before launching.`);
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
- sendCommandToPane(firstPane, first.command);
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
- sendCommandToPane(paneId, pane.command);
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
- // STANDING GRANT approval-gated tmux dispatch. The pure decode/decision logic lives in
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
- /** Apply the launch profile to every provider="pi" agent in the job (fire_task only — send_followup
1206
- * reuses an already-launched pane, so there is no new child command to configure). Non-pi agents are
1207
- * bounded by the grant only at the launch layer (agent/budget/count); their internal tool-loop
1208
- * enforcement is the target CLI's own responsibility (documented limitation, not a hidden gap). */
1209
- function applyLaunchProfile(job, source) {
1210
- for (const agent of job.agents) {
1211
- if (agent.provider !== "pi")
1212
- continue;
1213
- // Per-agent flags: `worktreeLane` is a per-agent property (only a lane-first goal->tmux
1214
- // dispatch sets it), so the shared `source` envelope is extended with it just for this agent's
1215
- // flag build -- every other agent in a multi-agent team keeps the plain grant-derived profile.
1216
- const flags = buildLaunchProfileFlags(agent.worktreeLane ? { ...source, worktreeLane: agent.worktreeLane } : source);
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
- // APPROVAL-GATED LAUNCH: resolved before any tmux/FS side effect. A standing grant must
1410
- // authorize every child provider and spend its budget per child process.
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
- applyManagedAuthorization(agent, managedAuthority, {
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
- applyLaunchProfile(job, {
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
- const launch = launchTmuxSession(job.sessionName, job.cwd, panes);
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
- injectPromptToPane(agent.paneId, fs.readFileSync(agent.promptPath, "utf8"), agent.provider);
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
- // APPROVAL-GATED LAUNCH (doctrine-regression mandatory): a follow-up dispatches a fresh
1528
- // turn into an already-running child, so there is no new child command to profile — only the
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
- applyManagedAuthorization(agent, managedAuthority, {
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
@@ -1766,12 +1667,6 @@ export default function tmuxAgentManagerExtension(pi) {
1766
1667
  let handoffTail = Promise.resolve();
1767
1668
  const jobWatchers = new Map();
1768
1669
  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
1670
  const closeJobWatchers = () => {
1776
1671
  for (const watcher of jobWatchers.values())
1777
1672
  watcher.close();
@@ -1810,16 +1705,13 @@ export default function tmuxAgentManagerExtension(pi) {
1810
1705
  // message remains only for lightweight/standalone hosts that omit that bridge entirely.
1811
1706
  if (!bridge.reportManagedLane) {
1812
1707
  pi.sendMessage({
1813
- customType: "tmux-background-completion",
1708
+ customType: "background-worker-completion",
1814
1709
  content: formatFireTaskHandoff(job),
1815
1710
  display: true,
1816
1711
  details: {
1817
- jobId: job.id,
1818
- sessionName: job.sessionName,
1819
- agents: job.agents.map((agent) => ({
1820
- id: agent.id,
1821
- name: agent.name,
1822
- status: agent.result?.status,
1712
+ records: job.agents.map((agent) => ({
1713
+ laneId: agentLaneId(job.id, agent.id),
1714
+ status: compactTmuxAgentStatus(agent.result?.status),
1823
1715
  })),
1824
1716
  },
1825
1717
  }, { triggerTurn: true, deliverAs: "followUp" });
@@ -1834,8 +1726,6 @@ export default function tmuxAgentManagerExtension(pi) {
1834
1726
  }
1835
1727
  return current;
1836
1728
  });
1837
- if (ctx.hasUI)
1838
- ctx.ui.notify(`tmux background task ${job.id} completed.`, "info");
1839
1729
  }
1840
1730
  const activeIds = new Set(jobs.filter((job) => !isFireTaskTerminal(job)).map((job) => job.id));
1841
1731
  for (const [jobId, watcher] of jobWatchers) {
@@ -1892,22 +1782,21 @@ export default function tmuxAgentManagerExtension(pi) {
1892
1782
  pi.registerTool({
1893
1783
  name: "tmux_agent_manager",
1894
1784
  label: "tmux Agent Manager",
1895
- description: "Specialized tmux-backed interactive provider/workspace manager. Native delegate is the standard cross-platform subagent path. Use this tool only when the owner explicitly requests persistent interactive CLI panes and tmux is available.",
1896
- promptSnippet: "tmux interactive panes only by owner request after guard; ordinary agents use native delegate.",
1785
+ 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.",
1786
+ promptSnippet: "Persistent tmux workers with inherited, immutable launch profiles; ordinary agents use native delegate.",
1897
1787
  promptGuidelines: [
1898
- "Ordinary agents: delegate. tmux only owner-requested panes after guard; never native Windows.",
1899
- "WSL tmux only if installed/requested. Never edit profiles during dispatch; native resolver owns them.",
1900
- "fire_task starts batch, returns after watcher armed; never wait/poll/peek.",
1788
+ "Ordinary agents: delegate. Tmux availability detection is automatic; guard is diagnostic only. Never use native Windows tmux.",
1789
+ "fire_task derives one immutable profile per agent; omitted fields inherit parent tools, thinking, and machine scope.",
1790
+ "tools, thinkingLevel, resourceProfile, and worktreeLane are Pi-only; configure external CLIs in command.",
1791
+ "Pi path narrows structured file tools; external path sets cwd only and is audited as a machine-wide host process.",
1792
+ "fire_task arms capture before atomically starting each CLI with its task; never wait/poll/peek.",
1901
1793
  "Teams: list/show_template, teamTemplate. Layout: workspace_plan, then launch_workspace.",
1902
1794
  "send_followup prompts live pane/re-arms watcher; no relaunch. dismiss stops tracking, not session.",
1903
1795
  "Cleanup: stop_job/stop_session dryRun, then confirm=yes-tmux-stop.",
1904
1796
  "No secrets in task/command; all persist: ~/.pi/agent/work/background/tmux-agent-manager/state/jobs.",
1905
1797
  "Display/status metadata only; verify result/log before completion claim.",
1906
- "Real fire_task/send_followup needs grant or one-shot UI approval. No grant/UI: refused; run grant_dispatch.",
1907
- "grant_dispatch: agent,maxLaunches; optional goalId,allowedTools,resourceProfile,writePaths,expiresInMinutes.",
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.",
1798
+ "Workers continue autonomously inside the assigned profile; no separate approval handshake exists.",
1799
+ "Non-Pi process behavior is CLI-owned; never represent Pi-only profile controls as enforced for an external CLI.",
1911
1800
  ],
1912
1801
  parameters: Type.Object({
1913
1802
  action: Type.Optional(Type.Union([
@@ -1930,10 +1819,8 @@ export default function tmuxAgentManagerExtension(pi) {
1930
1819
  Type.Literal("show_template"),
1931
1820
  Type.Literal("stop_job"),
1932
1821
  Type.Literal("stop_session"),
1933
- Type.Literal("grant_dispatch"),
1934
- Type.Literal("revoke_grant"),
1935
1822
  ], {
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, stop_session, grant_dispatch, or revoke_grant. Default status.",
1823
+ 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
1824
  })),
1938
1825
  title: Type.Optional(Type.String({ description: "Notification title or workspace title context." })),
1939
1826
  body: Type.Optional(Type.String({ description: "Notification body/status text, or fallback task text for fire_task." })),
@@ -1957,7 +1844,10 @@ export default function tmuxAgentManagerExtension(pi) {
1957
1844
  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
1845
  })),
1959
1846
  jobId: Type.Optional(Type.String({
1960
- description: "Job id for fire_task (optional), job_status/list_variables/set_variable/send_followup/dismiss (required).",
1847
+ description: "Existing job id for job_status/list_variables/set_variable/send_followup/dismiss. Never creates a fresh fire_task job.",
1848
+ })),
1849
+ launchKey: Type.Optional(Type.String({
1850
+ description: "Optional fresh fire_task identity. Use only for intentional stable naming; omitted creates a unique key. Never auto-retained from results.",
1961
1851
  })),
1962
1852
  agentId: Type.Optional(Type.String({
1963
1853
  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 +1857,7 @@ export default function tmuxAgentManagerExtension(pi) {
1967
1857
  })),
1968
1858
  variableValue: Type.Optional(Type.String({ description: "For set_variable: value to write. body/status can also supply the value." })),
1969
1859
  deadlineSeconds: Type.Optional(Type.Number({
1970
- description: "For fire_task: seconds before each event-driven pane watcher records a timeout. Default 1200.",
1860
+ description: "For fire_task: seconds before each event-driven watcher records timeout and terminates its owned pane. Default 1200.",
1971
1861
  minimum: 5,
1972
1862
  maximum: MAX_DEADLINE_SECONDS,
1973
1863
  })),
@@ -1975,43 +1865,33 @@ export default function tmuxAgentManagerExtension(pi) {
1975
1865
  provider: Type.Optional(providerSchema()),
1976
1866
  name: Type.Optional(Type.String({ description: "Pane/worker name." })),
1977
1867
  command: Type.Optional(Type.String({
1978
- description: "Interactive CLI start command for this pane, e.g. claude, agy, pi, or a custom wrapper CLI. The manager injects the prompt after launch and captures pane output.",
1868
+ 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.",
1869
+ })),
1870
+ cwd: Type.Optional(Type.String({
1871
+ description: "Legacy per-agent working directory; does not narrow filesystem scope.",
1872
+ })),
1873
+ path: Type.Optional(Type.String({
1874
+ description: "Optional workspace cwd. For Pi it narrows structured file tools; external CLIs remain machine-wide host-trusted processes.",
1875
+ })),
1876
+ tools: Type.Optional(Type.Array(Type.String(), {
1877
+ description: "Pi-only policy-owned tool override. Omitted inherits the parent's eligible worker tools; parent-only controls are removed.",
1878
+ minItems: 1,
1979
1879
  })),
1980
- cwd: Type.Optional(Type.String({ description: "Optional per-agent cwd." })),
1880
+ resourceProfile: Type.Optional(Type.String({
1881
+ description: "Pi-only resource-profile override. Omitted inherits the parent's effective resources.",
1882
+ })),
1883
+ thinkingLevel: Type.Optional(orchestrationThinkingLevelSchema("Pi-only thinking override. Omitted inherits the orchestrator's current level.")),
1884
+ worktreeLane: Type.Optional(Type.String({ description: "Pi-only worktree-sync lane binding for this worker." })),
1981
1885
  }), { description: "Agents/providers to lay out in tmux. Default: pi, agy, codex." })),
1982
1886
  dryRun: Type.Optional(Type.Boolean({
1983
1887
  description: "Preview only when true. Launch actions run by default; stop actions remain previews by default.",
1984
1888
  })),
1985
1889
  force: Type.Optional(Type.Boolean({
1986
- description: "For fire_task only: archive an existing job directory with the same jobId before launch. Existing tmux sessions are still refused; stop them first.",
1890
+ 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
1891
  })),
1988
1892
  confirm: Type.Optional(Type.String({ description: "Required for real stop_job/stop_session: yes-tmux-stop." })),
1989
1893
  goalId: Type.Optional(Type.String({
1990
- description: "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.",
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.",
1894
+ description: "Goal this fire_task/send_followup belongs to for durable lane ownership and recovery.",
2015
1895
  })),
2016
1896
  }),
2017
1897
  async execute(toolCallId, params, signal, onUpdate, ctx) {