@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
@@ -1,16 +1,112 @@
1
- import { CreateApiKeyRequest, CreateApiKeyResponse, Permission } from "@opengeni/contracts";
2
- import { createApiKey, listApiKeys, revokeApiKey } from "@opengeni/db";
1
+ import {
2
+ CreateApiKeyRequest,
3
+ CreateApiKeyResponse,
4
+ CreateOrganizationApiKeyRequest,
5
+ UpdateOrganizationApiKeyRequest,
6
+ normalizeOrganizationAccessPolicy,
7
+ type OrganizationAccessPolicy,
8
+ DEVELOPER_SETUP_API_KEY_PRESET,
9
+ Permission,
10
+ type AccessContext,
11
+ type ApiKey,
12
+ type OrganizationApiKeyAccess,
13
+ } from "@opengeni/contracts";
14
+ import {
15
+ createApiKey,
16
+ createOrganizationApiKey as createOrganizationApiKeyRecord,
17
+ listApiKeys,
18
+ listOrganizationApiKeys,
19
+ getOrganizationApiKey,
20
+ updateOrganizationApiKey,
21
+ OrganizationApiKeyWorkspaceScopeError,
22
+ OrganizationApiKeyLimitExceededError,
23
+ OrganizationServiceAccountNotFoundError,
24
+ OrganizationServiceAccountRoleError,
25
+ revokeApiKey,
26
+ revokeOrganizationApiKey,
27
+ } from "@opengeni/db";
28
+ import { configuredStaticUsageLimits } from "@opengeni/config";
3
29
  import { zValidator } from "@hono/zod-validator";
4
30
  import type { Hono } from "hono";
5
31
  import { HTTPException } from "hono/http-exception";
6
32
  import type { ApiRouteDeps } from "@opengeni/core";
7
- import { requireAccessGrant } from "@opengeni/core";
33
+ import {
34
+ accountScopedApiKeyWorkspaceAuthority,
35
+ organizationApiKeyAccess,
36
+ hasPermission,
37
+ organizationWorkspaceInScope,
38
+ requireAccessContext,
39
+ requireAccessGrantAuthorization,
40
+ requireApiKeyManagementContext,
41
+ requireExplicitPermissionDelegation,
42
+ type AccessGrantAuthorization,
43
+ } from "@opengeni/core";
8
44
  import { requireLimit } from "@opengeni/core";
9
45
 
46
+ export { organizationApiKeyAccess } from "@opengeni/core";
47
+
48
+ /** Permissions minted onto a `full` organization API key. */
49
+ export const organizationApiKeyPermissions: Permission[] = [
50
+ "account:read",
51
+ "workspace:create",
52
+ "workspace:read",
53
+ "workspace:admin",
54
+ "api_keys:manage",
55
+ ];
56
+
57
+ /**
58
+ * Permissions minted onto a `read` organization API key. The key inventories
59
+ * shared workspaces and reads their sessions, events, and files; it holds no
60
+ * `workspace:admin` wildcard, so every control, create, and key-management
61
+ * route denies it, and no `api_keys:manage`, so it cannot mint keys.
62
+ */
63
+ export const organizationReadApiKeyPermissions: Permission[] = [
64
+ "account:read",
65
+ "workspace:read",
66
+ "sessions:read",
67
+ "files:read",
68
+ ];
69
+
70
+ export function organizationApiKeyPermissionsForAccess(
71
+ access: OrganizationApiKeyAccess,
72
+ ): Permission[] {
73
+ return access === "developer_setup"
74
+ ? [...DEVELOPER_SETUP_API_KEY_PRESET.permissions]
75
+ : access === "read"
76
+ ? [...organizationReadApiKeyPermissions]
77
+ : [...organizationApiKeyPermissions];
78
+ }
79
+
80
+ /** Existing callers retain their expiry; setup keys default to one day. */
81
+ export function organizationApiKeyExpiryDate(
82
+ request: Pick<CreateOrganizationApiKeyRequest, "preset" | "expiresAt"> &
83
+ Partial<Pick<CreateOrganizationApiKeyRequest, "access">>,
84
+ now: Date = new Date(),
85
+ ): Date | null {
86
+ if (request.expiresAt !== undefined) return new Date(request.expiresAt);
87
+ return request.preset === "developer_setup" || request.access === "developer_setup"
88
+ ? new Date(now.getTime() + DEVELOPER_SETUP_API_KEY_PRESET.defaultExpiryHours * 60 * 60 * 1000)
89
+ : null;
90
+ }
91
+
92
+ function withOrganizationApiKeyAccess(apiKey: ApiKey): ApiKey {
93
+ const workspaceScope = apiKey.workspaceScope ?? { kind: "all" as const };
94
+ return {
95
+ ...apiKey,
96
+ access: organizationApiKeyAccess(apiKey.permissions),
97
+ workspaceScope,
98
+ policy: normalizeOrganizationAccessPolicy({
99
+ preset: "custom",
100
+ permissions: apiKey.permissions,
101
+ workspaceScope,
102
+ }),
103
+ };
104
+ }
105
+
10
106
  export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
11
107
  app.get("/v1/workspaces/:workspaceId/api-keys", async (c) => {
12
108
  const workspaceId = c.req.param("workspaceId");
13
- await requireAccessGrant(c, deps, workspaceId, "api_keys:manage");
109
+ await requireWorkspaceApiKeyControl(c, deps, workspaceId);
14
110
  return c.json({ apiKeys: await listApiKeys(deps.db, workspaceId) });
15
111
  });
16
112
 
@@ -19,11 +115,12 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
19
115
  zValidator("json", CreateApiKeyRequest.omit({ workspaceId: true })),
20
116
  async (c) => {
21
117
  const workspaceId = c.req.param("workspaceId");
22
- const grant = await requireAccessGrant(c, deps, workspaceId, "api_keys:manage");
118
+ const authorization = await requireWorkspaceApiKeyControl(c, deps, workspaceId);
119
+ const grant = authorization.grant;
23
120
  const body = c.req.valid("json");
24
121
  const permissions: Permission[] =
25
122
  body.permissions.length > 0 ? (body.permissions as Permission[]) : ["workspace:read"];
26
- ensureDelegablePermissions(grant.permissions, permissions);
123
+ ensureDelegablePermissions(authorization, permissions);
27
124
  await requireLimit(deps, {
28
125
  accountId: grant.accountId,
29
126
  workspaceId,
@@ -36,6 +133,7 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
36
133
  accountId: grant.accountId,
37
134
  workspaceId: grant.workspaceId,
38
135
  name: body.name,
136
+ description: body.description ?? null,
39
137
  prefix,
40
138
  keyHash: await sha256Hex(token),
41
139
  permissions,
@@ -47,15 +145,172 @@ export function registerApiKeyRoutes(app: Hono, deps: ApiRouteDeps): void {
47
145
 
48
146
  app.delete("/v1/workspaces/:workspaceId/api-keys/:apiKeyId", async (c) => {
49
147
  const workspaceId = c.req.param("workspaceId");
50
- await requireAccessGrant(c, deps, workspaceId, "api_keys:manage");
148
+ await requireWorkspaceApiKeyControl(c, deps, workspaceId);
51
149
  return c.json(await revokeApiKey(deps.db, workspaceId, c.req.param("apiKeyId")));
52
150
  });
151
+
152
+ app.get("/v1/organizations/:organizationId/api-keys", async (c) => {
153
+ const organizationId = c.req.param("organizationId");
154
+ const context = await requireAccessContext(c, deps);
155
+ requireOrganizationApiKeyControlPermission(context, organizationId);
156
+ return c.json({
157
+ apiKeys: (await listOrganizationApiKeys(deps.db, organizationId)).map(
158
+ withOrganizationApiKeyAccess,
159
+ ),
160
+ });
161
+ });
162
+
163
+ app.post(
164
+ "/v1/organizations/:organizationId/api-keys",
165
+ zValidator("json", CreateOrganizationApiKeyRequest),
166
+ async (c) => {
167
+ const organizationId = c.req.param("organizationId");
168
+ const context = await requireAccessContext(c, deps);
169
+ requireOrganizationApiKeyControlPermission(context, organizationId);
170
+ const body = c.req.valid("json");
171
+ const rawBody: Record<string, unknown> = await c.req.json();
172
+ if (body.policy && (rawBody.access !== undefined || rawBody.preset !== undefined))
173
+ throw new HTTPException(400, {
174
+ message: "Choose either a policy or a legacy access tier/preset",
175
+ });
176
+ const token = generateApiKeyToken();
177
+ const permissions =
178
+ body.policy?.permissions ??
179
+ (body.preset === "developer_setup"
180
+ ? [...DEVELOPER_SETUP_API_KEY_PRESET.permissions]
181
+ : organizationApiKeyPermissionsForAccess(body.access));
182
+ ensureOrganizationPolicyDelegable(
183
+ context,
184
+ body.policy ?? {
185
+ preset: "custom",
186
+ permissions,
187
+ workspaceScope: { kind: "all" },
188
+ },
189
+ !body.policy,
190
+ );
191
+ try {
192
+ const apiKey = await createOrganizationApiKeyRecord(deps.db, {
193
+ accountId: organizationId,
194
+ name: body.name,
195
+ description: body.description ?? null,
196
+ prefix: token.slice(0, 14),
197
+ keyHash: await sha256Hex(token),
198
+ permissions,
199
+ ...(body.policy ? { policy: body.policy } : {}),
200
+ expiresAt: organizationApiKeyExpiryDate(body),
201
+ maxActiveKeys: organizationApiKeyLimit(deps),
202
+ rotationSourceApiKeyId: authenticatedApiKeyId(context),
203
+ serviceAccountId: body.serviceAccountId ?? null,
204
+ createdBySubjectId: context.subjectId,
205
+ });
206
+ return c.json(
207
+ CreateApiKeyResponse.parse({ apiKey: withOrganizationApiKeyAccess(apiKey), token }),
208
+ 201,
209
+ );
210
+ } catch (error) {
211
+ if (error instanceof OrganizationApiKeyLimitExceededError) {
212
+ throw new HTTPException(429, { message: error.message });
213
+ }
214
+ if (error instanceof OrganizationApiKeyWorkspaceScopeError)
215
+ throw new HTTPException(400, { message: error.message });
216
+ throwServiceAccountError(error);
217
+ throw error;
218
+ }
219
+ },
220
+ );
221
+
222
+ app.get("/v1/organizations/:organizationId/api-keys/:apiKeyId", async (c) => {
223
+ const organizationId = c.req.param("organizationId");
224
+ requireOrganizationApiKeyControlPermission(await requireAccessContext(c, deps), organizationId);
225
+ const apiKey = await getOrganizationApiKey(deps.db, organizationId, c.req.param("apiKeyId"));
226
+ if (!apiKey) throw new HTTPException(404, { message: "API key not found" });
227
+ return c.json(withOrganizationApiKeyAccess(apiKey));
228
+ });
229
+
230
+ app.patch(
231
+ "/v1/organizations/:organizationId/api-keys/:apiKeyId",
232
+ zValidator("json", UpdateOrganizationApiKeyRequest),
233
+ async (c) => {
234
+ const organizationId = c.req.param("organizationId");
235
+ const context = await requireAccessContext(c, deps);
236
+ requireOrganizationApiKeyControlPermission(context, organizationId);
237
+ const body = c.req.valid("json");
238
+ if (body.policy) ensureOrganizationPolicyDelegable(context, body.policy);
239
+ try {
240
+ const apiKey = await updateOrganizationApiKey(
241
+ deps.db,
242
+ organizationId,
243
+ c.req.param("apiKeyId"),
244
+ body,
245
+ );
246
+ if (!apiKey) throw new HTTPException(404, { message: "API key not found" });
247
+ return c.json(withOrganizationApiKeyAccess(apiKey));
248
+ } catch (error) {
249
+ if (error instanceof OrganizationApiKeyWorkspaceScopeError)
250
+ throw new HTTPException(400, { message: error.message });
251
+ throwServiceAccountError(error);
252
+ throw error;
253
+ }
254
+ },
255
+ );
256
+
257
+ app.delete("/v1/organizations/:organizationId/api-keys/:apiKeyId", async (c) => {
258
+ const organizationId = c.req.param("organizationId");
259
+ const context = await requireAccessContext(c, deps);
260
+ requireOrganizationApiKeyControlPermission(context, organizationId);
261
+ const apiKey = await revokeOrganizationApiKey(deps.db, organizationId, c.req.param("apiKeyId"));
262
+ if (!apiKey) {
263
+ throw new HTTPException(404, { message: "API key not found" });
264
+ }
265
+ return c.json(withOrganizationApiKeyAccess(apiKey));
266
+ });
267
+ }
268
+
269
+ /** Service account problems read as the person would expect. */
270
+ export function throwServiceAccountError(error: unknown): void {
271
+ if (error instanceof OrganizationServiceAccountNotFoundError)
272
+ throw new HTTPException(404, { message: error.message });
273
+ if (error instanceof OrganizationServiceAccountRoleError)
274
+ throw new HTTPException(400, { message: error.message });
275
+ }
276
+
277
+ function requireAccountPermission(
278
+ context: AccessContext,
279
+ accountId: string,
280
+ permission: Permission,
281
+ ): void {
282
+ const grant = context.accountGrants.find((candidate) => candidate.accountId === accountId);
283
+ if (
284
+ !grant ||
285
+ (!grant.permissions.includes(permission) && !grant.permissions.includes("account:admin"))
286
+ ) {
287
+ throw new HTTPException(403, { message: `missing permission: ${permission}` });
288
+ }
53
289
  }
54
290
 
55
- function ensureDelegablePermissions(grantPermissions: Permission[], requested: Permission[]): void {
56
- if (grantPermissions.includes("workspace:admin")) {
291
+ function ensureDelegablePermissions(
292
+ authorization: AccessGrantAuthorization,
293
+ requested: Permission[],
294
+ ): void {
295
+ requireExplicitPermissionDelegation(authorization.grant, requested);
296
+ const grantPermissions = authorization.grant.permissions;
297
+ if (
298
+ grantPermissions.includes("workspace:admin") &&
299
+ authorization.grant.permissionMode !== "explicit"
300
+ ) {
301
+ const accountLiteralPermissions = new Set<Permission>([
302
+ "account:read",
303
+ "account:admin",
304
+ "workspace:create",
305
+ "billing:read",
306
+ "billing:manage",
307
+ ]);
308
+ const workspaceLiteralPermissions = new Set<Permission>(["members:manage", "secrets:read"]);
57
309
  const highTrustMissing = requested.filter(
58
- (permission) => permission === "secrets:read" && !grantPermissions.includes("secrets:read"),
310
+ (permission) =>
311
+ (accountLiteralPermissions.has(permission) &&
312
+ !authorization.accountGrant?.permissions.includes(permission)) ||
313
+ (workspaceLiteralPermissions.has(permission) && !grantPermissions.includes(permission)),
59
314
  );
60
315
  if (highTrustMissing.length === 0) return;
61
316
  throw new HTTPException(403, {
@@ -70,6 +325,77 @@ function ensureDelegablePermissions(grantPermissions: Permission[], requested: P
70
325
  }
71
326
  }
72
327
 
328
+ /** Key management is not a way for a policy credential to amplify itself. */
329
+ function ensureOrganizationPolicyDelegable(
330
+ context: AccessContext,
331
+ policy: OrganizationAccessPolicy,
332
+ legacy = false,
333
+ ): void {
334
+ const authority = accountScopedApiKeyWorkspaceAuthority(context);
335
+ const source = context.credential?.policy;
336
+ if (!authority || authority.permissionMode !== "explicit" || !source) return;
337
+ const requested = legacy
338
+ ? Permission.options.filter((permission) => hasPermission(policy.permissions, permission))
339
+ : policy.permissions;
340
+ if (requested.some((permission) => !source.permissions.includes(permission)))
341
+ throw new HTTPException(403, {
342
+ message: "cannot delegate permissions beyond organization key policy",
343
+ });
344
+ if (
345
+ source.workspaceScope.kind === "selected" &&
346
+ (policy.workspaceScope.kind === "all" ||
347
+ policy.workspaceScope.workspaceIds.some(
348
+ (id) => !organizationWorkspaceInScope(source.workspaceScope, id),
349
+ ))
350
+ )
351
+ throw new HTTPException(403, {
352
+ message: "cannot delegate workspaces beyond organization key scope",
353
+ });
354
+ }
355
+
356
+ export function requireOrganizationApiKeyControlPermission(
357
+ context: AccessContext,
358
+ organizationId: string,
359
+ ): void {
360
+ requireApiKeyManagementContext(context);
361
+ requireAccountPermission(context, organizationId, "api_keys:manage");
362
+ if (!context.subjectId.startsWith("api_key:")) return;
363
+ const authority = accountScopedApiKeyWorkspaceAuthority(context);
364
+ if (
365
+ !authority ||
366
+ authority.accountId !== organizationId ||
367
+ (!authority.permissions.includes("api_keys:manage") &&
368
+ !context.accountGrants.some(
369
+ (grant) =>
370
+ grant.accountId === organizationId && grant.permissions.includes("account:admin"),
371
+ ))
372
+ ) {
373
+ throw new HTTPException(403, { message: "organization API key authority required" });
374
+ }
375
+ }
376
+
377
+ async function requireWorkspaceApiKeyControl(
378
+ c: Parameters<typeof requireAccessContext>[0],
379
+ deps: ApiRouteDeps,
380
+ workspaceId: string,
381
+ ): Promise<AccessGrantAuthorization> {
382
+ requireApiKeyManagementContext(await requireAccessContext(c, deps));
383
+ return await requireAccessGrantAuthorization(c, deps, workspaceId, "api_keys:manage");
384
+ }
385
+
386
+ function authenticatedApiKeyId(context: AccessContext): string | null {
387
+ return context.subjectId.startsWith("api_key:")
388
+ ? context.subjectId.slice("api_key:".length)
389
+ : null;
390
+ }
391
+
392
+ function organizationApiKeyLimit(deps: ApiRouteDeps): number | null {
393
+ if (deps.settings.usageLimitsMode !== "static" && deps.settings.usageLimitsMode !== "managed") {
394
+ return null;
395
+ }
396
+ return configuredStaticUsageLimits(deps.settings).maxApiKeysPerWorkspace ?? null;
397
+ }
398
+
73
399
  function generateApiKeyToken(): string {
74
400
  const bytes = new Uint8Array(32);
75
401
  crypto.getRandomValues(bytes);
@@ -0,0 +1,279 @@
1
+ import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
2
+ import {
3
+ ArtifactCatalogItem,
4
+ ArtifactCatalogKind,
5
+ ArtifactCatalogListQuery,
6
+ ArtifactCatalogListResponse,
7
+ retainedArtifactReferenceFromFile,
8
+ retainedGeneratedImageReferenceFromFile,
9
+ type AccessGrant,
10
+ } from "@opengeni/contracts";
11
+ import {
12
+ editableArtifactId,
13
+ editableArtifactScope,
14
+ fileOwnerContextForAccess,
15
+ grantHasAgentAttemptAuthority,
16
+ hasPermission,
17
+ requireAccessGrantAuthorization,
18
+ requireLiveAgentAttemptAuthorization,
19
+ requireSessionAuthorization,
20
+ SessionAuthorizationDeniedError,
21
+ SessionAuthorizationUnavailableError,
22
+ type ApiRouteDeps,
23
+ } from "@opengeni/core";
24
+ import {
25
+ getFilesForSubject,
26
+ getGeneratedImageArtifact,
27
+ getSessionAuthorityProjection,
28
+ listArtifactCatalogCandidates,
29
+ withSessionRlsActorContext,
30
+ type ArtifactCatalogCandidate,
31
+ type ArtifactCatalogPosition,
32
+ type SessionRlsActorContext,
33
+ } from "@opengeni/db";
34
+ import type { Hono } from "hono";
35
+ import { HTTPException } from "hono/http-exception";
36
+ import { artifactCatalogCursorCodec } from "../artifact-catalog-cursor";
37
+ import {
38
+ editableArtifactActorForGrant,
39
+ editableArtifactHttpError,
40
+ isInvisibleListCandidate,
41
+ type EditableArtifactRouteDependencies,
42
+ } from "./editable-artifacts";
43
+ import { fileAuthoritySubjectIdForGrant } from "./files";
44
+
45
+ type Dependencies = ApiRouteDeps & EditableArtifactRouteDependencies;
46
+ const editableKinds = new Set(["document", "spreadsheet", "presentation"]);
47
+ const CANDIDATE_BATCH = 200;
48
+ const MAX_BATCHES = 10;
49
+ const iso = (value: Date | string) => new Date(value).toISOString();
50
+ const position = (row: ArtifactCatalogCandidate): ArtifactCatalogPosition => ({
51
+ key: row.sort_key,
52
+ kind: row.kind,
53
+ id: row.id,
54
+ });
55
+
56
+ export function registerArtifactCatalogRoutes(app: Hono, deps: Dependencies): void {
57
+ app.get("/v1/workspaces/:workspaceId/artifact-catalog", async (context) => {
58
+ const workspaceId = context.req.param("workspaceId");
59
+ const authorization = await requireAccessGrantAuthorization(context, deps, workspaceId);
60
+ const grant = authorization.grant;
61
+ const parsed = ArtifactCatalogListQuery.safeParse(context.req.query());
62
+ if (!parsed.success)
63
+ throw new HTTPException(422, { message: "Invalid artifact catalog query" });
64
+ const query = parsed.data;
65
+ const artifactsAllowed = hasPermission(grant.permissions, "artifacts:read");
66
+ const filesAllowed = hasPermission(grant.permissions, "files:read");
67
+ if (!artifactsAllowed && !filesAllowed)
68
+ throw new HTTPException(403, { message: "Artifact catalog read access required" });
69
+ let kinds = ArtifactCatalogKind.options.filter(
70
+ (kind) =>
71
+ (kind === "image" || kind === "file" ? filesAllowed : artifactsAllowed) &&
72
+ (!query.kind || kind === query.kind),
73
+ );
74
+ if (!deps.editableArtifacts) {
75
+ if (query.kind && editableKinds.has(query.kind) && artifactsAllowed)
76
+ throw new HTTPException(503, { message: "Editable artifacts are unavailable" });
77
+ kinds = kinds.filter((kind) => !editableKinds.has(kind));
78
+ }
79
+ const includesFiles = kinds.includes("file") || kinds.includes("image");
80
+ const actor: SessionRlsActorContext = includesFiles
81
+ ? await fileOwnerContextForAccess(deps, authorization, "files:read")
82
+ : { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
83
+ // Session visibility follows the frozen live initiator independently of
84
+ // which artifact domains were selected. Never infer it from token labels
85
+ // or require file ownership merely to discover Sites/editable artifacts.
86
+ if (grantHasAgentAttemptAuthority(grant) && actor.initiatingHumanSubjectId === undefined) {
87
+ const callerSessionId = grant.metadata?.["sessionId"];
88
+ try {
89
+ if (typeof callerSessionId !== "string")
90
+ throw new SessionAuthorizationDeniedError("caller_stale");
91
+ const attempt = await requireLiveAgentAttemptAuthorization(deps.db, grant, callerSessionId);
92
+ actor.initiatingHumanSubjectId = attempt.initiatingHumanSubjectId;
93
+ } catch (error) {
94
+ if (error instanceof SessionAuthorizationDeniedError)
95
+ throw new HTTPException(404, { message: "Session not found", cause: error });
96
+ throw error;
97
+ }
98
+ }
99
+ const sourceDecisions = new Map<string, boolean>();
100
+ const canReadSession = async (sessionId: string): Promise<boolean> => {
101
+ if (!hasPermission(grant.permissions, "sessions:read")) return false;
102
+ if (sourceDecisions.has(sessionId)) return sourceDecisions.get(sessionId)!;
103
+ try {
104
+ await requireSessionAuthorization(deps, grant, {
105
+ sessionId,
106
+ operation: "session.read",
107
+ surface: "http",
108
+ });
109
+ // The optional-host seam can return null for both a shared standalone
110
+ // session and an RLS-hidden/missing row. Prove visibility independently
111
+ // AFTER it, so a permissive null is never itself provenance authority.
112
+ if (!(await getSessionAuthorityProjection(deps.db, workspaceId, sessionId))) {
113
+ sourceDecisions.set(sessionId, false);
114
+ return false;
115
+ }
116
+ sourceDecisions.set(sessionId, true);
117
+ return true;
118
+ } catch (error) {
119
+ if (error instanceof SessionAuthorizationDeniedError) {
120
+ sourceDecisions.set(sessionId, false);
121
+ return false;
122
+ }
123
+ if (error instanceof SessionAuthorizationUnavailableError)
124
+ throw new HTTPException(503, { message: "Session authorization is unavailable" });
125
+ throw error;
126
+ }
127
+ };
128
+ context.header("cache-control", "private, no-store");
129
+ return withSessionRlsActorContext(actor, async () => {
130
+ if (query.sourceSessionId && !(await canReadSession(query.sourceSessionId)))
131
+ throw new HTTPException(404, { message: "Session not found" });
132
+ const secret = resolveFirstPartyDelegationSecret(deps.settings);
133
+ if (!secret)
134
+ throw new HTTPException(503, { message: "Artifact catalog cursor signing is unavailable" });
135
+ const filters = { ...query, cursor: undefined };
136
+ const codec = artifactCatalogCursorCodec(
137
+ secret,
138
+ JSON.stringify({
139
+ accountId: grant.accountId,
140
+ workspaceId,
141
+ subjectId: grant.subjectId,
142
+ principalKind: grant.principalKind,
143
+ metadata: grant.metadata,
144
+ permissions: [...grant.permissions].sort(),
145
+ initiatingHumanSubjectId: actor.initiatingHumanSubjectId ?? null,
146
+ owner: actor.privateFileOwnerSubjectId ?? null,
147
+ kinds,
148
+ filters,
149
+ }),
150
+ );
151
+ const prior = query.cursor ? codec.decode(query.cursor) : undefined;
152
+ const snapshotAt = prior?.snapshotAt ?? new Date().toISOString();
153
+ const expiresAt = prior?.expiresAt ?? Date.now() + 60 * 60_000;
154
+ let after = prior?.after;
155
+ const items: ArtifactCatalogItem[] = [];
156
+ let lastReturned: ArtifactCatalogPosition | undefined;
157
+ let nextCursor: string | null = null;
158
+ const fileSubject = includesFiles ? await fileAuthoritySubjectIdForGrant(deps, grant) : null;
159
+ for (let batch = 0; batch < MAX_BATCHES; batch++) {
160
+ const candidates = await listArtifactCatalogCandidates(
161
+ deps.db,
162
+ { accountId: grant.accountId, workspaceId },
163
+ {
164
+ query,
165
+ kinds,
166
+ snapshotAt,
167
+ ...(after ? { after } : {}),
168
+ limit: CANDIDATE_BATCH,
169
+ },
170
+ );
171
+ for (const candidate of candidates) {
172
+ after = position(candidate);
173
+ const item = await projectCandidate(deps, grant, candidate, fileSubject);
174
+ if (
175
+ !item ||
176
+ item.status !== query.status ||
177
+ (query.q && !item.title.toLowerCase().includes(query.q.toLowerCase()))
178
+ )
179
+ continue;
180
+ if (items.length === query.limit) {
181
+ nextCursor = codec.encode({ version: 1, snapshotAt, expiresAt, after: lastReturned! });
182
+ return context.json(ArtifactCatalogListResponse.parse({ items, nextCursor }));
183
+ }
184
+ if (candidate.source_session_id && (await canReadSession(candidate.source_session_id)))
185
+ item.sourceSessionId = candidate.source_session_id;
186
+ items.push(ArtifactCatalogItem.parse(item));
187
+ lastReturned = after;
188
+ }
189
+ if (candidates.length < CANDIDATE_BATCH) break;
190
+ // Bounded scanning may produce an empty page; the encrypted frontier
191
+ // reveals neither denied identity nor provenance and can be continued.
192
+ if (batch === MAX_BATCHES - 1 && after)
193
+ nextCursor = codec.encode({ version: 1, snapshotAt, expiresAt, after });
194
+ }
195
+ return context.json(ArtifactCatalogListResponse.parse({ items, nextCursor }));
196
+ });
197
+ });
198
+ }
199
+
200
+ async function projectCandidate(
201
+ deps: Dependencies,
202
+ grant: AccessGrant,
203
+ row: ArtifactCatalogCandidate,
204
+ fileSubject: string | null,
205
+ ): Promise<ArtifactCatalogItem | null> {
206
+ const item: ArtifactCatalogItem = {
207
+ id: row.id,
208
+ kind: row.kind,
209
+ title: row.title,
210
+ status: row.status,
211
+ createdAt: iso(row.created_at),
212
+ updatedAt: iso(row.updated_at),
213
+ };
214
+ if (row.version_id) item.versionId = row.version_id;
215
+ if (editableKinds.has(row.kind)) {
216
+ const scope = editableArtifactScope({
217
+ accountId: grant.accountId,
218
+ workspaceId: grant.workspaceId,
219
+ });
220
+ try {
221
+ // This is the HTTP application's read-only domain.getArtifact seam, NOT
222
+ // EditableArtifactAgentApplication.read (which touches session links).
223
+ const artifact = await deps.editableArtifacts!.readArtifact({
224
+ scope,
225
+ actor: editableArtifactActorForGrant(grant, "0000000000000001"),
226
+ artifactId: editableArtifactId(row.id),
227
+ });
228
+ if (
229
+ artifact.scope.accountId !== scope.accountId ||
230
+ artifact.scope.workspaceId !== scope.workspaceId ||
231
+ artifact.id !== row.id ||
232
+ artifact.modality !== row.kind
233
+ )
234
+ throw new Error("Artifact application returned mismatched catalog metadata");
235
+ return {
236
+ ...item,
237
+ title: artifact.title,
238
+ status: artifact.lifecycle,
239
+ createdAt: artifact.createdAt,
240
+ updatedAt: artifact.updatedAt,
241
+ };
242
+ } catch (error) {
243
+ if (isInvisibleListCandidate(error)) return null;
244
+ throw editableArtifactHttpError(error);
245
+ }
246
+ }
247
+ if (row.kind === "file" || row.kind === "image") {
248
+ const [file] = await getFilesForSubject(deps.db, {
249
+ accountId: grant.accountId,
250
+ workspaceId: grant.workspaceId,
251
+ subjectId: fileSubject,
252
+ fileIds: [row.id],
253
+ });
254
+ if (!file || file.status !== "ready") return null;
255
+ const generated =
256
+ row.origin === "generated_image"
257
+ ? await getGeneratedImageArtifact(deps.db, grant.workspaceId, row.id)
258
+ : null;
259
+ const reference =
260
+ row.origin === "generated_image"
261
+ ? generated?.status === "ready"
262
+ ? retainedGeneratedImageReferenceFromFile({
263
+ ...file,
264
+ width: generated.width,
265
+ height: generated.height,
266
+ })
267
+ : null
268
+ : retainedArtifactReferenceFromFile(file, "file");
269
+ if (!reference) return null;
270
+ return {
271
+ ...item,
272
+ file: reference,
273
+ filename: file.filename,
274
+ contentType: reference.contentType,
275
+ sizeBytes: file.sizeBytes,
276
+ };
277
+ }
278
+ return item;
279
+ }