@caupulican/pi-adaptative 0.81.40 → 0.81.42

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 (599) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +1 -0
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -1
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +1 -0
  6. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +5 -1
  7. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +3 -3
  8. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +8 -0
  9. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +26 -3
  10. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +3 -0
  11. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +4 -1
  12. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +2 -1
  13. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +2 -0
  14. package/dist/cli/args.d.ts +2 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +22 -1
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/core/agent-paths.d.ts +38 -0
  19. package/dist/core/agent-paths.d.ts.map +1 -1
  20. package/dist/core/agent-paths.js +77 -0
  21. package/dist/core/agent-paths.js.map +1 -1
  22. package/dist/core/agent-session-contracts.d.ts +311 -0
  23. package/dist/core/agent-session-contracts.d.ts.map +1 -0
  24. package/dist/core/agent-session-contracts.js +26 -0
  25. package/dist/core/agent-session-contracts.js.map +1 -0
  26. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  27. package/dist/core/agent-session-runtime.js +3 -0
  28. package/dist/core/agent-session-runtime.js.map +1 -1
  29. package/dist/core/agent-session.d.ts +26 -405
  30. package/dist/core/agent-session.d.ts.map +1 -1
  31. package/dist/core/agent-session.js +90 -316
  32. package/dist/core/agent-session.js.map +1 -1
  33. package/dist/core/auth-storage.d.ts.map +1 -1
  34. package/dist/core/auth-storage.js +3 -1
  35. package/dist/core/auth-storage.js.map +1 -1
  36. package/dist/core/autonomy/contracts.d.ts +2 -0
  37. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  38. package/dist/core/autonomy/contracts.js.map +1 -1
  39. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  40. package/dist/core/autonomy/envelope-enforcement.js +4 -9
  41. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  42. package/dist/core/autonomy/lane-tool-surface.d.ts +3 -1
  43. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  44. package/dist/core/autonomy/lane-tool-surface.js +19 -5
  45. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  46. package/dist/core/autonomy/path-scope.d.ts +14 -0
  47. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  48. package/dist/core/autonomy/path-scope.js +37 -6
  49. package/dist/core/autonomy/path-scope.js.map +1 -1
  50. package/dist/core/background-lane-controller.d.ts +30 -2
  51. package/dist/core/background-lane-controller.d.ts.map +1 -1
  52. package/dist/core/background-lane-controller.js +33 -1
  53. package/dist/core/background-lane-controller.js.map +1 -1
  54. package/dist/core/bedrock-sso-login.d.ts +16 -0
  55. package/dist/core/bedrock-sso-login.d.ts.map +1 -0
  56. package/dist/core/bedrock-sso-login.js +98 -0
  57. package/dist/core/bedrock-sso-login.js.map +1 -0
  58. package/dist/core/compaction-controller.d.ts.map +1 -1
  59. package/dist/core/compaction-controller.js +1 -1
  60. package/dist/core/compaction-controller.js.map +1 -1
  61. package/dist/core/compaction-support.d.ts +3 -8
  62. package/dist/core/compaction-support.d.ts.map +1 -1
  63. package/dist/core/compaction-support.js +3 -3
  64. package/dist/core/compaction-support.js.map +1 -1
  65. package/dist/core/context-pipeline.d.ts +1 -1
  66. package/dist/core/context-pipeline.d.ts.map +1 -1
  67. package/dist/core/context-pipeline.js +7 -33
  68. package/dist/core/context-pipeline.js.map +1 -1
  69. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  70. package/dist/core/cost/cost-summary.js +5 -3
  71. package/dist/core/cost/cost-summary.js.map +1 -1
  72. package/dist/core/cost/daily-usage.d.ts.map +1 -1
  73. package/dist/core/cost/daily-usage.js +4 -5
  74. package/dist/core/cost/daily-usage.js.map +1 -1
  75. package/dist/core/default-tool-surface.d.ts.map +1 -1
  76. package/dist/core/default-tool-surface.js +1 -0
  77. package/dist/core/default-tool-surface.js.map +1 -1
  78. package/dist/core/delegation/managed-lane-controller.d.ts +11 -1
  79. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
  80. package/dist/core/delegation/managed-lane-controller.js +84 -41
  81. package/dist/core/delegation/managed-lane-controller.js.map +1 -1
  82. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -1
  83. package/dist/core/delegation/session-worker-claim.js +4 -3
  84. package/dist/core/delegation/session-worker-claim.js.map +1 -1
  85. package/dist/core/delegation/worker-action-journal.d.ts +73 -0
  86. package/dist/core/delegation/worker-action-journal.d.ts.map +1 -0
  87. package/dist/core/delegation/worker-action-journal.js +297 -0
  88. package/dist/core/delegation/worker-action-journal.js.map +1 -0
  89. package/dist/core/delegation/worker-actions.d.ts +36 -1
  90. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  91. package/dist/core/delegation/worker-actions.js +171 -46
  92. package/dist/core/delegation/worker-actions.js.map +1 -1
  93. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +75 -0
  94. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -0
  95. package/dist/core/delegation/worker-agent-control-coordinator.js +218 -0
  96. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -0
  97. package/dist/core/delegation/worker-agent-control.d.ts +66 -0
  98. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -0
  99. package/dist/core/delegation/worker-agent-control.js +163 -0
  100. package/dist/core/delegation/worker-agent-control.js.map +1 -0
  101. package/dist/core/delegation/worker-attempt-executor.d.ts +80 -0
  102. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -0
  103. package/dist/core/delegation/worker-attempt-executor.js +343 -0
  104. package/dist/core/delegation/worker-attempt-executor.js.map +1 -0
  105. package/dist/core/delegation/worker-claim.d.ts +30 -0
  106. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  107. package/dist/core/delegation/worker-claim.js +205 -53
  108. package/dist/core/delegation/worker-claim.js.map +1 -1
  109. package/dist/core/delegation/worker-conversation-store.d.ts +116 -0
  110. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -0
  111. package/dist/core/delegation/worker-conversation-store.js +442 -0
  112. package/dist/core/delegation/worker-conversation-store.js.map +1 -0
  113. package/dist/core/delegation/worker-delegation-controller.d.ts +21 -5
  114. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  115. package/dist/core/delegation/worker-delegation-controller.js +386 -251
  116. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  117. package/dist/core/delegation/worker-delegation-request.d.ts +14 -0
  118. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  119. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  120. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +4 -2
  121. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
  122. package/dist/core/delegation/worker-dispatch-scheduler.js +16 -6
  123. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
  124. package/dist/core/delegation/worker-execution-policy.d.ts +2 -1
  125. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  126. package/dist/core/delegation/worker-execution-policy.js +5 -7
  127. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  128. package/dist/core/delegation/worker-lifecycle.d.ts +44 -1
  129. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  130. package/dist/core/delegation/worker-lifecycle.js +120 -0
  131. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  132. package/dist/core/delegation/worker-process-owner.d.ts +18 -0
  133. package/dist/core/delegation/worker-process-owner.d.ts.map +1 -0
  134. package/dist/core/delegation/worker-process-owner.js +54 -0
  135. package/dist/core/delegation/worker-process-owner.js.map +1 -0
  136. package/dist/core/delegation/worker-profile-resolver.d.ts +5 -1
  137. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  138. package/dist/core/delegation/worker-profile-resolver.js +8 -0
  139. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  140. package/dist/core/delegation/worker-recovery-coordinator.d.ts +54 -0
  141. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -0
  142. package/dist/core/delegation/worker-recovery-coordinator.js +181 -0
  143. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -0
  144. package/dist/core/delegation/worker-resource-catalog.d.ts +32 -0
  145. package/dist/core/delegation/worker-resource-catalog.d.ts.map +1 -0
  146. package/dist/core/delegation/worker-resource-catalog.js +116 -0
  147. package/dist/core/delegation/worker-resource-catalog.js.map +1 -0
  148. package/dist/core/delegation/worker-resource-materializer.d.ts +47 -0
  149. package/dist/core/delegation/worker-resource-materializer.d.ts.map +1 -0
  150. package/dist/core/delegation/worker-resource-materializer.js +137 -0
  151. package/dist/core/delegation/worker-resource-materializer.js.map +1 -0
  152. package/dist/core/delegation/worker-runner.d.ts +3 -1
  153. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  154. package/dist/core/delegation/worker-runner.js +189 -54
  155. package/dist/core/delegation/worker-runner.js.map +1 -1
  156. package/dist/core/delegation/worker-terminal-finalizer.d.ts +23 -0
  157. package/dist/core/delegation/worker-terminal-finalizer.d.ts.map +1 -0
  158. package/dist/core/delegation/worker-terminal-finalizer.js +12 -0
  159. package/dist/core/delegation/worker-terminal-finalizer.js.map +1 -0
  160. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +38 -0
  161. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -0
  162. package/dist/core/delegation/worker-write-reservation-coordinator.js +107 -0
  163. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -0
  164. package/dist/core/delegation/worker-write-reservation.d.ts +90 -0
  165. package/dist/core/delegation/worker-write-reservation.d.ts.map +1 -0
  166. package/dist/core/delegation/worker-write-reservation.js +348 -0
  167. package/dist/core/delegation/worker-write-reservation.js.map +1 -0
  168. package/dist/core/extension-import-authority.d.ts +6 -0
  169. package/dist/core/extension-import-authority.d.ts.map +1 -0
  170. package/dist/core/extension-import-authority.js +10 -0
  171. package/dist/core/extension-import-authority.js.map +1 -0
  172. package/dist/core/extensions/loader.d.ts.map +1 -1
  173. package/dist/core/extensions/loader.js +2 -3
  174. package/dist/core/extensions/loader.js.map +1 -1
  175. package/dist/core/extensions/runner.d.ts +3 -0
  176. package/dist/core/extensions/runner.d.ts.map +1 -1
  177. package/dist/core/extensions/runner.js +14 -5
  178. package/dist/core/extensions/runner.js.map +1 -1
  179. package/dist/core/extensions/stale-context.d.ts +2 -0
  180. package/dist/core/extensions/stale-context.d.ts.map +1 -0
  181. package/dist/core/extensions/stale-context.js +2 -0
  182. package/dist/core/extensions/stale-context.js.map +1 -0
  183. package/dist/core/extensions/types.d.ts +8 -0
  184. package/dist/core/extensions/types.d.ts.map +1 -1
  185. package/dist/core/extensions/types.js.map +1 -1
  186. package/dist/core/foreground-recovery-controller.d.ts +1 -0
  187. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  188. package/dist/core/foreground-recovery-controller.js +13 -13
  189. package/dist/core/foreground-recovery-controller.js.map +1 -1
  190. package/dist/core/goal-loop-controller.d.ts +14 -9
  191. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  192. package/dist/core/goal-loop-controller.js +47 -75
  193. package/dist/core/goal-loop-controller.js.map +1 -1
  194. package/dist/core/goals/compact-goal-context.d.ts +11 -0
  195. package/dist/core/goals/compact-goal-context.d.ts.map +1 -0
  196. package/dist/core/goals/compact-goal-context.js +59 -0
  197. package/dist/core/goals/compact-goal-context.js.map +1 -0
  198. package/dist/core/goals/goal-acceptance.d.ts.map +1 -1
  199. package/dist/core/goals/goal-acceptance.js +2 -0
  200. package/dist/core/goals/goal-acceptance.js.map +1 -1
  201. package/dist/core/goals/goal-auto-continue-controller.d.ts +2 -1
  202. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  203. package/dist/core/goals/goal-auto-continue-controller.js +5 -8
  204. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  205. package/dist/core/goals/goal-continuation-controller.d.ts +2 -2
  206. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  207. package/dist/core/goals/goal-continuation-controller.js +42 -11
  208. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  209. package/dist/core/goals/goal-continuation-defaults.d.ts +4 -29
  210. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  211. package/dist/core/goals/goal-continuation-defaults.js +4 -32
  212. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  213. package/dist/core/goals/goal-continuation-prompt.d.ts +8 -18
  214. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  215. package/dist/core/goals/goal-continuation-prompt.js +9 -179
  216. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  217. package/dist/core/goals/goal-lifecycle.d.ts +35 -12
  218. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -1
  219. package/dist/core/goals/goal-lifecycle.js +98 -13
  220. package/dist/core/goals/goal-lifecycle.js.map +1 -1
  221. package/dist/core/goals/goal-record.d.ts +17 -0
  222. package/dist/core/goals/goal-record.d.ts.map +1 -0
  223. package/dist/core/goals/goal-record.js +31 -0
  224. package/dist/core/goals/goal-record.js.map +1 -0
  225. package/dist/core/goals/goal-session-controller.d.ts +42 -0
  226. package/dist/core/goals/goal-session-controller.d.ts.map +1 -0
  227. package/dist/core/goals/goal-session-controller.js +128 -0
  228. package/dist/core/goals/goal-session-controller.js.map +1 -0
  229. package/dist/core/goals/goal-state.d.ts +45 -25
  230. package/dist/core/goals/goal-state.d.ts.map +1 -1
  231. package/dist/core/goals/goal-state.js +85 -15
  232. package/dist/core/goals/goal-state.js.map +1 -1
  233. package/dist/core/goals/goal-tool-core.d.ts +1 -4
  234. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  235. package/dist/core/goals/goal-tool-core.js +49 -34
  236. package/dist/core/goals/goal-tool-core.js.map +1 -1
  237. package/dist/core/goals/session-goal-state.d.ts +18 -8
  238. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  239. package/dist/core/goals/session-goal-state.js +134 -11
  240. package/dist/core/goals/session-goal-state.js.map +1 -1
  241. package/dist/core/human-input-controller.d.ts +47 -0
  242. package/dist/core/human-input-controller.d.ts.map +1 -0
  243. package/dist/core/human-input-controller.js +181 -0
  244. package/dist/core/human-input-controller.js.map +1 -0
  245. package/dist/core/local-runtime-controller.d.ts +1 -1
  246. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  247. package/dist/core/local-runtime-controller.js.map +1 -1
  248. package/dist/core/model-capability.d.ts +0 -19
  249. package/dist/core/model-capability.d.ts.map +1 -1
  250. package/dist/core/model-capability.js +0 -19
  251. package/dist/core/model-capability.js.map +1 -1
  252. package/dist/core/model-registry.d.ts +6 -1
  253. package/dist/core/model-registry.d.ts.map +1 -1
  254. package/dist/core/model-registry.js +13 -5
  255. package/dist/core/model-registry.js.map +1 -1
  256. package/dist/core/model-resolver.d.ts +16 -1
  257. package/dist/core/model-resolver.d.ts.map +1 -1
  258. package/dist/core/model-resolver.js +32 -16
  259. package/dist/core/model-resolver.js.map +1 -1
  260. package/dist/core/model-router-controller.d.ts +1 -1
  261. package/dist/core/model-router-controller.d.ts.map +1 -1
  262. package/dist/core/model-router-controller.js.map +1 -1
  263. package/dist/core/model-selection-controller.d.ts +1 -1
  264. package/dist/core/model-selection-controller.d.ts.map +1 -1
  265. package/dist/core/model-selection-controller.js.map +1 -1
  266. package/dist/core/models/model-endpoint.d.ts +3 -0
  267. package/dist/core/models/model-endpoint.d.ts.map +1 -0
  268. package/dist/core/models/model-endpoint.js +11 -0
  269. package/dist/core/models/model-endpoint.js.map +1 -0
  270. package/dist/core/orchestration/agent-resume.d.ts +2 -0
  271. package/dist/core/orchestration/agent-resume.d.ts.map +1 -1
  272. package/dist/core/orchestration/agent-resume.js +7 -0
  273. package/dist/core/orchestration/agent-resume.js.map +1 -1
  274. package/dist/core/orchestration/attempt-usage.d.ts +28 -0
  275. package/dist/core/orchestration/attempt-usage.d.ts.map +1 -0
  276. package/dist/core/orchestration/attempt-usage.js +168 -0
  277. package/dist/core/orchestration/attempt-usage.js.map +1 -0
  278. package/dist/core/orchestration/capability-gateway.d.ts +19 -1
  279. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  280. package/dist/core/orchestration/capability-gateway.js +128 -28
  281. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  282. package/dist/core/orchestration/contracts.d.ts +35 -0
  283. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  284. package/dist/core/orchestration/contracts.js +17 -0
  285. package/dist/core/orchestration/contracts.js.map +1 -1
  286. package/dist/core/orchestration/delegation-ledger.d.ts +17 -2
  287. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  288. package/dist/core/orchestration/delegation-ledger.js +81 -3
  289. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  290. package/dist/core/orchestration/event-store.d.ts +1 -0
  291. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  292. package/dist/core/orchestration/event-store.js +78 -38
  293. package/dist/core/orchestration/event-store.js.map +1 -1
  294. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  295. package/dist/core/orchestration/profile-registry.js +81 -41
  296. package/dist/core/orchestration/profile-registry.js.map +1 -1
  297. package/dist/core/orchestration/profile-store.d.ts.map +1 -1
  298. package/dist/core/orchestration/profile-store.js +18 -4
  299. package/dist/core/orchestration/profile-store.js.map +1 -1
  300. package/dist/core/orchestration/risk-budget.d.ts +2 -0
  301. package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
  302. package/dist/core/orchestration/risk-budget.js +26 -0
  303. package/dist/core/orchestration/risk-budget.js.map +1 -1
  304. package/dist/core/orchestration/task-runtime.d.ts +13 -1
  305. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  306. package/dist/core/orchestration/task-runtime.js +127 -16
  307. package/dist/core/orchestration/task-runtime.js.map +1 -1
  308. package/dist/core/orchestration/work-state-projection.d.ts.map +1 -1
  309. package/dist/core/orchestration/work-state-projection.js +1 -1
  310. package/dist/core/orchestration/work-state-projection.js.map +1 -1
  311. package/dist/core/orchestration/worker-execution-contract.d.ts +3 -1
  312. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  313. package/dist/core/orchestration/worker-execution-contract.js +75 -23
  314. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  315. package/dist/core/orchestration/worker-result-adapter.d.ts +9 -3
  316. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
  317. package/dist/core/orchestration/worker-result-adapter.js +167 -14
  318. package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
  319. package/dist/core/package-manager.d.ts.map +1 -1
  320. package/dist/core/package-manager.js +20 -3
  321. package/dist/core/package-manager.js.map +1 -1
  322. package/dist/core/profile-filter-controller.d.ts +8 -1
  323. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  324. package/dist/core/profile-filter-controller.js +23 -5
  325. package/dist/core/profile-filter-controller.js.map +1 -1
  326. package/dist/core/prompt-templates.d.ts +1 -0
  327. package/dist/core/prompt-templates.d.ts.map +1 -1
  328. package/dist/core/prompt-templates.js +18 -29
  329. package/dist/core/prompt-templates.js.map +1 -1
  330. package/dist/core/reflection-controller.d.ts +2 -2
  331. package/dist/core/reflection-controller.d.ts.map +1 -1
  332. package/dist/core/reflection-controller.js +92 -54
  333. package/dist/core/reflection-controller.js.map +1 -1
  334. package/dist/core/request-auth.d.ts +7 -0
  335. package/dist/core/request-auth.d.ts.map +1 -0
  336. package/dist/core/request-auth.js +8 -0
  337. package/dist/core/request-auth.js.map +1 -0
  338. package/dist/core/research/evidence-bundle.d.ts +20 -0
  339. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  340. package/dist/core/research/evidence-bundle.js +173 -69
  341. package/dist/core/research/evidence-bundle.js.map +1 -1
  342. package/dist/core/resource-loader.d.ts.map +1 -1
  343. package/dist/core/resource-loader.js +16 -6
  344. package/dist/core/resource-loader.js.map +1 -1
  345. package/dist/core/risk-classifier.d.ts.map +1 -1
  346. package/dist/core/risk-classifier.js +8 -0
  347. package/dist/core/risk-classifier.js.map +1 -1
  348. package/dist/core/runtime-builder.d.ts +22 -3
  349. package/dist/core/runtime-builder.d.ts.map +1 -1
  350. package/dist/core/runtime-builder.js +147 -80
  351. package/dist/core/runtime-builder.js.map +1 -1
  352. package/dist/core/sdk.d.ts.map +1 -1
  353. package/dist/core/sdk.js +7 -1
  354. package/dist/core/sdk.js.map +1 -1
  355. package/dist/core/secrets/credential-exposure-guard.d.ts +9 -0
  356. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -0
  357. package/dist/core/secrets/credential-exposure-guard.js +241 -0
  358. package/dist/core/secrets/credential-exposure-guard.js.map +1 -0
  359. package/dist/core/secrets/secret-dotenv.d.ts +27 -0
  360. package/dist/core/secrets/secret-dotenv.d.ts.map +1 -0
  361. package/dist/core/secrets/secret-dotenv.js +183 -0
  362. package/dist/core/secrets/secret-dotenv.js.map +1 -0
  363. package/dist/core/secrets/secret-vault-format.d.ts +112 -0
  364. package/dist/core/secrets/secret-vault-format.d.ts.map +1 -0
  365. package/dist/core/secrets/secret-vault-format.js +397 -0
  366. package/dist/core/secrets/secret-vault-format.js.map +1 -0
  367. package/dist/core/secrets/secret-vault.d.ts +72 -0
  368. package/dist/core/secrets/secret-vault.d.ts.map +1 -0
  369. package/dist/core/secrets/secret-vault.js +671 -0
  370. package/dist/core/secrets/secret-vault.js.map +1 -0
  371. package/dist/core/session-analytics.d.ts +4 -23
  372. package/dist/core/session-analytics.d.ts.map +1 -1
  373. package/dist/core/session-analytics.js +39 -69
  374. package/dist/core/session-analytics.js.map +1 -1
  375. package/dist/core/session-artifact-bundle.d.ts +35 -0
  376. package/dist/core/session-artifact-bundle.d.ts.map +1 -0
  377. package/dist/core/session-artifact-bundle.js +61 -0
  378. package/dist/core/session-artifact-bundle.js.map +1 -0
  379. package/dist/core/session-role.d.ts +5 -0
  380. package/dist/core/session-role.d.ts.map +1 -1
  381. package/dist/core/session-role.js +14 -1
  382. package/dist/core/session-role.js.map +1 -1
  383. package/dist/core/session-tree-navigator.d.ts +4 -6
  384. package/dist/core/session-tree-navigator.d.ts.map +1 -1
  385. package/dist/core/session-tree-navigator.js +4 -1
  386. package/dist/core/session-tree-navigator.js.map +1 -1
  387. package/dist/core/settings-manager.d.ts +1 -1
  388. package/dist/core/settings-manager.d.ts.map +1 -1
  389. package/dist/core/settings-manager.js +1 -1
  390. package/dist/core/settings-manager.js.map +1 -1
  391. package/dist/core/slash-commands.d.ts.map +1 -1
  392. package/dist/core/slash-commands.js +2 -2
  393. package/dist/core/slash-commands.js.map +1 -1
  394. package/dist/core/tool-capability-policy.d.ts.map +1 -1
  395. package/dist/core/tool-capability-policy.js +1 -0
  396. package/dist/core/tool-capability-policy.js.map +1 -1
  397. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  398. package/dist/core/tool-gate-controller.js +8 -2
  399. package/dist/core/tool-gate-controller.js.map +1 -1
  400. package/dist/core/tool-protocol-controller.d.ts +2 -4
  401. package/dist/core/tool-protocol-controller.d.ts.map +1 -1
  402. package/dist/core/tool-protocol-controller.js.map +1 -1
  403. package/dist/core/tool-recovery-stats.d.ts +21 -1
  404. package/dist/core/tool-recovery-stats.d.ts.map +1 -1
  405. package/dist/core/tool-recovery-stats.js.map +1 -1
  406. package/dist/core/tools/bash.d.ts +5 -0
  407. package/dist/core/tools/bash.d.ts.map +1 -1
  408. package/dist/core/tools/bash.js +59 -5
  409. package/dist/core/tools/bash.js.map +1 -1
  410. package/dist/core/tools/delegate-status.d.ts +6 -1
  411. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  412. package/dist/core/tools/delegate-status.js +106 -25
  413. package/dist/core/tools/delegate-status.js.map +1 -1
  414. package/dist/core/tools/delegate.d.ts +9 -1
  415. package/dist/core/tools/delegate.d.ts.map +1 -1
  416. package/dist/core/tools/delegate.js +329 -69
  417. package/dist/core/tools/delegate.js.map +1 -1
  418. package/dist/core/tools/git-filter.d.ts +1 -8
  419. package/dist/core/tools/git-filter.d.ts.map +1 -1
  420. package/dist/core/tools/git-filter.js +2 -63
  421. package/dist/core/tools/git-filter.js.map +1 -1
  422. package/dist/core/tools/goal.d.ts +5 -3
  423. package/dist/core/tools/goal.d.ts.map +1 -1
  424. package/dist/core/tools/goal.js +97 -19
  425. package/dist/core/tools/goal.js.map +1 -1
  426. package/dist/core/tools/index.d.ts +1 -0
  427. package/dist/core/tools/index.d.ts.map +1 -1
  428. package/dist/core/tools/index.js +1 -0
  429. package/dist/core/tools/index.js.map +1 -1
  430. package/dist/core/tools/orchestration-panel.d.ts +1 -4
  431. package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
  432. package/dist/core/tools/orchestration-panel.js +0 -44
  433. package/dist/core/tools/orchestration-panel.js.map +1 -1
  434. package/dist/core/tools/output-accumulator.d.ts +11 -0
  435. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  436. package/dist/core/tools/output-accumulator.js +36 -3
  437. package/dist/core/tools/output-accumulator.js.map +1 -1
  438. package/dist/core/tools/python.d.ts +2 -0
  439. package/dist/core/tools/python.d.ts.map +1 -1
  440. package/dist/core/tools/python.js +3 -1
  441. package/dist/core/tools/python.js.map +1 -1
  442. package/dist/core/tools/read.d.ts.map +1 -1
  443. package/dist/core/tools/read.js +1 -1
  444. package/dist/core/tools/read.js.map +1 -1
  445. package/dist/core/tools/run-process.d.ts +1 -1
  446. package/dist/core/tools/run-process.d.ts.map +1 -1
  447. package/dist/core/tools/run-process.js +69 -9
  448. package/dist/core/tools/run-process.js.map +1 -1
  449. package/dist/core/tools/search-command-guard.d.ts +14 -0
  450. package/dist/core/tools/search-command-guard.d.ts.map +1 -0
  451. package/dist/core/tools/search-command-guard.js +333 -0
  452. package/dist/core/tools/search-command-guard.js.map +1 -0
  453. package/dist/core/tools/secret-store.d.ts +38 -0
  454. package/dist/core/tools/secret-store.d.ts.map +1 -0
  455. package/dist/core/tools/secret-store.js +412 -0
  456. package/dist/core/tools/secret-store.js.map +1 -0
  457. package/dist/core/tools/shell-command-parser.d.ts +16 -0
  458. package/dist/core/tools/shell-command-parser.d.ts.map +1 -0
  459. package/dist/core/tools/shell-command-parser.js +113 -0
  460. package/dist/core/tools/shell-command-parser.js.map +1 -0
  461. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  462. package/dist/core/tools/shell-contract-router.js +12 -5
  463. package/dist/core/tools/shell-contract-router.js.map +1 -1
  464. package/dist/core/tools/task-steps.d.ts.map +1 -1
  465. package/dist/core/tools/task-steps.js +4 -7
  466. package/dist/core/tools/task-steps.js.map +1 -1
  467. package/dist/core/util/atomic-file.d.ts +6 -2
  468. package/dist/core/util/atomic-file.d.ts.map +1 -1
  469. package/dist/core/util/atomic-file.js +4 -4
  470. package/dist/core/util/atomic-file.js.map +1 -1
  471. package/dist/core/util/bounded-file.d.ts +15 -0
  472. package/dist/core/util/bounded-file.d.ts.map +1 -0
  473. package/dist/core/util/bounded-file.js +120 -0
  474. package/dist/core/util/bounded-file.js.map +1 -0
  475. package/dist/core/util/bounded-jsonl.d.ts.map +1 -1
  476. package/dist/core/util/bounded-jsonl.js +119 -9
  477. package/dist/core/util/bounded-jsonl.js.map +1 -1
  478. package/dist/core/util/bounded-value.d.ts +3 -0
  479. package/dist/core/util/bounded-value.d.ts.map +1 -0
  480. package/dist/core/util/bounded-value.js +12 -0
  481. package/dist/core/util/bounded-value.js.map +1 -0
  482. package/dist/core/util/value-guards.d.ts +2 -0
  483. package/dist/core/util/value-guards.d.ts.map +1 -1
  484. package/dist/core/util/value-guards.js +5 -0
  485. package/dist/core/util/value-guards.js.map +1 -1
  486. package/dist/index.d.ts +3 -2
  487. package/dist/index.d.ts.map +1 -1
  488. package/dist/index.js +2 -1
  489. package/dist/index.js.map +1 -1
  490. package/dist/main.d.ts.map +1 -1
  491. package/dist/main.js +25 -11
  492. package/dist/main.js.map +1 -1
  493. package/dist/modes/interactive/auth-dialogs-controller.d.ts +4 -0
  494. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  495. package/dist/modes/interactive/auth-dialogs-controller.js +60 -1
  496. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  497. package/dist/modes/interactive/components/activity-lane.d.ts +55 -0
  498. package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -0
  499. package/dist/modes/interactive/components/activity-lane.js +304 -0
  500. package/dist/modes/interactive/components/activity-lane.js.map +1 -0
  501. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  502. package/dist/modes/interactive/components/assistant-message.js +19 -10
  503. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  504. package/dist/modes/interactive/components/expandable-text.d.ts +12 -0
  505. package/dist/modes/interactive/components/expandable-text.d.ts.map +1 -0
  506. package/dist/modes/interactive/components/expandable-text.js +17 -0
  507. package/dist/modes/interactive/components/expandable-text.js.map +1 -0
  508. package/dist/modes/interactive/components/extension-editor.d.ts +15 -1
  509. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  510. package/dist/modes/interactive/components/extension-editor.js +30 -21
  511. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  512. package/dist/modes/interactive/components/extension-input.d.ts +1 -0
  513. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  514. package/dist/modes/interactive/components/extension-input.js +6 -1
  515. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  516. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  517. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/scoped-models-selector.js +23 -14
  519. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  520. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/session-selector.js +27 -44
  522. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  523. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  524. package/dist/modes/interactive/components/settings-selector.js +4 -4
  525. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  526. package/dist/modes/interactive/components/tool-execution.d.ts +19 -11
  527. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  528. package/dist/modes/interactive/components/tool-execution.js +53 -20
  529. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  530. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  531. package/dist/modes/interactive/components/tool-group.js +3 -0
  532. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  533. package/dist/modes/interactive/components/usage-action-selector.d.ts +16 -0
  534. package/dist/modes/interactive/components/usage-action-selector.d.ts.map +1 -0
  535. package/dist/modes/interactive/components/usage-action-selector.js +46 -0
  536. package/dist/modes/interactive/components/usage-action-selector.js.map +1 -0
  537. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
  538. package/dist/modes/interactive/extension-ui-host.js +1 -0
  539. package/dist/modes/interactive/extension-ui-host.js.map +1 -1
  540. package/dist/modes/interactive/external-editor.d.ts +15 -0
  541. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  542. package/dist/modes/interactive/external-editor.js +27 -19
  543. package/dist/modes/interactive/external-editor.js.map +1 -1
  544. package/dist/modes/interactive/history-reload-math.d.ts +6 -2
  545. package/dist/modes/interactive/history-reload-math.d.ts.map +1 -1
  546. package/dist/modes/interactive/history-reload-math.js +10 -5
  547. package/dist/modes/interactive/history-reload-math.js.map +1 -1
  548. package/dist/modes/interactive/interactive-event-controller.d.ts +74 -0
  549. package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -0
  550. package/dist/modes/interactive/interactive-event-controller.js +328 -0
  551. package/dist/modes/interactive/interactive-event-controller.js.map +1 -0
  552. package/dist/modes/interactive/interactive-mode.d.ts +20 -25
  553. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  554. package/dist/modes/interactive/interactive-mode.js +196 -608
  555. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  556. package/dist/modes/interactive/loaded-resources-view.d.ts +20 -0
  557. package/dist/modes/interactive/loaded-resources-view.d.ts.map +1 -0
  558. package/dist/modes/interactive/loaded-resources-view.js +147 -0
  559. package/dist/modes/interactive/loaded-resources-view.js.map +1 -0
  560. package/dist/modes/interactive/resource-display.d.ts.map +1 -1
  561. package/dist/modes/interactive/resource-display.js +8 -2
  562. package/dist/modes/interactive/resource-display.js.map +1 -1
  563. package/dist/modes/interactive/resource-shell-commands.d.ts +1 -5
  564. package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
  565. package/dist/modes/interactive/resource-shell-commands.js +0 -5
  566. package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
  567. package/dist/modes/interactive/session-flow-commands.d.ts +10 -6
  568. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  569. package/dist/modes/interactive/session-flow-commands.js +123 -70
  570. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  571. package/dist/modes/interactive/usage-commands.d.ts +34 -0
  572. package/dist/modes/interactive/usage-commands.d.ts.map +1 -0
  573. package/dist/modes/interactive/usage-commands.js +225 -0
  574. package/dist/modes/interactive/usage-commands.js.map +1 -0
  575. package/dist/utils/shell.d.ts.map +1 -1
  576. package/dist/utils/shell.js +5 -3
  577. package/dist/utils/shell.js.map +1 -1
  578. package/dist/utils/version-check.d.ts.map +1 -1
  579. package/dist/utils/version-check.js +3 -1
  580. package/dist/utils/version-check.js.map +1 -1
  581. package/docs/UI_POLISH_ORCHESTRATION.md +25 -31
  582. package/docs/UI_QUALITY_ORCHESTRATION.md +22 -24
  583. package/docs/providers.md +9 -1
  584. package/docs/tool-repair.md +3 -0
  585. package/docs/ui-reference/README.md +6 -6
  586. package/docs/ui-reference/orchestration-after.txt +14 -7
  587. package/docs/ui-reference/orchestration-before.txt +12 -6
  588. package/docs/ui-reference/orchestration-reference.txt +15 -5
  589. package/docs/usage.md +9 -1
  590. package/docs/windows.md +4 -2
  591. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  592. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  593. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  594. package/examples/extensions/sandbox/package-lock.json +2 -2
  595. package/examples/extensions/sandbox/package.json +1 -1
  596. package/examples/extensions/with-deps/package-lock.json +2 -2
  597. package/examples/extensions/with-deps/package.json +1 -1
  598. package/npm-shrinkwrap.json +13 -13
  599. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ ## [0.81.42] - 2026-07-27
2
+
3
+ ### Changed
4
+
5
+ - Rebuilt delegated workers around immutable owner-authored execution contracts, durable logical-agent identities, resumable conversations and mailboxes, fenced write reservations, bounded resource materialization, verified model-pinned compaction, and event-driven terminal handoffs.
6
+ - Consolidated session-owned orchestration events, actions, mailboxes, conversations, and deletion under one bounded artifact bundle while keeping cross-session write reservations globally coordinated.
7
+
8
+ ### Fixed
9
+
10
+ - Made the Windows shell engine preserve `$?`, support controlled `exit`, and accept `head`/`tail -N`, so diagnostic-capture command chains complete with a terminal result instead of losing status or treating `exit` as an external command.
11
+ - Added a provider- and tool-neutral WSL incident collector that packages the latest human session, recovery and orchestration records, explicitly requested command logs, TUI evidence, boot identity, installed-runtime hashes, bounded kernel diagnostics, and relevant Windows host events without collecting credential stores or emitting false missing-log warnings.
12
+ - Made pre-start worker failures publish exactly one durable terminal handoff, rejected malformed typed result envelopes, and handled null or Buffer filesystem watcher filenames on Windows.
13
+ - Made orchestration session paths portable across Windows and POSIX and bounded profile, process, event, result, and cumulative usage payloads at their owning contracts.
14
+ - Kept explicit empty worker tool surfaces inside the repair loop and persisted immediate or argument-repaired tool requests in durable execution order, preventing unavailable-tool calls from collapsing into completion errors or divergent transcripts.
15
+ - Preserved bounded host-observed changed-file progress when an agent-bound worker is suspended, so owner-session disposal remains resumable without fabricating terminal parent-session records.
16
+
17
+ ## [0.81.41] - 2026-07-27
18
+
19
+ ### Added
20
+
21
+ - Added a model-blind native secret store with a private plaintext dotenv editor, encrypted bounded profiles and workspace bindings under Pi state, metadata-only tool results, user-only UAC routing, normal Bash/Python environment activation, and credential-path/output exposure guards.
22
+ - Added a provider-neutral shell search guard that rejects broad `rg`/recursive `grep`/`find`/`fd` scans before execution and supports an explicit managed-file override with bounded disk use.
23
+ - Added an OpenAI subscription `/usage` flow for inspecting earned reset passes, reviewing reset details, confirming redemption, retrying idempotently, and refreshing remaining availability.
24
+ - Added one event-driven activity lane above the editor for routing, tools, task steps, goals, workers, queues, compaction, and retries, plus an explicit `--session-mode user|worker` audience contract for unattended terminals.
25
+ - Added managed Amazon Bedrock SSO login for the exact configured AWS profile, with bounded single-flight CLI execution, automatic pre-response recovery, and a strict no-browser boundary for worker and background requests.
26
+
27
+ ### Changed
28
+
29
+ - Made durable goals use compact Codex-style provider context and continue without implicit turn, active-time, or spend limits; only owner-supplied token and per-invocation limits stop execution, while time/spend remain observable accounting.
30
+ - Split AgentSession contracts, durable goal lifecycle/accounting, owner-input replay, interactive event dispatch, and startup-resource rendering into focused owners, with a repository check preventing either coordinator from reclaiming those responsibilities or exceeding its size boundary.
31
+
32
+ ### Fixed
33
+
34
+ - Kept retry-guided compaction provider failures inside the exact-request retry layer instead of restarting the compaction plan as an unknown failure.
35
+ - Removed successful orchestration bookkeeping from collapsed chat history, retained full detail on explicit expansion, and prevented resumed terminal state from replaying as new completion feedback.
36
+ - Deferred retained tool-result reads, renderer construction, and image conversion during collapsed history reload until explicit expansion, with the materialized payload released again on collapse.
37
+ - Prevented denied extension code and embedded profile content from crossing the read/import boundary, preserved exact CLI/live grants through reloads without invalidating their shared runtime, and made core acceptance fail on skipped or todo evidence.
38
+ - Preserved durable usage from tool hooks, compaction, branch summaries, and resumed sessions across session, daily, and cumulative accounting.
39
+ - Rejected resource directories as context files, resolved scoped model IDs literally, exposed unavailable selections, and clarified unsaved-session fork failures.
40
+ - Consolidated external-editor execution, restored the Windows terminal title after package checks, reported aborted retries as failures, and corrected manual update checks, npm uninstall compatibility, read-error rendering, and sibling extension paths.
41
+
1
42
  ## [0.81.40] - 2026-07-24
2
43
 
3
44
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-grant.d.ts","sourceRoot":"","sources":["../../../../src/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from \"node:crypto\";\nimport type { Usage } from \"@caupulican/pi-ai\";\n\n/**\n * The STANDING GRANT for approval-gated tmux dispatch (owner authorizes once via `grant_dispatch`;\n * the extension then dispatches unattended WITHIN the grant's bounds). Pure data + pure decode/decision\n * logic only — no session/tmux/filesystem access, so every function here is directly unit-testable.\n * The extension (index.ts) owns reading/writing session custom entries and calls into this module for\n * decoding and authorization decisions.\n *\n * HONEST TRUST BOUNDARY: this grant bounds WHO may be launched, HOW OFTEN, and WITH WHAT launch-time\n * envelope (pushed into the child's own CLI flags) — it never claims to gate the child's in-process\n * tool loop, which is a separate OS process outside this runtime's `beforeToolCall` machinery.\n */\n\nexport type Provider = \"pi\" | \"codex\" | \"agy\" | \"claude\" | \"opencode\" | \"custom\";\n\nexport const GRANT_CUSTOM_TYPE = \"tmux-dispatch-grant\";\nexport const GRANT_USAGE_CUSTOM_TYPE = \"tmux-dispatch-grant-usage\";\n\nexport interface TmuxDispatchGrantEnvelope {\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n}\n\nexport interface TmuxDispatchGrantBudget {\n\tmaxLaunches: number;\n\tmaxWallClockMs?: number;\n\tmaxUsdAdvisory?: number;\n}\n\nexport interface TmuxDispatchGrant {\n\tgrantId: string;\n\tcreatedAt: string;\n\tagent: Provider;\n\tscope: { goalId?: string };\n\tenvelope: TmuxDispatchGrantEnvelope;\n\tbudget: TmuxDispatchGrantBudget;\n\texpiresAt?: string;\n}\n\n/** Appended by `revoke_grant`. Shares `GRANT_CUSTOM_TYPE` so the SAME latest-on-branch read that\n * resolves an active grant also sees a revocation and stops treating any older grant beneath it as\n * active — a tombstone is a hard stop, never skipped over to resurrect an earlier grant. */\nexport interface TmuxDispatchGrantTombstone {\n\ttombstone: true;\n\tgrantId: string;\n\trevokedAt: string;\n}\n\nexport interface TmuxDispatchGrantUsage {\n\tgrantId: string;\n\tjobId: string;\n\tat: string;\n}\n\nconst PROVIDERS: readonly Provider[] = [\"pi\", \"codex\", \"agy\", \"claude\", \"opencode\", \"custom\"];\n\nfunction isProvider(value: unknown): value is Provider {\n\treturn typeof value === \"string\" && (PROVIDERS as readonly string[]).includes(value);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isTmuxDispatchGrantTombstone(value: unknown): value is TmuxDispatchGrantTombstone {\n\tif (!isPlainRecord(value)) return false;\n\treturn value.tombstone === true && typeof value.grantId === \"string\" && typeof value.revokedAt === \"string\";\n}\n\nexport function isTmuxDispatchGrant(value: unknown): value is TmuxDispatchGrant {\n\tif (!isPlainRecord(value) || value.tombstone === true) return false;\n\tif (typeof value.grantId !== \"string\" || typeof value.createdAt !== \"string\" || !isProvider(value.agent))\n\t\treturn false;\n\tif (!isPlainRecord(value.scope) || !isPlainRecord(value.envelope) || !isPlainRecord(value.budget)) return false;\n\treturn typeof value.budget.maxLaunches === \"number\" && Number.isFinite(value.budget.maxLaunches);\n}\n\nexport function isTmuxDispatchGrantUsage(value: unknown): value is TmuxDispatchGrantUsage {\n\tif (!isPlainRecord(value)) return false;\n\treturn typeof value.grantId === \"string\" && typeof value.jobId === \"string\" && typeof value.at === \"string\";\n}\n\nexport function makeGrantId(): string {\n\treturn `grant-${Date.now().toString(36)}-${crypto.randomBytes(4).toString(\"hex\")}`;\n}\n\nexport interface GrantCoverageRequest {\n\tagent: Provider;\n\tgoalId?: string;\n\tnowIso?: string;\n}\n\n/** Whether `grant` authorizes a launch for `request`: same agent, goal-scope match (an UNSCOPED grant\n * covers any goal including none; a goal-SCOPED grant covers ONLY that exact goalId), and the grant is\n * not past `expiresAt`. Budget (maxLaunches) is checked separately via {@link isGrantBudgetExhausted}\n * because counting usages requires the caller's session-entry access. */\nexport function grantCovers(grant: TmuxDispatchGrant, request: GrantCoverageRequest): boolean {\n\tif (grant.agent !== request.agent) return false;\n\tif (grant.scope.goalId && grant.scope.goalId !== request.goalId) return false;\n\tif (grant.expiresAt) {\n\t\tconst now = Date.parse(request.nowIso ?? new Date().toISOString());\n\t\tconst expiry = Date.parse(grant.expiresAt);\n\t\tif (Number.isFinite(expiry) && Number.isFinite(now) && now >= expiry) return false;\n\t}\n\treturn true;\n}\n\n/** Count spend entries for `grantId` among already-decoded custom-entry payloads (the caller walks the\n * session branch and passes the raw `data` values — this stays pure/session-agnostic). */\nexport function countGrantUsages(grantId: string, usageEntries: readonly unknown[]): number {\n\tlet count = 0;\n\tfor (const entry of usageEntries) if (isTmuxDispatchGrantUsage(entry) && entry.grantId === grantId) count++;\n\treturn count;\n}\n\nexport function isGrantBudgetExhausted(grant: TmuxDispatchGrant, usedCount: number): boolean {\n\treturn usedCount >= grant.budget.maxLaunches;\n}\n\nexport function buildGrantUsageEntry(\n\tgrantId: string,\n\tjobId: string,\n\tnowIso = new Date().toISOString(),\n): TmuxDispatchGrantUsage {\n\treturn { grantId, jobId, at: nowIso };\n}\n\nexport function buildTombstone(grantId: string, nowIso = new Date().toISOString()): TmuxDispatchGrantTombstone {\n\treturn { tombstone: true, grantId, revokedAt: nowIso };\n}\n\nexport interface GrantDispatchParams {\n\tagent: Provider;\n\tgoalId?: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\tmaxLaunches: number;\n\texpiresInMinutes?: number;\n\tmaxUsdAdvisory?: number;\n}\n\n/** Build a new grant record from `grant_dispatch` params. Pure — the caller (index.ts) owns the\n * approval gate (ui.confirm / opt-in flag) and the `pi.appendEntry` persistence; this only shapes the\n * data, so it stays independently testable and never itself decides whether creation is authorized. */\nexport function buildGrant(params: GrantDispatchParams, nowIso = new Date().toISOString()): TmuxDispatchGrant {\n\tif (!Number.isFinite(params.maxLaunches) || params.maxLaunches < 1)\n\t\tthrow new Error(\"grant_dispatch requires maxLaunches >= 1\");\n\tconst expiresAt =\n\t\tparams.expiresInMinutes !== undefined\n\t\t\t? new Date(Date.parse(nowIso) + Math.max(1, params.expiresInMinutes) * 60_000).toISOString()\n\t\t\t: undefined;\n\treturn {\n\t\tgrantId: makeGrantId(),\n\t\tcreatedAt: nowIso,\n\t\tagent: params.agent,\n\t\tscope: { goalId: params.goalId },\n\t\tenvelope: {\n\t\t\tallowedTools: params.allowedTools,\n\t\t\tresourceProfile: params.resourceProfile,\n\t\t\twritePaths: params.writePaths,\n\t\t},\n\t\tbudget: {\n\t\t\tmaxLaunches: Math.trunc(params.maxLaunches),\n\t\t\tmaxUsdAdvisory: params.maxUsdAdvisory,\n\t\t},\n\t\texpiresAt,\n\t};\n}\n\nexport function describeGrant(grant: TmuxDispatchGrant): string {\n\treturn [\n\t\t`Grant id: ${grant.grantId}`,\n\t\t`Agent: ${grant.agent}`,\n\t\tgrant.scope.goalId ? `Goal scope: ${grant.scope.goalId}` : \"Goal scope: none (covers any goal)\",\n\t\t`Max launches: ${grant.budget.maxLaunches}`,\n\t\tgrant.expiresAt ? `Expires: ${grant.expiresAt}` : \"Expires: never (until revoke_grant)\",\n\t\tgrant.envelope.allowedTools?.length\n\t\t\t? `Allowed tools: ${grant.envelope.allowedTools.join(\", \")}`\n\t\t\t: `Allowed tools: read-biased safe default (${DEFAULT_READ_BIASED_TOOLS.join(\", \")})`,\n\t\tgrant.envelope.resourceProfile\n\t\t\t? `Resource profile: ${grant.envelope.resourceProfile}`\n\t\t\t: \"Resource profile: none (child launches with --no-extensions --no-skills)\",\n\t\tgrant.envelope.writePaths?.length\n\t\t\t? `Write paths: ${grant.envelope.writePaths.join(\", \")}`\n\t\t\t: \"Write paths: none declared\",\n\t\tgrant.budget.maxUsdAdvisory !== undefined\n\t\t\t? `Advisory USD cap: ${grant.budget.maxUsdAdvisory} (not enforced across the process boundary — advisory only)`\n\t\t\t: undefined,\n\t]\n\t\t.filter((line): line is string => Boolean(line))\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Launch-profile flags: the REAL lever — pushed into the child `pi` CLI's own config.\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_READ_BIASED_TOOLS: readonly string[] = [\"read\", \"grep\", \"find\", \"ls\"];\n\nexport interface LaunchProfileSource {\n\t/** Human-readable identity for the scoped system prompt (e.g. \"grant <id>\" or a one-shot label). */\n\tidentity: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\t/**\n\t * Worktree-sync lane key this launch is bound to (per-agent -- see `applyLaunchProfile`'s caller,\n\t * which builds a per-agent `LaunchProfileSource` only when that agent carries `worktreeLane`).\n\t * When present, `buildLaunchProfileFlags` appends `--worktree-lane <key>` (the same CLI flag\n\t * `src/cli/args.ts` already exposes, sugar over the `PI_WORKTREE_LANE` env contract) and\n\t * `buildScopedSystemPrompt` appends one lane-doctrine sentence.\n\t */\n\tworktreeLane?: string;\n\t/**\n\t * Process-matrix parent identity for this launch (see `core/process-matrix/runtime.ts`): the\n\t * dispatching process's own pid/sessionId. When present, `buildLaunchProfileFlags` appends\n\t * `--parent-pid`/`--parent-session` (sugar over `PI_PARENT_PID`/`PI_PARENT_SESSION`) so the\n\t * child self-registers as a worker of this session and winds down gracefully if it disappears.\n\t */\n\tparentPid?: number;\n\tparentSession?: string;\n\t/** Stable goal/task identity used to fence process-matrix recovery. */\n\ttaskRef?: string;\n}\n\n/** A grant-covered launch derives its profile from the grant's envelope. */\nexport function launchProfileSourceFromGrant(grant: TmuxDispatchGrant): LaunchProfileSource {\n\treturn {\n\t\tidentity: `grant ${grant.grantId}`,\n\t\tallowedTools: grant.envelope.allowedTools,\n\t\tresourceProfile: grant.envelope.resourceProfile,\n\t\twritePaths: grant.envelope.writePaths,\n\t};\n}\n\n/** A one-shot interactively-approved launch (no standing grant) still gets the conservative default\n * profile — approving a single launch is not the same as defining an unrestricted envelope. */\nexport const ONE_SHOT_LAUNCH_PROFILE_SOURCE: LaunchProfileSource = {\n\tidentity: \"a one-shot owner-approved tmux dispatch (no standing grant)\",\n};\n\nexport interface LaunchProfileFlag {\n\tflag: string;\n\tvalue?: string;\n}\n\n/** Build the CLI flags to append to a child `pi` invocation so the grant's envelope lives in the\n * child's OWN launch config (verified flag names: --tools, --resource-profile, --no-extensions,\n * --no-skills, --append-system-prompt). Never includes --no-approve: the child must keep gating its\n * own hard stops. */\nexport function buildLaunchProfileFlags(source: LaunchProfileSource): LaunchProfileFlag[] {\n\tconst tools = source.allowedTools?.length ? source.allowedTools : DEFAULT_READ_BIASED_TOOLS;\n\tconst flags: LaunchProfileFlag[] = [{ flag: \"--tools\", value: tools.join(\",\") }];\n\tif (source.resourceProfile) flags.push({ flag: \"--resource-profile\", value: source.resourceProfile });\n\telse flags.push({ flag: \"--no-extensions\" }, { flag: \"--no-skills\" });\n\tif (source.worktreeLane) flags.push({ flag: \"--worktree-lane\", value: source.worktreeLane });\n\tif (source.parentPid !== undefined) flags.push({ flag: \"--parent-pid\", value: String(source.parentPid) });\n\tif (source.parentSession) flags.push({ flag: \"--parent-session\", value: source.parentSession });\n\tif (source.taskRef) flags.push({ flag: \"--task-ref\", value: source.taskRef });\n\tflags.push({ flag: \"--append-system-prompt\", value: buildScopedSystemPrompt(source) });\n\treturn flags;\n}\n\nexport function buildScopedSystemPrompt(source: LaunchProfileSource): string {\n\tconst paths = source.writePaths?.length ? source.writePaths.join(\", \") : \"no additional paths granted\";\n\tconst sentences = [\n\t\t`You are a tmux worker dispatched under ${source.identity}.`,\n\t\t`Stay within these write paths: ${paths}.`,\n\t\t\"Hard stops — publishing/npm, git push, tagging a release, changing credentials/auth, or destructive deletion — require returning BLOCKED for owner approval. Never self-approve a hard stop.\",\n\t];\n\tif (source.worktreeLane) {\n\t\tsentences.push(\n\t\t\t`You are bound to worktree-sync lane '${source.worktreeLane}': work only inside this lane's own worktree, integrate exclusively via worktree_sync land, and never touch main directly.`,\n\t\t);\n\t}\n\treturn sentences.join(\" \");\n}\n\n// ---------------------------------------------------------------------------\n// Advisory worker usage claim: budget stays advisory across the process boundary.\n// ---------------------------------------------------------------------------\n\n/** Permissively decode a worker-reported usage CLAIM (a cooperative worker may write this to a\n * well-known sibling file before printing its completion marker). Read-defensive: missing/malformed\n * numeric fields default to 0 rather than throwing — this is untrusted, OPTIONAL, advisory input, never\n * an authoritative billing record. Returns undefined only when `raw` isn't even an object, so the\n * caller can distinguish \"no claim offered\" (skip reporting) from \"a claim was offered\" (report it,\n * however partial). */\nexport function decodeTmuxWorkerUsageClaim(raw: unknown): Usage | undefined {\n\tif (!isPlainRecord(raw)) return undefined;\n\tconst num = (value: unknown): number => (typeof value === \"number\" && Number.isFinite(value) ? value : 0);\n\tconst cost = isPlainRecord(raw.cost) ? raw.cost : {};\n\treturn {\n\t\tinput: num(raw.input),\n\t\toutput: num(raw.output),\n\t\tcacheRead: num(raw.cacheRead),\n\t\tcacheWrite: num(raw.cacheWrite),\n\t\ttotalTokens: num(raw.totalTokens),\n\t\tcost: {\n\t\t\tinput: num(cost.input),\n\t\t\toutput: num(cost.output),\n\t\t\tcacheRead: num(cost.cacheRead),\n\t\t\tcacheWrite: num(cost.cacheWrite),\n\t\t\ttotal: num(cost.total),\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"dispatch-grant.d.ts","sourceRoot":"","sources":["../../../../src/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from \"node:crypto\";\nimport type { Usage } from \"@caupulican/pi-ai\";\n\n/**\n * The STANDING GRANT for approval-gated tmux dispatch (owner authorizes once via `grant_dispatch`;\n * the extension then dispatches unattended WITHIN the grant's bounds). Pure data + pure decode/decision\n * logic only — no session/tmux/filesystem access, so every function here is directly unit-testable.\n * The extension (index.ts) owns reading/writing session custom entries and calls into this module for\n * decoding and authorization decisions.\n *\n * HONEST TRUST BOUNDARY: this grant bounds WHO may be launched, HOW OFTEN, and WITH WHAT launch-time\n * envelope (pushed into the child's own CLI flags) — it never claims to gate the child's in-process\n * tool loop, which is a separate OS process outside this runtime's `beforeToolCall` machinery.\n */\n\nexport type Provider = \"pi\" | \"codex\" | \"agy\" | \"claude\" | \"opencode\" | \"custom\";\n\nexport const GRANT_CUSTOM_TYPE = \"tmux-dispatch-grant\";\nexport const GRANT_USAGE_CUSTOM_TYPE = \"tmux-dispatch-grant-usage\";\n\nexport interface TmuxDispatchGrantEnvelope {\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n}\n\nexport interface TmuxDispatchGrantBudget {\n\tmaxLaunches: number;\n\tmaxWallClockMs?: number;\n\tmaxUsdAdvisory?: number;\n}\n\nexport interface TmuxDispatchGrant {\n\tgrantId: string;\n\tcreatedAt: string;\n\tagent: Provider;\n\tscope: { goalId?: string };\n\tenvelope: TmuxDispatchGrantEnvelope;\n\tbudget: TmuxDispatchGrantBudget;\n\texpiresAt?: string;\n}\n\n/** Appended by `revoke_grant`. Shares `GRANT_CUSTOM_TYPE` so the SAME latest-on-branch read that\n * resolves an active grant also sees a revocation and stops treating any older grant beneath it as\n * active — a tombstone is a hard stop, never skipped over to resurrect an earlier grant. */\nexport interface TmuxDispatchGrantTombstone {\n\ttombstone: true;\n\tgrantId: string;\n\trevokedAt: string;\n}\n\nexport interface TmuxDispatchGrantUsage {\n\tgrantId: string;\n\tjobId: string;\n\tat: string;\n}\n\nconst PROVIDERS: readonly Provider[] = [\"pi\", \"codex\", \"agy\", \"claude\", \"opencode\", \"custom\"];\n\nfunction isProvider(value: unknown): value is Provider {\n\treturn typeof value === \"string\" && (PROVIDERS as readonly string[]).includes(value);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isTmuxDispatchGrantTombstone(value: unknown): value is TmuxDispatchGrantTombstone {\n\tif (!isPlainRecord(value)) return false;\n\treturn value.tombstone === true && typeof value.grantId === \"string\" && typeof value.revokedAt === \"string\";\n}\n\nexport function isTmuxDispatchGrant(value: unknown): value is TmuxDispatchGrant {\n\tif (!isPlainRecord(value) || value.tombstone === true) return false;\n\tif (typeof value.grantId !== \"string\" || typeof value.createdAt !== \"string\" || !isProvider(value.agent))\n\t\treturn false;\n\tif (!isPlainRecord(value.scope) || !isPlainRecord(value.envelope) || !isPlainRecord(value.budget)) return false;\n\treturn typeof value.budget.maxLaunches === \"number\" && Number.isFinite(value.budget.maxLaunches);\n}\n\nexport function isTmuxDispatchGrantUsage(value: unknown): value is TmuxDispatchGrantUsage {\n\tif (!isPlainRecord(value)) return false;\n\treturn typeof value.grantId === \"string\" && typeof value.jobId === \"string\" && typeof value.at === \"string\";\n}\n\nexport function makeGrantId(): string {\n\treturn `grant-${Date.now().toString(36)}-${crypto.randomBytes(4).toString(\"hex\")}`;\n}\n\nexport interface GrantCoverageRequest {\n\tagent: Provider;\n\tgoalId?: string;\n\tnowIso?: string;\n}\n\n/** Whether `grant` authorizes a launch for `request`: same agent, goal-scope match (an UNSCOPED grant\n * covers any goal including none; a goal-SCOPED grant covers ONLY that exact goalId), and the grant is\n * not past `expiresAt`. Budget (maxLaunches) is checked separately via {@link isGrantBudgetExhausted}\n * because counting usages requires the caller's session-entry access. */\nexport function grantCovers(grant: TmuxDispatchGrant, request: GrantCoverageRequest): boolean {\n\tif (grant.agent !== request.agent) return false;\n\tif (grant.scope.goalId && grant.scope.goalId !== request.goalId) return false;\n\tif (grant.expiresAt) {\n\t\tconst now = Date.parse(request.nowIso ?? new Date().toISOString());\n\t\tconst expiry = Date.parse(grant.expiresAt);\n\t\tif (Number.isFinite(expiry) && Number.isFinite(now) && now >= expiry) return false;\n\t}\n\treturn true;\n}\n\n/** Count spend entries for `grantId` among already-decoded custom-entry payloads (the caller walks the\n * session branch and passes the raw `data` values — this stays pure/session-agnostic). */\nexport function countGrantUsages(grantId: string, usageEntries: readonly unknown[]): number {\n\tlet count = 0;\n\tfor (const entry of usageEntries) if (isTmuxDispatchGrantUsage(entry) && entry.grantId === grantId) count++;\n\treturn count;\n}\n\nexport function isGrantBudgetExhausted(grant: TmuxDispatchGrant, usedCount: number): boolean {\n\treturn usedCount >= grant.budget.maxLaunches;\n}\n\nexport function buildGrantUsageEntry(\n\tgrantId: string,\n\tjobId: string,\n\tnowIso = new Date().toISOString(),\n): TmuxDispatchGrantUsage {\n\treturn { grantId, jobId, at: nowIso };\n}\n\nexport function buildTombstone(grantId: string, nowIso = new Date().toISOString()): TmuxDispatchGrantTombstone {\n\treturn { tombstone: true, grantId, revokedAt: nowIso };\n}\n\nexport interface GrantDispatchParams {\n\tagent: Provider;\n\tgoalId?: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\tmaxLaunches: number;\n\texpiresInMinutes?: number;\n\tmaxUsdAdvisory?: number;\n}\n\n/** Build a new grant record from `grant_dispatch` params. Pure — the caller (index.ts) owns the\n * approval gate (ui.confirm / opt-in flag) and the `pi.appendEntry` persistence; this only shapes the\n * data, so it stays independently testable and never itself decides whether creation is authorized. */\nexport function buildGrant(params: GrantDispatchParams, nowIso = new Date().toISOString()): TmuxDispatchGrant {\n\tif (!Number.isFinite(params.maxLaunches) || params.maxLaunches < 1)\n\t\tthrow new Error(\"grant_dispatch requires maxLaunches >= 1\");\n\tconst expiresAt =\n\t\tparams.expiresInMinutes !== undefined\n\t\t\t? new Date(Date.parse(nowIso) + Math.max(1, params.expiresInMinutes) * 60_000).toISOString()\n\t\t\t: undefined;\n\treturn {\n\t\tgrantId: makeGrantId(),\n\t\tcreatedAt: nowIso,\n\t\tagent: params.agent,\n\t\tscope: { goalId: params.goalId },\n\t\tenvelope: {\n\t\t\tallowedTools: params.allowedTools,\n\t\t\tresourceProfile: params.resourceProfile,\n\t\t\twritePaths: params.writePaths,\n\t\t},\n\t\tbudget: {\n\t\t\tmaxLaunches: Math.trunc(params.maxLaunches),\n\t\t\tmaxUsdAdvisory: params.maxUsdAdvisory,\n\t\t},\n\t\texpiresAt,\n\t};\n}\n\nexport function describeGrant(grant: TmuxDispatchGrant): string {\n\treturn [\n\t\t`Grant id: ${grant.grantId}`,\n\t\t`Agent: ${grant.agent}`,\n\t\tgrant.scope.goalId ? `Goal scope: ${grant.scope.goalId}` : \"Goal scope: none (covers any goal)\",\n\t\t`Max launches: ${grant.budget.maxLaunches}`,\n\t\tgrant.expiresAt ? `Expires: ${grant.expiresAt}` : \"Expires: never (until revoke_grant)\",\n\t\tgrant.envelope.allowedTools?.length\n\t\t\t? `Allowed tools: ${grant.envelope.allowedTools.join(\", \")}`\n\t\t\t: `Allowed tools: read-biased safe default (${DEFAULT_READ_BIASED_TOOLS.join(\", \")})`,\n\t\tgrant.envelope.resourceProfile\n\t\t\t? `Resource profile: ${grant.envelope.resourceProfile}`\n\t\t\t: \"Resource profile: none (child launches with --no-extensions --no-skills)\",\n\t\tgrant.envelope.writePaths?.length\n\t\t\t? `Write paths: ${grant.envelope.writePaths.join(\", \")}`\n\t\t\t: \"Write paths: none declared\",\n\t\tgrant.budget.maxUsdAdvisory !== undefined\n\t\t\t? `Advisory USD cap: ${grant.budget.maxUsdAdvisory} (not enforced across the process boundary — advisory only)`\n\t\t\t: undefined,\n\t]\n\t\t.filter((line): line is string => Boolean(line))\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Launch-profile flags: the REAL lever — pushed into the child `pi` CLI's own config.\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_READ_BIASED_TOOLS: readonly string[] = [\"read\", \"grep\", \"find\", \"ls\"];\n\nexport interface LaunchProfileSource {\n\t/** Human-readable identity for the scoped system prompt (e.g. \"grant <id>\" or a one-shot label). */\n\tidentity: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\t/**\n\t * Worktree-sync lane key this launch is bound to (per-agent -- see `applyLaunchProfile`'s caller,\n\t * which builds a per-agent `LaunchProfileSource` only when that agent carries `worktreeLane`).\n\t * When present, `buildLaunchProfileFlags` appends `--worktree-lane <key>` (the same CLI flag\n\t * `src/cli/args.ts` already exposes, sugar over the `PI_WORKTREE_LANE` env contract) and\n\t * `buildScopedSystemPrompt` appends one lane-doctrine sentence.\n\t */\n\tworktreeLane?: string;\n\t/**\n\t * Process-matrix parent identity for this launch (see `core/process-matrix/runtime.ts`): the\n\t * dispatching process's own pid/sessionId. When present, `buildLaunchProfileFlags` appends\n\t * `--parent-pid`/`--parent-session` (sugar over `PI_PARENT_PID`/`PI_PARENT_SESSION`) so the\n\t * child self-registers as a worker of this session and winds down gracefully if it disappears.\n\t */\n\tparentPid?: number;\n\tparentSession?: string;\n\t/** Stable goal/task identity used to fence process-matrix recovery. */\n\ttaskRef?: string;\n}\n\n/** A grant-covered launch derives its profile from the grant's envelope. */\nexport function launchProfileSourceFromGrant(grant: TmuxDispatchGrant): LaunchProfileSource {\n\treturn {\n\t\tidentity: `grant ${grant.grantId}`,\n\t\tallowedTools: grant.envelope.allowedTools,\n\t\tresourceProfile: grant.envelope.resourceProfile,\n\t\twritePaths: grant.envelope.writePaths,\n\t};\n}\n\n/** A one-shot interactively-approved launch (no standing grant) still gets the conservative default\n * profile — approving a single launch is not the same as defining an unrestricted envelope. */\nexport const ONE_SHOT_LAUNCH_PROFILE_SOURCE: LaunchProfileSource = {\n\tidentity: \"a one-shot owner-approved tmux dispatch (no standing grant)\",\n};\n\nexport interface LaunchProfileFlag {\n\tflag: string;\n\tvalue?: string;\n}\n\n/** Build the CLI flags to append to a child `pi` invocation so the grant's envelope lives in the\n * child's OWN launch config (verified flag names: --tools, --resource-profile, --no-extensions,\n * --no-skills, --append-system-prompt). Never includes --no-approve: the child must keep gating its\n * own hard stops. */\nexport function buildLaunchProfileFlags(source: LaunchProfileSource): LaunchProfileFlag[] {\n\tconst tools = source.allowedTools?.length ? source.allowedTools : DEFAULT_READ_BIASED_TOOLS;\n\tconst flags: LaunchProfileFlag[] = [{ flag: \"--tools\", value: tools.join(\",\") }];\n\tif (source.resourceProfile) flags.push({ flag: \"--resource-profile\", value: source.resourceProfile });\n\telse flags.push({ flag: \"--no-extensions\" }, { flag: \"--no-skills\" });\n\tif (source.worktreeLane) flags.push({ flag: \"--worktree-lane\", value: source.worktreeLane });\n\tif (source.parentPid !== undefined) flags.push({ flag: \"--parent-pid\", value: String(source.parentPid) });\n\tif (source.parentSession) flags.push({ flag: \"--parent-session\", value: source.parentSession });\n\tif (source.taskRef) flags.push({ flag: \"--task-ref\", value: source.taskRef });\n\tflags.push({ flag: \"--append-system-prompt\", value: buildScopedSystemPrompt(source) });\n\tflags.push({ flag: \"--session-mode\", value: \"worker\" });\n\treturn flags;\n}\n\nexport function buildScopedSystemPrompt(source: LaunchProfileSource): string {\n\tconst paths = source.writePaths?.length ? source.writePaths.join(\", \") : \"no additional paths granted\";\n\tconst sentences = [\n\t\t`You are a tmux worker dispatched under ${source.identity}.`,\n\t\t`Stay within these write paths: ${paths}.`,\n\t\t\"Hard stops — publishing/npm, git push, tagging a release, changing credentials/auth, or destructive deletion — require returning BLOCKED for owner approval. Never self-approve a hard stop.\",\n\t];\n\tif (source.worktreeLane) {\n\t\tsentences.push(\n\t\t\t`You are bound to worktree-sync lane '${source.worktreeLane}': work only inside this lane's own worktree, integrate exclusively via worktree_sync land, and never touch main directly.`,\n\t\t);\n\t}\n\treturn sentences.join(\" \");\n}\n\n// ---------------------------------------------------------------------------\n// Advisory worker usage claim: budget stays advisory across the process boundary.\n// ---------------------------------------------------------------------------\n\n/** Permissively decode a worker-reported usage CLAIM (a cooperative worker may write this to a\n * well-known sibling file before printing its completion marker). Read-defensive: missing/malformed\n * numeric fields default to 0 rather than throwing — this is untrusted, OPTIONAL, advisory input, never\n * an authoritative billing record. Returns undefined only when `raw` isn't even an object, so the\n * caller can distinguish \"no claim offered\" (skip reporting) from \"a claim was offered\" (report it,\n * however partial). */\nexport function decodeTmuxWorkerUsageClaim(raw: unknown): Usage | undefined {\n\tif (!isPlainRecord(raw)) return undefined;\n\tconst num = (value: unknown): number => (typeof value === \"number\" && Number.isFinite(value) ? value : 0);\n\tconst cost = isPlainRecord(raw.cost) ? raw.cost : {};\n\treturn {\n\t\tinput: num(raw.input),\n\t\toutput: num(raw.output),\n\t\tcacheRead: num(raw.cacheRead),\n\t\tcacheWrite: num(raw.cacheWrite),\n\t\ttotalTokens: num(raw.totalTokens),\n\t\tcost: {\n\t\t\tinput: num(cost.input),\n\t\t\toutput: num(cost.output),\n\t\t\tcacheRead: num(cost.cacheRead),\n\t\t\tcacheWrite: num(cost.cacheWrite),\n\t\t\ttotal: num(cost.total),\n\t\t},\n\t};\n}\n"]}
@@ -152,6 +152,7 @@ export function buildLaunchProfileFlags(source) {
152
152
  if (source.taskRef)
153
153
  flags.push({ flag: "--task-ref", value: source.taskRef });
154
154
  flags.push({ flag: "--append-system-prompt", value: buildScopedSystemPrompt(source) });
155
+ flags.push({ flag: "--session-mode", value: "worker" });
155
156
  return flags;
156
157
  }
157
158
  export function buildScopedSystemPrompt(source) {
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-grant.js","sourceRoot":"","sources":["../../../../src/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from \"node:crypto\";\nimport type { Usage } from \"@caupulican/pi-ai\";\n\n/**\n * The STANDING GRANT for approval-gated tmux dispatch (owner authorizes once via `grant_dispatch`;\n * the extension then dispatches unattended WITHIN the grant's bounds). Pure data + pure decode/decision\n * logic only — no session/tmux/filesystem access, so every function here is directly unit-testable.\n * The extension (index.ts) owns reading/writing session custom entries and calls into this module for\n * decoding and authorization decisions.\n *\n * HONEST TRUST BOUNDARY: this grant bounds WHO may be launched, HOW OFTEN, and WITH WHAT launch-time\n * envelope (pushed into the child's own CLI flags) — it never claims to gate the child's in-process\n * tool loop, which is a separate OS process outside this runtime's `beforeToolCall` machinery.\n */\n\nexport type Provider = \"pi\" | \"codex\" | \"agy\" | \"claude\" | \"opencode\" | \"custom\";\n\nexport const GRANT_CUSTOM_TYPE = \"tmux-dispatch-grant\";\nexport const GRANT_USAGE_CUSTOM_TYPE = \"tmux-dispatch-grant-usage\";\n\nexport interface TmuxDispatchGrantEnvelope {\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n}\n\nexport interface TmuxDispatchGrantBudget {\n\tmaxLaunches: number;\n\tmaxWallClockMs?: number;\n\tmaxUsdAdvisory?: number;\n}\n\nexport interface TmuxDispatchGrant {\n\tgrantId: string;\n\tcreatedAt: string;\n\tagent: Provider;\n\tscope: { goalId?: string };\n\tenvelope: TmuxDispatchGrantEnvelope;\n\tbudget: TmuxDispatchGrantBudget;\n\texpiresAt?: string;\n}\n\n/** Appended by `revoke_grant`. Shares `GRANT_CUSTOM_TYPE` so the SAME latest-on-branch read that\n * resolves an active grant also sees a revocation and stops treating any older grant beneath it as\n * active — a tombstone is a hard stop, never skipped over to resurrect an earlier grant. */\nexport interface TmuxDispatchGrantTombstone {\n\ttombstone: true;\n\tgrantId: string;\n\trevokedAt: string;\n}\n\nexport interface TmuxDispatchGrantUsage {\n\tgrantId: string;\n\tjobId: string;\n\tat: string;\n}\n\nconst PROVIDERS: readonly Provider[] = [\"pi\", \"codex\", \"agy\", \"claude\", \"opencode\", \"custom\"];\n\nfunction isProvider(value: unknown): value is Provider {\n\treturn typeof value === \"string\" && (PROVIDERS as readonly string[]).includes(value);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isTmuxDispatchGrantTombstone(value: unknown): value is TmuxDispatchGrantTombstone {\n\tif (!isPlainRecord(value)) return false;\n\treturn value.tombstone === true && typeof value.grantId === \"string\" && typeof value.revokedAt === \"string\";\n}\n\nexport function isTmuxDispatchGrant(value: unknown): value is TmuxDispatchGrant {\n\tif (!isPlainRecord(value) || value.tombstone === true) return false;\n\tif (typeof value.grantId !== \"string\" || typeof value.createdAt !== \"string\" || !isProvider(value.agent))\n\t\treturn false;\n\tif (!isPlainRecord(value.scope) || !isPlainRecord(value.envelope) || !isPlainRecord(value.budget)) return false;\n\treturn typeof value.budget.maxLaunches === \"number\" && Number.isFinite(value.budget.maxLaunches);\n}\n\nexport function isTmuxDispatchGrantUsage(value: unknown): value is TmuxDispatchGrantUsage {\n\tif (!isPlainRecord(value)) return false;\n\treturn typeof value.grantId === \"string\" && typeof value.jobId === \"string\" && typeof value.at === \"string\";\n}\n\nexport function makeGrantId(): string {\n\treturn `grant-${Date.now().toString(36)}-${crypto.randomBytes(4).toString(\"hex\")}`;\n}\n\nexport interface GrantCoverageRequest {\n\tagent: Provider;\n\tgoalId?: string;\n\tnowIso?: string;\n}\n\n/** Whether `grant` authorizes a launch for `request`: same agent, goal-scope match (an UNSCOPED grant\n * covers any goal including none; a goal-SCOPED grant covers ONLY that exact goalId), and the grant is\n * not past `expiresAt`. Budget (maxLaunches) is checked separately via {@link isGrantBudgetExhausted}\n * because counting usages requires the caller's session-entry access. */\nexport function grantCovers(grant: TmuxDispatchGrant, request: GrantCoverageRequest): boolean {\n\tif (grant.agent !== request.agent) return false;\n\tif (grant.scope.goalId && grant.scope.goalId !== request.goalId) return false;\n\tif (grant.expiresAt) {\n\t\tconst now = Date.parse(request.nowIso ?? new Date().toISOString());\n\t\tconst expiry = Date.parse(grant.expiresAt);\n\t\tif (Number.isFinite(expiry) && Number.isFinite(now) && now >= expiry) return false;\n\t}\n\treturn true;\n}\n\n/** Count spend entries for `grantId` among already-decoded custom-entry payloads (the caller walks the\n * session branch and passes the raw `data` values — this stays pure/session-agnostic). */\nexport function countGrantUsages(grantId: string, usageEntries: readonly unknown[]): number {\n\tlet count = 0;\n\tfor (const entry of usageEntries) if (isTmuxDispatchGrantUsage(entry) && entry.grantId === grantId) count++;\n\treturn count;\n}\n\nexport function isGrantBudgetExhausted(grant: TmuxDispatchGrant, usedCount: number): boolean {\n\treturn usedCount >= grant.budget.maxLaunches;\n}\n\nexport function buildGrantUsageEntry(\n\tgrantId: string,\n\tjobId: string,\n\tnowIso = new Date().toISOString(),\n): TmuxDispatchGrantUsage {\n\treturn { grantId, jobId, at: nowIso };\n}\n\nexport function buildTombstone(grantId: string, nowIso = new Date().toISOString()): TmuxDispatchGrantTombstone {\n\treturn { tombstone: true, grantId, revokedAt: nowIso };\n}\n\nexport interface GrantDispatchParams {\n\tagent: Provider;\n\tgoalId?: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\tmaxLaunches: number;\n\texpiresInMinutes?: number;\n\tmaxUsdAdvisory?: number;\n}\n\n/** Build a new grant record from `grant_dispatch` params. Pure — the caller (index.ts) owns the\n * approval gate (ui.confirm / opt-in flag) and the `pi.appendEntry` persistence; this only shapes the\n * data, so it stays independently testable and never itself decides whether creation is authorized. */\nexport function buildGrant(params: GrantDispatchParams, nowIso = new Date().toISOString()): TmuxDispatchGrant {\n\tif (!Number.isFinite(params.maxLaunches) || params.maxLaunches < 1)\n\t\tthrow new Error(\"grant_dispatch requires maxLaunches >= 1\");\n\tconst expiresAt =\n\t\tparams.expiresInMinutes !== undefined\n\t\t\t? new Date(Date.parse(nowIso) + Math.max(1, params.expiresInMinutes) * 60_000).toISOString()\n\t\t\t: undefined;\n\treturn {\n\t\tgrantId: makeGrantId(),\n\t\tcreatedAt: nowIso,\n\t\tagent: params.agent,\n\t\tscope: { goalId: params.goalId },\n\t\tenvelope: {\n\t\t\tallowedTools: params.allowedTools,\n\t\t\tresourceProfile: params.resourceProfile,\n\t\t\twritePaths: params.writePaths,\n\t\t},\n\t\tbudget: {\n\t\t\tmaxLaunches: Math.trunc(params.maxLaunches),\n\t\t\tmaxUsdAdvisory: params.maxUsdAdvisory,\n\t\t},\n\t\texpiresAt,\n\t};\n}\n\nexport function describeGrant(grant: TmuxDispatchGrant): string {\n\treturn [\n\t\t`Grant id: ${grant.grantId}`,\n\t\t`Agent: ${grant.agent}`,\n\t\tgrant.scope.goalId ? `Goal scope: ${grant.scope.goalId}` : \"Goal scope: none (covers any goal)\",\n\t\t`Max launches: ${grant.budget.maxLaunches}`,\n\t\tgrant.expiresAt ? `Expires: ${grant.expiresAt}` : \"Expires: never (until revoke_grant)\",\n\t\tgrant.envelope.allowedTools?.length\n\t\t\t? `Allowed tools: ${grant.envelope.allowedTools.join(\", \")}`\n\t\t\t: `Allowed tools: read-biased safe default (${DEFAULT_READ_BIASED_TOOLS.join(\", \")})`,\n\t\tgrant.envelope.resourceProfile\n\t\t\t? `Resource profile: ${grant.envelope.resourceProfile}`\n\t\t\t: \"Resource profile: none (child launches with --no-extensions --no-skills)\",\n\t\tgrant.envelope.writePaths?.length\n\t\t\t? `Write paths: ${grant.envelope.writePaths.join(\", \")}`\n\t\t\t: \"Write paths: none declared\",\n\t\tgrant.budget.maxUsdAdvisory !== undefined\n\t\t\t? `Advisory USD cap: ${grant.budget.maxUsdAdvisory} (not enforced across the process boundary — advisory only)`\n\t\t\t: undefined,\n\t]\n\t\t.filter((line): line is string => Boolean(line))\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Launch-profile flags: the REAL lever — pushed into the child `pi` CLI's own config.\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_READ_BIASED_TOOLS: readonly string[] = [\"read\", \"grep\", \"find\", \"ls\"];\n\nexport interface LaunchProfileSource {\n\t/** Human-readable identity for the scoped system prompt (e.g. \"grant <id>\" or a one-shot label). */\n\tidentity: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\t/**\n\t * Worktree-sync lane key this launch is bound to (per-agent -- see `applyLaunchProfile`'s caller,\n\t * which builds a per-agent `LaunchProfileSource` only when that agent carries `worktreeLane`).\n\t * When present, `buildLaunchProfileFlags` appends `--worktree-lane <key>` (the same CLI flag\n\t * `src/cli/args.ts` already exposes, sugar over the `PI_WORKTREE_LANE` env contract) and\n\t * `buildScopedSystemPrompt` appends one lane-doctrine sentence.\n\t */\n\tworktreeLane?: string;\n\t/**\n\t * Process-matrix parent identity for this launch (see `core/process-matrix/runtime.ts`): the\n\t * dispatching process's own pid/sessionId. When present, `buildLaunchProfileFlags` appends\n\t * `--parent-pid`/`--parent-session` (sugar over `PI_PARENT_PID`/`PI_PARENT_SESSION`) so the\n\t * child self-registers as a worker of this session and winds down gracefully if it disappears.\n\t */\n\tparentPid?: number;\n\tparentSession?: string;\n\t/** Stable goal/task identity used to fence process-matrix recovery. */\n\ttaskRef?: string;\n}\n\n/** A grant-covered launch derives its profile from the grant's envelope. */\nexport function launchProfileSourceFromGrant(grant: TmuxDispatchGrant): LaunchProfileSource {\n\treturn {\n\t\tidentity: `grant ${grant.grantId}`,\n\t\tallowedTools: grant.envelope.allowedTools,\n\t\tresourceProfile: grant.envelope.resourceProfile,\n\t\twritePaths: grant.envelope.writePaths,\n\t};\n}\n\n/** A one-shot interactively-approved launch (no standing grant) still gets the conservative default\n * profile — approving a single launch is not the same as defining an unrestricted envelope. */\nexport const ONE_SHOT_LAUNCH_PROFILE_SOURCE: LaunchProfileSource = {\n\tidentity: \"a one-shot owner-approved tmux dispatch (no standing grant)\",\n};\n\nexport interface LaunchProfileFlag {\n\tflag: string;\n\tvalue?: string;\n}\n\n/** Build the CLI flags to append to a child `pi` invocation so the grant's envelope lives in the\n * child's OWN launch config (verified flag names: --tools, --resource-profile, --no-extensions,\n * --no-skills, --append-system-prompt). Never includes --no-approve: the child must keep gating its\n * own hard stops. */\nexport function buildLaunchProfileFlags(source: LaunchProfileSource): LaunchProfileFlag[] {\n\tconst tools = source.allowedTools?.length ? source.allowedTools : DEFAULT_READ_BIASED_TOOLS;\n\tconst flags: LaunchProfileFlag[] = [{ flag: \"--tools\", value: tools.join(\",\") }];\n\tif (source.resourceProfile) flags.push({ flag: \"--resource-profile\", value: source.resourceProfile });\n\telse flags.push({ flag: \"--no-extensions\" }, { flag: \"--no-skills\" });\n\tif (source.worktreeLane) flags.push({ flag: \"--worktree-lane\", value: source.worktreeLane });\n\tif (source.parentPid !== undefined) flags.push({ flag: \"--parent-pid\", value: String(source.parentPid) });\n\tif (source.parentSession) flags.push({ flag: \"--parent-session\", value: source.parentSession });\n\tif (source.taskRef) flags.push({ flag: \"--task-ref\", value: source.taskRef });\n\tflags.push({ flag: \"--append-system-prompt\", value: buildScopedSystemPrompt(source) });\n\treturn flags;\n}\n\nexport function buildScopedSystemPrompt(source: LaunchProfileSource): string {\n\tconst paths = source.writePaths?.length ? source.writePaths.join(\", \") : \"no additional paths granted\";\n\tconst sentences = [\n\t\t`You are a tmux worker dispatched under ${source.identity}.`,\n\t\t`Stay within these write paths: ${paths}.`,\n\t\t\"Hard stops — publishing/npm, git push, tagging a release, changing credentials/auth, or destructive deletion — require returning BLOCKED for owner approval. Never self-approve a hard stop.\",\n\t];\n\tif (source.worktreeLane) {\n\t\tsentences.push(\n\t\t\t`You are bound to worktree-sync lane '${source.worktreeLane}': work only inside this lane's own worktree, integrate exclusively via worktree_sync land, and never touch main directly.`,\n\t\t);\n\t}\n\treturn sentences.join(\" \");\n}\n\n// ---------------------------------------------------------------------------\n// Advisory worker usage claim: budget stays advisory across the process boundary.\n// ---------------------------------------------------------------------------\n\n/** Permissively decode a worker-reported usage CLAIM (a cooperative worker may write this to a\n * well-known sibling file before printing its completion marker). Read-defensive: missing/malformed\n * numeric fields default to 0 rather than throwing — this is untrusted, OPTIONAL, advisory input, never\n * an authoritative billing record. Returns undefined only when `raw` isn't even an object, so the\n * caller can distinguish \"no claim offered\" (skip reporting) from \"a claim was offered\" (report it,\n * however partial). */\nexport function decodeTmuxWorkerUsageClaim(raw: unknown): Usage | undefined {\n\tif (!isPlainRecord(raw)) return undefined;\n\tconst num = (value: unknown): number => (typeof value === \"number\" && Number.isFinite(value) ? value : 0);\n\tconst cost = isPlainRecord(raw.cost) ? raw.cost : {};\n\treturn {\n\t\tinput: num(raw.input),\n\t\toutput: num(raw.output),\n\t\tcacheRead: num(raw.cacheRead),\n\t\tcacheWrite: num(raw.cacheWrite),\n\t\ttotalTokens: num(raw.totalTokens),\n\t\tcost: {\n\t\t\tinput: num(cost.input),\n\t\t\toutput: num(cost.output),\n\t\t\tcacheRead: num(cost.cacheRead),\n\t\t\tcacheWrite: num(cost.cacheWrite),\n\t\t\ttotal: num(cost.total),\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"dispatch-grant.js","sourceRoot":"","sources":["../../../../src/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts"],"names":[],"mappings":"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","sourcesContent":["import * as crypto from \"node:crypto\";\nimport type { Usage } from \"@caupulican/pi-ai\";\n\n/**\n * The STANDING GRANT for approval-gated tmux dispatch (owner authorizes once via `grant_dispatch`;\n * the extension then dispatches unattended WITHIN the grant's bounds). Pure data + pure decode/decision\n * logic only — no session/tmux/filesystem access, so every function here is directly unit-testable.\n * The extension (index.ts) owns reading/writing session custom entries and calls into this module for\n * decoding and authorization decisions.\n *\n * HONEST TRUST BOUNDARY: this grant bounds WHO may be launched, HOW OFTEN, and WITH WHAT launch-time\n * envelope (pushed into the child's own CLI flags) — it never claims to gate the child's in-process\n * tool loop, which is a separate OS process outside this runtime's `beforeToolCall` machinery.\n */\n\nexport type Provider = \"pi\" | \"codex\" | \"agy\" | \"claude\" | \"opencode\" | \"custom\";\n\nexport const GRANT_CUSTOM_TYPE = \"tmux-dispatch-grant\";\nexport const GRANT_USAGE_CUSTOM_TYPE = \"tmux-dispatch-grant-usage\";\n\nexport interface TmuxDispatchGrantEnvelope {\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n}\n\nexport interface TmuxDispatchGrantBudget {\n\tmaxLaunches: number;\n\tmaxWallClockMs?: number;\n\tmaxUsdAdvisory?: number;\n}\n\nexport interface TmuxDispatchGrant {\n\tgrantId: string;\n\tcreatedAt: string;\n\tagent: Provider;\n\tscope: { goalId?: string };\n\tenvelope: TmuxDispatchGrantEnvelope;\n\tbudget: TmuxDispatchGrantBudget;\n\texpiresAt?: string;\n}\n\n/** Appended by `revoke_grant`. Shares `GRANT_CUSTOM_TYPE` so the SAME latest-on-branch read that\n * resolves an active grant also sees a revocation and stops treating any older grant beneath it as\n * active — a tombstone is a hard stop, never skipped over to resurrect an earlier grant. */\nexport interface TmuxDispatchGrantTombstone {\n\ttombstone: true;\n\tgrantId: string;\n\trevokedAt: string;\n}\n\nexport interface TmuxDispatchGrantUsage {\n\tgrantId: string;\n\tjobId: string;\n\tat: string;\n}\n\nconst PROVIDERS: readonly Provider[] = [\"pi\", \"codex\", \"agy\", \"claude\", \"opencode\", \"custom\"];\n\nfunction isProvider(value: unknown): value is Provider {\n\treturn typeof value === \"string\" && (PROVIDERS as readonly string[]).includes(value);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isTmuxDispatchGrantTombstone(value: unknown): value is TmuxDispatchGrantTombstone {\n\tif (!isPlainRecord(value)) return false;\n\treturn value.tombstone === true && typeof value.grantId === \"string\" && typeof value.revokedAt === \"string\";\n}\n\nexport function isTmuxDispatchGrant(value: unknown): value is TmuxDispatchGrant {\n\tif (!isPlainRecord(value) || value.tombstone === true) return false;\n\tif (typeof value.grantId !== \"string\" || typeof value.createdAt !== \"string\" || !isProvider(value.agent))\n\t\treturn false;\n\tif (!isPlainRecord(value.scope) || !isPlainRecord(value.envelope) || !isPlainRecord(value.budget)) return false;\n\treturn typeof value.budget.maxLaunches === \"number\" && Number.isFinite(value.budget.maxLaunches);\n}\n\nexport function isTmuxDispatchGrantUsage(value: unknown): value is TmuxDispatchGrantUsage {\n\tif (!isPlainRecord(value)) return false;\n\treturn typeof value.grantId === \"string\" && typeof value.jobId === \"string\" && typeof value.at === \"string\";\n}\n\nexport function makeGrantId(): string {\n\treturn `grant-${Date.now().toString(36)}-${crypto.randomBytes(4).toString(\"hex\")}`;\n}\n\nexport interface GrantCoverageRequest {\n\tagent: Provider;\n\tgoalId?: string;\n\tnowIso?: string;\n}\n\n/** Whether `grant` authorizes a launch for `request`: same agent, goal-scope match (an UNSCOPED grant\n * covers any goal including none; a goal-SCOPED grant covers ONLY that exact goalId), and the grant is\n * not past `expiresAt`. Budget (maxLaunches) is checked separately via {@link isGrantBudgetExhausted}\n * because counting usages requires the caller's session-entry access. */\nexport function grantCovers(grant: TmuxDispatchGrant, request: GrantCoverageRequest): boolean {\n\tif (grant.agent !== request.agent) return false;\n\tif (grant.scope.goalId && grant.scope.goalId !== request.goalId) return false;\n\tif (grant.expiresAt) {\n\t\tconst now = Date.parse(request.nowIso ?? new Date().toISOString());\n\t\tconst expiry = Date.parse(grant.expiresAt);\n\t\tif (Number.isFinite(expiry) && Number.isFinite(now) && now >= expiry) return false;\n\t}\n\treturn true;\n}\n\n/** Count spend entries for `grantId` among already-decoded custom-entry payloads (the caller walks the\n * session branch and passes the raw `data` values — this stays pure/session-agnostic). */\nexport function countGrantUsages(grantId: string, usageEntries: readonly unknown[]): number {\n\tlet count = 0;\n\tfor (const entry of usageEntries) if (isTmuxDispatchGrantUsage(entry) && entry.grantId === grantId) count++;\n\treturn count;\n}\n\nexport function isGrantBudgetExhausted(grant: TmuxDispatchGrant, usedCount: number): boolean {\n\treturn usedCount >= grant.budget.maxLaunches;\n}\n\nexport function buildGrantUsageEntry(\n\tgrantId: string,\n\tjobId: string,\n\tnowIso = new Date().toISOString(),\n): TmuxDispatchGrantUsage {\n\treturn { grantId, jobId, at: nowIso };\n}\n\nexport function buildTombstone(grantId: string, nowIso = new Date().toISOString()): TmuxDispatchGrantTombstone {\n\treturn { tombstone: true, grantId, revokedAt: nowIso };\n}\n\nexport interface GrantDispatchParams {\n\tagent: Provider;\n\tgoalId?: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\tmaxLaunches: number;\n\texpiresInMinutes?: number;\n\tmaxUsdAdvisory?: number;\n}\n\n/** Build a new grant record from `grant_dispatch` params. Pure — the caller (index.ts) owns the\n * approval gate (ui.confirm / opt-in flag) and the `pi.appendEntry` persistence; this only shapes the\n * data, so it stays independently testable and never itself decides whether creation is authorized. */\nexport function buildGrant(params: GrantDispatchParams, nowIso = new Date().toISOString()): TmuxDispatchGrant {\n\tif (!Number.isFinite(params.maxLaunches) || params.maxLaunches < 1)\n\t\tthrow new Error(\"grant_dispatch requires maxLaunches >= 1\");\n\tconst expiresAt =\n\t\tparams.expiresInMinutes !== undefined\n\t\t\t? new Date(Date.parse(nowIso) + Math.max(1, params.expiresInMinutes) * 60_000).toISOString()\n\t\t\t: undefined;\n\treturn {\n\t\tgrantId: makeGrantId(),\n\t\tcreatedAt: nowIso,\n\t\tagent: params.agent,\n\t\tscope: { goalId: params.goalId },\n\t\tenvelope: {\n\t\t\tallowedTools: params.allowedTools,\n\t\t\tresourceProfile: params.resourceProfile,\n\t\t\twritePaths: params.writePaths,\n\t\t},\n\t\tbudget: {\n\t\t\tmaxLaunches: Math.trunc(params.maxLaunches),\n\t\t\tmaxUsdAdvisory: params.maxUsdAdvisory,\n\t\t},\n\t\texpiresAt,\n\t};\n}\n\nexport function describeGrant(grant: TmuxDispatchGrant): string {\n\treturn [\n\t\t`Grant id: ${grant.grantId}`,\n\t\t`Agent: ${grant.agent}`,\n\t\tgrant.scope.goalId ? `Goal scope: ${grant.scope.goalId}` : \"Goal scope: none (covers any goal)\",\n\t\t`Max launches: ${grant.budget.maxLaunches}`,\n\t\tgrant.expiresAt ? `Expires: ${grant.expiresAt}` : \"Expires: never (until revoke_grant)\",\n\t\tgrant.envelope.allowedTools?.length\n\t\t\t? `Allowed tools: ${grant.envelope.allowedTools.join(\", \")}`\n\t\t\t: `Allowed tools: read-biased safe default (${DEFAULT_READ_BIASED_TOOLS.join(\", \")})`,\n\t\tgrant.envelope.resourceProfile\n\t\t\t? `Resource profile: ${grant.envelope.resourceProfile}`\n\t\t\t: \"Resource profile: none (child launches with --no-extensions --no-skills)\",\n\t\tgrant.envelope.writePaths?.length\n\t\t\t? `Write paths: ${grant.envelope.writePaths.join(\", \")}`\n\t\t\t: \"Write paths: none declared\",\n\t\tgrant.budget.maxUsdAdvisory !== undefined\n\t\t\t? `Advisory USD cap: ${grant.budget.maxUsdAdvisory} (not enforced across the process boundary — advisory only)`\n\t\t\t: undefined,\n\t]\n\t\t.filter((line): line is string => Boolean(line))\n\t\t.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Launch-profile flags: the REAL lever — pushed into the child `pi` CLI's own config.\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_READ_BIASED_TOOLS: readonly string[] = [\"read\", \"grep\", \"find\", \"ls\"];\n\nexport interface LaunchProfileSource {\n\t/** Human-readable identity for the scoped system prompt (e.g. \"grant <id>\" or a one-shot label). */\n\tidentity: string;\n\tallowedTools?: string[];\n\tresourceProfile?: string;\n\twritePaths?: string[];\n\t/**\n\t * Worktree-sync lane key this launch is bound to (per-agent -- see `applyLaunchProfile`'s caller,\n\t * which builds a per-agent `LaunchProfileSource` only when that agent carries `worktreeLane`).\n\t * When present, `buildLaunchProfileFlags` appends `--worktree-lane <key>` (the same CLI flag\n\t * `src/cli/args.ts` already exposes, sugar over the `PI_WORKTREE_LANE` env contract) and\n\t * `buildScopedSystemPrompt` appends one lane-doctrine sentence.\n\t */\n\tworktreeLane?: string;\n\t/**\n\t * Process-matrix parent identity for this launch (see `core/process-matrix/runtime.ts`): the\n\t * dispatching process's own pid/sessionId. When present, `buildLaunchProfileFlags` appends\n\t * `--parent-pid`/`--parent-session` (sugar over `PI_PARENT_PID`/`PI_PARENT_SESSION`) so the\n\t * child self-registers as a worker of this session and winds down gracefully if it disappears.\n\t */\n\tparentPid?: number;\n\tparentSession?: string;\n\t/** Stable goal/task identity used to fence process-matrix recovery. */\n\ttaskRef?: string;\n}\n\n/** A grant-covered launch derives its profile from the grant's envelope. */\nexport function launchProfileSourceFromGrant(grant: TmuxDispatchGrant): LaunchProfileSource {\n\treturn {\n\t\tidentity: `grant ${grant.grantId}`,\n\t\tallowedTools: grant.envelope.allowedTools,\n\t\tresourceProfile: grant.envelope.resourceProfile,\n\t\twritePaths: grant.envelope.writePaths,\n\t};\n}\n\n/** A one-shot interactively-approved launch (no standing grant) still gets the conservative default\n * profile — approving a single launch is not the same as defining an unrestricted envelope. */\nexport const ONE_SHOT_LAUNCH_PROFILE_SOURCE: LaunchProfileSource = {\n\tidentity: \"a one-shot owner-approved tmux dispatch (no standing grant)\",\n};\n\nexport interface LaunchProfileFlag {\n\tflag: string;\n\tvalue?: string;\n}\n\n/** Build the CLI flags to append to a child `pi` invocation so the grant's envelope lives in the\n * child's OWN launch config (verified flag names: --tools, --resource-profile, --no-extensions,\n * --no-skills, --append-system-prompt). Never includes --no-approve: the child must keep gating its\n * own hard stops. */\nexport function buildLaunchProfileFlags(source: LaunchProfileSource): LaunchProfileFlag[] {\n\tconst tools = source.allowedTools?.length ? source.allowedTools : DEFAULT_READ_BIASED_TOOLS;\n\tconst flags: LaunchProfileFlag[] = [{ flag: \"--tools\", value: tools.join(\",\") }];\n\tif (source.resourceProfile) flags.push({ flag: \"--resource-profile\", value: source.resourceProfile });\n\telse flags.push({ flag: \"--no-extensions\" }, { flag: \"--no-skills\" });\n\tif (source.worktreeLane) flags.push({ flag: \"--worktree-lane\", value: source.worktreeLane });\n\tif (source.parentPid !== undefined) flags.push({ flag: \"--parent-pid\", value: String(source.parentPid) });\n\tif (source.parentSession) flags.push({ flag: \"--parent-session\", value: source.parentSession });\n\tif (source.taskRef) flags.push({ flag: \"--task-ref\", value: source.taskRef });\n\tflags.push({ flag: \"--append-system-prompt\", value: buildScopedSystemPrompt(source) });\n\tflags.push({ flag: \"--session-mode\", value: \"worker\" });\n\treturn flags;\n}\n\nexport function buildScopedSystemPrompt(source: LaunchProfileSource): string {\n\tconst paths = source.writePaths?.length ? source.writePaths.join(\", \") : \"no additional paths granted\";\n\tconst sentences = [\n\t\t`You are a tmux worker dispatched under ${source.identity}.`,\n\t\t`Stay within these write paths: ${paths}.`,\n\t\t\"Hard stops — publishing/npm, git push, tagging a release, changing credentials/auth, or destructive deletion — require returning BLOCKED for owner approval. Never self-approve a hard stop.\",\n\t];\n\tif (source.worktreeLane) {\n\t\tsentences.push(\n\t\t\t`You are bound to worktree-sync lane '${source.worktreeLane}': work only inside this lane's own worktree, integrate exclusively via worktree_sync land, and never touch main directly.`,\n\t\t);\n\t}\n\treturn sentences.join(\" \");\n}\n\n// ---------------------------------------------------------------------------\n// Advisory worker usage claim: budget stays advisory across the process boundary.\n// ---------------------------------------------------------------------------\n\n/** Permissively decode a worker-reported usage CLAIM (a cooperative worker may write this to a\n * well-known sibling file before printing its completion marker). Read-defensive: missing/malformed\n * numeric fields default to 0 rather than throwing — this is untrusted, OPTIONAL, advisory input, never\n * an authoritative billing record. Returns undefined only when `raw` isn't even an object, so the\n * caller can distinguish \"no claim offered\" (skip reporting) from \"a claim was offered\" (report it,\n * however partial). */\nexport function decodeTmuxWorkerUsageClaim(raw: unknown): Usage | undefined {\n\tif (!isPlainRecord(raw)) return undefined;\n\tconst num = (value: unknown): number => (typeof value === \"number\" && Number.isFinite(value) ? value : 0);\n\tconst cost = isPlainRecord(raw.cost) ? raw.cost : {};\n\treturn {\n\t\tinput: num(raw.input),\n\t\toutput: num(raw.output),\n\t\tcacheRead: num(raw.cacheRead),\n\t\tcacheWrite: num(raw.cacheWrite),\n\t\ttotalTokens: num(raw.totalTokens),\n\t\tcost: {\n\t\t\tinput: num(cost.input),\n\t\t\toutput: num(cost.output),\n\t\t\tcacheRead: num(cost.cacheRead),\n\t\t\tcacheWrite: num(cost.cacheWrite),\n\t\t\ttotal: num(cost.total),\n\t\t},\n\t};\n}\n"]}
@@ -262,6 +262,7 @@ export function buildLaunchProfileFlags(source: LaunchProfileSource): LaunchProf
262
262
  if (source.parentSession) flags.push({ flag: "--parent-session", value: source.parentSession });
263
263
  if (source.taskRef) flags.push({ flag: "--task-ref", value: source.taskRef });
264
264
  flags.push({ flag: "--append-system-prompt", value: buildScopedSystemPrompt(source) });
265
+ flags.push({ flag: "--session-mode", value: "worker" });
265
266
  return flags;
266
267
  }
267
268
 
@@ -97,7 +97,7 @@ def cat(ctx: "BuiltinContext") -> int:
97
97
 
98
98
 
99
99
  def _parse_n_flag(name: str, argv: list[str]) -> tuple[int, list[str]]:
100
- """Parse `[-n N] [FILE]` only. Any other flag/form -> unsupported-flag."""
100
+ """Parse `[-n N|-N] [FILE]`. Any other flag/form -> unsupported-flag."""
101
101
  n = 10
102
102
  operands: list[str] = []
103
103
  args = argv[1:]
@@ -118,6 +118,10 @@ def _parse_n_flag(name: str, argv: list[str]) -> tuple[int, list[str]]:
118
118
  n = _parse_int_operand(name, a[2:])
119
119
  i += 1
120
120
  continue
121
+ if len(a) > 1 and a[0] == "-" and a[1:].isdigit():
122
+ n = int(a[1:])
123
+ i += 1
124
+ continue
121
125
  if a.startswith("-") and a != "-":
122
126
  _refuse_flag(name, a)
123
127
  operands.append(a)
@@ -46,7 +46,7 @@ class ExecContext:
46
46
  stderr: BinaryIO | int | None = None
47
47
 
48
48
 
49
- # Frozen inline sets: state mutators and the runner builtin are NOT in commands/REGISTRY;
50
- # exec.py handles them inline because they need ShellState / the executor.
51
- STATE_BUILTINS = {"cd", "export", "unset"}
49
+ # Executor-owned builtins are NOT in commands/REGISTRY: state mutators and `exit`
50
+ # need ShellState/control-flow access, while the runner needs the executor.
51
+ STATE_BUILTINS = {"cd", "export", "unset", "exit"}
52
52
  RUNNER_BUILTINS = {"xargs"}
@@ -47,3 +47,11 @@ class UnsupportedConstruct(Exception):
47
47
  self.code = "unsupported"
48
48
  self.construct = construct
49
49
  self.message = message
50
+
51
+
52
+ class ShellExit(Exception):
53
+ """Controlled `exit` request carried to the nearest shell boundary."""
54
+
55
+ def __init__(self, exit_code: int) -> None:
56
+ super().__init__(exit_code)
57
+ self.exit_code = exit_code
@@ -17,7 +17,7 @@ import parser as parser_module
17
17
  import proc
18
18
  import tokens as tokens_module
19
19
  from context import RUNNER_BUILTINS, STATE_BUILTINS, BuiltinContext, ExecContext
20
- from errors import UnsupportedConstruct
20
+ from errors import ShellExit, UnsupportedConstruct
21
21
  from state import ShellState
22
22
 
23
23
  DEVNULL = os.devnull
@@ -209,6 +209,7 @@ def execute(list_node: nodes.CommandList, ctx: ExecContext) -> int:
209
209
  exit_code = 0
210
210
  for entry in list_node.entries:
211
211
  exit_code = _execute_andor(entry, ctx)
212
+ ctx.state.last_exit_code = exit_code
212
213
  return exit_code
213
214
 
214
215
 
@@ -222,6 +223,7 @@ def _execute_andor(andor: nodes.AndOr, ctx: ExecContext) -> int:
222
223
  if operator == "||" and exit_code == 0:
223
224
  continue
224
225
  exit_code = _execute_pipeline(pipeline, ctx)
226
+ ctx.state.last_exit_code = exit_code
225
227
  return exit_code
226
228
 
227
229
 
@@ -310,6 +312,8 @@ def _run_pipeline_elements(elements: list, ctx: ExecContext) -> int:
310
312
  # fabricated exit-0/empty-output success.
311
313
  refusals[idx] = exc
312
314
  results[idx] = 1
315
+ except ShellExit as exc:
316
+ results[idx] = exc.exit_code
313
317
  except BaseException as exc: # noqa: BLE001 - any other stage failure must not crash the engine
314
318
  # Any OTHER exception is a real per-stage failure, not a refusal: it
315
319
  # gets a named, actionable one-line message on the merged sink (never
@@ -477,7 +481,10 @@ def _execute_subshell(node: nodes.Subshell, ctx: ExecContext, stdin_stream, stdo
477
481
  )
478
482
  isolated_state = ctx.state.copy()
479
483
  inner_ctx = _sub_ctx(ctx, isolated_state, r_in, r_out, r_err)
480
- return execute(node.body, inner_ctx)
484
+ try:
485
+ return execute(node.body, inner_ctx)
486
+ except ShellExit as exc:
487
+ return exc.exit_code
481
488
  finally:
482
489
  tracker.close()
483
490
 
@@ -569,6 +576,19 @@ def _as_stream(fd_or_stream: BinaryIO | int, mode: str) -> BinaryIO:
569
576
 
570
577
 
571
578
  def _run_state_builtin(name: str, argv: list[str], ctx: ExecContext, out_stream: BinaryIO | int) -> int:
579
+ if name == "exit":
580
+ if len(argv) > 2:
581
+ _write_merged(out_stream, b"exit: too many arguments\n", ctx)
582
+ raise ShellExit(1)
583
+ if len(argv) == 1:
584
+ raise ShellExit(ctx.state.last_exit_code)
585
+ try:
586
+ exit_code = int(argv[1], 10)
587
+ except ValueError:
588
+ _write_merged(out_stream, f"exit: {argv[1]}: numeric argument required\n".encode("utf-8"), ctx)
589
+ raise ShellExit(2)
590
+ raise ShellExit(exit_code & 0xFF)
591
+
572
592
  if name == "cd":
573
593
  target = argv[1] if len(argv) > 1 else ctx.state.env.get("HOME")
574
594
  print_new_cwd = argv[1:2] == ["-"]
@@ -729,6 +749,9 @@ def run_command_substitution(src: str, ctx: ExecContext) -> tuple[str, int]:
729
749
  deadline=ctx.deadline,
730
750
  stderr=ctx.stderr,
731
751
  )
732
- exit_code = execute(ast, sub_ctx)
752
+ try:
753
+ exit_code = execute(ast, sub_ctx)
754
+ except ShellExit as exc:
755
+ exit_code = exc.exit_code
733
756
  text = buffer.getvalue().decode("utf-8", errors="replace")
734
757
  return text.rstrip("\n"), exit_code
@@ -97,6 +97,9 @@ def _resolve_param(name: str, op: str | None, arg: Word | None, ctx: "ExecContex
97
97
  split_and_glob_eligible is False here always; callers decide splitting based on
98
98
  quoting context (Raw vs DQ), not on the parameter's own nature.
99
99
  """
100
+ if name == "?" and op is None:
101
+ return str(ctx.state.last_exit_code), False
102
+
100
103
  env = ctx.state.env
101
104
  if op == "#len":
102
105
  value = env.get(name, "")
@@ -17,7 +17,7 @@ import exec as exec_module
17
17
  import parser as parser_module
18
18
  import tokens as tokens_module
19
19
  from context import ExecContext
20
- from errors import UnsupportedConstruct
20
+ from errors import ShellExit, UnsupportedConstruct
21
21
  from expand import ParamExpansionError
22
22
  from state import ShellState
23
23
 
@@ -97,6 +97,9 @@ def main() -> int:
97
97
  tokens = tokens_module.tokenize(command)
98
98
  ast = parser_module.parse(tokens)
99
99
  exit_code = exec_module.execute(ast, ctx)
100
+ except ShellExit as exc:
101
+ _write_frame(exc.exit_code, state.cwd, state.delta(original_env), None)
102
+ return 0
100
103
  except UnsupportedConstruct as exc:
101
104
  output.write(exc.message.encode("utf-8", errors="replace"))
102
105
  _write_frame(
@@ -64,6 +64,7 @@ class ShellState:
64
64
 
65
65
  cwd: str
66
66
  env: ShellEnvironment = field(default_factory=ShellEnvironment)
67
+ last_exit_code: int = 0
67
68
 
68
69
  def __post_init__(self) -> None:
69
70
  if not isinstance(self.env, ShellEnvironment):
@@ -71,7 +72,7 @@ class ShellState:
71
72
 
72
73
  def copy(self) -> "ShellState":
73
74
  """Return an independent copy (used for subshell isolation)."""
74
- return ShellState(cwd=self.cwd, env=self.env.copy())
75
+ return ShellState(cwd=self.cwd, env=self.env.copy(), last_exit_code=self.last_exit_code)
75
76
 
76
77
  def chdir(self, path: str) -> None:
77
78
  """Validate `path` exists and is a directory, then update cwd + OLDPWD."""
@@ -294,6 +294,8 @@ def _scan_dollar_form(src: str, pos: int) -> tuple[Segment, int]:
294
294
  )
295
295
  close = _find_closing_paren(src, pos + 2)
296
296
  return CmdSub(src=src[pos + 2 : close]), close + 1
297
+ if nxt == "?":
298
+ return Param(name="?", op=None, arg=None), pos + 2
297
299
  if nxt and (_IDENT_START_RE.match(nxt)):
298
300
  m = _IDENT_RE.match(src, pos + 1)
299
301
  assert m is not None
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { ThinkingLevel } from "@caupulican/pi-agent-core";
5
5
  import type { ExtensionFlag } from "../core/extensions/types.ts";
6
+ import { type TerminalSessionMode } from "../core/session-role.ts";
6
7
  export type Mode = "text" | "json" | "rpc";
7
8
  export interface Args {
8
9
  provider?: string;
@@ -14,6 +15,7 @@ export interface Args {
14
15
  parentPid?: number;
15
16
  parentSession?: string;
16
17
  taskRef?: string;
18
+ sessionMode?: TerminalSessionMode;
17
19
  thinking?: ThinkingLevel;
18
20
  continue?: boolean;
19
21
  resume?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tworktreeLane?: string;\n\tparentPid?: number;\n\tparentSession?: string;\n\ttaskRef?: string;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\tresourceProfiles?: string[];\n\tresourceProfileJson?: string[];\n\torchestrationProfile?: string;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\t/** Emit cumulative session usage (one JSON line, `__PI_USAGE__` prefix) to stderr at exit (text print mode). */\n\tprintUsage?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tprojectTrustOverride?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--worktree-lane\" && i + 1 < args.length) {\n\t\t\tresult.worktreeLane = args[++i];\n\t\t} else if (arg === \"--parent-pid\" && i + 1 < args.length) {\n\t\t\tconst parsed = Number.parseInt(args[++i], 10);\n\t\t\tif (Number.isFinite(parsed) && parsed > 0) {\n\t\t\t\tresult.parentPid = parsed;\n\t\t\t}\n\t\t} else if (arg === \"--parent-session\" && i + 1 < args.length) {\n\t\t\tresult.parentSession = args[++i];\n\t\t} else if (arg === \"--task-ref\" && i + 1 < args.length) {\n\t\t\tresult.taskRef = args[++i];\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--resource-profile\" && i + 1 < args.length) {\n\t\t\tresult.resourceProfiles = [\n\t\t\t\t...(result.resourceProfiles ?? []),\n\t\t\t\t...args[++i]\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter((name) => name.length > 0),\n\t\t\t];\n\t\t} else if (arg === \"--resource-profile-json\" && i + 1 < args.length) {\n\t\t\tresult.resourceProfileJson = [...(result.resourceProfileJson ?? []), args[++i]];\n\t\t} else if (arg === \"--orchestration-profile\" && i + 1 < args.length) {\n\t\t\tresult.orchestrationProfile = args[++i];\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print-usage\") {\n\t\t\tresult.printUsage = true;\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--approve\" || arg === \"-a\") {\n\t\t\tresult.projectTrustOverride = true;\n\t\t} else if (arg === \"--no-approve\" || arg === \"-na\") {\n\t\t\tresult.projectTrustOverride = false;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with native task, shell, Python, file, and context tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] [--approve|--no-approve]\n Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] [--approve|--no-approve]\n Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|pi] Update pi and installed extensions\n ${APP_NAME} list [--approve|--no-approve]\n List installed extensions from settings\n ${APP_NAME} config [--approve|--no-approve]\n Open TUI to enable/disable package resources\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (with --model, or alone to select provider default)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --worktree-lane <laneKey> Bind this session to a worktree-sync lane (sets PI_WORKTREE_LANE; enables the lane gate + epoch watcher)\n --parent-pid <pid> Declare this session as a process-matrix worker of parent pid (sets PI_PARENT_PID)\n --parent-session <id> Declare the parent session id for a process-matrix worker (sets PI_PARENT_SESSION)\n --task-ref <id> Bind process recovery to a goal/task identity (sets PI_TASK_REF)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --print-usage With -p: emit cumulative session usage (one JSON line,\n \"__PI_USAGE__\" prefix) to stderr at exit, for cost roll-up\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --resource-profile <names> Comma-separated resource profile names for this session\n --resource-profile-json <json> One-shot profile definitions as JSON or <resource-profile> tag text\n --orchestration-profile <id> Pin this session to an owner-authored orchestration profile\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh, max, ultra\n Ultra uses max model effort and reinforces available delegation\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md, CLAUDE.md, and GEMINI.md discovery\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --approve, -a Trust project-local files for this run\n --no-approve, -na Ignore project-local files for this run\n --offline Disable startup network operations (same as PI_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use a provider's default model\n ${APP_NAME} --provider openai-codex \"Use my ChatGPT subscription\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n AWS_PROFILE - AWS profile for Amazon Bedrock\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n PI_OFFLINE - Disable startup network operations when set to 1/true/yes\n PI_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute the bounded cross-platform shell contract\n python - Execute bounded code through the uv-managed Python runtime\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n task_steps - Track the active session checklist\n ask_question - Ask the human owner for a consequential choice\n context_audit - Inspect active context/tool/system prompt usage (read-only)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI argument parsing and help display\n */\n\nimport type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR, ENV_SESSION_DIR } from \"../config.ts\";\nimport type { ExtensionFlag } from \"../core/extensions/types.ts\";\nimport { isTerminalSessionMode, type TerminalSessionMode } from \"../core/session-role.ts\";\n\nexport type Mode = \"text\" | \"json\" | \"rpc\";\n\nexport interface Args {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tsystemPrompt?: string;\n\tappendSystemPrompt?: string[];\n\tworktreeLane?: string;\n\tparentPid?: number;\n\tparentSession?: string;\n\ttaskRef?: string;\n\tsessionMode?: TerminalSessionMode;\n\tthinking?: ThinkingLevel;\n\tcontinue?: boolean;\n\tresume?: boolean;\n\thelp?: boolean;\n\tversion?: boolean;\n\tmode?: Mode;\n\tname?: string;\n\tnoSession?: boolean;\n\tsession?: string;\n\tsessionId?: string;\n\tfork?: string;\n\tsessionDir?: string;\n\tmodels?: string[];\n\ttools?: string[];\n\texcludeTools?: string[];\n\tnoTools?: boolean;\n\tnoBuiltinTools?: boolean;\n\tresourceProfiles?: string[];\n\tresourceProfileJson?: string[];\n\torchestrationProfile?: string;\n\textensions?: string[];\n\tnoExtensions?: boolean;\n\tprint?: boolean;\n\t/** Emit cumulative session usage (one JSON line, `__PI_USAGE__` prefix) to stderr at exit (text print mode). */\n\tprintUsage?: boolean;\n\texport?: string;\n\tnoSkills?: boolean;\n\tskills?: string[];\n\tpromptTemplates?: string[];\n\tnoPromptTemplates?: boolean;\n\tthemes?: string[];\n\tnoThemes?: boolean;\n\tnoContextFiles?: boolean;\n\tlistModels?: string | true;\n\toffline?: boolean;\n\tverbose?: boolean;\n\tprojectTrustOverride?: boolean;\n\tmessages: string[];\n\tfileArgs: string[];\n\t/** Unknown flags (potentially extension flags) - map of flag name to value */\n\tunknownFlags: Map<string, boolean | string>;\n\tdiagnostics: Array<{ type: \"warning\" | \"error\"; message: string }>;\n}\n\nconst VALID_THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultra\"] as const;\n\nexport function isValidThinkingLevel(level: string): level is ThinkingLevel {\n\treturn VALID_THINKING_LEVELS.includes(level as ThinkingLevel);\n}\n\nexport function parseArgs(args: string[]): Args {\n\tconst result: Args = {\n\t\tmessages: [],\n\t\tfileArgs: [],\n\t\tunknownFlags: new Map(),\n\t\tdiagnostics: [],\n\t};\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\n\t\tif (arg === \"--help\" || arg === \"-h\") {\n\t\t\tresult.help = true;\n\t\t} else if (arg === \"--version\" || arg === \"-v\") {\n\t\t\tresult.version = true;\n\t\t} else if (arg === \"--mode\" && i + 1 < args.length) {\n\t\t\tconst mode = args[++i];\n\t\t\tif (mode === \"text\" || mode === \"json\" || mode === \"rpc\") {\n\t\t\t\tresult.mode = mode;\n\t\t\t}\n\t\t} else if (arg === \"--continue\" || arg === \"-c\") {\n\t\t\tresult.continue = true;\n\t\t} else if (arg === \"--resume\" || arg === \"-r\") {\n\t\t\tresult.resume = true;\n\t\t} else if (arg === \"--provider\" && i + 1 < args.length) {\n\t\t\tresult.provider = args[++i];\n\t\t} else if (arg === \"--model\" && i + 1 < args.length) {\n\t\t\tresult.model = args[++i];\n\t\t} else if (arg === \"--api-key\" && i + 1 < args.length) {\n\t\t\tresult.apiKey = args[++i];\n\t\t} else if (arg === \"--system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.systemPrompt = args[++i];\n\t\t} else if (arg === \"--append-system-prompt\" && i + 1 < args.length) {\n\t\t\tresult.appendSystemPrompt = result.appendSystemPrompt ?? [];\n\t\t\tresult.appendSystemPrompt.push(args[++i]);\n\t\t} else if (arg === \"--worktree-lane\" && i + 1 < args.length) {\n\t\t\tresult.worktreeLane = args[++i];\n\t\t} else if (arg === \"--parent-pid\" && i + 1 < args.length) {\n\t\t\tconst parsed = Number.parseInt(args[++i], 10);\n\t\t\tif (Number.isFinite(parsed) && parsed > 0) {\n\t\t\t\tresult.parentPid = parsed;\n\t\t\t}\n\t\t} else if (arg === \"--parent-session\" && i + 1 < args.length) {\n\t\t\tresult.parentSession = args[++i];\n\t\t} else if (arg === \"--task-ref\" && i + 1 < args.length) {\n\t\t\tresult.taskRef = args[++i];\n\t\t} else if (arg === \"--session-mode\") {\n\t\t\tconst mode = args[i + 1];\n\t\t\tif (!mode) {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--session-mode requires user or worker\" });\n\t\t\t} else {\n\t\t\t\ti++;\n\t\t\t\tif (isTerminalSessionMode(mode)) result.sessionMode = mode;\n\t\t\t\telse\n\t\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\tmessage: `Invalid session mode \"${mode}\". Use user or worker.`,\n\t\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--name\" || arg === \"-n\") {\n\t\t\tif (i + 1 < args.length) {\n\t\t\t\tresult.name = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({ type: \"error\", message: \"--name requires a value\" });\n\t\t\t}\n\t\t} else if (arg === \"--no-session\") {\n\t\t\tresult.noSession = true;\n\t\t} else if (arg === \"--session\" && i + 1 < args.length) {\n\t\t\tresult.session = args[++i];\n\t\t} else if (arg === \"--session-id\" && i + 1 < args.length) {\n\t\t\tresult.sessionId = args[++i];\n\t\t} else if (arg === \"--fork\" && i + 1 < args.length) {\n\t\t\tresult.fork = args[++i];\n\t\t} else if (arg === \"--session-dir\" && i + 1 < args.length) {\n\t\t\tresult.sessionDir = args[++i];\n\t\t} else if (arg === \"--models\" && i + 1 < args.length) {\n\t\t\tresult.models = args[++i].split(\",\").map((s) => s.trim());\n\t\t} else if (arg === \"--no-tools\" || arg === \"-nt\") {\n\t\t\tresult.noTools = true;\n\t\t} else if (arg === \"--no-builtin-tools\" || arg === \"-nbt\") {\n\t\t\tresult.noBuiltinTools = true;\n\t\t} else if ((arg === \"--tools\" || arg === \"-t\") && i + 1 < args.length) {\n\t\t\tresult.tools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if ((arg === \"--exclude-tools\" || arg === \"-xt\") && i + 1 < args.length) {\n\t\t\tresult.excludeTools = args[++i]\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((s) => s.trim())\n\t\t\t\t.filter((name) => name.length > 0);\n\t\t} else if (arg === \"--resource-profile\" && i + 1 < args.length) {\n\t\t\tresult.resourceProfiles = [\n\t\t\t\t...(result.resourceProfiles ?? []),\n\t\t\t\t...args[++i]\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((s) => s.trim())\n\t\t\t\t\t.filter((name) => name.length > 0),\n\t\t\t];\n\t\t} else if (arg === \"--resource-profile-json\" && i + 1 < args.length) {\n\t\t\tresult.resourceProfileJson = [...(result.resourceProfileJson ?? []), args[++i]];\n\t\t} else if (arg === \"--orchestration-profile\" && i + 1 < args.length) {\n\t\t\tresult.orchestrationProfile = args[++i];\n\t\t} else if (arg === \"--thinking\" && i + 1 < args.length) {\n\t\t\tconst level = args[++i];\n\t\t\tif (isValidThinkingLevel(level)) {\n\t\t\t\tresult.thinking = level;\n\t\t\t} else {\n\t\t\t\tresult.diagnostics.push({\n\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\tmessage: `Invalid thinking level \"${level}\". Valid values: ${VALID_THINKING_LEVELS.join(\", \")}`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (arg === \"--print-usage\") {\n\t\t\tresult.printUsage = true;\n\t\t} else if (arg === \"--print\" || arg === \"-p\") {\n\t\t\tresult.print = true;\n\t\t\tconst next = args[i + 1];\n\t\t\tif (next !== undefined && !next.startsWith(\"@\") && (!next.startsWith(\"-\") || next.startsWith(\"---\"))) {\n\t\t\t\tresult.messages.push(next);\n\t\t\t\ti++;\n\t\t\t}\n\t\t} else if (arg === \"--export\" && i + 1 < args.length) {\n\t\t\tresult.export = args[++i];\n\t\t} else if ((arg === \"--extension\" || arg === \"-e\") && i + 1 < args.length) {\n\t\t\tresult.extensions = result.extensions ?? [];\n\t\t\tresult.extensions.push(args[++i]);\n\t\t} else if (arg === \"--no-extensions\" || arg === \"-ne\") {\n\t\t\tresult.noExtensions = true;\n\t\t} else if (arg === \"--skill\" && i + 1 < args.length) {\n\t\t\tresult.skills = result.skills ?? [];\n\t\t\tresult.skills.push(args[++i]);\n\t\t} else if (arg === \"--prompt-template\" && i + 1 < args.length) {\n\t\t\tresult.promptTemplates = result.promptTemplates ?? [];\n\t\t\tresult.promptTemplates.push(args[++i]);\n\t\t} else if (arg === \"--theme\" && i + 1 < args.length) {\n\t\t\tresult.themes = result.themes ?? [];\n\t\t\tresult.themes.push(args[++i]);\n\t\t} else if (arg === \"--no-skills\" || arg === \"-ns\") {\n\t\t\tresult.noSkills = true;\n\t\t} else if (arg === \"--no-prompt-templates\" || arg === \"-np\") {\n\t\t\tresult.noPromptTemplates = true;\n\t\t} else if (arg === \"--no-themes\") {\n\t\t\tresult.noThemes = true;\n\t\t} else if (arg === \"--no-context-files\" || arg === \"-nc\") {\n\t\t\tresult.noContextFiles = true;\n\t\t} else if (arg === \"--list-models\") {\n\t\t\t// Check if next arg is a search pattern (not a flag or file arg)\n\t\t\tif (i + 1 < args.length && !args[i + 1].startsWith(\"-\") && !args[i + 1].startsWith(\"@\")) {\n\t\t\t\tresult.listModels = args[++i];\n\t\t\t} else {\n\t\t\t\tresult.listModels = true;\n\t\t\t}\n\t\t} else if (arg === \"--verbose\") {\n\t\t\tresult.verbose = true;\n\t\t} else if (arg === \"--approve\" || arg === \"-a\") {\n\t\t\tresult.projectTrustOverride = true;\n\t\t} else if (arg === \"--no-approve\" || arg === \"-na\") {\n\t\t\tresult.projectTrustOverride = false;\n\t\t} else if (arg === \"--offline\") {\n\t\t\tresult.offline = true;\n\t\t} else if (arg.startsWith(\"@\")) {\n\t\t\tresult.fileArgs.push(arg.slice(1)); // Remove @ prefix\n\t\t} else if (arg.startsWith(\"--\")) {\n\t\t\tconst eqIndex = arg.indexOf(\"=\");\n\t\t\tif (eqIndex !== -1) {\n\t\t\t\tresult.unknownFlags.set(arg.slice(2, eqIndex), arg.slice(eqIndex + 1));\n\t\t\t} else {\n\t\t\t\tconst flagName = arg.slice(2);\n\t\t\t\tconst next = args[i + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith(\"-\") && !next.startsWith(\"@\")) {\n\t\t\t\t\tresult.unknownFlags.set(flagName, next);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tresult.unknownFlags.set(flagName, true);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (arg.startsWith(\"-\") && !arg.startsWith(\"--\")) {\n\t\t\tresult.diagnostics.push({ type: \"error\", message: `Unknown option: ${arg}` });\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tresult.messages.push(arg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport function printHelp(extensionFlags?: ExtensionFlag[]): void {\n\tconst extensionFlagsText =\n\t\textensionFlags && extensionFlags.length > 0\n\t\t\t? `\\n${chalk.bold(\"Extension CLI Flags:\")}\\n${extensionFlags\n\t\t\t\t\t.map((flag) => {\n\t\t\t\t\t\tconst value = flag.type === \"string\" ? \" <value>\" : \"\";\n\t\t\t\t\t\tconst description = flag.description ?? `Registered by ${flag.extensionPath}`;\n\t\t\t\t\t\treturn ` --${flag.name}${value}`.padEnd(30) + description;\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")}\\n`\n\t\t\t: \"\";\n\tconsole.log(`${chalk.bold(APP_NAME)} - AI coding assistant with native task, shell, Python, file, and context tools\n\n${chalk.bold(\"Usage:\")}\n ${APP_NAME} [options] [@files...] [messages...]\n\n${chalk.bold(\"Commands:\")}\n ${APP_NAME} install <source> [-l] [--approve|--no-approve]\n Install extension source and add to settings\n ${APP_NAME} remove <source> [-l] [--approve|--no-approve]\n Remove extension source from settings\n ${APP_NAME} uninstall <source> [-l] Alias for remove\n ${APP_NAME} update [source|self|pi] Update pi and installed extensions\n ${APP_NAME} list [--approve|--no-approve]\n List installed extensions from settings\n ${APP_NAME} config [--approve|--no-approve]\n Open TUI to enable/disable package resources\n ${APP_NAME} <command> --help Show help for install/remove/uninstall/update/list\n\n${chalk.bold(\"Options:\")}\n --provider <name> Provider name (with --model, or alone to select provider default)\n --model <pattern> Model pattern or ID (supports \"provider/id\" and optional \":<thinking>\")\n --api-key <key> API key (defaults to env vars)\n --system-prompt <text> System prompt (default: coding assistant prompt)\n --append-system-prompt <text> Append text or file contents to the system prompt (can be used multiple times)\n --worktree-lane <laneKey> Bind this session to a worktree-sync lane (sets PI_WORKTREE_LANE; enables the lane gate + epoch watcher)\n --parent-pid <pid> Declare this session as a process-matrix worker of parent pid (sets PI_PARENT_PID)\n --parent-session <id> Declare the parent session id for a process-matrix worker (sets PI_PARENT_SESSION)\n --task-ref <id> Bind process recovery to a goal/task identity (sets PI_TASK_REF)\n --session-mode <user|worker> Declare whether a human is watching this terminal (default: user)\n --mode <mode> Output mode: text (default), json, or rpc\n --print, -p Non-interactive mode: process prompt and exit\n --print-usage With -p: emit cumulative session usage (one JSON line,\n \"__PI_USAGE__\" prefix) to stderr at exit, for cost roll-up\n --continue, -c Continue previous session\n --resume, -r Select a session to resume\n --session <path|id> Use specific session file or partial UUID\n --session-id <id> Use exact project session ID, creating it if missing\n --fork <path|id> Fork specific session file or partial UUID into a new session\n --session-dir <dir> Directory for session storage and lookup\n --no-session Don't save session (ephemeral)\n --name, -n <name> Set session display name\n --models <patterns> Comma-separated model patterns for Ctrl+P cycling\n Supports globs (anthropic/*, *sonnet*) and fuzzy matching\n --no-tools, -nt Disable all tools by default (built-in and extension)\n --no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled\n --tools, -t <tools> Comma-separated allowlist of tool names to enable\n Applies to built-in, extension, and custom tools\n --exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable\n Applies to built-in, extension, and custom tools\n --resource-profile <names> Comma-separated resource profile names for this session\n --resource-profile-json <json> One-shot profile definitions as JSON or <resource-profile> tag text\n --orchestration-profile <id> Pin this session to an owner-authored orchestration profile\n --thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh, max, ultra\n Ultra uses max model effort and reinforces available delegation\n --extension, -e <path> Load an extension file (can be used multiple times)\n --no-extensions, -ne Disable extension discovery (explicit -e paths still work)\n --skill <path> Load a skill file or directory (can be used multiple times)\n --no-skills, -ns Disable skills discovery and loading\n --prompt-template <path> Load a prompt template file or directory (can be used multiple times)\n --no-prompt-templates, -np Disable prompt template discovery and loading\n --theme <path> Load a theme file or directory (can be used multiple times)\n --no-themes Disable theme discovery and loading\n --no-context-files, -nc Disable AGENTS.md, CLAUDE.md, and GEMINI.md discovery\n --export <file> Export session file to HTML and exit\n --list-models [search] List available models (with optional fuzzy search)\n --verbose Force verbose startup (overrides quietStartup setting)\n --approve, -a Trust project-local files for this run\n --no-approve, -na Ignore project-local files for this run\n --offline Disable startup network operations (same as PI_OFFLINE=1)\n --help, -h Show this help\n --version, -v Show version number\n\nExtensions can register additional flags (e.g., --plan from plan-mode extension).${extensionFlagsText}\n\n${chalk.bold(\"Examples:\")}\n # Interactive mode\n ${APP_NAME}\n\n # Interactive mode with initial prompt\n ${APP_NAME} \"List all .ts files in src/\"\n\n # Include files in initial message\n ${APP_NAME} @prompt.md @image.png \"What color is the sky?\"\n\n # Non-interactive mode (process and exit)\n ${APP_NAME} -p \"List all .ts files in src/\"\n\n # Multiple messages (interactive)\n ${APP_NAME} \"Read package.json\" \"What dependencies do we have?\"\n\n # Continue previous session\n ${APP_NAME} --continue \"What did we discuss?\"\n\n # Start a named session\n ${APP_NAME} --name \"Refactor auth module\"\n\n # Use different model\n ${APP_NAME} --provider openai --model gpt-4o-mini \"Help me refactor this code\"\n\n # Use a provider's default model\n ${APP_NAME} --provider openai-codex \"Use my ChatGPT subscription\"\n\n # Use model with provider prefix (no --provider needed)\n ${APP_NAME} --model openai/gpt-4o \"Help me refactor this code\"\n\n # Use model with thinking level shorthand\n ${APP_NAME} --model sonnet:high \"Solve this complex problem\"\n\n # Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o\n\n # Limit to a specific provider with glob pattern\n ${APP_NAME} --models \"github-copilot/*\"\n\n # Cycle models with fixed thinking levels\n ${APP_NAME} --models sonnet:high,haiku:low\n\n # Start with a specific thinking level\n ${APP_NAME} --thinking high \"Solve this complex problem\"\n\n # Read-only mode (no file modifications possible)\n ${APP_NAME} --tools read,grep,find,ls -p \"Review the code in src/\"\n\n # Disable one tool while keeping the rest available\n ${APP_NAME} --exclude-tools ask_question\n\n # Export a session file to HTML\n ${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl\n ${APP_NAME} --export session.jsonl output.html\n\n${chalk.bold(\"Environment Variables:\")}\n ANTHROPIC_API_KEY - Anthropic Claude API key\n ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)\n OPENAI_API_KEY - OpenAI GPT API key\n AZURE_OPENAI_API_KEY - Azure OpenAI API key\n AZURE_OPENAI_BASE_URL - Azure OpenAI/Cognitive Services base URL (e.g. https://{resource}.openai.azure.com)\n AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)\n AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)\n AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)\n DEEPSEEK_API_KEY - DeepSeek API key\n GEMINI_API_KEY - Google Gemini API key\n GROQ_API_KEY - Groq API key\n CEREBRAS_API_KEY - Cerebras API key\n XAI_API_KEY - xAI Grok API key\n FIREWORKS_API_KEY - Fireworks API key\n TOGETHER_API_KEY - Together AI API key\n OPENROUTER_API_KEY - OpenRouter API key\n AI_GATEWAY_API_KEY - Vercel AI Gateway API key\n ZAI_API_KEY - ZAI API key\n MISTRAL_API_KEY - Mistral API key\n MINIMAX_API_KEY - MiniMax API key\n MOONSHOT_API_KEY - Moonshot AI API key\n OPENCODE_API_KEY - OpenCode Zen/OpenCode Go API key\n KIMI_API_KEY - Kimi For Coding API key\n CLOUDFLARE_API_KEY - Cloudflare API token (Workers AI and AI Gateway)\n CLOUDFLARE_ACCOUNT_ID - Cloudflare account id (required for both)\n CLOUDFLARE_GATEWAY_ID - Cloudflare AI Gateway slug (required for AI Gateway)\n QWEN_TOKEN_PLAN_API_KEY - Qwen Token Plan API key (international region)\n QWEN_TOKEN_PLAN_CN_API_KEY - Qwen Token Plan API key (China region)\n XIAOMI_API_KEY - Xiaomi MiMo API key (api.xiaomimimo.com billing)\n XIAOMI_TOKEN_PLAN_CN_API_KEY - Xiaomi MiMo Token Plan API key (China region)\n XIAOMI_TOKEN_PLAN_AMS_API_KEY - Xiaomi MiMo Token Plan API key (Amsterdam region)\n XIAOMI_TOKEN_PLAN_SGP_API_KEY - Xiaomi MiMo Token Plan API key (Singapore region)\n AWS_PROFILE - AWS profile for Amazon Bedrock (managed SSO login in user sessions)\n AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock\n AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock\n AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)\n AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)\n ${ENV_AGENT_DIR.padEnd(32)} - Config directory (default: ~/${CONFIG_DIR_NAME}/agent)\n ${ENV_SESSION_DIR.padEnd(32)} - Session storage directory (overridden by --session-dir)\n PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)\n PI_OFFLINE - Disable startup network operations when set to 1/true/yes\n PI_TELEMETRY - Override install telemetry when set to 1/true/yes or 0/false/no\n PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/)\n\n${chalk.bold(\"Built-in Tool Names:\")}\n read - Read file contents\n bash - Execute the bounded cross-platform shell contract\n python - Execute bounded code through the uv-managed Python runtime\n edit - Edit files with find/replace\n write - Write files (creates/overwrites)\n task_steps - Track the active session checklist\n ask_question - Ask the human owner for a consequential choice\n secret_store - Capture and manage model-blind owner credentials\n context_audit - Inspect active context/tool/system prompt usage (read-only)\n grep - Search file contents (read-only, off by default)\n find - Find files by glob pattern (read-only, off by default)\n ls - List directory contents (read-only, off by default)\n`);\n}\n"]}