@assemblyline-agents/runtime 5.0.9 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/README.md +18 -5
  2. package/dist/a2a-client.d.ts +1 -3
  3. package/dist/a2a-client.d.ts.map +1 -1
  4. package/dist/a2a-client.js +1 -4
  5. package/dist/a2a-client.js.map +1 -1
  6. package/dist/agent-event-handlers.d.ts +2 -0
  7. package/dist/agent-event-handlers.d.ts.map +1 -1
  8. package/dist/agent-event-handlers.js +1 -1
  9. package/dist/agent-event-handlers.js.map +1 -1
  10. package/dist/agent-hook-context.d.ts.map +1 -1
  11. package/dist/agent-hook-context.js +3 -1
  12. package/dist/agent-hook-context.js.map +1 -1
  13. package/dist/agent-hook-controller-factory.d.ts +5 -1
  14. package/dist/agent-hook-controller-factory.d.ts.map +1 -1
  15. package/dist/agent-hook-controller-factory.js +11 -9
  16. package/dist/agent-hook-controller-factory.js.map +1 -1
  17. package/dist/agent-hook-evaluation.d.ts +0 -2
  18. package/dist/agent-hook-evaluation.d.ts.map +1 -1
  19. package/dist/agent-hook-evaluation.js +10 -10
  20. package/dist/agent-hook-evaluation.js.map +1 -1
  21. package/dist/agent-hook-run-lifecycle.d.ts +1 -2
  22. package/dist/agent-hook-run-lifecycle.d.ts.map +1 -1
  23. package/dist/agent-hook-run-lifecycle.js +1 -2
  24. package/dist/agent-hook-run-lifecycle.js.map +1 -1
  25. package/dist/agent-surface.d.ts +5 -2
  26. package/dist/agent-surface.d.ts.map +1 -1
  27. package/dist/agent-surface.js +11 -3
  28. package/dist/agent-surface.js.map +1 -1
  29. package/dist/artifact-selection.js +1 -1
  30. package/dist/artifact-selection.js.map +1 -1
  31. package/dist/assembly-line-runtime.d.ts +9 -0
  32. package/dist/assembly-line-runtime.d.ts.map +1 -0
  33. package/dist/assembly-line-runtime.js +9 -0
  34. package/dist/assembly-line-runtime.js.map +1 -0
  35. package/dist/attachment-download.d.ts +17 -0
  36. package/dist/attachment-download.d.ts.map +1 -0
  37. package/dist/attachment-download.js +51 -0
  38. package/dist/attachment-download.js.map +1 -0
  39. package/dist/attachments.d.ts +2 -14
  40. package/dist/attachments.d.ts.map +1 -1
  41. package/dist/attachments.js +8 -80
  42. package/dist/attachments.js.map +1 -1
  43. package/dist/background-review-types.d.ts +2 -0
  44. package/dist/background-review-types.d.ts.map +1 -1
  45. package/dist/background-review.d.ts.map +1 -1
  46. package/dist/background-review.js +13 -0
  47. package/dist/background-review.js.map +1 -1
  48. package/dist/background-subagents.d.ts +1 -1
  49. package/dist/background-subagents.d.ts.map +1 -1
  50. package/dist/blob-storage.d.ts +12 -0
  51. package/dist/blob-storage.d.ts.map +1 -0
  52. package/dist/blob-storage.js +86 -0
  53. package/dist/blob-storage.js.map +1 -0
  54. package/dist/builtin-tool-executors.d.ts.map +1 -1
  55. package/dist/builtin-tool-executors.js +38 -29
  56. package/dist/builtin-tool-executors.js.map +1 -1
  57. package/dist/builtin-tools.d.ts +1 -2
  58. package/dist/builtin-tools.d.ts.map +1 -1
  59. package/dist/builtin-tools.js +20 -18
  60. package/dist/builtin-tools.js.map +1 -1
  61. package/dist/capability-inspection.d.ts +4 -0
  62. package/dist/capability-inspection.d.ts.map +1 -1
  63. package/dist/capability-inspection.js +10 -11
  64. package/dist/capability-inspection.js.map +1 -1
  65. package/dist/channel-credential-environment.d.ts +1 -1
  66. package/dist/channel-credential-environment.d.ts.map +1 -1
  67. package/dist/channel-credential-environment.js +1 -6
  68. package/dist/channel-credential-environment.js.map +1 -1
  69. package/dist/channel-ingress.d.ts +1 -2
  70. package/dist/channel-ingress.d.ts.map +1 -1
  71. package/dist/channel-ingress.js +2 -2
  72. package/dist/channel-ingress.js.map +1 -1
  73. package/dist/channel-operation.d.ts +0 -1
  74. package/dist/channel-operation.d.ts.map +1 -1
  75. package/dist/channel-operation.js +1 -2
  76. package/dist/channel-operation.js.map +1 -1
  77. package/dist/channel.d.ts +32 -3
  78. package/dist/channel.d.ts.map +1 -1
  79. package/dist/connection-assets.d.ts.map +1 -1
  80. package/dist/connection-assets.js.map +1 -1
  81. package/dist/connection-authorization-service.d.ts +52 -0
  82. package/dist/connection-authorization-service.d.ts.map +1 -0
  83. package/dist/connection-authorization-service.js +417 -0
  84. package/dist/connection-authorization-service.js.map +1 -0
  85. package/dist/connection-credential-materialization.d.ts +23 -0
  86. package/dist/connection-credential-materialization.d.ts.map +1 -0
  87. package/dist/connection-credential-materialization.js +223 -0
  88. package/dist/connection-credential-materialization.js.map +1 -0
  89. package/dist/connection-definition-loader.d.ts +6 -0
  90. package/dist/connection-definition-loader.d.ts.map +1 -0
  91. package/dist/connection-definition-loader.js +102 -0
  92. package/dist/connection-definition-loader.js.map +1 -0
  93. package/dist/connection-discovery.d.ts +11 -0
  94. package/dist/connection-discovery.d.ts.map +1 -0
  95. package/dist/connection-discovery.js +105 -0
  96. package/dist/connection-discovery.js.map +1 -0
  97. package/dist/connection-events.d.ts.map +1 -1
  98. package/dist/connection-events.js.map +1 -1
  99. package/dist/connection-oauth-client.d.ts +3 -0
  100. package/dist/connection-oauth-client.d.ts.map +1 -0
  101. package/dist/connection-oauth-client.js +64 -0
  102. package/dist/connection-oauth-client.js.map +1 -0
  103. package/dist/connection-runtime-types.d.ts +78 -0
  104. package/dist/connection-runtime-types.d.ts.map +1 -0
  105. package/dist/connection-runtime-types.js +31 -0
  106. package/dist/connection-runtime-types.js.map +1 -0
  107. package/dist/connection-search-scope.d.ts +13 -0
  108. package/dist/connection-search-scope.d.ts.map +1 -0
  109. package/dist/connection-search-scope.js +24 -0
  110. package/dist/connection-search-scope.js.map +1 -0
  111. package/dist/connections.d.ts +18 -167
  112. package/dist/connections.d.ts.map +1 -1
  113. package/dist/connections.js +71 -1017
  114. package/dist/connections.js.map +1 -1
  115. package/dist/context-policy-util.d.ts +0 -4
  116. package/dist/context-policy-util.d.ts.map +1 -1
  117. package/dist/context-policy-util.js +0 -10
  118. package/dist/context-policy-util.js.map +1 -1
  119. package/dist/conversation-context.d.ts +1 -1
  120. package/dist/conversation-context.d.ts.map +1 -1
  121. package/dist/conversation-context.js +12 -9
  122. package/dist/conversation-context.js.map +1 -1
  123. package/dist/default-outbound-route.d.ts +1 -0
  124. package/dist/default-outbound-route.d.ts.map +1 -1
  125. package/dist/default-outbound-route.js +1 -1
  126. package/dist/default-outbound-route.js.map +1 -1
  127. package/dist/delivery-queue.d.ts +1 -1
  128. package/dist/delivery-send.d.ts.map +1 -1
  129. package/dist/delivery-send.js +3 -2
  130. package/dist/delivery-send.js.map +1 -1
  131. package/dist/dynamic-schedule-execution.d.ts +4 -0
  132. package/dist/dynamic-schedule-execution.d.ts.map +1 -0
  133. package/dist/dynamic-schedule-execution.js +136 -0
  134. package/dist/dynamic-schedule-execution.js.map +1 -0
  135. package/dist/dynamic-schedule-runtime.d.ts +11 -20
  136. package/dist/dynamic-schedule-runtime.d.ts.map +1 -1
  137. package/dist/dynamic-schedule-runtime.js +27 -39
  138. package/dist/dynamic-schedule-runtime.js.map +1 -1
  139. package/dist/evolution-tracker.d.ts +52 -0
  140. package/dist/evolution-tracker.d.ts.map +1 -0
  141. package/dist/evolution-tracker.js +179 -0
  142. package/dist/evolution-tracker.js.map +1 -0
  143. package/dist/harness-capabilities.d.ts +7 -0
  144. package/dist/harness-capabilities.d.ts.map +1 -0
  145. package/dist/harness-capabilities.js +56 -0
  146. package/dist/harness-capabilities.js.map +1 -0
  147. package/dist/harness-continuation-persistence.d.ts +6 -0
  148. package/dist/harness-continuation-persistence.d.ts.map +1 -0
  149. package/dist/harness-continuation-persistence.js +11 -0
  150. package/dist/harness-continuation-persistence.js.map +1 -0
  151. package/dist/harness-execution-context.d.ts +10 -0
  152. package/dist/harness-execution-context.d.ts.map +1 -0
  153. package/dist/harness-execution-context.js +193 -0
  154. package/dist/harness-execution-context.js.map +1 -0
  155. package/dist/harness-loop-types.d.ts +67 -0
  156. package/dist/harness-loop-types.d.ts.map +1 -0
  157. package/dist/harness-loop-types.js +2 -0
  158. package/dist/harness-loop-types.js.map +1 -0
  159. package/dist/harness-loop.d.ts +13 -94
  160. package/dist/harness-loop.d.ts.map +1 -1
  161. package/dist/harness-loop.js +19 -551
  162. package/dist/harness-loop.js.map +1 -1
  163. package/dist/harness-tool-execution.d.ts +30 -0
  164. package/dist/harness-tool-execution.d.ts.map +1 -0
  165. package/dist/harness-tool-execution.js +397 -0
  166. package/dist/harness-tool-execution.js.map +1 -0
  167. package/dist/harness-transcript-resume.d.ts +18 -0
  168. package/dist/harness-transcript-resume.d.ts.map +1 -0
  169. package/dist/harness-transcript-resume.js +102 -0
  170. package/dist/harness-transcript-resume.js.map +1 -0
  171. package/dist/harness-turn-execution.d.ts +11 -0
  172. package/dist/harness-turn-execution.d.ts.map +1 -0
  173. package/dist/harness-turn-execution.js +157 -0
  174. package/dist/harness-turn-execution.js.map +1 -0
  175. package/dist/index.d.ts +10 -499
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +9 -2139
  178. package/dist/index.js.map +1 -1
  179. package/dist/local-sandbox.d.ts +2 -1
  180. package/dist/local-sandbox.d.ts.map +1 -1
  181. package/dist/mcp-client.d.ts +11 -1
  182. package/dist/mcp-client.d.ts.map +1 -1
  183. package/dist/mcp-client.js +39 -7
  184. package/dist/mcp-client.js.map +1 -1
  185. package/dist/media-processor.d.ts +3 -1
  186. package/dist/media-processor.d.ts.map +1 -1
  187. package/dist/media-processor.js +15 -1
  188. package/dist/media-processor.js.map +1 -1
  189. package/dist/memory-storage.d.ts +51 -0
  190. package/dist/memory-storage.d.ts.map +1 -0
  191. package/dist/memory-storage.js +324 -0
  192. package/dist/memory-storage.js.map +1 -0
  193. package/dist/model-credential-stores.d.ts.map +1 -1
  194. package/dist/model-credential-stores.js +6 -12
  195. package/dist/model-credential-stores.js.map +1 -1
  196. package/dist/model-provider-environment.d.ts +4 -0
  197. package/dist/model-provider-environment.d.ts.map +1 -0
  198. package/dist/model-provider-environment.js +36 -0
  199. package/dist/model-provider-environment.js.map +1 -0
  200. package/dist/plugin-embeddings.d.ts +4 -0
  201. package/dist/plugin-embeddings.d.ts.map +1 -0
  202. package/dist/plugin-embeddings.js +75 -0
  203. package/dist/plugin-embeddings.js.map +1 -0
  204. package/dist/public-api.d.ts +8 -5
  205. package/dist/public-api.d.ts.map +1 -1
  206. package/dist/public-api.js +6 -4
  207. package/dist/public-api.js.map +1 -1
  208. package/dist/relay-mcp-client.js +1 -1
  209. package/dist/relay-mcp-client.js.map +1 -1
  210. package/dist/replay-resume-authorization.d.ts +8 -0
  211. package/dist/replay-resume-authorization.d.ts.map +1 -0
  212. package/dist/replay-resume-authorization.js +181 -0
  213. package/dist/replay-resume-authorization.js.map +1 -0
  214. package/dist/replay-resume-connection.d.ts +6 -0
  215. package/dist/replay-resume-connection.d.ts.map +1 -0
  216. package/dist/replay-resume-connection.js +75 -0
  217. package/dist/replay-resume-connection.js.map +1 -0
  218. package/dist/replay-resume-controls.d.ts +6 -0
  219. package/dist/replay-resume-controls.d.ts.map +1 -0
  220. package/dist/replay-resume-controls.js +157 -0
  221. package/dist/replay-resume-controls.js.map +1 -0
  222. package/dist/replay-resume-harness.d.ts +14 -0
  223. package/dist/replay-resume-harness.d.ts.map +1 -0
  224. package/dist/replay-resume-harness.js +158 -0
  225. package/dist/replay-resume-harness.js.map +1 -0
  226. package/dist/replay-resume-recovery.d.ts +10 -0
  227. package/dist/replay-resume-recovery.d.ts.map +1 -0
  228. package/dist/replay-resume-recovery.js +62 -0
  229. package/dist/replay-resume-recovery.js.map +1 -0
  230. package/dist/replay-resume-shared.d.ts +16 -0
  231. package/dist/replay-resume-shared.d.ts.map +1 -0
  232. package/dist/replay-resume-shared.js +55 -0
  233. package/dist/replay-resume-shared.js.map +1 -0
  234. package/dist/replay-resume-types.d.ts +85 -0
  235. package/dist/replay-resume-types.d.ts.map +1 -0
  236. package/dist/replay-resume-types.js +2 -0
  237. package/dist/replay-resume-types.js.map +1 -0
  238. package/dist/replay-resume.d.ts +7 -119
  239. package/dist/replay-resume.d.ts.map +1 -1
  240. package/dist/replay-resume.js +5 -715
  241. package/dist/replay-resume.js.map +1 -1
  242. package/dist/resource-projection-util.d.ts +1 -2
  243. package/dist/resource-projection-util.d.ts.map +1 -1
  244. package/dist/resource-projection-util.js +2 -11
  245. package/dist/resource-projection-util.js.map +1 -1
  246. package/dist/resource-projection.d.ts.map +1 -1
  247. package/dist/resource-projection.js +67 -22
  248. package/dist/resource-projection.js.map +1 -1
  249. package/dist/run-execution-host.d.ts +98 -0
  250. package/dist/run-execution-host.d.ts.map +1 -0
  251. package/dist/run-execution-host.js +2 -0
  252. package/dist/run-execution-host.js.map +1 -0
  253. package/dist/run-execution.d.ts +6 -0
  254. package/dist/run-execution.d.ts.map +1 -0
  255. package/dist/run-execution.js +313 -0
  256. package/dist/run-execution.js.map +1 -0
  257. package/dist/run-initialization.d.ts +19 -0
  258. package/dist/run-initialization.d.ts.map +1 -0
  259. package/dist/run-initialization.js +171 -0
  260. package/dist/run-initialization.js.map +1 -0
  261. package/dist/run-recovery.d.ts +1 -1
  262. package/dist/run-recovery.js.map +1 -1
  263. package/dist/runtime-connection-definitions.d.ts +35 -0
  264. package/dist/runtime-connection-definitions.d.ts.map +1 -0
  265. package/dist/runtime-connection-definitions.js +97 -0
  266. package/dist/runtime-connection-definitions.js.map +1 -0
  267. package/dist/runtime-engine-background.d.ts +64 -0
  268. package/dist/runtime-engine-background.d.ts.map +1 -0
  269. package/dist/runtime-engine-background.js +136 -0
  270. package/dist/runtime-engine-background.js.map +1 -0
  271. package/dist/runtime-engine-base.d.ts +60 -0
  272. package/dist/runtime-engine-base.d.ts.map +1 -0
  273. package/dist/runtime-engine-base.js +192 -0
  274. package/dist/runtime-engine-base.js.map +1 -0
  275. package/dist/runtime-engine-capabilities.d.ts +81 -0
  276. package/dist/runtime-engine-capabilities.d.ts.map +1 -0
  277. package/dist/runtime-engine-capabilities.js +198 -0
  278. package/dist/runtime-engine-capabilities.js.map +1 -0
  279. package/dist/runtime-engine-config.d.ts +77 -0
  280. package/dist/runtime-engine-config.d.ts.map +1 -0
  281. package/dist/runtime-engine-config.js +187 -0
  282. package/dist/runtime-engine-config.js.map +1 -0
  283. package/dist/runtime-engine-foundation.d.ts +11 -0
  284. package/dist/runtime-engine-foundation.d.ts.map +1 -0
  285. package/dist/runtime-engine-foundation.js +121 -0
  286. package/dist/runtime-engine-foundation.js.map +1 -0
  287. package/dist/runtime-engine-hooks.d.ts +33 -0
  288. package/dist/runtime-engine-hooks.d.ts.map +1 -0
  289. package/dist/runtime-engine-hooks.js +149 -0
  290. package/dist/runtime-engine-hooks.js.map +1 -0
  291. package/dist/runtime-engine-integrations.d.ts +16 -0
  292. package/dist/runtime-engine-integrations.d.ts.map +1 -0
  293. package/dist/runtime-engine-integrations.js +177 -0
  294. package/dist/runtime-engine-integrations.js.map +1 -0
  295. package/dist/runtime-engine-operations.d.ts +87 -0
  296. package/dist/runtime-engine-operations.d.ts.map +1 -0
  297. package/dist/runtime-engine-operations.js +204 -0
  298. package/dist/runtime-engine-operations.js.map +1 -0
  299. package/dist/runtime-engine-resources.d.ts +18 -0
  300. package/dist/runtime-engine-resources.d.ts.map +1 -0
  301. package/dist/runtime-engine-resources.js +49 -0
  302. package/dist/runtime-engine-resources.js.map +1 -0
  303. package/dist/runtime-engine-run.d.ts +70 -0
  304. package/dist/runtime-engine-run.d.ts.map +1 -0
  305. package/dist/runtime-engine-run.js +202 -0
  306. package/dist/runtime-engine-run.js.map +1 -0
  307. package/dist/runtime-engine-scheduling.d.ts +22 -0
  308. package/dist/runtime-engine-scheduling.d.ts.map +1 -0
  309. package/dist/runtime-engine-scheduling.js +45 -0
  310. package/dist/runtime-engine-scheduling.js.map +1 -0
  311. package/dist/runtime-engine-tools.d.ts +8 -0
  312. package/dist/runtime-engine-tools.d.ts.map +1 -0
  313. package/dist/runtime-engine-tools.js +80 -0
  314. package/dist/runtime-engine-tools.js.map +1 -0
  315. package/dist/runtime-evolution.d.ts +17 -0
  316. package/dist/runtime-evolution.d.ts.map +1 -0
  317. package/dist/runtime-evolution.js +51 -0
  318. package/dist/runtime-evolution.js.map +1 -0
  319. package/dist/runtime-learning.d.ts +4 -6
  320. package/dist/runtime-learning.d.ts.map +1 -1
  321. package/dist/runtime-learning.js +109 -99
  322. package/dist/runtime-learning.js.map +1 -1
  323. package/dist/runtime-types.d.ts +21 -7
  324. package/dist/runtime-types.d.ts.map +1 -1
  325. package/dist/runtime-util.d.ts +4 -3
  326. package/dist/runtime-util.d.ts.map +1 -1
  327. package/dist/runtime-util.js +0 -7
  328. package/dist/runtime-util.js.map +1 -1
  329. package/dist/sandbox-fs.d.ts +2 -7
  330. package/dist/sandbox-fs.d.ts.map +1 -1
  331. package/dist/sandbox-fs.js +6 -37
  332. package/dist/sandbox-fs.js.map +1 -1
  333. package/dist/sandbox-handle.d.ts +1 -3
  334. package/dist/sandbox-handle.d.ts.map +1 -1
  335. package/dist/sandbox-handle.js +2 -5
  336. package/dist/sandbox-handle.js.map +1 -1
  337. package/dist/sandbox-state-sync.d.ts +2 -0
  338. package/dist/sandbox-state-sync.d.ts.map +1 -1
  339. package/dist/sandbox-state-sync.js +26 -2
  340. package/dist/sandbox-state-sync.js.map +1 -1
  341. package/dist/sandbox-tool-bridge.js +2 -3
  342. package/dist/sandbox-tool-bridge.js.map +1 -1
  343. package/dist/schedule-execution.d.ts +4 -4
  344. package/dist/schedule-execution.d.ts.map +1 -1
  345. package/dist/schedule-execution.js +45 -440
  346. package/dist/schedule-execution.js.map +1 -1
  347. package/dist/schedule-trigger-context.d.ts +18 -0
  348. package/dist/schedule-trigger-context.d.ts.map +1 -0
  349. package/dist/schedule-trigger-context.js +276 -0
  350. package/dist/schedule-trigger-context.js.map +1 -0
  351. package/dist/scheduler.d.ts +1 -14
  352. package/dist/scheduler.d.ts.map +1 -1
  353. package/dist/scheduler.js +1 -37
  354. package/dist/scheduler.js.map +1 -1
  355. package/dist/security.d.ts +0 -4
  356. package/dist/security.d.ts.map +1 -1
  357. package/dist/security.js +0 -22
  358. package/dist/security.js.map +1 -1
  359. package/dist/self-improvement.d.ts +5 -21
  360. package/dist/self-improvement.d.ts.map +1 -1
  361. package/dist/self-improvement.js +10 -40
  362. package/dist/self-improvement.js.map +1 -1
  363. package/dist/source-loader.d.ts +0 -3
  364. package/dist/source-loader.d.ts.map +1 -1
  365. package/dist/source-loader.js +1 -10
  366. package/dist/source-loader.js.map +1 -1
  367. package/dist/state/conversation-state.d.ts +117 -0
  368. package/dist/state/conversation-state.d.ts.map +1 -0
  369. package/dist/state/conversation-state.js +2 -0
  370. package/dist/state/conversation-state.js.map +1 -0
  371. package/dist/state/run-state.d.ts +129 -0
  372. package/dist/state/run-state.d.ts.map +1 -0
  373. package/dist/state/run-state.js +3 -0
  374. package/dist/state/run-state.js.map +1 -0
  375. package/dist/state/sandbox-memory-state.d.ts +159 -0
  376. package/dist/state/sandbox-memory-state.d.ts.map +1 -0
  377. package/dist/state/sandbox-memory-state.js +2 -0
  378. package/dist/state/sandbox-memory-state.js.map +1 -0
  379. package/dist/state/store-contracts.d.ts +329 -0
  380. package/dist/state/store-contracts.d.ts.map +1 -0
  381. package/dist/state/store-contracts.js +26 -0
  382. package/dist/state/store-contracts.js.map +1 -0
  383. package/dist/state/usage-state.d.ts +111 -0
  384. package/dist/state/usage-state.d.ts.map +1 -0
  385. package/dist/state/usage-state.js +2 -0
  386. package/dist/state/usage-state.js.map +1 -0
  387. package/dist/state/workspace-state.d.ts +126 -0
  388. package/dist/state/workspace-state.d.ts.map +1 -0
  389. package/dist/state/workspace-state.js +16 -0
  390. package/dist/state/workspace-state.js.map +1 -0
  391. package/dist/state-stores.d.ts +3 -15
  392. package/dist/state-stores.d.ts.map +1 -1
  393. package/dist/state-stores.js +36 -82
  394. package/dist/state-stores.js.map +1 -1
  395. package/dist/state.d.ts +7 -1075
  396. package/dist/state.d.ts.map +1 -1
  397. package/dist/state.js +7 -88
  398. package/dist/state.js.map +1 -1
  399. package/dist/storage.d.ts +4 -64
  400. package/dist/storage.d.ts.map +1 -1
  401. package/dist/storage.js +5 -408
  402. package/dist/storage.js.map +1 -1
  403. package/dist/subagent-execution.d.ts +1 -2
  404. package/dist/subagent-execution.d.ts.map +1 -1
  405. package/dist/subagent-execution.js +2 -26
  406. package/dist/subagent-execution.js.map +1 -1
  407. package/dist/subagent-handoff.d.ts +13 -0
  408. package/dist/subagent-handoff.d.ts.map +1 -1
  409. package/dist/subagent-handoff.js +74 -9
  410. package/dist/subagent-handoff.js.map +1 -1
  411. package/dist/tool-apis.d.ts +2 -2
  412. package/dist/tool-apis.d.ts.map +1 -1
  413. package/dist/tool-apis.js +26 -14
  414. package/dist/tool-apis.js.map +1 -1
  415. package/dist/tool-execution-host.d.ts +121 -0
  416. package/dist/tool-execution-host.d.ts.map +1 -0
  417. package/dist/tool-execution-host.js +2 -0
  418. package/dist/tool-execution-host.js.map +1 -0
  419. package/dist/tool-execution.d.ts +5 -141
  420. package/dist/tool-execution.d.ts.map +1 -1
  421. package/dist/tool-execution.js +6 -395
  422. package/dist/tool-execution.js.map +1 -1
  423. package/dist/usage-reconciliation-support.d.ts +20 -0
  424. package/dist/usage-reconciliation-support.d.ts.map +1 -0
  425. package/dist/usage-reconciliation-support.js +95 -0
  426. package/dist/usage-reconciliation-support.js.map +1 -0
  427. package/dist/usage-reconciliation.d.ts +14 -35
  428. package/dist/usage-reconciliation.d.ts.map +1 -1
  429. package/dist/usage-reconciliation.js +17 -394
  430. package/dist/usage-reconciliation.js.map +1 -1
  431. package/dist/workspace-paths.d.ts +3 -0
  432. package/dist/workspace-paths.d.ts.map +1 -0
  433. package/dist/workspace-paths.js +13 -0
  434. package/dist/workspace-paths.js.map +1 -0
  435. package/dist/workspace-service.d.ts +0 -1
  436. package/dist/workspace-service.d.ts.map +1 -1
  437. package/dist/workspace-service.js +1 -4
  438. package/dist/workspace-service.js.map +1 -1
  439. package/package.json +3 -3
  440. package/dist/run-timeline.d.ts +0 -11
  441. package/dist/run-timeline.d.ts.map +0 -1
  442. package/dist/run-timeline.js +0 -2
  443. package/dist/run-timeline.js.map +0 -1
  444. package/dist/trusted-attachments.d.ts +0 -20
  445. package/dist/trusted-attachments.d.ts.map +0 -1
  446. package/dist/trusted-attachments.js +0 -276
  447. package/dist/trusted-attachments.js.map +0 -1
@@ -1,178 +1,52 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import { randomUUID } from "node:crypto";
3
- import { ConnectionAuthorizationFailedError, ConnectionAuthorizationRequiredError, isConnectionAuthorizationFailedError, isConnectionAuthorizationRequiredError, resolveSandboxPath, shellQuote } from "@assemblyline-agents/core";
4
- import { OAUTH_STANDARD_TOKEN_FIELDS, buildOAuthChallenge, connectionPrincipalFor, connectionPrincipalKey, grantToTokenResult, isTokenExpired, normalizeAuthorization, randomUrlToken, resolveHeadersDefinition, storedGrantExpired, tokenCacheKey } from "./connection-auth-util.js";
5
- import { HttpApiConnectionClient, A2AConnectionClient, CredentialConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, SdkApiConnectionClient, readResponseBody } from "./connection-clients.js";
6
- import { SandboxCliConnectionClient } from "./sandbox-cli-client.js";
7
1
  import { audienceAllowsConnectionAccess } from "./audience.js";
8
- import { InMemoryConnectionAuthorizationSessionStore, InMemoryConnectionGrantStore } from "./connection-stores.js";
9
- import { connectionToolAccess, connectionToolApproval, isLiveConnection, isRecord, liveDefinitionUrl, parseQualifiedName, scoreMatch, stringValue, timestamp, tokenize } from "./connection-util.js";
10
- import { CredentialBroker } from "./credential-broker.js";
2
+ import { ConnectionAuthorizationService } from "./connection-authorization-service.js";
3
+ import { connectionPrincipalFor, connectionPrincipalKey, normalizeAuthorization } from "./connection-auth-util.js";
4
+ import { A2AConnectionClient, CredentialConnectionClient, HttpApiConnectionClient, McpConnectionClient, OpenApiConnectionClient, RelayMcpConnectionClient, SdkApiConnectionClient } from "./connection-clients.js";
5
+ import { materializeCredentialsWithContext } from "./connection-credential-materialization.js";
6
+ import { loadResolvedConnection } from "./connection-definition-loader.js";
7
+ import { searchConnections } from "./connection-discovery.js";
8
+ import { CONNECTION_PAIR_TOOL_NAME, ConnectionConfigurationError } from "./connection-runtime-types.js";
9
+ import { connectionToolAccess, connectionToolApproval, isLiveConnection, isRecord, liveDefinitionUrl, parseQualifiedName } from "./connection-util.js";
10
+ import { SandboxCliConnectionClient } from "./sandbox-cli-client.js";
11
11
  import { CredentialTransferConnectionClient } from "./credential-transfer-client.js";
12
- import { createConnectionAssetApi } from "./connection-assets.js";
13
12
  export * from "./connection-auth-util.js";
14
13
  export * from "./connection-clients.js";
14
+ export * from "./connection-credential-materialization.js";
15
+ export * from "./connection-runtime-types.js";
15
16
  export * from "./connection-stores.js";
16
17
  export * from "./connection-util.js";
17
18
  export * from "./computer-relay-protocol.js";
18
- export class ConnectionConfigurationError extends Error {
19
- constructor(message) {
20
- super(message);
21
- this.name = "ConnectionConfigurationError";
22
- }
23
- }
24
- export function isConnectionConfigurationError(error) {
25
- return error instanceof ConnectionConfigurationError;
26
- }
27
- const MATERIALIZED_CREDENTIAL_ROOT = "/workspace/.assembly-line/credentials/";
28
- // Compose the channel-visible response for a run parked on connection
29
- // authorization. The generic "authorization required" string is useless to the
30
- // human — the challenge URL is the thing they need to click.
31
- export async function connectionAuthorizationPrompt(store, authorizationSessionId, baseResponse) {
32
- const session = authorizationSessionId ? await store.getSession(authorizationSessionId) : undefined;
33
- const url = session && typeof session.challenge.url === "string" ? session.challenge.url : undefined;
34
- if (!url)
35
- return baseResponse || "Connection authorization required.";
36
- const prompt = `Authorization required for ${session?.connectionName ?? "a connection"} — approve here: ${url}`;
37
- return baseResponse ? `${baseResponse}\n\n${prompt}` : prompt;
38
- }
39
- /**
40
- * Project declared connection credentials into a freshly acquired sandbox.
41
- * Tokens resolve host-side (cached grant or refresh exchange — the grant
42
- * store is updated before anything reaches the sandbox), so recycling the
43
- * sandbox never loses a durable credential and never holds one. Subagent
44
- * sandboxes are excluded: they receive only their engine harness credential,
45
- * never the parent's connections. The emitted event carries paths only —
46
- * credential contents never reach the event log.
47
- */
48
- export async function materializeConnectionCredentialsIntoSandbox(run, registry, sandbox, appendEvent) {
49
- if (run.input.channel === "subagent" || typeof run.input.parentRunId === "string")
50
- return;
51
- const { materialized, pending } = await registry.materializeCredentials({
52
- id: run.id,
53
- input: run.input
54
- });
55
- if (materialized.length === 0 && pending.length === 0)
56
- return;
57
- for (const credential of materialized) {
58
- for (const file of credential.files) {
59
- assertMaterializedCredentialPath(credential.connection, file.path);
60
- await sandbox.writeFile(file.path, file.content);
61
- const physicalPath = resolveSandboxPath(sandbox.root, file.path);
62
- await sandbox.shell(`chmod ${file.mode.toString(8)} -- ${shellQuote(physicalPath)}`);
63
- }
64
- for (const command of credential.commands ?? []) {
65
- await sandbox.shell(command);
66
- }
67
- }
68
- await appendEvent("connections.credentials_materialized", {
69
- materialized: materialized.map((credential) => ({
70
- connection: credential.connection,
71
- files: credential.files.map((file) => file.path),
72
- ...(credential.audit ? { audit: credential.audit } : {})
73
- })),
74
- pending
75
- });
76
- }
77
- function assertMaterializedCredentialPath(connectionName, path) {
78
- const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
79
- if (normalized.split("/").includes("..") || !normalized.startsWith(MATERIALIZED_CREDENTIAL_ROOT)) {
80
- throw new ConnectionConfigurationError(`Connection ${connectionName} materialize files must be under ${MATERIALIZED_CREDENTIAL_ROOT}`);
81
- }
82
- }
83
- function assertRedactedAuditMetadata(connectionName, audit) {
84
- const visit = (value, path) => {
85
- if (Array.isArray(value)) {
86
- value.forEach((item, index) => visit(item, `${path}[${index}]`));
87
- return;
88
- }
89
- if (!value || typeof value !== "object")
90
- return;
91
- for (const [key, item] of Object.entries(value)) {
92
- if (/(?:token|secret|password|authorization|credential)/iu.test(key)) {
93
- throw new ConnectionConfigurationError(`Connection ${connectionName} audit metadata contains forbidden secret-like key ${path}.${key}.`);
94
- }
95
- visit(item, `${path}.${key}`);
96
- }
97
- };
98
- visit(audit, "audit");
99
- }
100
- export const CONNECTION_PAIR_TOOL_NAME = "pair";
101
- // The synthetic host-side pairing tool advertised for connections that define
102
- // redeemPairingCode. The input field is named `secret` so tool-call event
103
- // logging redacts the pasted one-time code automatically.
104
- function pairingToolItem(connection) {
105
- return {
106
- connection: connection.name,
107
- qualifiedName: `${connection.name}__${CONNECTION_PAIR_TOOL_NAME}`,
108
- tool: CONNECTION_PAIR_TOOL_NAME,
109
- description: `Pair ${connection.name} by redeeming a one-time pairing packet or claim code. When the user pastes binding/pairing instructions for ${connection.name}, call this with the FULL pasted text as \`secret\`; the host exchanges it and stores the credential durably. Only call this for a supplied packet; the tool's presence does not mean an existing grant is missing.`,
110
- inputSchema: {
111
- type: "object",
112
- additionalProperties: false,
113
- properties: {
114
- secret: {
115
- type: "string",
116
- description: "The full pasted pairing packet, binding instructions, or one-time claim code."
117
- }
118
- },
119
- required: ["secret"]
120
- }
121
- };
122
- }
123
- const CREDENTIAL_TEMPLATE_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_.]*)\s*\}\}/gu;
124
- function templateUsesToken(template) {
125
- return [...template.matchAll(CREDENTIAL_TEMPLATE_PATTERN)].some((match) => match[1] === "token");
126
- }
127
- // An unrendered placeholder in a credentials file is a silent misconfiguration
128
- // (the CLI reads garbage), so unknown keys fail loudly instead of passing through.
129
- function renderCredentialTemplate(template, values, connectionName) {
130
- return template.replace(CREDENTIAL_TEMPLATE_PATTERN, (_match, key) => {
131
- const value = values[key];
132
- if (value === undefined) {
133
- throw new Error(`Connection ${connectionName} materialize template references unknown placeholder {{${key}}}.`);
134
- }
135
- return value;
136
- });
137
- }
19
+ export * from "./credential-broker.js";
20
+ export * from "./credential-transfer-client.js";
21
+ /** Registry for discovery, execution, credential materialization, and authorization. */
138
22
  export class RuntimeConnectionRegistry {
139
23
  options;
140
24
  resolved = new Map();
141
25
  clients = new Map();
142
- tokenCache = new Map();
143
- executionContext = new AsyncLocalStorage();
144
- authorizationIntent = new AsyncLocalStorage();
145
- pendingAuthorizationCreations = new Map();
146
- grantStore;
147
- authorizationSessionStore;
148
- credentialBroker;
26
+ authorization;
149
27
  constructor(options) {
150
28
  this.options = options;
151
- this.grantStore = options.grantStore ?? options.credentialBroker?.grantStore ?? new InMemoryConnectionGrantStore();
152
- this.credentialBroker = options.credentialBroker ?? new CredentialBroker({
153
- store: { resolve: async () => undefined },
154
- grantStore: this.grantStore
155
- });
156
- this.authorizationSessionStore = options.authorizationSessionStore ?? new InMemoryConnectionAuthorizationSessionStore();
29
+ this.authorization = new ConnectionAuthorizationService(options, (connection, session) => this.resolveConnection(connection, session));
30
+ }
31
+ pluginRuntimePaths(connectionName) {
32
+ return this.options.pluginRuntime?.[connectionName];
157
33
  }
158
34
  hasLiveConnections() {
159
35
  return this.options.connections.some(isLiveConnection);
160
36
  }
161
37
  liveConnectionNames() {
162
- return this.options.connections
163
- .filter(isLiveConnection)
164
- .map((connection) => connection.name);
38
+ return this.options.connections.filter(isLiveConnection).map((connection) => connection.name);
165
39
  }
166
- /** Resolve the host-only event adapter without exposing it as a model tool. */
167
40
  async eventBindingContext(connectionName) {
168
41
  const compiled = this.options.connections.find((connection) => connection.name === connectionName);
169
42
  if (!compiled || !isLiveConnection(compiled)) {
170
43
  throw new ConnectionConfigurationError(`Connection ${connectionName} is not registered as a live connection.`);
171
44
  }
172
45
  const definition = (await this.resolveConnection(compiled)).definition;
173
- if (!definition.events)
46
+ if (!definition.events) {
174
47
  throw new ConnectionConfigurationError(`Connection ${connectionName} does not accept provider events.`);
175
- const context = this.resolverContext(compiled);
48
+ }
49
+ const context = this.authorization.resolverContext(compiled);
176
50
  let principal;
177
51
  if (definition.events.scope === "connection") {
178
52
  principal = { type: "app" };
@@ -199,99 +73,26 @@ export class RuntimeConnectionRegistry {
199
73
  config: context.config,
200
74
  credentials: context.credentials,
201
75
  getToken: async () => {
202
- if (!definition.auth)
76
+ if (!definition.auth) {
203
77
  throw new ConnectionConfigurationError(`Connection ${connectionName} event adapter requested a token without connection auth.`);
204
- return this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
78
+ }
79
+ return this.authorization.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
205
80
  }
206
81
  };
207
82
  }
208
83
  async search(input, execution) {
209
- return this.authorizationIntent.run("discover", () => this.withExecutionContext(execution, () => this.searchWithContext(input)));
210
- }
211
- async searchWithContext(input) {
212
- const keywords = input.keywords ?? "";
213
- const limit = input.limit ?? 10;
214
- const target = this.targetConnections(input.connection);
215
- const queryTokens = tokenize(keywords);
216
- const scored = [];
217
- const failures = [];
218
- const privateOnly = [];
219
- // Pairing tools surface even when the connection cannot authenticate yet —
220
- // redeeming a pasted packet is exactly how an unauthorized connection
221
- // becomes authorized, so it must not hide behind the failure path.
222
- const pairable = [];
223
- for (const connection of target) {
224
- // A personal connection stays registered in a shared run, but its tool
225
- // catalog, pairing, and authorization state are private-surface only.
226
- if (!this.connectionAccessible(connection)) {
227
- privateOnly.push({
228
- connection: connection.name,
229
- description: `${connection.name} is registered. Its personal tools, pairing, and authorization status are available only on a private surface such as a direct message; this does not mean a durable grant is missing.`,
230
- requiresPrivateAudience: true
231
- });
232
- continue;
233
- }
234
- let metadata;
235
- try {
236
- if (await this.supportsPairing(connection)) {
237
- pairable.push(pairingToolItem(connection));
238
- }
239
- metadata = await this.getClient(connection.name).getToolMetadata();
240
- }
241
- catch (error) {
242
- failures.push(this.connectionFailure(connection, error));
243
- continue;
244
- }
245
- for (const tool of metadata) {
246
- const score = keywords.trim() ? scoreMatch(queryTokens, tool) : 1;
247
- if (score <= 0)
248
- continue;
249
- const item = {
250
- connection: tool.connection,
251
- description: tool.description,
252
- inputSchema: tool.inputSchema,
253
- qualifiedName: tool.qualifiedName,
254
- tool: tool.name
255
- };
256
- if (tool.outputSchema)
257
- item.outputSchema = tool.outputSchema;
258
- scored.push({
259
- score,
260
- item
261
- });
262
- }
263
- }
264
- scored.sort((a, b) => b.score - a.score);
265
- const matches = scored.slice(0, limit).map((entry) => entry.item);
266
- if (matches.length > 0)
267
- return [...pairable, ...matches, ...privateOnly, ...failures];
268
- const privateConnectionNames = new Set(privateOnly.map((item) => item.connection));
269
- return [
270
- ...pairable,
271
- ...privateOnly,
272
- ...target.map((connection) => ({
273
- connection: connection.name,
274
- description: connection.description ?? `${connection.name} connection`
275
- })).filter((item) => !privateConnectionNames.has(item.connection)),
276
- ...failures
277
- ];
84
+ return this.authorization.runWithIntent("discover", () => this.authorization.withExecutionContext(execution, () => searchConnections(input, {
85
+ targetConnections: (name) => this.targetConnections(name),
86
+ connectionAccessible: (connection) => this.connectionAccessible(connection),
87
+ supportsPairing: (connection) => this.supportsPairing(connection),
88
+ toolMetadata: async (connectionName) => this.getClient(connectionName).getToolMetadata()
89
+ })));
278
90
  }
279
- // Deliberately does not swallow load errors: a definition module that fails
280
- // to resolve (for example a deployed artifact missing the connection's
281
- // package) must surface the real cause instead of silently hiding the
282
- // pairing tool that would otherwise be the recovery path.
283
91
  async supportsPairing(connection) {
284
92
  return typeof (await this.resolveConnection(connection)).definition.redeemPairingCode === "function";
285
93
  }
286
- /**
287
- * Report which live connections can redeem a pairing code right now, and
288
- * which cannot because their definition failed to resolve (missing package,
289
- * unset env, platform mismatch). Backs the core `pair` tool: a connection
290
- * that cannot even load must still be *diagnosable* from the model — the
291
- * resolve failure is the answer the user needs, not a hidden tool.
292
- */
293
94
  async pairingStatus() {
294
- return this.authorizationIntent.run("discover", async () => {
95
+ return this.authorization.runWithIntent("discover", async () => {
295
96
  const pairable = [];
296
97
  const unavailable = [];
297
98
  for (const connection of this.options.connections) {
@@ -311,11 +112,6 @@ export class RuntimeConnectionRegistry {
311
112
  return { pairable, unavailable };
312
113
  });
313
114
  }
314
- /**
315
- * Redeem a user-provided one-time pairing code host-side and persist the
316
- * resulting credentials in the grant store. The durable credential never
317
- * enters a sandbox; the model only relays the single-use code.
318
- */
319
115
  async redeemPairingCode(connectionName, code) {
320
116
  const compiled = this.options.connections.find((connection) => connection.name === connectionName);
321
117
  if (!compiled || !isLiveConnection(compiled)) {
@@ -330,15 +126,14 @@ export class RuntimeConnectionRegistry {
330
126
  throw new Error(`Connection ${connectionName} does not support pairing codes.`);
331
127
  if (!code.trim())
332
128
  throw new Error("A pairing code is required.");
333
- const context = this.resolverContext(compiled);
129
+ const context = this.authorization.resolverContext(compiled);
334
130
  const token = await redeem({ code }, context);
335
131
  const authorization = definition.auth
336
132
  ? await normalizeAuthorization(definition.auth, context)
337
133
  : { kind: "token", principalType: "user", getToken: async () => token };
338
134
  const principal = connectionPrincipalFor(compiled, authorization, context);
339
135
  const principalKey = connectionPrincipalKey(principal);
340
- await this.saveGrantFromToken(compiled.name, principal, principalKey, token);
341
- this.tokenCache.delete(tokenCacheKey(compiled.name, principal));
136
+ await this.authorization.storeToken(compiled, principal, principalKey, token);
342
137
  return {
343
138
  connected: true,
344
139
  connection: compiled.name,
@@ -346,7 +141,7 @@ export class RuntimeConnectionRegistry {
346
141
  };
347
142
  }
348
143
  async metadataForQualifiedName(qualifiedName) {
349
- return this.authorizationIntent.run("discover", async () => {
144
+ return this.authorization.runWithIntent("discover", async () => {
350
145
  const parsed = parseQualifiedName(qualifiedName);
351
146
  if (!parsed)
352
147
  return undefined;
@@ -365,16 +160,11 @@ export class RuntimeConnectionRegistry {
365
160
  if (!compiled || !this.toolAllowed(compiled)) {
366
161
  throw new Error(`Connection tool ${qualifiedName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
367
162
  }
368
- // "<connection>__pair" is the synthetic host-side pairing tool; it must
369
- // run before any client/auth resolution (the whole point is that the
370
- // connection is not authorized yet).
371
- if (parsed.toolName === CONNECTION_PAIR_TOOL_NAME) {
372
- if (compiled && await this.supportsPairing(compiled)) {
373
- const secret = isRecord(input) && typeof input.secret === "string" ? input.secret : "";
374
- return this.withExecutionContext(execution, () => this.redeemPairingCode(parsed.connectionName, secret));
375
- }
163
+ if (parsed.toolName === CONNECTION_PAIR_TOOL_NAME && await this.supportsPairing(compiled)) {
164
+ const secret = isRecord(input) && typeof input.secret === "string" ? input.secret : "";
165
+ return this.authorization.withExecutionContext(execution, () => this.redeemPairingCode(parsed.connectionName, secret));
376
166
  }
377
- return this.withExecutionContext(execution, () => this.getClient(parsed.connectionName).executeTool(parsed.toolName, input));
167
+ return this.authorization.withExecutionContext(execution, () => this.getClient(parsed.connectionName).executeTool(parsed.toolName, input));
378
168
  }
379
169
  async getConnectionApproval(connectionName, toolName) {
380
170
  const compiled = this.options.connections.find((connection) => connection.name === connectionName);
@@ -383,10 +173,6 @@ export class RuntimeConnectionRegistry {
383
173
  if (!this.toolAllowed(compiled)) {
384
174
  throw new Error(`Connection tool ${connectionName}__${toolName} is unavailable here: personal connections work only on a private surface such as a direct message.`);
385
175
  }
386
- // Pairing is a synthetic host-side bootstrap operation, not a provider
387
- // tool. It must bypass the provider connection's ordinary read/write
388
- // classification or discovery will advertise a tool that execution then
389
- // rejects before the redeemer can run.
390
176
  if (toolName === CONNECTION_PAIR_TOOL_NAME) {
391
177
  if (!await this.supportsPairing(compiled)) {
392
178
  throw new Error(`Connection ${connectionName} does not support pairing codes.`);
@@ -432,6 +218,12 @@ export class RuntimeConnectionRegistry {
432
218
  this.clients.set(connectionName, client);
433
219
  return client;
434
220
  }
221
+ async executeSandboxCli(invocation) {
222
+ if (!this.options.executeSandboxCli) {
223
+ throw new Error("Sandbox CLI connection execution requires an active agent run sandbox.");
224
+ }
225
+ return this.options.executeSandboxCli(invocation, this.authorization.currentExecutionContext());
226
+ }
435
227
  async transferCredential(input) {
436
228
  const source = this.options.connections.find((connection) => connection.name === input.sourceConnection);
437
229
  const sink = this.options.connections.find((connection) => connection.name === input.sinkConnection);
@@ -444,8 +236,8 @@ export class RuntimeConnectionRegistry {
444
236
  const sinkCapability = "credentialSink" in sinkDefinition ? sinkDefinition.credentialSink : undefined;
445
237
  if (!sourceCapability || !sinkCapability)
446
238
  throw new Error("Credential transfer source or sink is not configured.");
447
- const principal = this.connectionPrincipal(sink);
448
- const execution = this.executionContext.getStore();
239
+ const principal = this.authorization.connectionPrincipal(sink);
240
+ const execution = this.authorization.currentExecutionContext();
449
241
  const runId = execution?.runId ?? this.options.runId;
450
242
  const identity = {
451
243
  principal,
@@ -462,7 +254,7 @@ export class RuntimeConnectionRegistry {
462
254
  try {
463
255
  const value = await sourceCapability.resolve(input.reference, context);
464
256
  await sinkCapability.consume(value, input.target, context);
465
- await this.credentialBroker.recordTransfer({
257
+ await this.authorization.credentialBroker.recordTransfer({
466
258
  credential: input.reference,
467
259
  connection: sink.name,
468
260
  identity,
@@ -472,7 +264,7 @@ export class RuntimeConnectionRegistry {
472
264
  return { status: "filled" };
473
265
  }
474
266
  catch {
475
- await this.credentialBroker.recordTransfer({
267
+ await this.authorization.credentialBroker.recordTransfer({
476
268
  credential: input.reference,
477
269
  connection: sink.name,
478
270
  identity,
@@ -482,303 +274,31 @@ export class RuntimeConnectionRegistry {
482
274
  throw new Error(`Credential transfer from ${source.name} to ${sink.name} failed.`);
483
275
  }
484
276
  }
485
- async executeSandboxCli(invocation) {
486
- if (!this.options.executeSandboxCli) {
487
- throw new Error("Sandbox CLI connection execution requires an active agent run sandbox.");
488
- }
489
- return this.options.executeSandboxCli(invocation, this.executionContext.getStore());
490
- }
491
277
  async resolveHeaders(connection, definition) {
492
- const headers = {};
493
- const context = this.resolverContext(connection);
494
- if (definition.auth !== undefined) {
495
- const token = await this.resolveToken(connection, definition.auth, context, liveDefinitionUrl(definition));
496
- const tokenType = token.tokenType?.trim() || "Bearer";
497
- headers.Authorization = `${tokenType} ${token.token}`;
498
- }
499
- if (definition.headers !== undefined) {
500
- const resolved = await resolveHeadersDefinition(definition.headers, context);
501
- for (const [key, value] of Object.entries(resolved)) {
502
- if (key.toLowerCase() === "authorization" && headers.Authorization !== undefined) {
503
- throw new Error(`Connection ${connection.name} headers must not include Authorization when auth is configured.`);
504
- }
505
- headers[key] = value;
506
- }
507
- }
508
- return headers;
278
+ return this.authorization.resolveHeaders(connection, definition);
509
279
  }
510
- /**
511
- * Start (or resume) the authorization flow for a named connection and
512
- * report where to send the user's browser. Backs the host's authenticated
513
- * GET /assembly-line/connections/authorize endpoint (agent-control route class,
514
- * JSON — deliberately not a public 302, so unauthenticated callers cannot
515
- * trigger consent flows or enumerate connection names): the dashboard
516
- * fetches the URL and opens it; the provider redirects back to the public,
517
- * idempotency-claimed callback. Hitting it when a grant already exists
518
- * reports "connected" instead of minting a redundant session; a pending
519
- * session's challenge is reused.
520
- */
521
280
  async beginAuthorization(connectionName) {
522
- return this.authorizationIntent.run("authorize", () => this.beginAuthorizationWithContext(connectionName));
281
+ return this.authorization.beginAuthorization(connectionName);
523
282
  }
524
- async beginAuthorizationWithContext(connectionName) {
525
- const compiled = this.options.connections.find((connection) => connection.name === connectionName);
526
- if (!compiled || !isLiveConnection(compiled))
527
- return { status: "unavailable", reason: "unknown_connection" };
528
- let definition;
529
- try {
530
- definition = (await this.resolveConnection(compiled)).definition;
531
- }
532
- catch (error) {
533
- return { status: "unavailable", reason: error instanceof Error ? error.message : String(error) };
534
- }
535
- if (!definition.auth)
536
- return { status: "unavailable", reason: "connection_has_no_auth" };
537
- try {
538
- await this.resolveToken(compiled, definition.auth, this.resolverContext(compiled), liveDefinitionUrl(definition));
539
- return { status: "connected" };
540
- }
541
- catch (error) {
542
- if (isConnectionAuthorizationRequiredError(error)) {
543
- const url = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
544
- return url ? { status: "authorize", url } : { status: "unavailable", reason: error.reason };
545
- }
546
- return { status: "unavailable", reason: error instanceof Error ? error.message : String(error) };
547
- }
548
- }
549
- /**
550
- * Resolve every connection that declares `materialize` and render its
551
- * credential files for injection into the agent sandbox. Tokens resolve
552
- * through the normal host-side flow — cached grant, or a refresh exchange
553
- * that persists the rotated grant to the grant store BEFORE anything is
554
- * rendered — so the sandbox only ever sees the short-lived access token.
555
- *
556
- * A connection that cannot resolve is reported as pending unless the run
557
- * explicitly requested that credential. Explicit requests fail closed so a
558
- * run can never continue after a missing, revoked, or mismatched credential
559
- * it declared as required. Merely making a credential connection available
560
- * to the agent must not prevent unrelated sandbox work.
561
- */
562
283
  async materializeCredentials(run) {
563
- return this.authorizationIntent.run("discover", () => this.materializeCredentialsWithContext(run));
564
- }
565
- async materializeCredentialsWithContext(run) {
566
- const materialized = [];
567
- const pending = [];
568
- for (const compiled of this.options.connections) {
569
- if (!this.toolAllowed(compiled))
570
- continue;
571
- const credentialRequested = sandboxCredentialWasRequested(run, compiled.name);
572
- // Credential-file connections are leases, not ambient sandbox setup.
573
- // Merely configuring one on a root agent must never resolve or project
574
- // its secret into an unrelated sandbox acquisition.
575
- if (compiled.protocol === "credential" && !credentialRequested)
576
- continue;
577
- const failClosed = compiled.protocol === "credential" && credentialRequested;
578
- let context;
579
- let definition;
580
- try {
581
- context = this.resolverContext(compiled);
582
- definition = (await this.resolveConnection(compiled)).definition;
583
- }
584
- catch (error) {
585
- if (failClosed)
586
- throw error;
587
- pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
588
- continue;
589
- }
590
- const spec = definition.materialize;
591
- if (!spec || ((spec.files?.length ?? 0) === 0 && !spec.resolve))
592
- continue;
593
- const staticFiles = spec.files ?? [];
594
- const needsToken = staticFiles.some((file) => templateUsesToken(file.path) || templateUsesToken(file.content));
595
- let resolvedToken = { token: "" };
596
- if (needsToken) {
597
- if (!definition.auth) {
598
- pending.push({ connection: compiled.name, reason: "materialize templates use {{token}} but the connection has no auth" });
599
- continue;
600
- }
601
- try {
602
- resolvedToken = await this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
603
- }
604
- catch (error) {
605
- if (failClosed)
606
- throw error;
607
- if (isConnectionAuthorizationRequiredError(error)) {
608
- const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
609
- pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
610
- continue;
611
- }
612
- const reason = isConnectionAuthorizationFailedError(error)
613
- ? `${error.reason}: ${error.message}`
614
- : error instanceof Error
615
- ? error.message
616
- : String(error);
617
- pending.push({ connection: compiled.name, reason });
618
- continue;
619
- }
620
- }
621
- let files = staticFiles;
622
- let commands = [];
623
- let audit;
624
- if (spec.resolve) {
625
- if (!run) {
626
- pending.push({ connection: compiled.name, reason: "run_context_required" });
627
- continue;
628
- }
629
- let lazyToken;
630
- try {
631
- const authorization = definition.auth
632
- ? await normalizeAuthorization(definition.auth, context)
633
- : undefined;
634
- const principal = authorization
635
- ? connectionPrincipalFor(compiled, authorization, context)
636
- : { type: "app" };
637
- const resolved = await spec.resolve({
638
- connectionName: compiled.name,
639
- agentId: this.options.agentId ?? "",
640
- run,
641
- principal,
642
- session: context.session,
643
- config: context.config,
644
- credentials: context.credentials,
645
- fetch: this.options.fetch,
646
- getToken: async () => {
647
- if (!definition.auth)
648
- throw new Error(`Connection ${compiled.name} materialization requested a token without auth.`);
649
- lazyToken ??= this.resolveToken(compiled, definition.auth, context, liveDefinitionUrl(definition));
650
- return lazyToken;
651
- }
652
- });
653
- if (!resolved)
654
- continue;
655
- files = resolved.files;
656
- commands = resolved.commands ?? [];
657
- audit = resolved.audit;
658
- if (audit)
659
- assertRedactedAuditMetadata(compiled.name, audit);
660
- }
661
- catch (error) {
662
- if (failClosed)
663
- throw error;
664
- if (isConnectionAuthorizationRequiredError(error)) {
665
- const authorizeUrl = typeof error.challenge?.url === "string" ? error.challenge.url : undefined;
666
- pending.push({ connection: compiled.name, reason: `authorization_required: ${error.reason}`, ...(authorizeUrl ? { authorizeUrl } : {}) });
667
- continue;
668
- }
669
- const reason = isConnectionAuthorizationFailedError(error)
670
- ? `${error.reason}: ${error.message}`
671
- : error instanceof Error
672
- ? error.message
673
- : String(error);
674
- pending.push({ connection: compiled.name, reason });
675
- continue;
676
- }
677
- }
678
- const values = { token: resolvedToken.token };
679
- // Extra token-response fields (captured into the grant's credential bag)
680
- // are addressable as {{credential.<key>}} — e.g. a provider-issued user
681
- // or account id a sandbox-side CLI needs to build API paths.
682
- for (const [key, value] of Object.entries(resolvedToken.credential ?? {})) {
683
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
684
- values[`credential.${key}`] = String(value);
685
- }
686
- }
687
- try {
688
- materialized.push({
689
- connection: compiled.name,
690
- files: files.map((file) => ({
691
- path: renderCredentialTemplate(file.path, values, compiled.name),
692
- content: renderCredentialTemplate(file.content, values, compiled.name),
693
- mode: file.mode ?? 0o600
694
- })),
695
- ...(commands.length > 0 ? { commands } : {}),
696
- ...(audit ? { audit } : {})
697
- });
698
- }
699
- catch (error) {
700
- if (failClosed)
701
- throw error;
702
- pending.push({ connection: compiled.name, reason: error instanceof Error ? error.message : String(error) });
703
- }
704
- }
705
- return { materialized, pending };
284
+ return this.authorization.runWithIntent("discover", () => materializeCredentialsWithContext(this.options, run, {
285
+ toolAllowed: (connection) => this.toolAllowed(connection),
286
+ resolveConnection: (connection) => this.resolveConnection(connection),
287
+ resolveToken: (connection, auth, context, resolvedUrl) => this.authorization.resolveToken(connection, auth, context, resolvedUrl),
288
+ resolverContext: (connection) => this.authorization.resolverContext(connection)
289
+ }));
706
290
  }
707
291
  async evictToken(connection, definition) {
708
- if (!definition.auth)
709
- return;
710
- const context = this.resolverContext(connection);
711
- const authorization = await normalizeAuthorization(definition.auth, context);
712
- const principal = connectionPrincipalFor(connection, authorization, context);
713
- const cacheKey = tokenCacheKey(connection.name, principal);
714
- const principalKey = connectionPrincipalKey(principal);
715
- const cached = this.tokenCache.get(cacheKey);
716
- this.tokenCache.delete(cacheKey);
717
- if (cached?.authorization.kind === "oauth" || authorization.kind === "oauth") {
718
- await this.grantStore.deleteGrant(connection.name, principalKey);
719
- }
720
- await (cached?.authorization ?? authorization).evict?.({ principal, connection: { url: connection.url ?? "" } });
292
+ return this.authorization.evictToken(connection, definition);
721
293
  }
722
294
  async completeAuthorization(callback) {
723
- const state = callback.params.state;
724
- if (!state)
725
- throw new ConnectionAuthorizationFailedError("unknown", { reason: "missing_state", retryable: false });
726
- const session = await this.authorizationSessionStore.getSessionByState(state);
727
- if (!session)
728
- throw new ConnectionAuthorizationFailedError("unknown", { reason: "unknown_state", retryable: false });
729
- // Provider callbacks are routinely retried. Once the grant was saved and
730
- // the session completed, return the durable result without attempting to
731
- // exchange the one-time authorization code again.
732
- if (session.status === "completed")
733
- return session;
734
- if (session.expiresAt <= Date.now()) {
735
- await this.authorizationSessionStore.updateSession(session.id, { status: "expired" });
736
- throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "authorization_expired", retryable: true });
737
- }
738
- if (callback.params.error) {
739
- await this.authorizationSessionStore.updateSession(session.id, { status: "denied" });
740
- throw new ConnectionAuthorizationFailedError(session.connectionName, {
741
- reason: callback.params.error,
742
- retryable: true,
743
- message: callback.params.error_description ?? `Connection ${session.connectionName} authorization was denied.`
744
- });
745
- }
746
- const compiled = this.options.connections.find((connection) => connection.name === session.connectionName);
747
- if (!compiled || !isLiveConnection(compiled)) {
748
- throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "connection_not_found", retryable: false });
749
- }
750
- const resolved = await this.resolveConnection(compiled);
751
- if (!resolved.definition.auth) {
752
- throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "auth_not_configured", retryable: false });
753
- }
754
- const authorization = await normalizeAuthorization(resolved.definition.auth, this.resolverContext(compiled, session.session));
755
- const connectionContext = { url: compiled.url ?? "" };
756
- let token;
757
- if (authorization.kind === "oauth" && authorization.oauth) {
758
- token = await this.exchangeOAuthAuthorizationCode(authorization.oauth, session, callback);
759
- }
760
- else if (authorization.completeAuthorization) {
761
- const completeOptions = {
762
- principal: session.principal,
763
- connection: connectionContext,
764
- callbackUrl: session.callbackUrl,
765
- callback
766
- };
767
- if (session.resume !== undefined)
768
- completeOptions.resume = session.resume;
769
- token = await authorization.completeAuthorization(completeOptions);
770
- }
771
- else {
772
- throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "callback_not_supported", retryable: false });
773
- }
774
- await this.saveGrantFromToken(compiled.name, session.principal, session.principalKey, token);
775
- return this.authorizationSessionStore.updateSession(session.id, { status: "completed" });
295
+ return this.authorization.completeAuthorization(callback);
776
296
  }
777
297
  async getAuthorizationSession(id) {
778
- return this.authorizationSessionStore.getSession(id);
298
+ return this.authorization.getAuthorizationSession(id);
779
299
  }
780
300
  async updateAuthorizationSession(id, patch) {
781
- return this.authorizationSessionStore.updateSession(id, patch);
301
+ return this.authorization.updateAuthorizationSession(id, patch);
782
302
  }
783
303
  async close() {
784
304
  await Promise.allSettled([...this.clients.values()].map((client) => client.close()));
@@ -788,7 +308,7 @@ export class RuntimeConnectionRegistry {
788
308
  return this.options.fetch(input, init);
789
309
  }
790
310
  async processEnvironment(connection) {
791
- const context = this.factoryContext(connection);
311
+ const context = this.authorization.factoryContext(connection);
792
312
  const environment = {};
793
313
  for (const name of connection.requiredConfig ?? []) {
794
314
  const value = context.config[name];
@@ -818,493 +338,27 @@ export class RuntimeConnectionRegistry {
818
338
  return environment;
819
339
  }
820
340
  async credentialValue(connection, name, purpose) {
821
- return this.factoryContext(connection).credentials.get(name, { purpose, delivery: "relay" });
341
+ return this.authorization.factoryContext(connection).credentials.get(name, { purpose, delivery: "relay" });
822
342
  }
823
- async resolveConnection(compiled) {
343
+ async resolveConnection(compiled, session) {
344
+ if (session) {
345
+ return loadResolvedConnection(this.options, compiled, this.authorization.factoryContext(compiled, session));
346
+ }
824
347
  const existing = this.resolved.get(compiled.name);
825
348
  if (existing)
826
349
  return existing;
827
- const promise = this.loadResolvedConnection(compiled);
350
+ const promise = loadResolvedConnection(this.options, compiled, this.authorization.factoryContext(compiled));
828
351
  this.resolved.set(compiled.name, promise);
829
352
  return promise;
830
353
  }
831
- async loadResolvedConnection(compiled) {
832
- const platform = this.options.platform ?? process.platform;
833
- if (compiled.hostRequirements?.platforms?.length && !compiled.hostRequirements.platforms.includes(platform)) {
834
- throw new Error(`${compiled.hostRequirements.message} Current runtime platform: ${platform}.`);
835
- }
836
- const loaded = await this.options.loadDefinition(compiled);
837
- const factoryContext = this.factoryContext(compiled);
838
- const dynamic = compiled.source.path.startsWith("dynamic://");
839
- const raw = !dynamic && isConnectionFactory(loaded)
840
- ? await loaded.create(factoryContext)
841
- : loaded;
842
- if (!isRecord(raw))
843
- throw new Error(`Connection ${compiled.name} did not export an object.`);
844
- if (compiled.protocol === "mcp") {
845
- if (raw.transport === "stdio") {
846
- if (compiled.source.path.startsWith("dynamic://")) {
847
- throw new Error(`Dynamic MCP connection ${compiled.name} cannot launch a stdio process.`);
848
- }
849
- if (typeof raw.command !== "string" || !raw.command.trim()) {
850
- throw new Error(`Stdio MCP connection ${compiled.name} must export command.`);
851
- }
852
- if (raw.args !== undefined && (!Array.isArray(raw.args) || raw.args.some((argument) => typeof argument !== "string"))) {
853
- throw new Error(`Stdio MCP connection ${compiled.name} args must contain only strings.`);
854
- }
855
- }
856
- else if (raw.transport === "relay") {
857
- if (compiled.source.path.startsWith("dynamic://")) {
858
- throw new Error(`Dynamic MCP connection ${compiled.name} cannot use a device relay.`);
859
- }
860
- if (typeof raw.url !== "string" || typeof raw.credential !== "string") {
861
- throw new Error(`Relay MCP connection ${compiled.name} must export url and credential.`);
862
- }
863
- }
864
- else if (typeof raw.url !== "string") {
865
- throw new Error(`HTTP MCP connection ${compiled.name} must export url.`);
866
- }
867
- if (typeof raw.description !== "string")
868
- throw new Error(`MCP connection ${compiled.name} must export description.`);
869
- return { compiled, definition: raw };
870
- }
871
- if (compiled.protocol === "openapi") {
872
- if (raw.spec === undefined)
873
- throw new Error(`OpenAPI connection ${compiled.name} must export spec.`);
874
- if (typeof raw.description !== "string")
875
- throw new Error(`OpenAPI connection ${compiled.name} must export description.`);
876
- return { compiled, definition: raw };
877
- }
878
- if (compiled.protocol === "a2a") {
879
- if (typeof raw.agentCardUrl !== "string")
880
- throw new Error(`A2A connection ${compiled.name} must export agentCardUrl.`);
881
- if (typeof raw.description !== "string")
882
- throw new Error(`A2A connection ${compiled.name} must export description.`);
883
- if (!raw.access || typeof raw.access !== "object")
884
- throw new Error(`A2A connection ${compiled.name} must export access.`);
885
- return { compiled, definition: raw };
886
- }
887
- if (compiled.protocol === "http") {
888
- if (typeof raw.baseUrl !== "string")
889
- throw new Error(`HTTP API connection ${compiled.name} must export baseUrl.`);
890
- if (typeof raw.description !== "string")
891
- throw new Error(`HTTP API connection ${compiled.name} must export description.`);
892
- if (!Array.isArray(raw.operations))
893
- throw new Error(`HTTP API connection ${compiled.name} must export operations.`);
894
- return { compiled, definition: raw };
895
- }
896
- if (compiled.protocol === "sdk") {
897
- if (compiled.source.path.startsWith("dynamic://")) {
898
- throw new Error(`Dynamic SDK API connection ${compiled.name} is not allowed.`);
899
- }
900
- if (typeof raw.description !== "string" || !Array.isArray(raw.operations) || typeof raw.execute !== "function") {
901
- throw new Error(`SDK API connection ${compiled.name} must export description, operations, and execute.`);
902
- }
903
- if (!raw.access || typeof raw.access !== "object") {
904
- throw new Error(`SDK API connection ${compiled.name} must export access.`);
905
- }
906
- return { compiled, definition: raw };
907
- }
908
- if (compiled.protocol === "cli") {
909
- if (compiled.source.path.startsWith("dynamic://")) {
910
- throw new Error(`Dynamic CLI connection ${compiled.name} is not allowed.`);
911
- }
912
- if (raw.transport !== "sandbox" || typeof raw.command !== "string" || !raw.command.trim()) {
913
- throw new Error(`Sandbox CLI connection ${compiled.name} must export transport "sandbox" and command.`);
914
- }
915
- if (typeof raw.description !== "string" || !Array.isArray(raw.tools)) {
916
- throw new Error(`Sandbox CLI connection ${compiled.name} must export description and tools.`);
917
- }
918
- return { compiled, definition: raw };
919
- }
920
- if (compiled.protocol === "credential") {
921
- if (typeof raw.description !== "string" || !raw.auth || !raw.materialize || typeof raw.materialize !== "object") {
922
- throw new Error(`Credential connection ${compiled.name} must export description, auth, and materialize.`);
923
- }
924
- return { compiled, definition: raw };
925
- }
926
- throw new Error(`Connection ${compiled.name} is declaration-only.`);
927
- }
928
354
  targetConnections(name) {
929
- return this.options.connections.filter((connection) => {
930
- if (!isLiveConnection(connection))
931
- return false;
932
- return !name || connection.name === name;
933
- });
355
+ return this.options.connections.filter((connection) => isLiveConnection(connection) && (!name || connection.name === name));
934
356
  }
935
- /**
936
- * The single audience rule: personal (user-subject) connections exist only
937
- * on private surfaces. Everything else — discovery, execution, pairing,
938
- * and credential materialization — derives from this one predicate.
939
- */
940
357
  connectionAccessible(connection) {
941
- const audience = this.options.audience ?? { private: true };
942
- return audienceAllowsConnectionAccess(audience, connection.subject);
358
+ return audienceAllowsConnectionAccess(this.options.audience ?? { private: true }, connection.subject);
943
359
  }
944
360
  toolAllowed(connection) {
945
361
  return isLiveConnection(connection) && this.connectionAccessible(connection);
946
362
  }
947
- connectionFailure(connection, error) {
948
- if (isConnectionAuthorizationRequiredError(error)) {
949
- return {
950
- connection: connection.name,
951
- description: connection.description ?? `${connection.name} connection`,
952
- needsAuthorization: true,
953
- error: error.reason
954
- };
955
- }
956
- if (isConnectionAuthorizationFailedError(error)) {
957
- return {
958
- connection: connection.name,
959
- description: connection.description ?? `${connection.name} connection`,
960
- error: `${error.reason}: ${error.message}`
961
- };
962
- }
963
- return {
964
- connection: connection.name,
965
- description: connection.description ?? `${connection.name} connection`,
966
- error: error instanceof Error ? error.message : String(error)
967
- };
968
- }
969
- async resolveToken(connection, auth, context, resolvedUrl) {
970
- const authorization = await normalizeAuthorization(auth, context);
971
- const principal = connectionPrincipalFor(connection, authorization, context);
972
- const principalKey = connectionPrincipalKey(principal);
973
- const cacheKey = tokenCacheKey(connection.name, principal);
974
- const cached = this.tokenCache.get(cacheKey);
975
- if (cached && !isTokenExpired(cached.token))
976
- return cached.token;
977
- const connectionContext = { url: resolvedUrl ?? connection.url ?? "" };
978
- if (authorization.kind === "oauth" || authorization.kind === "interactive") {
979
- const stored = await this.grantStore.getGrant(connection.name, principalKey);
980
- if (stored && !storedGrantExpired(stored)) {
981
- const token = grantToTokenResult(stored);
982
- this.tokenCache.set(cacheKey, { token, authorization, principal, connection: connectionContext });
983
- return token;
984
- }
985
- if (stored?.refreshToken && authorization.kind === "oauth" && authorization.oauth) {
986
- const refreshed = await this.refreshOAuthGrant(authorization.oauth, connection.name, principal, principalKey, stored.refreshToken);
987
- // saveGrantFromToken preserved the credential bag on the grant; mirror
988
- // that on the returned token so template rendering sees it too.
989
- if (!refreshed.credential && stored.credential)
990
- refreshed.credential = stored.credential;
991
- this.tokenCache.set(cacheKey, { token: refreshed, authorization, principal, connection: connectionContext });
992
- return refreshed;
993
- }
994
- if (authorization.kind === "oauth") {
995
- throw await this.createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext);
996
- }
997
- }
998
- try {
999
- const token = await authorization.getToken({ principal, connection: connectionContext });
1000
- this.tokenCache.set(cacheKey, { token, authorization, principal, connection: connectionContext });
1001
- return token;
1002
- }
1003
- catch (error) {
1004
- if (isConnectionAuthorizationRequiredError(error) && authorization.kind === "interactive") {
1005
- if (this.authorizationIntent.getStore() === "discover") {
1006
- throw this.authorizationRequiredWithoutSession(connection, error);
1007
- }
1008
- throw await this.createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext, error);
1009
- }
1010
- if (isConnectionAuthorizationRequiredError(error) || isConnectionAuthorizationFailedError(error))
1011
- throw error;
1012
- throw error;
1013
- }
1014
- }
1015
- resolverContext(connection, session = this.options.session) {
1016
- const factory = this.factoryContext(connection, session);
1017
- return {
1018
- config: factory.config,
1019
- credentials: factory.credentials,
1020
- fetch: this.options.fetch,
1021
- session
1022
- };
1023
- }
1024
- factoryContext(connection, session = this.options.session) {
1025
- const principal = this.connectionPrincipal(connection, session);
1026
- const audience = this.options.audience ?? { private: true };
1027
- const identity = () => {
1028
- const runId = this.executionContext.getStore()?.runId ?? this.options.runId;
1029
- return { principal, audience, ...(runId ? { runId } : {}) };
1030
- };
1031
- const configNames = [...(connection.requiredConfig ?? []), ...(connection.optionalConfig ?? [])];
1032
- return {
1033
- connectionName: connection.name,
1034
- agentId: this.options.agentId ?? "",
1035
- principal,
1036
- audience,
1037
- config: Object.freeze(Object.fromEntries(configNames.map((name) => [name, this.options.config?.[name]]))),
1038
- credentials: this.credentialBroker.scoped(connection, identity),
1039
- ...(this.options.blob && (this.executionContext.getStore()?.runId ?? this.options.runId) ? {
1040
- assets: createConnectionAssetApi({
1041
- blob: this.options.blob,
1042
- runId: (this.executionContext.getStore()?.runId ?? this.options.runId),
1043
- sourceConnection: connection.name
1044
- })
1045
- } : {}),
1046
- fetch: this.options.fetch,
1047
- session
1048
- };
1049
- }
1050
- connectionPrincipal(connection, session = this.options.session) {
1051
- if (connection.subject !== "user")
1052
- return { type: "app" };
1053
- if (session.principal?.type === "user")
1054
- return session.principal;
1055
- if (session.userId) {
1056
- return {
1057
- type: "user",
1058
- id: session.userId,
1059
- issuer: session.channel ?? "assembly-line"
1060
- };
1061
- }
1062
- throw new ConnectionConfigurationError(`Connection ${connection.name} requires an authenticated user principal.`);
1063
- }
1064
- async withExecutionContext(execution, fn) {
1065
- return execution ? this.executionContext.run(execution, fn) : fn();
1066
- }
1067
- async createAuthorizationRequiredError(connection, authorization, principal, principalKey, context, connectionContext, cause) {
1068
- if (this.authorizationIntent.getStore() === "discover") {
1069
- return this.authorizationRequiredWithoutSession(connection, cause);
1070
- }
1071
- const key = `${connection.name}\0${principalKey}`;
1072
- const pending = this.pendingAuthorizationCreations.get(key);
1073
- if (pending)
1074
- return pending;
1075
- const creation = this.createAuthorizationRequiredErrorWithContext(connection, authorization, principal, principalKey, context, connectionContext, cause);
1076
- this.pendingAuthorizationCreations.set(key, creation);
1077
- try {
1078
- return await creation;
1079
- }
1080
- finally {
1081
- if (this.pendingAuthorizationCreations.get(key) === creation) {
1082
- this.pendingAuthorizationCreations.delete(key);
1083
- }
1084
- }
1085
- }
1086
- authorizationRequiredWithoutSession(connection, cause) {
1087
- const options = {
1088
- reason: cause?.reason ?? "authorization_required"
1089
- };
1090
- if (cause?.message)
1091
- options.message = cause.message;
1092
- if (cause?.challenge)
1093
- options.challenge = cause.challenge;
1094
- return new ConnectionAuthorizationRequiredError(connection.name, options);
1095
- }
1096
- async createAuthorizationRequiredErrorWithContext(connection, authorization, principal, principalKey, context, connectionContext, cause) {
1097
- await this.authorizationSessionStore.deleteExpiredSessions?.(Date.now());
1098
- const existing = await this.authorizationSessionStore.findPendingSession(connection.name, principalKey);
1099
- if (existing) {
1100
- const options = {
1101
- reason: cause?.reason ?? "authorization_required",
1102
- challenge: existing.challenge
1103
- };
1104
- if (cause?.message)
1105
- options.message = cause.message;
1106
- return new ConnectionAuthorizationRequiredError(connection.name, {
1107
- ...options
1108
- });
1109
- }
1110
- const callbackUrl = this.callbackUrl();
1111
- const id = randomUUID();
1112
- const state = randomUrlToken();
1113
- const expiresAt = Date.now() + 10 * 60_000;
1114
- let challenge;
1115
- let resume;
1116
- let codeVerifier;
1117
- if (authorization.kind === "oauth" && authorization.oauth) {
1118
- codeVerifier = randomUrlToken(48);
1119
- const oauthChallengeOptions = {
1120
- state,
1121
- callbackUrl,
1122
- codeVerifier
1123
- };
1124
- if (authorization.displayName)
1125
- oauthChallengeOptions.displayName = authorization.displayName;
1126
- challenge = buildOAuthChallenge(authorization.oauth, oauthChallengeOptions);
1127
- }
1128
- else if (authorization.startAuthorization) {
1129
- const started = await authorization.startAuthorization({ principal, connection: connectionContext, callbackUrl, state });
1130
- challenge = started.challenge;
1131
- resume = started.resume;
1132
- if (!challenge.url && cause?.challenge?.url)
1133
- challenge.url = cause.challenge.url;
1134
- if (!challenge.instructions && cause?.challenge?.instructions)
1135
- challenge.instructions = cause.challenge.instructions;
1136
- }
1137
- else {
1138
- challenge = cause?.challenge ?? { instructions: `Authorize connection ${connection.name} and retry.` };
1139
- if (authorization.displayName)
1140
- challenge.displayName = authorization.displayName;
1141
- }
1142
- challenge.sessionId = id;
1143
- challenge.state = state;
1144
- challenge.expiresAt = expiresAt;
1145
- const session = {
1146
- id,
1147
- state,
1148
- connectionName: connection.name,
1149
- principal,
1150
- principalKey,
1151
- session: context.session,
1152
- callbackUrl,
1153
- challenge,
1154
- status: "pending",
1155
- expiresAt,
1156
- createdAt: timestamp(),
1157
- updatedAt: timestamp()
1158
- };
1159
- if (codeVerifier)
1160
- session.codeVerifier = codeVerifier;
1161
- if (resume !== undefined)
1162
- session.resume = resume;
1163
- const execution = this.executionContext.getStore();
1164
- const runId = execution?.runId ?? this.options.runId;
1165
- if (runId)
1166
- session.runId = runId;
1167
- if (execution?.toolCallId)
1168
- session.toolCallId = execution.toolCallId;
1169
- if (execution?.toolName)
1170
- session.toolName = execution.toolName;
1171
- await this.authorizationSessionStore.createSession(session);
1172
- const errorOptions = {
1173
- reason: cause?.reason ?? "authorization_required",
1174
- challenge
1175
- };
1176
- if (cause?.message)
1177
- errorOptions.message = cause.message;
1178
- return new ConnectionAuthorizationRequiredError(connection.name, errorOptions);
1179
- }
1180
- callbackUrl() {
1181
- const base = (this.options.callbackBaseUrl ?? this.options.config?.ASSEMBLY_LINE_PUBLIC_URL ?? "http://localhost").replace(/\/+$/u, "");
1182
- return `${base}/assembly-line/connections/callback`;
1183
- }
1184
- async exchangeOAuthAuthorizationCode(oauth, session, callback) {
1185
- const code = callback.params.code;
1186
- if (!code) {
1187
- throw new ConnectionAuthorizationFailedError(session.connectionName, { reason: "missing_code", retryable: true });
1188
- }
1189
- return this.requestOAuthToken(oauth, {
1190
- grant_type: "authorization_code",
1191
- code,
1192
- redirect_uri: oauth.redirectUri ?? session.callbackUrl,
1193
- code_verifier: session.codeVerifier
1194
- });
1195
- }
1196
- async refreshOAuthGrant(oauth, connectionName, principal, principalKey, refreshToken) {
1197
- const token = await this.requestOAuthToken(oauth, {
1198
- grant_type: "refresh_token",
1199
- refresh_token: refreshToken
1200
- });
1201
- if (!token.refreshToken)
1202
- token.refreshToken = refreshToken;
1203
- await this.saveGrantFromToken(connectionName, principal, principalKey, token);
1204
- return token;
1205
- }
1206
- async requestOAuthToken(oauth, fields) {
1207
- const body = new URLSearchParams();
1208
- const headers = {
1209
- accept: "application/json",
1210
- "content-type": "application/x-www-form-urlencoded"
1211
- };
1212
- if (oauth.tokenEndpointAuthMethod === "client_secret_basic") {
1213
- if (!oauth.clientSecret) {
1214
- throw new ConnectionAuthorizationFailedError("oauth", { reason: "missing_client_secret" });
1215
- }
1216
- headers.authorization = `Basic ${Buffer.from(`${oauth.clientId}:${oauth.clientSecret}`).toString("base64")}`;
1217
- }
1218
- else {
1219
- body.set("client_id", oauth.clientId);
1220
- if (oauth.clientSecret)
1221
- body.set("client_secret", oauth.clientSecret);
1222
- }
1223
- for (const [key, value] of Object.entries(oauth.tokenParams ?? {}))
1224
- body.set(key, value);
1225
- for (const [key, value] of Object.entries(fields)) {
1226
- if (value !== undefined)
1227
- body.set(key, value);
1228
- }
1229
- const response = await this.request(oauth.tokenUrl, {
1230
- method: "POST",
1231
- headers,
1232
- body
1233
- });
1234
- const payload = await readResponseBody(response);
1235
- if (!response.ok || !isRecord(payload)) {
1236
- throw new ConnectionAuthorizationFailedError("oauth", {
1237
- reason: "token_exchange_failed",
1238
- message: `OAuth token exchange failed with HTTP ${response.status}.`
1239
- });
1240
- }
1241
- const accessToken = stringValue(payload.access_token);
1242
- if (!accessToken) {
1243
- throw new ConnectionAuthorizationFailedError("oauth", { reason: "missing_access_token" });
1244
- }
1245
- const token = { token: accessToken };
1246
- const refreshToken = stringValue(payload.refresh_token);
1247
- const tokenType = oauth.tokenType?.trim() || stringValue(payload.token_type);
1248
- const scope = stringValue(payload.scope);
1249
- const expiresIn = typeof payload.expires_in === "number" ? payload.expires_in : Number(payload.expires_in);
1250
- if (refreshToken)
1251
- token.refreshToken = refreshToken;
1252
- if (tokenType)
1253
- token.tokenType = tokenType;
1254
- if (scope)
1255
- token.scopes = scope.split(/\s+/u).filter(Boolean);
1256
- if (Number.isFinite(expiresIn) && expiresIn > 0)
1257
- token.expiresAt = Date.now() + expiresIn * 1000;
1258
- // Non-standard response fields (provider identity ids and the like) ride
1259
- // along as the grant's credential bag; RFC 6749 §5.1 permits extras.
1260
- const credential = {};
1261
- for (const [key, value] of Object.entries(payload)) {
1262
- if (OAUTH_STANDARD_TOKEN_FIELDS.has(key))
1263
- continue;
1264
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
1265
- credential[key] = value;
1266
- }
1267
- if (Object.keys(credential).length > 0)
1268
- token.credential = credential;
1269
- return token;
1270
- }
1271
- async saveGrantFromToken(connectionName, principal, principalKey, token) {
1272
- const now = timestamp();
1273
- const existing = await this.grantStore.getGrant(connectionName, principalKey);
1274
- const grant = {
1275
- connectionName,
1276
- principalKey,
1277
- principal,
1278
- token: token.token,
1279
- createdAt: existing?.createdAt ?? now,
1280
- updatedAt: now
1281
- };
1282
- if (token.refreshToken)
1283
- grant.refreshToken = token.refreshToken;
1284
- else if (existing?.refreshToken)
1285
- grant.refreshToken = existing.refreshToken;
1286
- if (token.tokenType)
1287
- grant.tokenType = token.tokenType;
1288
- if (token.scopes)
1289
- grant.scopes = token.scopes;
1290
- if (token.expiresAt)
1291
- grant.expiresAt = token.expiresAt;
1292
- // A response that omits extras must not erase identity captured earlier.
1293
- if (token.credential)
1294
- grant.credential = token.credential;
1295
- else if (existing?.credential)
1296
- grant.credential = existing.credential;
1297
- await this.grantStore.saveGrant(grant);
1298
- }
1299
- }
1300
- function sandboxCredentialWasRequested(run, connectionName) {
1301
- const requests = run?.input.sandboxCredentials;
1302
- return Boolean(requests
1303
- && typeof requests === "object"
1304
- && !Array.isArray(requests)
1305
- && Object.prototype.hasOwnProperty.call(requests, connectionName));
1306
- }
1307
- function isConnectionFactory(value) {
1308
- return Boolean(value && typeof value === "object" && typeof value.create === "function");
1309
363
  }
1310
364
  //# sourceMappingURL=connections.js.map