@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
@@ -0,0 +1,3 @@
1
+ /** Shared loopback classification for local execution, routing, and cache-warm decisions. */
2
+ export declare function isLoopbackModelEndpoint(baseUrl: string): boolean;
3
+ //# sourceMappingURL=model-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-endpoint.d.ts","sourceRoot":"","sources":["../../../src/core/models/model-endpoint.ts"],"names":[],"mappings":"AAEA,6FAA6F;AAC7F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMhE","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** Shared loopback classification for local execution, routing, and cache-warm decisions. */\nexport function isLoopbackModelEndpoint(baseUrl: string): boolean {\n\ttry {\n\t\treturn LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -0,0 +1,11 @@
1
+ const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
2
+ /** Shared loopback classification for local execution, routing, and cache-warm decisions. */
3
+ export function isLoopbackModelEndpoint(baseUrl) {
4
+ try {
5
+ return LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ //# sourceMappingURL=model-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-endpoint.js","sourceRoot":"","sources":["../../../src/core/models/model-endpoint.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAE3E,6FAA6F;AAC7F,MAAM,UAAU,uBAAuB,CAAC,OAAe,EAAW;IACjE,IAAI,CAAC;QACJ,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD","sourcesContent":["const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\"]);\n\n/** Shared loopback classification for local execution, routing, and cache-warm decisions. */\nexport function isLoopbackModelEndpoint(baseUrl: string): boolean {\n\ttry {\n\t\treturn LOOPBACK_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -16,6 +16,8 @@ export interface BuildPiResumeLaunchSpecOptions {
16
16
  export declare function isAgentIdentity(value: unknown): value is AgentIdentityContract;
17
17
  /** Build and validate the one logical identity shared by orchestration and process supervision. */
18
18
  export declare function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract;
19
+ /** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */
20
+ export declare function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean;
19
21
  /**
20
22
  * Build an argv-safe launch specification for the exact persisted Pi session behind a logical
21
23
  * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-resume.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAyB,MAAM,gBAAgB,CAAC;AAE5G,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAiC9E;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,GAAG,qBAAqB,CAQ7G;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,8BAA8B,GACrC,kBAAkB,CA2BpB","sourcesContent":["import { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
1
+ {"version":3,"file":"agent-resume.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAyB,MAAM,gBAAgB,CAAC;AAE5G,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,8BAA8B;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAiC9E;AAED,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,GAAG,qBAAqB,CAQ7G;AAED,oGAAoG;AACpG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAIpG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,EAAE,8BAA8B,GACrC,kBAAkB,CA2BpB","sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */\nexport function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean {\n\tconst { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;\n\tconst { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;\n\treturn isDeepStrictEqual(leftIdentity, rightIdentity);\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from "node:util";
1
2
  import { PI_ORCHESTRATION_AGENT_ID_ENV } from "../process-identity.js";
2
3
  import { isPlainRecord } from "../util/value-guards.js";
3
4
  import { isResourcePointerKind } from "./contracts.js";
@@ -48,6 +49,12 @@ export function createAgentIdentity(agentId, resumeContext) {
48
49
  throw new TypeError("Agent resume context is invalid.");
49
50
  return identity;
50
51
  }
52
+ /** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */
53
+ export function sameAgentResumeIdentity(left, right) {
54
+ const { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;
55
+ const { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;
56
+ return isDeepStrictEqual(leftIdentity, rightIdentity);
57
+ }
51
58
  /**
52
59
  * Build an argv-safe launch specification for the exact persisted Pi session behind a logical
53
60
  * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-resume.js","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
1
+ {"version":3,"file":"agent-resume.js","sourceRoot":"","sources":["../../../src/core/orchestration/agent-resume.ts"],"names":[],"mappings":"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","sourcesContent":["import { isDeepStrictEqual } from \"node:util\";\nimport { PI_ORCHESTRATION_AGENT_ID_ENV } from \"../process-identity.ts\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport { type AgentIdentityContract, type AgentResumeContext, isResourcePointerKind } from \"./contracts.ts\";\n\nexport interface PiResumeLaunchSpec {\n\texecutable: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: Readonly<Record<string, string>>;\n}\n\nexport interface BuildPiResumeLaunchSpecOptions {\n\texecutable?: string;\n\targsPrefix?: readonly string[];\n\tparentPid: number;\n\tparentSessionId: string;\n\ttaskRef?: string;\n\twakePrompt?: string;\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nexport function isAgentIdentity(value: unknown): value is AgentIdentityContract {\n\tif (!isPlainRecord(value) || !isPlainRecord(value.resumeContext)) return false;\n\tconst context = value.resumeContext;\n\treturn (\n\t\ttypeof value.agentId === \"string\" &&\n\t\tvalue.agentId.trim().length > 0 &&\n\t\t(context.provider === \"pi\" || context.provider === \"external\") &&\n\t\ttypeof context.sessionId === \"string\" &&\n\t\tcontext.sessionId.trim().length > 0 &&\n\t\ttypeof context.cwd === \"string\" &&\n\t\tcontext.cwd.trim().length > 0 &&\n\t\tisOptionalString(context.sessionDir) &&\n\t\tisOptionalString(context.sessionFile) &&\n\t\tisOptionalString(context.worktreeLaneKey) &&\n\t\tisOptionalString(context.orchestrationProfileId) &&\n\t\tisOptionalString(context.modelRef) &&\n\t\tisOptionalString(context.latestCheckpointId) &&\n\t\tArray.isArray(context.resourceProfileNames) &&\n\t\tcontext.resourceProfileNames.every((name) => typeof name === \"string\" && name.trim().length > 0) &&\n\t\tArray.isArray(context.contextPointers) &&\n\t\tcontext.contextPointers.every(\n\t\t\t(pointer) =>\n\t\t\t\tisPlainRecord(pointer) &&\n\t\t\t\ttypeof pointer.id === \"string\" &&\n\t\t\t\tpointer.id.trim().length > 0 &&\n\t\t\t\tisResourcePointerKind(pointer.kind) &&\n\t\t\t\ttypeof pointer.uri === \"string\" &&\n\t\t\t\tpointer.uri.trim().length > 0 &&\n\t\t\t\ttypeof pointer.readOnly === \"boolean\" &&\n\t\t\t\tisOptionalString(pointer.digest) &&\n\t\t\t\t(pointer.metadata === undefined || isPlainRecord(pointer.metadata)),\n\t\t)\n\t);\n}\n\n/** Build and validate the one logical identity shared by orchestration and process supervision. */\nexport function createAgentIdentity(agentId: string, resumeContext: AgentResumeContext): AgentIdentityContract {\n\tconst normalizedAgentId = agentId.trim();\n\tif (!normalizedAgentId) throw new TypeError(\"A logical agent id is required.\");\n\tif (!resumeContext.sessionId.trim()) throw new TypeError(\"An agent session id is required.\");\n\tif (!resumeContext.cwd.trim()) throw new TypeError(\"An agent working directory is required.\");\n\tconst identity = { agentId: normalizedAgentId, resumeContext: structuredClone(resumeContext) };\n\tif (!isAgentIdentity(identity)) throw new TypeError(\"Agent resume context is invalid.\");\n\treturn identity;\n}\n\n/** Compare only the immutable identity behind a resumable agent; checkpoint position is mutable. */\nexport function sameAgentResumeIdentity(left: AgentResumeContext, right: AgentResumeContext): boolean {\n\tconst { latestCheckpointId: _leftCheckpoint, ...leftIdentity } = left;\n\tconst { latestCheckpointId: _rightCheckpoint, ...rightIdentity } = right;\n\treturn isDeepStrictEqual(leftIdentity, rightIdentity);\n}\n\n/**\n * Build an argv-safe launch specification for the exact persisted Pi session behind a logical\n * agent. The caller passes argv directly to spawn; no shell quoting or command string is involved.\n */\nexport function buildPiResumeLaunchSpec(\n\tagent: AgentIdentityContract,\n\toptions: BuildPiResumeLaunchSpecOptions,\n): PiResumeLaunchSpec {\n\tconst identity = createAgentIdentity(agent.agentId, agent.resumeContext);\n\tconst context = identity.resumeContext;\n\tif (context.provider !== \"pi\") throw new TypeError(`Agent '${identity.agentId}' is not backed by a Pi session.`);\n\tif (!Number.isSafeInteger(options.parentPid) || options.parentPid <= 0) {\n\t\tthrow new TypeError(\"A positive parent pid is required to resume a Pi agent.\");\n\t}\n\tif (!options.parentSessionId.trim()) throw new TypeError(\"A parent session id is required to resume a Pi agent.\");\n\tconst args: string[] = [...(options.argsPrefix ?? [])];\n\tif (context.sessionDir) args.push(\"--session-dir\", context.sessionDir);\n\targs.push(\"--session\", context.sessionFile ?? context.sessionId);\n\targs.push(\"--parent-pid\", String(options.parentPid));\n\targs.push(\"--parent-session\", options.parentSessionId);\n\tif (options.taskRef?.trim()) args.push(\"--task-ref\", options.taskRef.trim());\n\tif (context.worktreeLaneKey) args.push(\"--worktree-lane\", context.worktreeLaneKey);\n\tif (context.orchestrationProfileId) {\n\t\targs.push(\"--orchestration-profile\", context.orchestrationProfileId);\n\t} else if (context.resourceProfileNames.length > 0) {\n\t\targs.push(\"--resource-profile\", context.resourceProfileNames.join(\",\"));\n\t}\n\tif (options.wakePrompt?.trim()) args.push(\"--print\", options.wakePrompt.trim());\n\treturn {\n\t\texecutable: options.executable ?? \"pi\",\n\t\targs,\n\t\tcwd: context.cwd,\n\t\tenv: { PI_SESSION_ROLE: \"worker\", [PI_ORCHESTRATION_AGENT_ID_ENV]: identity.agentId },\n\t};\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { Usage } from "@caupulican/pi-ai";
2
+ import type { AttemptUsageSnapshot } from "./contracts.ts";
3
+ export declare const EMPTY_ATTEMPT_USAGE: AttemptUsageSnapshot;
4
+ /** Parse an untrusted provider-neutral usage report into a closed, known-field value. */
5
+ export declare function validateProviderUsage(value: unknown, label?: string): Usage;
6
+ /** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */
7
+ export declare function validateAttemptUsageSnapshot(usage: AttemptUsageSnapshot, label?: string): AttemptUsageSnapshot;
8
+ export declare function attemptUsageFromGatewayUsage(usage: {
9
+ toolCalls: number;
10
+ inputTokens: number;
11
+ outputTokens: number;
12
+ cacheReadTokens: number;
13
+ cacheWriteTokens: number;
14
+ totalTokens: number;
15
+ costUsd: number;
16
+ wallClockMs: number;
17
+ }): AttemptUsageSnapshot;
18
+ export declare function addAttemptUsage(base: AttemptUsageSnapshot, delta: AttemptUsageSnapshot, label?: string): AttemptUsageSnapshot;
19
+ /**
20
+ * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.
21
+ * Both inputs are cumulative views of the same attempt, so summing would double-count; component
22
+ * maxima close append-before-checkpoint crash windows conservatively.
23
+ */
24
+ export declare function reconcileAttemptUsage(checkpoint: AttemptUsageSnapshot, transcript: AttemptUsageSnapshot): AttemptUsageSnapshot;
25
+ export declare function remainingTokenBudget(maxTokens: number | undefined, usage: AttemptUsageSnapshot): number | undefined;
26
+ /** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */
27
+ export declare function providerUsageFromAttemptUsage(usage: AttemptUsageSnapshot): Usage;
28
+ //# sourceMappingURL=attempt-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-usage.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/attempt-usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,eAAO,MAAM,mBAAmB,EAAE,oBASjC,CAAC;AAwBF,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAmB,GAAG,KAAK,CAyCrF;AAED,oGAAoG;AACpG,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,KAAK,SAAkB,GACrB,oBAAoB,CA0BtB;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACpB,GAAG,oBAAoB,CAcvB;AAED,wBAAgB,eAAe,CAC9B,IAAI,EAAE,oBAAoB,EAC1B,KAAK,EAAE,oBAAoB,EAC3B,KAAK,SAAkB,GACrB,oBAAoB,CAgBtB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,oBAAoB,EAChC,UAAU,EAAE,oBAAoB,GAC9B,oBAAoB,CAatB;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAInH;AAED,iGAAiG;AACjG,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,KAAK,CAUhF","sourcesContent":["import type { Usage } from \"@caupulican/pi-ai\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport type { AttemptUsageSnapshot } from \"./contracts.ts\";\n\nexport const EMPTY_ATTEMPT_USAGE: AttemptUsageSnapshot = {\n\ttoolCalls: 0,\n\tinputTokens: 0,\n\toutputTokens: 0,\n\tcacheReadTokens: 0,\n\tcacheWriteTokens: 0,\n\ttotalTokens: 0,\n\tcostUsd: 0,\n\tactiveWallClockMs: 0,\n};\n\n/**\n * Validate an untrusted provider-neutral usage report before it reaches durable accounting.\n * Provider totals are authoritative and therefore need not equal the detail field sum.\n */\nconst PROVIDER_USAGE_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"totalTokens\", \"cost\"] as const;\nconst PROVIDER_USAGE_COST_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"total\"] as const;\n\nfunction parseExactDataRecord(value: unknown, fields: readonly string[], label: string): Record<string, unknown> {\n\tif (!isPlainRecord(value)) throw new TypeError(`${label} must be a plain object.`);\n\tfor (const key of Reflect.ownKeys(value)) {\n\t\tif (typeof key !== \"string\" || !fields.includes(key))\n\t\t\tthrow new TypeError(`${label} contains an unsupported field.`);\n\t}\n\tconst parsed: Record<string, unknown> = {};\n\tfor (const field of fields) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, field);\n\t\tif (!descriptor || !(\"value\" in descriptor)) throw new TypeError(`${label}.${field} must be a data field.`);\n\t\tparsed[field] = descriptor.value;\n\t}\n\treturn parsed;\n}\n\n/** Parse an untrusted provider-neutral usage report into a closed, known-field value. */\nexport function validateProviderUsage(value: unknown, label = \"provider usage\"): Usage {\n\tconst usage = parseExactDataRecord(value, PROVIDER_USAGE_FIELDS, label);\n\tconst cost = parseExactDataRecord(usage.cost, PROVIDER_USAGE_COST_FIELDS, `${label}.cost`);\n\tconst tokenFields: Array<[string, number]> = [\n\t\t[\"input\", usage.input as number],\n\t\t[\"output\", usage.output as number],\n\t\t[\"cacheRead\", usage.cacheRead as number],\n\t\t[\"cacheWrite\", usage.cacheWrite as number],\n\t\t[\"totalTokens\", usage.totalTokens as number],\n\t];\n\tfor (const [field, value] of tokenFields) {\n\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.${field} must be a non-negative safe integer.`);\n\t\t}\n\t}\n\tconst costFields: Array<[string, number]> = [\n\t\t[\"input\", cost.input as number],\n\t\t[\"output\", cost.output as number],\n\t\t[\"cacheRead\", cost.cacheRead as number],\n\t\t[\"cacheWrite\", cost.cacheWrite as number],\n\t\t[\"total\", cost.total as number],\n\t];\n\tfor (const [field, value] of costFields) {\n\t\tif (!Number.isFinite(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.cost.${field} must be finite and non-negative.`);\n\t\t}\n\t}\n\treturn {\n\t\tinput: usage.input as number,\n\t\toutput: usage.output as number,\n\t\tcacheRead: usage.cacheRead as number,\n\t\tcacheWrite: usage.cacheWrite as number,\n\t\ttotalTokens: usage.totalTokens as number,\n\t\tcost: {\n\t\t\tinput: cost.input as number,\n\t\t\toutput: cost.output as number,\n\t\t\tcacheRead: cost.cacheRead as number,\n\t\t\tcacheWrite: cost.cacheWrite as number,\n\t\t\ttotal: cost.total as number,\n\t\t},\n\t};\n}\n\n/** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */\nexport function validateAttemptUsageSnapshot(\n\tusage: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tif (!Number.isSafeInteger(usage.toolCalls) || usage.toolCalls < 0) {\n\t\tthrow new Error(`${label}.toolCalls must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.inputTokens) || usage.inputTokens < 0) {\n\t\tthrow new Error(`${label}.inputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.outputTokens) || usage.outputTokens < 0) {\n\t\tthrow new Error(`${label}.outputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheReadTokens) || usage.cacheReadTokens < 0) {\n\t\tthrow new Error(`${label}.cacheReadTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheWriteTokens) || usage.cacheWriteTokens < 0) {\n\t\tthrow new Error(`${label}.cacheWriteTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.totalTokens) || usage.totalTokens < 0) {\n\t\tthrow new Error(`${label}.totalTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isFinite(usage.costUsd) || usage.costUsd < 0) {\n\t\tthrow new Error(`${label}.costUsd must be finite and non-negative.`);\n\t}\n\tif (!Number.isFinite(usage.activeWallClockMs) || usage.activeWallClockMs < 0) {\n\t\tthrow new Error(`${label}.activeWallClockMs must be finite and non-negative.`);\n\t}\n\treturn structuredClone(usage);\n}\n\nexport function attemptUsageFromGatewayUsage(usage: {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}): AttemptUsageSnapshot {\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: usage.toolCalls,\n\t\t\tinputTokens: usage.inputTokens,\n\t\t\toutputTokens: usage.outputTokens,\n\t\t\tcacheReadTokens: usage.cacheReadTokens,\n\t\t\tcacheWriteTokens: usage.cacheWriteTokens,\n\t\t\ttotalTokens: usage.totalTokens,\n\t\t\tcostUsd: usage.costUsd,\n\t\t\tactiveWallClockMs: usage.wallClockMs,\n\t\t},\n\t\t\"gateway usage\",\n\t);\n}\n\nexport function addAttemptUsage(\n\tbase: AttemptUsageSnapshot,\n\tdelta: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(base, `${label} base`);\n\tvalidateAttemptUsageSnapshot(delta, `${label} delta`);\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: base.toolCalls + delta.toolCalls,\n\t\t\tinputTokens: base.inputTokens + delta.inputTokens,\n\t\t\toutputTokens: base.outputTokens + delta.outputTokens,\n\t\t\tcacheReadTokens: base.cacheReadTokens + delta.cacheReadTokens,\n\t\t\tcacheWriteTokens: base.cacheWriteTokens + delta.cacheWriteTokens,\n\t\t\ttotalTokens: base.totalTokens + delta.totalTokens,\n\t\t\tcostUsd: base.costUsd + delta.costUsd,\n\t\t\tactiveWallClockMs: base.activeWallClockMs + delta.activeWallClockMs,\n\t\t},\n\t\tlabel,\n\t);\n}\n\n/**\n * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.\n * Both inputs are cumulative views of the same attempt, so summing would double-count; component\n * maxima close append-before-checkpoint crash windows conservatively.\n */\nexport function reconcileAttemptUsage(\n\tcheckpoint: AttemptUsageSnapshot,\n\ttranscript: AttemptUsageSnapshot,\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(checkpoint, \"attempt checkpoint usage\");\n\tvalidateAttemptUsageSnapshot(transcript, \"attempt transcript usage\");\n\treturn validateAttemptUsageSnapshot({\n\t\ttoolCalls: Math.max(checkpoint.toolCalls, transcript.toolCalls),\n\t\tinputTokens: Math.max(checkpoint.inputTokens, transcript.inputTokens),\n\t\toutputTokens: Math.max(checkpoint.outputTokens, transcript.outputTokens),\n\t\tcacheReadTokens: Math.max(checkpoint.cacheReadTokens, transcript.cacheReadTokens),\n\t\tcacheWriteTokens: Math.max(checkpoint.cacheWriteTokens, transcript.cacheWriteTokens),\n\t\ttotalTokens: Math.max(checkpoint.totalTokens, transcript.totalTokens),\n\t\tcostUsd: Math.max(checkpoint.costUsd, transcript.costUsd),\n\t\tactiveWallClockMs: Math.max(checkpoint.activeWallClockMs, transcript.activeWallClockMs),\n\t});\n}\n\nexport function remainingTokenBudget(maxTokens: number | undefined, usage: AttemptUsageSnapshot): number | undefined {\n\tif (maxTokens === undefined) return undefined;\n\tconst remaining = maxTokens - usage.totalTokens;\n\treturn Math.max(0, remaining);\n}\n\n/** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */\nexport function providerUsageFromAttemptUsage(usage: AttemptUsageSnapshot): Usage {\n\tconst validated = validateAttemptUsageSnapshot(usage);\n\treturn {\n\t\tinput: validated.inputTokens,\n\t\toutput: validated.outputTokens,\n\t\tcacheRead: validated.cacheReadTokens,\n\t\tcacheWrite: validated.cacheWriteTokens,\n\t\ttotalTokens: validated.totalTokens,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: validated.costUsd },\n\t};\n}\n"]}
@@ -0,0 +1,168 @@
1
+ import { isPlainRecord } from "../util/value-guards.js";
2
+ export const EMPTY_ATTEMPT_USAGE = {
3
+ toolCalls: 0,
4
+ inputTokens: 0,
5
+ outputTokens: 0,
6
+ cacheReadTokens: 0,
7
+ cacheWriteTokens: 0,
8
+ totalTokens: 0,
9
+ costUsd: 0,
10
+ activeWallClockMs: 0,
11
+ };
12
+ /**
13
+ * Validate an untrusted provider-neutral usage report before it reaches durable accounting.
14
+ * Provider totals are authoritative and therefore need not equal the detail field sum.
15
+ */
16
+ const PROVIDER_USAGE_FIELDS = ["input", "output", "cacheRead", "cacheWrite", "totalTokens", "cost"];
17
+ const PROVIDER_USAGE_COST_FIELDS = ["input", "output", "cacheRead", "cacheWrite", "total"];
18
+ function parseExactDataRecord(value, fields, label) {
19
+ if (!isPlainRecord(value))
20
+ throw new TypeError(`${label} must be a plain object.`);
21
+ for (const key of Reflect.ownKeys(value)) {
22
+ if (typeof key !== "string" || !fields.includes(key))
23
+ throw new TypeError(`${label} contains an unsupported field.`);
24
+ }
25
+ const parsed = {};
26
+ for (const field of fields) {
27
+ const descriptor = Object.getOwnPropertyDescriptor(value, field);
28
+ if (!descriptor || !("value" in descriptor))
29
+ throw new TypeError(`${label}.${field} must be a data field.`);
30
+ parsed[field] = descriptor.value;
31
+ }
32
+ return parsed;
33
+ }
34
+ /** Parse an untrusted provider-neutral usage report into a closed, known-field value. */
35
+ export function validateProviderUsage(value, label = "provider usage") {
36
+ const usage = parseExactDataRecord(value, PROVIDER_USAGE_FIELDS, label);
37
+ const cost = parseExactDataRecord(usage.cost, PROVIDER_USAGE_COST_FIELDS, `${label}.cost`);
38
+ const tokenFields = [
39
+ ["input", usage.input],
40
+ ["output", usage.output],
41
+ ["cacheRead", usage.cacheRead],
42
+ ["cacheWrite", usage.cacheWrite],
43
+ ["totalTokens", usage.totalTokens],
44
+ ];
45
+ for (const [field, value] of tokenFields) {
46
+ if (!Number.isSafeInteger(value) || value < 0) {
47
+ throw new Error(`${label}.${field} must be a non-negative safe integer.`);
48
+ }
49
+ }
50
+ const costFields = [
51
+ ["input", cost.input],
52
+ ["output", cost.output],
53
+ ["cacheRead", cost.cacheRead],
54
+ ["cacheWrite", cost.cacheWrite],
55
+ ["total", cost.total],
56
+ ];
57
+ for (const [field, value] of costFields) {
58
+ if (!Number.isFinite(value) || value < 0) {
59
+ throw new Error(`${label}.cost.${field} must be finite and non-negative.`);
60
+ }
61
+ }
62
+ return {
63
+ input: usage.input,
64
+ output: usage.output,
65
+ cacheRead: usage.cacheRead,
66
+ cacheWrite: usage.cacheWrite,
67
+ totalTokens: usage.totalTokens,
68
+ cost: {
69
+ input: cost.input,
70
+ output: cost.output,
71
+ cacheRead: cost.cacheRead,
72
+ cacheWrite: cost.cacheWrite,
73
+ total: cost.total,
74
+ },
75
+ };
76
+ }
77
+ /** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */
78
+ export function validateAttemptUsageSnapshot(usage, label = "attempt usage") {
79
+ if (!Number.isSafeInteger(usage.toolCalls) || usage.toolCalls < 0) {
80
+ throw new Error(`${label}.toolCalls must be a non-negative safe integer.`);
81
+ }
82
+ if (!Number.isSafeInteger(usage.inputTokens) || usage.inputTokens < 0) {
83
+ throw new Error(`${label}.inputTokens must be a non-negative safe integer.`);
84
+ }
85
+ if (!Number.isSafeInteger(usage.outputTokens) || usage.outputTokens < 0) {
86
+ throw new Error(`${label}.outputTokens must be a non-negative safe integer.`);
87
+ }
88
+ if (!Number.isSafeInteger(usage.cacheReadTokens) || usage.cacheReadTokens < 0) {
89
+ throw new Error(`${label}.cacheReadTokens must be a non-negative safe integer.`);
90
+ }
91
+ if (!Number.isSafeInteger(usage.cacheWriteTokens) || usage.cacheWriteTokens < 0) {
92
+ throw new Error(`${label}.cacheWriteTokens must be a non-negative safe integer.`);
93
+ }
94
+ if (!Number.isSafeInteger(usage.totalTokens) || usage.totalTokens < 0) {
95
+ throw new Error(`${label}.totalTokens must be a non-negative safe integer.`);
96
+ }
97
+ if (!Number.isFinite(usage.costUsd) || usage.costUsd < 0) {
98
+ throw new Error(`${label}.costUsd must be finite and non-negative.`);
99
+ }
100
+ if (!Number.isFinite(usage.activeWallClockMs) || usage.activeWallClockMs < 0) {
101
+ throw new Error(`${label}.activeWallClockMs must be finite and non-negative.`);
102
+ }
103
+ return structuredClone(usage);
104
+ }
105
+ export function attemptUsageFromGatewayUsage(usage) {
106
+ return validateAttemptUsageSnapshot({
107
+ toolCalls: usage.toolCalls,
108
+ inputTokens: usage.inputTokens,
109
+ outputTokens: usage.outputTokens,
110
+ cacheReadTokens: usage.cacheReadTokens,
111
+ cacheWriteTokens: usage.cacheWriteTokens,
112
+ totalTokens: usage.totalTokens,
113
+ costUsd: usage.costUsd,
114
+ activeWallClockMs: usage.wallClockMs,
115
+ }, "gateway usage");
116
+ }
117
+ export function addAttemptUsage(base, delta, label = "attempt usage") {
118
+ validateAttemptUsageSnapshot(base, `${label} base`);
119
+ validateAttemptUsageSnapshot(delta, `${label} delta`);
120
+ return validateAttemptUsageSnapshot({
121
+ toolCalls: base.toolCalls + delta.toolCalls,
122
+ inputTokens: base.inputTokens + delta.inputTokens,
123
+ outputTokens: base.outputTokens + delta.outputTokens,
124
+ cacheReadTokens: base.cacheReadTokens + delta.cacheReadTokens,
125
+ cacheWriteTokens: base.cacheWriteTokens + delta.cacheWriteTokens,
126
+ totalTokens: base.totalTokens + delta.totalTokens,
127
+ costUsd: base.costUsd + delta.costUsd,
128
+ activeWallClockMs: base.activeWallClockMs + delta.activeWallClockMs,
129
+ }, label);
130
+ }
131
+ /**
132
+ * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.
133
+ * Both inputs are cumulative views of the same attempt, so summing would double-count; component
134
+ * maxima close append-before-checkpoint crash windows conservatively.
135
+ */
136
+ export function reconcileAttemptUsage(checkpoint, transcript) {
137
+ validateAttemptUsageSnapshot(checkpoint, "attempt checkpoint usage");
138
+ validateAttemptUsageSnapshot(transcript, "attempt transcript usage");
139
+ return validateAttemptUsageSnapshot({
140
+ toolCalls: Math.max(checkpoint.toolCalls, transcript.toolCalls),
141
+ inputTokens: Math.max(checkpoint.inputTokens, transcript.inputTokens),
142
+ outputTokens: Math.max(checkpoint.outputTokens, transcript.outputTokens),
143
+ cacheReadTokens: Math.max(checkpoint.cacheReadTokens, transcript.cacheReadTokens),
144
+ cacheWriteTokens: Math.max(checkpoint.cacheWriteTokens, transcript.cacheWriteTokens),
145
+ totalTokens: Math.max(checkpoint.totalTokens, transcript.totalTokens),
146
+ costUsd: Math.max(checkpoint.costUsd, transcript.costUsd),
147
+ activeWallClockMs: Math.max(checkpoint.activeWallClockMs, transcript.activeWallClockMs),
148
+ });
149
+ }
150
+ export function remainingTokenBudget(maxTokens, usage) {
151
+ if (maxTokens === undefined)
152
+ return undefined;
153
+ const remaining = maxTokens - usage.totalTokens;
154
+ return Math.max(0, remaining);
155
+ }
156
+ /** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */
157
+ export function providerUsageFromAttemptUsage(usage) {
158
+ const validated = validateAttemptUsageSnapshot(usage);
159
+ return {
160
+ input: validated.inputTokens,
161
+ output: validated.outputTokens,
162
+ cacheRead: validated.cacheReadTokens,
163
+ cacheWrite: validated.cacheWriteTokens,
164
+ totalTokens: validated.totalTokens,
165
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: validated.costUsd },
166
+ };
167
+ }
168
+ //# sourceMappingURL=attempt-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attempt-usage.js","sourceRoot":"","sources":["../../../src/core/orchestration/attempt-usage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Usage } from \"@caupulican/pi-ai\";\nimport { isPlainRecord } from \"../util/value-guards.ts\";\nimport type { AttemptUsageSnapshot } from \"./contracts.ts\";\n\nexport const EMPTY_ATTEMPT_USAGE: AttemptUsageSnapshot = {\n\ttoolCalls: 0,\n\tinputTokens: 0,\n\toutputTokens: 0,\n\tcacheReadTokens: 0,\n\tcacheWriteTokens: 0,\n\ttotalTokens: 0,\n\tcostUsd: 0,\n\tactiveWallClockMs: 0,\n};\n\n/**\n * Validate an untrusted provider-neutral usage report before it reaches durable accounting.\n * Provider totals are authoritative and therefore need not equal the detail field sum.\n */\nconst PROVIDER_USAGE_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"totalTokens\", \"cost\"] as const;\nconst PROVIDER_USAGE_COST_FIELDS = [\"input\", \"output\", \"cacheRead\", \"cacheWrite\", \"total\"] as const;\n\nfunction parseExactDataRecord(value: unknown, fields: readonly string[], label: string): Record<string, unknown> {\n\tif (!isPlainRecord(value)) throw new TypeError(`${label} must be a plain object.`);\n\tfor (const key of Reflect.ownKeys(value)) {\n\t\tif (typeof key !== \"string\" || !fields.includes(key))\n\t\t\tthrow new TypeError(`${label} contains an unsupported field.`);\n\t}\n\tconst parsed: Record<string, unknown> = {};\n\tfor (const field of fields) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, field);\n\t\tif (!descriptor || !(\"value\" in descriptor)) throw new TypeError(`${label}.${field} must be a data field.`);\n\t\tparsed[field] = descriptor.value;\n\t}\n\treturn parsed;\n}\n\n/** Parse an untrusted provider-neutral usage report into a closed, known-field value. */\nexport function validateProviderUsage(value: unknown, label = \"provider usage\"): Usage {\n\tconst usage = parseExactDataRecord(value, PROVIDER_USAGE_FIELDS, label);\n\tconst cost = parseExactDataRecord(usage.cost, PROVIDER_USAGE_COST_FIELDS, `${label}.cost`);\n\tconst tokenFields: Array<[string, number]> = [\n\t\t[\"input\", usage.input as number],\n\t\t[\"output\", usage.output as number],\n\t\t[\"cacheRead\", usage.cacheRead as number],\n\t\t[\"cacheWrite\", usage.cacheWrite as number],\n\t\t[\"totalTokens\", usage.totalTokens as number],\n\t];\n\tfor (const [field, value] of tokenFields) {\n\t\tif (!Number.isSafeInteger(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.${field} must be a non-negative safe integer.`);\n\t\t}\n\t}\n\tconst costFields: Array<[string, number]> = [\n\t\t[\"input\", cost.input as number],\n\t\t[\"output\", cost.output as number],\n\t\t[\"cacheRead\", cost.cacheRead as number],\n\t\t[\"cacheWrite\", cost.cacheWrite as number],\n\t\t[\"total\", cost.total as number],\n\t];\n\tfor (const [field, value] of costFields) {\n\t\tif (!Number.isFinite(value) || value < 0) {\n\t\t\tthrow new Error(`${label}.cost.${field} must be finite and non-negative.`);\n\t\t}\n\t}\n\treturn {\n\t\tinput: usage.input as number,\n\t\toutput: usage.output as number,\n\t\tcacheRead: usage.cacheRead as number,\n\t\tcacheWrite: usage.cacheWrite as number,\n\t\ttotalTokens: usage.totalTokens as number,\n\t\tcost: {\n\t\t\tinput: cost.input as number,\n\t\t\toutput: cost.output as number,\n\t\t\tcacheRead: cost.cacheRead as number,\n\t\t\tcacheWrite: cost.cacheWrite as number,\n\t\t\ttotal: cost.total as number,\n\t\t},\n\t};\n}\n\n/** Validate the one durable cumulative usage shape shared by checkpoints, recovery, and results. */\nexport function validateAttemptUsageSnapshot(\n\tusage: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tif (!Number.isSafeInteger(usage.toolCalls) || usage.toolCalls < 0) {\n\t\tthrow new Error(`${label}.toolCalls must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.inputTokens) || usage.inputTokens < 0) {\n\t\tthrow new Error(`${label}.inputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.outputTokens) || usage.outputTokens < 0) {\n\t\tthrow new Error(`${label}.outputTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheReadTokens) || usage.cacheReadTokens < 0) {\n\t\tthrow new Error(`${label}.cacheReadTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.cacheWriteTokens) || usage.cacheWriteTokens < 0) {\n\t\tthrow new Error(`${label}.cacheWriteTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isSafeInteger(usage.totalTokens) || usage.totalTokens < 0) {\n\t\tthrow new Error(`${label}.totalTokens must be a non-negative safe integer.`);\n\t}\n\tif (!Number.isFinite(usage.costUsd) || usage.costUsd < 0) {\n\t\tthrow new Error(`${label}.costUsd must be finite and non-negative.`);\n\t}\n\tif (!Number.isFinite(usage.activeWallClockMs) || usage.activeWallClockMs < 0) {\n\t\tthrow new Error(`${label}.activeWallClockMs must be finite and non-negative.`);\n\t}\n\treturn structuredClone(usage);\n}\n\nexport function attemptUsageFromGatewayUsage(usage: {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}): AttemptUsageSnapshot {\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: usage.toolCalls,\n\t\t\tinputTokens: usage.inputTokens,\n\t\t\toutputTokens: usage.outputTokens,\n\t\t\tcacheReadTokens: usage.cacheReadTokens,\n\t\t\tcacheWriteTokens: usage.cacheWriteTokens,\n\t\t\ttotalTokens: usage.totalTokens,\n\t\t\tcostUsd: usage.costUsd,\n\t\t\tactiveWallClockMs: usage.wallClockMs,\n\t\t},\n\t\t\"gateway usage\",\n\t);\n}\n\nexport function addAttemptUsage(\n\tbase: AttemptUsageSnapshot,\n\tdelta: AttemptUsageSnapshot,\n\tlabel = \"attempt usage\",\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(base, `${label} base`);\n\tvalidateAttemptUsageSnapshot(delta, `${label} delta`);\n\treturn validateAttemptUsageSnapshot(\n\t\t{\n\t\t\ttoolCalls: base.toolCalls + delta.toolCalls,\n\t\t\tinputTokens: base.inputTokens + delta.inputTokens,\n\t\t\toutputTokens: base.outputTokens + delta.outputTokens,\n\t\t\tcacheReadTokens: base.cacheReadTokens + delta.cacheReadTokens,\n\t\t\tcacheWriteTokens: base.cacheWriteTokens + delta.cacheWriteTokens,\n\t\t\ttotalTokens: base.totalTokens + delta.totalTokens,\n\t\t\tcostUsd: base.costUsd + delta.costUsd,\n\t\t\tactiveWallClockMs: base.activeWallClockMs + delta.activeWallClockMs,\n\t\t},\n\t\tlabel,\n\t);\n}\n\n/**\n * Recover the strongest cumulative evidence across an atomic checkpoint and its durable transcript.\n * Both inputs are cumulative views of the same attempt, so summing would double-count; component\n * maxima close append-before-checkpoint crash windows conservatively.\n */\nexport function reconcileAttemptUsage(\n\tcheckpoint: AttemptUsageSnapshot,\n\ttranscript: AttemptUsageSnapshot,\n): AttemptUsageSnapshot {\n\tvalidateAttemptUsageSnapshot(checkpoint, \"attempt checkpoint usage\");\n\tvalidateAttemptUsageSnapshot(transcript, \"attempt transcript usage\");\n\treturn validateAttemptUsageSnapshot({\n\t\ttoolCalls: Math.max(checkpoint.toolCalls, transcript.toolCalls),\n\t\tinputTokens: Math.max(checkpoint.inputTokens, transcript.inputTokens),\n\t\toutputTokens: Math.max(checkpoint.outputTokens, transcript.outputTokens),\n\t\tcacheReadTokens: Math.max(checkpoint.cacheReadTokens, transcript.cacheReadTokens),\n\t\tcacheWriteTokens: Math.max(checkpoint.cacheWriteTokens, transcript.cacheWriteTokens),\n\t\ttotalTokens: Math.max(checkpoint.totalTokens, transcript.totalTokens),\n\t\tcostUsd: Math.max(checkpoint.costUsd, transcript.costUsd),\n\t\tactiveWallClockMs: Math.max(checkpoint.activeWallClockMs, transcript.activeWallClockMs),\n\t});\n}\n\nexport function remainingTokenBudget(maxTokens: number | undefined, usage: AttemptUsageSnapshot): number | undefined {\n\tif (maxTokens === undefined) return undefined;\n\tconst remaining = maxTokens - usage.totalTokens;\n\treturn Math.max(0, remaining);\n}\n\n/** Convert durable cumulative accounting into the provider-neutral report shape exactly once. */\nexport function providerUsageFromAttemptUsage(usage: AttemptUsageSnapshot): Usage {\n\tconst validated = validateAttemptUsageSnapshot(usage);\n\treturn {\n\t\tinput: validated.inputTokens,\n\t\toutput: validated.outputTokens,\n\t\tcacheRead: validated.cacheReadTokens,\n\t\tcacheWrite: validated.cacheWriteTokens,\n\t\ttotalTokens: validated.totalTokens,\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: validated.costUsd },\n\t};\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ExecutionGrant, ToolCapabilityManifest } from "./contracts.ts";
1
+ import type { AttemptUsageSnapshot, ExecutionGrant, ToolCapabilityManifest } from "./contracts.ts";
2
2
  export type GatewayDecisionCode = "allowed" | "grant_expired" | "tool_not_granted" | "manifest_name_mismatch" | "capability_not_granted" | "path_argument_required" | "path_outside_scope" | "tool_call_budget_exhausted" | "token_budget_exhausted" | "cost_budget_exhausted" | "wall_clock_budget_exhausted";
3
3
  export interface GatewayAuditRecord {
4
4
  grantId: string;
@@ -11,22 +11,32 @@ export interface GatewayAuditRecord {
11
11
  export interface CapabilityGatewayOptions {
12
12
  grant: ExecutionGrant;
13
13
  cwd: string;
14
+ /** Durable active usage carried forward when an attempt resumes in a new process. */
15
+ initialUsage?: GatewayInitialUsage;
14
16
  now?: () => number;
15
17
  onAudit?: (record: GatewayAuditRecord) => void;
16
18
  }
17
19
  export interface GatewayUsageDelta {
18
20
  inputTokens?: number;
19
21
  outputTokens?: number;
22
+ cacheReadTokens?: number;
23
+ cacheWriteTokens?: number;
24
+ /** Provider-authoritative total. Defaults to the supplied detail sum when omitted. */
25
+ totalTokens?: number;
20
26
  costUsd?: number;
21
27
  }
22
28
  export interface GatewayUsageSnapshot {
23
29
  toolCalls: number;
24
30
  inputTokens: number;
25
31
  outputTokens: number;
32
+ cacheReadTokens: number;
33
+ cacheWriteTokens: number;
26
34
  totalTokens: number;
27
35
  costUsd: number;
28
36
  wallClockMs: number;
29
37
  }
38
+ /** Persistable cumulative usage from a prior active segment; excludes restart downtime. */
39
+ export type GatewayInitialUsage = AttemptUsageSnapshot;
30
40
  export declare class CapabilityGatewayDeniedError extends Error {
31
41
  readonly reasonCode: GatewayDecisionCode;
32
42
  constructor(reasonCode: GatewayDecisionCode, message: string);
@@ -42,18 +52,26 @@ export declare class CapabilityGateway {
42
52
  private readonly now;
43
53
  private readonly onAudit?;
44
54
  private readonly startedAt;
55
+ private readonly initialWallClockMs;
45
56
  private toolCalls;
46
57
  private inputTokens;
47
58
  private outputTokens;
59
+ private cacheReadTokens;
60
+ private cacheWriteTokens;
61
+ private totalTokens;
48
62
  private costUsd;
49
63
  constructor(options: CapabilityGatewayOptions);
50
64
  execute<T>(manifest: ToolCapabilityManifest, toolName: string, params: unknown, invoke: () => Promise<T> | T): Promise<T>;
51
65
  /** Immediate pre-execution authorization for runtimes that expose a before-tool-call hook. */
52
66
  authorizeToolCall(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void;
53
67
  recordUsage(delta: GatewayUsageDelta): void;
68
+ /** Enforce resumed cumulative budgets before a provider request that has no tool-call boundary. */
69
+ assertBudgetAvailable(subject?: string): void;
54
70
  getUsage(): GatewayUsageSnapshot;
55
71
  private authorize;
56
72
  private enforceBudget;
73
+ private currentTime;
74
+ private wallClockMsAt;
57
75
  private pathAllowed;
58
76
  private deny;
59
77
  private audit;
@@ -1 +1 @@
1
- {"version":3,"file":"capability-gateway.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAqB,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEhG,MAAM,MAAM,mBAAmB,GAC5B,SAAS,GACT,eAAe,GACf,kBAAkB,GAClB,wBAAwB,GACxB,wBAAwB,GACxB,wBAAwB,GACxB,oBAAoB,GACpB,4BAA4B,GAC5B,wBAAwB,GACxB,uBAAuB,GACvB,6BAA6B,CAAC;AAEjC,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,UAAU,EAAE,mBAAmB,CAAC;IAChC,EAAE,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,wBAAwB;IACxC,KAAK,EAAE,cAAc,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACtD,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC;IAEzC,YAAY,UAAU,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAI3D;CACD;AA2BD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAK;IAEpB,YAAY,OAAO,EAAE,wBAAwB,EAM5C;IAEK,OAAO,CAAC,CAAC,EACd,QAAQ,EAAE,sBAAsB,EAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAGZ;IAED,8FAA8F;IAC9F,iBAAiB,CAAC,QAAQ,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAI3F;IAED,WAAW,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAI1C;IAED,QAAQ,IAAI,oBAAoB,CAS/B;IAED,OAAO,CAAC,SAAS;IA+BjB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,KAAK;CAUb","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { extractPathArguments } from \"../autonomy/envelope-enforcement.ts\";\nimport { safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport type { ExecutionGrant, HarnessCapability, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport type GatewayDecisionCode =\n\t| \"allowed\"\n\t| \"grant_expired\"\n\t| \"tool_not_granted\"\n\t| \"manifest_name_mismatch\"\n\t| \"capability_not_granted\"\n\t| \"path_argument_required\"\n\t| \"path_outside_scope\"\n\t| \"tool_call_budget_exhausted\"\n\t| \"token_budget_exhausted\"\n\t| \"cost_budget_exhausted\"\n\t| \"wall_clock_budget_exhausted\";\n\nexport interface GatewayAuditRecord {\n\tgrantId: string;\n\tattemptId: string;\n\ttoolName: string;\n\toutcome: \"allow\" | \"deny\";\n\treasonCode: GatewayDecisionCode;\n\tat: string;\n}\n\nexport interface CapabilityGatewayOptions {\n\tgrant: ExecutionGrant;\n\tcwd: string;\n\tnow?: () => number;\n\tonAudit?: (record: GatewayAuditRecord) => void;\n}\n\nexport interface GatewayUsageDelta {\n\tinputTokens?: number;\n\toutputTokens?: number;\n\tcostUsd?: number;\n}\n\nexport interface GatewayUsageSnapshot {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}\n\nexport class CapabilityGatewayDeniedError extends Error {\n\treadonly reasonCode: GatewayDecisionCode;\n\n\tconstructor(reasonCode: GatewayDecisionCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CapabilityGatewayDeniedError\";\n\t\tthis.reasonCode = reasonCode;\n\t}\n}\n\nconst PATH_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\n\t\"filesystem.read\",\n\t\"filesystem.write\",\n\t\"worktree.read\",\n\t\"worktree.mutate\",\n]);\n\nconst WRITE_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\"filesystem.write\", \"worktree.mutate\"]);\n\nfunction isWithinRoot(target: string, root: string): boolean {\n\tconst relativePath = relative(root, target);\n\treturn (\n\t\trelativePath === \"\" ||\n\t\t(!relativePath.startsWith(`..${sep}`) && relativePath !== \"..\" && !isAbsolute(relativePath))\n\t);\n}\n\nfunction resolveRealPath(cwd: string, value: string): string | undefined {\n\ttry {\n\t\treturn safeRealpathSync(resolve(cwd, value));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Per-invocation enforcement for an already-compiled grant. The compiler controls what may be\n * loaded; this gateway independently rechecks authority, path scope, expiry, and cumulative budget\n * immediately before execution.\n */\nexport class CapabilityGateway {\n\tprivate readonly grant: ExecutionGrant;\n\tprivate readonly cwd: string;\n\tprivate readonly now: () => number;\n\tprivate readonly onAudit?: (record: GatewayAuditRecord) => void;\n\tprivate readonly startedAt: number;\n\tprivate toolCalls = 0;\n\tprivate inputTokens = 0;\n\tprivate outputTokens = 0;\n\tprivate costUsd = 0;\n\n\tconstructor(options: CapabilityGatewayOptions) {\n\t\tthis.grant = options.grant;\n\t\tthis.cwd = options.cwd;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.onAudit = options.onAudit;\n\t\tthis.startedAt = this.now();\n\t}\n\n\tasync execute<T>(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tinvoke: () => Promise<T> | T,\n\t): Promise<T> {\n\t\tthis.authorizeToolCall(manifest, toolName, params);\n\t\treturn await invoke();\n\t}\n\n\t/** Immediate pre-execution authorization for runtimes that expose a before-tool-call hook. */\n\tauthorizeToolCall(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void {\n\t\tthis.authorize(manifest, toolName, params);\n\t\tthis.toolCalls++;\n\t\tthis.audit(toolName, \"allow\", \"allowed\");\n\t}\n\n\trecordUsage(delta: GatewayUsageDelta): void {\n\t\tthis.inputTokens += delta.inputTokens ?? 0;\n\t\tthis.outputTokens += delta.outputTokens ?? 0;\n\t\tthis.costUsd += delta.costUsd ?? 0;\n\t}\n\n\tgetUsage(): GatewayUsageSnapshot {\n\t\treturn {\n\t\t\ttoolCalls: this.toolCalls,\n\t\t\tinputTokens: this.inputTokens,\n\t\t\toutputTokens: this.outputTokens,\n\t\t\ttotalTokens: this.inputTokens + this.outputTokens,\n\t\t\tcostUsd: this.costUsd,\n\t\t\twallClockMs: Math.max(0, this.now() - this.startedAt),\n\t\t};\n\t}\n\n\tprivate authorize(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void {\n\t\tif (this.grant.expiresAt && Date.parse(this.grant.expiresAt) <= this.now()) {\n\t\t\tthis.deny(toolName, \"grant_expired\", `Execution grant '${this.grant.grantId}' expired.`);\n\t\t}\n\t\tif (!this.grant.allowedTools.includes(toolName)) {\n\t\t\tthis.deny(toolName, \"tool_not_granted\", `Tool '${toolName}' is not in grant '${this.grant.grantId}'.`);\n\t\t}\n\t\tif (manifest.toolName !== toolName) {\n\t\t\tthis.deny(toolName, \"manifest_name_mismatch\", `Tool '${toolName}' does not match its capability manifest.`);\n\t\t}\n\t\tif (!manifest.capabilities.every((capability) => this.grant.capabilities.includes(capability))) {\n\t\t\tthis.deny(toolName, \"capability_not_granted\", `Tool '${toolName}' requires an ungranted capability.`);\n\t\t}\n\t\tthis.enforceBudget(toolName);\n\n\t\tif (manifest.capabilities.some((capability) => PATH_CAPABILITIES.has(capability))) {\n\t\t\tconst allowedPaths = manifest.capabilities.some((capability) => WRITE_CAPABILITIES.has(capability))\n\t\t\t\t? this.grant.writePaths\n\t\t\t\t: this.grant.readPaths;\n\t\t\tconst paths = extractPathArguments(params);\n\t\t\tif (paths.length === 0) {\n\t\t\t\tthis.deny(toolName, \"path_argument_required\", `Tool '${toolName}' requires an explicit path argument.`);\n\t\t\t}\n\t\t\tfor (const rawPath of paths) {\n\t\t\t\tif (!this.pathAllowed(rawPath, allowedPaths)) {\n\t\t\t\t\tthis.deny(toolName, \"path_outside_scope\", `Path '${rawPath}' is outside grant '${this.grant.grantId}'.`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate enforceBudget(toolName: string): void {\n\t\tconst budget = this.grant.budget;\n\t\tif (budget.maxToolCalls !== undefined && this.toolCalls >= budget.maxToolCalls) {\n\t\t\tthis.deny(toolName, \"tool_call_budget_exhausted\", \"Tool-call budget exhausted.\");\n\t\t}\n\t\tif (budget.maxTokens !== undefined && this.inputTokens + this.outputTokens >= budget.maxTokens) {\n\t\t\tthis.deny(toolName, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (budget.maxCostUsd !== undefined && this.costUsd >= budget.maxCostUsd) {\n\t\t\tthis.deny(toolName, \"cost_budget_exhausted\", \"Cost budget exhausted.\");\n\t\t}\n\t\tif (budget.maxWallClockMs !== undefined && this.now() - this.startedAt >= budget.maxWallClockMs) {\n\t\t\tthis.deny(toolName, \"wall_clock_budget_exhausted\", \"Wall-clock budget exhausted.\");\n\t\t}\n\t}\n\n\tprivate pathAllowed(rawPath: string, allowedPaths: readonly string[]): boolean {\n\t\tconst target = resolveRealPath(this.cwd, rawPath);\n\t\tif (!target || allowedPaths.length === 0) return false;\n\t\tfor (const denied of this.grant.deniedPaths) {\n\t\t\tconst root = resolveRealPath(this.cwd, denied);\n\t\t\tif (root && isWithinRoot(target, root)) return false;\n\t\t}\n\t\treturn allowedPaths.some((allowed) => {\n\t\t\tconst root = resolveRealPath(this.cwd, allowed);\n\t\t\treturn root !== undefined && isWithinRoot(target, root);\n\t\t});\n\t}\n\n\tprivate deny(toolName: string, reasonCode: GatewayDecisionCode, message: string): never {\n\t\tthis.audit(toolName, \"deny\", reasonCode);\n\t\tthrow new CapabilityGatewayDeniedError(reasonCode, message);\n\t}\n\n\tprivate audit(toolName: string, outcome: \"allow\" | \"deny\", reasonCode: GatewayDecisionCode): void {\n\t\tthis.onAudit?.({\n\t\t\tgrantId: this.grant.grantId,\n\t\t\tattemptId: this.grant.attemptId,\n\t\t\ttoolName,\n\t\t\toutcome,\n\t\t\treasonCode,\n\t\t\tat: new Date(this.now()).toISOString(),\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"capability-gateway.d.ts","sourceRoot":"","sources":["../../../src/core/orchestration/capability-gateway.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from \"node:path\";\nimport { extractPathArguments } from \"../autonomy/envelope-enforcement.ts\";\nimport { isPathWithinScope, safeRealpathSync } from \"../autonomy/path-scope.ts\";\nimport { validateAttemptUsageSnapshot } from \"./attempt-usage.ts\";\nimport type { AttemptUsageSnapshot, ExecutionGrant, HarnessCapability, ToolCapabilityManifest } from \"./contracts.ts\";\n\nexport type GatewayDecisionCode =\n\t| \"allowed\"\n\t| \"grant_expired\"\n\t| \"tool_not_granted\"\n\t| \"manifest_name_mismatch\"\n\t| \"capability_not_granted\"\n\t| \"path_argument_required\"\n\t| \"path_outside_scope\"\n\t| \"tool_call_budget_exhausted\"\n\t| \"token_budget_exhausted\"\n\t| \"cost_budget_exhausted\"\n\t| \"wall_clock_budget_exhausted\";\n\nexport interface GatewayAuditRecord {\n\tgrantId: string;\n\tattemptId: string;\n\ttoolName: string;\n\toutcome: \"allow\" | \"deny\";\n\treasonCode: GatewayDecisionCode;\n\tat: string;\n}\n\nexport interface CapabilityGatewayOptions {\n\tgrant: ExecutionGrant;\n\tcwd: string;\n\t/** Durable active usage carried forward when an attempt resumes in a new process. */\n\tinitialUsage?: GatewayInitialUsage;\n\tnow?: () => number;\n\tonAudit?: (record: GatewayAuditRecord) => void;\n}\n\nexport interface GatewayUsageDelta {\n\tinputTokens?: number;\n\toutputTokens?: number;\n\tcacheReadTokens?: number;\n\tcacheWriteTokens?: number;\n\t/** Provider-authoritative total. Defaults to the supplied detail sum when omitted. */\n\ttotalTokens?: number;\n\tcostUsd?: number;\n}\n\nexport interface GatewayUsageSnapshot {\n\ttoolCalls: number;\n\tinputTokens: number;\n\toutputTokens: number;\n\tcacheReadTokens: number;\n\tcacheWriteTokens: number;\n\ttotalTokens: number;\n\tcostUsd: number;\n\twallClockMs: number;\n}\n\n/** Persistable cumulative usage from a prior active segment; excludes restart downtime. */\nexport type GatewayInitialUsage = AttemptUsageSnapshot;\n\nexport class CapabilityGatewayDeniedError extends Error {\n\treadonly reasonCode: GatewayDecisionCode;\n\n\tconstructor(reasonCode: GatewayDecisionCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"CapabilityGatewayDeniedError\";\n\t\tthis.reasonCode = reasonCode;\n\t}\n}\n\nconst PATH_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\n\t\"filesystem.read\",\n\t\"filesystem.write\",\n\t\"worktree.read\",\n\t\"worktree.mutate\",\n]);\n\nconst WRITE_CAPABILITIES: ReadonlySet<HarnessCapability> = new Set([\"filesystem.write\", \"worktree.mutate\"]);\n\nfunction resolveRealPath(cwd: string, value: string): string | undefined {\n\ttry {\n\t\treturn safeRealpathSync(resolve(cwd, value));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction validateInitialUsage(usage: GatewayInitialUsage): void {\n\ttry {\n\t\tvalidateAttemptUsageSnapshot(usage, \"CapabilityGateway: initial usage\");\n\t} catch {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: initial usage must contain finite non-negative values and safe-integer counts.\",\n\t\t);\n\t}\n}\n\nfunction validatedUsageDelta(delta: GatewayUsageDelta): Required<GatewayUsageDelta> {\n\tconst inputTokens = delta.inputTokens === undefined ? 0 : delta.inputTokens;\n\tconst outputTokens = delta.outputTokens === undefined ? 0 : delta.outputTokens;\n\tconst cacheReadTokens = delta.cacheReadTokens === undefined ? 0 : delta.cacheReadTokens;\n\tconst cacheWriteTokens = delta.cacheWriteTokens === undefined ? 0 : delta.cacheWriteTokens;\n\tconst totalTokens =\n\t\tdelta.totalTokens === undefined\n\t\t\t? inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens\n\t\t\t: delta.totalTokens;\n\tconst costUsd = delta.costUsd === undefined ? 0 : delta.costUsd;\n\tif (\n\t\t!Number.isSafeInteger(inputTokens) ||\n\t\tinputTokens < 0 ||\n\t\t!Number.isSafeInteger(outputTokens) ||\n\t\toutputTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\tcacheReadTokens < 0 ||\n\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\tcacheWriteTokens < 0 ||\n\t\t!Number.isSafeInteger(totalTokens) ||\n\t\ttotalTokens < 0 ||\n\t\t!Number.isFinite(costUsd) ||\n\t\tcostUsd < 0\n\t) {\n\t\tthrow new Error(\n\t\t\t\"CapabilityGateway: usage delta must contain finite non-negative values and safe-integer token counts.\",\n\t\t);\n\t}\n\treturn { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens, totalTokens, costUsd };\n}\n\n/**\n * Per-invocation enforcement for an already-compiled grant. The compiler controls what may be\n * loaded; this gateway independently rechecks authority, path scope, expiry, and cumulative budget\n * immediately before execution.\n */\nexport class CapabilityGateway {\n\tprivate readonly grant: ExecutionGrant;\n\tprivate readonly cwd: string;\n\tprivate readonly now: () => number;\n\tprivate readonly onAudit?: (record: GatewayAuditRecord) => void;\n\tprivate readonly startedAt: number;\n\tprivate readonly initialWallClockMs: number;\n\tprivate toolCalls: number;\n\tprivate inputTokens: number;\n\tprivate outputTokens: number;\n\tprivate cacheReadTokens: number;\n\tprivate cacheWriteTokens: number;\n\tprivate totalTokens: number;\n\tprivate costUsd: number;\n\n\tconstructor(options: CapabilityGatewayOptions) {\n\t\tconst initialUsage: GatewayInitialUsage = options.initialUsage ?? {\n\t\t\ttoolCalls: 0,\n\t\t\tinputTokens: 0,\n\t\t\toutputTokens: 0,\n\t\t\tcacheReadTokens: 0,\n\t\t\tcacheWriteTokens: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcostUsd: 0,\n\t\t\tactiveWallClockMs: 0,\n\t\t};\n\t\tvalidateInitialUsage(initialUsage);\n\t\tthis.grant = options.grant;\n\t\tthis.cwd = options.cwd;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.onAudit = options.onAudit;\n\t\tthis.startedAt = this.currentTime();\n\t\tthis.initialWallClockMs = initialUsage.activeWallClockMs;\n\t\tthis.toolCalls = initialUsage.toolCalls;\n\t\tthis.inputTokens = initialUsage.inputTokens;\n\t\tthis.outputTokens = initialUsage.outputTokens;\n\t\tthis.cacheReadTokens = initialUsage.cacheReadTokens;\n\t\tthis.cacheWriteTokens = initialUsage.cacheWriteTokens;\n\t\tthis.totalTokens = initialUsage.totalTokens;\n\t\tthis.costUsd = initialUsage.costUsd;\n\t}\n\n\tasync execute<T>(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tinvoke: () => Promise<T> | T,\n\t): Promise<T> {\n\t\tthis.authorizeToolCall(manifest, toolName, params);\n\t\treturn await invoke();\n\t}\n\n\t/** Immediate pre-execution authorization for runtimes that expose a before-tool-call hook. */\n\tauthorizeToolCall(manifest: ToolCapabilityManifest, toolName: string, params: unknown): void {\n\t\tconst now = this.currentTime();\n\t\tconst wallClockMs = this.wallClockMsAt(now);\n\t\tthis.authorize(manifest, toolName, params, now, wallClockMs);\n\t\tconst toolCalls = this.toolCalls + 1;\n\t\tif (!Number.isSafeInteger(toolCalls)) {\n\t\t\tthrow new Error(\"CapabilityGateway: tool-call count would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.toolCalls = toolCalls;\n\t\tthis.audit(toolName, \"allow\", \"allowed\");\n\t}\n\n\trecordUsage(delta: GatewayUsageDelta): void {\n\t\tconst validated = validatedUsageDelta(delta);\n\t\tconst inputTokens = this.inputTokens + validated.inputTokens;\n\t\tconst outputTokens = this.outputTokens + validated.outputTokens;\n\t\tconst cacheReadTokens = this.cacheReadTokens + validated.cacheReadTokens;\n\t\tconst cacheWriteTokens = this.cacheWriteTokens + validated.cacheWriteTokens;\n\t\tconst totalTokens = this.totalTokens + validated.totalTokens;\n\t\tconst costUsd = this.costUsd + validated.costUsd;\n\t\tif (\n\t\t\t!Number.isSafeInteger(inputTokens) ||\n\t\t\t!Number.isSafeInteger(outputTokens) ||\n\t\t\t!Number.isSafeInteger(cacheReadTokens) ||\n\t\t\t!Number.isSafeInteger(cacheWriteTokens) ||\n\t\t\t!Number.isSafeInteger(totalTokens) ||\n\t\t\t!Number.isFinite(costUsd)\n\t\t) {\n\t\t\tthrow new Error(\"CapabilityGateway: usage delta would exceed safe cumulative usage bounds.\");\n\t\t}\n\t\tthis.inputTokens = inputTokens;\n\t\tthis.outputTokens = outputTokens;\n\t\tthis.cacheReadTokens = cacheReadTokens;\n\t\tthis.cacheWriteTokens = cacheWriteTokens;\n\t\tthis.totalTokens = totalTokens;\n\t\tthis.costUsd = costUsd;\n\t}\n\n\t/** Enforce resumed cumulative budgets before a provider request that has no tool-call boundary. */\n\tassertBudgetAvailable(subject = \"provider\"): void {\n\t\tthis.enforceBudget(subject, this.wallClockMsAt(this.currentTime()));\n\t}\n\n\tgetUsage(): GatewayUsageSnapshot {\n\t\treturn {\n\t\t\ttoolCalls: this.toolCalls,\n\t\t\tinputTokens: this.inputTokens,\n\t\t\toutputTokens: this.outputTokens,\n\t\t\tcacheReadTokens: this.cacheReadTokens,\n\t\t\tcacheWriteTokens: this.cacheWriteTokens,\n\t\t\ttotalTokens: this.totalTokens,\n\t\t\tcostUsd: this.costUsd,\n\t\t\twallClockMs: this.wallClockMsAt(this.currentTime()),\n\t\t};\n\t}\n\n\tprivate authorize(\n\t\tmanifest: ToolCapabilityManifest,\n\t\ttoolName: string,\n\t\tparams: unknown,\n\t\tnow: number,\n\t\twallClockMs: number,\n\t): void {\n\t\tif (this.grant.expiresAt && Date.parse(this.grant.expiresAt) <= now) {\n\t\t\tthis.deny(toolName, \"grant_expired\", `Execution grant '${this.grant.grantId}' expired.`);\n\t\t}\n\t\tif (!this.grant.allowedTools.includes(toolName)) {\n\t\t\tthis.deny(toolName, \"tool_not_granted\", `Tool '${toolName}' is not in grant '${this.grant.grantId}'.`);\n\t\t}\n\t\tif (manifest.toolName !== toolName) {\n\t\t\tthis.deny(toolName, \"manifest_name_mismatch\", `Tool '${toolName}' does not match its capability manifest.`);\n\t\t}\n\t\tif (!manifest.capabilities.every((capability) => this.grant.capabilities.includes(capability))) {\n\t\t\tthis.deny(toolName, \"capability_not_granted\", `Tool '${toolName}' requires an ungranted capability.`);\n\t\t}\n\t\tthis.enforceBudget(toolName, wallClockMs);\n\n\t\tif (manifest.capabilities.some((capability) => PATH_CAPABILITIES.has(capability))) {\n\t\t\tconst allowedPaths = manifest.capabilities.some((capability) => WRITE_CAPABILITIES.has(capability))\n\t\t\t\t? this.grant.writePaths\n\t\t\t\t: this.grant.readPaths;\n\t\t\tconst paths = extractPathArguments(params);\n\t\t\tif (paths.length === 0) {\n\t\t\t\tthis.deny(toolName, \"path_argument_required\", `Tool '${toolName}' requires an explicit path argument.`);\n\t\t\t}\n\t\t\tfor (const rawPath of paths) {\n\t\t\t\tif (!this.pathAllowed(rawPath, allowedPaths)) {\n\t\t\t\t\tthis.deny(toolName, \"path_outside_scope\", `Path '${rawPath}' is outside grant '${this.grant.grantId}'.`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate enforceBudget(toolName: string, wallClockMs: number): void {\n\t\tconst budget = this.grant.budget;\n\t\tif (budget.maxToolCalls !== undefined && this.toolCalls >= budget.maxToolCalls) {\n\t\t\tthis.deny(toolName, \"tool_call_budget_exhausted\", \"Tool-call budget exhausted.\");\n\t\t}\n\t\tif (budget.maxTokens !== undefined && this.totalTokens >= budget.maxTokens) {\n\t\t\tthis.deny(toolName, \"token_budget_exhausted\", \"Token budget exhausted.\");\n\t\t}\n\t\tif (budget.maxCostUsd !== undefined && this.costUsd >= budget.maxCostUsd) {\n\t\t\tthis.deny(toolName, \"cost_budget_exhausted\", \"Cost budget exhausted.\");\n\t\t}\n\t\tif (budget.maxWallClockMs !== undefined && wallClockMs >= budget.maxWallClockMs) {\n\t\t\tthis.deny(toolName, \"wall_clock_budget_exhausted\", \"Wall-clock budget exhausted.\");\n\t\t}\n\t}\n\n\tprivate currentTime(): number {\n\t\tconst now = this.now();\n\t\tif (!Number.isFinite(now)) {\n\t\t\tthrow new Error(\"CapabilityGateway: clock source must return a finite time.\");\n\t\t}\n\t\treturn now;\n\t}\n\n\tprivate wallClockMsAt(now: number): number {\n\t\tconst elapsed = now - this.startedAt;\n\t\tif (!Number.isFinite(elapsed)) {\n\t\t\tthrow new Error(\"CapabilityGateway: active wall-clock elapsed time must be finite.\");\n\t\t}\n\t\tconst wallClockMs = this.initialWallClockMs + Math.max(0, elapsed);\n\t\tif (!Number.isFinite(wallClockMs) || wallClockMs < 0) {\n\t\t\tthrow new Error(\"CapabilityGateway: accumulated wall-clock usage must be finite and non-negative.\");\n\t\t}\n\t\treturn wallClockMs;\n\t}\n\n\tprivate pathAllowed(rawPath: string, allowedPaths: readonly string[]): boolean {\n\t\tconst target = resolveRealPath(this.cwd, rawPath);\n\t\tif (!target || allowedPaths.length === 0) return false;\n\t\tfor (const denied of this.grant.deniedPaths) {\n\t\t\tconst root = resolveRealPath(this.cwd, denied);\n\t\t\tif (root && isPathWithinScope(target, root)) return false;\n\t\t}\n\t\treturn allowedPaths.some((allowed) => {\n\t\t\tconst root = resolveRealPath(this.cwd, allowed);\n\t\t\treturn root !== undefined && isPathWithinScope(target, root);\n\t\t});\n\t}\n\n\tprivate deny(toolName: string, reasonCode: GatewayDecisionCode, message: string): never {\n\t\tthis.audit(toolName, \"deny\", reasonCode);\n\t\tthrow new CapabilityGatewayDeniedError(reasonCode, message);\n\t}\n\n\tprivate audit(toolName: string, outcome: \"allow\" | \"deny\", reasonCode: GatewayDecisionCode): void {\n\t\tthis.onAudit?.({\n\t\t\tgrantId: this.grant.grantId,\n\t\t\tattemptId: this.grant.attemptId,\n\t\t\ttoolName,\n\t\t\toutcome,\n\t\t\treasonCode,\n\t\t\tat: new Date(this.currentTime()).toISOString(),\n\t\t});\n\t}\n}\n"]}