@opengeni/api-router 0.30.1 → 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 (436) 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-office-import.d.ts +7 -4
  35. package/dist/editable-artifact-source-session.d.ts +3 -0
  36. package/dist/editable-artifact-websocket.d.ts +10 -18
  37. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  38. package/dist/fatal-process-boundary.d.ts +25 -0
  39. package/dist/github-access.d.ts +31 -2
  40. package/dist/http/acting-person.d.ts +24 -0
  41. package/dist/http/api-contract-compat.d.ts +19 -0
  42. package/dist/http/api-error.d.ts +27 -1
  43. package/dist/http/cors.d.ts +17 -0
  44. package/dist/http/deprecation.d.ts +42 -0
  45. package/dist/http/interaction-control-error.d.ts +13 -0
  46. package/dist/http/keyed-admission.d.ts +12 -0
  47. package/dist/http/local-browser-boundary.d.ts +72 -0
  48. package/dist/http/metrics-listener.d.ts +22 -0
  49. package/dist/http/path-identifier.d.ts +17 -0
  50. package/dist/http/registered-route-label.d.ts +16 -0
  51. package/dist/http/request-body.d.ts +27 -0
  52. package/dist/http/request-source.d.ts +52 -0
  53. package/dist/http/sse.d.ts +17 -6
  54. package/dist/http/unmatched-route.d.ts +18 -0
  55. package/dist/http/user-content.d.ts +36 -0
  56. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  57. package/dist/index.d.ts +10 -3
  58. package/dist/index.js +1129 -166
  59. package/dist/index.js.map +1 -1
  60. package/dist/integration-endpoint-test.d.ts +20 -0
  61. package/dist/integrations/api-integrations.d.ts +2 -2
  62. package/dist/integrations/atlassian.d.ts +29 -3
  63. package/dist/integrations/connect-authority.d.ts +1 -0
  64. package/dist/integrations/connect-callback-return.d.ts +6 -0
  65. package/dist/integrations/fiken.d.ts +16 -2
  66. package/dist/integrations/github-app-connect.d.ts +47 -0
  67. package/dist/integrations/github-installation-proof.d.ts +3 -0
  68. package/dist/integrations/github-lens-connect.d.ts +116 -0
  69. package/dist/integrations/github-skill-source.d.ts +1 -5
  70. package/dist/integrations/google-drive.d.ts +25 -0
  71. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  72. package/dist/integrations/marketplace-plugin.d.ts +2691 -0
  73. package/dist/integrations/oauth-client.d.ts +178 -18
  74. package/dist/integrations/oauth-profiles.d.ts +222 -0
  75. package/dist/integrations/oauth-return-path.d.ts +1 -0
  76. package/dist/integrations/personal-github-repositories.d.ts +84 -0
  77. package/dist/integrations/personal-github.d.ts +42 -0
  78. package/dist/integrations/pr-review-provider.d.ts +22 -0
  79. package/dist/integrations/provider-oauth.d.ts +19 -1
  80. package/dist/integrations/slack-app-home.d.ts +24 -0
  81. package/dist/integrations/slack-bot.d.ts +367 -12
  82. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  83. package/dist/integrations/slack-install.d.ts +20 -0
  84. package/dist/integrations/slack-interactions.d.ts +113 -10
  85. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  86. package/dist/integrations/slack-routing.d.ts +120 -0
  87. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  88. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  89. package/dist/integrations/social-oauth.d.ts +7 -0
  90. package/dist/interaction-agent-access.d.ts +15 -0
  91. package/dist/interaction-frame-proxy.d.ts +68 -0
  92. package/dist/interaction-metrics.d.ts +2 -0
  93. package/dist/mcp/action-catalog-types.d.ts +9 -0
  94. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  95. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  96. package/dist/mcp/child-read-evidence.d.ts +9 -0
  97. package/dist/mcp/command-read.d.ts +24 -0
  98. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  99. package/dist/mcp/contract-input.d.ts +11 -0
  100. package/dist/mcp/documents.d.ts +7 -5
  101. package/dist/mcp/files.d.ts +2 -2
  102. package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
  103. package/dist/mcp/knowledge-entries.d.ts +5 -0
  104. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  105. package/dist/mcp/receipts.d.ts +9 -0
  106. package/dist/mcp/request-abort.d.ts +19 -0
  107. package/dist/mcp/scheduled-task-input.d.ts +1189 -0
  108. package/dist/mcp/scheduled-task-view.d.ts +22 -4
  109. package/dist/mcp/server.d.ts +255 -3
  110. package/dist/mcp/session-event-view.d.ts +94 -0
  111. package/dist/mcp/session-view.d.ts +66 -2
  112. package/dist/mcp/session-wait.d.ts +174 -0
  113. package/dist/mcp-oauth.d.ts +52 -0
  114. package/dist/model-catalog.d.ts +11 -31
  115. package/dist/observability.d.ts +3 -1
  116. package/dist/organization-mcp.d.ts +52 -0
  117. package/dist/organization-recovery-notifications.d.ts +41 -0
  118. package/dist/routes/analytics-consent.d.ts +26 -0
  119. package/dist/routes/api-integrations.d.ts +25 -0
  120. package/dist/routes/api-keys.d.ts +17 -0
  121. package/dist/routes/artifact-catalog.d.ts +6 -0
  122. package/dist/routes/automations.d.ts +19 -0
  123. package/dist/routes/billing.d.ts +49 -0
  124. package/dist/routes/browser-sessions.d.ts +71 -3
  125. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  126. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  127. package/dist/routes/client-errors.d.ts +146 -0
  128. package/dist/routes/codex.d.ts +76 -2
  129. package/dist/routes/company-brain.d.ts +3 -0
  130. package/dist/routes/company-profile.d.ts +2 -1
  131. package/dist/routes/computer-sessions.d.ts +14 -0
  132. package/dist/routes/connect.d.ts +5 -0
  133. package/dist/routes/editable-artifacts.d.ts +2 -0
  134. package/dist/routes/external-identity-links.d.ts +6 -0
  135. package/dist/routes/feedback.d.ts +3 -0
  136. package/dist/routes/files.d.ts +4 -0
  137. package/dist/routes/github-browser-pages.d.ts +12 -0
  138. package/dist/routes/github.d.ts +2 -0
  139. package/dist/routes/insights-response-cache.d.ts +10 -0
  140. package/dist/routes/insights-usage.d.ts +12 -0
  141. package/dist/routes/insights.d.ts +30 -1
  142. package/dist/routes/knowledge.d.ts +3 -0
  143. package/dist/routes/managed-auth-session-sets.d.ts +26 -0
  144. package/dist/routes/managed-onboarding.d.ts +29 -0
  145. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  146. package/dist/routes/model-connection-access.d.ts +3 -0
  147. package/dist/routes/organization-integration-policy.d.ts +3 -0
  148. package/dist/routes/organization-integrations.d.ts +6 -0
  149. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  150. package/dist/routes/organization-model-providers.d.ts +3 -0
  151. package/dist/routes/organization-recovery.d.ts +19 -0
  152. package/dist/routes/organization-service-accounts.d.ts +3 -0
  153. package/dist/routes/organization-sessions.d.ts +26 -0
  154. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  155. package/dist/routes/personal-github.d.ts +3 -0
  156. package/dist/routes/pr-review-github.d.ts +3 -0
  157. package/dist/routes/pr-review.d.ts +3 -0
  158. package/dist/routes/session-artifact-associations.d.ts +4 -0
  159. package/dist/routes/session-history-imports.d.ts +9 -0
  160. package/dist/routes/sessions.d.ts +44 -6
  161. package/dist/routes/skill-content.d.ts +4 -0
  162. package/dist/routes/skill-install-authority.d.ts +6 -0
  163. package/dist/routes/subscription-account-pools.d.ts +34 -0
  164. package/dist/routes/subscription-pool-access.d.ts +16 -0
  165. package/dist/routes/supergrok.d.ts +6 -1
  166. package/dist/routes/usage-allowances.d.ts +12 -0
  167. package/dist/routes/user-resource-authorities.d.ts +3 -0
  168. package/dist/routes/workspace-artifacts.d.ts +2 -1
  169. package/dist/routes/workspace-capture.d.ts +10 -2
  170. package/dist/routes/workspace-integrations.d.ts +35 -0
  171. package/dist/routes/workspace-learning.d.ts +3 -0
  172. package/dist/routes/workspace-model-providers.d.ts +4 -0
  173. package/dist/routes/workspaces.d.ts +29 -0
  174. package/dist/sandbox/auth-callout.d.ts +8 -0
  175. package/dist/sandbox/channel-a.d.ts +19 -1
  176. package/dist/sandbox/connection-authority.d.ts +3 -0
  177. package/dist/sandbox/enrollment.d.ts +9 -1
  178. package/dist/sandbox/machines.d.ts +5 -2
  179. package/dist/sandbox/metrics-ingestion.d.ts +28 -20
  180. package/dist/sandbox/rematerialize.d.ts +2 -0
  181. package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
  182. package/dist/sandbox/viewer.d.ts +26 -1
  183. package/dist/sandbox-file-artifacts.d.ts +50 -0
  184. package/dist/scheduled-task-deletion.d.ts +13 -0
  185. package/dist/session-create-observability.d.ts +5 -0
  186. package/dist/session-event-view-MPS2OZD3.js +292 -0
  187. package/dist/session-event-view-MPS2OZD3.js.map +1 -0
  188. package/dist/site-session-origin.d.ts +13 -0
  189. package/dist/site-uploads.d.ts +41 -0
  190. package/dist/slack-reaction-files.d.ts +1 -1
  191. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  192. package/dist/temporal-schedule-sync.d.ts +26 -0
  193. package/dist/transcription/providers/xai-subscription.d.ts +1 -0
  194. package/dist/transcription/service.d.ts +2 -1
  195. package/dist/work-discovery-observability.d.ts +33 -0
  196. package/dist/workspace-artifact-content.d.ts +28 -0
  197. package/dist/workspace-artifact-provenance.d.ts +7 -0
  198. package/dist/workspace-delete-observability.d.ts +10 -0
  199. package/dist/workspace-deletion.d.ts +6 -0
  200. package/dist/workspace-state-projection.d.ts +3 -5
  201. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  202. package/dist/workspace-tool-gateway.d.ts +123 -0
  203. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  204. package/package.json +23 -19
  205. package/src/access-grant-rls.ts +75 -0
  206. package/src/admission-trace.ts +14 -0
  207. package/src/api-websocket.ts +23 -0
  208. package/src/app.ts +1974 -256
  209. package/src/artifact-catalog-cursor.ts +60 -0
  210. package/src/auth/managed-auth-attempt-context.ts +70 -0
  211. package/src/auth/managed-auth-rate-limits.ts +252 -0
  212. package/src/auth/managed-auth-session-adapter.ts +222 -0
  213. package/src/auth/managed-auth.ts +536 -60
  214. package/src/auth/managed-email.ts +174 -0
  215. package/src/auth/managed-sign-in-notifications.ts +81 -0
  216. package/src/auth/new-signups-gate.ts +49 -0
  217. package/src/auth/organization-user-setup.ts +307 -0
  218. package/src/auth/signup-funnel-metrics.ts +164 -0
  219. package/src/browser-controller-authority.ts +1 -186
  220. package/src/browser-ephemeral.ts +39 -0
  221. package/src/browser-state-authority.ts +1 -236
  222. package/src/claude-subscription-account-conflicts.ts +12 -0
  223. package/src/claude-subscription-account-usage.ts +48 -0
  224. package/src/claude-subscription-oauth.ts +362 -0
  225. package/src/claude-subscription-profile.ts +73 -0
  226. package/src/claude-subscription-usage.ts +97 -0
  227. package/src/claude-workspace-connection.ts +82 -0
  228. package/src/codemode.ts +194 -30
  229. package/src/codex-realtime.ts +8 -2
  230. package/src/company-brain-okf.ts +340 -0
  231. package/src/connected-machine-computer-access.ts +33 -0
  232. package/src/connection-ownership.ts +199 -0
  233. package/src/controller-data-plane.ts +81 -0
  234. package/src/editable-artifact-native-kernel.ts +26 -15
  235. package/src/editable-artifact-office-import.ts +77 -9
  236. package/src/editable-artifact-production.ts +23 -7
  237. package/src/editable-artifact-source-session.ts +123 -0
  238. package/src/editable-artifact-websocket.ts +27 -21
  239. package/src/editable-artifact-workspace-files.ts +31 -6
  240. package/src/fatal-process-boundary.ts +246 -0
  241. package/src/gateway-realtime.ts +1 -1
  242. package/src/github-access.ts +147 -5
  243. package/src/http/acting-person.ts +71 -0
  244. package/src/http/api-contract-compat.ts +49 -0
  245. package/src/http/api-error.ts +115 -1
  246. package/src/http/auth.ts +48 -0
  247. package/src/http/cors.ts +67 -0
  248. package/src/http/deprecation.ts +118 -0
  249. package/src/http/interaction-control-error.ts +164 -0
  250. package/src/http/keyed-admission.ts +29 -0
  251. package/src/http/local-browser-boundary.ts +343 -0
  252. package/src/http/metrics-listener.ts +57 -0
  253. package/src/http/path-identifier.ts +54 -0
  254. package/src/http/registered-route-label.ts +34 -0
  255. package/src/http/request-body.ts +127 -0
  256. package/src/http/request-source.ts +178 -0
  257. package/src/http/sse.ts +486 -132
  258. package/src/http/unmatched-route.ts +60 -0
  259. package/src/http/user-content.ts +158 -0
  260. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  261. package/src/index.ts +157 -52
  262. package/src/integration-endpoint-test.ts +291 -0
  263. package/src/integrations/api-integrations.ts +98 -11
  264. package/src/integrations/atlassian.ts +474 -136
  265. package/src/integrations/connect-authority.ts +2 -0
  266. package/src/integrations/connect-callback-return.ts +86 -0
  267. package/src/integrations/fiken.ts +354 -64
  268. package/src/integrations/github-app-connect.ts +435 -0
  269. package/src/integrations/github-installation-proof.ts +60 -0
  270. package/src/integrations/github-lens-connect.ts +76 -0
  271. package/src/integrations/github-skill-source.ts +2 -142
  272. package/src/integrations/google-drive.ts +820 -222
  273. package/src/integrations/integration-commit-authority.ts +69 -0
  274. package/src/integrations/marketplace-plugin.ts +48 -0
  275. package/src/integrations/oauth-client.ts +1356 -459
  276. package/src/integrations/oauth-profiles.ts +537 -0
  277. package/src/integrations/oauth-return-path.ts +18 -0
  278. package/src/integrations/personal-github-repositories.ts +674 -0
  279. package/src/integrations/personal-github.ts +825 -0
  280. package/src/integrations/pr-review-provider.ts +246 -0
  281. package/src/integrations/provider-oauth.ts +250 -22
  282. package/src/integrations/slack-app-home.ts +300 -0
  283. package/src/integrations/slack-bot.ts +963 -239
  284. package/src/integrations/slack-file-upload-flow.ts +147 -0
  285. package/src/integrations/slack-install.ts +83 -0
  286. package/src/integrations/slack-interactions.ts +3170 -474
  287. package/src/integrations/slack-mrkdwn.ts +193 -0
  288. package/src/integrations/slack-routing.ts +337 -0
  289. package/src/integrations/slack-session-defaults.ts +224 -0
  290. package/src/integrations/slack-task-file-upload.ts +323 -0
  291. package/src/integrations/social-oauth.ts +213 -15
  292. package/src/interaction-agent-access.ts +48 -0
  293. package/src/interaction-frame-proxy.ts +409 -0
  294. package/src/interaction-metrics.ts +30 -0
  295. package/src/mcp/action-catalog-types.ts +9 -0
  296. package/src/mcp/action-catalog.gen.ts +6696 -0
  297. package/src/mcp/capability-account-readiness.ts +17 -0
  298. package/src/mcp/child-read-evidence.ts +19 -0
  299. package/src/mcp/command-read.ts +109 -0
  300. package/src/mcp/company-profile-agent-admin.ts +198 -0
  301. package/src/mcp/contract-input.ts +141 -0
  302. package/src/mcp/documents.ts +65 -377
  303. package/src/mcp/editable-artifacts.ts +12 -6
  304. package/src/mcp/files.ts +80 -38
  305. package/src/mcp/first-party-tool-permissions.ts +305 -0
  306. package/src/mcp/knowledge-entries.ts +322 -0
  307. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  308. package/src/mcp/receipts.ts +34 -0
  309. package/src/mcp/request-abort.ts +44 -0
  310. package/src/mcp/scheduled-task-input.ts +76 -0
  311. package/src/mcp/scheduled-task-view.ts +44 -3
  312. package/src/mcp/server.ts +3030 -1267
  313. package/src/mcp/session-event-view.ts +383 -0
  314. package/src/mcp/session-view.ts +84 -3
  315. package/src/mcp/session-wait.ts +665 -0
  316. package/src/mcp-oauth.ts +1123 -0
  317. package/src/model-catalog.ts +91 -333
  318. package/src/observability.ts +15 -1
  319. package/src/organization-mcp.ts +397 -0
  320. package/src/organization-recovery-notifications.ts +103 -0
  321. package/src/routes/analytics-consent.ts +123 -0
  322. package/src/routes/api-integrations.ts +364 -74
  323. package/src/routes/api-keys.ts +336 -10
  324. package/src/routes/artifact-catalog.ts +279 -0
  325. package/src/routes/automations.ts +768 -0
  326. package/src/routes/billing.ts +461 -46
  327. package/src/routes/browser-sessions.ts +839 -131
  328. package/src/routes/canonical-human-identities.ts +90 -14
  329. package/src/routes/capabilities.ts +107 -6
  330. package/src/routes/channels.ts +20 -3
  331. package/src/routes/claude-subscription-accounts.ts +334 -0
  332. package/src/routes/claude-subscription-oauth.ts +142 -0
  333. package/src/routes/client-errors.ts +362 -0
  334. package/src/routes/codex.ts +833 -116
  335. package/src/routes/company-brain.ts +322 -0
  336. package/src/routes/company-profile.ts +65 -1
  337. package/src/routes/computer-sessions.ts +557 -106
  338. package/src/routes/connect.ts +1938 -0
  339. package/src/routes/connections.ts +1040 -349
  340. package/src/routes/documents.ts +488 -287
  341. package/src/routes/editable-artifacts.ts +79 -9
  342. package/src/routes/enrollments.ts +135 -39
  343. package/src/routes/environments.ts +154 -58
  344. package/src/routes/external-identity-links.ts +201 -0
  345. package/src/routes/feedback.ts +113 -0
  346. package/src/routes/files.ts +337 -39
  347. package/src/routes/github-browser-pages.ts +162 -0
  348. package/src/routes/github.ts +446 -144
  349. package/src/routes/insights-response-cache.ts +83 -0
  350. package/src/routes/insights-usage.ts +345 -0
  351. package/src/routes/insights.ts +153 -19
  352. package/src/routes/install.ts +133 -38
  353. package/src/routes/integration-facets.ts +107 -33
  354. package/src/routes/interaction-resources.ts +30 -3
  355. package/src/routes/knowledge.ts +488 -0
  356. package/src/routes/machines.ts +313 -15
  357. package/src/routes/managed-auth-session-sets.ts +1218 -0
  358. package/src/routes/managed-onboarding.ts +337 -0
  359. package/src/routes/managed-sign-in-methods.ts +433 -0
  360. package/src/routes/memory-slack-publications.ts +3 -2
  361. package/src/routes/model-connection-access.ts +208 -0
  362. package/src/routes/organization-integration-policy.ts +116 -0
  363. package/src/routes/organization-integrations.ts +326 -0
  364. package/src/routes/organization-mcp-connections.ts +178 -0
  365. package/src/routes/organization-memberships.ts +1019 -27
  366. package/src/routes/organization-model-providers.ts +293 -0
  367. package/src/routes/organization-recovery.ts +325 -0
  368. package/src/routes/organization-service-accounts.ts +128 -0
  369. package/src/routes/organization-sessions.ts +350 -0
  370. package/src/routes/personal-github-git-broker.ts +785 -0
  371. package/src/routes/personal-github.ts +370 -0
  372. package/src/routes/plugins.ts +251 -22
  373. package/src/routes/pr-review-github.ts +881 -0
  374. package/src/routes/pr-review.ts +622 -0
  375. package/src/routes/preference-registry.ts +12 -93
  376. package/src/routes/rigs.ts +167 -50
  377. package/src/routes/scheduled-tasks.ts +346 -41
  378. package/src/routes/session-artifact-associations.ts +87 -0
  379. package/src/routes/session-history-imports.ts +280 -0
  380. package/src/routes/sessions.ts +2306 -264
  381. package/src/routes/skill-content.ts +297 -0
  382. package/src/routes/skill-install-authority.ts +64 -0
  383. package/src/routes/skills.ts +62 -9
  384. package/src/routes/social.ts +46 -18
  385. package/src/routes/subscription-account-pools.ts +436 -0
  386. package/src/routes/subscription-pool-access.ts +126 -0
  387. package/src/routes/supergrok.ts +219 -303
  388. package/src/routes/transcription-recordings.ts +38 -3
  389. package/src/routes/transcriptions.ts +4 -0
  390. package/src/routes/usage-allowances.ts +378 -0
  391. package/src/routes/user-resource-authorities.ts +138 -0
  392. package/src/routes/video-generation.ts +14 -10
  393. package/src/routes/workspace-artifacts.ts +275 -69
  394. package/src/routes/workspace-capture.ts +21 -0
  395. package/src/routes/workspace-integrations.ts +576 -0
  396. package/src/routes/workspace-learning.ts +159 -0
  397. package/src/routes/workspace-model-providers.ts +148 -0
  398. package/src/routes/workspace-state.ts +38 -25
  399. package/src/routes/workspaces.ts +1004 -127
  400. package/src/sandbox/auth-callout.ts +96 -19
  401. package/src/sandbox/channel-a.ts +323 -24
  402. package/src/sandbox/connection-authority.ts +3 -0
  403. package/src/sandbox/enrollment.ts +81 -6
  404. package/src/sandbox/machines.ts +218 -76
  405. package/src/sandbox/metrics-ingestion.ts +411 -62
  406. package/src/sandbox/rematerialize.ts +121 -19
  407. package/src/sandbox/session-attach-variable-sets.ts +131 -0
  408. package/src/sandbox/viewer.ts +280 -25
  409. package/src/sandbox-file-artifacts.ts +413 -0
  410. package/src/scheduled-task-deletion.ts +138 -0
  411. package/src/session-create-observability.ts +46 -0
  412. package/src/session-realtime-context.ts +6 -3
  413. package/src/site-session-origin.ts +48 -0
  414. package/src/site-uploads.ts +224 -0
  415. package/src/slack-reaction-files.ts +16 -5
  416. package/src/temporal-schedule-cleanup.ts +13 -0
  417. package/src/temporal-schedule-sync.ts +74 -0
  418. package/src/transcription/providers/codex-subscription.ts +2 -0
  419. package/src/transcription/providers/openai.ts +1 -0
  420. package/src/transcription/providers/xai-subscription.ts +49 -3
  421. package/src/transcription/segmenter.ts +8 -2
  422. package/src/transcription/service.ts +43 -6
  423. package/src/work-discovery-observability.ts +121 -0
  424. package/src/workspace-artifact-content.ts +144 -0
  425. package/src/workspace-artifact-provenance.ts +104 -0
  426. package/src/workspace-delete-observability.ts +75 -0
  427. package/src/workspace-deletion.ts +73 -0
  428. package/src/workspace-state-projection.ts +18 -148
  429. package/src/workspace-tool-gateway-observability.ts +100 -0
  430. package/src/workspace-tool-gateway.ts +870 -0
  431. package/src/workspace-webhook-dispatch.ts +190 -0
  432. package/src/xai-subscription-auth.ts +9 -2
  433. package/dist/chunk-2PQMSRCB.js +0 -49759
  434. package/dist/chunk-2PQMSRCB.js.map +0 -1
  435. package/dist/routes/packs.d.ts +0 -3
  436. package/src/routes/packs.ts +0 -729
@@ -1,8 +1,12 @@
1
+ import { readSessionAttachmentFiles } from "@opengeni/core";
2
+ import { ZodError } from "zod";
1
3
  import {
2
4
  CompleteFileUploadResponse,
3
5
  CreateFileUploadRequest,
4
6
  CreateFileUploadResponse,
5
7
  FileAsset,
8
+ FileListRequest,
9
+ FileListResponse,
6
10
  FileDownloadUrlResponse,
7
11
  RETAINED_OUTPUT_DEFAULT_PAGE_BYTES,
8
12
  RETAINED_OUTPUT_MAX_PAGE_BYTES,
@@ -11,22 +15,27 @@ import {
11
15
  retainedGeneratedImageReferenceFromFile,
12
16
  retainedGeneratedVideoReferenceFromFile,
13
17
  retainedScreenshotReferenceFromFile,
18
+ retainedSessionScreenshotKindFromObjectKey,
14
19
  VideoArtifactPlaybackSource,
15
20
  resolveRetainedOutputRange,
16
21
  type RetainedArtifactMetadata,
17
22
  type RetainedOutputUnavailableReason,
18
23
  } from "@opengeni/contracts";
19
24
  import {
25
+ withSessionRlsActorContext,
20
26
  claimFileUploadCleanup,
21
27
  completeFileUploadCleanup,
22
28
  completeFileUpload,
23
29
  createFileUpload,
24
30
  getFileUpload,
25
31
  getGeneratedImageArtifact,
32
+ recordAuditEvent,
26
33
  getGeneratedVideoArtifact,
34
+ getFilesForSubject,
35
+ listFilesForSubject,
27
36
  getRetainedFileArtifact,
28
37
  getRetainedScreenshotArtifact,
29
- requireFile,
38
+ requireFileForSubject,
30
39
  type RetainedFileArtifact,
31
40
  type GeneratedImageArtifact,
32
41
  type GeneratedVideoArtifact,
@@ -34,24 +43,111 @@ import {
34
43
  } from "@opengeni/db";
35
44
  import type { Context, Hono } from "hono";
36
45
  import { HTTPException } from "hono/http-exception";
37
- import { requireAccessGrant } from "@opengeni/core";
46
+ import {
47
+ fileOwnerContextForAccess,
48
+ requireAccessGrantAuthorization,
49
+ grantHasAgentAttemptAuthority,
50
+ requireAccessGrant,
51
+ requireLiveAgentAttemptAuthorization,
52
+ requireSessionAuthorization,
53
+ SessionAuthorizationDeniedError,
54
+ SessionAuthorizationUnavailableError,
55
+ } from "@opengeni/core";
38
56
  import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
39
57
  import type { ApiRouteDeps } from "@opengeni/core";
58
+ import { retryWhileMissing } from "@opengeni/storage";
40
59
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
41
60
  import { buildFilesMcpServer } from "../mcp/files";
61
+ import { userContentResponseHeaders, userContentSignedGetUrlOptions } from "../http/user-content";
62
+ import { mcpOAuthAuthenticateHeader, resolveMcpOAuthRouteAccess } from "../mcp-oauth";
63
+ import { withAccessGrantSessionRlsContext } from "../access-grant-rls";
64
+ import {
65
+ buildWorkspaceToolGatewayMcpServer,
66
+ prepareMcpOAuthWorkspaceToolGateway,
67
+ } from "../workspace-tool-gateway";
68
+ import { parseRequestJson } from "../http/request-body";
42
69
 
43
70
  export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
44
71
  const { db, objectStorage } = deps;
45
72
 
73
+ const fileRequestAuthority = new WeakMap<Request, { owner: string | null; agent: boolean }>();
74
+ // This scope covers metadata, original bytes, range reads and finalization.
75
+ // A service/agent subject string alone cannot unlock a personal attachment.
76
+ for (const path of [
77
+ "/v1/workspaces/:workspaceId/files",
78
+ "/v1/workspaces/:workspaceId/files/*",
79
+ "/v1/workspaces/:workspaceId/artifacts/*",
80
+ "/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/*",
81
+ ]) {
82
+ app.use(path, async (c, next) => {
83
+ const permission = c.req.path.includes("/files/uploads") ? "files:upload" : "files:read";
84
+ const access = await requireAccessGrantAuthorization(
85
+ c,
86
+ deps,
87
+ c.req.param("workspaceId") ?? "",
88
+ permission,
89
+ );
90
+ const actor = await fileOwnerContextForAccess(deps, access, permission);
91
+ fileRequestAuthority.set(c.req.raw, {
92
+ owner: actor.privateFileOwnerSubjectId ?? null,
93
+ agent: access.grant.principalKind === "agent_attempt",
94
+ });
95
+ return withSessionRlsActorContext(actor, next);
96
+ });
97
+ }
98
+
46
99
  app.all("/v1/workspaces/:workspaceId/mcp/files", async (c) => {
47
100
  const workspaceId = c.req.param("workspaceId");
48
- const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
49
- const transport = new WebStandardStreamableHTTPServerTransport({
50
- enableJsonResponse: true,
101
+ let oauthAccess: Awaited<ReturnType<typeof resolveMcpOAuthRouteAccess>>;
102
+ try {
103
+ oauthAccess = await resolveMcpOAuthRouteAccess(deps, c.req.raw, workspaceId);
104
+ } catch (error) {
105
+ if (error instanceof HTTPException && error.status === 401) {
106
+ c.header("www-authenticate", mcpOAuthAuthenticateHeader(deps, new URL(c.req.url).pathname));
107
+ }
108
+ throw error;
109
+ }
110
+ if (oauthAccess) {
111
+ return await withAccessGrantSessionRlsContext(deps, oauthAccess.grant, async () => {
112
+ const prepared = await prepareMcpOAuthWorkspaceToolGateway(
113
+ deps,
114
+ oauthAccess.grant,
115
+ oauthAccess.allowedToolIdentities,
116
+ );
117
+ const transport = new WebStandardStreamableHTTPServerTransport({
118
+ enableJsonResponse: true,
119
+ });
120
+ const server = buildWorkspaceToolGatewayMcpServer(
121
+ prepared,
122
+ oauthAccess.grant,
123
+ deps.observability,
124
+ );
125
+ try {
126
+ await server.connect(transport);
127
+ return await transport.handleRequest(c.req.raw);
128
+ } finally {
129
+ await Promise.allSettled([server.close(), prepared.close()]);
130
+ }
131
+ });
132
+ }
133
+ let access: Awaited<ReturnType<typeof requireAccessGrantAuthorization>>;
134
+ try {
135
+ access = await requireAccessGrantAuthorization(c, deps, workspaceId, "files:read");
136
+ } catch (error) {
137
+ if (deps.settings.mcpOauthEnabled && error instanceof HTTPException && error.status === 401) {
138
+ c.header("www-authenticate", mcpOAuthAuthenticateHeader(deps, new URL(c.req.url).pathname));
139
+ }
140
+ throw error;
141
+ }
142
+ const { grant } = access;
143
+ return await withAccessGrantSessionRlsContext(deps, grant, async () => {
144
+ const transport = new WebStandardStreamableHTTPServerTransport({
145
+ enableJsonResponse: true,
146
+ });
147
+ const server = buildFilesMcpServer(deps, access);
148
+ await server.connect(transport);
149
+ return await transport.handleRequest(c.req.raw);
51
150
  });
52
- const server = buildFilesMcpServer(deps, grant);
53
- await server.connect(transport);
54
- return await transport.handleRequest(c.req.raw);
55
151
  });
56
152
 
57
153
  app.post("/v1/workspaces/:workspaceId/files/uploads", async (c) => {
@@ -62,7 +158,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
62
158
  message: "object storage is not configured",
63
159
  });
64
160
  }
65
- const payload = CreateFileUploadRequest.parse(await c.req.json());
161
+ const payload = await parseRequestJson(c, CreateFileUploadRequest);
162
+ const privateOwner = fileRequestAuthority.get(c.req.raw)?.owner ?? null;
163
+ const personal =
164
+ payload.scope === "personal" ||
165
+ (fileRequestAuthority.get(c.req.raw)?.agent && privateOwner !== null);
166
+ if (personal && !privateOwner)
167
+ throw new HTTPException(403, { message: "Personal uploads require the authenticated owner" });
66
168
  await requireLimit(deps, {
67
169
  accountId: grant.accountId,
68
170
  workspaceId,
@@ -86,6 +188,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
86
188
  accountId: grant.accountId,
87
189
  workspaceId,
88
190
  fileId,
191
+ privateOwnerSubjectId: personal ? privateOwner : null,
89
192
  filename: payload.filename,
90
193
  safeFilename,
91
194
  contentType: payload.contentType,
@@ -95,6 +198,23 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
95
198
  objectKey,
96
199
  expiresAt: signed.expiresAt,
97
200
  });
201
+ // Every principal-facing signed URL issuance is a
202
+ // metadata-only audit fact. Awaited before the URL leaves the platform:
203
+ // no recorded fact, no bearer capability. Never the URL or object key.
204
+ await recordAuditEvent(db, {
205
+ accountId: grant.accountId,
206
+ workspaceId,
207
+ subjectId: grant.subjectId,
208
+ action: "file.signed_upload.issued",
209
+ targetType: "workspace_file",
210
+ targetId: fileId,
211
+ metadata: {
212
+ fileId,
213
+ contentType: payload.contentType,
214
+ sizeBytes: payload.sizeBytes,
215
+ expiresAt: signed.expiresAt.toISOString(),
216
+ },
217
+ });
98
218
  return c.json(
99
219
  CreateFileUploadResponse.parse({
100
220
  fileId: upload.file.id,
@@ -226,11 +346,13 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
226
346
  const file = await rejectAndCleanObject(409, "file upload has expired", "expired");
227
347
  return c.json(CompleteFileUploadResponse.parse({ file }));
228
348
  }
229
- const head = await objectStorage.headFile(upload.file).catch((error) => {
230
- throw new HTTPException(409, {
231
- message: `uploaded object is not available: ${error instanceof Error ? error.message : String(error)}`,
232
- });
349
+ const head = await retryWhileMissing(async () => {
350
+ if (!(await objectStorage.fileExists(upload.file))) return null;
351
+ return await objectStorage.headFile(upload.file);
233
352
  });
353
+ if (!head) {
354
+ throw new HTTPException(409, { message: "uploaded object is not available" });
355
+ }
234
356
  if (Number(head.ContentLength ?? -1) !== upload.file.sizeBytes) {
235
357
  const file = await rejectAndCleanObject(
236
358
  422,
@@ -263,10 +385,53 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
263
385
  return c.json(CompleteFileUploadResponse.parse({ file }));
264
386
  });
265
387
 
388
+ app.get("/v1/workspaces/:workspaceId/files", async (c) => {
389
+ const workspaceId = c.req.param("workspaceId");
390
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
391
+ const parsed = FileListRequest.safeParse({
392
+ ...c.req.query(),
393
+ ...(c.req.query("limit") !== undefined ? { limit: Number(c.req.query("limit")) } : {}),
394
+ });
395
+ if (!parsed.success) throw new HTTPException(422, { message: "Invalid file list request" });
396
+ c.header("cache-control", "private, no-store");
397
+ return c.json(
398
+ FileListResponse.parse(
399
+ await listFilesForSubject(db, {
400
+ accountId: grant.accountId,
401
+ workspaceId,
402
+ subjectId: grant.subjectId,
403
+ ...parsed.data,
404
+ }).catch((error: unknown) => {
405
+ if (error instanceof ZodError)
406
+ throw new HTTPException(422, { message: "Invalid file cursor" });
407
+ throw error;
408
+ }),
409
+ ),
410
+ );
411
+ });
412
+
413
+ const readRequestedFile = async (
414
+ c: Context,
415
+ grant: Awaited<ReturnType<typeof requireAccessGrant>>,
416
+ ) => {
417
+ const fileId = c.req.param("fileId") ?? "";
418
+ const sessionId = c.req.query("sessionId");
419
+ if (sessionId) {
420
+ if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/iu.test(sessionId)) return null;
421
+ return (await readSessionAttachmentFiles(deps, grant, sessionId, [fileId]))[0] ?? null;
422
+ }
423
+ return requireFileForSubject(db, {
424
+ accountId: grant.accountId,
425
+ workspaceId: grant.workspaceId,
426
+ subjectId: grant.subjectId,
427
+ fileId,
428
+ }).catch(() => null);
429
+ };
430
+
266
431
  app.get("/v1/workspaces/:workspaceId/files/:fileId", async (c) => {
267
432
  const workspaceId = c.req.param("workspaceId");
268
- await requireAccessGrant(c, deps, workspaceId, "files:read");
269
- const file = await requireFile(db, workspaceId, c.req.param("fileId")).catch(() => null);
433
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
434
+ const file = await readRequestedFile(c, grant).catch(() => null);
270
435
  if (!file) {
271
436
  throw new HTTPException(404, { message: "file not found" });
272
437
  }
@@ -275,9 +440,14 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
275
440
 
276
441
  app.get("/v1/workspaces/:workspaceId/artifacts/:artifactId", async (c) => {
277
442
  const workspaceId = c.req.param("workspaceId");
278
- await requireAccessGrant(c, deps, workspaceId, "files:read");
443
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
279
444
  const artifactId = retainedArtifactId(c.req.param("artifactId"));
280
- const artifact = await getWorkspaceArtifact(db, workspaceId, artifactId);
445
+ const artifact = await getWorkspaceArtifact(db, {
446
+ accountId: grant.accountId,
447
+ workspaceId,
448
+ subjectId: await fileAuthoritySubjectIdForGrant(deps, grant),
449
+ artifactId,
450
+ });
281
451
  if (!artifact) {
282
452
  return c.json(retainedArtifactUnavailable(artifactId, "deleted"), 404);
283
453
  }
@@ -286,9 +456,14 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
286
456
 
287
457
  app.get("/v1/workspaces/:workspaceId/artifacts/:artifactId/content", async (c) => {
288
458
  const workspaceId = c.req.param("workspaceId");
289
- await requireAccessGrant(c, deps, workspaceId, "files:read");
459
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
290
460
  const artifactId = retainedArtifactId(c.req.param("artifactId"));
291
- const artifact = await getWorkspaceArtifact(db, workspaceId, artifactId);
461
+ const artifact = await getWorkspaceArtifact(db, {
462
+ accountId: grant.accountId,
463
+ workspaceId,
464
+ subjectId: await fileAuthoritySubjectIdForGrant(deps, grant),
465
+ artifactId,
466
+ });
292
467
  if (!artifact) {
293
468
  return c.json(retainedArtifactUnavailable(artifactId, "deleted"), 404);
294
469
  }
@@ -304,7 +479,7 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
304
479
 
305
480
  app.post("/v1/workspaces/:workspaceId/artifacts/:artifactId/playback-source", async (c) => {
306
481
  const workspaceId = c.req.param("workspaceId");
307
- await requireAccessGrant(c, deps, workspaceId, "files:read");
482
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
308
483
  if (!objectStorage) {
309
484
  throw new HTTPException(503, { message: "object storage is not configured" });
310
485
  }
@@ -317,10 +492,29 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
317
492
  throw new HTTPException(409, { message: `generated video is ${retained.file.status}` });
318
493
  }
319
494
  const file = generatedVideoFileAsset(retained.file);
320
- if (!(await objectStorage.fileExists(file))) {
495
+ const videoPresent = await retryWhileMissing(async () =>
496
+ (await objectStorage.fileExists(file)) ? true : null,
497
+ );
498
+ if (!videoPresent) {
321
499
  throw new HTTPException(410, { message: "generated video bytes are unavailable" });
322
500
  }
323
- const signed = await objectStorage.createGetUrl({ key: file.objectKey });
501
+ const signed = await objectStorage.createGetUrl({
502
+ key: file.objectKey,
503
+ ...userContentSignedGetUrlOptions(file.contentType, file.filename),
504
+ });
505
+ await recordAuditEvent(db, {
506
+ accountId: grant.accountId,
507
+ workspaceId,
508
+ subjectId: grant.subjectId,
509
+ action: "file.signed_url.issued",
510
+ targetType: "retained_artifact",
511
+ targetId: artifactId,
512
+ metadata: {
513
+ artifactId,
514
+ kind: "video_playback",
515
+ expiresAt: signed.expiresAt.toISOString(),
516
+ },
517
+ });
324
518
  return c.json(
325
519
  VideoArtifactPlaybackSource.parse({
326
520
  schemaVersion: 1,
@@ -335,9 +529,40 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
335
529
  );
336
530
  });
337
531
 
532
+ // These two retained-screenshot routes are registered before the session
533
+ // route module, so its `/sessions/:sessionId/*` authorization middleware
534
+ // does not cover them: an agent attempt must pass the same core seam here
535
+ // (private sessions, Slack ownership, and the 0426 agent-access scope) so a
536
+ // known artifact id cannot read into a tree the attempt may not reach.
537
+ const requireAgentSessionAccess = async (
538
+ c: Context,
539
+ grant: Awaited<ReturnType<typeof requireAccessGrant>>,
540
+ ): Promise<void> => {
541
+ if (!grantHasAgentAttemptAuthority(grant)) return;
542
+ try {
543
+ await requireSessionAuthorization(deps, grant, {
544
+ sessionId: c.req.param("sessionId") ?? "",
545
+ operation: "session.read",
546
+ surface: "http",
547
+ });
548
+ } catch (error) {
549
+ if (error instanceof SessionAuthorizationDeniedError) {
550
+ throw new HTTPException(404, { message: "session not found", cause: error });
551
+ }
552
+ if (error instanceof SessionAuthorizationUnavailableError) {
553
+ throw new HTTPException(503, {
554
+ message: "session authorization is unavailable",
555
+ cause: error,
556
+ });
557
+ }
558
+ throw error;
559
+ }
560
+ };
561
+
338
562
  app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId", async (c) => {
339
563
  const workspaceId = c.req.param("workspaceId");
340
- await requireAccessGrant(c, deps, workspaceId, "files:read");
564
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
565
+ await requireAgentSessionAccess(c, grant);
341
566
  const artifactId = retainedArtifactId(c.req.param("artifactId"));
342
567
  const artifact = await getRetainedScreenshotArtifact(
343
568
  db,
@@ -355,7 +580,8 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
355
580
  "/v1/workspaces/:workspaceId/sessions/:sessionId/artifacts/:artifactId/content",
356
581
  async (c) => {
357
582
  const workspaceId = c.req.param("workspaceId");
358
- await requireAccessGrant(c, deps, workspaceId, "files:read");
583
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
584
+ await requireAgentSessionAccess(c, grant);
359
585
  const artifactId = retainedArtifactId(c.req.param("artifactId"));
360
586
  const artifact = await getRetainedScreenshotArtifact(
361
587
  db,
@@ -378,20 +604,42 @@ export function registerFileRoutes(app: Hono, deps: ApiRouteDeps): void {
378
604
 
379
605
  app.post("/v1/workspaces/:workspaceId/files/:fileId/download-url", async (c) => {
380
606
  const workspaceId = c.req.param("workspaceId");
381
- await requireAccessGrant(c, deps, workspaceId, "files:read");
607
+ const grant = await requireAccessGrant(c, deps, workspaceId, "files:read");
382
608
  if (!objectStorage) {
383
609
  throw new HTTPException(503, {
384
610
  message: "object storage is not configured",
385
611
  });
386
612
  }
387
- const file = await requireFile(db, workspaceId, c.req.param("fileId")).catch(() => null);
613
+ const file = await readRequestedFile(c, grant).catch(() => null);
388
614
  if (!file) {
389
615
  throw new HTTPException(404, { message: "file not found" });
390
616
  }
391
617
  if (file.status !== "ready") {
392
618
  throw new HTTPException(409, { message: `file is ${file.status}` });
393
619
  }
394
- const signed = await objectStorage.createGetUrl({ key: file.objectKey });
620
+ const present = await retryWhileMissing(async () =>
621
+ (await objectStorage.fileExists(file)) ? true : null,
622
+ );
623
+ if (!present) {
624
+ throw new HTTPException(410, { message: "file bytes are unavailable" });
625
+ }
626
+ const signed = await objectStorage.createGetUrl({
627
+ key: file.objectKey,
628
+ ...userContentSignedGetUrlOptions(file.contentType, file.filename),
629
+ });
630
+ await recordAuditEvent(db, {
631
+ accountId: grant.accountId,
632
+ workspaceId,
633
+ subjectId: grant.subjectId,
634
+ action: "file.signed_url.issued",
635
+ targetType: "workspace_file",
636
+ targetId: file.id,
637
+ metadata: {
638
+ fileId: file.id,
639
+ kind: "download",
640
+ expiresAt: signed.expiresAt.toISOString(),
641
+ },
642
+ });
395
643
  return c.json(
396
644
  FileDownloadUrlResponse.parse({
397
645
  url: signed.url,
@@ -446,25 +694,33 @@ async function serveRetainedArtifactContent(
446
694
  );
447
695
  }
448
696
 
697
+ // Stored bytes and their content type are user- or agent-chosen; this origin
698
+ // also serves the console, so the response is a sandboxed, non-embeddable
699
+ // document and active markup downloads instead of rendering.
449
700
  const headers = {
450
701
  "Accept-Ranges": range.acceptRanges,
451
702
  "Cache-Control": "private, no-store",
452
703
  "Content-Length": String(range.length),
453
704
  "Content-Type": metadata.contentType,
454
- "X-Content-Type-Options": "nosniff",
705
+ ...userContentResponseHeaders(metadata.contentType, file.filename),
455
706
  ...(range.contentRange ? { "Content-Range": range.contentRange } : {}),
456
707
  };
457
708
  if (range.kind === "empty") {
458
- if (!(await objectStorage.fileExists(file))) {
709
+ const present = await retryWhileMissing(async () =>
710
+ (await objectStorage.fileExists(file)) ? true : null,
711
+ );
712
+ if (!present) {
459
713
  return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
460
714
  }
461
715
  return c.body(null, 200, headers);
462
716
  }
463
717
 
464
- const bytes = await objectStorage.getFileRange(file, {
465
- start: range.start,
466
- end: range.end,
467
- });
718
+ const bytes = await retryWhileMissing(async () =>
719
+ objectStorage.getFileRange(file, {
720
+ start: range.start,
721
+ end: range.end,
722
+ }),
723
+ );
468
724
  if (!bytes) {
469
725
  return c.json(retainedArtifactUnavailable(artifactId, "missing_storage"), 410);
470
726
  }
@@ -537,14 +793,18 @@ function retainedArtifactMetadata(artifact: RetainedFileArtifact): RetainedArtif
537
793
 
538
794
  async function getWorkspaceArtifact(
539
795
  db: ApiRouteDeps["db"],
540
- workspaceId: string,
541
- artifactId: string,
796
+ input: {
797
+ accountId: string;
798
+ workspaceId: string;
799
+ subjectId: string | null;
800
+ artifactId: string;
801
+ },
542
802
  ): Promise<{ file: RetainedFileArtifact["file"]; metadata: RetainedArtifactMetadata } | null> {
543
- const generated = await getGeneratedImageArtifact(db, workspaceId, artifactId);
803
+ const generated = await getGeneratedImageArtifact(db, input.workspaceId, input.artifactId);
544
804
  if (generated) {
545
805
  return { file: generated.file, metadata: retainedGeneratedImageMetadata(generated) };
546
806
  }
547
- const generatedVideo = await getGeneratedVideoArtifact(db, workspaceId, artifactId);
807
+ const generatedVideo = await getGeneratedVideoArtifact(db, input.workspaceId, input.artifactId);
548
808
  if (generatedVideo && !generatedVideo.artifact.deletedAt) {
549
809
  const file = generatedVideoFileAsset(generatedVideo.file);
550
810
  return {
@@ -555,8 +815,43 @@ async function getWorkspaceArtifact(
555
815
  }),
556
816
  };
557
817
  }
558
- const artifact = await getRetainedFileArtifact(db, workspaceId, artifactId);
559
- return artifact ? { file: artifact.file, metadata: retainedArtifactMetadata(artifact) } : null;
818
+ const artifact = await getRetainedFileArtifact(db, input.workspaceId, input.artifactId);
819
+ if (!artifact) return null;
820
+ const [authorizedFile] = await getFilesForSubject(db, {
821
+ accountId: input.accountId,
822
+ workspaceId: input.workspaceId,
823
+ subjectId: input.subjectId,
824
+ fileIds: [artifact.file.id],
825
+ });
826
+ if (!authorizedFile) return null;
827
+ const authorizedArtifact = { ...artifact, file: authorizedFile };
828
+ return {
829
+ file: authorizedFile,
830
+ metadata: retainedArtifactMetadata(authorizedArtifact),
831
+ };
832
+ }
833
+
834
+ /** Human and stable service grants already carry their immutable subject.
835
+ * Agent tokens carry only a technical worker subject, so resolve the exact
836
+ * live attempt and use the initiating human frozen on its durable turn. */
837
+ export async function fileAuthoritySubjectIdForGrant(
838
+ deps: Pick<ApiRouteDeps, "db">,
839
+ grant: import("@opengeni/contracts").AccessGrant,
840
+ ): Promise<string | null> {
841
+ if (grant.principalKind !== "agent_attempt") return grant.subjectId;
842
+ const sessionId = grant.metadata?.["sessionId"];
843
+ if (typeof sessionId !== "string") {
844
+ throw new HTTPException(404, { message: "file not found" });
845
+ }
846
+ try {
847
+ const actor = await requireLiveAgentAttemptAuthorization(deps.db, grant, sessionId);
848
+ return actor.initiatingHumanSubjectId;
849
+ } catch (error) {
850
+ if (error instanceof SessionAuthorizationDeniedError) {
851
+ throw new HTTPException(404, { message: "file not found", cause: error });
852
+ }
853
+ throw error;
854
+ }
560
855
  }
561
856
 
562
857
  function generatedVideoFileAsset(
@@ -605,12 +900,15 @@ function retainedScreenshotMetadata(
605
900
  artifact.sessionId &&
606
901
  artifact.retentionExpiresAt.getTime() > Date.now()
607
902
  ) {
903
+ const kind = retainedSessionScreenshotKindFromObjectKey(artifact.file.objectKey);
904
+ if (!kind) return retainedArtifactUnavailable(artifact.artifactId, "invalid_content");
608
905
  const reference = retainedScreenshotReferenceFromFile({
609
906
  ...artifact.file,
610
907
  sessionId: artifact.sessionId,
611
908
  width: artifact.width,
612
909
  height: artifact.height,
613
910
  expiresAt: artifact.retentionExpiresAt.toISOString(),
911
+ kind,
614
912
  });
615
913
  if (reference) return reference;
616
914
  }