@opengeni/api-router 0.30.3 → 1.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 (434) hide show
  1. package/dist/access-grant-rls.d.ts +3 -0
  2. package/dist/admission-trace.d.ts +8 -0
  3. package/dist/api-websocket.d.ts +18 -0
  4. package/dist/app.d.ts +50 -2
  5. package/dist/app.js +12 -1
  6. package/dist/artifact-catalog-cursor.d.ts +27 -0
  7. package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
  8. package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
  9. package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
  10. package/dist/auth/managed-auth.d.ts +102 -3
  11. package/dist/auth/managed-email.d.ts +29 -0
  12. package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
  13. package/dist/auth/new-signups-gate.d.ts +23 -0
  14. package/dist/auth/organization-user-setup.d.ts +80 -0
  15. package/dist/auth/signup-funnel-metrics.d.ts +44 -0
  16. package/dist/browser-controller-authority.d.ts +1 -61
  17. package/dist/browser-ephemeral.d.ts +5 -0
  18. package/dist/browser-state-authority.d.ts +1 -27
  19. package/dist/chunk-6TZ5LG3O.js +123948 -0
  20. package/dist/chunk-6TZ5LG3O.js.map +1 -0
  21. package/dist/chunk-OGQDXVXY.js +18 -0
  22. package/dist/chunk-OGQDXVXY.js.map +1 -0
  23. package/dist/claude-subscription-account-conflicts.d.ts +1 -0
  24. package/dist/claude-subscription-account-usage.d.ts +39 -0
  25. package/dist/claude-subscription-oauth.d.ts +25 -0
  26. package/dist/claude-subscription-profile.d.ts +9 -0
  27. package/dist/claude-subscription-usage.d.ts +44 -0
  28. package/dist/claude-workspace-connection.d.ts +15 -0
  29. package/dist/codemode.d.ts +32 -1
  30. package/dist/company-brain-okf.d.ts +24 -0
  31. package/dist/connected-machine-computer-access.d.ts +11 -0
  32. package/dist/connection-ownership.d.ts +54 -0
  33. package/dist/controller-data-plane.d.ts +32 -0
  34. package/dist/editable-artifact-source-session.d.ts +3 -0
  35. package/dist/editable-artifact-websocket.d.ts +10 -18
  36. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  37. package/dist/fatal-process-boundary.d.ts +25 -0
  38. package/dist/github-access.d.ts +31 -2
  39. package/dist/http/acting-person.d.ts +24 -0
  40. package/dist/http/api-contract-compat.d.ts +19 -0
  41. package/dist/http/api-error.d.ts +27 -1
  42. package/dist/http/cors.d.ts +17 -0
  43. package/dist/http/deprecation.d.ts +42 -0
  44. package/dist/http/interaction-control-error.d.ts +13 -0
  45. package/dist/http/keyed-admission.d.ts +12 -0
  46. package/dist/http/local-browser-boundary.d.ts +72 -0
  47. package/dist/http/metrics-listener.d.ts +22 -0
  48. package/dist/http/path-identifier.d.ts +17 -0
  49. package/dist/http/registered-route-label.d.ts +16 -0
  50. package/dist/http/request-body.d.ts +27 -0
  51. package/dist/http/request-source.d.ts +52 -0
  52. package/dist/http/sse.d.ts +17 -6
  53. package/dist/http/unmatched-route.d.ts +18 -0
  54. package/dist/http/user-content.d.ts +36 -0
  55. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  56. package/dist/index.d.ts +10 -3
  57. package/dist/index.js +1091 -162
  58. package/dist/index.js.map +1 -1
  59. package/dist/integration-endpoint-test.d.ts +20 -0
  60. package/dist/integrations/api-integrations.d.ts +2 -2
  61. package/dist/integrations/atlassian.d.ts +29 -3
  62. package/dist/integrations/connect-authority.d.ts +1 -0
  63. package/dist/integrations/connect-callback-return.d.ts +6 -0
  64. package/dist/integrations/fiken.d.ts +16 -2
  65. package/dist/integrations/github-app-connect.d.ts +47 -0
  66. package/dist/integrations/github-installation-proof.d.ts +3 -0
  67. package/dist/integrations/github-lens-connect.d.ts +116 -0
  68. package/dist/integrations/github-skill-source.d.ts +1 -5
  69. package/dist/integrations/google-drive.d.ts +24 -0
  70. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  71. package/dist/integrations/marketplace-plugin.d.ts +2691 -0
  72. package/dist/integrations/oauth-client.d.ts +178 -18
  73. package/dist/integrations/oauth-profiles.d.ts +222 -0
  74. package/dist/integrations/oauth-return-path.d.ts +1 -0
  75. package/dist/integrations/personal-github-repositories.d.ts +84 -0
  76. package/dist/integrations/personal-github.d.ts +42 -0
  77. package/dist/integrations/pr-review-provider.d.ts +22 -0
  78. package/dist/integrations/provider-oauth.d.ts +19 -1
  79. package/dist/integrations/slack-app-home.d.ts +24 -0
  80. package/dist/integrations/slack-bot.d.ts +367 -12
  81. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  82. package/dist/integrations/slack-install.d.ts +20 -0
  83. package/dist/integrations/slack-interactions.d.ts +107 -9
  84. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  85. package/dist/integrations/slack-routing.d.ts +120 -0
  86. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  87. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  88. package/dist/integrations/social-oauth.d.ts +7 -0
  89. package/dist/interaction-agent-access.d.ts +15 -0
  90. package/dist/interaction-frame-proxy.d.ts +68 -0
  91. package/dist/interaction-metrics.d.ts +2 -0
  92. package/dist/mcp/action-catalog-types.d.ts +9 -0
  93. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  94. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  95. package/dist/mcp/child-read-evidence.d.ts +9 -0
  96. package/dist/mcp/command-read.d.ts +24 -0
  97. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  98. package/dist/mcp/contract-input.d.ts +11 -0
  99. package/dist/mcp/documents.d.ts +7 -5
  100. package/dist/mcp/files.d.ts +2 -2
  101. package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
  102. package/dist/mcp/knowledge-entries.d.ts +5 -0
  103. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  104. package/dist/mcp/receipts.d.ts +9 -0
  105. package/dist/mcp/request-abort.d.ts +19 -0
  106. package/dist/mcp/scheduled-task-input.d.ts +1189 -0
  107. package/dist/mcp/scheduled-task-view.d.ts +22 -4
  108. package/dist/mcp/server.d.ts +255 -3
  109. package/dist/mcp/session-event-view.d.ts +94 -0
  110. package/dist/mcp/session-view.d.ts +66 -2
  111. package/dist/mcp/session-wait.d.ts +174 -0
  112. package/dist/mcp-oauth.d.ts +52 -0
  113. package/dist/model-catalog.d.ts +11 -31
  114. package/dist/observability.d.ts +3 -1
  115. package/dist/organization-mcp.d.ts +52 -0
  116. package/dist/organization-recovery-notifications.d.ts +41 -0
  117. package/dist/routes/analytics-consent.d.ts +26 -0
  118. package/dist/routes/api-integrations.d.ts +25 -0
  119. package/dist/routes/api-keys.d.ts +17 -0
  120. package/dist/routes/artifact-catalog.d.ts +6 -0
  121. package/dist/routes/automations.d.ts +19 -0
  122. package/dist/routes/billing.d.ts +49 -0
  123. package/dist/routes/browser-sessions.d.ts +59 -2
  124. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  125. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  126. package/dist/routes/client-errors.d.ts +146 -0
  127. package/dist/routes/codex.d.ts +76 -2
  128. package/dist/routes/company-brain.d.ts +3 -0
  129. package/dist/routes/company-profile.d.ts +2 -1
  130. package/dist/routes/computer-sessions.d.ts +14 -0
  131. package/dist/routes/connect.d.ts +5 -0
  132. package/dist/routes/editable-artifacts.d.ts +2 -0
  133. package/dist/routes/external-identity-links.d.ts +6 -0
  134. package/dist/routes/feedback.d.ts +3 -0
  135. package/dist/routes/github-browser-pages.d.ts +12 -0
  136. package/dist/routes/github.d.ts +2 -0
  137. package/dist/routes/insights-response-cache.d.ts +10 -0
  138. package/dist/routes/insights-usage.d.ts +12 -0
  139. package/dist/routes/insights.d.ts +30 -1
  140. package/dist/routes/knowledge.d.ts +3 -0
  141. package/dist/routes/managed-auth-session-sets.d.ts +26 -0
  142. package/dist/routes/managed-onboarding.d.ts +29 -0
  143. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  144. package/dist/routes/model-connection-access.d.ts +3 -0
  145. package/dist/routes/organization-integration-policy.d.ts +3 -0
  146. package/dist/routes/organization-integrations.d.ts +6 -0
  147. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  148. package/dist/routes/organization-model-providers.d.ts +3 -0
  149. package/dist/routes/organization-recovery.d.ts +19 -0
  150. package/dist/routes/organization-service-accounts.d.ts +3 -0
  151. package/dist/routes/organization-sessions.d.ts +26 -0
  152. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  153. package/dist/routes/personal-github.d.ts +3 -0
  154. package/dist/routes/pr-review-github.d.ts +3 -0
  155. package/dist/routes/pr-review.d.ts +3 -0
  156. package/dist/routes/session-artifact-associations.d.ts +4 -0
  157. package/dist/routes/session-history-imports.d.ts +9 -0
  158. package/dist/routes/sessions.d.ts +44 -6
  159. package/dist/routes/skill-content.d.ts +4 -0
  160. package/dist/routes/skill-install-authority.d.ts +6 -0
  161. package/dist/routes/subscription-account-pools.d.ts +34 -0
  162. package/dist/routes/subscription-pool-access.d.ts +16 -0
  163. package/dist/routes/supergrok.d.ts +6 -1
  164. package/dist/routes/usage-allowances.d.ts +12 -0
  165. package/dist/routes/user-resource-authorities.d.ts +3 -0
  166. package/dist/routes/workspace-artifacts.d.ts +2 -1
  167. package/dist/routes/workspace-capture.d.ts +10 -2
  168. package/dist/routes/workspace-integrations.d.ts +35 -0
  169. package/dist/routes/workspace-learning.d.ts +3 -0
  170. package/dist/routes/workspace-model-providers.d.ts +4 -0
  171. package/dist/routes/workspaces.d.ts +29 -0
  172. package/dist/sandbox/auth-callout.d.ts +8 -0
  173. package/dist/sandbox/channel-a.d.ts +19 -1
  174. package/dist/sandbox/connection-authority.d.ts +3 -0
  175. package/dist/sandbox/enrollment.d.ts +9 -1
  176. package/dist/sandbox/machines.d.ts +5 -2
  177. package/dist/sandbox/metrics-ingestion.d.ts +28 -20
  178. package/dist/sandbox/rematerialize.d.ts +2 -0
  179. package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
  180. package/dist/sandbox/viewer.d.ts +26 -1
  181. package/dist/sandbox-file-artifacts.d.ts +50 -0
  182. package/dist/scheduled-task-deletion.d.ts +13 -0
  183. package/dist/session-create-observability.d.ts +5 -0
  184. package/dist/session-event-view-MPS2OZD3.js +292 -0
  185. package/dist/session-event-view-MPS2OZD3.js.map +1 -0
  186. package/dist/site-session-origin.d.ts +13 -0
  187. package/dist/site-uploads.d.ts +41 -0
  188. package/dist/slack-reaction-files.d.ts +1 -1
  189. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  190. package/dist/temporal-schedule-sync.d.ts +26 -0
  191. package/dist/transcription/providers/xai-subscription.d.ts +1 -0
  192. package/dist/transcription/service.d.ts +2 -1
  193. package/dist/work-discovery-observability.d.ts +33 -0
  194. package/dist/workspace-artifact-content.d.ts +28 -0
  195. package/dist/workspace-artifact-provenance.d.ts +7 -0
  196. package/dist/workspace-delete-observability.d.ts +10 -0
  197. package/dist/workspace-deletion.d.ts +6 -0
  198. package/dist/workspace-state-projection.d.ts +3 -5
  199. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  200. package/dist/workspace-tool-gateway.d.ts +123 -0
  201. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  202. package/package.json +23 -19
  203. package/src/access-grant-rls.ts +75 -0
  204. package/src/admission-trace.ts +14 -0
  205. package/src/api-websocket.ts +23 -0
  206. package/src/app.ts +1967 -252
  207. package/src/artifact-catalog-cursor.ts +60 -0
  208. package/src/auth/managed-auth-attempt-context.ts +70 -0
  209. package/src/auth/managed-auth-rate-limits.ts +252 -0
  210. package/src/auth/managed-auth-session-adapter.ts +222 -0
  211. package/src/auth/managed-auth.ts +536 -60
  212. package/src/auth/managed-email.ts +174 -0
  213. package/src/auth/managed-sign-in-notifications.ts +81 -0
  214. package/src/auth/new-signups-gate.ts +49 -0
  215. package/src/auth/organization-user-setup.ts +307 -0
  216. package/src/auth/signup-funnel-metrics.ts +164 -0
  217. package/src/browser-controller-authority.ts +1 -186
  218. package/src/browser-ephemeral.ts +39 -0
  219. package/src/browser-state-authority.ts +1 -236
  220. package/src/claude-subscription-account-conflicts.ts +12 -0
  221. package/src/claude-subscription-account-usage.ts +48 -0
  222. package/src/claude-subscription-oauth.ts +362 -0
  223. package/src/claude-subscription-profile.ts +73 -0
  224. package/src/claude-subscription-usage.ts +97 -0
  225. package/src/claude-workspace-connection.ts +82 -0
  226. package/src/codemode.ts +194 -30
  227. package/src/codex-realtime.ts +8 -2
  228. package/src/company-brain-okf.ts +340 -0
  229. package/src/connected-machine-computer-access.ts +33 -0
  230. package/src/connection-ownership.ts +199 -0
  231. package/src/controller-data-plane.ts +81 -0
  232. package/src/editable-artifact-native-kernel.ts +26 -15
  233. package/src/editable-artifact-office-import.ts +28 -3
  234. package/src/editable-artifact-production.ts +23 -7
  235. package/src/editable-artifact-source-session.ts +123 -0
  236. package/src/editable-artifact-websocket.ts +27 -21
  237. package/src/editable-artifact-workspace-files.ts +31 -6
  238. package/src/fatal-process-boundary.ts +246 -0
  239. package/src/gateway-realtime.ts +1 -1
  240. package/src/github-access.ts +147 -5
  241. package/src/http/acting-person.ts +71 -0
  242. package/src/http/api-contract-compat.ts +49 -0
  243. package/src/http/api-error.ts +115 -1
  244. package/src/http/auth.ts +48 -0
  245. package/src/http/cors.ts +67 -0
  246. package/src/http/deprecation.ts +118 -0
  247. package/src/http/interaction-control-error.ts +164 -0
  248. package/src/http/keyed-admission.ts +29 -0
  249. package/src/http/local-browser-boundary.ts +343 -0
  250. package/src/http/metrics-listener.ts +57 -0
  251. package/src/http/path-identifier.ts +54 -0
  252. package/src/http/registered-route-label.ts +34 -0
  253. package/src/http/request-body.ts +127 -0
  254. package/src/http/request-source.ts +178 -0
  255. package/src/http/sse.ts +486 -132
  256. package/src/http/unmatched-route.ts +60 -0
  257. package/src/http/user-content.ts +158 -0
  258. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  259. package/src/index.ts +157 -52
  260. package/src/integration-endpoint-test.ts +291 -0
  261. package/src/integrations/api-integrations.ts +98 -11
  262. package/src/integrations/atlassian.ts +474 -136
  263. package/src/integrations/connect-authority.ts +2 -0
  264. package/src/integrations/connect-callback-return.ts +86 -0
  265. package/src/integrations/fiken.ts +354 -64
  266. package/src/integrations/github-app-connect.ts +435 -0
  267. package/src/integrations/github-installation-proof.ts +60 -0
  268. package/src/integrations/github-lens-connect.ts +76 -0
  269. package/src/integrations/github-skill-source.ts +2 -142
  270. package/src/integrations/google-drive.ts +717 -225
  271. package/src/integrations/integration-commit-authority.ts +69 -0
  272. package/src/integrations/marketplace-plugin.ts +48 -0
  273. package/src/integrations/oauth-client.ts +1356 -459
  274. package/src/integrations/oauth-profiles.ts +537 -0
  275. package/src/integrations/oauth-return-path.ts +18 -0
  276. package/src/integrations/personal-github-repositories.ts +674 -0
  277. package/src/integrations/personal-github.ts +825 -0
  278. package/src/integrations/pr-review-provider.ts +246 -0
  279. package/src/integrations/provider-oauth.ts +250 -22
  280. package/src/integrations/slack-app-home.ts +300 -0
  281. package/src/integrations/slack-bot.ts +963 -239
  282. package/src/integrations/slack-file-upload-flow.ts +147 -0
  283. package/src/integrations/slack-install.ts +83 -0
  284. package/src/integrations/slack-interactions.ts +3167 -474
  285. package/src/integrations/slack-mrkdwn.ts +193 -0
  286. package/src/integrations/slack-routing.ts +337 -0
  287. package/src/integrations/slack-session-defaults.ts +224 -0
  288. package/src/integrations/slack-task-file-upload.ts +323 -0
  289. package/src/integrations/social-oauth.ts +213 -15
  290. package/src/interaction-agent-access.ts +48 -0
  291. package/src/interaction-frame-proxy.ts +409 -0
  292. package/src/interaction-metrics.ts +30 -0
  293. package/src/mcp/action-catalog-types.ts +9 -0
  294. package/src/mcp/action-catalog.gen.ts +6696 -0
  295. package/src/mcp/capability-account-readiness.ts +17 -0
  296. package/src/mcp/child-read-evidence.ts +19 -0
  297. package/src/mcp/command-read.ts +109 -0
  298. package/src/mcp/company-profile-agent-admin.ts +198 -0
  299. package/src/mcp/contract-input.ts +141 -0
  300. package/src/mcp/documents.ts +65 -377
  301. package/src/mcp/editable-artifacts.ts +12 -6
  302. package/src/mcp/files.ts +80 -43
  303. package/src/mcp/first-party-tool-permissions.ts +305 -0
  304. package/src/mcp/knowledge-entries.ts +322 -0
  305. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  306. package/src/mcp/receipts.ts +34 -0
  307. package/src/mcp/request-abort.ts +44 -0
  308. package/src/mcp/scheduled-task-input.ts +76 -0
  309. package/src/mcp/scheduled-task-view.ts +44 -3
  310. package/src/mcp/server.ts +3030 -1267
  311. package/src/mcp/session-event-view.ts +383 -0
  312. package/src/mcp/session-view.ts +84 -3
  313. package/src/mcp/session-wait.ts +665 -0
  314. package/src/mcp-oauth.ts +1123 -0
  315. package/src/model-catalog.ts +91 -333
  316. package/src/observability.ts +15 -1
  317. package/src/organization-mcp.ts +397 -0
  318. package/src/organization-recovery-notifications.ts +103 -0
  319. package/src/routes/analytics-consent.ts +123 -0
  320. package/src/routes/api-integrations.ts +364 -74
  321. package/src/routes/api-keys.ts +336 -10
  322. package/src/routes/artifact-catalog.ts +279 -0
  323. package/src/routes/automations.ts +768 -0
  324. package/src/routes/billing.ts +461 -46
  325. package/src/routes/browser-sessions.ts +780 -112
  326. package/src/routes/canonical-human-identities.ts +90 -14
  327. package/src/routes/capabilities.ts +107 -6
  328. package/src/routes/channels.ts +20 -3
  329. package/src/routes/claude-subscription-accounts.ts +334 -0
  330. package/src/routes/claude-subscription-oauth.ts +142 -0
  331. package/src/routes/client-errors.ts +362 -0
  332. package/src/routes/codex.ts +833 -116
  333. package/src/routes/company-brain.ts +322 -0
  334. package/src/routes/company-profile.ts +65 -1
  335. package/src/routes/computer-sessions.ts +557 -106
  336. package/src/routes/connect.ts +1938 -0
  337. package/src/routes/connections.ts +1039 -349
  338. package/src/routes/documents.ts +488 -287
  339. package/src/routes/editable-artifacts.ts +79 -9
  340. package/src/routes/enrollments.ts +135 -39
  341. package/src/routes/environments.ts +154 -58
  342. package/src/routes/external-identity-links.ts +201 -0
  343. package/src/routes/feedback.ts +113 -0
  344. package/src/routes/files.ts +267 -33
  345. package/src/routes/github-browser-pages.ts +162 -0
  346. package/src/routes/github.ts +446 -144
  347. package/src/routes/insights-response-cache.ts +83 -0
  348. package/src/routes/insights-usage.ts +345 -0
  349. package/src/routes/insights.ts +153 -19
  350. package/src/routes/install.ts +133 -38
  351. package/src/routes/integration-facets.ts +107 -33
  352. package/src/routes/interaction-resources.ts +30 -3
  353. package/src/routes/knowledge.ts +488 -0
  354. package/src/routes/machines.ts +313 -15
  355. package/src/routes/managed-auth-session-sets.ts +1218 -0
  356. package/src/routes/managed-onboarding.ts +337 -0
  357. package/src/routes/managed-sign-in-methods.ts +433 -0
  358. package/src/routes/memory-slack-publications.ts +3 -2
  359. package/src/routes/model-connection-access.ts +208 -0
  360. package/src/routes/organization-integration-policy.ts +116 -0
  361. package/src/routes/organization-integrations.ts +326 -0
  362. package/src/routes/organization-mcp-connections.ts +178 -0
  363. package/src/routes/organization-memberships.ts +1019 -27
  364. package/src/routes/organization-model-providers.ts +293 -0
  365. package/src/routes/organization-recovery.ts +325 -0
  366. package/src/routes/organization-service-accounts.ts +128 -0
  367. package/src/routes/organization-sessions.ts +350 -0
  368. package/src/routes/personal-github-git-broker.ts +785 -0
  369. package/src/routes/personal-github.ts +370 -0
  370. package/src/routes/plugins.ts +251 -22
  371. package/src/routes/pr-review-github.ts +881 -0
  372. package/src/routes/pr-review.ts +622 -0
  373. package/src/routes/preference-registry.ts +12 -93
  374. package/src/routes/rigs.ts +167 -50
  375. package/src/routes/scheduled-tasks.ts +346 -41
  376. package/src/routes/session-artifact-associations.ts +87 -0
  377. package/src/routes/session-history-imports.ts +280 -0
  378. package/src/routes/sessions.ts +2306 -264
  379. package/src/routes/skill-content.ts +297 -0
  380. package/src/routes/skill-install-authority.ts +64 -0
  381. package/src/routes/skills.ts +62 -9
  382. package/src/routes/social.ts +46 -18
  383. package/src/routes/subscription-account-pools.ts +436 -0
  384. package/src/routes/subscription-pool-access.ts +126 -0
  385. package/src/routes/supergrok.ts +219 -303
  386. package/src/routes/transcription-recordings.ts +38 -3
  387. package/src/routes/transcriptions.ts +4 -0
  388. package/src/routes/usage-allowances.ts +378 -0
  389. package/src/routes/user-resource-authorities.ts +138 -0
  390. package/src/routes/video-generation.ts +14 -10
  391. package/src/routes/workspace-artifacts.ts +275 -69
  392. package/src/routes/workspace-capture.ts +21 -0
  393. package/src/routes/workspace-integrations.ts +576 -0
  394. package/src/routes/workspace-learning.ts +159 -0
  395. package/src/routes/workspace-model-providers.ts +148 -0
  396. package/src/routes/workspace-state.ts +38 -25
  397. package/src/routes/workspaces.ts +1004 -127
  398. package/src/sandbox/auth-callout.ts +96 -19
  399. package/src/sandbox/channel-a.ts +323 -24
  400. package/src/sandbox/connection-authority.ts +3 -0
  401. package/src/sandbox/enrollment.ts +81 -6
  402. package/src/sandbox/machines.ts +218 -76
  403. package/src/sandbox/metrics-ingestion.ts +411 -62
  404. package/src/sandbox/rematerialize.ts +121 -19
  405. package/src/sandbox/session-attach-variable-sets.ts +131 -0
  406. package/src/sandbox/viewer.ts +280 -25
  407. package/src/sandbox-file-artifacts.ts +413 -0
  408. package/src/scheduled-task-deletion.ts +138 -0
  409. package/src/session-create-observability.ts +46 -0
  410. package/src/session-realtime-context.ts +6 -3
  411. package/src/site-session-origin.ts +48 -0
  412. package/src/site-uploads.ts +224 -0
  413. package/src/slack-reaction-files.ts +16 -5
  414. package/src/temporal-schedule-cleanup.ts +13 -0
  415. package/src/temporal-schedule-sync.ts +74 -0
  416. package/src/transcription/providers/codex-subscription.ts +2 -0
  417. package/src/transcription/providers/openai.ts +1 -0
  418. package/src/transcription/providers/xai-subscription.ts +49 -3
  419. package/src/transcription/segmenter.ts +8 -2
  420. package/src/transcription/service.ts +43 -6
  421. package/src/work-discovery-observability.ts +121 -0
  422. package/src/workspace-artifact-content.ts +144 -0
  423. package/src/workspace-artifact-provenance.ts +104 -0
  424. package/src/workspace-delete-observability.ts +75 -0
  425. package/src/workspace-deletion.ts +73 -0
  426. package/src/workspace-state-projection.ts +18 -148
  427. package/src/workspace-tool-gateway-observability.ts +100 -0
  428. package/src/workspace-tool-gateway.ts +870 -0
  429. package/src/workspace-webhook-dispatch.ts +190 -0
  430. package/src/xai-subscription-auth.ts +9 -2
  431. package/dist/chunk-WCXHI3FF.js +0 -49920
  432. package/dist/chunk-WCXHI3FF.js.map +0 -1
  433. package/dist/routes/packs.d.ts +0 -3
  434. package/src/routes/packs.ts +0 -729
@@ -0,0 +1,768 @@
1
+ import { environmentsEncryptionKeyBytes } from "@opengeni/config";
2
+ import { modelUnavailableHttpException } from "@opengeni/core";
3
+ import {
4
+ AUTOMATION_WEBHOOK_MAX_BYTES,
5
+ AUTOMATION_MAX_MATCHED_TRIGGERS,
6
+ AutomationNormalizedEvent,
7
+ AutomationWebhookResult,
8
+ CreateAutomationSourceRequest,
9
+ CreateAutomationTriggerRequest,
10
+ TriggerAutomationManuallyRequest,
11
+ UpdateAutomationSourceRequest,
12
+ UpdateAutomationTriggerRequest,
13
+ stableJson,
14
+ resolveBundledSkillSelection,
15
+ type AccessGrant,
16
+ type BundledSkillId,
17
+ } from "@opengeni/contracts";
18
+ import {
19
+ AutomationCredentialRestrictionConflictError,
20
+ AutomationDeliveryConflictError,
21
+ AutomationRevisionConflictError,
22
+ createAutomationRun,
23
+ createAutomationSource,
24
+ createAutomationTrigger,
25
+ decryptVariableSetValue,
26
+ encryptVariableSetValue,
27
+ getAutomationSourceSecret,
28
+ getAutomationTriggerRevisions,
29
+ getSession,
30
+ listActiveAutomationTriggersForSource,
31
+ listAutomationRuns,
32
+ listAutomationSources,
33
+ listAutomationTriggers,
34
+ recordAutomationEvent,
35
+ resolveAutomationWebhookEndpoint,
36
+ updateAutomationSource,
37
+ updateAutomationTrigger,
38
+ withWorkspaceGatewayCustomModelReadLock,
39
+ withWorkspaceOpenRouterCustomModelReadLock,
40
+ type AutomationSourceSecret,
41
+ } from "@opengeni/db";
42
+ import {
43
+ automationRequestDigest,
44
+ automationCredentialRestrictionForGrant,
45
+ assertWorkspaceModelPolicyAllows,
46
+ buildAutomationAcceptedExecution,
47
+ canonicalConfiguredModel,
48
+ creationInitiatorForGrant,
49
+ workspaceCustomModelReference,
50
+ lockActiveCustomModelForAdmission,
51
+ requireAccessGrant,
52
+ requireAutomationAdapter,
53
+ requirePermission,
54
+ requireAutomationCredentialRestrictionPermissions,
55
+ resolveWorkspaceCatalogSettings,
56
+ type ApiRouteDeps,
57
+ } from "@opengeni/core";
58
+ import type { Hono } from "hono";
59
+ import { HTTPException } from "hono/http-exception";
60
+ import { parseRequestJson } from "../http/request-body";
61
+
62
+ async function callerBundleSelection(
63
+ deps: ApiRouteDeps,
64
+ grant: AccessGrant,
65
+ requested: BundledSkillId[] | undefined,
66
+ ) {
67
+ const actor = creationInitiatorForGrant(grant).actor;
68
+ const parent = actor ? await getSession(deps.db, grant.workspaceId, actor.sessionId) : null;
69
+ if (actor && (!parent || parent.accountId !== grant.accountId))
70
+ throw new HTTPException(403, {
71
+ message: "Automation Skill selection requires the creating agent's session",
72
+ });
73
+ try {
74
+ return resolveBundledSkillSelection(requested, parent?.bundledSkillIds);
75
+ } catch (error) {
76
+ throw new HTTPException(422, {
77
+ message: error instanceof Error ? error.message : "Invalid bundled Skill selection",
78
+ });
79
+ }
80
+ }
81
+
82
+ export function registerAutomationRoutes(app: Hono, deps: ApiRouteDeps): void {
83
+ app.get("/v1/workspaces/:workspaceId/automations/sources", async (c) => {
84
+ const workspaceId = c.req.param("workspaceId");
85
+ await requireAccessGrant(c, deps, workspaceId, "workspace:read");
86
+ return c.json({
87
+ sources: await listAutomationSources(deps.db, workspaceId),
88
+ });
89
+ });
90
+
91
+ app.post("/v1/workspaces/:workspaceId/automations/sources", async (c) => {
92
+ const workspaceId = c.req.param("workspaceId");
93
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
94
+ requirePermission(grant, "secrets:write");
95
+ const request = await parseRequestJson(c, CreateAutomationSourceRequest);
96
+ assertGenericTriggerMutable(request);
97
+ requireAutomationAdapter(request.adapterId).validateSourceConfiguration(request.configuration);
98
+ const key = requireEncryptionKey(deps);
99
+ const source = await createAutomationSource(deps.db, {
100
+ accountId: grant.accountId,
101
+ workspaceId,
102
+ createdBySubjectId: grant.subjectId,
103
+ request,
104
+ webhookSecretEncrypted: encryptVariableSetValue(key, request.webhookSecret),
105
+ });
106
+ return c.json(source, 201);
107
+ });
108
+
109
+ app.patch("/v1/workspaces/:workspaceId/automations/sources/:sourceId", async (c) => {
110
+ const workspaceId = c.req.param("workspaceId");
111
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
112
+ const request = await parseRequestJson(c, UpdateAutomationSourceRequest);
113
+ const current = await requireSource(
114
+ deps,
115
+ grant.accountId,
116
+ workspaceId,
117
+ c.req.param("sourceId"),
118
+ );
119
+ assertGenericSourceMutable(current);
120
+ if (request.configuration) {
121
+ requireAutomationAdapter(current.adapterId).validateSourceConfiguration(
122
+ request.configuration,
123
+ );
124
+ }
125
+ let webhookSecretEncrypted: string | undefined;
126
+ if (request.webhookSecret !== undefined) {
127
+ requirePermission(grant, "secrets:write");
128
+ webhookSecretEncrypted = encryptVariableSetValue(
129
+ requireEncryptionKey(deps),
130
+ request.webhookSecret,
131
+ );
132
+ }
133
+ const source = await updateAutomationSource(deps.db, {
134
+ workspaceId,
135
+ sourceId: current.id,
136
+ request,
137
+ ...(webhookSecretEncrypted ? { webhookSecretEncrypted } : {}),
138
+ });
139
+ if (!source)
140
+ throw new HTTPException(404, {
141
+ message: "automation source not found",
142
+ });
143
+ return c.json(source);
144
+ });
145
+
146
+ app.delete("/v1/workspaces/:workspaceId/automations/sources/:sourceId", async (c) => {
147
+ const workspaceId = c.req.param("workspaceId");
148
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
149
+ const current = await requireSource(
150
+ deps,
151
+ grant.accountId,
152
+ workspaceId,
153
+ c.req.param("sourceId"),
154
+ );
155
+ assertGenericSourceMutable(current);
156
+ const source = await updateAutomationSource(deps.db, {
157
+ workspaceId,
158
+ sourceId: c.req.param("sourceId"),
159
+ request: { status: "disabled" },
160
+ });
161
+ if (!source)
162
+ throw new HTTPException(404, {
163
+ message: "automation source not found",
164
+ });
165
+ return c.body(null, 204);
166
+ });
167
+
168
+ app.get("/v1/workspaces/:workspaceId/automations/triggers", async (c) => {
169
+ const workspaceId = c.req.param("workspaceId");
170
+ await requireAccessGrant(c, deps, workspaceId, "workspace:read");
171
+ return c.json({
172
+ triggers: await listAutomationTriggers(deps.db, workspaceId),
173
+ });
174
+ });
175
+
176
+ app.post("/v1/workspaces/:workspaceId/automations/triggers", async (c) => {
177
+ const workspaceId = c.req.param("workspaceId");
178
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
179
+ const request = await parseRequestJson(c, CreateAutomationTriggerRequest);
180
+ const credentialRestriction = await automationCredentialRestrictionForGrant(deps.db, grant);
181
+ requireAutomationCredentialRestrictionPermissions(
182
+ credentialRestriction,
183
+ request.sessionTemplate.firstPartyMcpPermissions,
184
+ );
185
+ request.sessionTemplate.bundledSkillIds = await callerBundleSelection(
186
+ deps,
187
+ grant,
188
+ request.sessionTemplate.bundledSkillIds,
189
+ );
190
+ const source = await requireSource(deps, grant.accountId, workspaceId, request.sourceId);
191
+ assertGenericSourceMutable(source);
192
+ if (source.status !== "active") {
193
+ throw new HTTPException(409, {
194
+ message: "automation source is disabled",
195
+ });
196
+ }
197
+ const adapter = requireAutomationAdapter(source.adapterId);
198
+ adapter.validateTriggerConfiguration(request.configuration);
199
+ adapter.validateTriggerParameters(request.parameters);
200
+ for (const permission of request.sessionTemplate.firstPartyMcpPermissions) {
201
+ requirePermission(grant, permission);
202
+ }
203
+ const catalogSettings = (
204
+ await resolveWorkspaceCatalogSettings(deps.db, deps.settings, {
205
+ accountId: grant.accountId,
206
+ workspaceId,
207
+ })
208
+ ).settings;
209
+ const model = canonicalConfiguredModel(catalogSettings, request.sessionTemplate.model) ?? null;
210
+ if (model) {
211
+ await assertWorkspaceModelPolicyAllows(deps.db, catalogSettings, workspaceId, model);
212
+ }
213
+ const normalizedRequest = {
214
+ ...request,
215
+ sessionTemplate: { ...request.sessionTemplate, model },
216
+ };
217
+ const beforeCreateCommit = model
218
+ ? workspaceCustomModelCommitGuard({
219
+ settings: catalogSettings,
220
+ accountId: grant.accountId,
221
+ workspaceId,
222
+ modelId: model,
223
+ })
224
+ : undefined;
225
+ return c.json(
226
+ await createAutomationTrigger(deps.db, {
227
+ accountId: grant.accountId,
228
+ workspaceId,
229
+ createdBySubjectId: grant.subjectId,
230
+ request: normalizedRequest,
231
+ adapterId: adapter.id,
232
+ ...(credentialRestriction ? { credentialRestriction } : {}),
233
+ ...(beforeCreateCommit ? { beforeCreateCommit } : {}),
234
+ }),
235
+ 201,
236
+ );
237
+ });
238
+
239
+ app.patch("/v1/workspaces/:workspaceId/automations/triggers/:triggerId", async (c) => {
240
+ const workspaceId = c.req.param("workspaceId");
241
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
242
+ const request = await parseRequestJson(c, UpdateAutomationTriggerRequest);
243
+ const existing = (await listAutomationTriggers(deps.db, workspaceId)).find(
244
+ (trigger) => trigger.id === c.req.param("triggerId"),
245
+ );
246
+ if (!existing)
247
+ throw new HTTPException(404, {
248
+ message: "automation trigger not found",
249
+ });
250
+ assertGenericTriggerMutable(existing);
251
+ const credentialRestriction =
252
+ existing.sessionTemplate.credentialRestriction ??
253
+ (await automationCredentialRestrictionForGrant(deps.db, grant));
254
+ requireAutomationCredentialRestrictionPermissions(
255
+ credentialRestriction,
256
+ (request.sessionTemplate ?? existing.sessionTemplate).firstPartyMcpPermissions,
257
+ );
258
+ if (request.configuration) {
259
+ requireAutomationAdapter(existing.adapterId).validateTriggerConfiguration(
260
+ request.configuration,
261
+ );
262
+ }
263
+ if (request.parameters) {
264
+ requireAutomationAdapter(existing.adapterId).validateTriggerParameters(request.parameters);
265
+ }
266
+ if (request.sessionTemplate) {
267
+ request.sessionTemplate.bundledSkillIds = await callerBundleSelection(
268
+ deps,
269
+ grant,
270
+ request.sessionTemplate.bundledSkillIds,
271
+ );
272
+ for (const permission of request.sessionTemplate.firstPartyMcpPermissions) {
273
+ requirePermission(grant, permission);
274
+ }
275
+ }
276
+ const catalogSettings = (
277
+ await resolveWorkspaceCatalogSettings(deps.db, deps.settings, {
278
+ accountId: grant.accountId,
279
+ workspaceId,
280
+ })
281
+ ).settings;
282
+ const normalizedRequest = request.sessionTemplate
283
+ ? {
284
+ ...request,
285
+ sessionTemplate: {
286
+ ...request.sessionTemplate,
287
+ model: canonicalConfiguredModel(catalogSettings, request.sessionTemplate.model) ?? null,
288
+ },
289
+ }
290
+ : request;
291
+ if (normalizedRequest.sessionTemplate?.model) {
292
+ await assertWorkspaceModelPolicyAllows(
293
+ deps.db,
294
+ catalogSettings,
295
+ workspaceId,
296
+ normalizedRequest.sessionTemplate.model,
297
+ );
298
+ }
299
+ const effectiveModel = normalizedRequest.sessionTemplate
300
+ ? normalizedRequest.sessionTemplate.model
301
+ : existing.sessionTemplate.model;
302
+ const materialExecutionChange =
303
+ request.eventTypes !== undefined ||
304
+ request.configuration !== undefined ||
305
+ request.parameters !== undefined ||
306
+ request.sessionTemplate !== undefined ||
307
+ (request.status === "active" && existing.status !== "active");
308
+ const beforeUpdateCommit =
309
+ materialExecutionChange && effectiveModel
310
+ ? workspaceCustomModelCommitGuard({
311
+ settings: catalogSettings,
312
+ accountId: grant.accountId,
313
+ workspaceId,
314
+ modelId: effectiveModel,
315
+ })
316
+ : undefined;
317
+ try {
318
+ const trigger = await updateAutomationTrigger(deps.db, {
319
+ workspaceId,
320
+ triggerId: c.req.param("triggerId"),
321
+ subjectId: grant.subjectId,
322
+ request: normalizedRequest,
323
+ ...(credentialRestriction ? { credentialRestriction } : {}),
324
+ ...(beforeUpdateCommit ? { beforeUpdateCommit } : {}),
325
+ });
326
+ if (!trigger)
327
+ throw new HTTPException(404, {
328
+ message: "automation trigger not found",
329
+ });
330
+ return c.json(trigger);
331
+ } catch (error) {
332
+ if (error instanceof AutomationRevisionConflictError) {
333
+ throw new HTTPException(409, { message: error.message });
334
+ }
335
+ throw error;
336
+ }
337
+ });
338
+
339
+ app.delete("/v1/workspaces/:workspaceId/automations/triggers/:triggerId", async (c) => {
340
+ const workspaceId = c.req.param("workspaceId");
341
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
342
+ const expectedRevision = Number(c.req.query("expectedRevision"));
343
+ if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 1) {
344
+ throw new HTTPException(400, {
345
+ message: "expectedRevision must be a positive integer",
346
+ });
347
+ }
348
+ const existing = (await listAutomationTriggers(deps.db, workspaceId)).find(
349
+ (trigger) => trigger.id === c.req.param("triggerId"),
350
+ );
351
+ if (!existing)
352
+ throw new HTTPException(404, {
353
+ message: "automation trigger not found",
354
+ });
355
+ assertGenericTriggerMutable(existing);
356
+ try {
357
+ const trigger = await updateAutomationTrigger(deps.db, {
358
+ workspaceId,
359
+ triggerId: c.req.param("triggerId"),
360
+ subjectId: grant.subjectId,
361
+ request: { expectedRevision, status: "disabled" },
362
+ });
363
+ if (!trigger)
364
+ throw new HTTPException(404, {
365
+ message: "automation trigger not found",
366
+ });
367
+ return c.body(null, 204);
368
+ } catch (error) {
369
+ if (error instanceof AutomationRevisionConflictError) {
370
+ throw new HTTPException(409, { message: error.message });
371
+ }
372
+ throw error;
373
+ }
374
+ });
375
+
376
+ app.get("/v1/workspaces/:workspaceId/automations/runs", async (c) => {
377
+ const workspaceId = c.req.param("workspaceId");
378
+ await requireAccessGrant(c, deps, workspaceId, "workspace:read");
379
+ return c.json({ runs: await listAutomationRuns(deps.db, workspaceId) });
380
+ });
381
+
382
+ app.post("/v1/workspaces/:workspaceId/automations/sources/:sourceId/events", async (c) => {
383
+ const workspaceId = c.req.param("workspaceId");
384
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
385
+ const request = await parseRequestJson(c, TriggerAutomationManuallyRequest);
386
+ const credentialRestriction = await automationCredentialRestrictionForGrant(deps.db, grant);
387
+ const source = await requireSource(deps, grant.accountId, workspaceId, c.req.param("sourceId"));
388
+ if (source.status !== "active") {
389
+ throw new HTTPException(409, {
390
+ message: "automation source is disabled",
391
+ });
392
+ }
393
+ const normalizedEvent = AutomationNormalizedEvent.parse({
394
+ adapterId: source.adapterId,
395
+ eventType: request.eventType,
396
+ occurrenceKey: request.occurrenceKey,
397
+ occurredAt: request.occurredAt,
398
+ subject: request.subject,
399
+ resource: request.resource,
400
+ payload: request.payload,
401
+ });
402
+ const bytes = new TextEncoder().encode(stableJson(request));
403
+ try {
404
+ return c.json(
405
+ await acceptAutomationEvent(deps, source, {
406
+ deliveryKey:
407
+ request.deliveryId ?? `manual:${automationRequestDigest(source.adapterId, bytes)}`,
408
+ requestDigest: automationRequestDigest(source.adapterId, bytes),
409
+ normalizedEvent,
410
+ ...(credentialRestriction ? { credentialRestriction } : {}),
411
+ }),
412
+ 202,
413
+ );
414
+ } catch (error) {
415
+ if (
416
+ error instanceof AutomationDeliveryConflictError ||
417
+ error instanceof AutomationCredentialRestrictionConflictError
418
+ ) {
419
+ throw new HTTPException(409, { message: error.message });
420
+ }
421
+ throw error;
422
+ }
423
+ });
424
+
425
+ app.post("/v1/webhooks/automations/:endpointId", async (c) => {
426
+ const endpointId = c.req.param("endpointId");
427
+ if (
428
+ !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
429
+ endpointId,
430
+ )
431
+ ) {
432
+ throw new HTTPException(404, {
433
+ message: "automation endpoint not found",
434
+ });
435
+ }
436
+ const endpoint = await resolveAutomationWebhookEndpoint(deps.db, endpointId);
437
+ if (!endpoint)
438
+ throw new HTTPException(404, {
439
+ message: "automation endpoint not found",
440
+ });
441
+ const source = await requireSource(
442
+ deps,
443
+ endpoint.accountId,
444
+ endpoint.workspaceId,
445
+ endpoint.sourceId,
446
+ );
447
+ if (source.status !== "active") {
448
+ throw new HTTPException(410, {
449
+ message: "automation source is disabled",
450
+ });
451
+ }
452
+ const rawBody = await readAutomationWebhookBody(c.req.raw, AUTOMATION_WEBHOOK_MAX_BYTES);
453
+ const adapter = requireAutomationAdapter(source.adapterId);
454
+ const secret = decryptVariableSetValue(
455
+ requireEncryptionKey(deps),
456
+ source.webhookSecretEncrypted,
457
+ );
458
+ if (
459
+ !adapter.verify({
460
+ rawBody,
461
+ headers: c.req.raw.headers,
462
+ secret,
463
+ sourceConfiguration: source.configuration,
464
+ })
465
+ ) {
466
+ throw new HTTPException(401, {
467
+ message: "automation signature is invalid",
468
+ });
469
+ }
470
+ const requestDigest = automationRequestDigest(source.adapterId, rawBody);
471
+ try {
472
+ const result = await acceptAutomationEvent(deps, source, {
473
+ deliveryKey: adapter.deliveryKey({
474
+ headers: c.req.raw.headers,
475
+ requestDigest,
476
+ }),
477
+ requestDigest,
478
+ normalizedEvent: adapter.normalize({
479
+ rawBody,
480
+ headers: c.req.raw.headers,
481
+ sourceConfiguration: source.configuration,
482
+ }),
483
+ });
484
+ return c.json(result, 202);
485
+ } catch (error) {
486
+ if (
487
+ error instanceof AutomationDeliveryConflictError ||
488
+ error instanceof AutomationCredentialRestrictionConflictError
489
+ ) {
490
+ throw new HTTPException(409, { message: error.message });
491
+ }
492
+ throw error;
493
+ }
494
+ });
495
+ }
496
+
497
+ export async function acceptAutomationEvent(
498
+ deps: ApiRouteDeps,
499
+ source: AutomationSourceSecret,
500
+ input: {
501
+ deliveryKey: string;
502
+ requestDigest: string;
503
+ normalizedEvent: AutomationNormalizedEvent;
504
+ /** Canonical manual caller restriction, not normalized event metadata. */
505
+ credentialRestriction?: "developer_setup";
506
+ },
507
+ ) {
508
+ const { credentialRestriction: _untrustedRestriction, ...eventData } = input.normalizedEvent;
509
+ const normalizedEvent = AutomationNormalizedEvent.parse(eventData);
510
+ if (normalizedEvent.adapterId !== source.adapterId) {
511
+ throw new HTTPException(422, {
512
+ message: "automation event adapter does not match source",
513
+ });
514
+ }
515
+ const adapter = requireAutomationAdapter(source.adapterId);
516
+ const triggers = await listActiveAutomationTriggersForSource(deps.db, {
517
+ accountId: source.accountId,
518
+ workspaceId: source.workspaceId,
519
+ sourceId: source.id,
520
+ });
521
+ const matchingByEvent = triggers.filter((trigger) =>
522
+ adapter.matches({ event: normalizedEvent, trigger }),
523
+ );
524
+ if (matchingByEvent.length > AUTOMATION_MAX_MATCHED_TRIGGERS) {
525
+ throw new HTTPException(422, {
526
+ message: `automation event matches more than ${AUTOMATION_MAX_MATCHED_TRIGGERS} triggers`,
527
+ });
528
+ }
529
+ const stored =
530
+ matchingByEvent.length === 0
531
+ ? await recordAutomationEvent(deps.db, {
532
+ accountId: source.accountId,
533
+ workspaceId: source.workspaceId,
534
+ sourceId: source.id,
535
+ sourceVersion: source.version,
536
+ sourceConfiguration: source.configuration,
537
+ matchedTriggerRevisions: [],
538
+ deliveryKey: input.deliveryKey,
539
+ requestDigest: input.requestDigest,
540
+ normalizedEvent,
541
+ ...(input.credentialRestriction
542
+ ? { credentialRestriction: input.credentialRestriction }
543
+ : {}),
544
+ ignoredReason: "no_matching_triggers",
545
+ })
546
+ : await withWorkspaceGatewayCustomModelReadLock(
547
+ deps.db,
548
+ { accountId: source.accountId, workspaceId: source.workspaceId },
549
+ async (gatewayLockedDb) =>
550
+ await withWorkspaceOpenRouterCustomModelReadLock(
551
+ gatewayLockedDb,
552
+ { accountId: source.accountId, workspaceId: source.workspaceId },
553
+ async (lockedDb) => {
554
+ const catalogSourceSettings = deps.catalogSourceSettings ?? deps.settings;
555
+ const catalogSettings = (
556
+ await resolveWorkspaceCatalogSettings(lockedDb, catalogSourceSettings, {
557
+ accountId: source.accountId,
558
+ workspaceId: source.workspaceId,
559
+ })
560
+ ).settings;
561
+ const matchingAtAcceptance = [];
562
+ for (const trigger of matchingByEvent) {
563
+ try {
564
+ const render = adapter.render({
565
+ event: normalizedEvent,
566
+ trigger,
567
+ source,
568
+ });
569
+ requireAutomationCredentialRestrictionPermissions(
570
+ trigger.sessionTemplate.credentialRestriction ?? input.credentialRestriction,
571
+ render.sessionTemplate.firstPartyMcpPermissions,
572
+ );
573
+ const requestedModel =
574
+ render.sessionTemplate.model ?? catalogSettings.openaiModel;
575
+ const model = canonicalConfiguredModel(catalogSettings, requestedModel);
576
+ if (!model) continue;
577
+ await assertWorkspaceModelPolicyAllows(
578
+ lockedDb,
579
+ catalogSettings,
580
+ source.workspaceId,
581
+ model,
582
+ );
583
+ matchingAtAcceptance.push(trigger);
584
+ } catch (error) {
585
+ if (isUnprocessableEntity(error)) continue;
586
+ throw error;
587
+ }
588
+ }
589
+ return await recordAutomationEvent(lockedDb, {
590
+ accountId: source.accountId,
591
+ workspaceId: source.workspaceId,
592
+ sourceId: source.id,
593
+ sourceVersion: source.version,
594
+ sourceConfiguration: source.configuration,
595
+ matchedTriggerRevisions: matchingAtAcceptance.map((trigger) => ({
596
+ triggerId: trigger.id,
597
+ revision: trigger.revision,
598
+ })),
599
+ deliveryKey: input.deliveryKey,
600
+ requestDigest: input.requestDigest,
601
+ normalizedEvent,
602
+ ...(input.credentialRestriction
603
+ ? { credentialRestriction: input.credentialRestriction }
604
+ : {}),
605
+ ignoredReason:
606
+ matchingAtAcceptance.length === 0 ? "no_executable_triggers" : null,
607
+ });
608
+ },
609
+ ),
610
+ );
611
+ const matching = await getAutomationTriggerRevisions(deps.db, {
612
+ workspaceId: source.workspaceId,
613
+ refs: stored.event.matchedTriggerRevisions,
614
+ });
615
+ const acceptedSource = {
616
+ ...source,
617
+ version: stored.event.sourceVersion,
618
+ configuration: stored.event.sourceConfiguration,
619
+ };
620
+ const { credentialRestriction, ...acceptedEvent } = stored.event.normalizedEvent;
621
+ const runIds: string[] = [];
622
+ const triggerAutomationRun = deps.workflowClient.triggerAutomationRun;
623
+ if (matching.length > 0 && !triggerAutomationRun) {
624
+ throw new HTTPException(503, {
625
+ message: "automation dispatcher is unavailable",
626
+ });
627
+ }
628
+ for (const trigger of matching) {
629
+ const render = adapter.render({
630
+ event: acceptedEvent,
631
+ trigger,
632
+ source: acceptedSource,
633
+ });
634
+ const acceptedExecution = buildAutomationAcceptedExecution({
635
+ accountId: source.accountId,
636
+ workspaceId: source.workspaceId,
637
+ source: acceptedSource,
638
+ trigger,
639
+ eventId: stored.event.id,
640
+ event: acceptedEvent,
641
+ render,
642
+ ...(credentialRestriction ? { credentialRestriction } : {}),
643
+ });
644
+ const { run } = await createAutomationRun(deps.db, {
645
+ accountId: source.accountId,
646
+ workspaceId: source.workspaceId,
647
+ sourceId: source.id,
648
+ triggerId: trigger.id,
649
+ triggerRevision: trigger.revision,
650
+ eventId: stored.event.id,
651
+ occurrenceKey: stored.event.normalizedEvent.occurrenceKey,
652
+ acceptedExecution,
653
+ });
654
+ runIds.push(run.id);
655
+ await triggerAutomationRun!({
656
+ accountId: source.accountId,
657
+ workspaceId: source.workspaceId,
658
+ runId: run.id,
659
+ });
660
+ }
661
+ return AutomationWebhookResult.parse({
662
+ accepted: true,
663
+ duplicate: stored.duplicate,
664
+ ignoredReason: stored.event.ignoredReason,
665
+ eventId: stored.event.id,
666
+ runIds,
667
+ });
668
+ }
669
+
670
+ function isUnprocessableEntity(error: unknown): boolean {
671
+ return (
672
+ error instanceof Error &&
673
+ "status" in error &&
674
+ (error as Error & { status?: unknown }).status === 422
675
+ );
676
+ }
677
+
678
+ function workspaceCustomModelCommitGuard(input: {
679
+ settings: ApiRouteDeps["settings"];
680
+ accountId: string;
681
+ workspaceId: string;
682
+ modelId: string;
683
+ }): ((tx: ApiRouteDeps["db"]) => Promise<void>) | undefined {
684
+ const reference = workspaceCustomModelReference(input.settings, input.modelId);
685
+ if (!reference) return undefined;
686
+ return async (tx): Promise<void> => {
687
+ const active = await lockActiveCustomModelForAdmission(tx, {
688
+ accountId: input.accountId,
689
+ workspaceId: input.workspaceId,
690
+ reference,
691
+ });
692
+ if (!active) {
693
+ throw modelUnavailableHttpException(input.modelId);
694
+ }
695
+ };
696
+ }
697
+
698
+ async function requireSource(
699
+ deps: ApiRouteDeps,
700
+ accountId: string,
701
+ workspaceId: string,
702
+ sourceId: string,
703
+ ): Promise<AutomationSourceSecret> {
704
+ const source = await getAutomationSourceSecret(deps.db, {
705
+ accountId,
706
+ workspaceId,
707
+ sourceId,
708
+ });
709
+ if (!source) throw new HTTPException(404, { message: "automation source not found" });
710
+ return source;
711
+ }
712
+
713
+ function assertGenericSourceMutable(source: AutomationSourceSecret): void {
714
+ assertGenericTriggerMutable(source);
715
+ }
716
+
717
+ function assertGenericTriggerMutable(trigger: { adapterId: string }): void {
718
+ if (trigger.adapterId === "source-control.pull-request.v1") {
719
+ throw new HTTPException(409, {
720
+ message: "PR Review automations must be managed through the PR Review setup API",
721
+ });
722
+ }
723
+ }
724
+
725
+ function requireEncryptionKey(deps: ApiRouteDeps): Uint8Array {
726
+ const key = environmentsEncryptionKeyBytes(deps.settings);
727
+ if (!key) {
728
+ throw new HTTPException(503, {
729
+ message: "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for automations",
730
+ });
731
+ }
732
+ return key;
733
+ }
734
+
735
+ export async function readAutomationWebhookBody(
736
+ request: Request,
737
+ maxBytes: number,
738
+ ): Promise<Uint8Array> {
739
+ const declared = Number(request.headers.get("content-length"));
740
+ if (Number.isFinite(declared) && declared > maxBytes) {
741
+ throw new HTTPException(413, {
742
+ message: "automation webhook payload is too large",
743
+ });
744
+ }
745
+ if (!request.body) return new Uint8Array();
746
+ const reader = request.body.getReader();
747
+ const chunks: Uint8Array[] = [];
748
+ let length = 0;
749
+ while (true) {
750
+ const { value, done } = await reader.read();
751
+ if (done) break;
752
+ length += value.byteLength;
753
+ if (length > maxBytes) {
754
+ await reader.cancel();
755
+ throw new HTTPException(413, {
756
+ message: "automation webhook payload is too large",
757
+ });
758
+ }
759
+ chunks.push(value);
760
+ }
761
+ const result = new Uint8Array(length);
762
+ let offset = 0;
763
+ for (const chunk of chunks) {
764
+ result.set(chunk, offset);
765
+ offset += chunk.byteLength;
766
+ }
767
+ return result;
768
+ }