@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,60 @@
1
+ import type { Context, Hono } from "hono";
2
+ import { matchedRoutes } from "hono/route";
3
+
4
+ type RegisteredRoute = { method: string; path: string; handler: unknown };
5
+
6
+ const PROBED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const;
7
+
8
+ /**
9
+ * Whether a matched Hono route is a terminal handler rather than middleware.
10
+ *
11
+ * Method-specific registrations (`app.get`, `app.put`, `app.on([...])`) always
12
+ * belong to a real route. `app.use` and `app.all` both register the `ALL`
13
+ * method; Hono itself tells them apart by arity - middleware takes
14
+ * `(context, next)` - so the same rule is applied here.
15
+ */
16
+ function isTerminalRoute(route: RegisteredRoute): boolean {
17
+ if (route.method.toUpperCase() !== "ALL") return true;
18
+ return typeof route.handler === "function" && route.handler.length < 2;
19
+ }
20
+
21
+ function routesFor(app: Hono, method: string, path: string): RegisteredRoute[] {
22
+ // Every Hono router returns `[[ [handler, route], params ][], stash?]`.
23
+ const result = app.router.match(method, path) as unknown as [
24
+ Array<[[unknown, RegisteredRoute], unknown]>,
25
+ ...unknown[],
26
+ ];
27
+ return result[0].map(([[, route]]) => route);
28
+ }
29
+
30
+ export type UnmatchedRoute = { status: 404 } | { status: 405; allow: string[] };
31
+
32
+ /**
33
+ * Classify a request no registered handler can answer, BEFORE authentication
34
+ * or authorization middleware runs. Those layers otherwise turn a request for a
35
+ * route that does not exist into an authorization-shaped failure (for example
36
+ * the fail-closed session operation classifier answering a retryable 503), so
37
+ * a client probing an unknown method or path would be told to retry forever.
38
+ *
39
+ * Returns null when the request has a handler, or when classification itself
40
+ * fails (fail open: the request keeps its previous routing behavior).
41
+ */
42
+ export function unmatchedRoute(app: Hono, c: Context): UnmatchedRoute | null {
43
+ const method = c.req.method.toUpperCase();
44
+ // CORS preflights are answered by middleware and have no route handlers.
45
+ if (method === "OPTIONS") return null;
46
+ try {
47
+ if (matchedRoutes(c).some((route) => isTerminalRoute(route as RegisteredRoute))) {
48
+ return null;
49
+ }
50
+ const effectiveMethod = method === "HEAD" ? "GET" : method;
51
+ const allow = PROBED_METHODS.filter(
52
+ (candidate) =>
53
+ candidate !== effectiveMethod &&
54
+ routesFor(app, candidate, c.req.path).some(isTerminalRoute),
55
+ );
56
+ return allow.length > 0 ? { status: 405, allow } : { status: 404 };
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Response headers for user-controlled bytes served from the API origin.
3
+ *
4
+ * Retained files, screenshots, exports, and live frames carry bytes and, for
5
+ * uploads, a content type chosen by a user or agent. The API shares its origin
6
+ * with the web console in the default deployment, so a stored `text/html` file
7
+ * opened by top-level navigation would otherwise run as that origin.
8
+ *
9
+ * Every such response therefore:
10
+ * - is a sandboxed document with no script, form, popup, plugin, or network
11
+ * capability (`sandbox` plus `default-src 'none'`). Raster images and text
12
+ * still render when a URL is opened directly;
13
+ * - cannot be embedded by another site as a no-cors subresource (CORP);
14
+ * - is never re-sniffed into a different type (`nosniff`).
15
+ *
16
+ * Audio and video keep their real origin (`sandbox allow-same-origin`, still
17
+ * without `allow-scripts`): the browser's synthesized media document re-fetches
18
+ * its own URL, and from an opaque origin that same-origin-only request would be
19
+ * refused. With scripts disabled and `nosniff`, such a response can only ever
20
+ * be a media player, never attacker markup.
21
+ *
22
+ * Markup types that a browser would render as an active document (HTML, XML,
23
+ * SVG, multipart) are additionally served as attachments, so a direct
24
+ * navigation downloads the bytes instead of showing attacker-authored markup on
25
+ * the app origin. The console never navigates to these routes: it reads bytes
26
+ * through the SDK and renders them in app-owned elements, so neither header
27
+ * changes an in-app preview. Signed object-storage GET URLs for active markup
28
+ * carry the same attachment as a signed response override
29
+ * (`userContentSignedGetUrlOptions`), because the storage endpoint is not
30
+ * always a separate site.
31
+ */
32
+ const USER_CONTENT_FETCH_DIRECTIVES =
33
+ "default-src 'none'; img-src 'self' data: blob:; media-src 'self' data: blob:; style-src 'unsafe-inline'";
34
+
35
+ export const USER_CONTENT_SECURITY_POLICY = `${USER_CONTENT_FETCH_DIRECTIVES}; sandbox`;
36
+
37
+ /** Media players only; scripts stay disabled (see the module comment). */
38
+ export const USER_MEDIA_CONTENT_SECURITY_POLICY = `${USER_CONTENT_FETCH_DIRECTIVES}; sandbox allow-same-origin`;
39
+
40
+ export const USER_CONTENT_SECURITY_HEADERS = Object.freeze({
41
+ "Content-Security-Policy": USER_CONTENT_SECURITY_POLICY,
42
+ "Cross-Origin-Resource-Policy": "same-origin",
43
+ "X-Content-Type-Options": "nosniff",
44
+ } as const);
45
+
46
+ const ACTIVE_DOCUMENT_TYPES = new Set(["text/html", "text/xml", "text/xsl", "application/xml"]);
47
+
48
+ /** Media type essence (no parameters), lowercased. */
49
+ function mediaTypeEssence(contentType: string): string {
50
+ return (contentType.split(";", 1)[0] ?? "").trim().toLowerCase();
51
+ }
52
+
53
+ /**
54
+ * True when a browser would render this type as an active document (HTML,
55
+ * XHTML, SVG, any XML dialect, or multipart replacement streams). Anything that
56
+ * is not a clean `type/subtype` token is treated as active too.
57
+ */
58
+ export function isActiveUserContentType(contentType: string): boolean {
59
+ const essence = mediaTypeEssence(contentType);
60
+ if (!/^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/.test(essence)) return true;
61
+ return (
62
+ ACTIVE_DOCUMENT_TYPES.has(essence) ||
63
+ essence.endsWith("+xml") ||
64
+ essence.startsWith("multipart/")
65
+ );
66
+ }
67
+
68
+ const DISPOSITION_FILENAME_MAX_CHARS = 200;
69
+
70
+ /** RFC 8187 `attr-char` percent-encoding (stricter than `encodeURIComponent`). */
71
+ function encodeExtendedParameterValue(value: string): string {
72
+ return encodeURIComponent(value).replace(
73
+ /['()*]/g,
74
+ (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`,
75
+ );
76
+ }
77
+
78
+ /**
79
+ * RFC 6266 filename parameters: an ASCII-only quoted `filename` that cannot
80
+ * break out of the header, plus an RFC 8187 `filename*` carrying the original
81
+ * name (for example non-Latin names) whenever the ASCII form had to change it.
82
+ */
83
+ function dispositionFilenameParameters(filename: string): string {
84
+ const cleaned = Array.from(
85
+ filename
86
+ .replace(/[\u0000-\u001f\u007f/\\]+/g, "_")
87
+ // Lone surrogates cannot be percent-encoded as UTF-8.
88
+ .replace(/[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g, "_")
89
+ .trim(),
90
+ )
91
+ .slice(0, DISPOSITION_FILENAME_MAX_CHARS)
92
+ .join("");
93
+ const ascii =
94
+ cleaned
95
+ .replace(/[^A-Za-z0-9._ -]+/g, "_")
96
+ .replace(/\s+/g, " ")
97
+ .trim()
98
+ .slice(0, DISPOSITION_FILENAME_MAX_CHARS) || "download";
99
+ if (!cleaned || ascii === cleaned) return `filename="${ascii}"`;
100
+ return `filename="${ascii}"; filename*=UTF-8''${encodeExtendedParameterValue(cleaned)}`;
101
+ }
102
+
103
+ /** `Content-Disposition: attachment` for active markup; inline types are unchanged. */
104
+ export function userContentDispositionHeaders(
105
+ contentType: string,
106
+ filename?: string | null,
107
+ ): { "Content-Disposition": string } | Record<string, never> {
108
+ if (!isActiveUserContentType(contentType)) return {};
109
+ return {
110
+ "Content-Disposition": filename
111
+ ? `attachment; ${dispositionFilenameParameters(filename)}`
112
+ : "attachment",
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Signed object-storage GET options for one stored object: active markup gets
118
+ * a signed `attachment` response override, so a browser that opens the URL
119
+ * downloads the bytes instead of rendering them with their stored type. The
120
+ * storage endpoint is not always a separate site (local development serves it
121
+ * from loopback, and a preview may route the bucket path on the app origin),
122
+ * so a signed URL follows the same rule as the API routes. Spread into
123
+ * `createGetUrl`; inline types add nothing.
124
+ */
125
+ export function userContentSignedGetUrlOptions(
126
+ contentType: string,
127
+ filename?: string | null,
128
+ ): { responseContentDisposition: string } | Record<string, never> {
129
+ const headers = userContentDispositionHeaders(contentType, filename);
130
+ return "Content-Disposition" in headers
131
+ ? { responseContentDisposition: headers["Content-Disposition"] }
132
+ : {};
133
+ }
134
+
135
+ /** True for audio and video types the browser plays in a synthesized media document. */
136
+ export function isPlayableMediaUserContentType(contentType: string): boolean {
137
+ const essence = mediaTypeEssence(contentType);
138
+ return !isActiveUserContentType(essence) && /^(audio|video)\//.test(essence);
139
+ }
140
+
141
+ /** Sandbox policy for one response; only playable media keeps its origin. */
142
+ export function userContentSecurityPolicy(contentType: string): string {
143
+ return isPlayableMediaUserContentType(contentType)
144
+ ? USER_MEDIA_CONTENT_SECURITY_POLICY
145
+ : USER_CONTENT_SECURITY_POLICY;
146
+ }
147
+
148
+ /** Full header set for one user-content response with the given content type. */
149
+ export function userContentResponseHeaders(
150
+ contentType: string,
151
+ filename?: string | null,
152
+ ): Record<string, string> {
153
+ return {
154
+ ...USER_CONTENT_SECURITY_HEADERS,
155
+ "Content-Security-Policy": userContentSecurityPolicy(contentType),
156
+ ...userContentDispositionHeaders(contentType, filename),
157
+ };
158
+ }
@@ -0,0 +1,29 @@
1
+ import { localBrowserBoundaryResponse, type LocalBrowserBoundary } from "./local-browser-boundary";
2
+
3
+ export type ApiWebSocketUpgradeRoute<Server> = {
4
+ handles(request: Request): boolean;
5
+ upgrade(request: Request, server: Server): Response | undefined;
6
+ };
7
+
8
+ export type ApiWebSocketUpgradeDispatch =
9
+ | { handled: false }
10
+ | { handled: true; response: Response | undefined };
11
+
12
+ /**
13
+ * WebSocket upgrades bypass the Hono app, so the local-mode browser boundary
14
+ * (./local-browser-boundary.ts) is applied here before a transport upgrades.
15
+ * Returns `{ handled: false }` when no transport claims the request, so the
16
+ * caller dispatches it to the app, where the boundary runs as middleware.
17
+ */
18
+ export function dispatchApiWebSocketUpgrade<Server>(
19
+ request: Request,
20
+ server: Server,
21
+ routes: ReadonlyArray<ApiWebSocketUpgradeRoute<Server>>,
22
+ localBrowserBoundary: LocalBrowserBoundary | null,
23
+ ): ApiWebSocketUpgradeDispatch {
24
+ const route = routes.find((candidate) => candidate.handles(request));
25
+ if (!route) return { handled: false };
26
+ const rejection = localBrowserBoundary?.rejection(request);
27
+ if (rejection) return { handled: true, response: localBrowserBoundaryResponse(rejection) };
28
+ return { handled: true, response: route.upgrade(request, server) };
29
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { scheduledTaskKnowledgeSource } from "@opengeni/contracts";
1
2
  import {
2
3
  dbSearchPath,
3
4
  getSettings,
5
+ resolveFirstPartyDelegationSecret,
4
6
  resolveNatsCalloutConfig,
5
7
  resolveNatsControlPlaneAuth,
6
8
  retryStartupDependency,
@@ -14,16 +16,24 @@ import type {
14
16
  } from "@opengeni/contracts";
15
17
  import {
16
18
  assertRuntimeDatabasePosture,
19
+ isRetryableRuntimeDatabaseStartupError,
17
20
  createDb,
18
21
  markSessionWorkflowWakeDelivered,
19
22
  runtimeDatabaseReadyCheck,
20
23
  type Database,
21
24
  } from "@opengeni/db";
22
25
  import { createNatsEventBus, type ResponderConnection } from "@opengeni/events";
23
- import { createObservability, logStartupDependencyRetry } from "@opengeni/observability";
26
+ import {
27
+ createObservability,
28
+ logStartupDependencyRetry,
29
+ type Observability,
30
+ } from "@opengeni/observability";
24
31
  import { createObjectStorage } from "@opengeni/storage";
25
32
  import { isArtifactRuntimeConfigured } from "@opengeni/artifact-tool/runtime/development";
26
- import { SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID } from "@opengeni/core";
33
+ import {
34
+ resolveCatalogSettings,
35
+ SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID,
36
+ } from "@opengeni/core";
27
37
  import {
28
38
  Connection,
29
39
  Client as TemporalClient,
@@ -33,21 +43,31 @@ import {
33
43
  } from "@temporalio/client";
34
44
  import type { ScheduleOptions, ScheduleSpec, ScheduleUpdateOptions } from "@temporalio/client";
35
45
  import { createAppComposition, type DocumentIndexClient, type SessionWorkflowClient } from "./app";
36
- import { observabilityEventLogger } from "./observability";
46
+ import { observabilityEventBusOptions } from "./observability";
37
47
  import { startAuthCalloutResponder } from "./sandbox/auth-callout";
38
48
  import { startHelloIngestion, startMetricsIngestion } from "./sandbox/metrics-ingestion";
39
49
  import { startSlackInteractionPump } from "./integrations/slack-interactions";
40
50
  import { startMemorySlackPublicationPump } from "./memory-slack-delivery";
41
51
  import { startTemporalScheduleCleanupPump } from "./temporal-schedule-cleanup";
52
+ import { createTemporalScheduleSynchronizer } from "./temporal-schedule-sync";
53
+ import { startWorkspaceWebhookDispatchPump } from "./workspace-webhook-dispatch";
54
+ import { cleanupScheduledTaskConnectorAuthorization } from "./scheduled-task-deletion";
42
55
  import {
43
56
  EDITABLE_ARTIFACT_LIVE_WEBSOCKET_MAX_MESSAGE_BYTES,
44
57
  EditableArtifactWebSocketTransport,
45
- type EditableArtifactWebSocketConnection,
46
58
  } from "./editable-artifact-websocket";
59
+ import { editableArtifactSourceSessionAuthorizer } from "./editable-artifact-source-session";
60
+ import type { ApiWebSocketConnection } from "./api-websocket";
61
+ import { InteractionFrameProxyTransport } from "./interaction-frame-proxy";
62
+ import { apiRequestBindingsForTransportPeer } from "./http/request-source";
63
+ import { startApiMetricsListener } from "./http/metrics-listener";
64
+ import { createLocalBrowserBoundary } from "./http/local-browser-boundary";
65
+ import { dispatchApiWebSocketUpgrade } from "./http/websocket-upgrade-dispatch";
47
66
  import {
48
67
  createStandaloneEditableArtifactApplication,
49
68
  type StandaloneEditableArtifactApplication,
50
69
  } from "./editable-artifact-production";
70
+ import { installApiFatalProcessBoundary } from "./fatal-process-boundary";
51
71
 
52
72
  /**
53
73
  * A REJECT_DUPLICATE start collides on the deterministic workflowId when the
@@ -87,7 +107,42 @@ export async function createTemporalWorkflowClient(
87
107
  connection,
88
108
  namespace: settings.temporalNamespace,
89
109
  });
110
+ const scheduledTasks = createTemporalScheduleSynchronizer({
111
+ db,
112
+ withDeadline: (deadline, work) => temporal.withDeadline(deadline, work),
113
+ upsert: async (task) => {
114
+ const options = temporalScheduleOptions(task, settings.temporalTaskQueue);
115
+ try {
116
+ await temporal.schedule
117
+ .getHandle(task.temporalScheduleId)
118
+ .update(() => temporalScheduleUpdateOptions(options));
119
+ } catch (error) {
120
+ if (!shouldCreateScheduleAfterUpdateError(error)) throw error;
121
+ await temporal.schedule.create(options);
122
+ }
123
+ },
124
+ remove: async (temporalScheduleId) => {
125
+ try {
126
+ await temporal.schedule.getHandle(temporalScheduleId).delete();
127
+ } catch (error) {
128
+ if (!(error instanceof ScheduleNotFoundError)) throw error;
129
+ }
130
+ },
131
+ });
90
132
  const client: SessionWorkflowClient = {
133
+ triggerAutomationRun: async ({ accountId, workspaceId, runId }) => {
134
+ try {
135
+ await temporal.workflow.start("automationRunWorkflow", {
136
+ taskQueue: settings.temporalTaskQueue,
137
+ workflowId: `automation-run:${runId}`,
138
+ workflowIdReusePolicy: "REJECT_DUPLICATE",
139
+ args: [{ accountId, workspaceId, runId }],
140
+ });
141
+ } catch (error) {
142
+ if (isWorkflowAlreadyStarted(error)) return;
143
+ throw error;
144
+ }
145
+ },
91
146
  signalUserMessage: async ({ eventId, workflowId }) => {
92
147
  await temporal.workflow.getHandle(workflowId).signal("userMessage", eventId);
93
148
  },
@@ -98,6 +153,7 @@ export async function createTemporalWorkflowClient(
98
153
  workflowId,
99
154
  wakeRevision,
100
155
  interruptionRequested,
156
+ onSignalAccepted,
101
157
  }) => {
102
158
  await temporal.workflow.signalWithStart("sessionWorkflow", {
103
159
  taskQueue: settings.temporalTaskQueue,
@@ -106,7 +162,8 @@ export async function createTemporalWorkflowClient(
106
162
  args: [{ accountId, workspaceId, sessionId }],
107
163
  signal: interruptionRequested ? "sessionControl" : "queueChanged",
108
164
  });
109
- await markSessionWorkflowWakeDelivered(db, {
165
+ onSignalAccepted?.();
166
+ return await markSessionWorkflowWakeDelivered(db, {
110
167
  accountId,
111
168
  workspaceId,
112
169
  sessionId,
@@ -167,41 +224,15 @@ export async function createTemporalWorkflowClient(
167
224
  wakeRevision: workflowWakeRevision,
168
225
  });
169
226
  },
170
- syncScheduledTask: async ({ task }) => {
171
- const schedule = temporal.schedule.getHandle(task.temporalScheduleId);
172
- if (task.schedule.type === "manual") {
173
- try {
174
- await schedule.delete();
175
- } catch (error) {
176
- if (!(error instanceof ScheduleNotFoundError)) throw error;
177
- }
178
- return;
179
- }
180
- const options = temporalScheduleOptions(task, settings.temporalTaskQueue);
181
- try {
182
- await schedule.update(() => temporalScheduleUpdateOptions(options));
183
- } catch (error) {
184
- if (!shouldCreateScheduleAfterUpdateError(error)) {
185
- throw error;
186
- }
187
- await temporal.schedule.create(options);
188
- }
189
- },
190
- deleteScheduledTaskSchedule: async ({ temporalScheduleId }) => {
191
- try {
192
- await temporal.withDeadline(Date.now() + 5_000, async () => {
193
- await temporal.schedule.getHandle(temporalScheduleId).delete();
194
- });
195
- } catch (error) {
196
- if (error instanceof ScheduleNotFoundError) return;
197
- throw error;
198
- }
199
- },
227
+ syncScheduledTask: async ({ task, onFailure }) => scheduledTasks.sync(task, onFailure),
228
+ deleteScheduledTaskSchedule: async ({ temporalScheduleId }) =>
229
+ scheduledTasks.remove(temporalScheduleId),
200
230
  triggerScheduledTask: async ({
201
231
  task,
202
232
  agentRunUsageIdempotencyKey,
203
233
  triggerWorkflowId,
204
234
  initiator,
235
+ credentialRestriction,
205
236
  triggerType = "manual",
206
237
  }) => {
207
238
  // Deterministic workflowId (derived from the trigger token by the
@@ -222,6 +253,7 @@ export async function createTemporalWorkflowClient(
222
253
  triggerType,
223
254
  agentRunUsageIdempotencyKey,
224
255
  initiator,
256
+ ...(credentialRestriction ? { credentialRestriction } : {}),
225
257
  },
226
258
  ],
227
259
  });
@@ -237,7 +269,7 @@ export async function createTemporalWorkflowClient(
237
269
  startRigVerification: async ({ workspaceId, changeId, versionId, workflowId }) => {
238
270
  const targetId = changeId ?? versionId;
239
271
  if (!targetId) {
240
- throw new Error("rig verification requires changeId or versionId");
272
+ throw new Error("sandbox environment verification requires changeId or versionId");
241
273
  }
242
274
  try {
243
275
  await temporal.workflow.start("rigVerificationWorkflow", {
@@ -283,14 +315,21 @@ export async function createTemporalWorkflowClient(
283
315
  };
284
316
  }
285
317
 
286
- export async function startApi() {
287
- const settings = getSettings();
288
- const observability = createObservability(settings, { component: "api" });
318
+ export async function startApi(
319
+ options: {
320
+ settings?: ReturnType<typeof getSettings>;
321
+ observability?: Observability;
322
+ } = {},
323
+ ) {
324
+ const settings = options.settings ?? getSettings();
325
+ const observability =
326
+ options.observability ?? createObservability(settings, { component: "api" });
289
327
  // Step I: standalone → dbSchema unset → searchPath undefined → today's plain
290
328
  // handle (public). Embedded → scoped to the dedicated schema + the host's RLS
291
329
  // strategy.
292
330
  const searchPath = dbSearchPath(settings);
293
331
  const dbClient = createDb(settings.databaseUrl, {
332
+ max: settings.apiDatabasePoolMax,
294
333
  ...(searchPath ? { searchPath } : {}),
295
334
  rlsStrategy: settings.rlsStrategy,
296
335
  });
@@ -303,17 +342,41 @@ export async function startApi() {
303
342
  rlsStrategy: settings.rlsStrategy,
304
343
  expectedRole: settings.runtimeDatabaseRole,
305
344
  targetSchema: settings.dbSchema.trim() || "public",
345
+ organizationTenancyCanonicalActivationEnabled:
346
+ settings.organizationTenancyCanonicalActivationEnabled,
306
347
  } as const;
307
348
  // The PRIVILEGED control-plane NATS login (M-AUTH): when the server runs with
308
349
  // auth_callout, api/worker authenticate as a static account user permitted to
309
- // request `agent.*.rpc`. Null in local dev (anonymous connect — the bus default).
350
+ // request exact generation-fenced agent RPC subjects. Null in local dev
351
+ // (anonymous connect — the bus default).
310
352
  const controlPlaneAuth = resolveNatsControlPlaneAuth(settings);
311
353
  try {
354
+ // Browser, Computer, Terminal, and Files can cold-create the home sandbox
355
+ // directly from this API process. Resolve the same private-registry Modal
356
+ // image before the API becomes reachable so the first human interaction
357
+ // does not pay the provider import/build latency. This mirrors the turn
358
+ // worker's startup boundary and is a no-op for provider-native image IDs,
359
+ // public images, and non-Modal backends.
360
+ const { ensureModalRegistryImage } = await import("@opengeni/runtime/sandbox");
361
+ await retryStartupDependency(
362
+ "Modal private-registry image",
363
+ () => ensureModalRegistryImage(settings),
364
+ { ...retryOptions, onRetry },
365
+ );
312
366
  await retryStartupDependency(
313
367
  "PostgreSQL runtime posture",
314
368
  () => assertRuntimeDatabasePosture(dbClient.db, databasePosture),
369
+ { ...retryOptions, onRetry, shouldRetry: isRetryableRuntimeDatabaseStartupError },
370
+ );
371
+ const resolvedCatalog = await retryStartupDependency(
372
+ "model catalog",
373
+ () => resolveCatalogSettings(dbClient.db, settings),
315
374
  { ...retryOptions, onRetry },
316
375
  );
376
+ observability.info("OpenGeni model catalog resolved", {
377
+ catalogSource: resolvedCatalog.source,
378
+ catalogVersion: resolvedCatalog.version,
379
+ });
317
380
  bus = await retryStartupDependency(
318
381
  "NATS",
319
382
  () =>
@@ -322,7 +385,7 @@ export async function startApi() {
322
385
  controlPlaneAuth
323
386
  ? { user: controlPlaneAuth.user, pass: controlPlaneAuth.password }
324
387
  : undefined,
325
- { logger: observabilityEventLogger(observability) },
388
+ observabilityEventBusOptions(observability),
326
389
  ),
327
390
  {
328
391
  ...retryOptions,
@@ -385,32 +448,60 @@ export async function startApi() {
385
448
  behavior: "http_and_websocket_fail_closed",
386
449
  });
387
450
  }
388
- const artifactWebSockets = new EditableArtifactWebSocketTransport(routeDeps.editableArtifacts);
389
- const server = Bun.serve<EditableArtifactWebSocketConnection>({
451
+ const artifactWebSockets = new EditableArtifactWebSocketTransport(
452
+ routeDeps.editableArtifacts,
453
+ editableArtifactSourceSessionAuthorizer(routeDeps),
454
+ );
455
+ const interactionFrameProxies = new InteractionFrameProxyTransport(
456
+ resolveFirstPartyDelegationSecret(settings),
457
+ );
458
+ // WebSocket upgrades bypass the Hono app, so the local-mode browser boundary
459
+ // (http/local-browser-boundary.ts) is applied to them in the dispatcher;
460
+ // every other request meets it in the Hono middleware.
461
+ const localBrowserBoundary = createLocalBrowserBoundary(settings, {
462
+ warn: (message, attributes) => observability.warn(message, attributes),
463
+ });
464
+ const webSocketUpgrades = [interactionFrameProxies, artifactWebSockets];
465
+ const server = Bun.serve<ApiWebSocketConnection>({
390
466
  hostname: settings.apiHost,
391
467
  port: settings.apiPort,
392
468
  idleTimeout: 255,
393
469
  fetch: (request, bunServer) => {
394
- if (artifactWebSockets.handles(request)) {
395
- return artifactWebSockets.upgrade(request, bunServer);
396
- }
397
- return app.fetch(request);
470
+ const upgrade = dispatchApiWebSocketUpgrade(
471
+ request,
472
+ bunServer,
473
+ webSocketUpgrades,
474
+ localBrowserBoundary,
475
+ );
476
+ if (upgrade.handled) return upgrade.response;
477
+ return app.fetch(
478
+ request,
479
+ apiRequestBindingsForTransportPeer(bunServer.requestIP(request)?.address),
480
+ );
398
481
  },
399
482
  websocket: {
400
483
  maxPayloadLength: EDITABLE_ARTIFACT_LIVE_WEBSOCKET_MAX_MESSAGE_BYTES,
401
484
  backpressureLimit: 16 * 1024 * 1024,
402
485
  closeOnBackpressureLimit: true,
403
- open: (socket) => artifactWebSockets.websocket.open(socket),
404
- message: (socket, message) => artifactWebSockets.websocket.message(socket, message),
405
- close: (socket) => artifactWebSockets.websocket.close(socket),
486
+ open: (socket) => socket.data.attach(socket),
487
+ message: (socket, message) => socket.data.receive(message),
488
+ close: (socket) => socket.data.transportClosed(),
406
489
  },
407
490
  });
491
+ const metricsServer = startApiMetricsListener(settings, observability);
408
492
  const stopSlackInteractionPump = settings.slackSigningSecret
409
493
  ? startSlackInteractionPump(routeDeps)
410
494
  : undefined;
411
495
  const stopMemorySlackPublicationPump = startMemorySlackPublicationPump(routeDeps);
496
+ const stopWorkspaceWebhookDispatchPump = startWorkspaceWebhookDispatchPump({
497
+ db: dbClient.db,
498
+ settings,
499
+ observability,
500
+ });
412
501
  const stopTemporalScheduleCleanupPump = startTemporalScheduleCleanupPump({
413
502
  db: dbClient.db,
503
+ cleanupConnectorAuthorization: async (claim) =>
504
+ await cleanupScheduledTaskConnectorAuthorization(routeDeps, claim),
414
505
  deleteSchedule: async (temporalScheduleId) => {
415
506
  await workflowClient.client.deleteScheduledTaskSchedule({ temporalScheduleId });
416
507
  },
@@ -469,16 +560,21 @@ export async function startApi() {
469
560
  observability.info("OpenGeni API listening", {
470
561
  host: settings.apiHost,
471
562
  port: settings.apiPort,
563
+ ...(metricsServer ? { metricsPort: settings.apiMetricsPort } : {}),
472
564
  });
473
565
  return {
474
566
  server,
475
567
  close: async () => {
476
568
  server.stop(true);
569
+ metricsServer?.stop(true);
477
570
  stopSlackInteractionPump?.();
478
571
  await stopMemorySlackPublicationPump();
479
572
  stopMetricsIngestion?.();
480
573
  stopHelloIngestion?.();
481
574
  await stopTemporalScheduleCleanupPump();
575
+ await stopWorkspaceWebhookDispatchPump();
576
+ // Write queued presence before the database pool closes.
577
+ await routeDeps.userPresence?.close().catch(() => undefined);
482
578
  await Promise.allSettled([
483
579
  Promise.resolve(editableArtifactComposition?.close()),
484
580
  authCalloutResponder?.close(),
@@ -491,7 +587,16 @@ export async function startApi() {
491
587
  }
492
588
 
493
589
  if (import.meta.main) {
494
- await startApi();
590
+ const fatalBoundary = installApiFatalProcessBoundary();
591
+ try {
592
+ const settings = getSettings();
593
+ const observability = createObservability(settings, { component: "api" });
594
+ fatalBoundary.attachObservability(observability);
595
+ await startApi({ settings, observability });
596
+ fatalBoundary.markRunning();
597
+ } catch (error) {
598
+ await fatalBoundary.reportStartupFailure(error);
599
+ }
495
600
  }
496
601
 
497
602
  export function temporalOverlapPolicy(policy: ScheduledTaskOverlapPolicy): ScheduleOverlapPolicy {
@@ -612,7 +717,7 @@ function temporalScheduleOptions(task: ScheduledTask, taskQueue: string): Schedu
612
717
 
613
718
  function scheduledTaskEffectivelyPaused(task: ScheduledTask): boolean {
614
719
  if (task.status === "paused") return true;
615
- if (task.action.kind !== "knowledge_source_sync") return false;
720
+ if (!scheduledTaskKnowledgeSource(task)) return false;
616
721
  const control = task.metadata.knowledgeSourceSync;
617
722
  if (!control || typeof control !== "object") return false;
618
723
  const record = control as Record<string, unknown>;