@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,870 @@
1
+ import { isVerifiedDelegatedHumanAuthorization, knowledgeContextForGateway } from "@opengeni/core";
2
+ import { createHash, randomBytes } from "node:crypto";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
5
+ import { withSiteSessionOrigin } from "@opengeni/core";
6
+ import { resolveSiteSessionOrigin } from "./site-session-origin";
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema,
12
+ type CallToolResult,
13
+ } from "@modelcontextprotocol/sdk/types.js";
14
+ import { CODEX_CLIENT_VERSION } from "@opengeni/codex";
15
+ import { IntegrationInvocationError } from "@opengeni/capabilities";
16
+ import type { Settings } from "@opengeni/config";
17
+ import {
18
+ FIRST_PARTY_MCP_TOOL_NAMES,
19
+ ToolGatewayCallRequest,
20
+ ToolGatewayCallResponse,
21
+ ToolGatewayApprovalRequest,
22
+ ToolGatewayApprovalResponse,
23
+ ToolGatewayDeclarationsResponse,
24
+ type AccessGrant,
25
+ type ToolGatewayCatalog,
26
+ type ToolGatewayIdentity,
27
+ type ToolRef,
28
+ } from "@opengeni/contracts";
29
+ import {
30
+ availableMcpAccountBindings,
31
+ buildApiIntegrationMcpServers,
32
+ expandApiIntegrationAccountRoutes,
33
+ expandMcpAccountRoutes,
34
+ hasPermission,
35
+ externalActorContinuationForAuthorization,
36
+ isVerifiedOrganizationServiceAuthorization,
37
+ externalContinuationCommitAuthorizer,
38
+ requireResolvedAccessGrantAuthorization,
39
+ resolveCodexAppsCredentialIdForRun,
40
+ resolveWorkspaceCatalogSettings,
41
+ settingsWithEnabledCapabilityMcpServers,
42
+ type AccessGrantAuthorization,
43
+ type ApiRouteDeps,
44
+ } from "@opengeni/core";
45
+ import {
46
+ buildCodexTokenResolver,
47
+ buildConnectionTokenResolver,
48
+ buildSlackApiRateLimiter,
49
+ lockActiveExternalOrganizationKeyAuthority,
50
+ withAccountRls,
51
+ requireWorkspace,
52
+ withCodexAppsRequestAuthorization,
53
+ consumeToolGatewayApproval,
54
+ getWorkspaceArtifactContentRef,
55
+ issueToolGatewayApproval,
56
+ ToolGatewayApprovalOperationStartedError,
57
+ ToolGatewayApprovalRateLimitError,
58
+ WorkspaceArtifactNotFoundError,
59
+ type ApiIntegrationRuntime,
60
+ } from "@opengeni/db";
61
+ import {
62
+ type LocalMcpServerRegistration,
63
+ type PreparedWorkspaceToolGatewayTools,
64
+ type ResolveConnectionCredentialInput,
65
+ type ResolveConnectionCredentialResult,
66
+ prepareWorkspaceToolGatewayTools,
67
+ } from "@opengeni/runtime/workspace-tool-gateway";
68
+ import {
69
+ ToolGatewayApprovalRequiredError,
70
+ ToolGatewayCatalogStaleError,
71
+ ToolGatewayInputValidationError,
72
+ ToolGatewayToolNotFoundError,
73
+ digestCanonicalJson,
74
+ generateToolGatewayDeclarations,
75
+ type PreparedToolGatewayCall,
76
+ type ToolGatewayDefinition,
77
+ } from "@opengeni/tool-gateway";
78
+ import { HTTPException } from "hono/http-exception";
79
+
80
+ import { ApiHttpError } from "./http/api-error";
81
+ import { buildDocumentsMcpServer } from "./mcp/documents";
82
+ import { buildFilesMcpServer } from "./mcp/files";
83
+ import { buildOpenGeniMcpServer } from "./mcp/server";
84
+ import { startWorkspaceToolGatewayObservation } from "./workspace-tool-gateway-observability";
85
+ import type { Observability } from "@opengeni/observability";
86
+
87
+ export type PreparedWorkspaceToolGateway = Pick<
88
+ PreparedWorkspaceToolGatewayTools,
89
+ "toolGateway" | "toolGatewayCatalog" | "close"
90
+ > & {
91
+ toolGateway: NonNullable<PreparedWorkspaceToolGatewayTools["toolGateway"]>;
92
+ toolGatewayCatalog: NonNullable<PreparedWorkspaceToolGatewayTools["toolGatewayCatalog"]>;
93
+ reauthorize?: () => Promise<void>;
94
+ };
95
+
96
+ type WorkspaceSiteToolContext = {
97
+ siteArtifactId: string;
98
+ siteVersionId: string;
99
+ identity: { serverId: string; toolName: string };
100
+ };
101
+
102
+ type AuthorizeWorkspaceSiteTool = (
103
+ db: ApiRouteDeps["db"],
104
+ grant: AccessGrant,
105
+ context: WorkspaceSiteToolContext,
106
+ ) => Promise<void>;
107
+
108
+ export function grantUsesAttemptScopedMcp(grant: AccessGrant): boolean {
109
+ return (
110
+ grant.principalKind === "agent_attempt" ||
111
+ grant.metadata?.delegated === true ||
112
+ typeof grant.metadata?.sessionId === "string"
113
+ );
114
+ }
115
+
116
+ export function requireWorkspaceToolGatewayGrant(grant: AccessGrant): void {
117
+ if (
118
+ grantUsesAttemptScopedMcp(grant) ||
119
+ (grant.principalKind !== undefined && grant.principalKind !== "human_session")
120
+ ) {
121
+ throw new HTTPException(403, { message: "current-human tool access required" });
122
+ }
123
+ }
124
+
125
+ export function requireWorkspaceToolGatewayAuthorization(
126
+ authorization: AccessGrantAuthorization,
127
+ ): AccessGrant {
128
+ const grant = requireResolvedAccessGrantAuthorization(
129
+ authorization,
130
+ authorization.grant.workspaceId,
131
+ );
132
+ if (isVerifiedOrganizationServiceAuthorization(authorization)) {
133
+ if (grantUsesAttemptScopedMcp(grant))
134
+ throw new HTTPException(403, {
135
+ message: "service tool access cannot carry attempt authority",
136
+ });
137
+ return grant;
138
+ }
139
+ requireWorkspaceToolGatewayGrant(grant);
140
+ if (
141
+ !authorization.canonicalManagedHumanSession &&
142
+ !authorization.canonicalLocalHumanSession &&
143
+ !isVerifiedDelegatedHumanAuthorization(authorization) &&
144
+ !externalActorContinuationForAuthorization(authorization)
145
+ ) {
146
+ throw new HTTPException(403, { message: "current-human tool access required" });
147
+ }
148
+ return grant;
149
+ }
150
+
151
+ export async function prepareWorkspaceToolGateway(
152
+ routeDeps: ApiRouteDeps,
153
+ authorization: AccessGrantAuthorization,
154
+ ): Promise<PreparedWorkspaceToolGateway> {
155
+ const grant = requireWorkspaceToolGatewayAuthorization(authorization);
156
+ const external = externalActorContinuationForAuthorization(authorization);
157
+ const reauthorizeExternal = externalContinuationCommitAuthorizer(authorization);
158
+ const service = isVerifiedOrganizationServiceAuthorization(authorization);
159
+ const scope = {
160
+ accountId: grant.accountId,
161
+ workspaceId: grant.workspaceId,
162
+ subjectId: grant.subjectId,
163
+ };
164
+ const permissions = [...grant.permissions];
165
+ const reauthorize =
166
+ external || service
167
+ ? async () => {
168
+ await withAccountRls(routeDeps.db, scope.accountId, async (tx) => {
169
+ if (reauthorizeExternal) await reauthorizeExternal(tx);
170
+ else {
171
+ const live = await lockActiveExternalOrganizationKeyAuthority(
172
+ tx,
173
+ scope.accountId,
174
+ scope.subjectId.slice("api_key:".length),
175
+ scope.workspaceId,
176
+ );
177
+ const workspace = await requireWorkspace(tx, scope.workspaceId);
178
+ if (
179
+ !live ||
180
+ permissions.some(
181
+ (permission) => !hasPermission(live.permissions, permission, live.permissionMode),
182
+ ) ||
183
+ workspace.accountId !== scope.accountId ||
184
+ workspace.kind !== "shared"
185
+ )
186
+ throw new HTTPException(403, { message: "service gateway authority changed" });
187
+ }
188
+ });
189
+ }
190
+ : undefined;
191
+ await reauthorize?.();
192
+ const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
193
+ routeDeps,
194
+ grant,
195
+ undefined,
196
+ reauthorize,
197
+ );
198
+ try {
199
+ await reauthorize?.();
200
+ } catch (error) {
201
+ await prepared.close();
202
+ throw error;
203
+ }
204
+ return { ...prepared, ...(reauthorize ? { reauthorize } : {}) };
205
+ }
206
+
207
+ export async function prepareMcpOAuthWorkspaceToolGateway(
208
+ routeDeps: ApiRouteDeps,
209
+ grant: AccessGrant,
210
+ allowedIdentities: readonly { serverId: string; toolName: string }[],
211
+ ): Promise<PreparedWorkspaceToolGateway> {
212
+ if (grant.metadata?.mcpOAuth !== true || grant.principalKind !== "human_session") {
213
+ throw new HTTPException(403, { message: "MCP OAuth authority is invalid" });
214
+ }
215
+ return await prepareWorkspaceToolGatewayForGrant(routeDeps, grant, allowedIdentities);
216
+ }
217
+
218
+ export async function prepareWorkspaceToolGatewayForGrant(
219
+ routeDeps: ApiRouteDeps,
220
+ grant: AccessGrant,
221
+ allowedIdentities?: readonly { serverId: string; toolName: string }[],
222
+ ): Promise<PreparedWorkspaceToolGateway> {
223
+ return await prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, allowedIdentities);
224
+ }
225
+
226
+ /** Keep live caller authority independent of native connection acquisition and refresh. */
227
+ export function withWorkspaceConnectionAuthorization(
228
+ resolve: ReturnType<typeof buildConnectionTokenResolver>,
229
+ reauthorize?: () => Promise<void>,
230
+ ): ReturnType<typeof buildConnectionTokenResolver> {
231
+ if (!reauthorize) return resolve;
232
+ return async (input) => {
233
+ await reauthorize();
234
+ const result = await resolve(input);
235
+ await reauthorize();
236
+ if (result.status !== "ok") return result;
237
+ return {
238
+ ...result,
239
+ authorizeProviderRequest: async () => {
240
+ try {
241
+ await reauthorize();
242
+ return result.authorizeProviderRequest ? await result.authorizeProviderRequest() : true;
243
+ } catch {
244
+ return false;
245
+ }
246
+ },
247
+ };
248
+ };
249
+ }
250
+
251
+ async function prepareWorkspaceToolGatewayForGrantInternal(
252
+ routeDeps: ApiRouteDeps,
253
+ grant: AccessGrant,
254
+ allowedIdentities?: readonly { serverId: string; toolName: string }[],
255
+ reauthorize?: () => Promise<void>,
256
+ ): Promise<PreparedWorkspaceToolGateway> {
257
+ const catalogSourceSettings = routeDeps.catalogSourceSettings ?? routeDeps.settings;
258
+ const resolvedCatalog = await resolveWorkspaceCatalogSettings(
259
+ routeDeps.db,
260
+ catalogSourceSettings,
261
+ {
262
+ accountId: grant.accountId,
263
+ workspaceId: grant.workspaceId,
264
+ },
265
+ );
266
+ let integrations: readonly ApiIntegrationRuntime[] = [];
267
+ const settings = await settingsWithEnabledCapabilityMcpServers(
268
+ routeDeps.db,
269
+ grant.workspaceId,
270
+ resolvedCatalog.settings,
271
+ {
272
+ subjectId: grant.subjectId,
273
+ onResolvedApiIntegrations: (resolved) => {
274
+ integrations = resolved;
275
+ },
276
+ },
277
+ );
278
+ // Transport admission has already verified the current caller. A service
279
+ // receives workspace accounts only; human transports use their exact subject.
280
+ const accountBindings = await availableMcpAccountBindings({
281
+ db: routeDeps.db,
282
+ accountId: grant.accountId,
283
+ workspaceId: grant.workspaceId,
284
+ settings,
285
+ tools: allGatewayToolRefs(settings),
286
+ source:
287
+ grant.principalKind === "service" || grant.principalKind === "api_key"
288
+ ? { kind: "none" }
289
+ : { kind: "subject", accountId: grant.accountId, subjectId: grant.subjectId },
290
+ });
291
+ const accountRoutes = expandMcpAccountRoutes({
292
+ settings,
293
+ tools: allGatewayToolRefs(settings),
294
+ bindings: accountBindings,
295
+ });
296
+ // OAuth/Site identities are account-qualified. Intersect only after expansion;
297
+ // canonical connector IDs are never aliases for an account's execution route.
298
+ const gatewaySettings = workspaceToolGatewaySettingsForGrant(
299
+ accountRoutes.settings,
300
+ grant,
301
+ allowedIdentities,
302
+ );
303
+ const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
304
+ const deps = { ...routeDeps, catalogSourceSettings, settings: gatewaySettings };
305
+ const resolveConnection = withWorkspaceConnectionAuthorization(
306
+ buildConnectionTokenResolver(routeDeps.db, gatewaySettings),
307
+ reauthorize,
308
+ );
309
+ const resolveCredential = async (
310
+ input: ResolveConnectionCredentialInput,
311
+ ): Promise<ResolveConnectionCredentialResult> =>
312
+ await resolveConnection({
313
+ ...input,
314
+ ...(input.connectionRef.subjectScope === "subject" ? { subjectId: grant.subjectId } : {}),
315
+ });
316
+ const firstPartyServers = await Promise.all([
317
+ ...(gatewayServerIds.has("opengeni")
318
+ ? [inMemoryMcpRegistration("opengeni", buildOpenGeniMcpServer(deps, grant))]
319
+ : []),
320
+ ...(gatewayServerIds.has("files")
321
+ ? [inMemoryMcpRegistration("files", buildFilesMcpServer(deps, grant))]
322
+ : []),
323
+ ...(gatewayServerIds.has("docs") && hasPermission(grant.permissions, "documents:search")
324
+ ? [
325
+ inMemoryMcpRegistration(
326
+ "docs",
327
+ buildDocumentsMcpServer(
328
+ routeDeps.db,
329
+ grant.accountId,
330
+ grant.workspaceId,
331
+ routeDeps.getDocumentServices(),
332
+ {
333
+ knowledge: await knowledgeContextForGateway(routeDeps, grant),
334
+ settings: gatewaySettings,
335
+ },
336
+ ),
337
+ ),
338
+ ]
339
+ : []),
340
+ ]);
341
+ const apiIntegrationServers = buildApiIntegrationMcpServers({
342
+ settings: gatewaySettings,
343
+ integrations: expandApiIntegrationAccountRoutes({
344
+ integrations,
345
+ bindings: accountBindings,
346
+ tools: allGatewayToolRefs(gatewaySettings),
347
+ }),
348
+ authority: {
349
+ accountId: grant.accountId,
350
+ workspaceId: grant.workspaceId,
351
+ initiatingSubjectId: grant.subjectId,
352
+ },
353
+ resolveCredential: async (input) =>
354
+ await resolveConnection({
355
+ ...input,
356
+ ...(input.connectionRef.subjectScope === "subject" ? { subjectId: grant.subjectId } : {}),
357
+ }),
358
+ });
359
+ const codexAppsCredentialId = gatewayServerIds.has("codex_apps")
360
+ ? await resolveCodexAppsCredentialIdForRun(routeDeps.db, grant.workspaceId)
361
+ : null;
362
+ const codexAppsAuth = codexAppsCredentialId
363
+ ? (() => {
364
+ const resolver = buildCodexTokenResolver(
365
+ routeDeps.db,
366
+ settings,
367
+ grant.workspaceId,
368
+ codexAppsCredentialId,
369
+ );
370
+ return {
371
+ clientVersion: CODEX_CLIENT_VERSION,
372
+ withAuthorization: async <T>(
373
+ use: (token: { accessToken: string; chatgptAccountId: string | null }) => Promise<T>,
374
+ ): Promise<T> =>
375
+ await resolver.getToken().then(
376
+ async (token) =>
377
+ await withCodexAppsRequestAuthorization(
378
+ routeDeps.db,
379
+ {
380
+ workspaceId: grant.workspaceId,
381
+ credentialId: codexAppsCredentialId,
382
+ },
383
+ async () => await use(token),
384
+ ),
385
+ ),
386
+ };
387
+ })()
388
+ : undefined;
389
+ const localMcpServers = [...firstPartyServers, ...apiIntegrationServers];
390
+ const prepared = await prepareWorkspaceToolGatewayTools(
391
+ gatewaySettings,
392
+ allGatewayToolRefs(gatewaySettings),
393
+ {
394
+ accountId: grant.accountId,
395
+ workspaceId: grant.workspaceId,
396
+ subjectId: grant.subjectId,
397
+ credentialSubjectId: grant.subjectId,
398
+ mcpAccountLabels: accountRoutes.accountLabels,
399
+ resolveCredential,
400
+ slackRateLimit: buildSlackApiRateLimiter(routeDeps.db, gatewaySettings),
401
+ localMcpServers,
402
+ ...(codexAppsAuth ? { codexAppsAuth } : {}),
403
+ workspaceToolGateway: {
404
+ requireApproval: (entry, _caller, context) =>
405
+ entry.approval === "human" && context.transportMeta?.approvalConfirmed !== true,
406
+ filterDefinition: workspaceToolGatewayDefinitionFilter(gatewaySettings, allowedIdentities),
407
+ },
408
+ },
409
+ );
410
+ if (!prepared.toolGateway || !prepared.toolGatewayCatalog) {
411
+ await prepared.close().catch(() => undefined);
412
+ throw new Error("workspace tool gateway preparation did not produce a gateway");
413
+ }
414
+ return {
415
+ toolGateway: prepared.toolGateway,
416
+ toolGatewayCatalog: prepared.toolGatewayCatalog,
417
+ close: prepared.close,
418
+ };
419
+ }
420
+
421
+ export function workspaceToolGatewayDefinitionFilter(
422
+ settings: Pick<Settings, "allowedFirstPartyMcpTools">,
423
+ allowedIdentities?: readonly ToolGatewayIdentity[],
424
+ ): (definition: ToolGatewayDefinition) => boolean {
425
+ const allowedFirstPartyTools: ReadonlySet<string> = new Set(
426
+ settings.allowedFirstPartyMcpTools ?? FIRST_PARTY_MCP_TOOL_NAMES,
427
+ );
428
+ const frozenIdentityKeys = allowedIdentities
429
+ ? new Set(allowedIdentities.map(workspaceToolGatewayIdentityKey))
430
+ : null;
431
+ return (definition) => {
432
+ if (
433
+ definition.identity.serverId === "opengeni" &&
434
+ !allowedFirstPartyTools.has(definition.identity.toolName)
435
+ ) {
436
+ return false;
437
+ }
438
+ if (
439
+ definition.approval === "human" &&
440
+ definition.requiresProviderPreflight === true &&
441
+ !definition.preflightCall
442
+ ) {
443
+ return false;
444
+ }
445
+ return frozenIdentityKeys?.has(workspaceToolGatewayIdentityKey(definition.identity)) ?? true;
446
+ };
447
+ }
448
+
449
+ function workspaceToolGatewayIdentityKey(identity: ToolGatewayIdentity): string {
450
+ return JSON.stringify([identity.serverId, identity.toolName]);
451
+ }
452
+
453
+ export function workspaceToolGatewaySettingsForGrant(
454
+ settings: Settings,
455
+ grant: AccessGrant,
456
+ allowedIdentities?: readonly { serverId: string; toolName: string }[],
457
+ ): Settings {
458
+ const allowedServerIds = allowedIdentities
459
+ ? new Set(allowedIdentities.map((identity) => identity.serverId))
460
+ : null;
461
+ return {
462
+ ...settings,
463
+ mcpServers: settings.mcpServers.filter((server) => {
464
+ if (allowedServerIds && !allowedServerIds.has(server.id)) return false;
465
+ if (server.id === "docs" && !hasPermission(grant.permissions, "documents:search")) {
466
+ return false;
467
+ }
468
+ if (server.id === "files" && !hasPermission(grant.permissions, "files:read")) return false;
469
+ return true;
470
+ }),
471
+ };
472
+ }
473
+
474
+ export function buildWorkspaceToolGatewayMcpServer(
475
+ prepared: PreparedWorkspaceToolGateway,
476
+ grant: AccessGrant,
477
+ observability?: Observability,
478
+ ): Server {
479
+ const callableEntries = prepared.toolGatewayCatalog.entries.filter(
480
+ (entry) => entry.approval !== "human",
481
+ );
482
+ const server = new Server(
483
+ { name: "opengeni-tool-gateway", version: "1.0.0" },
484
+ { capabilities: { tools: {} } },
485
+ );
486
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
487
+ await prepared.reauthorize?.();
488
+ return {
489
+ tools: callableEntries.map((entry) => ({
490
+ name: entry.modelName,
491
+ ...(entry.title ? { title: entry.title } : {}),
492
+ ...(entry.description ? { description: entry.description } : {}),
493
+ inputSchema: entry.inputSchema,
494
+ ...(entry.outputSchema ? { outputSchema: entry.outputSchema } : {}),
495
+ ...(entry.annotations ? { annotations: entry.annotations } : {}),
496
+ ...(entry.icons ? { icons: entry.icons } : {}),
497
+ _meta: {
498
+ "opengeni/identity": entry.identity,
499
+ "opengeni/path": entry.codemodePath,
500
+ "opengeni/source": entry.source,
501
+ "opengeni/approval": entry.approval,
502
+ "opengeni/catalogDigest": prepared.toolGatewayCatalog.digest,
503
+ },
504
+ })),
505
+ };
506
+ });
507
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
508
+ const entry = callableEntries.find((candidate) => candidate.modelName === request.params.name);
509
+ if (!entry) throw new ToolGatewayToolNotFoundError();
510
+ const observation = startWorkspaceToolGatewayObservation(observability, {
511
+ adapter: "mcp",
512
+ operation: "call",
513
+ source: entry.source,
514
+ });
515
+ try {
516
+ await prepared.reauthorize?.();
517
+ const call = await prepared.toolGateway.prepareCall(
518
+ {
519
+ operationId: crypto.randomUUID(),
520
+ catalogDigest: prepared.toolGatewayCatalog.digest,
521
+ identity: entry.identity,
522
+ arguments: request.params.arguments ?? {},
523
+ caller: { kind: "mcp", subjectId: grant.subjectId },
524
+ },
525
+ { signal: extra.signal },
526
+ );
527
+ await prepared.reauthorize?.();
528
+ const result = (await call.execute()) as CallToolResult;
529
+ observation.end(result.isError ? "tool_error" : "ok");
530
+ return result;
531
+ } catch (error) {
532
+ observation.end(workspaceToolGatewayOutcome(error));
533
+ throw error;
534
+ }
535
+ });
536
+ return server;
537
+ }
538
+
539
+ export async function callWorkspaceToolGateway(
540
+ prepared: PreparedWorkspaceToolGateway,
541
+ grant: AccessGrant,
542
+ input: unknown,
543
+ db?: ApiRouteDeps["db"],
544
+ consumeApproval: typeof consumeToolGatewayApproval = consumeToolGatewayApproval,
545
+ observability?: Observability,
546
+ authorizeSiteTool: AuthorizeWorkspaceSiteTool = requireWorkspaceSiteToolAuthorization,
547
+ resolveOrigin: typeof resolveSiteSessionOrigin = resolveSiteSessionOrigin,
548
+ ) {
549
+ const request = ToolGatewayCallRequest.parse(input);
550
+ const operationId = request.operationId ?? crypto.randomUUID();
551
+ const entry = prepared.toolGatewayCatalog.entries.find(
552
+ (candidate) =>
553
+ candidate.identity.serverId === request.identity.serverId &&
554
+ candidate.identity.toolName === request.identity.toolName,
555
+ );
556
+ const observation = startWorkspaceToolGatewayObservation(observability, {
557
+ adapter: "http",
558
+ operation: "call",
559
+ source: entry?.source ?? "aggregate",
560
+ });
561
+ try {
562
+ const siteContext =
563
+ request.siteArtifactId && request.siteVersionId
564
+ ? {
565
+ siteArtifactId: request.siteArtifactId,
566
+ siteVersionId: request.siteVersionId,
567
+ identity: request.identity,
568
+ }
569
+ : null;
570
+ await prepared.reauthorize?.();
571
+ if (siteContext) {
572
+ if (!db) throw new HTTPException(503, { message: "site_tool_authorization_unavailable" });
573
+ await authorizeSiteTool(db, grant, siteContext);
574
+ }
575
+ // Approval is adapter-owned. Prepare provisionally so provider preflight can
576
+ // run, then require the ordinary single-use capability before execution.
577
+ const transportMeta: Record<string, unknown> = { approvalConfirmed: true };
578
+ const preparedCall = await prepared.toolGateway.prepareCall(
579
+ {
580
+ operationId,
581
+ catalogDigest: request.catalogDigest,
582
+ identity: request.identity,
583
+ arguments: request.arguments,
584
+ caller: { kind: "http", subjectId: grant.subjectId },
585
+ },
586
+ { transportMeta },
587
+ );
588
+ await prepared.reauthorize?.();
589
+ let approvalConfirmed = false;
590
+ const approvalRequired = preparedCall.entry.approval === "human";
591
+ if (approvalRequired && request.approvalToken && db) {
592
+ approvalConfirmed = await consumeApproval(db, {
593
+ tokenHash: hashOpaqueValue(request.approvalToken),
594
+ accountId: grant.accountId,
595
+ workspaceId: grant.workspaceId,
596
+ subjectId: grant.subjectId,
597
+ operationId,
598
+ catalogDigest: request.catalogDigest,
599
+ identity: request.identity,
600
+ argumentsDigest: digestCanonicalJson(request.arguments),
601
+ approvalAuthorityDigest: preparedCall.approvalAuthorityDigest,
602
+ });
603
+ }
604
+ if (approvalRequired && !approvalConfirmed) {
605
+ throw new HTTPException(409, { message: "tool_gateway_approval_required" });
606
+ }
607
+ transportMeta.approvalConfirmed = approvalConfirmed;
608
+ const origin =
609
+ siteContext && db
610
+ ? await resolveOrigin(
611
+ db,
612
+ grant.workspaceId,
613
+ siteContext.siteArtifactId,
614
+ siteContext.siteVersionId,
615
+ )
616
+ : null;
617
+ await prepared.reauthorize?.();
618
+ const result = await (origin
619
+ ? withSiteSessionOrigin(origin, () => preparedCall.execute())
620
+ : preparedCall.execute());
621
+ observation.end(result.isError ? "tool_error" : "ok");
622
+ return ToolGatewayCallResponse.parse({
623
+ operationId,
624
+ catalogDigest: prepared.toolGatewayCatalog.digest,
625
+ result,
626
+ });
627
+ } catch (error) {
628
+ observation.end(workspaceToolGatewayOutcome(error));
629
+ throwWorkspaceToolGatewayHttpError(error);
630
+ }
631
+ }
632
+
633
+ export async function approveWorkspaceToolGatewayCall(
634
+ prepared: PreparedWorkspaceToolGateway,
635
+ grant: AccessGrant,
636
+ db: ApiRouteDeps["db"],
637
+ input: unknown,
638
+ issueApproval: typeof issueToolGatewayApproval = issueToolGatewayApproval,
639
+ observability?: Observability,
640
+ ) {
641
+ const request = ToolGatewayApprovalRequest.parse(input);
642
+ let preparedCall: PreparedToolGatewayCall;
643
+ try {
644
+ await prepared.reauthorize?.();
645
+ preparedCall = await prepared.toolGateway.prepareCall(
646
+ {
647
+ operationId: request.operationId,
648
+ catalogDigest: request.catalogDigest,
649
+ identity: request.identity,
650
+ arguments: request.arguments,
651
+ caller: { kind: "http", subjectId: grant.subjectId },
652
+ },
653
+ { transportMeta: { approvalConfirmed: true } },
654
+ );
655
+ } catch (error) {
656
+ throwWorkspaceToolGatewayHttpError(error);
657
+ }
658
+ if (preparedCall.entry.approval !== "human") {
659
+ throw new HTTPException(422, { message: "tool_does_not_require_human_approval" });
660
+ }
661
+ const observation = startWorkspaceToolGatewayObservation(observability, {
662
+ adapter: "http",
663
+ operation: "approval",
664
+ source: preparedCall.entry.source,
665
+ });
666
+ const approvalToken = `ogta_${randomBytes(32).toString("base64url")}`;
667
+ const expiresAt = new Date(Date.now() + 5 * 60_000);
668
+ try {
669
+ await prepared.reauthorize?.();
670
+ await issueApproval(db, {
671
+ tokenHash: hashOpaqueValue(approvalToken),
672
+ accountId: grant.accountId,
673
+ workspaceId: grant.workspaceId,
674
+ subjectId: grant.subjectId,
675
+ operationId: request.operationId,
676
+ catalogDigest: request.catalogDigest,
677
+ identity: request.identity,
678
+ argumentsDigest: digestCanonicalJson(request.arguments),
679
+ approvalAuthorityDigest: preparedCall.approvalAuthorityDigest,
680
+ expiresAt,
681
+ });
682
+ } catch (error) {
683
+ if (error instanceof ToolGatewayApprovalRateLimitError) {
684
+ observation.end("rate_limited");
685
+ throw new HTTPException(429, { message: "tool_approval_rate_limited", cause: error });
686
+ }
687
+ if (error instanceof ToolGatewayApprovalOperationStartedError) {
688
+ observation.end("failed");
689
+ throw new ApiHttpError(409, {
690
+ code: "conflict",
691
+ message:
692
+ "This tool operation may already have started and cannot be approved again. Reconcile its outcome before creating a new operation.",
693
+ retryable: false,
694
+ outcomeUnknown: true,
695
+ details: {
696
+ code: "tool_gateway_operation_already_started",
697
+ operationId: request.operationId,
698
+ },
699
+ });
700
+ }
701
+ observation.end("failed");
702
+ throw error;
703
+ }
704
+ observation.end("ok");
705
+ return ToolGatewayApprovalResponse.parse({
706
+ operationId: request.operationId,
707
+ catalogDigest: request.catalogDigest,
708
+ identity: preparedCall.entry.identity,
709
+ approvalToken,
710
+ expiresAt: expiresAt.toISOString(),
711
+ });
712
+ }
713
+
714
+ function catalogStaleHttpError(): ApiHttpError {
715
+ return new ApiHttpError(409, {
716
+ code: "conflict",
717
+ message: "The workspace tool catalog changed; retry with the current catalog.",
718
+ retryable: true,
719
+ details: { code: "catalog_stale" },
720
+ });
721
+ }
722
+
723
+ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
724
+ if (error instanceof ToolGatewayCatalogStaleError) {
725
+ throw catalogStaleHttpError();
726
+ }
727
+ if (error instanceof ToolGatewayToolNotFoundError) {
728
+ throw new HTTPException(404, { message: error.code, cause: error });
729
+ }
730
+ if (error instanceof ToolGatewayInputValidationError) {
731
+ // The same value-free summary the model sees: which properties are missing
732
+ // or mistyped, never the submitted argument values.
733
+ throw new ApiHttpError(422, {
734
+ code: "validation_failed",
735
+ message: error.message,
736
+ retryable: false,
737
+ details: {
738
+ code: error.code,
739
+ issues: error.issues,
740
+ omittedIssueCount: error.omittedIssueCount,
741
+ },
742
+ });
743
+ }
744
+ if (error instanceof ToolGatewayApprovalRequiredError) {
745
+ throw new HTTPException(409, { message: error.code, cause: error });
746
+ }
747
+ if (error instanceof IntegrationInvocationError && error.outcome === "unknown") {
748
+ throw new ApiHttpError(502, {
749
+ code: "upstream_unavailable",
750
+ message:
751
+ "The integration call ended without a confirmed provider result. Inspect the external system before retrying.",
752
+ retryable: false,
753
+ outcomeUnknown: true,
754
+ details: { code: "tool_outcome_unknown", providerCode: error.code },
755
+ });
756
+ }
757
+ throw error;
758
+ }
759
+
760
+ export async function requireWorkspaceSiteToolAuthorization(
761
+ db: ApiRouteDeps["db"],
762
+ grant: AccessGrant,
763
+ context: WorkspaceSiteToolContext,
764
+ resolveVersion = getWorkspaceArtifactContentRef,
765
+ ): Promise<void> {
766
+ try {
767
+ const { status, version } = await resolveVersion(
768
+ db,
769
+ grant.workspaceId,
770
+ context.siteArtifactId,
771
+ context.siteVersionId,
772
+ );
773
+ const requested = version.requestedTools.some(
774
+ (identity) =>
775
+ identity.serverId === context.identity.serverId &&
776
+ identity.toolName === context.identity.toolName,
777
+ );
778
+ if (status !== "active" || version.id !== context.siteVersionId || !requested) {
779
+ throw new HTTPException(403, { message: "site_tool_not_authorized" });
780
+ }
781
+ } catch (error) {
782
+ if (error instanceof WorkspaceArtifactNotFoundError) {
783
+ throw new HTTPException(403, { message: "site_tool_not_authorized" });
784
+ }
785
+ throw error;
786
+ }
787
+ }
788
+
789
+ function hashOpaqueValue(value: string): string {
790
+ return createHash("sha256").update(value, "utf8").digest("hex");
791
+ }
792
+
793
+ function workspaceToolGatewayOutcome(error: unknown) {
794
+ if (error instanceof ToolGatewayApprovalRequiredError) return "approval_required" as const;
795
+ if (error instanceof ToolGatewayCatalogStaleError) return "catalog_stale" as const;
796
+ if (error instanceof ToolGatewayInputValidationError) return "invalid_input" as const;
797
+ if (error instanceof ToolGatewayToolNotFoundError) return "not_found" as const;
798
+ return "failed" as const;
799
+ }
800
+
801
+ function allGatewayToolRefs(settings: Settings): ToolRef[] {
802
+ return settings.mcpServers.map((server) => ({
803
+ kind: "mcp" as const,
804
+ id: server.id,
805
+ ...(server.connectionRef ? { optional: true } : {}),
806
+ }));
807
+ }
808
+
809
+ async function inMemoryMcpRegistration(
810
+ id: string,
811
+ server: McpServer,
812
+ ): Promise<LocalMcpServerRegistration> {
813
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
814
+ const client = new Client({ name: `opengeni-tool-gateway-${id}`, version: "1.0.0" });
815
+ let connected = false;
816
+ let closed = false;
817
+ type RuntimeMcpServer = LocalMcpServerRegistration["server"];
818
+ type RuntimeMcpTools = Awaited<ReturnType<RuntimeMcpServer["listTools"]>>;
819
+ type RuntimeMcpCallResult = Awaited<ReturnType<NonNullable<RuntimeMcpServer["callToolResult"]>>>;
820
+ return {
821
+ id,
822
+ server: {
823
+ name: `opengeni-tool-gateway-local:${id}`,
824
+ cacheToolsList: false,
825
+ connect: async () => {
826
+ if (closed) throw new Error(`Local MCP server ${id} is closed`);
827
+ if (connected) return;
828
+ await server.connect(serverTransport);
829
+ await client.connect(clientTransport);
830
+ connected = true;
831
+ },
832
+ close: async () => {
833
+ if (closed) return;
834
+ closed = true;
835
+ await Promise.allSettled([client.close(), server.close()]);
836
+ },
837
+ listTools: async () => (await client.listTools()).tools as unknown as RuntimeMcpTools,
838
+ callTool: async (toolName, args, _meta, options) =>
839
+ (
840
+ (await client.callTool(
841
+ { name: toolName, arguments: args ?? {} },
842
+ undefined,
843
+ options?.signal ? { signal: options.signal } : undefined,
844
+ )) as CallToolResult
845
+ ).content,
846
+ callToolResult: async (toolName, args, _meta, options) =>
847
+ (await client.callTool(
848
+ { name: toolName, arguments: args ?? {} },
849
+ undefined,
850
+ options?.signal ? { signal: options.signal } : undefined,
851
+ )) as unknown as RuntimeMcpCallResult,
852
+ invalidateToolsCache: async () => undefined,
853
+ },
854
+ };
855
+ }
856
+
857
+ export function toolGatewayCatalogResponse(catalog: ToolGatewayCatalog): ToolGatewayCatalog {
858
+ return catalog;
859
+ }
860
+
861
+ export function workspaceToolGatewayDeclarations(
862
+ prepared: PreparedWorkspaceToolGateway,
863
+ ): ToolGatewayDeclarationsResponse {
864
+ const moduleSpecifier = "@opengeni/sdk";
865
+ return ToolGatewayDeclarationsResponse.parse({
866
+ catalogDigest: prepared.toolGatewayCatalog.digest,
867
+ moduleSpecifier,
868
+ source: generateToolGatewayDeclarations(prepared.toolGatewayCatalog, { moduleSpecifier }),
869
+ });
870
+ }