@opengeni/api-router 3.0.1 → 3.1.2

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 (546) hide show
  1. package/dist/access-grant-rls.d.ts +8 -1
  2. package/dist/admission-trace.d.ts +8 -0
  3. package/dist/app.d.ts +50 -3
  4. package/dist/app.js +10 -1
  5. package/dist/artifact-catalog-cursor.d.ts +1 -0
  6. package/dist/attached-browser-viewer.d.ts +11 -0
  7. package/dist/auth/managed-auth-attempt-context.d.ts +2 -0
  8. package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
  9. package/dist/auth/managed-auth-session-adapter.d.ts +9 -1
  10. package/dist/auth/managed-auth.d.ts +72 -5
  11. package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
  12. package/dist/auth/new-signups-gate.d.ts +23 -0
  13. package/dist/auth/signup-funnel-metrics.d.ts +44 -0
  14. package/dist/azure-live.d.ts +28 -0
  15. package/dist/browser-action-fences.d.ts +13 -0
  16. package/dist/browser-controller-authority.d.ts +1 -61
  17. package/dist/browser-ephemeral.d.ts +5 -0
  18. package/dist/browser-lost-runtime-end.d.ts +24 -0
  19. package/dist/browser-state-authority.d.ts +1 -27
  20. package/dist/chunk-OGQDXVXY.js +18 -0
  21. package/dist/chunk-OGQDXVXY.js.map +1 -0
  22. package/dist/{chunk-AJ62T5QN.js → chunk-OT56OY4V.js} +80503 -39869
  23. package/dist/chunk-OT56OY4V.js.map +1 -0
  24. package/dist/claude-default-models.d.ts +19 -0
  25. package/dist/claude-subscription-core.d.ts +50 -0
  26. package/dist/claude-subscription-oauth.d.ts +36 -0
  27. package/dist/claude-subscription-profile.d.ts +9 -0
  28. package/dist/claude-subscription-usage.d.ts +6 -0
  29. package/dist/claude-workspace-connection.d.ts +15 -0
  30. package/dist/codemode.d.ts +40 -5
  31. package/dist/codex-realtime.d.ts +29 -3
  32. package/dist/codex-session-pointers.d.ts +19 -0
  33. package/dist/connection-ownership.d.ts +6 -3
  34. package/dist/controller-data-plane.d.ts +16 -0
  35. package/dist/credit-promotion-snapshot.d.ts +6 -0
  36. package/dist/editable-artifact-source-session.d.ts +3 -0
  37. package/dist/editable-artifact-websocket.d.ts +5 -3
  38. package/dist/fatal-process-boundary.d.ts +9 -1
  39. package/dist/github-access.d.ts +7 -1
  40. package/dist/github-browser-flow.d.ts +1 -1
  41. package/dist/http/acting-person.d.ts +36 -0
  42. package/dist/http/api-contract-compat.d.ts +19 -0
  43. package/dist/http/api-error.d.ts +50 -1
  44. package/dist/http/auth.d.ts +8 -1
  45. package/dist/http/cors.d.ts +12 -0
  46. package/dist/http/deprecation.d.ts +42 -0
  47. package/dist/http/keyed-admission.d.ts +12 -0
  48. package/dist/http/local-browser-boundary.d.ts +72 -0
  49. package/dist/http/metrics-listener.d.ts +22 -0
  50. package/dist/http/path-identifier.d.ts +17 -0
  51. package/dist/http/registered-route-label.d.ts +16 -0
  52. package/dist/http/rejection-telemetry.d.ts +34 -0
  53. package/dist/http/request-body.d.ts +27 -0
  54. package/dist/http/request-source.d.ts +41 -3
  55. package/dist/http/sse.d.ts +12 -1
  56. package/dist/http/unmatched-route.d.ts +18 -0
  57. package/dist/http/user-content.d.ts +36 -0
  58. package/dist/http/variable-sets-removed.d.ts +41 -0
  59. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  60. package/dist/index.js +1279 -108
  61. package/dist/index.js.map +1 -1
  62. package/dist/integration-connect-metrics.d.ts +35 -0
  63. package/dist/integration-endpoint-test.d.ts +20 -0
  64. package/dist/integrations/api-integrations.d.ts +2 -2
  65. package/dist/integrations/atlassian.d.ts +8 -3
  66. package/dist/integrations/fiken.d.ts +3 -1
  67. package/dist/integrations/github-lens-connect.d.ts +37 -66
  68. package/dist/integrations/google-drive.d.ts +2 -0
  69. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  70. package/dist/integrations/marketplace-plugin.d.ts +43 -43
  71. package/dist/integrations/oauth-client.d.ts +171 -12
  72. package/dist/integrations/oauth-profiles.d.ts +42 -13
  73. package/dist/integrations/onepassword-vault-provider.d.ts +9 -0
  74. package/dist/integrations/slack-bot.d.ts +285 -7
  75. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  76. package/dist/integrations/slack-interactions.d.ts +89 -10
  77. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  78. package/dist/integrations/slack-routing.d.ts +1 -1
  79. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  80. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  81. package/dist/integrations/slack-tool-review.d.ts +5 -0
  82. package/dist/interaction-agent-access.d.ts +13 -8
  83. package/dist/interaction-agent-version.d.ts +21 -0
  84. package/dist/interaction-holder-heartbeat.d.ts +5 -0
  85. package/dist/interaction-metrics.d.ts +27 -2
  86. package/dist/interaction-route-stages.d.ts +25 -0
  87. package/dist/interaction-secrets-vault.d.ts +20 -0
  88. package/dist/interaction-secrets.d.ts +103 -0
  89. package/dist/mcp/action-catalog-types.d.ts +14 -0
  90. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  91. package/dist/mcp/action-descriptions.d.ts +4 -0
  92. package/dist/mcp/action-schema-imports.gen.d.ts +1 -0
  93. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  94. package/dist/mcp/child-read-evidence.d.ts +9 -0
  95. package/dist/mcp/company-profile-agent-admin.d.ts +12 -0
  96. package/dist/mcp/contract-input.d.ts +13 -0
  97. package/dist/mcp/documents.d.ts +2 -0
  98. package/dist/mcp/first-party-tool-permissions.d.ts +3 -606
  99. package/dist/mcp/notification-tools.d.ts +26 -0
  100. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  101. package/dist/mcp/scheduled-task-input.d.ts +1309 -0
  102. package/dist/mcp/scheduled-task-view.d.ts +217 -3
  103. package/dist/mcp/secret-tools.d.ts +14 -0
  104. package/dist/mcp/server.d.ts +108 -5
  105. package/dist/mcp/session-admin-access-tools.d.ts +33 -0
  106. package/dist/mcp/session-event-errors.d.ts +28 -0
  107. package/dist/mcp/session-event-view.d.ts +62 -0
  108. package/dist/mcp/session-view.d.ts +15 -0
  109. package/dist/mcp/session-wait.d.ts +18 -0
  110. package/dist/mcp/site-agent-clamp-tools.d.ts +38 -0
  111. package/dist/mcp/site-data-tools.d.ts +22 -0
  112. package/dist/mcp/site-publication-tools.d.ts +19 -0
  113. package/dist/mcp/site-sql-tools.d.ts +27 -0
  114. package/dist/mcp-oauth.d.ts +37 -4
  115. package/dist/model-calls/chat-completions.d.ts +87 -0
  116. package/dist/model-calls/pending-usage-settlements.d.ts +57 -0
  117. package/dist/model-calls/service.d.ts +36 -0
  118. package/dist/model-catalog.d.ts +9 -3
  119. package/dist/native-push-dispatch.d.ts +69 -0
  120. package/dist/observability.d.ts +3 -1
  121. package/dist/organization-mcp.d.ts +82 -0
  122. package/dist/prepared-mcp-actor.d.ts +77 -0
  123. package/dist/prepared-mcp-permissions.d.ts +8 -0
  124. package/dist/routes/analytics-consent.d.ts +26 -0
  125. package/dist/routes/api-integrations.d.ts +11 -0
  126. package/dist/routes/api-keys.d.ts +7 -7
  127. package/dist/routes/automations.d.ts +2 -0
  128. package/dist/routes/billing.d.ts +59 -0
  129. package/dist/routes/browser-sessions.d.ts +65 -2
  130. package/dist/routes/chat-completions.d.ts +11 -0
  131. package/dist/routes/claude-core-usage.d.ts +20 -0
  132. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  133. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  134. package/dist/routes/client-errors.d.ts +182 -0
  135. package/dist/routes/codex-core.d.ts +401 -0
  136. package/dist/routes/codex.d.ts +64 -2
  137. package/dist/routes/computer-sessions.d.ts +6 -1
  138. package/dist/routes/desktop-distribution.d.ts +32 -0
  139. package/dist/routes/github-browser-fonts.gen.d.ts +2 -0
  140. package/dist/routes/github-browser-pages.d.ts +27 -0
  141. package/dist/routes/inbox.d.ts +13 -0
  142. package/dist/routes/insights-response-cache.d.ts +10 -0
  143. package/dist/routes/insights-usage.d.ts +12 -0
  144. package/dist/routes/insights.d.ts +28 -0
  145. package/dist/routes/interaction-placement-message.d.ts +7 -0
  146. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  147. package/dist/routes/model-account-choice.d.ts +13 -0
  148. package/dist/routes/model-account-settings.d.ts +19 -0
  149. package/dist/routes/native-app-auth.d.ts +12 -0
  150. package/dist/routes/organization-integration-policy.d.ts +8 -0
  151. package/dist/routes/organization-integrations.d.ts +6 -0
  152. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  153. package/dist/routes/organization-model-defaults.d.ts +9 -0
  154. package/dist/routes/organization-service-accounts.d.ts +3 -0
  155. package/dist/routes/personal-api-keys.d.ts +3 -0
  156. package/dist/routes/session-admin-access.d.ts +3 -0
  157. package/dist/routes/session-artifact-associations.d.ts +4 -0
  158. package/dist/routes/session-history-imports.d.ts +20 -0
  159. package/dist/routes/sessions.d.ts +38 -2
  160. package/dist/routes/site-data.d.ts +18 -0
  161. package/dist/routes/site-publishing.d.ts +5 -0
  162. package/dist/routes/site-runtime.d.ts +31 -0
  163. package/dist/routes/site-sql.d.ts +16 -0
  164. package/dist/routes/slack-bot-access.d.ts +3 -0
  165. package/dist/routes/subscription-account-pools.d.ts +21 -0
  166. package/dist/routes/subscription-core-http.d.ts +5 -0
  167. package/dist/routes/subscription-pool-access.d.ts +37 -0
  168. package/dist/routes/subscription-pool-core.d.ts +153 -0
  169. package/dist/routes/supergrok.d.ts +6 -6
  170. package/dist/routes/usage-allowances.d.ts +12 -0
  171. package/dist/routes/variable-sets-removed.d.ts +2 -0
  172. package/dist/routes/vault-providers.d.ts +34 -0
  173. package/dist/routes/vaults.d.ts +3 -0
  174. package/dist/routes/workspace-integrations.d.ts +35 -0
  175. package/dist/routes/workspace-model-providers.d.ts +4 -0
  176. package/dist/routes/workspaces.d.ts +11 -2
  177. package/dist/sandbox/agent-update.d.ts +64 -0
  178. package/dist/sandbox/auth-callout.d.ts +8 -0
  179. package/dist/sandbox/channel-a.d.ts +15 -1
  180. package/dist/sandbox/enrollment.d.ts +26 -4
  181. package/dist/sandbox/machine-audience.d.ts +29 -0
  182. package/dist/sandbox/machines.d.ts +15 -2
  183. package/dist/sandbox/metrics-ingestion.d.ts +6 -2
  184. package/dist/sandbox/screen-control.d.ts +85 -0
  185. package/dist/sandbox/viewer.d.ts +5 -1
  186. package/dist/sandbox-file-artifacts.d.ts +17 -1
  187. package/dist/scheduled-task-blockers.d.ts +39 -0
  188. package/dist/scheduled-task-deletion.d.ts +3 -4
  189. package/dist/scheduled-task-lifecycle.d.ts +14 -0
  190. package/dist/secret-answers.d.ts +40 -0
  191. package/dist/session-create-observability.d.ts +5 -0
  192. package/dist/session-event-view-36GO3IL7.js +528 -0
  193. package/dist/session-event-view-36GO3IL7.js.map +1 -0
  194. package/dist/signed-state-secret.d.ts +18 -0
  195. package/dist/site-agent-guard.d.ts +53 -0
  196. package/dist/site-data/operations.d.ts +103 -0
  197. package/dist/site-data/service.d.ts +203 -0
  198. package/dist/site-frame-guard.d.ts +32 -0
  199. package/dist/site-publishing/agent-data-tools.d.ts +84 -0
  200. package/dist/site-publishing/agent-model.d.ts +58 -0
  201. package/dist/site-publishing/agent-session.d.ts +16 -0
  202. package/dist/site-publishing/content-scanner.d.ts +31 -0
  203. package/dist/site-publishing/edge-sync.d.ts +48 -0
  204. package/dist/site-publishing/gateway-signature.d.ts +56 -0
  205. package/dist/site-publishing/limits.d.ts +63 -0
  206. package/dist/site-publishing/page-tool-authority.d.ts +30 -0
  207. package/dist/site-publishing/platform-usage.d.ts +113 -0
  208. package/dist/site-publishing/platform.d.ts +21 -0
  209. package/dist/site-publishing/readiness.d.ts +62 -0
  210. package/dist/site-publishing/runtime.d.ts +179 -0
  211. package/dist/site-publishing/service.d.ts +264 -0
  212. package/dist/site-publishing/session-events.d.ts +22 -0
  213. package/dist/site-publishing/session-views.d.ts +36 -0
  214. package/dist/site-publishing/viewer.d.ts +40 -0
  215. package/dist/site-session-origin.d.ts +31 -6
  216. package/dist/site-sql/format.d.ts +47 -0
  217. package/dist/site-sql/gateway.d.ts +50 -0
  218. package/dist/site-sql/service.d.ts +213 -0
  219. package/dist/temporal-schedule-sync.d.ts +26 -0
  220. package/dist/transcription/billing-refusal.d.ts +8 -0
  221. package/dist/transcription/normalize.d.ts +23 -0
  222. package/dist/transcription/providers/azure-mai.d.ts +12 -0
  223. package/dist/transcription/providers/azure-openai.d.ts +4 -0
  224. package/dist/transcription/providers/openai.d.ts +16 -0
  225. package/dist/transcription/providers/subscription-core.d.ts +39 -0
  226. package/dist/transcription/service.d.ts +10 -1
  227. package/dist/transcription/wav-duration.d.ts +8 -0
  228. package/dist/workspace-tool-gateway-attestations.d.ts +37 -0
  229. package/dist/workspace-tool-gateway-observability.d.ts +16 -0
  230. package/dist/workspace-tool-gateway-secrets.d.ts +27 -0
  231. package/dist/workspace-tool-gateway.d.ts +102 -6
  232. package/dist/workspace-tool-target-fence.d.ts +116 -0
  233. package/dist/workspace-tool-target.d.ts +225 -0
  234. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  235. package/dist/xai-subscription-core.d.ts +60 -0
  236. package/package.json +25 -20
  237. package/src/access-grant-rls.ts +64 -14
  238. package/src/admission-trace.ts +14 -0
  239. package/src/app.ts +1684 -252
  240. package/src/artifact-catalog-cursor.ts +1 -0
  241. package/src/attached-browser-viewer.ts +26 -0
  242. package/src/auth/managed-auth-attempt-context.ts +9 -0
  243. package/src/auth/managed-auth-rate-limits.ts +252 -0
  244. package/src/auth/managed-auth-session-adapter.ts +64 -29
  245. package/src/auth/managed-auth.ts +303 -35
  246. package/src/auth/managed-email.ts +18 -1
  247. package/src/auth/managed-sign-in-notifications.ts +81 -0
  248. package/src/auth/new-signups-gate.ts +49 -0
  249. package/src/auth/organization-user-setup.ts +3 -3
  250. package/src/auth/signup-funnel-metrics.ts +164 -0
  251. package/src/azure-live.ts +103 -0
  252. package/src/browser-action-fences.ts +45 -0
  253. package/src/browser-controller-authority.ts +1 -186
  254. package/src/browser-ephemeral.ts +39 -0
  255. package/src/browser-lost-runtime-end.ts +71 -0
  256. package/src/browser-state-authority.ts +1 -236
  257. package/src/claude-default-models.ts +37 -0
  258. package/src/claude-subscription-core.ts +154 -0
  259. package/src/claude-subscription-oauth.ts +352 -0
  260. package/src/claude-subscription-profile.ts +73 -0
  261. package/src/claude-subscription-usage.ts +51 -0
  262. package/src/claude-workspace-connection.ts +82 -0
  263. package/src/codemode.ts +236 -39
  264. package/src/codex-realtime.ts +181 -53
  265. package/src/codex-session-pointers.ts +80 -0
  266. package/src/company-brain-okf.ts +1 -1
  267. package/src/connected-machine-computer-access.ts +2 -1
  268. package/src/connection-ownership.ts +19 -13
  269. package/src/controller-data-plane.ts +34 -0
  270. package/src/credit-promotion-snapshot.ts +36 -0
  271. package/src/editable-artifact-production.ts +10 -0
  272. package/src/editable-artifact-source-session.ts +123 -0
  273. package/src/editable-artifact-websocket.ts +16 -3
  274. package/src/fatal-process-boundary.ts +67 -3
  275. package/src/gateway-realtime.ts +12 -8
  276. package/src/github-access.ts +31 -5
  277. package/src/github-browser-flow.ts +1 -1
  278. package/src/http/acting-person.ts +105 -0
  279. package/src/http/api-contract-compat.ts +49 -0
  280. package/src/http/api-error.ts +210 -2
  281. package/src/http/auth.ts +68 -4
  282. package/src/http/cors.ts +37 -5
  283. package/src/http/deprecation.ts +118 -0
  284. package/src/http/keyed-admission.ts +29 -0
  285. package/src/http/local-browser-boundary.ts +343 -0
  286. package/src/http/metrics-listener.ts +57 -0
  287. package/src/http/path-identifier.ts +54 -0
  288. package/src/http/registered-route-label.ts +34 -0
  289. package/src/http/rejection-telemetry.ts +120 -0
  290. package/src/http/request-body.ts +127 -0
  291. package/src/http/request-source.ts +154 -13
  292. package/src/http/sse.ts +88 -28
  293. package/src/http/unmatched-route.ts +60 -0
  294. package/src/http/user-content.ts +158 -0
  295. package/src/http/variable-sets-removed.ts +122 -0
  296. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  297. package/src/index.ts +107 -57
  298. package/src/integration-connect-metrics.ts +228 -0
  299. package/src/integration-endpoint-test.ts +291 -0
  300. package/src/integrations/api-integrations.ts +81 -7
  301. package/src/integrations/atlassian.ts +202 -83
  302. package/src/integrations/fiken.ts +66 -38
  303. package/src/integrations/github-app-connect.ts +64 -18
  304. package/src/integrations/github-lens-connect.ts +8 -29
  305. package/src/integrations/google-drive.ts +448 -152
  306. package/src/integrations/integration-commit-authority.ts +69 -0
  307. package/src/integrations/marketplace-plugin.ts +1 -1
  308. package/src/integrations/oauth-client.ts +733 -193
  309. package/src/integrations/oauth-profiles.ts +116 -45
  310. package/src/integrations/onepassword-vault-provider.ts +16 -0
  311. package/src/integrations/personal-github-repositories.ts +1 -1
  312. package/src/integrations/personal-github.ts +61 -35
  313. package/src/integrations/provider-oauth.ts +67 -85
  314. package/src/integrations/slack-app-home.ts +5 -5
  315. package/src/integrations/slack-bot.ts +893 -201
  316. package/src/integrations/slack-file-upload-flow.ts +147 -0
  317. package/src/integrations/slack-install.ts +7 -7
  318. package/src/integrations/slack-interactions.ts +1589 -299
  319. package/src/integrations/slack-mrkdwn.ts +193 -0
  320. package/src/integrations/slack-routing.ts +2 -2
  321. package/src/integrations/slack-session-defaults.ts +224 -0
  322. package/src/integrations/slack-task-file-upload.ts +323 -0
  323. package/src/integrations/slack-tool-review.ts +65 -0
  324. package/src/integrations/social-oauth.ts +90 -58
  325. package/src/interaction-agent-access.ts +40 -20
  326. package/src/interaction-agent-version.ts +85 -0
  327. package/src/interaction-holder-heartbeat.ts +24 -8
  328. package/src/interaction-metrics.ts +164 -2
  329. package/src/interaction-route-stages.ts +94 -0
  330. package/src/interaction-secrets-vault.ts +213 -0
  331. package/src/interaction-secrets.ts +426 -0
  332. package/src/mcp/action-catalog-types.ts +14 -0
  333. package/src/mcp/action-catalog.gen.ts +8014 -0
  334. package/src/mcp/action-descriptions.ts +299 -0
  335. package/src/mcp/action-schema-imports.gen.ts +304 -0
  336. package/src/mcp/capability-account-readiness.ts +17 -0
  337. package/src/mcp/child-read-evidence.ts +19 -0
  338. package/src/mcp/company-profile-agent-admin.ts +13 -0
  339. package/src/mcp/contract-input.ts +165 -0
  340. package/src/mcp/documents.ts +9 -2
  341. package/src/mcp/editable-artifacts.ts +29 -11
  342. package/src/mcp/files.ts +5 -1
  343. package/src/mcp/first-party-tool-permissions.ts +7 -285
  344. package/src/mcp/knowledge-entries.ts +108 -34
  345. package/src/mcp/notification-tools.ts +480 -0
  346. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  347. package/src/mcp/scheduled-task-input.ts +89 -0
  348. package/src/mcp/scheduled-task-view.ts +63 -3
  349. package/src/mcp/secret-tools.ts +185 -0
  350. package/src/mcp/server.ts +2213 -1116
  351. package/src/mcp/session-admin-access-tools.ts +163 -0
  352. package/src/mcp/session-event-errors.ts +123 -0
  353. package/src/mcp/session-event-view.ts +395 -13
  354. package/src/mcp/session-view.ts +3 -0
  355. package/src/mcp/session-wait.ts +67 -7
  356. package/src/mcp/site-agent-clamp-tools.ts +140 -0
  357. package/src/mcp/site-data-tools.ts +339 -0
  358. package/src/mcp/site-publication-tools.ts +201 -0
  359. package/src/mcp/site-sql-tools.ts +427 -0
  360. package/src/mcp-oauth.ts +635 -45
  361. package/src/model-calls/chat-completions.ts +352 -0
  362. package/src/model-calls/pending-usage-settlements.ts +125 -0
  363. package/src/model-calls/service.ts +694 -0
  364. package/src/model-catalog.ts +68 -17
  365. package/src/native-push-dispatch.ts +457 -0
  366. package/src/observability.ts +15 -1
  367. package/src/organization-mcp.ts +485 -0
  368. package/src/prepared-mcp-actor.ts +154 -0
  369. package/src/prepared-mcp-permissions.ts +38 -0
  370. package/src/routes/analytics-consent.ts +123 -0
  371. package/src/routes/api-integrations.ts +268 -9
  372. package/src/routes/api-keys.ts +164 -25
  373. package/src/routes/artifact-catalog.ts +111 -0
  374. package/src/routes/automations.ts +163 -90
  375. package/src/routes/billing.ts +535 -59
  376. package/src/routes/browser-sessions.ts +966 -342
  377. package/src/routes/canonical-human-identities.ts +61 -0
  378. package/src/routes/capabilities.ts +112 -5
  379. package/src/routes/channels.ts +4 -3
  380. package/src/routes/chat-completions.ts +235 -0
  381. package/src/routes/claude-core-usage.ts +147 -0
  382. package/src/routes/claude-subscription-accounts.ts +233 -0
  383. package/src/routes/claude-subscription-oauth.ts +175 -0
  384. package/src/routes/client-errors.ts +394 -0
  385. package/src/routes/codex-core.ts +687 -0
  386. package/src/routes/codex.ts +1277 -1119
  387. package/src/routes/company-brain.ts +4 -0
  388. package/src/routes/computer-sessions.ts +319 -85
  389. package/src/routes/connect.ts +277 -175
  390. package/src/routes/connections.ts +690 -363
  391. package/src/routes/desktop-distribution.ts +217 -0
  392. package/src/routes/documents.ts +13 -7
  393. package/src/routes/editable-artifacts.ts +74 -4
  394. package/src/routes/enrollments.ts +110 -16
  395. package/src/routes/external-identity-links.ts +13 -12
  396. package/src/routes/files.ts +41 -16
  397. package/src/routes/github-browser-fonts.gen.ts +12 -0
  398. package/src/routes/github-browser-pages.ts +258 -0
  399. package/src/routes/github.ts +200 -75
  400. package/src/routes/inbox.ts +353 -0
  401. package/src/routes/insights-response-cache.ts +83 -0
  402. package/src/routes/insights-usage.ts +345 -0
  403. package/src/routes/insights.ts +103 -8
  404. package/src/routes/install.ts +51 -10
  405. package/src/routes/integration-facets.ts +70 -28
  406. package/src/routes/interaction-placement-message.ts +12 -0
  407. package/src/routes/knowledge.ts +136 -27
  408. package/src/routes/machines.ts +189 -135
  409. package/src/routes/managed-auth-session-sets.ts +43 -10
  410. package/src/routes/managed-onboarding.ts +39 -19
  411. package/src/routes/managed-sign-in-methods.ts +433 -0
  412. package/src/routes/memory-slack-publications.ts +3 -2
  413. package/src/routes/model-account-choice.ts +194 -0
  414. package/src/routes/model-account-settings.ts +235 -0
  415. package/src/routes/model-connection-access.ts +210 -48
  416. package/src/routes/native-app-auth.ts +296 -0
  417. package/src/routes/organization-integration-policy.ts +116 -0
  418. package/src/routes/organization-integrations.ts +326 -0
  419. package/src/routes/organization-mcp-connections.ts +183 -0
  420. package/src/routes/organization-memberships.ts +205 -19
  421. package/src/routes/organization-model-defaults.ts +110 -0
  422. package/src/routes/organization-model-providers.ts +74 -3
  423. package/src/routes/organization-service-accounts.ts +128 -0
  424. package/src/routes/organization-sessions.ts +21 -5
  425. package/src/routes/personal-api-keys.ts +184 -0
  426. package/src/routes/personal-github-git-broker.ts +2 -2
  427. package/src/routes/personal-github.ts +9 -4
  428. package/src/routes/plugins.ts +46 -7
  429. package/src/routes/pr-review-github.ts +105 -118
  430. package/src/routes/pr-review.ts +88 -82
  431. package/src/routes/preference-registry.ts +18 -0
  432. package/src/routes/rigs.ts +48 -13
  433. package/src/routes/scheduled-tasks.ts +347 -27
  434. package/src/routes/session-admin-access.ts +271 -0
  435. package/src/routes/session-artifact-associations.ts +164 -0
  436. package/src/routes/session-history-imports.ts +305 -0
  437. package/src/routes/sessions.ts +1682 -363
  438. package/src/routes/site-data.ts +340 -0
  439. package/src/routes/site-publishing.ts +584 -0
  440. package/src/routes/site-runtime.ts +1944 -0
  441. package/src/routes/site-sql.ts +263 -0
  442. package/src/routes/skill-content.ts +72 -7
  443. package/src/routes/skills.ts +5 -4
  444. package/src/routes/slack-bot-access.ts +108 -0
  445. package/src/routes/social.ts +27 -15
  446. package/src/routes/subscription-account-pools.ts +209 -0
  447. package/src/routes/subscription-core-http.ts +18 -0
  448. package/src/routes/subscription-pool-access.ts +180 -0
  449. package/src/routes/subscription-pool-core.ts +552 -0
  450. package/src/routes/supergrok.ts +174 -655
  451. package/src/routes/transcription-recordings.ts +71 -4
  452. package/src/routes/transcriptions.ts +26 -2
  453. package/src/routes/usage-allowances.ts +381 -0
  454. package/src/routes/variable-sets-removed.ts +19 -0
  455. package/src/routes/vault-providers.ts +782 -0
  456. package/src/routes/vaults.ts +421 -0
  457. package/src/routes/video-generation.ts +30 -12
  458. package/src/routes/workspace-artifacts.ts +165 -53
  459. package/src/routes/workspace-integrations.ts +576 -0
  460. package/src/routes/workspace-model-providers.ts +148 -0
  461. package/src/routes/workspace-state.ts +4 -0
  462. package/src/routes/workspaces.ts +625 -117
  463. package/src/sandbox/agent-update.ts +315 -0
  464. package/src/sandbox/auth-callout.ts +104 -21
  465. package/src/sandbox/channel-a.ts +165 -55
  466. package/src/sandbox/enrollment.ts +131 -22
  467. package/src/sandbox/machine-audience.ts +192 -0
  468. package/src/sandbox/machines.ts +75 -31
  469. package/src/sandbox/metrics-ingestion.ts +133 -2
  470. package/src/sandbox/rematerialize.ts +51 -21
  471. package/src/sandbox/screen-control.ts +289 -0
  472. package/src/sandbox/viewer.ts +33 -58
  473. package/src/sandbox-file-artifacts.ts +53 -27
  474. package/src/scheduled-task-blockers.ts +293 -0
  475. package/src/scheduled-task-deletion.ts +13 -3
  476. package/src/scheduled-task-lifecycle.ts +84 -0
  477. package/src/secret-answers.ts +119 -0
  478. package/src/session-create-observability.ts +46 -0
  479. package/src/session-realtime-context.ts +26 -4
  480. package/src/signed-state-secret.ts +58 -0
  481. package/src/site-agent-guard.ts +261 -0
  482. package/src/site-data/operations.ts +777 -0
  483. package/src/site-data/service.ts +1043 -0
  484. package/src/site-frame-guard.ts +107 -0
  485. package/src/site-publishing/agent-data-tools.ts +250 -0
  486. package/src/site-publishing/agent-model.ts +127 -0
  487. package/src/site-publishing/agent-session.ts +148 -0
  488. package/src/site-publishing/content-scanner.ts +93 -0
  489. package/src/site-publishing/edge-sync.ts +148 -0
  490. package/src/site-publishing/gateway-signature.ts +198 -0
  491. package/src/site-publishing/limits.ts +315 -0
  492. package/src/site-publishing/page-tool-authority.ts +111 -0
  493. package/src/site-publishing/platform-usage.ts +479 -0
  494. package/src/site-publishing/platform.ts +98 -0
  495. package/src/site-publishing/readiness.ts +330 -0
  496. package/src/site-publishing/runtime.ts +831 -0
  497. package/src/site-publishing/service.ts +1282 -0
  498. package/src/site-publishing/session-events.ts +262 -0
  499. package/src/site-publishing/session-views.ts +262 -0
  500. package/src/site-publishing/viewer.ts +75 -0
  501. package/src/site-session-origin.ts +91 -6
  502. package/src/site-sql/format.ts +134 -0
  503. package/src/site-sql/gateway.ts +168 -0
  504. package/src/site-sql/service.ts +1636 -0
  505. package/src/site-uploads.ts +27 -5
  506. package/src/temporal-schedule-sync.ts +74 -0
  507. package/src/transcription/billing-refusal.ts +14 -0
  508. package/src/transcription/normalize.ts +77 -0
  509. package/src/transcription/providers/azure-mai.ts +98 -0
  510. package/src/transcription/providers/azure-openai.ts +16 -7
  511. package/src/transcription/providers/codex-subscription.ts +190 -67
  512. package/src/transcription/providers/openai.ts +79 -5
  513. package/src/transcription/providers/subscription-core.ts +125 -0
  514. package/src/transcription/providers/xai-subscription.ts +81 -83
  515. package/src/transcription/segmenter.ts +7 -1
  516. package/src/transcription/service.ts +239 -10
  517. package/src/transcription/wav-duration.ts +39 -0
  518. package/src/workspace-deletion.ts +25 -1
  519. package/src/workspace-tool-gateway-attestations.ts +133 -0
  520. package/src/workspace-tool-gateway-observability.ts +64 -0
  521. package/src/workspace-tool-gateway-secrets.ts +104 -0
  522. package/src/workspace-tool-gateway.ts +921 -103
  523. package/src/workspace-tool-target-fence.ts +380 -0
  524. package/src/workspace-tool-target.ts +554 -0
  525. package/src/workspace-webhook-dispatch.ts +225 -0
  526. package/src/xai-realtime.ts +85 -81
  527. package/src/xai-subscription-core.ts +172 -0
  528. package/dist/browser-auth-broker.d.ts +0 -20
  529. package/dist/chunk-AJ62T5QN.js.map +0 -1
  530. package/dist/connection-authority-owner.d.ts +0 -59
  531. package/dist/routes/connection-authorities.d.ts +0 -3
  532. package/dist/routes/environments.d.ts +0 -5
  533. package/dist/routes/host-mcp-bindings.d.ts +0 -5
  534. package/dist/routes/host-mcp-resolvers.d.ts +0 -3
  535. package/dist/routes/packs.d.ts +0 -3
  536. package/dist/session-event-view-MU3HMNV3.js +0 -264
  537. package/dist/session-event-view-MU3HMNV3.js.map +0 -1
  538. package/dist/xai-subscription-auth.d.ts +0 -23
  539. package/src/browser-auth-broker.ts +0 -155
  540. package/src/connection-authority-owner.ts +0 -61
  541. package/src/routes/connection-authorities.ts +0 -139
  542. package/src/routes/environments.ts +0 -388
  543. package/src/routes/host-mcp-bindings.ts +0 -122
  544. package/src/routes/host-mcp-resolvers.ts +0 -41
  545. package/src/routes/packs.ts +0 -784
  546. package/src/xai-subscription-auth.ts +0 -139
@@ -1,4 +1,4 @@
1
- import { knowledgeContextForGateway } from "@opengeni/core";
1
+ import { isVerifiedDelegatedHumanAuthorization, knowledgeContextForGateway } from "@opengeni/core";
2
2
  import { createHash, randomBytes } from "node:crypto";
3
3
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
4
  import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
@@ -6,55 +6,70 @@ import { withSiteSessionOrigin } from "@opengeni/core";
6
6
  import { resolveSiteSessionOrigin } from "./site-session-origin";
7
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
8
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { recordToolApproval } from "@opengeni/observability";
9
10
  import {
10
11
  CallToolRequestSchema,
11
12
  ListToolsRequestSchema,
12
13
  type CallToolResult,
13
14
  } from "@modelcontextprotocol/sdk/types.js";
14
- import { CODEX_CLIENT_VERSION } from "@opengeni/codex";
15
15
  import { IntegrationInvocationError } from "@opengeni/capabilities";
16
+ import { CODEX_APPS_MCP_SERVER_ID } from "@opengeni/codex";
16
17
  import type { Settings } from "@opengeni/config";
17
18
  import {
18
19
  FIRST_PARTY_MCP_TOOL_NAMES,
20
+ EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS,
19
21
  ToolGatewayCallRequest,
20
22
  ToolGatewayCallResponse,
21
23
  ToolGatewayApprovalRequest,
22
24
  ToolGatewayApprovalResponse,
23
25
  ToolGatewayDeclarationsResponse,
26
+ toolPolicyActionName,
27
+ isSecretReferenceError,
24
28
  type AccessGrant,
25
- type McpGatewayCredentialAuthority,
26
29
  type ToolGatewayCatalog,
27
30
  type ToolGatewayIdentity,
28
31
  type ToolRef,
32
+ type ToolGatewayTarget,
29
33
  } from "@opengeni/contracts";
30
34
  import {
35
+ availableMcpAccountBindings,
31
36
  buildApiIntegrationMcpServers,
37
+ expandApiIntegrationAccountRoutes,
38
+ expandMcpAccountRoutes,
32
39
  hasPermission,
33
40
  externalActorContinuationForAuthorization,
34
41
  isVerifiedOrganizationServiceAuthorization,
35
42
  externalContinuationCommitAuthorizer,
36
43
  requireResolvedAccessGrantAuthorization,
37
- resolveCodexAppsCredentialIdForRun,
44
+ codexAppsRequestAuthForDesignation,
45
+ resolveCodexAppsDesignationForRun,
38
46
  resolveWorkspaceCatalogSettings,
39
47
  settingsWithEnabledCapabilityMcpServers,
48
+ settingsWithEnabledCapabilityMcpServersForTargets,
49
+ visibleMcpAccountConnectionsByIds,
40
50
  type AccessGrantAuthorization,
41
51
  type ApiRouteDeps,
52
+ siteViewerForGrant,
42
53
  } from "@opengeni/core";
43
54
  import {
44
- buildCodexTokenResolver,
45
55
  buildConnectionTokenResolver,
46
- buildHostGatewayConnectionTokenResolver,
47
- lockActiveExternalOrganizationKey,
56
+ buildSlackApiRateLimiter,
57
+ lockActiveExternalOrganizationKeyAuthority,
48
58
  withAccountRls,
49
59
  requireWorkspace,
50
- withCodexAppsRequestAuthorization,
51
60
  consumeToolGatewayApproval,
52
61
  getWorkspaceArtifactContentRef,
53
62
  issueToolGatewayApproval,
54
63
  ToolGatewayApprovalOperationStartedError,
55
64
  ToolGatewayApprovalRateLimitError,
56
65
  WorkspaceArtifactNotFoundError,
66
+ listConnectorToolPermissionPolicies,
67
+ projectConnectorToolPermission,
68
+ resolveConnectorActionPolicy,
69
+ connectorActionPolicyDecision,
57
70
  type ApiIntegrationRuntime,
71
+ type Database,
72
+ type EnabledMcpCapabilityServer,
58
73
  } from "@opengeni/db";
59
74
  import {
60
75
  type LocalMcpServerRegistration,
@@ -62,14 +77,18 @@ import {
62
77
  type ResolveConnectionCredentialInput,
63
78
  type ResolveConnectionCredentialResult,
64
79
  prepareWorkspaceToolGatewayTools,
80
+ sessionMcpApprovalConnectionId,
65
81
  } from "@opengeni/runtime/workspace-tool-gateway";
66
82
  import {
67
83
  ToolGatewayApprovalRequiredError,
84
+ ToolGatewayBlockedError,
68
85
  ToolGatewayCatalogStaleError,
69
86
  ToolGatewayInputValidationError,
70
87
  ToolGatewayToolNotFoundError,
71
88
  digestCanonicalJson,
72
89
  generateToolGatewayDeclarations,
90
+ safeNamespaceSegment,
91
+ projectToolGatewayPath,
73
92
  type PreparedToolGatewayCall,
74
93
  type ToolGatewayDefinition,
75
94
  } from "@opengeni/tool-gateway";
@@ -80,7 +99,26 @@ import { buildDocumentsMcpServer } from "./mcp/documents";
80
99
  import { buildFilesMcpServer } from "./mcp/files";
81
100
  import { buildOpenGeniMcpServer } from "./mcp/server";
82
101
  import { startWorkspaceToolGatewayObservation } from "./workspace-tool-gateway-observability";
102
+ import {
103
+ withSiteSecretContext,
104
+ workspaceGatewaySecretArguments,
105
+ } from "./workspace-tool-gateway-secrets";
106
+ import { vaultProviderRegistryFor } from "./routes/vault-providers";
107
+ import type { TargetAuthorityFence } from "./workspace-tool-target-fence";
108
+ import {
109
+ digestWorkspaceToolGatewayCatalogEntry,
110
+ type WorkspaceToolGatewayCatalogAttestations,
111
+ } from "./workspace-tool-gateway-attestations";
83
112
  import type { Observability } from "@opengeni/observability";
113
+ import {
114
+ isPublishedSiteToolAuthorization,
115
+ publishedSiteToolAuthority,
116
+ type PublishedSiteToolAuthorization,
117
+ } from "./site-publishing/page-tool-authority";
118
+
119
+ export type WorkspaceToolGatewayAuthorization =
120
+ | AccessGrantAuthorization
121
+ | PublishedSiteToolAuthorization;
84
122
 
85
123
  export type PreparedWorkspaceToolGateway = Pick<
86
124
  PreparedWorkspaceToolGatewayTools,
@@ -121,8 +159,9 @@ export function requireWorkspaceToolGatewayGrant(grant: AccessGrant): void {
121
159
  }
122
160
 
123
161
  export function requireWorkspaceToolGatewayAuthorization(
124
- authorization: AccessGrantAuthorization,
162
+ authorization: WorkspaceToolGatewayAuthorization,
125
163
  ): AccessGrant {
164
+ if (isPublishedSiteToolAuthorization(authorization)) return authorization.grant;
126
165
  const grant = requireResolvedAccessGrantAuthorization(
127
166
  authorization,
128
167
  authorization.grant.workspaceId,
@@ -138,6 +177,7 @@ export function requireWorkspaceToolGatewayAuthorization(
138
177
  if (
139
178
  !authorization.canonicalManagedHumanSession &&
140
179
  !authorization.canonicalLocalHumanSession &&
180
+ !isVerifiedDelegatedHumanAuthorization(authorization) &&
141
181
  !externalActorContinuationForAuthorization(authorization)
142
182
  ) {
143
183
  throw new HTTPException(403, { message: "current-human tool access required" });
@@ -147,9 +187,66 @@ export function requireWorkspaceToolGatewayAuthorization(
147
187
 
148
188
  export async function prepareWorkspaceToolGateway(
149
189
  routeDeps: ApiRouteDeps,
150
- authorization: AccessGrantAuthorization,
190
+ authorization: WorkspaceToolGatewayAuthorization,
191
+ options: {
192
+ /**
193
+ * Prepare only these exact (account-qualified) identities. Servers outside
194
+ * them are never constructed, credentialed, connected, or listed; the
195
+ * resulting catalog and digest describe only this subset.
196
+ */
197
+ allowedIdentities?: readonly ToolGatewayIdentity[];
198
+ } & WorkspaceToolGatewayPreparationOptions = {},
151
199
  ): Promise<PreparedWorkspaceToolGateway> {
152
200
  const grant = requireWorkspaceToolGatewayAuthorization(authorization);
201
+ if (isPublishedSiteToolAuthorization(authorization)) {
202
+ const site = publishedSiteToolAuthority(authorization);
203
+ const allowed = site.requestedTools.filter(
204
+ (identity) =>
205
+ !options.allowedIdentities ||
206
+ options.allowedIdentities.some(
207
+ (item) => item.serverId === identity.serverId && item.toolName === identity.toolName,
208
+ ),
209
+ );
210
+ // Targeted preparation replaces its allowlist with the target. It must be
211
+ // an exact declared identity first; paths are resolved by the Site adapter.
212
+ const target = options.target;
213
+ if (
214
+ target &&
215
+ (!("identity" in target) ||
216
+ !allowed.some(
217
+ (item) =>
218
+ item.serverId === target.identity.serverId &&
219
+ item.toolName === target.identity.toolName,
220
+ ))
221
+ )
222
+ throw new HTTPException(403, { message: "site_tool_not_authorized" });
223
+ await site.reauthorize();
224
+ const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
225
+ routeDeps,
226
+ grant,
227
+ allowed,
228
+ site.reauthorize,
229
+ options,
230
+ );
231
+ try {
232
+ await site.reauthorize();
233
+ } catch (error) {
234
+ await prepared.close().catch(() => undefined);
235
+ throw error;
236
+ }
237
+ return { ...prepared, reauthorize: site.reauthorize };
238
+ }
239
+ if (options.fence) {
240
+ // The fence owns live caller authority for a targeted request: admission
241
+ // ran before preparation, and each physical request evaluates it once.
242
+ return await prepareWorkspaceToolGatewayForGrantInternal(
243
+ routeDeps,
244
+ grant,
245
+ options.allowedIdentities,
246
+ undefined,
247
+ options,
248
+ );
249
+ }
153
250
  const external = externalActorContinuationForAuthorization(authorization);
154
251
  const reauthorizeExternal = externalContinuationCommitAuthorizer(authorization);
155
252
  const service = isVerifiedOrganizationServiceAuthorization(authorization);
@@ -165,15 +262,18 @@ export async function prepareWorkspaceToolGateway(
165
262
  await withAccountRls(routeDeps.db, scope.accountId, async (tx) => {
166
263
  if (reauthorizeExternal) await reauthorizeExternal(tx);
167
264
  else {
168
- const live = await lockActiveExternalOrganizationKey(
265
+ const live = await lockActiveExternalOrganizationKeyAuthority(
169
266
  tx,
170
267
  scope.accountId,
171
268
  scope.subjectId.slice("api_key:".length),
269
+ scope.workspaceId,
172
270
  );
173
271
  const workspace = await requireWorkspace(tx, scope.workspaceId);
174
272
  if (
175
273
  !live ||
176
- permissions.some((permission) => !hasPermission(live, permission)) ||
274
+ permissions.some(
275
+ (permission) => !hasPermission(live.permissions, permission, live.permissionMode),
276
+ ) ||
177
277
  workspace.accountId !== scope.accountId ||
178
278
  workspace.kind !== "shared"
179
279
  )
@@ -182,29 +282,233 @@ export async function prepareWorkspaceToolGateway(
182
282
  });
183
283
  }
184
284
  : undefined;
185
- await reauthorize?.();
285
+ const reauthorizeCaller = async () => {
286
+ options.signal?.throwIfAborted();
287
+ await options.reauthorize?.();
288
+ await reauthorize?.();
289
+ };
290
+ await reauthorizeCaller();
186
291
  const prepared = await prepareWorkspaceToolGatewayForGrantInternal(
187
292
  routeDeps,
188
293
  grant,
189
- undefined,
190
- reauthorize
191
- ? {
192
- authority: {
193
- kind: external ? "external_user" : "organization_service",
194
- subjectId: scope.subjectId,
195
- permissions,
196
- },
197
- reauthorize,
198
- }
199
- : undefined,
294
+ options.allowedIdentities,
295
+ reauthorizeCaller,
296
+ options,
200
297
  );
201
298
  try {
202
- await reauthorize?.();
299
+ await reauthorizeCaller();
203
300
  } catch (error) {
204
- await prepared.close();
301
+ await prepared.close().catch(() => undefined);
205
302
  throw error;
206
303
  }
207
- return { ...prepared, ...(reauthorize ? { reauthorize } : {}) };
304
+ return {
305
+ ...prepared,
306
+ reauthorize: async () => {
307
+ await reauthorizeCaller();
308
+ await prepared.reauthorize?.();
309
+ },
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Bounded caller scope for catalog attestations. It binds the exact caller,
315
+ * actor class, and permission set; attestations still grant no authority.
316
+ */
317
+ export function workspaceToolGatewayAttestationScope(
318
+ authorization: AccessGrantAuthorization,
319
+ ): string {
320
+ const grant = authorization.grant;
321
+ const external = externalActorContinuationForAuthorization(authorization);
322
+ return digestCanonicalJson({
323
+ version: 1,
324
+ accountId: grant.accountId,
325
+ workspaceId: grant.workspaceId,
326
+ subjectId: grant.subjectId,
327
+ principalKind: grant.principalKind ?? null,
328
+ permissions: [...new Set(grant.permissions)].sort(),
329
+ actor: isVerifiedOrganizationServiceAuthorization(authorization)
330
+ ? "service"
331
+ : external
332
+ ? { external: digestCanonicalJson(external) }
333
+ : "human",
334
+ });
335
+ }
336
+
337
+ export type WorkspaceToolGatewayPreparationScope = "complete" | "target";
338
+ export type WorkspaceToolGatewayAttestationOutcome = "hit" | "miss" | "mismatch" | "bypass";
339
+
340
+ export type WorkspaceToolGatewayTiming = {
341
+ /** Wall time spent preparing gateways (all preparations, including fallback). */
342
+ prepareMs: number;
343
+ /** Wall time from the last successful preparation to the response. */
344
+ callMs: number;
345
+ scope: WorkspaceToolGatewayPreparationScope;
346
+ attestation: WorkspaceToolGatewayAttestationOutcome;
347
+ };
348
+
349
+ /** Prepare one complete current-caller catalog and remember its content-free attestation. */
350
+ export async function prepareAttestedWorkspaceToolGateway(
351
+ routeDeps: ApiRouteDeps,
352
+ authorization: AccessGrantAuthorization,
353
+ attestations: WorkspaceToolGatewayCatalogAttestations | undefined,
354
+ prepare: typeof prepareWorkspaceToolGateway = prepareWorkspaceToolGateway,
355
+ ): Promise<PreparedWorkspaceToolGateway> {
356
+ const prepared = await prepare(routeDeps, authorization);
357
+ attestations?.record(
358
+ workspaceToolGatewayAttestationScope(authorization),
359
+ prepared.toolGatewayCatalog,
360
+ );
361
+ return prepared;
362
+ }
363
+
364
+ /**
365
+ * HTTP/SDK/Site call adapter. When the caller's `catalogDigest` names a
366
+ * complete catalog this process prepared for the same caller scope, only the
367
+ * target identity's connector is prepared live and its entry must match the
368
+ * attested entry exactly. Every other case (approval capability, unknown or
369
+ * expired digest, missing/changed entry) prepares the complete catalog and
370
+ * applies the unchanged stale-catalog contract.
371
+ */
372
+ export async function callWorkspaceToolGatewayForCaller(
373
+ routeDeps: ApiRouteDeps,
374
+ authorization: AccessGrantAuthorization,
375
+ request: ToolGatewayCallRequest,
376
+ options: {
377
+ attestations?: WorkspaceToolGatewayCatalogAttestations;
378
+ observability?: Observability;
379
+ /** Must enforce current-caller gateway authorization before provider work. */
380
+ prepare?: typeof prepareWorkspaceToolGateway;
381
+ authorizeSiteTool?: AuthorizeWorkspaceSiteTool;
382
+ resolveOrigin?: typeof resolveSiteSessionOrigin;
383
+ /** Called once with the preparation split, whether the call succeeds or fails. */
384
+ onPreparation?: (
385
+ preparation: Pick<WorkspaceToolGatewayTiming, "prepareMs" | "scope" | "attestation">,
386
+ ) => void;
387
+ } = {},
388
+ ): Promise<{ response: ToolGatewayCallResponse; timing: WorkspaceToolGatewayTiming }> {
389
+ // `prepare` (prepareWorkspaceToolGateway in production) rejects any caller
390
+ // without live current-caller gateway authority before provider work. The
391
+ // attestation lookup below grants nothing and reveals nothing to a caller
392
+ // that preparation then rejects.
393
+ const grant = authorization.grant;
394
+ const prepare = options.prepare ?? prepareWorkspaceToolGateway;
395
+ const callGateway = (
396
+ prepared: PreparedWorkspaceToolGateway,
397
+ callRequest: ToolGatewayCallRequest,
398
+ ) =>
399
+ callWorkspaceToolGateway(
400
+ prepared,
401
+ grant,
402
+ callRequest,
403
+ routeDeps.db,
404
+ undefined,
405
+ options.observability,
406
+ options.authorizeSiteTool,
407
+ options.resolveOrigin,
408
+ );
409
+ const scope = workspaceToolGatewayAttestationScope(authorization);
410
+ let prepareMs = 0;
411
+ const timedPrepare = async (allowedIdentities?: readonly ToolGatewayIdentity[]) => {
412
+ const startedAt = performance.now();
413
+ try {
414
+ return await prepare(
415
+ routeDeps,
416
+ authorization,
417
+ // A target-only call must behave exactly like the complete call: only
418
+ // provider construction narrows, never the settings first-party tools see.
419
+ allowedIdentities ? { allowedIdentities, firstPartySettings: "caller" } : {},
420
+ );
421
+ } finally {
422
+ prepareMs += performance.now() - startedAt;
423
+ }
424
+ };
425
+ const attestedEntry =
426
+ request.approvalToken === undefined
427
+ ? options.attestations?.entryDigest(scope, request.catalogDigest, request.identity)
428
+ : undefined;
429
+ let attestation: WorkspaceToolGatewayAttestationOutcome =
430
+ request.approvalToken !== undefined || !options.attestations
431
+ ? "bypass"
432
+ : attestedEntry
433
+ ? "hit"
434
+ : "miss";
435
+ let preparedScope: WorkspaceToolGatewayPreparationScope = "complete";
436
+ try {
437
+ if (attestedEntry) {
438
+ preparedScope = "target";
439
+ const target = await timedPrepare([request.identity]);
440
+ try {
441
+ const entry = target.toolGatewayCatalog.entries.find(
442
+ (candidate) =>
443
+ candidate.identity.serverId === request.identity.serverId &&
444
+ candidate.identity.toolName === request.identity.toolName,
445
+ );
446
+ if (entry && digestWorkspaceToolGatewayCatalogEntry(entry) === attestedEntry) {
447
+ const callStartedAt = performance.now();
448
+ // The target-only gateway verifies its own digest; the caller's
449
+ // complete digest was verified through the exact attested entry.
450
+ const response = await callGateway(target, {
451
+ ...request,
452
+ catalogDigest: target.toolGatewayCatalog.digest,
453
+ });
454
+ return {
455
+ response: ToolGatewayCallResponse.parse({
456
+ ...response,
457
+ catalogDigest: request.catalogDigest,
458
+ }),
459
+ timing: {
460
+ prepareMs,
461
+ callMs: performance.now() - callStartedAt,
462
+ scope: "target",
463
+ attestation,
464
+ },
465
+ };
466
+ }
467
+ attestation = "mismatch";
468
+ preparedScope = "complete";
469
+ } finally {
470
+ await target.close();
471
+ }
472
+ }
473
+ const prepared = await timedPrepare();
474
+ try {
475
+ options.attestations?.record(scope, prepared.toolGatewayCatalog);
476
+ const callStartedAt = performance.now();
477
+ const response = await callGateway(prepared, request);
478
+ return {
479
+ response,
480
+ timing: {
481
+ prepareMs,
482
+ callMs: performance.now() - callStartedAt,
483
+ scope: "complete",
484
+ attestation,
485
+ },
486
+ };
487
+ } finally {
488
+ await prepared.close();
489
+ }
490
+ } finally {
491
+ try {
492
+ options.onPreparation?.({ prepareMs, scope: preparedScope, attestation });
493
+ } catch {
494
+ // Telemetry must never change gateway execution truth.
495
+ }
496
+ }
497
+ }
498
+
499
+ /** Content-free Server-Timing for gateway preparation versus execution. */
500
+ export function workspaceToolGatewayServerTiming(timing: {
501
+ prepareMs: number;
502
+ callMs?: number;
503
+ scope: WorkspaceToolGatewayPreparationScope;
504
+ attestation?: WorkspaceToolGatewayAttestationOutcome;
505
+ }): string {
506
+ const prepare = `gw-prepare;dur=${timing.prepareMs.toFixed(1)};desc="${timing.scope}${
507
+ timing.attestation ? `/${timing.attestation}` : ""
508
+ }"`;
509
+ return timing.callMs === undefined
510
+ ? prepare
511
+ : `${prepare}, gw-call;dur=${timing.callMs.toFixed(1)}`;
208
512
  }
209
513
 
210
514
  export async function prepareMcpOAuthWorkspaceToolGateway(
@@ -222,26 +526,263 @@ export async function prepareWorkspaceToolGatewayForGrant(
222
526
  routeDeps: ApiRouteDeps,
223
527
  grant: AccessGrant,
224
528
  allowedIdentities?: readonly { serverId: string; toolName: string }[],
529
+ options: WorkspaceToolGatewayPreparationOptions = {},
225
530
  ): Promise<PreparedWorkspaceToolGateway> {
226
- return await prepareWorkspaceToolGatewayForGrantInternal(routeDeps, grant, allowedIdentities);
531
+ return await prepareWorkspaceToolGatewayForGrantInternal(
532
+ routeDeps,
533
+ grant,
534
+ allowedIdentities,
535
+ undefined,
536
+ options,
537
+ );
538
+ }
539
+
540
+ export type WorkspaceToolGatewayPreparationOptions = {
541
+ target?: ToolGatewayTarget;
542
+ signal?: AbortSignal;
543
+ /** Trusted transport callback, never a request-body field. */
544
+ reauthorize?: () => Promise<void>;
545
+ /**
546
+ * Trusted per-request live fence of a targeted or manifest request (never a
547
+ * request-body field). Required with `target`; replaces `reauthorize`.
548
+ */
549
+ fence?: TargetAuthorityFence;
550
+ /**
551
+ * Settings visible to first-party (opengeni/files/docs) tool handlers.
552
+ * `prepared` (default, MCP OAuth): the identity-narrowed server set.
553
+ * `caller`: the complete caller-authorized set, so a target-only call
554
+ * executes exactly as it would from the complete gateway.
555
+ */
556
+ firstPartySettings?: "prepared" | "caller";
557
+ };
558
+
559
+ /** Keep live caller authority independent of native connection acquisition and refresh. */
560
+ export function withWorkspaceConnectionAuthorization(
561
+ resolve: ReturnType<typeof buildConnectionTokenResolver>,
562
+ reauthorize?: () => Promise<void>,
563
+ ): ReturnType<typeof buildConnectionTokenResolver> {
564
+ if (!reauthorize) return resolve;
565
+ return async (input) => {
566
+ await reauthorize();
567
+ const result = await resolve(input);
568
+ await reauthorize();
569
+ if (result.status !== "ok") return result;
570
+ return {
571
+ ...result,
572
+ authorizeProviderRequest: async () => {
573
+ try {
574
+ await reauthorize();
575
+ const allowed = result.authorizeProviderRequest
576
+ ? await result.authorizeProviderRequest()
577
+ : true;
578
+ await reauthorize();
579
+ return allowed;
580
+ } catch {
581
+ return false;
582
+ }
583
+ },
584
+ };
585
+ };
586
+ }
587
+
588
+ /**
589
+ * Credential boundary of a fenced targeted request. Before an execution-mode
590
+ * resolve (which may refresh a token or record use) only the live caller is
591
+ * checked. The returned hook runs immediately before one physical provider
592
+ * request: the native hook, then (targeted) a non-refreshing native preflight
593
+ * that must still name the same connection version, then one full fence
594
+ * evaluation, the hook's last step. A hook retained past the request scope
595
+ * reads nothing.
596
+ */
597
+ export function withTargetFenceConnectionAuthorization(
598
+ resolve: ReturnType<typeof buildConnectionTokenResolver>,
599
+ fence: Pick<TargetAuthorityFence, "caller" | "allows" | "assertOpen">,
600
+ options: { preflight: boolean },
601
+ ): ReturnType<typeof buildConnectionTokenResolver> {
602
+ return async (input) => {
603
+ await fence.caller();
604
+ const result = await resolve(input);
605
+ if (result.status !== "ok") return result;
606
+ return {
607
+ ...result,
608
+ authorizeProviderRequest: async () => {
609
+ try {
610
+ fence.assertOpen();
611
+ if (result.authorizeProviderRequest && !(await result.authorizeProviderRequest()))
612
+ return false;
613
+ if (options.preflight) {
614
+ const live = await resolve({
615
+ ...input,
616
+ forceRefresh: false,
617
+ credentialResolutionMode: "preflight",
618
+ });
619
+ if (
620
+ live.status !== "ok" ||
621
+ live.connectionId !== result.connectionId ||
622
+ live.connectionVersion !== result.connectionVersion ||
623
+ (live.authorizeProviderRequest && !(await live.authorizeProviderRequest()))
624
+ )
625
+ return false;
626
+ }
627
+ } catch {
628
+ return false;
629
+ }
630
+ return await fence.allows();
631
+ },
632
+ };
633
+ };
634
+ }
635
+
636
+ /**
637
+ * The route part of a targeted fence. It reads only the rows that can produce
638
+ * the selected servers (their capability installations and explicit claimants,
639
+ * integrations with their runtime keys, the selected connections, and Codex
640
+ * only for a Codex target) and feeds them through the same projections as
641
+ * preparation. Any difference from the prepared route denies.
642
+ */
643
+ function workspaceToolGatewayTargetRouteCheck(input: {
644
+ grant: AccessGrant;
645
+ staticSettings: Settings;
646
+ gatewaySettings: Settings;
647
+ integrations: readonly ApiIntegrationRuntime[];
648
+ capabilityServers: readonly EnabledMcpCapabilityServer[];
649
+ accountBindings: Awaited<ReturnType<typeof availableMcpAccountBindings>>;
650
+ bindingSource: Parameters<typeof availableMcpAccountBindings>[0]["source"];
651
+ }): (db: Database, stop: AbortSignal) => Promise<void> {
652
+ const { grant, gatewaySettings, bindingSource } = input;
653
+ const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
654
+ const selectedBindings = input.accountBindings.filter((binding) =>
655
+ gatewayServerIds.has(binding.serverId),
656
+ );
657
+ // Canonical connector ids plus the account route ids themselves: any row
658
+ // that could produce, shadow or collide with the selected route is read.
659
+ const serverIds = [
660
+ ...new Set(
661
+ gatewaySettings.mcpServers.flatMap((server) => [
662
+ server.id,
663
+ ...selectedBindings
664
+ .filter((binding) => binding.serverId === server.id)
665
+ .map((binding) => binding.canonicalServerId),
666
+ ]),
667
+ ),
668
+ ];
669
+ const capabilityIds = [
670
+ ...new Set(
671
+ input.capabilityServers
672
+ .filter((server) => serverIds.includes(server.id))
673
+ .map((server) => server.capabilityId),
674
+ ),
675
+ ];
676
+ const connectionIds = (scope: "workspace" | "subject") =>
677
+ selectedBindings
678
+ .filter((binding) => binding.subjectScope === scope)
679
+ .map((binding) => binding.connectionId);
680
+ const integrationRoutes = (
681
+ integrations: readonly ApiIntegrationRuntime[],
682
+ bindings: typeof input.accountBindings,
683
+ ) =>
684
+ expandApiIntegrationAccountRoutes({
685
+ integrations,
686
+ bindings,
687
+ tools: allGatewayToolRefs(gatewaySettings),
688
+ });
689
+ const expectedServers = digestCanonicalJson(gatewaySettings.mcpServers);
690
+ const expectedIntegrations = digestCanonicalJson(
691
+ integrationRoutes(input.integrations, input.accountBindings),
692
+ );
693
+ const changed = () => new HTTPException(403, { message: "tool_access_changed" });
694
+ return async (db, stop) => {
695
+ let currentIntegrations: readonly ApiIntegrationRuntime[] = [];
696
+ const currentSettings = await settingsWithEnabledCapabilityMcpServersForTargets(
697
+ db,
698
+ grant.workspaceId,
699
+ input.staticSettings,
700
+ {
701
+ subjectId: grant.subjectId,
702
+ accountId: grant.accountId,
703
+ serverIds,
704
+ capabilityIds,
705
+ onResolvedApiIntegrations: (items) => {
706
+ currentIntegrations = items;
707
+ },
708
+ signal: stop,
709
+ },
710
+ );
711
+ stop.throwIfAborted();
712
+ const connections = await visibleMcpAccountConnectionsByIds(db, {
713
+ accountId: grant.accountId,
714
+ workspaceId: grant.workspaceId,
715
+ source: bindingSource,
716
+ workspaceConnectionIds: connectionIds("workspace"),
717
+ personalConnectionIds: connectionIds("subject"),
718
+ });
719
+ // Reads nothing more: the selected connections are supplied.
720
+ const currentBindings = await availableMcpAccountBindings({
721
+ db,
722
+ accountId: grant.accountId,
723
+ workspaceId: grant.workspaceId,
724
+ settings: currentSettings,
725
+ tools: allGatewayToolRefs(currentSettings),
726
+ source: bindingSource,
727
+ connections,
728
+ });
729
+ let currentServers: string;
730
+ let currentIntegrationRoutes: string;
731
+ try {
732
+ const currentRoutes = expandMcpAccountRoutes({
733
+ settings: currentSettings,
734
+ tools: allGatewayToolRefs(currentSettings),
735
+ bindings: currentBindings,
736
+ });
737
+ currentServers = digestCanonicalJson(
738
+ workspaceToolGatewaySettingsForGrant(currentRoutes.settings, grant).mcpServers.filter(
739
+ (server) => gatewayServerIds.has(server.id),
740
+ ),
741
+ );
742
+ currentIntegrationRoutes = digestCanonicalJson(
743
+ integrationRoutes(currentIntegrations, currentBindings),
744
+ );
745
+ } catch {
746
+ // A route that can no longer be expanded is a changed route.
747
+ throw changed();
748
+ }
749
+ if (currentServers !== expectedServers || currentIntegrationRoutes !== expectedIntegrations)
750
+ throw changed();
751
+ };
227
752
  }
228
753
 
229
754
  async function prepareWorkspaceToolGatewayForGrantInternal(
230
755
  routeDeps: ApiRouteDeps,
231
756
  grant: AccessGrant,
232
757
  allowedIdentities?: readonly { serverId: string; toolName: string }[],
233
- hostContext?: { authority: McpGatewayCredentialAuthority; reauthorize: () => Promise<void> },
758
+ reauthorize?: () => Promise<void>,
759
+ options: WorkspaceToolGatewayPreparationOptions = {},
234
760
  ): Promise<PreparedWorkspaceToolGateway> {
235
761
  const catalogSourceSettings = routeDeps.catalogSourceSettings ?? routeDeps.settings;
236
- const resolvedCatalog = await resolveWorkspaceCatalogSettings(
237
- routeDeps.db,
238
- catalogSourceSettings,
239
- {
240
- accountId: grant.accountId,
241
- workspaceId: grant.workspaceId,
242
- },
243
- );
762
+ // Remote tools do not consume the model catalog. First-party handlers keep
763
+ // the complete caller model/settings context (not a narrowed provider view).
764
+ const targetNamespace =
765
+ options.target &&
766
+ ("identity" in options.target ? options.target.identity.serverId : options.target.path[0]);
767
+ const needsModelContext =
768
+ !targetNamespace || ["opengeni", "docs", "files", "artifacts"].includes(targetNamespace);
769
+ const resolvedCatalog = needsModelContext
770
+ ? await resolveWorkspaceCatalogSettings(routeDeps.db, catalogSourceSettings, {
771
+ accountId: grant.accountId,
772
+ workspaceId: grant.workspaceId,
773
+ })
774
+ : { settings: catalogSourceSettings };
244
775
  let integrations: readonly ApiIntegrationRuntime[] = [];
776
+ let capabilityServers: readonly EnabledMcpCapabilityServer[] = [];
777
+ // One designation read serves the catalog overlay and the Apps request
778
+ // authentication below (every Apps request is rechecked by the database).
779
+ let codexAppsDesignationRead: ReturnType<typeof resolveCodexAppsDesignationForRun> | null = null;
780
+ const codexAppsDesignationForGrant = () =>
781
+ (codexAppsDesignationRead ??= resolveCodexAppsDesignationForRun(
782
+ routeDeps.db,
783
+ grant.workspaceId,
784
+ { accountId: grant.accountId },
785
+ ));
245
786
  const settings = await settingsWithEnabledCapabilityMcpServers(
246
787
  routeDeps.db,
247
788
  grant.workspaceId,
@@ -251,29 +792,92 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
251
792
  onResolvedApiIntegrations: (resolved) => {
252
793
  integrations = resolved;
253
794
  },
795
+ onResolvedCapabilityServers: (resolved) => {
796
+ capabilityServers = resolved;
797
+ },
798
+ ...(resolvedCatalog.settings.codexConnectedAppsEnabled
799
+ ? { codexApps: codexAppsDesignationForGrant() }
800
+ : {}),
254
801
  },
255
802
  );
256
- const gatewaySettings = workspaceToolGatewaySettingsForGrant(settings, grant, allowedIdentities);
803
+ // Transport admission has already verified the current caller. A service
804
+ // receives workspace accounts only; human transports use their exact subject.
805
+ const bindingSource: Parameters<typeof availableMcpAccountBindings>[0]["source"] =
806
+ grant.principalKind === "service" || grant.principalKind === "api_key"
807
+ ? { kind: "none" }
808
+ : { kind: "subject", accountId: grant.accountId, subjectId: grant.subjectId };
809
+ const accountBindings = await availableMcpAccountBindings({
810
+ db: routeDeps.db,
811
+ accountId: grant.accountId,
812
+ workspaceId: grant.workspaceId,
813
+ settings,
814
+ tools: allGatewayToolRefs(settings),
815
+ source: bindingSource,
816
+ });
817
+ const accountRoutes = expandMcpAccountRoutes({
818
+ settings,
819
+ tools: allGatewayToolRefs(settings),
820
+ bindings: accountBindings,
821
+ });
822
+ const callerSettings = workspaceToolGatewaySettingsForGrant(accountRoutes.settings, grant);
823
+ const targetServerId = options.target
824
+ ? workspaceToolGatewayTargetServer(callerSettings, options.target)
825
+ : undefined;
826
+ if (options.target && "identity" in options.target) allowedIdentities = [options.target.identity];
827
+ // OAuth/Site identities are account-qualified. Intersect only after expansion;
828
+ // canonical connector IDs are never aliases for an account's execution route.
829
+ const gatewaySettings = workspaceToolGatewaySettingsForGrant(
830
+ targetServerId
831
+ ? {
832
+ ...accountRoutes.settings,
833
+ mcpServers: accountRoutes.settings.mcpServers.filter(
834
+ (server) => server.id === targetServerId,
835
+ ),
836
+ }
837
+ : accountRoutes.settings,
838
+ grant,
839
+ allowedIdentities,
840
+ );
257
841
  const gatewayServerIds = new Set(gatewaySettings.mcpServers.map((server) => server.id));
258
- const deps = { ...routeDeps, catalogSourceSettings, settings: gatewaySettings };
842
+ const fence = options.fence;
843
+ // Targeted preparation is always fenced: its live route and policy checks
844
+ // exist only as fence parts. Frozen attempt/Codemode decisions and the
845
+ // complete gateway retain their existing lifecycle semantics.
846
+ if (options.target && !fence) throw new Error("targeted tool preparation requires a live fence");
847
+ if (fence && options.target)
848
+ fence.setRoute(
849
+ workspaceToolGatewayTargetRouteCheck({
850
+ grant,
851
+ staticSettings: resolvedCatalog.settings,
852
+ gatewaySettings,
853
+ integrations,
854
+ capabilityServers,
855
+ accountBindings,
856
+ bindingSource,
857
+ }),
858
+ );
859
+ const firstPartySettings =
860
+ options.firstPartySettings === "caller" && (allowedIdentities || options.target)
861
+ ? callerSettings
862
+ : gatewaySettings;
863
+ const deps = { ...routeDeps, catalogSourceSettings, settings: firstPartySettings };
259
864
  const nativeResolveConnection = buildConnectionTokenResolver(routeDeps.db, gatewaySettings);
260
- const hostPort = routeDeps.connectionCredentials?.mcpGatewayCredentials;
261
- const hostResolveConnection =
262
- hostContext && hostPort
263
- ? buildHostGatewayConnectionTokenResolver(
264
- hostPort,
265
- {
266
- accountId: grant.accountId,
267
- workspaceId: grant.workspaceId,
268
- authority: hostContext.authority,
269
- },
270
- hostContext.reauthorize,
271
- )
272
- : undefined;
273
- const resolveConnection: typeof nativeResolveConnection = async (input) =>
274
- input.connectionRef.authoritySource === "host" && hostResolveConnection
275
- ? await hostResolveConnection(input)
276
- : await nativeResolveConnection(input);
865
+ const resolveConnection = fence
866
+ ? withTargetFenceConnectionAuthorization(nativeResolveConnection, fence, {
867
+ preflight: options.target !== undefined,
868
+ })
869
+ : withWorkspaceConnectionAuthorization(nativeResolveConnection, reauthorize);
870
+ // The runtime's guarded transport runs this before every remote MCP request.
871
+ // Connection-broker requests are already fenced by their credential hook, so
872
+ // only Codex Apps and connectors without a connection reference (the
873
+ // runtime's own routing rule, widened to every Codex id) evaluate it here.
874
+ const authorizeTransportRequest = fence
875
+ ? gatewaySettings.mcpServers.some(
876
+ (server) => server.id === CODEX_APPS_MCP_SERVER_ID || !server.connectionRef,
877
+ )
878
+ ? async () => await fence.full()
879
+ : async () => fence.assertOpen()
880
+ : reauthorize;
277
881
  const resolveCredential = async (
278
882
  input: ResolveConnectionCredentialInput,
279
883
  ): Promise<ResolveConnectionCredentialResult> =>
@@ -299,6 +903,7 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
299
903
  routeDeps.getDocumentServices(),
300
904
  {
301
905
  knowledge: await knowledgeContextForGateway(routeDeps, grant),
906
+ settings: firstPartySettings,
302
907
  },
303
908
  ),
304
909
  ),
@@ -307,7 +912,11 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
307
912
  ]);
308
913
  const apiIntegrationServers = buildApiIntegrationMcpServers({
309
914
  settings: gatewaySettings,
310
- integrations: integrations.filter((integration) => gatewayServerIds.has(integration.serverId)),
915
+ integrations: expandApiIntegrationAccountRoutes({
916
+ integrations,
917
+ bindings: accountBindings,
918
+ tools: allGatewayToolRefs(gatewaySettings),
919
+ }),
311
920
  authority: {
312
921
  accountId: grant.accountId,
313
922
  workspaceId: grant.workspaceId,
@@ -319,37 +928,51 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
319
928
  ...(input.connectionRef.subjectScope === "subject" ? { subjectId: grant.subjectId } : {}),
320
929
  }),
321
930
  });
322
- const codexAppsCredentialId = gatewayServerIds.has("codex_apps")
323
- ? await resolveCodexAppsCredentialIdForRun(routeDeps.db, grant.workspaceId)
931
+ // Legacy designation without a Codex cutover row; the core designation
932
+ // (rechecked by the database on every request) with an enabled cutover;
933
+ // none with a disabled cutover.
934
+ const codexAppsDesignation = gatewayServerIds.has("codex_apps")
935
+ ? await codexAppsDesignationForGrant()
324
936
  : null;
325
- const codexAppsAuth = codexAppsCredentialId
326
- ? (() => {
327
- const resolver = buildCodexTokenResolver(
328
- routeDeps.db,
329
- settings,
330
- grant.workspaceId,
331
- codexAppsCredentialId,
332
- );
333
- return {
334
- clientVersion: CODEX_CLIENT_VERSION,
335
- withAuthorization: async <T>(
336
- use: (token: { accessToken: string; chatgptAccountId: string | null }) => Promise<T>,
337
- ): Promise<T> =>
338
- await resolver.getToken().then(
339
- async (token) =>
340
- await withCodexAppsRequestAuthorization(
341
- routeDeps.db,
342
- {
343
- workspaceId: grant.workspaceId,
344
- credentialId: codexAppsCredentialId,
345
- },
346
- async () => await use(token),
347
- ),
348
- ),
349
- };
350
- })()
937
+ const codexAppsAuth = codexAppsDesignation
938
+ ? codexAppsRequestAuthForDesignation(
939
+ routeDeps.db,
940
+ settings,
941
+ grant.workspaceId,
942
+ codexAppsDesignation,
943
+ )
351
944
  : undefined;
352
945
  const localMcpServers = [...firstPartyServers, ...apiIntegrationServers];
946
+ const policyTargets = new Map(
947
+ gatewaySettings.mcpServers.map((config) => {
948
+ const binding = accountBindings.find((candidate) => candidate.serverId === config.id);
949
+ return [
950
+ config.id,
951
+ {
952
+ connectionId:
953
+ binding?.connectionId ??
954
+ config.connectionRef?.connectionId ??
955
+ sessionMcpApprovalConnectionId(config.id, config.url),
956
+ serverId: binding?.canonicalServerId ?? config.id,
957
+ },
958
+ ] as const;
959
+ }),
960
+ );
961
+ const policiesByServer = new Map(
962
+ await Promise.all(
963
+ [...policyTargets].map(
964
+ async ([id, target]) =>
965
+ [
966
+ id,
967
+ await listConnectorToolPermissionPolicies(routeDeps.db, {
968
+ ...grant,
969
+ connectionId: target.connectionId,
970
+ }),
971
+ ] as const,
972
+ ),
973
+ ),
974
+ );
975
+ const recommendations = new Map<string, "allow" | "ask">();
353
976
  const prepared = await prepareWorkspaceToolGatewayTools(
354
977
  gatewaySettings,
355
978
  allGatewayToolRefs(gatewaySettings),
@@ -357,14 +980,114 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
357
980
  accountId: grant.accountId,
358
981
  workspaceId: grant.workspaceId,
359
982
  subjectId: grant.subjectId,
983
+ ...(options.signal ? { requestSignal: options.signal } : {}),
984
+ ...(authorizeTransportRequest ? { authorizeProviderRequest: authorizeTransportRequest } : {}),
360
985
  credentialSubjectId: grant.subjectId,
986
+ mcpAccountLabels: accountRoutes.accountLabels,
361
987
  resolveCredential,
988
+ slackRateLimit: buildSlackApiRateLimiter(routeDeps.db, gatewaySettings),
362
989
  localMcpServers,
363
990
  ...(codexAppsAuth ? { codexAppsAuth } : {}),
364
991
  workspaceToolGateway: {
365
- requireApproval: (entry, _caller, context) =>
366
- entry.approval === "human" && context.transportMeta?.approvalConfirmed !== true,
367
- filterDefinition: workspaceToolGatewayDefinitionFilter(gatewaySettings, allowedIdentities),
992
+ resolveArguments: workspaceGatewaySecretArguments({
993
+ db: routeDeps.db,
994
+ settings: gatewaySettings,
995
+ grant,
996
+ providersForSubject: (subjectId) =>
997
+ vaultProviderRegistryFor(routeDeps, { workspaceId: grant.workspaceId, subjectId }),
998
+ }),
999
+ mapDefinition: (definition) => {
1000
+ // Raw/local providers have no native credential revision. The legacy
1001
+ // gateway fallback binds the whole public catalog, including Ask/Allow
1002
+ // presentation. Targeted approvals instead use the runtime's existing
1003
+ // executable authority plus the exact selected configuration already
1004
+ // fenced by the target route check. Never replace adapter authority.
1005
+ if (options.target && definition.approvalAuthorityDigest === undefined) {
1006
+ const config = gatewaySettings.mcpServers.find(
1007
+ (server) => server.id === definition.identity.serverId,
1008
+ );
1009
+ if (!config || !definition.effectAuthorityDigest)
1010
+ throw new Error("target approval authority unavailable");
1011
+ definition = {
1012
+ ...definition,
1013
+ approvalAuthorityDigest: digestCanonicalJson({
1014
+ domain: "opengeni.target-tool-config-authority",
1015
+ version: 1,
1016
+ accountId: grant.accountId,
1017
+ workspaceId: grant.workspaceId,
1018
+ identity: definition.identity,
1019
+ effectAuthorityDigest: definition.effectAuthorityDigest,
1020
+ configuration: config,
1021
+ }),
1022
+ };
1023
+ }
1024
+ const recommendation = definition.approval === "human" ? "ask" : "allow";
1025
+ recommendations.set(workspaceToolGatewayIdentityKey(definition.identity), recommendation);
1026
+ const target = policyTargets.get(definition.identity.serverId);
1027
+ if (!target) return definition;
1028
+ const permission = projectConnectorToolPermission(
1029
+ policiesByServer.get(definition.identity.serverId) ?? [],
1030
+ {
1031
+ ...target,
1032
+ toolName: definition.identity.toolName,
1033
+ defaultDecision: recommendation,
1034
+ },
1035
+ );
1036
+ // A transport without a verified approval round trip omits any tool
1037
+ // which could ask. Its execution still resolves the exact arguments.
1038
+ return { ...definition, approval: permission.approvalRequired ? "human" : "policy" };
1039
+ },
1040
+ resolveApproval: async ({ call, entry }) => {
1041
+ const defaultDecision =
1042
+ recommendations.get(workspaceToolGatewayIdentityKey(entry.identity)) ??
1043
+ (entry.approval === "human" ? "ask" : "allow");
1044
+ const target = policyTargets.get(entry.identity.serverId);
1045
+ if (!target) return defaultDecision;
1046
+ const resolveSelectedPolicy = async (db: Database = routeDeps.db) =>
1047
+ resolveConnectorActionPolicy(
1048
+ await listConnectorToolPermissionPolicies(db, {
1049
+ ...grant,
1050
+ connectionId: target.connectionId,
1051
+ }),
1052
+ {
1053
+ ...target,
1054
+ toolName: entry.identity.toolName,
1055
+ defaultDecision,
1056
+ actionName: toolPolicyActionName(
1057
+ entry.identity.toolName,
1058
+ entry.inputSchema,
1059
+ call.arguments,
1060
+ ),
1061
+ },
1062
+ );
1063
+ const resolved = await resolveSelectedPolicy();
1064
+ const decision = !resolved.managed
1065
+ ? defaultDecision
1066
+ : connectorActionPolicyDecision(resolved);
1067
+ recordToolApproval(decision, resolved.managed ? resolved.source : "default");
1068
+ // Only targeted current requests fence their decided action policy
1069
+ // live, at every later physical-request boundary of this call.
1070
+ if (fence && options.target)
1071
+ fence.setPolicy(async (db) => {
1072
+ const live = await resolveSelectedPolicy(db);
1073
+ const current = live.managed ? connectorActionPolicyDecision(live) : defaultDecision;
1074
+ if (current === "block") throw new ToolGatewayBlockedError();
1075
+ if (current === "ask" && decision !== "ask")
1076
+ throw new ToolGatewayApprovalRequiredError();
1077
+ });
1078
+ return decision;
1079
+ },
1080
+ filterDefinition: (definition) => {
1081
+ if (!workspaceToolGatewayDefinitionFilter(gatewaySettings, allowedIdentities)(definition))
1082
+ return false;
1083
+ const target = options.target;
1084
+ if (!target || "identity" in target) return true;
1085
+ const path = projectToolGatewayPath(definition);
1086
+ return (
1087
+ path.length === target.path.length &&
1088
+ path.every((part, index) => part === target.path[index])
1089
+ );
1090
+ },
368
1091
  },
369
1092
  },
370
1093
  );
@@ -372,6 +1095,10 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
372
1095
  await prepared.close().catch(() => undefined);
373
1096
  throw new Error("workspace tool gateway preparation did not produce a gateway");
374
1097
  }
1098
+ if (options.signal?.aborted) {
1099
+ await prepared.close().catch(() => undefined);
1100
+ options.signal.throwIfAborted();
1101
+ }
375
1102
  return {
376
1103
  toolGateway: prepared.toolGateway,
377
1104
  toolGatewayCatalog: prepared.toolGatewayCatalog,
@@ -379,6 +1106,34 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
379
1106
  };
380
1107
  }
381
1108
 
1109
+ /** Select one authorized connector without constructing any provider. */
1110
+ export function workspaceToolGatewayTargetServer(
1111
+ settings: Pick<Settings, "mcpServers">,
1112
+ target: ToolGatewayTarget,
1113
+ ): string {
1114
+ if ("identity" in target) {
1115
+ if (settings.mcpServers.some((server) => server.id === target.identity.serverId))
1116
+ return target.identity.serverId;
1117
+ } else {
1118
+ const candidates = new Set(
1119
+ settings.mcpServers
1120
+ .filter((server) => safeNamespaceSegment(server.id) === target.path[0])
1121
+ .map((server) => server.id),
1122
+ );
1123
+ if (
1124
+ Object.values(EDITABLE_ARTIFACT_MCP_CODEMODE_PATHS).some(
1125
+ (path) =>
1126
+ path.length === target.path.length &&
1127
+ path.every((part, index) => part === target.path[index]),
1128
+ ) &&
1129
+ settings.mcpServers.some((server) => server.id === "opengeni")
1130
+ )
1131
+ candidates.add("opengeni");
1132
+ if (candidates.size === 1) return [...candidates][0]!;
1133
+ }
1134
+ throw new HTTPException(404, { message: "tool_unavailable" });
1135
+ }
1136
+
382
1137
  export function workspaceToolGatewayDefinitionFilter(
383
1138
  settings: Pick<Settings, "allowedFirstPartyMcpTools">,
384
1139
  allowedIdentities?: readonly ToolGatewayIdentity[],
@@ -548,7 +1303,9 @@ export async function callWorkspaceToolGateway(
548
1303
  );
549
1304
  await prepared.reauthorize?.();
550
1305
  let approvalConfirmed = false;
551
- const approvalRequired = preparedCall.entry.approval === "human";
1306
+ const approvalRequired =
1307
+ preparedCall.approvalDecision === "ask" ||
1308
+ (preparedCall.approvalDecision === undefined && preparedCall.entry.approval === "human");
552
1309
  if (approvalRequired && request.approvalToken && db) {
553
1310
  approvalConfirmed = await consumeApproval(db, {
554
1311
  tokenHash: hashOpaqueValue(request.approvalToken),
@@ -568,17 +1325,16 @@ export async function callWorkspaceToolGateway(
568
1325
  transportMeta.approvalConfirmed = approvalConfirmed;
569
1326
  const origin =
570
1327
  siteContext && db
571
- ? await resolveOrigin(
572
- db,
573
- grant.workspaceId,
574
- siteContext.siteArtifactId,
575
- siteContext.siteVersionId,
576
- )
1328
+ ? await resolveOrigin(db, grant, siteContext.siteArtifactId, siteContext.siteVersionId)
577
1329
  : null;
578
1330
  await prepared.reauthorize?.();
579
- const result = await (origin
580
- ? withSiteSessionOrigin(origin, () => preparedCall.execute())
581
- : preparedCall.execute());
1331
+ // Secret references in a Site call resolve with the viewer's reach.
1332
+ const secretContext = siteContext
1333
+ ? { siteId: siteContext.siteArtifactId, siteVersion: siteContext.siteVersionId }
1334
+ : null;
1335
+ const result = await withSiteSecretContext(secretContext, () =>
1336
+ origin ? withSiteSessionOrigin(origin, () => preparedCall.execute()) : preparedCall.execute(),
1337
+ );
582
1338
  observation.end(result.isError ? "tool_error" : "ok");
583
1339
  return ToolGatewayCallResponse.parse({
584
1340
  operationId,
@@ -616,7 +1372,10 @@ export async function approveWorkspaceToolGatewayCall(
616
1372
  } catch (error) {
617
1373
  throwWorkspaceToolGatewayHttpError(error);
618
1374
  }
619
- if (preparedCall.entry.approval !== "human") {
1375
+ if (
1376
+ preparedCall.approvalDecision !== "ask" &&
1377
+ !(preparedCall.approvalDecision === undefined && preparedCall.entry.approval === "human")
1378
+ ) {
620
1379
  throw new HTTPException(422, { message: "tool_does_not_require_human_approval" });
621
1380
  }
622
1381
  const observation = startWorkspaceToolGatewayObservation(observability, {
@@ -681,7 +1440,7 @@ function catalogStaleHttpError(): ApiHttpError {
681
1440
  });
682
1441
  }
683
1442
 
684
- function throwWorkspaceToolGatewayHttpError(error: unknown): never {
1443
+ export function throwWorkspaceToolGatewayHttpError(error: unknown): never {
685
1444
  if (error instanceof ToolGatewayCatalogStaleError) {
686
1445
  throw catalogStaleHttpError();
687
1446
  }
@@ -689,11 +1448,42 @@ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
689
1448
  throw new HTTPException(404, { message: error.code, cause: error });
690
1449
  }
691
1450
  if (error instanceof ToolGatewayInputValidationError) {
692
- throw new HTTPException(422, { message: error.code, cause: error });
1451
+ // The same value-free summary the model sees: which properties are missing
1452
+ // or mistyped, never the submitted argument values.
1453
+ throw new ApiHttpError(422, {
1454
+ code: "validation_failed",
1455
+ message: error.message,
1456
+ retryable: false,
1457
+ details: {
1458
+ code: error.code,
1459
+ issues: error.issues,
1460
+ omittedIssueCount: error.omittedIssueCount,
1461
+ },
1462
+ });
693
1463
  }
694
1464
  if (error instanceof ToolGatewayApprovalRequiredError) {
695
1465
  throw new HTTPException(409, { message: error.code, cause: error });
696
1466
  }
1467
+ if (error instanceof ToolGatewayBlockedError) {
1468
+ throw new HTTPException(403, { message: error.code, cause: error });
1469
+ }
1470
+ if (isSecretReferenceError(error)) {
1471
+ // Nothing ran. The failures name references, never values.
1472
+ throw new ApiHttpError(422, {
1473
+ code: "validation_failed",
1474
+ message: error.message,
1475
+ retryable: false,
1476
+ details: {
1477
+ code: error.code,
1478
+ failures: error.failures.map((failure) => ({
1479
+ raw: failure.raw,
1480
+ code: failure.code,
1481
+ message: failure.message,
1482
+ ...(failure.candidates ? { candidates: failure.candidates } : {}),
1483
+ })),
1484
+ },
1485
+ });
1486
+ }
697
1487
  if (error instanceof IntegrationInvocationError && error.outcome === "unknown") {
698
1488
  throw new ApiHttpError(502, {
699
1489
  code: "upstream_unavailable",
@@ -707,16 +1497,44 @@ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
707
1497
  throw error;
708
1498
  }
709
1499
 
1500
+ /** A saved Site version, only when the caller can see the Site. */
1501
+ export type WorkspaceSiteVersionResolver = (
1502
+ db: ApiRouteDeps["db"],
1503
+ grant: AccessGrant,
1504
+ artifactId: string,
1505
+ versionId: string,
1506
+ ) => Promise<{
1507
+ status: Awaited<ReturnType<typeof getWorkspaceArtifactContentRef>>["status"];
1508
+ version: Pick<
1509
+ Awaited<ReturnType<typeof getWorkspaceArtifactContentRef>>["version"],
1510
+ "id" | "requestedTools"
1511
+ >;
1512
+ }>;
1513
+
1514
+ const resolveVisibleSiteVersion: WorkspaceSiteVersionResolver = async (
1515
+ db,
1516
+ grant,
1517
+ artifactId,
1518
+ versionId,
1519
+ ) =>
1520
+ await getWorkspaceArtifactContentRef(
1521
+ db,
1522
+ grant.workspaceId,
1523
+ await siteViewerForGrant(db, grant),
1524
+ artifactId,
1525
+ versionId,
1526
+ );
1527
+
710
1528
  export async function requireWorkspaceSiteToolAuthorization(
711
1529
  db: ApiRouteDeps["db"],
712
1530
  grant: AccessGrant,
713
1531
  context: WorkspaceSiteToolContext,
714
- resolveVersion = getWorkspaceArtifactContentRef,
1532
+ resolveVersion: WorkspaceSiteVersionResolver = resolveVisibleSiteVersion,
715
1533
  ): Promise<void> {
716
1534
  try {
717
1535
  const { status, version } = await resolveVersion(
718
1536
  db,
719
- grant.workspaceId,
1537
+ grant,
720
1538
  context.siteArtifactId,
721
1539
  context.siteVersionId,
722
1540
  );