@caupulican/pi-adaptative 0.81.40 → 0.81.41

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 (406) hide show
  1. package/CHANGELOG.md +25 -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/cli/args.d.ts +2 -0
  7. package/dist/cli/args.d.ts.map +1 -1
  8. package/dist/cli/args.js +22 -1
  9. package/dist/cli/args.js.map +1 -1
  10. package/dist/core/agent-paths.d.ts +6 -0
  11. package/dist/core/agent-paths.d.ts.map +1 -1
  12. package/dist/core/agent-paths.js +12 -0
  13. package/dist/core/agent-paths.js.map +1 -1
  14. package/dist/core/agent-session-contracts.d.ts +293 -0
  15. package/dist/core/agent-session-contracts.d.ts.map +1 -0
  16. package/dist/core/agent-session-contracts.js +26 -0
  17. package/dist/core/agent-session-contracts.js.map +1 -0
  18. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  19. package/dist/core/agent-session-runtime.js +3 -0
  20. package/dist/core/agent-session-runtime.js.map +1 -1
  21. package/dist/core/agent-session.d.ts +26 -405
  22. package/dist/core/agent-session.d.ts.map +1 -1
  23. package/dist/core/agent-session.js +86 -307
  24. package/dist/core/agent-session.js.map +1 -1
  25. package/dist/core/auth-storage.d.ts.map +1 -1
  26. package/dist/core/auth-storage.js +3 -1
  27. package/dist/core/auth-storage.js.map +1 -1
  28. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  29. package/dist/core/autonomy/lane-tool-surface.js +2 -1
  30. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  31. package/dist/core/background-lane-controller.d.ts +3 -1
  32. package/dist/core/background-lane-controller.d.ts.map +1 -1
  33. package/dist/core/background-lane-controller.js +1 -0
  34. package/dist/core/background-lane-controller.js.map +1 -1
  35. package/dist/core/bedrock-sso-login.d.ts +16 -0
  36. package/dist/core/bedrock-sso-login.d.ts.map +1 -0
  37. package/dist/core/bedrock-sso-login.js +98 -0
  38. package/dist/core/bedrock-sso-login.js.map +1 -0
  39. package/dist/core/compaction-controller.d.ts.map +1 -1
  40. package/dist/core/compaction-controller.js +1 -1
  41. package/dist/core/compaction-controller.js.map +1 -1
  42. package/dist/core/compaction-support.d.ts +3 -8
  43. package/dist/core/compaction-support.d.ts.map +1 -1
  44. package/dist/core/compaction-support.js +3 -3
  45. package/dist/core/compaction-support.js.map +1 -1
  46. package/dist/core/context-pipeline.d.ts +1 -1
  47. package/dist/core/context-pipeline.d.ts.map +1 -1
  48. package/dist/core/context-pipeline.js +7 -33
  49. package/dist/core/context-pipeline.js.map +1 -1
  50. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  51. package/dist/core/cost/cost-summary.js +5 -3
  52. package/dist/core/cost/cost-summary.js.map +1 -1
  53. package/dist/core/cost/daily-usage.d.ts.map +1 -1
  54. package/dist/core/cost/daily-usage.js +4 -5
  55. package/dist/core/cost/daily-usage.js.map +1 -1
  56. package/dist/core/default-tool-surface.d.ts.map +1 -1
  57. package/dist/core/default-tool-surface.js +1 -0
  58. package/dist/core/default-tool-surface.js.map +1 -1
  59. package/dist/core/extension-import-authority.d.ts +6 -0
  60. package/dist/core/extension-import-authority.d.ts.map +1 -0
  61. package/dist/core/extension-import-authority.js +10 -0
  62. package/dist/core/extension-import-authority.js.map +1 -0
  63. package/dist/core/extensions/loader.d.ts.map +1 -1
  64. package/dist/core/extensions/loader.js +2 -3
  65. package/dist/core/extensions/loader.js.map +1 -1
  66. package/dist/core/extensions/runner.d.ts +3 -0
  67. package/dist/core/extensions/runner.d.ts.map +1 -1
  68. package/dist/core/extensions/runner.js +14 -5
  69. package/dist/core/extensions/runner.js.map +1 -1
  70. package/dist/core/extensions/stale-context.d.ts +2 -0
  71. package/dist/core/extensions/stale-context.d.ts.map +1 -0
  72. package/dist/core/extensions/stale-context.js +2 -0
  73. package/dist/core/extensions/stale-context.js.map +1 -0
  74. package/dist/core/extensions/types.d.ts +8 -0
  75. package/dist/core/extensions/types.d.ts.map +1 -1
  76. package/dist/core/extensions/types.js.map +1 -1
  77. package/dist/core/foreground-recovery-controller.d.ts +1 -0
  78. package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
  79. package/dist/core/foreground-recovery-controller.js +13 -13
  80. package/dist/core/foreground-recovery-controller.js.map +1 -1
  81. package/dist/core/goal-loop-controller.d.ts +14 -9
  82. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  83. package/dist/core/goal-loop-controller.js +47 -75
  84. package/dist/core/goal-loop-controller.js.map +1 -1
  85. package/dist/core/goals/compact-goal-context.d.ts +11 -0
  86. package/dist/core/goals/compact-goal-context.d.ts.map +1 -0
  87. package/dist/core/goals/compact-goal-context.js +59 -0
  88. package/dist/core/goals/compact-goal-context.js.map +1 -0
  89. package/dist/core/goals/goal-acceptance.d.ts.map +1 -1
  90. package/dist/core/goals/goal-acceptance.js +2 -0
  91. package/dist/core/goals/goal-acceptance.js.map +1 -1
  92. package/dist/core/goals/goal-auto-continue-controller.d.ts +2 -1
  93. package/dist/core/goals/goal-auto-continue-controller.d.ts.map +1 -1
  94. package/dist/core/goals/goal-auto-continue-controller.js +5 -8
  95. package/dist/core/goals/goal-auto-continue-controller.js.map +1 -1
  96. package/dist/core/goals/goal-continuation-controller.d.ts +2 -2
  97. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  98. package/dist/core/goals/goal-continuation-controller.js +42 -11
  99. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  100. package/dist/core/goals/goal-continuation-defaults.d.ts +4 -29
  101. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  102. package/dist/core/goals/goal-continuation-defaults.js +4 -32
  103. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  104. package/dist/core/goals/goal-continuation-prompt.d.ts +8 -18
  105. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  106. package/dist/core/goals/goal-continuation-prompt.js +9 -179
  107. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  108. package/dist/core/goals/goal-lifecycle.d.ts +35 -12
  109. package/dist/core/goals/goal-lifecycle.d.ts.map +1 -1
  110. package/dist/core/goals/goal-lifecycle.js +98 -13
  111. package/dist/core/goals/goal-lifecycle.js.map +1 -1
  112. package/dist/core/goals/goal-record.d.ts +17 -0
  113. package/dist/core/goals/goal-record.d.ts.map +1 -0
  114. package/dist/core/goals/goal-record.js +31 -0
  115. package/dist/core/goals/goal-record.js.map +1 -0
  116. package/dist/core/goals/goal-session-controller.d.ts +42 -0
  117. package/dist/core/goals/goal-session-controller.d.ts.map +1 -0
  118. package/dist/core/goals/goal-session-controller.js +128 -0
  119. package/dist/core/goals/goal-session-controller.js.map +1 -0
  120. package/dist/core/goals/goal-state.d.ts +45 -25
  121. package/dist/core/goals/goal-state.d.ts.map +1 -1
  122. package/dist/core/goals/goal-state.js +85 -15
  123. package/dist/core/goals/goal-state.js.map +1 -1
  124. package/dist/core/goals/goal-tool-core.d.ts +1 -4
  125. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  126. package/dist/core/goals/goal-tool-core.js +49 -34
  127. package/dist/core/goals/goal-tool-core.js.map +1 -1
  128. package/dist/core/goals/session-goal-state.d.ts +18 -8
  129. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  130. package/dist/core/goals/session-goal-state.js +134 -11
  131. package/dist/core/goals/session-goal-state.js.map +1 -1
  132. package/dist/core/human-input-controller.d.ts +47 -0
  133. package/dist/core/human-input-controller.d.ts.map +1 -0
  134. package/dist/core/human-input-controller.js +181 -0
  135. package/dist/core/human-input-controller.js.map +1 -0
  136. package/dist/core/local-runtime-controller.d.ts +1 -1
  137. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  138. package/dist/core/local-runtime-controller.js.map +1 -1
  139. package/dist/core/model-capability.d.ts +0 -19
  140. package/dist/core/model-capability.d.ts.map +1 -1
  141. package/dist/core/model-capability.js +0 -19
  142. package/dist/core/model-capability.js.map +1 -1
  143. package/dist/core/model-registry.d.ts +6 -1
  144. package/dist/core/model-registry.d.ts.map +1 -1
  145. package/dist/core/model-registry.js +13 -5
  146. package/dist/core/model-registry.js.map +1 -1
  147. package/dist/core/model-resolver.d.ts +16 -1
  148. package/dist/core/model-resolver.d.ts.map +1 -1
  149. package/dist/core/model-resolver.js +32 -16
  150. package/dist/core/model-resolver.js.map +1 -1
  151. package/dist/core/model-router-controller.d.ts +1 -1
  152. package/dist/core/model-router-controller.d.ts.map +1 -1
  153. package/dist/core/model-router-controller.js.map +1 -1
  154. package/dist/core/model-selection-controller.d.ts +1 -1
  155. package/dist/core/model-selection-controller.d.ts.map +1 -1
  156. package/dist/core/model-selection-controller.js.map +1 -1
  157. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  158. package/dist/core/orchestration/delegation-ledger.js +3 -0
  159. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  160. package/dist/core/orchestration/work-state-projection.d.ts.map +1 -1
  161. package/dist/core/orchestration/work-state-projection.js +1 -1
  162. package/dist/core/orchestration/work-state-projection.js.map +1 -1
  163. package/dist/core/package-manager.d.ts.map +1 -1
  164. package/dist/core/package-manager.js +20 -3
  165. package/dist/core/package-manager.js.map +1 -1
  166. package/dist/core/profile-filter-controller.d.ts +8 -1
  167. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  168. package/dist/core/profile-filter-controller.js +23 -5
  169. package/dist/core/profile-filter-controller.js.map +1 -1
  170. package/dist/core/prompt-templates.d.ts +1 -0
  171. package/dist/core/prompt-templates.d.ts.map +1 -1
  172. package/dist/core/prompt-templates.js +18 -29
  173. package/dist/core/prompt-templates.js.map +1 -1
  174. package/dist/core/reflection-controller.d.ts +1 -1
  175. package/dist/core/reflection-controller.d.ts.map +1 -1
  176. package/dist/core/reflection-controller.js +3 -1
  177. package/dist/core/reflection-controller.js.map +1 -1
  178. package/dist/core/request-auth.d.ts +7 -0
  179. package/dist/core/request-auth.d.ts.map +1 -0
  180. package/dist/core/request-auth.js +8 -0
  181. package/dist/core/request-auth.js.map +1 -0
  182. package/dist/core/resource-loader.d.ts.map +1 -1
  183. package/dist/core/resource-loader.js +16 -6
  184. package/dist/core/resource-loader.js.map +1 -1
  185. package/dist/core/risk-classifier.d.ts.map +1 -1
  186. package/dist/core/risk-classifier.js +8 -0
  187. package/dist/core/risk-classifier.js.map +1 -1
  188. package/dist/core/runtime-builder.d.ts +15 -3
  189. package/dist/core/runtime-builder.d.ts.map +1 -1
  190. package/dist/core/runtime-builder.js +132 -79
  191. package/dist/core/runtime-builder.js.map +1 -1
  192. package/dist/core/sdk.d.ts.map +1 -1
  193. package/dist/core/sdk.js +7 -1
  194. package/dist/core/sdk.js.map +1 -1
  195. package/dist/core/secrets/credential-exposure-guard.d.ts +9 -0
  196. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -0
  197. package/dist/core/secrets/credential-exposure-guard.js +241 -0
  198. package/dist/core/secrets/credential-exposure-guard.js.map +1 -0
  199. package/dist/core/secrets/secret-dotenv.d.ts +27 -0
  200. package/dist/core/secrets/secret-dotenv.d.ts.map +1 -0
  201. package/dist/core/secrets/secret-dotenv.js +183 -0
  202. package/dist/core/secrets/secret-dotenv.js.map +1 -0
  203. package/dist/core/secrets/secret-vault-format.d.ts +112 -0
  204. package/dist/core/secrets/secret-vault-format.d.ts.map +1 -0
  205. package/dist/core/secrets/secret-vault-format.js +397 -0
  206. package/dist/core/secrets/secret-vault-format.js.map +1 -0
  207. package/dist/core/secrets/secret-vault.d.ts +72 -0
  208. package/dist/core/secrets/secret-vault.d.ts.map +1 -0
  209. package/dist/core/secrets/secret-vault.js +671 -0
  210. package/dist/core/secrets/secret-vault.js.map +1 -0
  211. package/dist/core/session-analytics.d.ts +4 -23
  212. package/dist/core/session-analytics.d.ts.map +1 -1
  213. package/dist/core/session-analytics.js +39 -69
  214. package/dist/core/session-analytics.js.map +1 -1
  215. package/dist/core/session-role.d.ts +5 -0
  216. package/dist/core/session-role.d.ts.map +1 -1
  217. package/dist/core/session-role.js +14 -1
  218. package/dist/core/session-role.js.map +1 -1
  219. package/dist/core/session-tree-navigator.d.ts +4 -6
  220. package/dist/core/session-tree-navigator.d.ts.map +1 -1
  221. package/dist/core/session-tree-navigator.js +4 -1
  222. package/dist/core/session-tree-navigator.js.map +1 -1
  223. package/dist/core/settings-manager.d.ts +1 -1
  224. package/dist/core/settings-manager.d.ts.map +1 -1
  225. package/dist/core/settings-manager.js +1 -1
  226. package/dist/core/settings-manager.js.map +1 -1
  227. package/dist/core/slash-commands.d.ts.map +1 -1
  228. package/dist/core/slash-commands.js +2 -2
  229. package/dist/core/slash-commands.js.map +1 -1
  230. package/dist/core/tool-capability-policy.d.ts.map +1 -1
  231. package/dist/core/tool-capability-policy.js +1 -0
  232. package/dist/core/tool-capability-policy.js.map +1 -1
  233. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  234. package/dist/core/tool-gate-controller.js +8 -2
  235. package/dist/core/tool-gate-controller.js.map +1 -1
  236. package/dist/core/tool-protocol-controller.d.ts +2 -4
  237. package/dist/core/tool-protocol-controller.d.ts.map +1 -1
  238. package/dist/core/tool-protocol-controller.js.map +1 -1
  239. package/dist/core/tool-recovery-stats.d.ts +21 -1
  240. package/dist/core/tool-recovery-stats.d.ts.map +1 -1
  241. package/dist/core/tool-recovery-stats.js.map +1 -1
  242. package/dist/core/tools/bash.d.ts +5 -0
  243. package/dist/core/tools/bash.d.ts.map +1 -1
  244. package/dist/core/tools/bash.js +59 -5
  245. package/dist/core/tools/bash.js.map +1 -1
  246. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  247. package/dist/core/tools/delegate-status.js +6 -7
  248. package/dist/core/tools/delegate-status.js.map +1 -1
  249. package/dist/core/tools/delegate.d.ts.map +1 -1
  250. package/dist/core/tools/delegate.js +6 -8
  251. package/dist/core/tools/delegate.js.map +1 -1
  252. package/dist/core/tools/git-filter.d.ts +1 -8
  253. package/dist/core/tools/git-filter.d.ts.map +1 -1
  254. package/dist/core/tools/git-filter.js +2 -63
  255. package/dist/core/tools/git-filter.js.map +1 -1
  256. package/dist/core/tools/goal.d.ts +5 -3
  257. package/dist/core/tools/goal.d.ts.map +1 -1
  258. package/dist/core/tools/goal.js +97 -19
  259. package/dist/core/tools/goal.js.map +1 -1
  260. package/dist/core/tools/index.d.ts +1 -0
  261. package/dist/core/tools/index.d.ts.map +1 -1
  262. package/dist/core/tools/index.js +1 -0
  263. package/dist/core/tools/index.js.map +1 -1
  264. package/dist/core/tools/orchestration-panel.d.ts +1 -4
  265. package/dist/core/tools/orchestration-panel.d.ts.map +1 -1
  266. package/dist/core/tools/orchestration-panel.js +0 -44
  267. package/dist/core/tools/orchestration-panel.js.map +1 -1
  268. package/dist/core/tools/output-accumulator.d.ts +11 -0
  269. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  270. package/dist/core/tools/output-accumulator.js +36 -3
  271. package/dist/core/tools/output-accumulator.js.map +1 -1
  272. package/dist/core/tools/python.d.ts +2 -0
  273. package/dist/core/tools/python.d.ts.map +1 -1
  274. package/dist/core/tools/python.js +3 -1
  275. package/dist/core/tools/python.js.map +1 -1
  276. package/dist/core/tools/read.d.ts.map +1 -1
  277. package/dist/core/tools/read.js +1 -1
  278. package/dist/core/tools/read.js.map +1 -1
  279. package/dist/core/tools/search-command-guard.d.ts +14 -0
  280. package/dist/core/tools/search-command-guard.d.ts.map +1 -0
  281. package/dist/core/tools/search-command-guard.js +333 -0
  282. package/dist/core/tools/search-command-guard.js.map +1 -0
  283. package/dist/core/tools/secret-store.d.ts +38 -0
  284. package/dist/core/tools/secret-store.d.ts.map +1 -0
  285. package/dist/core/tools/secret-store.js +412 -0
  286. package/dist/core/tools/secret-store.js.map +1 -0
  287. package/dist/core/tools/shell-command-parser.d.ts +16 -0
  288. package/dist/core/tools/shell-command-parser.d.ts.map +1 -0
  289. package/dist/core/tools/shell-command-parser.js +113 -0
  290. package/dist/core/tools/shell-command-parser.js.map +1 -0
  291. package/dist/core/tools/task-steps.d.ts.map +1 -1
  292. package/dist/core/tools/task-steps.js +4 -7
  293. package/dist/core/tools/task-steps.js.map +1 -1
  294. package/dist/core/util/atomic-file.d.ts +6 -2
  295. package/dist/core/util/atomic-file.d.ts.map +1 -1
  296. package/dist/core/util/atomic-file.js +4 -4
  297. package/dist/core/util/atomic-file.js.map +1 -1
  298. package/dist/index.d.ts +3 -2
  299. package/dist/index.d.ts.map +1 -1
  300. package/dist/index.js +2 -1
  301. package/dist/index.js.map +1 -1
  302. package/dist/main.d.ts.map +1 -1
  303. package/dist/main.js +25 -11
  304. package/dist/main.js.map +1 -1
  305. package/dist/modes/interactive/auth-dialogs-controller.d.ts +4 -0
  306. package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
  307. package/dist/modes/interactive/auth-dialogs-controller.js +60 -1
  308. package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
  309. package/dist/modes/interactive/components/activity-lane.d.ts +55 -0
  310. package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -0
  311. package/dist/modes/interactive/components/activity-lane.js +304 -0
  312. package/dist/modes/interactive/components/activity-lane.js.map +1 -0
  313. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  314. package/dist/modes/interactive/components/assistant-message.js +19 -10
  315. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  316. package/dist/modes/interactive/components/expandable-text.d.ts +12 -0
  317. package/dist/modes/interactive/components/expandable-text.d.ts.map +1 -0
  318. package/dist/modes/interactive/components/expandable-text.js +17 -0
  319. package/dist/modes/interactive/components/expandable-text.js.map +1 -0
  320. package/dist/modes/interactive/components/extension-editor.d.ts +15 -1
  321. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  322. package/dist/modes/interactive/components/extension-editor.js +30 -21
  323. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  324. package/dist/modes/interactive/components/extension-input.d.ts +1 -0
  325. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  326. package/dist/modes/interactive/components/extension-input.js +6 -1
  327. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  328. package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
  329. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  330. package/dist/modes/interactive/components/scoped-models-selector.js +23 -14
  331. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  332. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  333. package/dist/modes/interactive/components/settings-selector.js +4 -4
  334. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  335. package/dist/modes/interactive/components/tool-execution.d.ts +19 -11
  336. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  337. package/dist/modes/interactive/components/tool-execution.js +53 -20
  338. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  339. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  340. package/dist/modes/interactive/components/tool-group.js +3 -0
  341. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  342. package/dist/modes/interactive/components/usage-action-selector.d.ts +16 -0
  343. package/dist/modes/interactive/components/usage-action-selector.d.ts.map +1 -0
  344. package/dist/modes/interactive/components/usage-action-selector.js +46 -0
  345. package/dist/modes/interactive/components/usage-action-selector.js.map +1 -0
  346. package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
  347. package/dist/modes/interactive/extension-ui-host.js +1 -0
  348. package/dist/modes/interactive/extension-ui-host.js.map +1 -1
  349. package/dist/modes/interactive/external-editor.d.ts +15 -0
  350. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  351. package/dist/modes/interactive/external-editor.js +27 -19
  352. package/dist/modes/interactive/external-editor.js.map +1 -1
  353. package/dist/modes/interactive/history-reload-math.d.ts +6 -2
  354. package/dist/modes/interactive/history-reload-math.d.ts.map +1 -1
  355. package/dist/modes/interactive/history-reload-math.js +10 -5
  356. package/dist/modes/interactive/history-reload-math.js.map +1 -1
  357. package/dist/modes/interactive/interactive-event-controller.d.ts +74 -0
  358. package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -0
  359. package/dist/modes/interactive/interactive-event-controller.js +328 -0
  360. package/dist/modes/interactive/interactive-event-controller.js.map +1 -0
  361. package/dist/modes/interactive/interactive-mode.d.ts +20 -25
  362. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  363. package/dist/modes/interactive/interactive-mode.js +196 -608
  364. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  365. package/dist/modes/interactive/loaded-resources-view.d.ts +20 -0
  366. package/dist/modes/interactive/loaded-resources-view.d.ts.map +1 -0
  367. package/dist/modes/interactive/loaded-resources-view.js +147 -0
  368. package/dist/modes/interactive/loaded-resources-view.js.map +1 -0
  369. package/dist/modes/interactive/resource-display.d.ts.map +1 -1
  370. package/dist/modes/interactive/resource-display.js +8 -2
  371. package/dist/modes/interactive/resource-display.js.map +1 -1
  372. package/dist/modes/interactive/resource-shell-commands.d.ts +1 -5
  373. package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
  374. package/dist/modes/interactive/resource-shell-commands.js +0 -5
  375. package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
  376. package/dist/modes/interactive/session-flow-commands.d.ts +10 -6
  377. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  378. package/dist/modes/interactive/session-flow-commands.js +123 -70
  379. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  380. package/dist/modes/interactive/usage-commands.d.ts +34 -0
  381. package/dist/modes/interactive/usage-commands.d.ts.map +1 -0
  382. package/dist/modes/interactive/usage-commands.js +225 -0
  383. package/dist/modes/interactive/usage-commands.js.map +1 -0
  384. package/dist/utils/shell.d.ts.map +1 -1
  385. package/dist/utils/shell.js +5 -3
  386. package/dist/utils/shell.js.map +1 -1
  387. package/dist/utils/version-check.d.ts.map +1 -1
  388. package/dist/utils/version-check.js +3 -1
  389. package/dist/utils/version-check.js.map +1 -1
  390. package/docs/UI_POLISH_ORCHESTRATION.md +25 -31
  391. package/docs/UI_QUALITY_ORCHESTRATION.md +22 -24
  392. package/docs/providers.md +9 -1
  393. package/docs/ui-reference/README.md +6 -6
  394. package/docs/ui-reference/orchestration-after.txt +14 -7
  395. package/docs/ui-reference/orchestration-before.txt +12 -6
  396. package/docs/ui-reference/orchestration-reference.txt +15 -5
  397. package/docs/usage.md +9 -1
  398. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  399. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  400. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  401. package/examples/extensions/sandbox/package-lock.json +2 -2
  402. package/examples/extensions/sandbox/package.json +1 -1
  403. package/examples/extensions/with-deps/package-lock.json +2 -2
  404. package/examples/extensions/with-deps/package.json +1 -1
  405. package/npm-shrinkwrap.json +13 -13
  406. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@caupulican/pi-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@caupulican/pi-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Recover an OAuth credential rejected before a response body was consumed.\n\t * The lock re-checks the file so a concurrent process's rotation wins without\n\t * causing an unnecessary refresh.\n\t */\n\tasync recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise<string | undefined> {\n\t\tif (this.runtimeOverrides.has(providerId)) return undefined;\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) return undefined;\n\n\t\treturn this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tconst credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") return { result: undefined };\n\n\t\t\tconst currentKey = provider.getApiKey(credential);\n\t\t\tif (currentKey !== rejectedApiKey && Date.now() < credential.expires) {\n\t\t\t\treturn { result: currentKey };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(credential);\n\t\t\t\tconst nextCredential: OAuthCredential = { type: \"oauth\", ...refreshed };\n\t\t\t\tconst nextData = { ...currentData, [providerId]: nextCredential };\n\t\t\t\tthis.data = nextData;\n\t\t\t\tthis.loadError = null;\n\t\t\t\treturn { result: provider.getApiKey(nextCredential), next: JSON.stringify(nextData, null, 2) };\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\treturn { result: undefined };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\tif (!provider) {\n\t\t\t\t// Unknown OAuth provider, can't get API key\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Check if token needs refresh\n\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\tif (needsRefresh) {\n\t\t\t\t// Use locked refresh to prevent race conditions\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\treturn result.apiKey;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t// Refresh failed - re-read file to check if another instance succeeded\n\t\t\t\t\tthis.reload();\n\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\tif (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\n\t\t\t\t\t\t// Another instance refreshed successfully, use those credentials\n\t\t\t\t\t\treturn provider.getApiKey(updatedCred);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Refresh truly failed - return undefined so model discovery skips this provider\n\t\t\t\t\t// User can /login to re-authenticate (credentials preserved for retry)\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Token not expired, use current access token\n\t\t\t\treturn provider.getApiKey(cred);\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
1
+ {"version":3,"file":"auth-storage.d.ts","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\tgetEnvAuthHeaders,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@caupulican/pi-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@caupulican/pi-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvAuthHeaders(provider)) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Recover an OAuth credential rejected before a response body was consumed.\n\t * The lock re-checks the file so a concurrent process's rotation wins without\n\t * causing an unnecessary refresh.\n\t */\n\tasync recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise<string | undefined> {\n\t\tif (this.runtimeOverrides.has(providerId)) return undefined;\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) return undefined;\n\n\t\treturn this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tconst credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") return { result: undefined };\n\n\t\t\tconst currentKey = provider.getApiKey(credential);\n\t\t\tif (currentKey !== rejectedApiKey && Date.now() < credential.expires) {\n\t\t\t\treturn { result: currentKey };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(credential);\n\t\t\t\tconst nextCredential: OAuthCredential = { type: \"oauth\", ...refreshed };\n\t\t\t\tconst nextData = { ...currentData, [providerId]: nextCredential };\n\t\t\t\tthis.data = nextData;\n\t\t\t\tthis.loadError = null;\n\t\t\t\treturn { result: provider.getApiKey(nextCredential), next: JSON.stringify(nextData, null, 2) };\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\treturn { result: undefined };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\tif (!provider) {\n\t\t\t\t// Unknown OAuth provider, can't get API key\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Check if token needs refresh\n\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\tif (needsRefresh) {\n\t\t\t\t// Use locked refresh to prevent race conditions\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\treturn result.apiKey;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t// Refresh failed - re-read file to check if another instance succeeded\n\t\t\t\t\tthis.reload();\n\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\tif (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\n\t\t\t\t\t\t// Another instance refreshed successfully, use those credentials\n\t\t\t\t\t\treturn provider.getApiKey(updatedCred);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Refresh truly failed - return undefined so model discovery skips this provider\n\t\t\t\t\t// User can /login to re-authenticate (credentials preserved for retry)\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Token not expired, use current access token\n\t\t\t\treturn provider.getApiKey(cred);\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  * Uses file locking to prevent race conditions when multiple pi instances
6
6
  * try to refresh tokens simultaneously.
7
7
  */
8
- import { findEnvKeys, getEnvApiKey, } from "@caupulican/pi-ai";
8
+ import { findEnvKeys, getEnvApiKey, getEnvAuthHeaders, } from "@caupulican/pi-ai";
9
9
  import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@caupulican/pi-ai/oauth";
10
10
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
11
11
  import { dirname, join } from "path";
@@ -276,6 +276,8 @@ export class AuthStorage {
276
276
  return true;
277
277
  if (this.data[provider])
278
278
  return true;
279
+ if (getEnvAuthHeaders(provider))
280
+ return true;
279
281
  if (getEnvApiKey(provider))
280
282
  return true;
281
283
  if (this.fallbackResolver?.(provider))
@@ -1 +1 @@
1
- {"version":3,"file":"auth-storage.js","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@caupulican/pi-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@caupulican/pi-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Recover an OAuth credential rejected before a response body was consumed.\n\t * The lock re-checks the file so a concurrent process's rotation wins without\n\t * causing an unnecessary refresh.\n\t */\n\tasync recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise<string | undefined> {\n\t\tif (this.runtimeOverrides.has(providerId)) return undefined;\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) return undefined;\n\n\t\treturn this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tconst credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") return { result: undefined };\n\n\t\t\tconst currentKey = provider.getApiKey(credential);\n\t\t\tif (currentKey !== rejectedApiKey && Date.now() < credential.expires) {\n\t\t\t\treturn { result: currentKey };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(credential);\n\t\t\t\tconst nextCredential: OAuthCredential = { type: \"oauth\", ...refreshed };\n\t\t\t\tconst nextData = { ...currentData, [providerId]: nextCredential };\n\t\t\t\tthis.data = nextData;\n\t\t\t\tthis.loadError = null;\n\t\t\t\treturn { result: provider.getApiKey(nextCredential), next: JSON.stringify(nextData, null, 2) };\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\treturn { result: undefined };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\tif (!provider) {\n\t\t\t\t// Unknown OAuth provider, can't get API key\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Check if token needs refresh\n\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\tif (needsRefresh) {\n\t\t\t\t// Use locked refresh to prevent race conditions\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\treturn result.apiKey;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t// Refresh failed - re-read file to check if another instance succeeded\n\t\t\t\t\tthis.reload();\n\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\tif (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\n\t\t\t\t\t\t// Another instance refreshed successfully, use those credentials\n\t\t\t\t\t\treturn provider.getApiKey(updatedCred);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Refresh truly failed - return undefined so model discovery skips this provider\n\t\t\t\t\t// User can /login to re-authenticate (credentials preserved for retry)\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Token not expired, use current access token\n\t\t\t\treturn provider.getApiKey(cred);\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
1
+ {"version":3,"file":"auth-storage.js","sourceRoot":"","sources":["../../src/core/auth-storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n *\n * Uses file locking to prevent race conditions when multiple pi instances\n * try to refresh tokens simultaneously.\n */\n\nimport {\n\tfindEnvKeys,\n\tgetEnvApiKey,\n\tgetEnvAuthHeaders,\n\ttype OAuthCredentials,\n\ttype OAuthLoginCallbacks,\n\ttype OAuthProviderId,\n} from \"@caupulican/pi-ai\";\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from \"@caupulican/pi-ai/oauth\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../config.ts\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { resolveConfigValue } from \"./resolve-config-value.ts\";\n\nexport type ApiKeyCredential = {\n\ttype: \"api_key\";\n\tkey: string;\n};\n\nexport type OAuthCredential = {\n\ttype: \"oauth\";\n} & OAuthCredentials;\n\nexport type AuthCredential = ApiKeyCredential | OAuthCredential;\n\nexport type AuthStorageData = Record<string, AuthCredential>;\n\nexport type AuthStatus = {\n\tconfigured: boolean;\n\tsource?: \"stored\" | \"runtime\" | \"environment\" | \"fallback\" | \"models_json_key\" | \"models_json_command\";\n\tlabel?: string;\n};\n\ntype LockResult<T> = {\n\tresult: T;\n\tnext?: string;\n};\n\nconst AUTH_FILE_WRITE_OPTIONS = { encoding: \"utf-8\", mode: 0o600 } as const;\n\nexport interface AuthStorageBackend {\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T;\n\twithLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T>;\n}\n\nexport class FileAuthStorageBackend implements AuthStorageBackend {\n\tprivate authPath: string;\n\n\tconstructor(authPath: string = join(getAgentDir(), \"auth.json\")) {\n\t\tthis.authPath = normalizePath(authPath);\n\t}\n\n\tprivate ensureParentDir(): void {\n\t\tconst dir = dirname(this.authPath);\n\t\tif (!existsSync(dir)) {\n\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t}\n\t}\n\n\tprivate ensureFileExists(): void {\n\t\tif (!existsSync(this.authPath)) {\n\t\t\twriteFileSync(this.authPath, \"{}\", AUTH_FILE_WRITE_OPTIONS);\n\t\t\tchmodSync(this.authPath, 0o600);\n\t\t}\n\t}\n\n\tprivate acquireLockSyncWithRetry(path: string): () => void {\n\t\tconst maxAttempts = 10;\n\t\tconst delayMs = 20;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn lockfile.lockSync(path, { realpath: false });\n\t\t\t} catch (error) {\n\t\t\t\tconst code =\n\t\t\t\t\ttypeof error === \"object\" && error !== null && \"code\" in error\n\t\t\t\t\t\t? String((error as { code?: unknown }).code)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (code !== \"ELOCKED\" || attempt === maxAttempts) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tlastError = error;\n\t\t\t\tconst start = Date.now();\n\t\t\t\twhile (Date.now() - start < delayMs) {\n\t\t\t\t\t// Sleep synchronously to avoid changing callers to async.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthrow (lastError as Error) ?? new Error(\"Failed to acquire auth storage lock\");\n\t}\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => void) | undefined;\n\t\ttry {\n\t\t\trelease = this.acquireLockSyncWithRetry(this.authPath);\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = fn(current);\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t}\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tthis.ensureParentDir();\n\t\tthis.ensureFileExists();\n\n\t\tlet release: (() => Promise<void>) | undefined;\n\t\tlet lockCompromised = false;\n\t\tlet lockCompromisedError: Error | undefined;\n\t\tconst throwIfCompromised = () => {\n\t\t\tif (lockCompromised) {\n\t\t\t\tthrow lockCompromisedError ?? new Error(\"Auth storage lock was compromised\");\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\trelease = await lockfile.lock(this.authPath, {\n\t\t\t\tretries: {\n\t\t\t\t\tretries: 10,\n\t\t\t\t\tfactor: 2,\n\t\t\t\t\tminTimeout: 100,\n\t\t\t\t\tmaxTimeout: 10000,\n\t\t\t\t\trandomize: true,\n\t\t\t\t},\n\t\t\t\tstale: 30000,\n\t\t\t\tonCompromised: (err) => {\n\t\t\t\t\tlockCompromised = true;\n\t\t\t\t\tlockCompromisedError = err;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tthrowIfCompromised();\n\t\t\tconst current = existsSync(this.authPath) ? readFileSync(this.authPath, \"utf-8\") : undefined;\n\t\t\tconst { result, next } = await fn(current);\n\t\t\tthrowIfCompromised();\n\t\t\tif (next !== undefined) {\n\t\t\t\twriteFileSync(this.authPath, next, AUTH_FILE_WRITE_OPTIONS);\n\t\t\t\tchmodSync(this.authPath, 0o600);\n\t\t\t}\n\t\t\tthrowIfCompromised();\n\t\t\treturn result;\n\t\t} finally {\n\t\t\tif (release) {\n\t\t\t\ttry {\n\t\t\t\t\tawait release();\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore unlock errors when lock is compromised.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class InMemoryAuthStorageBackend implements AuthStorageBackend {\n\tprivate value: string | undefined;\n\n\twithLock<T>(fn: (current: string | undefined) => LockResult<T>): T {\n\t\tconst { result, next } = fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n\n\tasync withLockAsync<T>(fn: (current: string | undefined) => Promise<LockResult<T>>): Promise<T> {\n\t\tconst { result, next } = await fn(this.value);\n\t\tif (next !== undefined) {\n\t\t\tthis.value = next;\n\t\t}\n\t\treturn result;\n\t}\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n\tprivate data: AuthStorageData = {};\n\tprivate runtimeOverrides: Map<string, string> = new Map();\n\tprivate fallbackResolver?: (provider: string) => string | undefined;\n\tprivate loadError: Error | null = null;\n\tprivate errors: Error[] = [];\n\tprivate storage: AuthStorageBackend;\n\n\tprivate constructor(storage: AuthStorageBackend) {\n\t\tthis.storage = storage;\n\t\tthis.reload();\n\t}\n\n\tstatic create(authPath?: string): AuthStorage {\n\t\treturn new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), \"auth.json\")));\n\t}\n\n\tstatic fromStorage(storage: AuthStorageBackend): AuthStorage {\n\t\treturn new AuthStorage(storage);\n\t}\n\n\tstatic inMemory(data: AuthStorageData = {}): AuthStorage {\n\t\tconst storage = new InMemoryAuthStorageBackend();\n\t\tstorage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));\n\t\treturn AuthStorage.fromStorage(storage);\n\t}\n\n\t/**\n\t * Set a runtime API key override (not persisted to disk).\n\t * Used for CLI --api-key flag.\n\t */\n\tsetRuntimeApiKey(provider: string, apiKey: string): void {\n\t\tthis.runtimeOverrides.set(provider, apiKey);\n\t}\n\n\t/**\n\t * Remove a runtime API key override.\n\t */\n\tremoveRuntimeApiKey(provider: string): void {\n\t\tthis.runtimeOverrides.delete(provider);\n\t}\n\n\t/**\n\t * Set a fallback resolver for API keys not found in auth.json or env vars.\n\t * Used for custom provider keys from models.json.\n\t */\n\tsetFallbackResolver(resolver: (provider: string) => string | undefined): void {\n\t\tthis.fallbackResolver = resolver;\n\t}\n\n\tprivate recordError(error: unknown): void {\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\tthis.errors.push(normalizedError);\n\t}\n\n\tprivate parseStorageData(content: string | undefined): AuthStorageData {\n\t\tif (!content) {\n\t\t\treturn {};\n\t\t}\n\t\treturn JSON.parse(content) as AuthStorageData;\n\t}\n\n\t/**\n\t * Reload credentials from storage.\n\t */\n\treload(): void {\n\t\tlet content: string | undefined;\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tcontent = current;\n\t\t\t\treturn { result: undefined };\n\t\t\t});\n\t\t\tthis.data = this.parseStorageData(content);\n\t\t\tthis.loadError = null;\n\t\t} catch (error) {\n\t\t\tthis.loadError = error as Error;\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\tprivate persistProviderChange(provider: string, credential: AuthCredential | undefined): void {\n\t\tif (this.loadError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.storage.withLock((current) => {\n\t\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\t\tconst merged: AuthStorageData = { ...currentData };\n\t\t\t\tif (credential) {\n\t\t\t\t\tmerged[provider] = credential;\n\t\t\t\t} else {\n\t\t\t\t\tdelete merged[provider];\n\t\t\t\t}\n\t\t\t\treturn { result: undefined, next: JSON.stringify(merged, null, 2) };\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis.recordError(error);\n\t\t}\n\t}\n\n\t/**\n\t * Get credential for a provider.\n\t */\n\tget(provider: string): AuthCredential | undefined {\n\t\treturn this.data[provider] ?? undefined;\n\t}\n\n\t/**\n\t * Set credential for a provider.\n\t */\n\tset(provider: string, credential: AuthCredential): void {\n\t\tthis.data[provider] = credential;\n\t\tthis.persistProviderChange(provider, credential);\n\t}\n\n\t/**\n\t * Remove credential for a provider.\n\t */\n\tremove(provider: string): void {\n\t\tdelete this.data[provider];\n\t\tthis.persistProviderChange(provider, undefined);\n\t}\n\n\t/**\n\t * List all providers with credentials.\n\t */\n\tlist(): string[] {\n\t\treturn Object.keys(this.data);\n\t}\n\n\t/**\n\t * Check if credentials exist for a provider in auth.json.\n\t */\n\thas(provider: string): boolean {\n\t\treturn provider in this.data;\n\t}\n\n\t/**\n\t * Check if any form of auth is configured for a provider.\n\t * Unlike getApiKey(), this doesn't refresh OAuth tokens.\n\t */\n\thasAuth(provider: string): boolean {\n\t\tif (this.runtimeOverrides.has(provider)) return true;\n\t\tif (this.data[provider]) return true;\n\t\tif (getEnvAuthHeaders(provider)) return true;\n\t\tif (getEnvApiKey(provider)) return true;\n\t\tif (this.fallbackResolver?.(provider)) return true;\n\t\treturn false;\n\t}\n\n\t/**\n\t * Return auth status without exposing credential values or refreshing tokens.\n\t */\n\tgetAuthStatus(provider: string): AuthStatus {\n\t\tif (this.data[provider]) {\n\t\t\treturn { configured: true, source: \"stored\" };\n\t\t}\n\n\t\tif (this.runtimeOverrides.has(provider)) {\n\t\t\treturn { configured: false, source: \"runtime\", label: \"--api-key\" };\n\t\t}\n\n\t\tconst envKeys = findEnvKeys(provider);\n\t\tif (envKeys?.[0]) {\n\t\t\treturn { configured: false, source: \"environment\", label: envKeys[0] };\n\t\t}\n\n\t\tif (this.fallbackResolver?.(provider)) {\n\t\t\treturn { configured: false, source: \"fallback\", label: \"custom provider config\" };\n\t\t}\n\n\t\treturn { configured: false };\n\t}\n\n\t/**\n\t * Get all credentials (for passing to getOAuthApiKey).\n\t */\n\tgetAll(): AuthStorageData {\n\t\treturn { ...this.data };\n\t}\n\n\tdrainErrors(): Error[] {\n\t\tconst drained = [...this.errors];\n\t\tthis.errors = [];\n\t\treturn drained;\n\t}\n\n\t/**\n\t * Login to an OAuth provider.\n\t */\n\tasync login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\tthrow new Error(`Unknown OAuth provider: ${providerId}`);\n\t\t}\n\n\t\tconst credentials = await provider.login(callbacks);\n\t\tthis.set(providerId, { type: \"oauth\", ...credentials });\n\t}\n\n\t/**\n\t * Logout from a provider.\n\t */\n\tlogout(provider: string): void {\n\t\tthis.remove(provider);\n\t}\n\n\t/**\n\t * Refresh OAuth token with backend locking to prevent race conditions.\n\t * Multiple pi instances may try to refresh simultaneously when tokens expire.\n\t */\n\tprivate async refreshOAuthTokenWithLock(\n\t\tproviderId: OAuthProviderId,\n\t): Promise<{ apiKey: string; newCredentials: OAuthCredentials } | null> {\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst result = await this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tthis.loadError = null;\n\n\t\t\tconst cred = currentData[providerId];\n\t\t\tif (cred?.type !== \"oauth\") {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tif (Date.now() < cred.expires) {\n\t\t\t\treturn { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };\n\t\t\t}\n\n\t\t\tconst oauthCreds: Record<string, OAuthCredentials> = {};\n\t\t\tfor (const [key, value] of Object.entries(currentData)) {\n\t\t\t\tif (value.type === \"oauth\") {\n\t\t\t\t\toauthCreds[key] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst refreshed = await getOAuthApiKey(providerId, oauthCreds);\n\t\t\tif (!refreshed) {\n\t\t\t\treturn { result: null };\n\t\t\t}\n\n\t\t\tconst merged: AuthStorageData = {\n\t\t\t\t...currentData,\n\t\t\t\t[providerId]: { type: \"oauth\", ...refreshed.newCredentials },\n\t\t\t};\n\t\t\tthis.data = merged;\n\t\t\tthis.loadError = null;\n\t\t\treturn { result: refreshed, next: JSON.stringify(merged, null, 2) };\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Recover an OAuth credential rejected before a response body was consumed.\n\t * The lock re-checks the file so a concurrent process's rotation wins without\n\t * causing an unnecessary refresh.\n\t */\n\tasync recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise<string | undefined> {\n\t\tif (this.runtimeOverrides.has(providerId)) return undefined;\n\t\tconst provider = getOAuthProvider(providerId);\n\t\tif (!provider) return undefined;\n\n\t\treturn this.storage.withLockAsync(async (current) => {\n\t\t\tconst currentData = this.parseStorageData(current);\n\t\t\tthis.data = currentData;\n\t\t\tconst credential = currentData[providerId];\n\t\t\tif (credential?.type !== \"oauth\") return { result: undefined };\n\n\t\t\tconst currentKey = provider.getApiKey(credential);\n\t\t\tif (currentKey !== rejectedApiKey && Date.now() < credential.expires) {\n\t\t\t\treturn { result: currentKey };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst refreshed = await provider.refreshToken(credential);\n\t\t\t\tconst nextCredential: OAuthCredential = { type: \"oauth\", ...refreshed };\n\t\t\t\tconst nextData = { ...currentData, [providerId]: nextCredential };\n\t\t\t\tthis.data = nextData;\n\t\t\t\tthis.loadError = null;\n\t\t\t\treturn { result: provider.getApiKey(nextCredential), next: JSON.stringify(nextData, null, 2) };\n\t\t\t} catch (error) {\n\t\t\t\tthis.recordError(error);\n\t\t\t\treturn { result: undefined };\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get API key for a provider.\n\t * Priority:\n\t * 1. Runtime override (CLI --api-key)\n\t * 2. API key from auth.json\n\t * 3. OAuth token from auth.json (auto-refreshed with locking)\n\t * 4. Environment variable\n\t * 5. Fallback resolver (models.json custom providers)\n\t */\n\tasync getApiKey(providerId: string, options?: { includeFallback?: boolean }): Promise<string | undefined> {\n\t\t// Runtime override takes highest priority\n\t\tconst runtimeKey = this.runtimeOverrides.get(providerId);\n\t\tif (runtimeKey) {\n\t\t\treturn runtimeKey;\n\t\t}\n\n\t\tconst cred = this.data[providerId];\n\n\t\tif (cred?.type === \"api_key\") {\n\t\t\treturn resolveConfigValue(cred.key);\n\t\t}\n\n\t\tif (cred?.type === \"oauth\") {\n\t\t\tconst provider = getOAuthProvider(providerId);\n\t\t\tif (!provider) {\n\t\t\t\t// Unknown OAuth provider, can't get API key\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Check if token needs refresh\n\t\t\tconst needsRefresh = Date.now() >= cred.expires;\n\n\t\t\tif (needsRefresh) {\n\t\t\t\t// Use locked refresh to prevent race conditions\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.refreshOAuthTokenWithLock(providerId);\n\t\t\t\t\tif (result) {\n\t\t\t\t\t\treturn result.apiKey;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.recordError(error);\n\t\t\t\t\t// Refresh failed - re-read file to check if another instance succeeded\n\t\t\t\t\tthis.reload();\n\t\t\t\t\tconst updatedCred = this.data[providerId];\n\n\t\t\t\t\tif (updatedCred?.type === \"oauth\" && Date.now() < updatedCred.expires) {\n\t\t\t\t\t\t// Another instance refreshed successfully, use those credentials\n\t\t\t\t\t\treturn provider.getApiKey(updatedCred);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Refresh truly failed - return undefined so model discovery skips this provider\n\t\t\t\t\t// User can /login to re-authenticate (credentials preserved for retry)\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Token not expired, use current access token\n\t\t\t\treturn provider.getApiKey(cred);\n\t\t\t}\n\t\t}\n\n\t\t// Fall back to environment variable\n\t\tconst envKey = getEnvApiKey(providerId);\n\t\tif (envKey) return envKey;\n\n\t\t// Fall back to custom resolver (e.g., models.json custom providers)\n\t\tif (options?.includeFallback !== false) {\n\t\t\treturn this.fallbackResolver?.(providerId) ?? undefined;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get all registered OAuth providers\n\t */\n\tgetOAuthProviders() {\n\t\treturn getOAuthProviders();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"lane-tool-surface.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/lane-tool-surface.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAgC,MAAM,wCAAwC,CAAC;AACzG,OAAO,KAAK,EACX,cAAc,EACd,4BAA4B,EAC5B,sBAAsB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAsBhE,MAAM,WAAW,eAAe;IAC/B,oGAAoG;IACpG,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,8FAA8F;IAC9F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,yEAAyE;IACzE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iGAAiG;IACjG,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC/D,+EAA+E;IAC/E,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,yEAAyE;IACzE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,wGAAwG;IACxG,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,qGAAqG;IACrG,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAClD;AA6DD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAiGtF","sourcesContent":["import path from \"node:path\";\nimport type { AgentLoopConfig, AgentTool } from \"@caupulican/pi-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport { CapabilityGateway, CapabilityGatewayDeniedError } from \"../orchestration/capability-gateway.ts\";\nimport type {\n\tExecutionGrant,\n\tOrchestrationExecutionPolicy,\n\tToolCapabilityManifest,\n} from \"../orchestration/contracts.ts\";\nimport type { NormalizedProfile } from \"../profile-registry.ts\";\nimport { matchesResourceProfilePattern } from \"../settings-manager.ts\";\nimport { createEditTool } from \"../tools/edit.ts\";\nimport { createFindTool } from \"../tools/find.ts\";\nimport { createGrepTool } from \"../tools/grep.ts\";\nimport { createLsTool } from \"../tools/ls.ts\";\nimport { createReadTool } from \"../tools/read.ts\";\nimport { createRunProcessTool } from \"../tools/run-process.ts\";\nimport { createWriteTool } from \"../tools/write.ts\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { evaluateToolGate } from \"./gates.ts\";\n\nconst READ_ONLY_LANE_TOOL_NAMES = [\"read\", \"grep\", \"find\", \"ls\"] as const;\nconst MEMORY_LANE_TOOL_NAME = \"memory\" as const;\nconst WRITE_LANE_TOOL_NAMES = [\"write\", \"edit\"] as const;\nconst PROCESS_LANE_TOOL_NAME = \"run_process\" as const;\nconst laneMemorySchema = Type.Object({\n\tquery: Type.String({ description: \"What relevant standing memory or prior evidence to retrieve\" }),\n});\ntype LaneMemoryParams = Static<typeof laneMemorySchema>;\nconst WRITE_LANE_TOOL_NAME_SET = new Set<string>(WRITE_LANE_TOOL_NAMES);\n\nexport interface LaneToolSurface {\n\t/** Fresh tools owned by this lane only; no foreground tool instances or extension state leak in. */\n\ttools: AgentTool[];\n\t/** Exact names after profile globs are expanded. Safe to persist in a capability envelope. */\n\tallowedTools: string[];\n\t/** Exact safe-candidate names denied by the profile's block patterns. */\n\tdeniedTools: string[];\n\t/** Explicit grants that bind to no classified lane candidate (opaque tools stay fail-closed). */\n\tunboundAllowPatterns: string[];\n\t/** Per-call path and capability gate for the isolated child loop. */\n\tbeforeToolCall: NonNullable<AgentLoopConfig[\"beforeToolCall\"]>;\n\t/** Canonical cumulative authority/budget meter for a compiled worker grant. */\n\tgateway?: CapabilityGateway;\n}\n\nexport interface LaneToolSurfaceOptions {\n\tcwd: string;\n\tprofile?: NormalizedProfile;\n\t/** Private harness state that generic file tools must never traverse. */\n\tdeniedPaths?: readonly string[];\n\t/** Orchestrator-requested, policy-filtered read-only memory retrieval. Omitted means no memory tool. */\n\treadMemory?: (query: string) => Promise<string>;\n\t/** Research never sets this. Workers require both this flag and at least one write path. */\n\twriteEnabled?: boolean;\n\twritePaths?: readonly string[];\n\t/** Present only for process-capable owner profiles; absent means no process tool is materialized. */\n\texecutionPolicy?: OrchestrationExecutionPolicy;\n\tprocessMaxWallClockMs?: number;\n\t/** Compiled policy path. When present, it is the only authorization source for this surface. */\n\tgrant?: ExecutionGrant;\n\ttoolManifests?: readonly ToolCapabilityManifest[];\n}\n\nfunction strictLaneProfilePatterns(profile: NormalizedProfile | undefined): {\n\tallow: string[];\n\tblock: string[];\n} {\n\tif (!profile) return { allow: [], block: [] };\n\tconst filter = profile.resources.tools;\n\tconst allow = [...(filter?.allow ?? [])];\n\tconst block = [...(filter?.block ?? [])];\n\t// Strict UAC: a shipped profile is the complete authority grant. Grant-all must be explicit.\n\tif (allow.length === 0 && block.length === 0) return { allow: [], block: [\"*\"] };\n\treturn { allow, block };\n}\n\nfunction resolveWriteRoots(cwd: string, writePaths: readonly string[]): string[] {\n\treturn writePaths.map((entry) => (path.isAbsolute(entry) ? path.resolve(entry) : path.resolve(cwd, entry)));\n}\n\nfunction createLaneTools(\n\tcwd: string,\n\tnames: readonly string[],\n\treadMemory?: (query: string) => Promise<string>,\n\texecutionPolicy?: OrchestrationExecutionPolicy,\n\tprocessMaxWallClockMs = 0,\n): AgentTool[] {\n\tconst factories = new Map<string, () => AgentTool>([\n\t\t[\"read\", () => createReadTool(cwd)],\n\t\t[\"grep\", () => createGrepTool(cwd)],\n\t\t[\"find\", () => createFindTool(cwd)],\n\t\t[\"ls\", () => createLsTool(cwd)],\n\t\t[\"write\", () => createWriteTool(cwd)],\n\t\t[\"edit\", () => createEditTool(cwd)],\n\t]);\n\tif (executionPolicy) {\n\t\tfactories.set(PROCESS_LANE_TOOL_NAME, () =>\n\t\t\tcreateRunProcessTool(cwd, { policy: executionPolicy, maxWallClockMs: processMaxWallClockMs }),\n\t\t);\n\t}\n\tif (readMemory) {\n\t\tfactories.set(MEMORY_LANE_TOOL_NAME, () => ({\n\t\t\tname: MEMORY_LANE_TOOL_NAME,\n\t\t\tlabel: \"Read Memory\",\n\t\t\tdescription:\n\t\t\t\t\"Retrieve bounded, source-labeled standing memory relevant to this delegated task. Read-only: no memory writes or lifecycle actions are available.\",\n\t\t\tparameters: laneMemorySchema,\n\t\t\texecute: async (_toolCallId, params) => {\n\t\t\t\tconst { query } = params as LaneMemoryParams;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: await readMemory(query) }],\n\t\t\t\t\tdetails: { readOnly: true },\n\t\t\t\t};\n\t\t\t},\n\t\t}));\n\t}\n\treturn names.flatMap((name) => {\n\t\tconst factory = factories.get(name);\n\t\treturn factory ? [factory()] : [];\n\t});\n}\n\n/**\n * Materialize a fresh, fail-closed tool surface for one isolated lane.\n *\n * Profiles select only from classified built-ins. Opaque extension tools, shell, memory, goals,\n * and `delegate` are never candidates, so a wildcard cannot manufacture unknown authority or\n * recursively spawn more workers. Write/edit additionally require the explicit worker write switch\n * and a non-empty path scope; research therefore stays read-only regardless of profile contents.\n */\nexport function createLaneToolSurface(options: LaneToolSurfaceOptions): LaneToolSurface {\n\tconst writeCapable = options.writeEnabled === true && (options.writePaths?.length ?? 0) > 0;\n\tconst candidateNames = [\n\t\t...READ_ONLY_LANE_TOOL_NAMES,\n\t\t...(options.readMemory ? [MEMORY_LANE_TOOL_NAME] : []),\n\t\t...(writeCapable ? WRITE_LANE_TOOL_NAMES : []),\n\t\t...(options.executionPolicy ? [PROCESS_LANE_TOOL_NAME] : []),\n\t];\n\tconst patterns = strictLaneProfilePatterns(options.profile);\n\tconst compiledToolNames = new Set(options.grant?.allowedTools ?? []);\n\tconst deniedTools = candidateNames.filter((name) =>\n\t\toptions.grant ? !compiledToolNames.has(name) : matchesResourceProfilePattern(name, patterns.block),\n\t);\n\tconst unboundAllowPatterns = options.grant\n\t\t? []\n\t\t: patterns.allow.filter(\n\t\t\t\t(pattern) => !candidateNames.some((name) => matchesResourceProfilePattern(name, [pattern])),\n\t\t\t);\n\tconst allowedTools = options.grant\n\t\t? candidateNames.filter((name) => compiledToolNames.has(name))\n\t\t: candidateNames.filter(\n\t\t\t\t(name) =>\n\t\t\t\t\t(patterns.allow.length === 0 || matchesResourceProfilePattern(name, patterns.allow)) &&\n\t\t\t\t\t!matchesResourceProfilePattern(name, patterns.block),\n\t\t\t);\n\tconst allowedToolSet = new Set<string>(allowedTools);\n\tconst manifestsByName = new Map(options.toolManifests?.map((manifest) => [manifest.toolName, manifest]) ?? []);\n\tif (options.grant?.allowedTools.some((name) => !manifestsByName.has(name))) {\n\t\tthrow new Error(\"Compiled lane grant references a tool without a capability manifest.\");\n\t}\n\tconst gateway = options.grant ? new CapabilityGateway({ grant: options.grant, cwd: options.cwd }) : undefined;\n\tconst deniedPaths = options.deniedPaths?.map((entry) => path.resolve(entry));\n\tconst readEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-read-tools\",\n\t\tcapabilities: [\n\t\t\t\"filesystem.read\",\n\t\t\t...(allowedToolSet.has(MEMORY_LANE_TOOL_NAME) ? ([\"memory.query\"] as const) : []),\n\t\t\t...(allowedToolSet.has(PROCESS_LANE_TOOL_NAME) ? ([\"process.exec\"] as const) : []),\n\t\t],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: [path.resolve(options.cwd)],\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\tconst writeEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-write-tools\",\n\t\tcapabilities: [\"filesystem.write\"],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: resolveWriteRoots(options.cwd, options.writePaths ?? []),\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\n\treturn {\n\t\ttools: createLaneTools(\n\t\t\toptions.cwd,\n\t\t\tallowedTools,\n\t\t\toptions.readMemory,\n\t\t\toptions.executionPolicy,\n\t\t\toptions.processMaxWallClockMs,\n\t\t),\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tunboundAllowPatterns,\n\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\tif (!allowedToolSet.has(toolCall.name)) {\n\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' is outside the materialized UAC surface.` };\n\t\t\t}\n\t\t\tif (gateway) {\n\t\t\t\tconst manifest = manifestsByName.get(toolCall.name);\n\t\t\t\tif (!manifest) {\n\t\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' has no compiled capability manifest.` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tgateway.authorizeToolCall(manifest, toolCall.name, args);\n\t\t\t\t\treturn undefined;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof CapabilityGatewayDeniedError) {\n\t\t\t\t\t\treturn { block: true, reason: `Lane tool blocked (${error.reasonCode}): ${error.message}` };\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst outcome = evaluateToolGate({\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\targs,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenvelope: WRITE_LANE_TOOL_NAME_SET.has(toolCall.name) ? writeEnvelope : readEnvelope,\n\t\t\t});\n\t\t\tif (outcome.outcome === \"allow\") return undefined;\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: `Lane tool blocked (${outcome.reasonCode}): ${outcome.message ?? \"capability gate denied it\"}`,\n\t\t\t};\n\t\t},\n\t\t...(gateway ? { gateway } : {}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"lane-tool-surface.d.ts","sourceRoot":"","sources":["../../../src/core/autonomy/lane-tool-surface.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,EAAE,iBAAiB,EAAgC,MAAM,wCAAwC,CAAC;AACzG,OAAO,KAAK,EACX,cAAc,EACd,4BAA4B,EAC5B,sBAAsB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAuBhE,MAAM,WAAW,eAAe;IAC/B,oGAAoG;IACpG,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,8FAA8F;IAC9F,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,yEAAyE;IACzE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iGAAiG;IACjG,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC/D,+EAA+E;IAC/E,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,yEAAyE;IACzE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,wGAAwG;IACxG,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,qGAAqG;IACrG,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,aAAa,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAClD;AA6DD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAiGtF","sourcesContent":["import path from \"node:path\";\nimport type { AgentLoopConfig, AgentTool } from \"@caupulican/pi-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport { CapabilityGateway, CapabilityGatewayDeniedError } from \"../orchestration/capability-gateway.ts\";\nimport type {\n\tExecutionGrant,\n\tOrchestrationExecutionPolicy,\n\tToolCapabilityManifest,\n} from \"../orchestration/contracts.ts\";\nimport type { NormalizedProfile } from \"../profile-registry.ts\";\nimport { wrapToolWithCredentialExposureGuard } from \"../secrets/credential-exposure-guard.ts\";\nimport { matchesResourceProfilePattern } from \"../settings-manager.ts\";\nimport { createEditTool } from \"../tools/edit.ts\";\nimport { createFindTool } from \"../tools/find.ts\";\nimport { createGrepTool } from \"../tools/grep.ts\";\nimport { createLsTool } from \"../tools/ls.ts\";\nimport { createReadTool } from \"../tools/read.ts\";\nimport { createRunProcessTool } from \"../tools/run-process.ts\";\nimport { createWriteTool } from \"../tools/write.ts\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { evaluateToolGate } from \"./gates.ts\";\n\nconst READ_ONLY_LANE_TOOL_NAMES = [\"read\", \"grep\", \"find\", \"ls\"] as const;\nconst MEMORY_LANE_TOOL_NAME = \"memory\" as const;\nconst WRITE_LANE_TOOL_NAMES = [\"write\", \"edit\"] as const;\nconst PROCESS_LANE_TOOL_NAME = \"run_process\" as const;\nconst laneMemorySchema = Type.Object({\n\tquery: Type.String({ description: \"What relevant standing memory or prior evidence to retrieve\" }),\n});\ntype LaneMemoryParams = Static<typeof laneMemorySchema>;\nconst WRITE_LANE_TOOL_NAME_SET = new Set<string>(WRITE_LANE_TOOL_NAMES);\n\nexport interface LaneToolSurface {\n\t/** Fresh tools owned by this lane only; no foreground tool instances or extension state leak in. */\n\ttools: AgentTool[];\n\t/** Exact names after profile globs are expanded. Safe to persist in a capability envelope. */\n\tallowedTools: string[];\n\t/** Exact safe-candidate names denied by the profile's block patterns. */\n\tdeniedTools: string[];\n\t/** Explicit grants that bind to no classified lane candidate (opaque tools stay fail-closed). */\n\tunboundAllowPatterns: string[];\n\t/** Per-call path and capability gate for the isolated child loop. */\n\tbeforeToolCall: NonNullable<AgentLoopConfig[\"beforeToolCall\"]>;\n\t/** Canonical cumulative authority/budget meter for a compiled worker grant. */\n\tgateway?: CapabilityGateway;\n}\n\nexport interface LaneToolSurfaceOptions {\n\tcwd: string;\n\tprofile?: NormalizedProfile;\n\t/** Private harness state that generic file tools must never traverse. */\n\tdeniedPaths?: readonly string[];\n\t/** Orchestrator-requested, policy-filtered read-only memory retrieval. Omitted means no memory tool. */\n\treadMemory?: (query: string) => Promise<string>;\n\t/** Research never sets this. Workers require both this flag and at least one write path. */\n\twriteEnabled?: boolean;\n\twritePaths?: readonly string[];\n\t/** Present only for process-capable owner profiles; absent means no process tool is materialized. */\n\texecutionPolicy?: OrchestrationExecutionPolicy;\n\tprocessMaxWallClockMs?: number;\n\t/** Compiled policy path. When present, it is the only authorization source for this surface. */\n\tgrant?: ExecutionGrant;\n\ttoolManifests?: readonly ToolCapabilityManifest[];\n}\n\nfunction strictLaneProfilePatterns(profile: NormalizedProfile | undefined): {\n\tallow: string[];\n\tblock: string[];\n} {\n\tif (!profile) return { allow: [], block: [] };\n\tconst filter = profile.resources.tools;\n\tconst allow = [...(filter?.allow ?? [])];\n\tconst block = [...(filter?.block ?? [])];\n\t// Strict UAC: a shipped profile is the complete authority grant. Grant-all must be explicit.\n\tif (allow.length === 0 && block.length === 0) return { allow: [], block: [\"*\"] };\n\treturn { allow, block };\n}\n\nfunction resolveWriteRoots(cwd: string, writePaths: readonly string[]): string[] {\n\treturn writePaths.map((entry) => (path.isAbsolute(entry) ? path.resolve(entry) : path.resolve(cwd, entry)));\n}\n\nfunction createLaneTools(\n\tcwd: string,\n\tnames: readonly string[],\n\treadMemory?: (query: string) => Promise<string>,\n\texecutionPolicy?: OrchestrationExecutionPolicy,\n\tprocessMaxWallClockMs = 0,\n): AgentTool[] {\n\tconst factories = new Map<string, () => AgentTool>([\n\t\t[\"read\", () => createReadTool(cwd)],\n\t\t[\"grep\", () => createGrepTool(cwd)],\n\t\t[\"find\", () => createFindTool(cwd)],\n\t\t[\"ls\", () => createLsTool(cwd)],\n\t\t[\"write\", () => createWriteTool(cwd)],\n\t\t[\"edit\", () => createEditTool(cwd)],\n\t]);\n\tif (executionPolicy) {\n\t\tfactories.set(PROCESS_LANE_TOOL_NAME, () =>\n\t\t\tcreateRunProcessTool(cwd, { policy: executionPolicy, maxWallClockMs: processMaxWallClockMs }),\n\t\t);\n\t}\n\tif (readMemory) {\n\t\tfactories.set(MEMORY_LANE_TOOL_NAME, () => ({\n\t\t\tname: MEMORY_LANE_TOOL_NAME,\n\t\t\tlabel: \"Read Memory\",\n\t\t\tdescription:\n\t\t\t\t\"Retrieve bounded, source-labeled standing memory relevant to this delegated task. Read-only: no memory writes or lifecycle actions are available.\",\n\t\t\tparameters: laneMemorySchema,\n\t\t\texecute: async (_toolCallId, params) => {\n\t\t\t\tconst { query } = params as LaneMemoryParams;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: await readMemory(query) }],\n\t\t\t\t\tdetails: { readOnly: true },\n\t\t\t\t};\n\t\t\t},\n\t\t}));\n\t}\n\treturn names.flatMap((name) => {\n\t\tconst factory = factories.get(name);\n\t\treturn factory ? [wrapToolWithCredentialExposureGuard(factory(), cwd)] : [];\n\t});\n}\n\n/**\n * Materialize a fresh, fail-closed tool surface for one isolated lane.\n *\n * Profiles select only from classified built-ins. Opaque extension tools, shell, memory, goals,\n * and `delegate` are never candidates, so a wildcard cannot manufacture unknown authority or\n * recursively spawn more workers. Write/edit additionally require the explicit worker write switch\n * and a non-empty path scope; research therefore stays read-only regardless of profile contents.\n */\nexport function createLaneToolSurface(options: LaneToolSurfaceOptions): LaneToolSurface {\n\tconst writeCapable = options.writeEnabled === true && (options.writePaths?.length ?? 0) > 0;\n\tconst candidateNames = [\n\t\t...READ_ONLY_LANE_TOOL_NAMES,\n\t\t...(options.readMemory ? [MEMORY_LANE_TOOL_NAME] : []),\n\t\t...(writeCapable ? WRITE_LANE_TOOL_NAMES : []),\n\t\t...(options.executionPolicy ? [PROCESS_LANE_TOOL_NAME] : []),\n\t];\n\tconst patterns = strictLaneProfilePatterns(options.profile);\n\tconst compiledToolNames = new Set(options.grant?.allowedTools ?? []);\n\tconst deniedTools = candidateNames.filter((name) =>\n\t\toptions.grant ? !compiledToolNames.has(name) : matchesResourceProfilePattern(name, patterns.block),\n\t);\n\tconst unboundAllowPatterns = options.grant\n\t\t? []\n\t\t: patterns.allow.filter(\n\t\t\t\t(pattern) => !candidateNames.some((name) => matchesResourceProfilePattern(name, [pattern])),\n\t\t\t);\n\tconst allowedTools = options.grant\n\t\t? candidateNames.filter((name) => compiledToolNames.has(name))\n\t\t: candidateNames.filter(\n\t\t\t\t(name) =>\n\t\t\t\t\t(patterns.allow.length === 0 || matchesResourceProfilePattern(name, patterns.allow)) &&\n\t\t\t\t\t!matchesResourceProfilePattern(name, patterns.block),\n\t\t\t);\n\tconst allowedToolSet = new Set<string>(allowedTools);\n\tconst manifestsByName = new Map(options.toolManifests?.map((manifest) => [manifest.toolName, manifest]) ?? []);\n\tif (options.grant?.allowedTools.some((name) => !manifestsByName.has(name))) {\n\t\tthrow new Error(\"Compiled lane grant references a tool without a capability manifest.\");\n\t}\n\tconst gateway = options.grant ? new CapabilityGateway({ grant: options.grant, cwd: options.cwd }) : undefined;\n\tconst deniedPaths = options.deniedPaths?.map((entry) => path.resolve(entry));\n\tconst readEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-read-tools\",\n\t\tcapabilities: [\n\t\t\t\"filesystem.read\",\n\t\t\t...(allowedToolSet.has(MEMORY_LANE_TOOL_NAME) ? ([\"memory.query\"] as const) : []),\n\t\t\t...(allowedToolSet.has(PROCESS_LANE_TOOL_NAME) ? ([\"process.exec\"] as const) : []),\n\t\t],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: [path.resolve(options.cwd)],\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\tconst writeEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-write-tools\",\n\t\tcapabilities: [\"filesystem.write\"],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: resolveWriteRoots(options.cwd, options.writePaths ?? []),\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\n\treturn {\n\t\ttools: createLaneTools(\n\t\t\toptions.cwd,\n\t\t\tallowedTools,\n\t\t\toptions.readMemory,\n\t\t\toptions.executionPolicy,\n\t\t\toptions.processMaxWallClockMs,\n\t\t),\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tunboundAllowPatterns,\n\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\tif (!allowedToolSet.has(toolCall.name)) {\n\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' is outside the materialized UAC surface.` };\n\t\t\t}\n\t\t\tif (gateway) {\n\t\t\t\tconst manifest = manifestsByName.get(toolCall.name);\n\t\t\t\tif (!manifest) {\n\t\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' has no compiled capability manifest.` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tgateway.authorizeToolCall(manifest, toolCall.name, args);\n\t\t\t\t\treturn undefined;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof CapabilityGatewayDeniedError) {\n\t\t\t\t\t\treturn { block: true, reason: `Lane tool blocked (${error.reasonCode}): ${error.message}` };\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst outcome = evaluateToolGate({\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\targs,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenvelope: WRITE_LANE_TOOL_NAME_SET.has(toolCall.name) ? writeEnvelope : readEnvelope,\n\t\t\t});\n\t\t\tif (outcome.outcome === \"allow\") return undefined;\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: `Lane tool blocked (${outcome.reasonCode}): ${outcome.message ?? \"capability gate denied it\"}`,\n\t\t\t};\n\t\t},\n\t\t...(gateway ? { gateway } : {}),\n\t};\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { Type } from "typebox";
3
3
  import { CapabilityGateway, CapabilityGatewayDeniedError } from "../orchestration/capability-gateway.js";
4
+ import { wrapToolWithCredentialExposureGuard } from "../secrets/credential-exposure-guard.js";
4
5
  import { matchesResourceProfilePattern } from "../settings-manager.js";
5
6
  import { createEditTool } from "../tools/edit.js";
6
7
  import { createFindTool } from "../tools/find.js";
@@ -61,7 +62,7 @@ function createLaneTools(cwd, names, readMemory, executionPolicy, processMaxWall
61
62
  }
62
63
  return names.flatMap((name) => {
63
64
  const factory = factories.get(name);
64
- return factory ? [factory()] : [];
65
+ return factory ? [wrapToolWithCredentialExposureGuard(factory(), cwd)] : [];
65
66
  });
66
67
  }
67
68
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"lane-tool-surface.js","sourceRoot":"","sources":["../../../src/core/autonomy/lane-tool-surface.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport type { AgentLoopConfig, AgentTool } from \"@caupulican/pi-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport { CapabilityGateway, CapabilityGatewayDeniedError } from \"../orchestration/capability-gateway.ts\";\nimport type {\n\tExecutionGrant,\n\tOrchestrationExecutionPolicy,\n\tToolCapabilityManifest,\n} from \"../orchestration/contracts.ts\";\nimport type { NormalizedProfile } from \"../profile-registry.ts\";\nimport { matchesResourceProfilePattern } from \"../settings-manager.ts\";\nimport { createEditTool } from \"../tools/edit.ts\";\nimport { createFindTool } from \"../tools/find.ts\";\nimport { createGrepTool } from \"../tools/grep.ts\";\nimport { createLsTool } from \"../tools/ls.ts\";\nimport { createReadTool } from \"../tools/read.ts\";\nimport { createRunProcessTool } from \"../tools/run-process.ts\";\nimport { createWriteTool } from \"../tools/write.ts\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { evaluateToolGate } from \"./gates.ts\";\n\nconst READ_ONLY_LANE_TOOL_NAMES = [\"read\", \"grep\", \"find\", \"ls\"] as const;\nconst MEMORY_LANE_TOOL_NAME = \"memory\" as const;\nconst WRITE_LANE_TOOL_NAMES = [\"write\", \"edit\"] as const;\nconst PROCESS_LANE_TOOL_NAME = \"run_process\" as const;\nconst laneMemorySchema = Type.Object({\n\tquery: Type.String({ description: \"What relevant standing memory or prior evidence to retrieve\" }),\n});\ntype LaneMemoryParams = Static<typeof laneMemorySchema>;\nconst WRITE_LANE_TOOL_NAME_SET = new Set<string>(WRITE_LANE_TOOL_NAMES);\n\nexport interface LaneToolSurface {\n\t/** Fresh tools owned by this lane only; no foreground tool instances or extension state leak in. */\n\ttools: AgentTool[];\n\t/** Exact names after profile globs are expanded. Safe to persist in a capability envelope. */\n\tallowedTools: string[];\n\t/** Exact safe-candidate names denied by the profile's block patterns. */\n\tdeniedTools: string[];\n\t/** Explicit grants that bind to no classified lane candidate (opaque tools stay fail-closed). */\n\tunboundAllowPatterns: string[];\n\t/** Per-call path and capability gate for the isolated child loop. */\n\tbeforeToolCall: NonNullable<AgentLoopConfig[\"beforeToolCall\"]>;\n\t/** Canonical cumulative authority/budget meter for a compiled worker grant. */\n\tgateway?: CapabilityGateway;\n}\n\nexport interface LaneToolSurfaceOptions {\n\tcwd: string;\n\tprofile?: NormalizedProfile;\n\t/** Private harness state that generic file tools must never traverse. */\n\tdeniedPaths?: readonly string[];\n\t/** Orchestrator-requested, policy-filtered read-only memory retrieval. Omitted means no memory tool. */\n\treadMemory?: (query: string) => Promise<string>;\n\t/** Research never sets this. Workers require both this flag and at least one write path. */\n\twriteEnabled?: boolean;\n\twritePaths?: readonly string[];\n\t/** Present only for process-capable owner profiles; absent means no process tool is materialized. */\n\texecutionPolicy?: OrchestrationExecutionPolicy;\n\tprocessMaxWallClockMs?: number;\n\t/** Compiled policy path. When present, it is the only authorization source for this surface. */\n\tgrant?: ExecutionGrant;\n\ttoolManifests?: readonly ToolCapabilityManifest[];\n}\n\nfunction strictLaneProfilePatterns(profile: NormalizedProfile | undefined): {\n\tallow: string[];\n\tblock: string[];\n} {\n\tif (!profile) return { allow: [], block: [] };\n\tconst filter = profile.resources.tools;\n\tconst allow = [...(filter?.allow ?? [])];\n\tconst block = [...(filter?.block ?? [])];\n\t// Strict UAC: a shipped profile is the complete authority grant. Grant-all must be explicit.\n\tif (allow.length === 0 && block.length === 0) return { allow: [], block: [\"*\"] };\n\treturn { allow, block };\n}\n\nfunction resolveWriteRoots(cwd: string, writePaths: readonly string[]): string[] {\n\treturn writePaths.map((entry) => (path.isAbsolute(entry) ? path.resolve(entry) : path.resolve(cwd, entry)));\n}\n\nfunction createLaneTools(\n\tcwd: string,\n\tnames: readonly string[],\n\treadMemory?: (query: string) => Promise<string>,\n\texecutionPolicy?: OrchestrationExecutionPolicy,\n\tprocessMaxWallClockMs = 0,\n): AgentTool[] {\n\tconst factories = new Map<string, () => AgentTool>([\n\t\t[\"read\", () => createReadTool(cwd)],\n\t\t[\"grep\", () => createGrepTool(cwd)],\n\t\t[\"find\", () => createFindTool(cwd)],\n\t\t[\"ls\", () => createLsTool(cwd)],\n\t\t[\"write\", () => createWriteTool(cwd)],\n\t\t[\"edit\", () => createEditTool(cwd)],\n\t]);\n\tif (executionPolicy) {\n\t\tfactories.set(PROCESS_LANE_TOOL_NAME, () =>\n\t\t\tcreateRunProcessTool(cwd, { policy: executionPolicy, maxWallClockMs: processMaxWallClockMs }),\n\t\t);\n\t}\n\tif (readMemory) {\n\t\tfactories.set(MEMORY_LANE_TOOL_NAME, () => ({\n\t\t\tname: MEMORY_LANE_TOOL_NAME,\n\t\t\tlabel: \"Read Memory\",\n\t\t\tdescription:\n\t\t\t\t\"Retrieve bounded, source-labeled standing memory relevant to this delegated task. Read-only: no memory writes or lifecycle actions are available.\",\n\t\t\tparameters: laneMemorySchema,\n\t\t\texecute: async (_toolCallId, params) => {\n\t\t\t\tconst { query } = params as LaneMemoryParams;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: await readMemory(query) }],\n\t\t\t\t\tdetails: { readOnly: true },\n\t\t\t\t};\n\t\t\t},\n\t\t}));\n\t}\n\treturn names.flatMap((name) => {\n\t\tconst factory = factories.get(name);\n\t\treturn factory ? [factory()] : [];\n\t});\n}\n\n/**\n * Materialize a fresh, fail-closed tool surface for one isolated lane.\n *\n * Profiles select only from classified built-ins. Opaque extension tools, shell, memory, goals,\n * and `delegate` are never candidates, so a wildcard cannot manufacture unknown authority or\n * recursively spawn more workers. Write/edit additionally require the explicit worker write switch\n * and a non-empty path scope; research therefore stays read-only regardless of profile contents.\n */\nexport function createLaneToolSurface(options: LaneToolSurfaceOptions): LaneToolSurface {\n\tconst writeCapable = options.writeEnabled === true && (options.writePaths?.length ?? 0) > 0;\n\tconst candidateNames = [\n\t\t...READ_ONLY_LANE_TOOL_NAMES,\n\t\t...(options.readMemory ? [MEMORY_LANE_TOOL_NAME] : []),\n\t\t...(writeCapable ? WRITE_LANE_TOOL_NAMES : []),\n\t\t...(options.executionPolicy ? [PROCESS_LANE_TOOL_NAME] : []),\n\t];\n\tconst patterns = strictLaneProfilePatterns(options.profile);\n\tconst compiledToolNames = new Set(options.grant?.allowedTools ?? []);\n\tconst deniedTools = candidateNames.filter((name) =>\n\t\toptions.grant ? !compiledToolNames.has(name) : matchesResourceProfilePattern(name, patterns.block),\n\t);\n\tconst unboundAllowPatterns = options.grant\n\t\t? []\n\t\t: patterns.allow.filter(\n\t\t\t\t(pattern) => !candidateNames.some((name) => matchesResourceProfilePattern(name, [pattern])),\n\t\t\t);\n\tconst allowedTools = options.grant\n\t\t? candidateNames.filter((name) => compiledToolNames.has(name))\n\t\t: candidateNames.filter(\n\t\t\t\t(name) =>\n\t\t\t\t\t(patterns.allow.length === 0 || matchesResourceProfilePattern(name, patterns.allow)) &&\n\t\t\t\t\t!matchesResourceProfilePattern(name, patterns.block),\n\t\t\t);\n\tconst allowedToolSet = new Set<string>(allowedTools);\n\tconst manifestsByName = new Map(options.toolManifests?.map((manifest) => [manifest.toolName, manifest]) ?? []);\n\tif (options.grant?.allowedTools.some((name) => !manifestsByName.has(name))) {\n\t\tthrow new Error(\"Compiled lane grant references a tool without a capability manifest.\");\n\t}\n\tconst gateway = options.grant ? new CapabilityGateway({ grant: options.grant, cwd: options.cwd }) : undefined;\n\tconst deniedPaths = options.deniedPaths?.map((entry) => path.resolve(entry));\n\tconst readEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-read-tools\",\n\t\tcapabilities: [\n\t\t\t\"filesystem.read\",\n\t\t\t...(allowedToolSet.has(MEMORY_LANE_TOOL_NAME) ? ([\"memory.query\"] as const) : []),\n\t\t\t...(allowedToolSet.has(PROCESS_LANE_TOOL_NAME) ? ([\"process.exec\"] as const) : []),\n\t\t],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: [path.resolve(options.cwd)],\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\tconst writeEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-write-tools\",\n\t\tcapabilities: [\"filesystem.write\"],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: resolveWriteRoots(options.cwd, options.writePaths ?? []),\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\n\treturn {\n\t\ttools: createLaneTools(\n\t\t\toptions.cwd,\n\t\t\tallowedTools,\n\t\t\toptions.readMemory,\n\t\t\toptions.executionPolicy,\n\t\t\toptions.processMaxWallClockMs,\n\t\t),\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tunboundAllowPatterns,\n\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\tif (!allowedToolSet.has(toolCall.name)) {\n\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' is outside the materialized UAC surface.` };\n\t\t\t}\n\t\t\tif (gateway) {\n\t\t\t\tconst manifest = manifestsByName.get(toolCall.name);\n\t\t\t\tif (!manifest) {\n\t\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' has no compiled capability manifest.` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tgateway.authorizeToolCall(manifest, toolCall.name, args);\n\t\t\t\t\treturn undefined;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof CapabilityGatewayDeniedError) {\n\t\t\t\t\t\treturn { block: true, reason: `Lane tool blocked (${error.reasonCode}): ${error.message}` };\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst outcome = evaluateToolGate({\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\targs,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenvelope: WRITE_LANE_TOOL_NAME_SET.has(toolCall.name) ? writeEnvelope : readEnvelope,\n\t\t\t});\n\t\t\tif (outcome.outcome === \"allow\") return undefined;\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: `Lane tool blocked (${outcome.reasonCode}): ${outcome.message ?? \"capability gate denied it\"}`,\n\t\t\t};\n\t\t},\n\t\t...(gateway ? { gateway } : {}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"lane-tool-surface.js","sourceRoot":"","sources":["../../../src/core/autonomy/lane-tool-surface.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AAOzG,OAAO,EAAE,mCAAmC,EAAE,MAAM,yCAAyC,CAAC;AAC9F,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAC1E,MAAM,qBAAqB,GAAG,QAAiB,CAAC;AAChD,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,MAAM,CAAU,CAAC;AACzD,MAAM,sBAAsB,GAAG,aAAsB,CAAC;AACtD,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC;IACpC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC;CAClG,CAAC,CAAC;AAEH,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAS,qBAAqB,CAAC,CAAC;AAmCxE,SAAS,yBAAyB,CAAC,OAAsC,EAGvE;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC;IACvC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,6FAA6F;IAC7F,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IACjF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAAA,CACxB;AAED,SAAS,iBAAiB,CAAC,GAAW,EAAE,UAA6B,EAAY;IAChF,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAAA,CAC5G;AAED,SAAS,eAAe,CACvB,GAAW,EACX,KAAwB,EACxB,UAA+C,EAC/C,eAA8C,EAC9C,qBAAqB,GAAG,CAAC,EACX;IACd,MAAM,SAAS,GAAG,IAAI,GAAG,CAA0B;QAClD,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;KACnC,CAAC,CAAC;IACH,IAAI,eAAe,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAC1C,oBAAoB,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,CAAC,CAC7F,CAAC;IACH,CAAC;IACD,IAAI,UAAU,EAAE,CAAC;QAChB,SAAS,CAAC,GAAG,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3C,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,aAAa;YACpB,WAAW,EACV,mJAAmJ;YACpJ,UAAU,EAAE,gBAAgB;YAC5B,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;gBACvC,MAAM,EAAE,KAAK,EAAE,GAAG,MAA0B,CAAC;gBAC7C,OAAO;oBACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;oBACnE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;iBAC3B,CAAC;YAAA,CACF;SACD,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,mCAAmC,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAAA,CAC5E,CAAC,CAAC;AAAA,CACH;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA+B,EAAmB;IACvF,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5F,MAAM,cAAc,GAAG;QACtB,GAAG,yBAAyB;QAC5B,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC;IACF,MAAM,QAAQ,GAAG,yBAAyB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAClD,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,6BAA6B,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAClG,CAAC;IACF,MAAM,oBAAoB,GAAG,OAAO,CAAC,KAAK;QACzC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CACrB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,6BAA6B,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAC3F,CAAC;IACJ,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK;QACjC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC,CAAC,cAAc,CAAC,MAAM,CACrB,CAAC,IAAI,EAAE,EAAE,CACR,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,6BAA6B,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;YACpF,CAAC,6BAA6B,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CACrD,CAAC;IACJ,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,YAAY,CAAC,CAAC;IACrD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/G,IAAI,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9G,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,MAAM,YAAY,GAAuB;QACxC,EAAE,EAAE,0BAA0B;QAC9B,YAAY,EAAE;YACb,iBAAiB;YACjB,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAE,CAAC,cAAc,CAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACjF,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAE,CAAC,cAAc,CAAW,CAAC,CAAC,CAAC,EAAE,CAAC;SAClF;QACD,YAAY;QACZ,WAAW;QACX,YAAY,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACzC,GAAG,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC;IACF,MAAM,aAAa,GAAuB;QACzC,EAAE,EAAE,2BAA2B;QAC/B,YAAY,EAAE,CAAC,kBAAkB,CAAC;QAClC,YAAY;QACZ,WAAW;QACX,YAAY,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QACtE,GAAG,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC;IAEF,OAAO;QACN,KAAK,EAAE,eAAe,CACrB,OAAO,CAAC,GAAG,EACX,YAAY,EACZ,OAAO,CAAC,UAAU,EAClB,OAAO,CAAC,eAAe,EACvB,OAAO,CAAC,qBAAqB,CAC7B;QACD,YAAY;QACZ,WAAW;QACX,oBAAoB;QACpB,cAAc,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,QAAQ,CAAC,IAAI,4CAA4C,EAAE,CAAC;YACzG,CAAC;YACD,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACf,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,QAAQ,CAAC,IAAI,wCAAwC,EAAE,CAAC;gBACrG,CAAC;gBACD,IAAI,CAAC;oBACJ,OAAO,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBACzD,OAAO,SAAS,CAAC;gBAClB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,IAAI,KAAK,YAAY,4BAA4B,EAAE,CAAC;wBACnD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,sBAAsB,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC7F,CAAC;oBACD,MAAM,KAAK,CAAC;gBACb,CAAC;YACF,CAAC;YACD,MAAM,OAAO,GAAG,gBAAgB,CAAC;gBAChC,QAAQ,EAAE,QAAQ,CAAC,IAAI;gBACvB,IAAI;gBACJ,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,QAAQ,EAAE,wBAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY;aACpF,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO;gBAAE,OAAO,SAAS,CAAC;YAClD,OAAO;gBACN,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,sBAAsB,OAAO,CAAC,UAAU,MAAM,OAAO,CAAC,OAAO,IAAI,2BAA2B,EAAE;aACtG,CAAC;QAAA,CACF;QACD,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/B,CAAC;AAAA,CACF","sourcesContent":["import path from \"node:path\";\nimport type { AgentLoopConfig, AgentTool } from \"@caupulican/pi-agent-core\";\nimport { type Static, Type } from \"typebox\";\nimport { CapabilityGateway, CapabilityGatewayDeniedError } from \"../orchestration/capability-gateway.ts\";\nimport type {\n\tExecutionGrant,\n\tOrchestrationExecutionPolicy,\n\tToolCapabilityManifest,\n} from \"../orchestration/contracts.ts\";\nimport type { NormalizedProfile } from \"../profile-registry.ts\";\nimport { wrapToolWithCredentialExposureGuard } from \"../secrets/credential-exposure-guard.ts\";\nimport { matchesResourceProfilePattern } from \"../settings-manager.ts\";\nimport { createEditTool } from \"../tools/edit.ts\";\nimport { createFindTool } from \"../tools/find.ts\";\nimport { createGrepTool } from \"../tools/grep.ts\";\nimport { createLsTool } from \"../tools/ls.ts\";\nimport { createReadTool } from \"../tools/read.ts\";\nimport { createRunProcessTool } from \"../tools/run-process.ts\";\nimport { createWriteTool } from \"../tools/write.ts\";\nimport type { CapabilityEnvelope } from \"./contracts.ts\";\nimport { evaluateToolGate } from \"./gates.ts\";\n\nconst READ_ONLY_LANE_TOOL_NAMES = [\"read\", \"grep\", \"find\", \"ls\"] as const;\nconst MEMORY_LANE_TOOL_NAME = \"memory\" as const;\nconst WRITE_LANE_TOOL_NAMES = [\"write\", \"edit\"] as const;\nconst PROCESS_LANE_TOOL_NAME = \"run_process\" as const;\nconst laneMemorySchema = Type.Object({\n\tquery: Type.String({ description: \"What relevant standing memory or prior evidence to retrieve\" }),\n});\ntype LaneMemoryParams = Static<typeof laneMemorySchema>;\nconst WRITE_LANE_TOOL_NAME_SET = new Set<string>(WRITE_LANE_TOOL_NAMES);\n\nexport interface LaneToolSurface {\n\t/** Fresh tools owned by this lane only; no foreground tool instances or extension state leak in. */\n\ttools: AgentTool[];\n\t/** Exact names after profile globs are expanded. Safe to persist in a capability envelope. */\n\tallowedTools: string[];\n\t/** Exact safe-candidate names denied by the profile's block patterns. */\n\tdeniedTools: string[];\n\t/** Explicit grants that bind to no classified lane candidate (opaque tools stay fail-closed). */\n\tunboundAllowPatterns: string[];\n\t/** Per-call path and capability gate for the isolated child loop. */\n\tbeforeToolCall: NonNullable<AgentLoopConfig[\"beforeToolCall\"]>;\n\t/** Canonical cumulative authority/budget meter for a compiled worker grant. */\n\tgateway?: CapabilityGateway;\n}\n\nexport interface LaneToolSurfaceOptions {\n\tcwd: string;\n\tprofile?: NormalizedProfile;\n\t/** Private harness state that generic file tools must never traverse. */\n\tdeniedPaths?: readonly string[];\n\t/** Orchestrator-requested, policy-filtered read-only memory retrieval. Omitted means no memory tool. */\n\treadMemory?: (query: string) => Promise<string>;\n\t/** Research never sets this. Workers require both this flag and at least one write path. */\n\twriteEnabled?: boolean;\n\twritePaths?: readonly string[];\n\t/** Present only for process-capable owner profiles; absent means no process tool is materialized. */\n\texecutionPolicy?: OrchestrationExecutionPolicy;\n\tprocessMaxWallClockMs?: number;\n\t/** Compiled policy path. When present, it is the only authorization source for this surface. */\n\tgrant?: ExecutionGrant;\n\ttoolManifests?: readonly ToolCapabilityManifest[];\n}\n\nfunction strictLaneProfilePatterns(profile: NormalizedProfile | undefined): {\n\tallow: string[];\n\tblock: string[];\n} {\n\tif (!profile) return { allow: [], block: [] };\n\tconst filter = profile.resources.tools;\n\tconst allow = [...(filter?.allow ?? [])];\n\tconst block = [...(filter?.block ?? [])];\n\t// Strict UAC: a shipped profile is the complete authority grant. Grant-all must be explicit.\n\tif (allow.length === 0 && block.length === 0) return { allow: [], block: [\"*\"] };\n\treturn { allow, block };\n}\n\nfunction resolveWriteRoots(cwd: string, writePaths: readonly string[]): string[] {\n\treturn writePaths.map((entry) => (path.isAbsolute(entry) ? path.resolve(entry) : path.resolve(cwd, entry)));\n}\n\nfunction createLaneTools(\n\tcwd: string,\n\tnames: readonly string[],\n\treadMemory?: (query: string) => Promise<string>,\n\texecutionPolicy?: OrchestrationExecutionPolicy,\n\tprocessMaxWallClockMs = 0,\n): AgentTool[] {\n\tconst factories = new Map<string, () => AgentTool>([\n\t\t[\"read\", () => createReadTool(cwd)],\n\t\t[\"grep\", () => createGrepTool(cwd)],\n\t\t[\"find\", () => createFindTool(cwd)],\n\t\t[\"ls\", () => createLsTool(cwd)],\n\t\t[\"write\", () => createWriteTool(cwd)],\n\t\t[\"edit\", () => createEditTool(cwd)],\n\t]);\n\tif (executionPolicy) {\n\t\tfactories.set(PROCESS_LANE_TOOL_NAME, () =>\n\t\t\tcreateRunProcessTool(cwd, { policy: executionPolicy, maxWallClockMs: processMaxWallClockMs }),\n\t\t);\n\t}\n\tif (readMemory) {\n\t\tfactories.set(MEMORY_LANE_TOOL_NAME, () => ({\n\t\t\tname: MEMORY_LANE_TOOL_NAME,\n\t\t\tlabel: \"Read Memory\",\n\t\t\tdescription:\n\t\t\t\t\"Retrieve bounded, source-labeled standing memory relevant to this delegated task. Read-only: no memory writes or lifecycle actions are available.\",\n\t\t\tparameters: laneMemorySchema,\n\t\t\texecute: async (_toolCallId, params) => {\n\t\t\t\tconst { query } = params as LaneMemoryParams;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\" as const, text: await readMemory(query) }],\n\t\t\t\t\tdetails: { readOnly: true },\n\t\t\t\t};\n\t\t\t},\n\t\t}));\n\t}\n\treturn names.flatMap((name) => {\n\t\tconst factory = factories.get(name);\n\t\treturn factory ? [wrapToolWithCredentialExposureGuard(factory(), cwd)] : [];\n\t});\n}\n\n/**\n * Materialize a fresh, fail-closed tool surface for one isolated lane.\n *\n * Profiles select only from classified built-ins. Opaque extension tools, shell, memory, goals,\n * and `delegate` are never candidates, so a wildcard cannot manufacture unknown authority or\n * recursively spawn more workers. Write/edit additionally require the explicit worker write switch\n * and a non-empty path scope; research therefore stays read-only regardless of profile contents.\n */\nexport function createLaneToolSurface(options: LaneToolSurfaceOptions): LaneToolSurface {\n\tconst writeCapable = options.writeEnabled === true && (options.writePaths?.length ?? 0) > 0;\n\tconst candidateNames = [\n\t\t...READ_ONLY_LANE_TOOL_NAMES,\n\t\t...(options.readMemory ? [MEMORY_LANE_TOOL_NAME] : []),\n\t\t...(writeCapable ? WRITE_LANE_TOOL_NAMES : []),\n\t\t...(options.executionPolicy ? [PROCESS_LANE_TOOL_NAME] : []),\n\t];\n\tconst patterns = strictLaneProfilePatterns(options.profile);\n\tconst compiledToolNames = new Set(options.grant?.allowedTools ?? []);\n\tconst deniedTools = candidateNames.filter((name) =>\n\t\toptions.grant ? !compiledToolNames.has(name) : matchesResourceProfilePattern(name, patterns.block),\n\t);\n\tconst unboundAllowPatterns = options.grant\n\t\t? []\n\t\t: patterns.allow.filter(\n\t\t\t\t(pattern) => !candidateNames.some((name) => matchesResourceProfilePattern(name, [pattern])),\n\t\t\t);\n\tconst allowedTools = options.grant\n\t\t? candidateNames.filter((name) => compiledToolNames.has(name))\n\t\t: candidateNames.filter(\n\t\t\t\t(name) =>\n\t\t\t\t\t(patterns.allow.length === 0 || matchesResourceProfilePattern(name, patterns.allow)) &&\n\t\t\t\t\t!matchesResourceProfilePattern(name, patterns.block),\n\t\t\t);\n\tconst allowedToolSet = new Set<string>(allowedTools);\n\tconst manifestsByName = new Map(options.toolManifests?.map((manifest) => [manifest.toolName, manifest]) ?? []);\n\tif (options.grant?.allowedTools.some((name) => !manifestsByName.has(name))) {\n\t\tthrow new Error(\"Compiled lane grant references a tool without a capability manifest.\");\n\t}\n\tconst gateway = options.grant ? new CapabilityGateway({ grant: options.grant, cwd: options.cwd }) : undefined;\n\tconst deniedPaths = options.deniedPaths?.map((entry) => path.resolve(entry));\n\tconst readEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-read-tools\",\n\t\tcapabilities: [\n\t\t\t\"filesystem.read\",\n\t\t\t...(allowedToolSet.has(MEMORY_LANE_TOOL_NAME) ? ([\"memory.query\"] as const) : []),\n\t\t\t...(allowedToolSet.has(PROCESS_LANE_TOOL_NAME) ? ([\"process.exec\"] as const) : []),\n\t\t],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: [path.resolve(options.cwd)],\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\tconst writeEnvelope: CapabilityEnvelope = {\n\t\tid: \"isolated-lane-write-tools\",\n\t\tcapabilities: [\"filesystem.write\"],\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tallowedPaths: resolveWriteRoots(options.cwd, options.writePaths ?? []),\n\t\t...(deniedPaths && deniedPaths.length > 0 ? { deniedPaths } : {}),\n\t};\n\n\treturn {\n\t\ttools: createLaneTools(\n\t\t\toptions.cwd,\n\t\t\tallowedTools,\n\t\t\toptions.readMemory,\n\t\t\toptions.executionPolicy,\n\t\t\toptions.processMaxWallClockMs,\n\t\t),\n\t\tallowedTools,\n\t\tdeniedTools,\n\t\tunboundAllowPatterns,\n\t\tbeforeToolCall: async ({ toolCall, args }) => {\n\t\t\tif (!allowedToolSet.has(toolCall.name)) {\n\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' is outside the materialized UAC surface.` };\n\t\t\t}\n\t\t\tif (gateway) {\n\t\t\t\tconst manifest = manifestsByName.get(toolCall.name);\n\t\t\t\tif (!manifest) {\n\t\t\t\t\treturn { block: true, reason: `Lane tool '${toolCall.name}' has no compiled capability manifest.` };\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tgateway.authorizeToolCall(manifest, toolCall.name, args);\n\t\t\t\t\treturn undefined;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof CapabilityGatewayDeniedError) {\n\t\t\t\t\t\treturn { block: true, reason: `Lane tool blocked (${error.reasonCode}): ${error.message}` };\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst outcome = evaluateToolGate({\n\t\t\t\ttoolName: toolCall.name,\n\t\t\t\targs,\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tenvelope: WRITE_LANE_TOOL_NAME_SET.has(toolCall.name) ? writeEnvelope : readEnvelope,\n\t\t\t});\n\t\t\tif (outcome.outcome === \"allow\") return undefined;\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason: `Lane tool blocked (${outcome.reasonCode}): ${outcome.message ?? \"capability gate denied it\"}`,\n\t\t\t};\n\t\t},\n\t\t...(gateway ? { gateway } : {}),\n\t};\n}\n"]}
@@ -14,7 +14,7 @@
14
14
  * controller never touches `prompt()`, the last-assistant-message, retry, or streaming state.
15
15
  */
16
16
  import type { Api, Model } from "@caupulican/pi-ai";
17
- import type { GoalContinuationLoopOptions, GoalContinuationLoopResult, PromptOptions, ResearchLaneRunOutcome, WorkerDelegationRunOutcome } from "./agent-session.ts";
17
+ import type { GoalContinuationLoopOptions, GoalContinuationLoopResult, PromptOptions, ResearchLaneRunOutcome, WorkerDelegationRunOutcome } from "./agent-session-contracts.ts";
18
18
  import { type LaneRecord } from "./autonomy/lane-tracker.ts";
19
19
  import { type WorkerDelegationControllerDeps } from "./delegation/worker-delegation-controller.ts";
20
20
  import type { WorkerDelegationRequest } from "./delegation/worker-delegation-request.ts";
@@ -42,6 +42,8 @@ export interface BackgroundLaneControllerDeps extends WorkerDelegationController
42
42
  getGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;
43
43
  /** Drive-loop boundary: the session's bounded goal-continuation loop (owns `prompt()`, not us). */
44
44
  continueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;
45
+ /** Persist an explicit stopped state when the selected surface cannot drive the active goal. */
46
+ markGoalToolUnavailable(): void;
45
47
  }
46
48
  export declare class BackgroundLaneController {
47
49
  /** Live lane registry — the real source for AutonomyStatusSnapshot.activeLaneCount. */
@@ -1 +1 @@
1
- {"version":3,"file":"background-lane-controller.d.ts","sourceRoot":"","sources":["../../src/core/background-lane-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Execution-plane coordinator for goal continuation, research, managed lanes, worker delegation,\n * and model fitness.\n *\n * Coordination state is owned by focused controllers. This coordinator retains one AgentSession\n * composition seam and a shared lane read model. Everything it needs\n * — the session manager, settings, model registry, live model, capability envelope, the goal\n * continuation LOOP, the isolated-completion primitive, spawned-usage accounting, and the telemetry\n * sink — is reached through narrow deps accessors rather than the whole AgentSession.\n *\n * Drive-loop boundary (deliberate): the idle triggers ({@link scheduleGoalAutoContinueFromIdle},\n * {@link scheduleResearchLaneFromIdle}) are invoked from the session's prompt tail as one-line\n * delegations; goal auto-continue itself only ever asks the session to `continueGoalLoop`, so this\n * controller never touches `prompt()`, the last-assistant-message, retry, or streaming state.\n */\n\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type {\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n\tResearchLaneRunOutcome,\n\tWorkerDelegationRunOutcome,\n} from \"./agent-session.ts\";\nimport { type LaneRecord, LaneTracker } from \"./autonomy/lane-tracker.ts\";\nimport { appendLaneRecordSnapshot, getLatestLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport { ManagedLaneController } from \"./delegation/managed-lane-controller.ts\";\nimport {\n\tWorkerDelegationController,\n\ttype WorkerDelegationControllerDeps,\n} from \"./delegation/worker-delegation-controller.ts\";\nimport type { WorkerDelegationRequest } from \"./delegation/worker-delegation-request.ts\";\nimport { WorkerLifecycle } from \"./delegation/worker-lifecycle.ts\";\nimport { WorkerNotificationCoordinator } from \"./delegation/worker-notification-coordinator.ts\";\nimport type { ManagedLaneEvent } from \"./extensions/types.ts\";\nimport { GoalAutoContinueController } from \"./goals/goal-auto-continue-controller.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goals/goal-runtime-snapshot.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { StoredFitnessReport } from \"./models/fitness-store.ts\";\nimport type { TaskRuntimeProjection } from \"./orchestration/task-runtime.ts\";\nimport { LaneModelResolver, type LaneModelResolverDeps } from \"./research/lane-model-resolver.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport { ModelFitnessController, type ModelFitnessControllerDeps } from \"./research/model-fitness-controller.ts\";\nimport { ResearchLaneController, type ResearchLaneControllerDeps } from \"./research/research-lane-controller.ts\";\nimport { getActiveSessionBranchEntries } from \"./session-snapshot.ts\";\n\nexport { isLocalExecutionModel } from \"./delegation/worker-delegation-controller.ts\";\nexport { clampLaneMaxUsd } from \"./research/lane-model-resolver.ts\";\n\nexport interface BackgroundLaneControllerDeps\n\textends WorkerDelegationControllerDeps,\n\t\tResearchLaneControllerDeps,\n\t\tModelFitnessControllerDeps,\n\t\tLaneModelResolverDeps {\n\t/** True iff the `goal` tool is in the session's ACTIVE surface -- the capability-adaptive gate\n\t * for every goal-continuation loop (see `continueGoalLoopExclusive`): a surface without the\n\t * goal tool (lean capability blocklist, worker role ceiling, --tools/profile exclusion) must\n\t * never be driven with continuation prompts it cannot execute. */\n\tisGoalToolActive(): boolean;\n\t/** Capability profile of the SESSION model (gates background lanes, scales continuation budgets). */\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Continuation gate + goal state for the idle autosteer scheduler. */\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\t/** Drive-loop boundary: the session's bounded goal-continuation loop (owns `prompt()`, not us). */\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n}\n\nexport class BackgroundLaneController {\n\t/** Live lane registry — the real source for AutonomyStatusSnapshot.activeLaneCount. */\n\tprivate readonly _laneTracker = new LaneTracker();\n\tprivate readonly _laneModels: LaneModelResolver;\n\tprivate readonly _goalAutoContinue: GoalAutoContinueController;\n\tprivate readonly _research: ResearchLaneController;\n\tprivate readonly _fitness: ModelFitnessController;\n\t/** Lazily materialized only when managed-lane state is queried or reported. */\n\tprivate _managedLanes: ManagedLaneController | undefined;\n\t/** Lazily materialized so a UAC surface without `delegate` allocates no worker runtime state. */\n\tprivate _workers: WorkerDelegationController | undefined;\n\t/** One durable lifecycle shared by every worker execution adapter. */\n\tprivate _workerLifecycle: WorkerLifecycle | undefined;\n\t/** Shared terminal outbox for managed and in-process workers; lazy under UAC omission. */\n\tprivate _workerNotifications: WorkerNotificationCoordinator | undefined;\n\tprivate readonly deps: BackgroundLaneControllerDeps;\n\n\t/** Emit a warning without ever throwing — used from disposal-adjacent persistence where a\n\t * listener failure (or a bare test double missing `emit`) must never block or crash cleanup. */\n\tprivate _safeWarn(message: string): void {\n\t\ttry {\n\t\t\tthis.deps.emit({ type: \"warning\", message });\n\t\t} catch {\n\t\t\t// Dispose must never throw.\n\t\t}\n\t}\n\n\tprivate _recordWorkerTerminal(record: LaneRecord, durableNotificationId: string): void {\n\t\tthis._getWorkerNotificationCoordinator().recordTerminal(record, durableNotificationId);\n\t}\n\n\tconstructor(deps: BackgroundLaneControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis._laneModels = new LaneModelResolver(deps);\n\t\tthis._research = new ResearchLaneController(deps, this._laneTracker, this._laneModels);\n\t\tthis._fitness = new ModelFitnessController(deps, this._laneModels);\n\t\tthis._goalAutoContinue = new GoalAutoContinueController({\n\t\t\tisDisposed: deps.isDisposed,\n\t\t\tisGoalToolActive: deps.isGoalToolActive,\n\t\t\tgetSettingsManager: deps.getSettingsManager,\n\t\t\tgetModelCapabilityProfile: deps.getModelCapabilityProfile,\n\t\t\tgetGoalRuntimeSnapshot: deps.getGoalRuntimeSnapshot,\n\t\t\thasInFlightLaneForGoal: (goalId) => this._hasInFlightLaneForGoal(goalId),\n\t\t\tcontinueGoalLoop: deps.continueGoalLoop,\n\t\t\temit: deps.emit,\n\t\t});\n\t}\n\n\tprivate _getWorkerController(): WorkerDelegationController {\n\t\tthis._workers ??= new WorkerDelegationController(\n\t\t\tthis.deps,\n\t\t\tthis._getWorkerNotificationCoordinator(),\n\t\t\tthis._getWorkerLifecycle(),\n\t\t);\n\t\treturn this._workers;\n\t}\n\n\tprivate _getWorkerLifecycle(): WorkerLifecycle {\n\t\tif (this._workerLifecycle) return this._workerLifecycle;\n\t\tconst lifecycle = new WorkerLifecycle({\n\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\tsessionId: this.deps.getSessionId(),\n\t\t});\n\t\tthis._workerLifecycle = lifecycle;\n\t\tfor (const notification of lifecycle.getPendingTerminalNotifications()) {\n\t\t\tthis._getWorkerNotificationCoordinator().recordTerminal(notification.record, notification.notificationId);\n\t\t}\n\t\treturn lifecycle;\n\t}\n\n\tprivate _getWorkerNotificationCoordinator(): WorkerNotificationCoordinator {\n\t\tthis._workerNotifications ??= new WorkerNotificationCoordinator({\n\t\t\tgetWorkerRecords: () => this._workerLifecycle?.getAllRecords() ?? [],\n\t\t\temitStatus: (status) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.deps.emit({\n\t\t\t\t\t\ttype: \"delegate_workers\",\n\t\t\t\t\t\t...status,\n\t\t\t\t\t\tterminalSinceFlush: [...status.terminalSinceFlush],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// A partial integration must not crash the event-driven terminal outbox.\n\t\t\t\t}\n\t\t\t},\n\t\t\tnotify: (records) => this.deps.notifyWorkerTerminalHandoff(records),\n\t\t\twarn: (message) => this._safeWarn(message),\n\t\t\tmarkDurableDelivered: (notificationIds) => this._workerLifecycle?.markNotificationsDelivered(notificationIds),\n\t\t});\n\t\treturn this._workerNotifications;\n\t}\n\n\tprivate _getManagedLaneController(): ManagedLaneController {\n\t\tthis._managedLanes ??= new ManagedLaneController(\n\t\t\tthis.deps,\n\t\t\tthis._getWorkerLifecycle(),\n\t\t\t(record, notificationId) => this._recordWorkerTerminal(record, notificationId),\n\t\t);\n\t\treturn this._managedLanes;\n\t}\n\n\tprivate _hydrateManagedLanes(): void {\n\t\tif (this._managedLanes) {\n\t\t\tthis._managedLanes.ensureHydrated();\n\t\t\treturn;\n\t\t}\n\t\tconst hasPersistedActiveManagedLane = getLatestLaneRecordSnapshots(\n\t\t\tgetActiveSessionBranchEntries(this.deps.getSessionManager()),\n\t\t).some((record) => record.type === \"tmux-worker\" && (record.status === \"queued\" || record.status === \"running\"));\n\t\tif (hasPersistedActiveManagedLane) this._getManagedLaneController().ensureHydrated();\n\t}\n\n\t/** Live lane records tracked by this process (running and terminal). */\n\tgetLaneRecords(): LaneRecord[] {\n\t\tthis._hydrateManagedLanes();\n\t\tconst workerRecords =\n\t\t\tthis._workers?.getRecords() ??\n\t\t\t(this.deps.isDelegateToolActive?.() ? this._getWorkerController().getRecords() : []);\n\t\treturn [\n\t\t\t...this._laneTracker.getRecords(),\n\t\t\t...workerRecords,\n\t\t\t...(this._workerLifecycle?.getManagedRecords() ?? []),\n\t\t];\n\t}\n\n\t/** Does not materialize the worker controller when UAC omitted delegation. */\n\tgetTaskRuntimeSnapshot(): TaskRuntimeProjection | undefined {\n\t\treturn this._workerLifecycle?.getTaskRuntimeSnapshot();\n\t}\n\n\t/** Reconcile only when delegation has already been materialized; UAC omission stays zero-load. */\n\tsynchronizeGoalState(goal: GoalState): void {\n\t\tthis._workers?.synchronizeGoalState(goal);\n\t}\n\n\t/**\n\t * Resolve a tracked managed-lane dispatch. The caller's stable id is also the canonical durable\n\t * lane id, so this is an existence check rather than an id translation.\n\t */\n\tresolveManagedLaneId(callerLaneId: string): string | undefined {\n\t\treturn this._getManagedLaneController().resolve(callerLaneId);\n\t}\n\n\t/** Live count of active lanes — the real source for AutonomyStatusSnapshot.activeLaneCount. */\n\tgetActiveLaneCount(): number {\n\t\treturn this.getLaneRecords().filter((record) => record.status === \"queued\" || record.status === \"running\").length;\n\t}\n\n\t/** Belt-and-braces guard: whether ANY queued/running lane is tagged with this goalId. */\n\tprivate _hasInFlightLaneForGoal(goalId: string): boolean {\n\t\treturn this.getLaneRecords().some(\n\t\t\t(record) => record.goalId === goalId && (record.status === \"queued\" || record.status === \"running\"),\n\t\t);\n\t}\n\n\t/** Delegate the out-of-process dispatch/terminal claim to its single lifecycle owner. */\n\trecordManagedLane(event: ManagedLaneEvent): LaneRecord | undefined {\n\t\treturn this._getManagedLaneController().record(event);\n\t}\n\n\t/** Why the last idle research-lane evaluation skipped, for /autonomy diagnostics. */\n\tgetLastResearchLaneSkipReason(): string | undefined {\n\t\treturn this._research.getLastSkipReason();\n\t}\n\n\t/**\n\t * Abort in-flight research and delegate worker disposal to its single owning controller.\n\t *\n\t * This synchronous body is the LAST provably-safe write window for canceled/in-flight work.\n\t * `dispose()` (agent-session.ts) has already set the session's own disposed flag but has not yet\n\t * returned — no successor session (e.g. a `/reload` adoption) can exist yet, so an append here\n\t * cannot interleave with one; a post-await continuation resuming AFTER this method returns must\n\t * not append (see the disposed branch in `runWorkerDelegationOnce`). Persist FIRST, then\n\t * complete-in-memory, so a throw from one lane's persist cannot skip another's; each persist gets\n\t * its own try/catch — dispose must never throw.\n\t */\n\tabortInFlightLanes(): void {\n\t\tthis._goalAutoContinue.clearTimer();\n\t\tthis._research.abort();\n\t\tthis._fitness.abort();\n\t\tfor (const record of this._laneTracker.getRecords()) {\n\t\t\tif (record.status !== \"queued\" && record.status !== \"running\") continue;\n\t\t\tif (record.type === \"tmux-worker\") continue;\n\t\t\tconst canceled = this._laneTracker.complete(record.laneId, {\n\t\t\t\tstatus: \"canceled\",\n\t\t\t\treasonCode: \"session_disposed\",\n\t\t\t});\n\t\t\tif (!canceled) continue;\n\t\t\ttry {\n\t\t\t\tappendLaneRecordSnapshot(this.deps.getSessionManager(), canceled);\n\t\t\t} catch (error) {\n\t\t\t\tthis._safeWarn(\n\t\t\t\t\t`Failed to persist canceled lane record ${canceled.laneId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis._managedLanes?.release();\n\n\t\tthis._workers?.abort();\n\t\tthis._workerNotifications?.dispose();\n\t}\n\n\tclearGoalAutoContinueTimer(): void {\n\t\tthis._goalAutoContinue.clearTimer();\n\t}\n\n\tscheduleGoalAutoContinueFromIdle(options?: PromptOptions): void {\n\t\tthis._goalAutoContinue.scheduleFromIdle(options);\n\t}\n\n\t/**\n\t * Single-flight entry point for EVERY goal-continuation loop invocation — idle autosteer\n\t * ({@link _runScheduledGoalAutoContinue}) AND the manual `/goal start` / `/goal-continue`\n\t * commands (reached through `AgentSession.continueGoalLoop`). Both paths ultimately submit\n\t * continuation prompts through the session's single `prompt()` path, so two loops racing throws\n\t * \"Agent is already processing\" from whichever submits second. `_isGoalAutoContinuing` is the\n\t * ONE owner of that mutex; `deps.continueGoalLoop` (the raw {@link GoalLoopController} loop)\n\t * must never be called directly outside this method, or the guard is bypassed.\n\t */\n\tasync continueGoalLoopExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\treturn this._goalAutoContinue.continueExclusive(options);\n\t}\n\n\tclearResearchLaneTimer(): void {\n\t\tthis._research.clearTimer();\n\t}\n\n\tscheduleResearchLaneFromIdle(): void {\n\t\tthis._research.scheduleFromIdle();\n\t}\n\n\tresolveLaneModel(configuredPattern: string | undefined): Model<Api> | undefined {\n\t\treturn this._laneModels.resolveModel(configuredPattern);\n\t}\n\n\tgetOrchestrationProfileCatalog(): Array<{ profileId: string; role: string; description: string }> {\n\t\treturn this._getWorkerController().getProfileCatalog();\n\t}\n\n\t/**\n\t * Run one bounded, read-only research pass and persist its results: evidence bundle snapshot,\n\t * terminal lane record, and spawned-usage cost report (single-hop invariant, idempotent on the\n\t * lane's reportId). Explicit calls (e.g. `/autonomy research`) express user intent and bypass the\n\t * enabled/mode/dedupe gates the idle scheduler enforces; budget and capability gates always apply.\n\t */\n\tasync runResearchLaneOnce(request?: {\n\t\tquery?: string;\n\t\tcontext?: string;\n\t\tgoalId?: string;\n\t}): Promise<ResearchLaneRunOutcome> {\n\t\treturn this._research.runOnce(request);\n\t}\n\t/** Start a durable, profile-bound worker delegation. */\n\tstartWorkerDelegation(\n\t\trequest: WorkerDelegationRequest,\n\t): { started: false; skipReason: string } | { started: true; record: LaneRecord } {\n\t\treturn this._getWorkerController().start(request);\n\t}\n\n\t/** Run one worker immediately; used by focused integrations and tests. */\n\tasync runWorkerDelegationOnce(\n\t\trequest: WorkerDelegationRequest,\n\t\tonStarted?: (record: LaneRecord) => void,\n\t\texistingRecord?: LaneRecord,\n\t): Promise<WorkerDelegationRunOutcome> {\n\t\treturn this._getWorkerController().runOnce(request, onStarted, existingRecord);\n\t}\n\t/**\n\t * Probe a candidate model against the subagent contracts (research/worker/judge/search/\n\t * tool-call surfaces) via {@link runModelFitnessProbe}. The model must resolve and\n\t * authenticate; every probe call runs as an isolated completion on that model, and probe\n\t * spend is reported through spawned-usage accounting.\n\t */\n\tasync runModelFitness(args: {\n\t\tmodel: string;\n\t\ttrials?: number;\n\t\t/** LLM tool-call id, present only via the model_fitness tool path — see model-fitness.ts. */\n\t\ttoolCallId?: string;\n\t}): Promise<{ started: true; model: string; report: ModelFitnessReport } | { started: false; skipReason: string }> {\n\t\treturn this._fitness.run(args);\n\t}\n\n\t/** Start every capacity-eligible queued worker at the owner session's foreground-idle boundary. */\n\tdrainQueuedWorkerDelegations(): void {\n\t\tif (this.deps.isDelegateToolActive?.()) this._getWorkerController().drain();\n\t}\n\n\t/** Fitness reports persisted for THIS host (measured evidence for architect/profile decisions). */\n\tgetStoredFitnessReports(): StoredFitnessReport[] {\n\t\treturn this._fitness.getStoredReports();\n\t}\n}\n"]}
1
+ {"version":3,"file":"background-lane-controller.d.ts","sourceRoot":"","sources":["../../src/core/background-lane-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Execution-plane coordinator for goal continuation, research, managed lanes, worker delegation,\n * and model fitness.\n *\n * Coordination state is owned by focused controllers. This coordinator retains one AgentSession\n * composition seam and a shared lane read model. Everything it needs\n * — the session manager, settings, model registry, live model, capability envelope, the goal\n * continuation LOOP, the isolated-completion primitive, spawned-usage accounting, and the telemetry\n * sink — is reached through narrow deps accessors rather than the whole AgentSession.\n *\n * Drive-loop boundary (deliberate): the idle triggers ({@link scheduleGoalAutoContinueFromIdle},\n * {@link scheduleResearchLaneFromIdle}) are invoked from the session's prompt tail as one-line\n * delegations; goal auto-continue itself only ever asks the session to `continueGoalLoop`, so this\n * controller never touches `prompt()`, the last-assistant-message, retry, or streaming state.\n */\n\nimport type { Api, Model } from \"@caupulican/pi-ai\";\nimport type {\n\tGoalContinuationLoopOptions,\n\tGoalContinuationLoopResult,\n\tPromptOptions,\n\tResearchLaneRunOutcome,\n\tWorkerDelegationRunOutcome,\n} from \"./agent-session-contracts.ts\";\nimport { type LaneRecord, LaneTracker } from \"./autonomy/lane-tracker.ts\";\nimport { appendLaneRecordSnapshot, getLatestLaneRecordSnapshots } from \"./autonomy/session-lane-record.ts\";\nimport { ManagedLaneController } from \"./delegation/managed-lane-controller.ts\";\nimport {\n\tWorkerDelegationController,\n\ttype WorkerDelegationControllerDeps,\n} from \"./delegation/worker-delegation-controller.ts\";\nimport type { WorkerDelegationRequest } from \"./delegation/worker-delegation-request.ts\";\nimport { WorkerLifecycle } from \"./delegation/worker-lifecycle.ts\";\nimport { WorkerNotificationCoordinator } from \"./delegation/worker-notification-coordinator.ts\";\nimport type { ManagedLaneEvent } from \"./extensions/types.ts\";\nimport { GoalAutoContinueController } from \"./goals/goal-auto-continue-controller.ts\";\nimport type { GoalRuntimeSnapshot, GoalRuntimeSnapshotSettings } from \"./goals/goal-runtime-snapshot.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport type { ModelCapabilityProfile } from \"./model-capability.ts\";\nimport type { StoredFitnessReport } from \"./models/fitness-store.ts\";\nimport type { TaskRuntimeProjection } from \"./orchestration/task-runtime.ts\";\nimport { LaneModelResolver, type LaneModelResolverDeps } from \"./research/lane-model-resolver.ts\";\nimport type { ModelFitnessReport } from \"./research/model-fitness.ts\";\nimport { ModelFitnessController, type ModelFitnessControllerDeps } from \"./research/model-fitness-controller.ts\";\nimport { ResearchLaneController, type ResearchLaneControllerDeps } from \"./research/research-lane-controller.ts\";\nimport { getActiveSessionBranchEntries } from \"./session-snapshot.ts\";\n\nexport { isLocalExecutionModel } from \"./delegation/worker-delegation-controller.ts\";\nexport { clampLaneMaxUsd } from \"./research/lane-model-resolver.ts\";\n\nexport interface BackgroundLaneControllerDeps\n\textends WorkerDelegationControllerDeps,\n\t\tResearchLaneControllerDeps,\n\t\tModelFitnessControllerDeps,\n\t\tLaneModelResolverDeps {\n\t/** True iff the `goal` tool is in the session's ACTIVE surface -- the capability-adaptive gate\n\t * for every goal-continuation loop (see `continueGoalLoopExclusive`): a surface without the\n\t * goal tool (lean capability blocklist, worker role ceiling, --tools/profile exclusion) must\n\t * never be driven with continuation prompts it cannot execute. */\n\tisGoalToolActive(): boolean;\n\t/** Capability profile of the SESSION model (gates background lanes, scales continuation budgets). */\n\tgetModelCapabilityProfile(): ModelCapabilityProfile;\n\t/** Continuation gate + goal state for the idle autosteer scheduler. */\n\tgetGoalRuntimeSnapshot(settings: GoalRuntimeSnapshotSettings): GoalRuntimeSnapshot;\n\t/** Drive-loop boundary: the session's bounded goal-continuation loop (owns `prompt()`, not us). */\n\tcontinueGoalLoop(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult>;\n\t/** Persist an explicit stopped state when the selected surface cannot drive the active goal. */\n\tmarkGoalToolUnavailable(): void;\n}\n\nexport class BackgroundLaneController {\n\t/** Live lane registry — the real source for AutonomyStatusSnapshot.activeLaneCount. */\n\tprivate readonly _laneTracker = new LaneTracker();\n\tprivate readonly _laneModels: LaneModelResolver;\n\tprivate readonly _goalAutoContinue: GoalAutoContinueController;\n\tprivate readonly _research: ResearchLaneController;\n\tprivate readonly _fitness: ModelFitnessController;\n\t/** Lazily materialized only when managed-lane state is queried or reported. */\n\tprivate _managedLanes: ManagedLaneController | undefined;\n\t/** Lazily materialized so a UAC surface without `delegate` allocates no worker runtime state. */\n\tprivate _workers: WorkerDelegationController | undefined;\n\t/** One durable lifecycle shared by every worker execution adapter. */\n\tprivate _workerLifecycle: WorkerLifecycle | undefined;\n\t/** Shared terminal outbox for managed and in-process workers; lazy under UAC omission. */\n\tprivate _workerNotifications: WorkerNotificationCoordinator | undefined;\n\tprivate readonly deps: BackgroundLaneControllerDeps;\n\n\t/** Emit a warning without ever throwing — used from disposal-adjacent persistence where a\n\t * listener failure (or a bare test double missing `emit`) must never block or crash cleanup. */\n\tprivate _safeWarn(message: string): void {\n\t\ttry {\n\t\t\tthis.deps.emit({ type: \"warning\", message });\n\t\t} catch {\n\t\t\t// Dispose must never throw.\n\t\t}\n\t}\n\n\tprivate _recordWorkerTerminal(record: LaneRecord, durableNotificationId: string): void {\n\t\tthis._getWorkerNotificationCoordinator().recordTerminal(record, durableNotificationId);\n\t}\n\n\tconstructor(deps: BackgroundLaneControllerDeps) {\n\t\tthis.deps = deps;\n\t\tthis._laneModels = new LaneModelResolver(deps);\n\t\tthis._research = new ResearchLaneController(deps, this._laneTracker, this._laneModels);\n\t\tthis._fitness = new ModelFitnessController(deps, this._laneModels);\n\t\tthis._goalAutoContinue = new GoalAutoContinueController({\n\t\t\tisDisposed: deps.isDisposed,\n\t\t\tisGoalToolActive: deps.isGoalToolActive,\n\t\t\tgetSettingsManager: deps.getSettingsManager,\n\t\t\tgetModelCapabilityProfile: deps.getModelCapabilityProfile,\n\t\t\tgetGoalRuntimeSnapshot: deps.getGoalRuntimeSnapshot,\n\t\t\thasInFlightLaneForGoal: (goalId) => this._hasInFlightLaneForGoal(goalId),\n\t\t\tcontinueGoalLoop: deps.continueGoalLoop,\n\t\t\tmarkGoalToolUnavailable: deps.markGoalToolUnavailable,\n\t\t\temit: deps.emit,\n\t\t});\n\t}\n\n\tprivate _getWorkerController(): WorkerDelegationController {\n\t\tthis._workers ??= new WorkerDelegationController(\n\t\t\tthis.deps,\n\t\t\tthis._getWorkerNotificationCoordinator(),\n\t\t\tthis._getWorkerLifecycle(),\n\t\t);\n\t\treturn this._workers;\n\t}\n\n\tprivate _getWorkerLifecycle(): WorkerLifecycle {\n\t\tif (this._workerLifecycle) return this._workerLifecycle;\n\t\tconst lifecycle = new WorkerLifecycle({\n\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\tsessionId: this.deps.getSessionId(),\n\t\t});\n\t\tthis._workerLifecycle = lifecycle;\n\t\tfor (const notification of lifecycle.getPendingTerminalNotifications()) {\n\t\t\tthis._getWorkerNotificationCoordinator().recordTerminal(notification.record, notification.notificationId);\n\t\t}\n\t\treturn lifecycle;\n\t}\n\n\tprivate _getWorkerNotificationCoordinator(): WorkerNotificationCoordinator {\n\t\tthis._workerNotifications ??= new WorkerNotificationCoordinator({\n\t\t\tgetWorkerRecords: () => this._workerLifecycle?.getAllRecords() ?? [],\n\t\t\temitStatus: (status) => {\n\t\t\t\ttry {\n\t\t\t\t\tthis.deps.emit({\n\t\t\t\t\t\ttype: \"delegate_workers\",\n\t\t\t\t\t\t...status,\n\t\t\t\t\t\tterminalSinceFlush: [...status.terminalSinceFlush],\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// A partial integration must not crash the event-driven terminal outbox.\n\t\t\t\t}\n\t\t\t},\n\t\t\tnotify: (records) => this.deps.notifyWorkerTerminalHandoff(records),\n\t\t\twarn: (message) => this._safeWarn(message),\n\t\t\tmarkDurableDelivered: (notificationIds) => this._workerLifecycle?.markNotificationsDelivered(notificationIds),\n\t\t});\n\t\treturn this._workerNotifications;\n\t}\n\n\tprivate _getManagedLaneController(): ManagedLaneController {\n\t\tthis._managedLanes ??= new ManagedLaneController(\n\t\t\tthis.deps,\n\t\t\tthis._getWorkerLifecycle(),\n\t\t\t(record, notificationId) => this._recordWorkerTerminal(record, notificationId),\n\t\t);\n\t\treturn this._managedLanes;\n\t}\n\n\tprivate _hydrateManagedLanes(): void {\n\t\tif (this._managedLanes) {\n\t\t\tthis._managedLanes.ensureHydrated();\n\t\t\treturn;\n\t\t}\n\t\tconst hasPersistedActiveManagedLane = getLatestLaneRecordSnapshots(\n\t\t\tgetActiveSessionBranchEntries(this.deps.getSessionManager()),\n\t\t).some((record) => record.type === \"tmux-worker\" && (record.status === \"queued\" || record.status === \"running\"));\n\t\tif (hasPersistedActiveManagedLane) this._getManagedLaneController().ensureHydrated();\n\t}\n\n\t/** Live lane records tracked by this process (running and terminal). */\n\tgetLaneRecords(): LaneRecord[] {\n\t\tthis._hydrateManagedLanes();\n\t\tconst workerRecords =\n\t\t\tthis._workers?.getRecords() ??\n\t\t\t(this.deps.isDelegateToolActive?.() ? this._getWorkerController().getRecords() : []);\n\t\treturn [\n\t\t\t...this._laneTracker.getRecords(),\n\t\t\t...workerRecords,\n\t\t\t...(this._workerLifecycle?.getManagedRecords() ?? []),\n\t\t];\n\t}\n\n\t/** Does not materialize the worker controller when UAC omitted delegation. */\n\tgetTaskRuntimeSnapshot(): TaskRuntimeProjection | undefined {\n\t\treturn this._workerLifecycle?.getTaskRuntimeSnapshot();\n\t}\n\n\t/** Reconcile only when delegation has already been materialized; UAC omission stays zero-load. */\n\tsynchronizeGoalState(goal: GoalState): void {\n\t\tthis._workers?.synchronizeGoalState(goal);\n\t}\n\n\t/**\n\t * Resolve a tracked managed-lane dispatch. The caller's stable id is also the canonical durable\n\t * lane id, so this is an existence check rather than an id translation.\n\t */\n\tresolveManagedLaneId(callerLaneId: string): string | undefined {\n\t\treturn this._getManagedLaneController().resolve(callerLaneId);\n\t}\n\n\t/** Live count of active lanes — the real source for AutonomyStatusSnapshot.activeLaneCount. */\n\tgetActiveLaneCount(): number {\n\t\treturn this.getLaneRecords().filter((record) => record.status === \"queued\" || record.status === \"running\").length;\n\t}\n\n\t/** Belt-and-braces guard: whether ANY queued/running lane is tagged with this goalId. */\n\tprivate _hasInFlightLaneForGoal(goalId: string): boolean {\n\t\treturn this.getLaneRecords().some(\n\t\t\t(record) => record.goalId === goalId && (record.status === \"queued\" || record.status === \"running\"),\n\t\t);\n\t}\n\n\t/** Delegate the out-of-process dispatch/terminal claim to its single lifecycle owner. */\n\trecordManagedLane(event: ManagedLaneEvent): LaneRecord | undefined {\n\t\treturn this._getManagedLaneController().record(event);\n\t}\n\n\t/** Why the last idle research-lane evaluation skipped, for /autonomy diagnostics. */\n\tgetLastResearchLaneSkipReason(): string | undefined {\n\t\treturn this._research.getLastSkipReason();\n\t}\n\n\t/**\n\t * Abort in-flight research and delegate worker disposal to its single owning controller.\n\t *\n\t * This synchronous body is the LAST provably-safe write window for canceled/in-flight work.\n\t * `dispose()` (agent-session.ts) has already set the session's own disposed flag but has not yet\n\t * returned — no successor session (e.g. a `/reload` adoption) can exist yet, so an append here\n\t * cannot interleave with one; a post-await continuation resuming AFTER this method returns must\n\t * not append (see the disposed branch in `runWorkerDelegationOnce`). Persist FIRST, then\n\t * complete-in-memory, so a throw from one lane's persist cannot skip another's; each persist gets\n\t * its own try/catch — dispose must never throw.\n\t */\n\tabortInFlightLanes(): void {\n\t\tthis._goalAutoContinue.clearTimer();\n\t\tthis._research.abort();\n\t\tthis._fitness.abort();\n\t\tfor (const record of this._laneTracker.getRecords()) {\n\t\t\tif (record.status !== \"queued\" && record.status !== \"running\") continue;\n\t\t\tif (record.type === \"tmux-worker\") continue;\n\t\t\tconst canceled = this._laneTracker.complete(record.laneId, {\n\t\t\t\tstatus: \"canceled\",\n\t\t\t\treasonCode: \"session_disposed\",\n\t\t\t});\n\t\t\tif (!canceled) continue;\n\t\t\ttry {\n\t\t\t\tappendLaneRecordSnapshot(this.deps.getSessionManager(), canceled);\n\t\t\t} catch (error) {\n\t\t\t\tthis._safeWarn(\n\t\t\t\t\t`Failed to persist canceled lane record ${canceled.laneId}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthis._managedLanes?.release();\n\n\t\tthis._workers?.abort();\n\t\tthis._workerNotifications?.dispose();\n\t}\n\n\tclearGoalAutoContinueTimer(): void {\n\t\tthis._goalAutoContinue.clearTimer();\n\t}\n\n\tscheduleGoalAutoContinueFromIdle(options?: PromptOptions): void {\n\t\tthis._goalAutoContinue.scheduleFromIdle(options);\n\t}\n\n\t/**\n\t * Single-flight entry point for EVERY goal-continuation loop invocation — idle autosteer\n\t * ({@link _runScheduledGoalAutoContinue}) AND the manual `/goal start` / `/goal-continue`\n\t * commands (reached through `AgentSession.continueGoalLoop`). Both paths ultimately submit\n\t * continuation prompts through the session's single `prompt()` path, so two loops racing throws\n\t * \"Agent is already processing\" from whichever submits second. `_isGoalAutoContinuing` is the\n\t * ONE owner of that mutex; `deps.continueGoalLoop` (the raw {@link GoalLoopController} loop)\n\t * must never be called directly outside this method, or the guard is bypassed.\n\t */\n\tasync continueGoalLoopExclusive(options: GoalContinuationLoopOptions): Promise<GoalContinuationLoopResult> {\n\t\treturn this._goalAutoContinue.continueExclusive(options);\n\t}\n\n\tclearResearchLaneTimer(): void {\n\t\tthis._research.clearTimer();\n\t}\n\n\tscheduleResearchLaneFromIdle(): void {\n\t\tthis._research.scheduleFromIdle();\n\t}\n\n\tresolveLaneModel(configuredPattern: string | undefined): Model<Api> | undefined {\n\t\treturn this._laneModels.resolveModel(configuredPattern);\n\t}\n\n\tgetOrchestrationProfileCatalog(): Array<{ profileId: string; role: string; description: string }> {\n\t\treturn this._getWorkerController().getProfileCatalog();\n\t}\n\n\t/**\n\t * Run one bounded, read-only research pass and persist its results: evidence bundle snapshot,\n\t * terminal lane record, and spawned-usage cost report (single-hop invariant, idempotent on the\n\t * lane's reportId). Explicit calls (e.g. `/autonomy research`) express user intent and bypass the\n\t * enabled/mode/dedupe gates the idle scheduler enforces; budget and capability gates always apply.\n\t */\n\tasync runResearchLaneOnce(request?: {\n\t\tquery?: string;\n\t\tcontext?: string;\n\t\tgoalId?: string;\n\t}): Promise<ResearchLaneRunOutcome> {\n\t\treturn this._research.runOnce(request);\n\t}\n\t/** Start a durable, profile-bound worker delegation. */\n\tstartWorkerDelegation(\n\t\trequest: WorkerDelegationRequest,\n\t): { started: false; skipReason: string } | { started: true; record: LaneRecord } {\n\t\treturn this._getWorkerController().start(request);\n\t}\n\n\t/** Run one worker immediately; used by focused integrations and tests. */\n\tasync runWorkerDelegationOnce(\n\t\trequest: WorkerDelegationRequest,\n\t\tonStarted?: (record: LaneRecord) => void,\n\t\texistingRecord?: LaneRecord,\n\t): Promise<WorkerDelegationRunOutcome> {\n\t\treturn this._getWorkerController().runOnce(request, onStarted, existingRecord);\n\t}\n\t/**\n\t * Probe a candidate model against the subagent contracts (research/worker/judge/search/\n\t * tool-call surfaces) via {@link runModelFitnessProbe}. The model must resolve and\n\t * authenticate; every probe call runs as an isolated completion on that model, and probe\n\t * spend is reported through spawned-usage accounting.\n\t */\n\tasync runModelFitness(args: {\n\t\tmodel: string;\n\t\ttrials?: number;\n\t\t/** LLM tool-call id, present only via the model_fitness tool path — see model-fitness.ts. */\n\t\ttoolCallId?: string;\n\t}): Promise<{ started: true; model: string; report: ModelFitnessReport } | { started: false; skipReason: string }> {\n\t\treturn this._fitness.run(args);\n\t}\n\n\t/** Start every capacity-eligible queued worker at the owner session's foreground-idle boundary. */\n\tdrainQueuedWorkerDelegations(): void {\n\t\tif (this.deps.isDelegateToolActive?.()) this._getWorkerController().drain();\n\t}\n\n\t/** Fitness reports persisted for THIS host (measured evidence for architect/profile decisions). */\n\tgetStoredFitnessReports(): StoredFitnessReport[] {\n\t\treturn this._fitness.getStoredReports();\n\t}\n}\n"]}
@@ -68,6 +68,7 @@ export class BackgroundLaneController {
68
68
  getGoalRuntimeSnapshot: deps.getGoalRuntimeSnapshot,
69
69
  hasInFlightLaneForGoal: (goalId) => this._hasInFlightLaneForGoal(goalId),
70
70
  continueGoalLoop: deps.continueGoalLoop,
71
+ markGoalToolUnavailable: deps.markGoalToolUnavailable,
71
72
  emit: deps.emit,
72
73
  });
73
74
  }