@opengeni/api-router 0.30.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/dist/access-grant-rls.d.ts +3 -0
  2. package/dist/admission-trace.d.ts +8 -0
  3. package/dist/api-websocket.d.ts +18 -0
  4. package/dist/app.d.ts +50 -2
  5. package/dist/app.js +12 -1
  6. package/dist/artifact-catalog-cursor.d.ts +27 -0
  7. package/dist/auth/managed-auth-attempt-context.d.ts +10 -0
  8. package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
  9. package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
  10. package/dist/auth/managed-auth.d.ts +102 -3
  11. package/dist/auth/managed-email.d.ts +29 -0
  12. package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
  13. package/dist/auth/new-signups-gate.d.ts +23 -0
  14. package/dist/auth/organization-user-setup.d.ts +80 -0
  15. package/dist/auth/signup-funnel-metrics.d.ts +44 -0
  16. package/dist/browser-controller-authority.d.ts +1 -61
  17. package/dist/browser-ephemeral.d.ts +5 -0
  18. package/dist/browser-state-authority.d.ts +1 -27
  19. package/dist/chunk-6TZ5LG3O.js +123948 -0
  20. package/dist/chunk-6TZ5LG3O.js.map +1 -0
  21. package/dist/chunk-OGQDXVXY.js +18 -0
  22. package/dist/chunk-OGQDXVXY.js.map +1 -0
  23. package/dist/claude-subscription-account-conflicts.d.ts +1 -0
  24. package/dist/claude-subscription-account-usage.d.ts +39 -0
  25. package/dist/claude-subscription-oauth.d.ts +25 -0
  26. package/dist/claude-subscription-profile.d.ts +9 -0
  27. package/dist/claude-subscription-usage.d.ts +44 -0
  28. package/dist/claude-workspace-connection.d.ts +15 -0
  29. package/dist/codemode.d.ts +32 -1
  30. package/dist/company-brain-okf.d.ts +24 -0
  31. package/dist/connected-machine-computer-access.d.ts +11 -0
  32. package/dist/connection-ownership.d.ts +54 -0
  33. package/dist/controller-data-plane.d.ts +32 -0
  34. package/dist/editable-artifact-source-session.d.ts +3 -0
  35. package/dist/editable-artifact-websocket.d.ts +10 -18
  36. package/dist/editable-artifact-workspace-files.d.ts +1 -1
  37. package/dist/fatal-process-boundary.d.ts +25 -0
  38. package/dist/github-access.d.ts +31 -2
  39. package/dist/http/acting-person.d.ts +24 -0
  40. package/dist/http/api-contract-compat.d.ts +19 -0
  41. package/dist/http/api-error.d.ts +27 -1
  42. package/dist/http/cors.d.ts +17 -0
  43. package/dist/http/deprecation.d.ts +42 -0
  44. package/dist/http/interaction-control-error.d.ts +13 -0
  45. package/dist/http/keyed-admission.d.ts +12 -0
  46. package/dist/http/local-browser-boundary.d.ts +72 -0
  47. package/dist/http/metrics-listener.d.ts +22 -0
  48. package/dist/http/path-identifier.d.ts +17 -0
  49. package/dist/http/registered-route-label.d.ts +16 -0
  50. package/dist/http/request-body.d.ts +27 -0
  51. package/dist/http/request-source.d.ts +52 -0
  52. package/dist/http/sse.d.ts +17 -6
  53. package/dist/http/unmatched-route.d.ts +18 -0
  54. package/dist/http/user-content.d.ts +36 -0
  55. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  56. package/dist/index.d.ts +10 -3
  57. package/dist/index.js +1091 -162
  58. package/dist/index.js.map +1 -1
  59. package/dist/integration-endpoint-test.d.ts +20 -0
  60. package/dist/integrations/api-integrations.d.ts +2 -2
  61. package/dist/integrations/atlassian.d.ts +29 -3
  62. package/dist/integrations/connect-authority.d.ts +1 -0
  63. package/dist/integrations/connect-callback-return.d.ts +6 -0
  64. package/dist/integrations/fiken.d.ts +16 -2
  65. package/dist/integrations/github-app-connect.d.ts +47 -0
  66. package/dist/integrations/github-installation-proof.d.ts +3 -0
  67. package/dist/integrations/github-lens-connect.d.ts +116 -0
  68. package/dist/integrations/github-skill-source.d.ts +1 -5
  69. package/dist/integrations/google-drive.d.ts +24 -0
  70. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  71. package/dist/integrations/marketplace-plugin.d.ts +2691 -0
  72. package/dist/integrations/oauth-client.d.ts +178 -18
  73. package/dist/integrations/oauth-profiles.d.ts +222 -0
  74. package/dist/integrations/oauth-return-path.d.ts +1 -0
  75. package/dist/integrations/personal-github-repositories.d.ts +84 -0
  76. package/dist/integrations/personal-github.d.ts +42 -0
  77. package/dist/integrations/pr-review-provider.d.ts +22 -0
  78. package/dist/integrations/provider-oauth.d.ts +19 -1
  79. package/dist/integrations/slack-app-home.d.ts +24 -0
  80. package/dist/integrations/slack-bot.d.ts +367 -12
  81. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  82. package/dist/integrations/slack-install.d.ts +20 -0
  83. package/dist/integrations/slack-interactions.d.ts +107 -9
  84. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  85. package/dist/integrations/slack-routing.d.ts +120 -0
  86. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  87. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  88. package/dist/integrations/social-oauth.d.ts +7 -0
  89. package/dist/interaction-agent-access.d.ts +15 -0
  90. package/dist/interaction-frame-proxy.d.ts +68 -0
  91. package/dist/interaction-metrics.d.ts +2 -0
  92. package/dist/mcp/action-catalog-types.d.ts +9 -0
  93. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  94. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  95. package/dist/mcp/child-read-evidence.d.ts +9 -0
  96. package/dist/mcp/command-read.d.ts +24 -0
  97. package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
  98. package/dist/mcp/contract-input.d.ts +11 -0
  99. package/dist/mcp/documents.d.ts +7 -5
  100. package/dist/mcp/files.d.ts +2 -2
  101. package/dist/mcp/first-party-tool-permissions.d.ts +650 -0
  102. package/dist/mcp/knowledge-entries.d.ts +5 -0
  103. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  104. package/dist/mcp/receipts.d.ts +9 -0
  105. package/dist/mcp/request-abort.d.ts +19 -0
  106. package/dist/mcp/scheduled-task-input.d.ts +1189 -0
  107. package/dist/mcp/scheduled-task-view.d.ts +22 -4
  108. package/dist/mcp/server.d.ts +255 -3
  109. package/dist/mcp/session-event-view.d.ts +94 -0
  110. package/dist/mcp/session-view.d.ts +66 -2
  111. package/dist/mcp/session-wait.d.ts +174 -0
  112. package/dist/mcp-oauth.d.ts +52 -0
  113. package/dist/model-catalog.d.ts +11 -31
  114. package/dist/observability.d.ts +3 -1
  115. package/dist/organization-mcp.d.ts +52 -0
  116. package/dist/organization-recovery-notifications.d.ts +41 -0
  117. package/dist/routes/analytics-consent.d.ts +26 -0
  118. package/dist/routes/api-integrations.d.ts +25 -0
  119. package/dist/routes/api-keys.d.ts +17 -0
  120. package/dist/routes/artifact-catalog.d.ts +6 -0
  121. package/dist/routes/automations.d.ts +19 -0
  122. package/dist/routes/billing.d.ts +49 -0
  123. package/dist/routes/browser-sessions.d.ts +59 -2
  124. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  125. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  126. package/dist/routes/client-errors.d.ts +146 -0
  127. package/dist/routes/codex.d.ts +76 -2
  128. package/dist/routes/company-brain.d.ts +3 -0
  129. package/dist/routes/company-profile.d.ts +2 -1
  130. package/dist/routes/computer-sessions.d.ts +14 -0
  131. package/dist/routes/connect.d.ts +5 -0
  132. package/dist/routes/editable-artifacts.d.ts +2 -0
  133. package/dist/routes/external-identity-links.d.ts +6 -0
  134. package/dist/routes/feedback.d.ts +3 -0
  135. package/dist/routes/github-browser-pages.d.ts +12 -0
  136. package/dist/routes/github.d.ts +2 -0
  137. package/dist/routes/insights-response-cache.d.ts +10 -0
  138. package/dist/routes/insights-usage.d.ts +12 -0
  139. package/dist/routes/insights.d.ts +30 -1
  140. package/dist/routes/knowledge.d.ts +3 -0
  141. package/dist/routes/managed-auth-session-sets.d.ts +26 -0
  142. package/dist/routes/managed-onboarding.d.ts +29 -0
  143. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  144. package/dist/routes/model-connection-access.d.ts +3 -0
  145. package/dist/routes/organization-integration-policy.d.ts +3 -0
  146. package/dist/routes/organization-integrations.d.ts +6 -0
  147. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  148. package/dist/routes/organization-model-providers.d.ts +3 -0
  149. package/dist/routes/organization-recovery.d.ts +19 -0
  150. package/dist/routes/organization-service-accounts.d.ts +3 -0
  151. package/dist/routes/organization-sessions.d.ts +26 -0
  152. package/dist/routes/personal-github-git-broker.d.ts +29 -0
  153. package/dist/routes/personal-github.d.ts +3 -0
  154. package/dist/routes/pr-review-github.d.ts +3 -0
  155. package/dist/routes/pr-review.d.ts +3 -0
  156. package/dist/routes/session-artifact-associations.d.ts +4 -0
  157. package/dist/routes/session-history-imports.d.ts +9 -0
  158. package/dist/routes/sessions.d.ts +44 -6
  159. package/dist/routes/skill-content.d.ts +4 -0
  160. package/dist/routes/skill-install-authority.d.ts +6 -0
  161. package/dist/routes/subscription-account-pools.d.ts +34 -0
  162. package/dist/routes/subscription-pool-access.d.ts +16 -0
  163. package/dist/routes/supergrok.d.ts +6 -1
  164. package/dist/routes/usage-allowances.d.ts +12 -0
  165. package/dist/routes/user-resource-authorities.d.ts +3 -0
  166. package/dist/routes/workspace-artifacts.d.ts +2 -1
  167. package/dist/routes/workspace-capture.d.ts +10 -2
  168. package/dist/routes/workspace-integrations.d.ts +35 -0
  169. package/dist/routes/workspace-learning.d.ts +3 -0
  170. package/dist/routes/workspace-model-providers.d.ts +4 -0
  171. package/dist/routes/workspaces.d.ts +29 -0
  172. package/dist/sandbox/auth-callout.d.ts +8 -0
  173. package/dist/sandbox/channel-a.d.ts +19 -1
  174. package/dist/sandbox/connection-authority.d.ts +3 -0
  175. package/dist/sandbox/enrollment.d.ts +9 -1
  176. package/dist/sandbox/machines.d.ts +5 -2
  177. package/dist/sandbox/metrics-ingestion.d.ts +28 -20
  178. package/dist/sandbox/rematerialize.d.ts +2 -0
  179. package/dist/sandbox/session-attach-variable-sets.d.ts +30 -0
  180. package/dist/sandbox/viewer.d.ts +26 -1
  181. package/dist/sandbox-file-artifacts.d.ts +50 -0
  182. package/dist/scheduled-task-deletion.d.ts +13 -0
  183. package/dist/session-create-observability.d.ts +5 -0
  184. package/dist/session-event-view-MPS2OZD3.js +292 -0
  185. package/dist/session-event-view-MPS2OZD3.js.map +1 -0
  186. package/dist/site-session-origin.d.ts +13 -0
  187. package/dist/site-uploads.d.ts +41 -0
  188. package/dist/slack-reaction-files.d.ts +1 -1
  189. package/dist/temporal-schedule-cleanup.d.ts +1 -0
  190. package/dist/temporal-schedule-sync.d.ts +26 -0
  191. package/dist/transcription/providers/xai-subscription.d.ts +1 -0
  192. package/dist/transcription/service.d.ts +2 -1
  193. package/dist/work-discovery-observability.d.ts +33 -0
  194. package/dist/workspace-artifact-content.d.ts +28 -0
  195. package/dist/workspace-artifact-provenance.d.ts +7 -0
  196. package/dist/workspace-delete-observability.d.ts +10 -0
  197. package/dist/workspace-deletion.d.ts +6 -0
  198. package/dist/workspace-state-projection.d.ts +3 -5
  199. package/dist/workspace-tool-gateway-observability.d.ts +17 -0
  200. package/dist/workspace-tool-gateway.d.ts +123 -0
  201. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  202. package/package.json +23 -19
  203. package/src/access-grant-rls.ts +75 -0
  204. package/src/admission-trace.ts +14 -0
  205. package/src/api-websocket.ts +23 -0
  206. package/src/app.ts +1967 -252
  207. package/src/artifact-catalog-cursor.ts +60 -0
  208. package/src/auth/managed-auth-attempt-context.ts +70 -0
  209. package/src/auth/managed-auth-rate-limits.ts +252 -0
  210. package/src/auth/managed-auth-session-adapter.ts +222 -0
  211. package/src/auth/managed-auth.ts +536 -60
  212. package/src/auth/managed-email.ts +174 -0
  213. package/src/auth/managed-sign-in-notifications.ts +81 -0
  214. package/src/auth/new-signups-gate.ts +49 -0
  215. package/src/auth/organization-user-setup.ts +307 -0
  216. package/src/auth/signup-funnel-metrics.ts +164 -0
  217. package/src/browser-controller-authority.ts +1 -186
  218. package/src/browser-ephemeral.ts +39 -0
  219. package/src/browser-state-authority.ts +1 -236
  220. package/src/claude-subscription-account-conflicts.ts +12 -0
  221. package/src/claude-subscription-account-usage.ts +48 -0
  222. package/src/claude-subscription-oauth.ts +362 -0
  223. package/src/claude-subscription-profile.ts +73 -0
  224. package/src/claude-subscription-usage.ts +97 -0
  225. package/src/claude-workspace-connection.ts +82 -0
  226. package/src/codemode.ts +194 -30
  227. package/src/codex-realtime.ts +8 -2
  228. package/src/company-brain-okf.ts +340 -0
  229. package/src/connected-machine-computer-access.ts +33 -0
  230. package/src/connection-ownership.ts +199 -0
  231. package/src/controller-data-plane.ts +81 -0
  232. package/src/editable-artifact-native-kernel.ts +26 -15
  233. package/src/editable-artifact-office-import.ts +28 -3
  234. package/src/editable-artifact-production.ts +23 -7
  235. package/src/editable-artifact-source-session.ts +123 -0
  236. package/src/editable-artifact-websocket.ts +27 -21
  237. package/src/editable-artifact-workspace-files.ts +31 -6
  238. package/src/fatal-process-boundary.ts +246 -0
  239. package/src/gateway-realtime.ts +1 -1
  240. package/src/github-access.ts +147 -5
  241. package/src/http/acting-person.ts +71 -0
  242. package/src/http/api-contract-compat.ts +49 -0
  243. package/src/http/api-error.ts +115 -1
  244. package/src/http/auth.ts +48 -0
  245. package/src/http/cors.ts +67 -0
  246. package/src/http/deprecation.ts +118 -0
  247. package/src/http/interaction-control-error.ts +164 -0
  248. package/src/http/keyed-admission.ts +29 -0
  249. package/src/http/local-browser-boundary.ts +343 -0
  250. package/src/http/metrics-listener.ts +57 -0
  251. package/src/http/path-identifier.ts +54 -0
  252. package/src/http/registered-route-label.ts +34 -0
  253. package/src/http/request-body.ts +127 -0
  254. package/src/http/request-source.ts +178 -0
  255. package/src/http/sse.ts +486 -132
  256. package/src/http/unmatched-route.ts +60 -0
  257. package/src/http/user-content.ts +158 -0
  258. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  259. package/src/index.ts +157 -52
  260. package/src/integration-endpoint-test.ts +291 -0
  261. package/src/integrations/api-integrations.ts +98 -11
  262. package/src/integrations/atlassian.ts +474 -136
  263. package/src/integrations/connect-authority.ts +2 -0
  264. package/src/integrations/connect-callback-return.ts +86 -0
  265. package/src/integrations/fiken.ts +354 -64
  266. package/src/integrations/github-app-connect.ts +435 -0
  267. package/src/integrations/github-installation-proof.ts +60 -0
  268. package/src/integrations/github-lens-connect.ts +76 -0
  269. package/src/integrations/github-skill-source.ts +2 -142
  270. package/src/integrations/google-drive.ts +717 -225
  271. package/src/integrations/integration-commit-authority.ts +69 -0
  272. package/src/integrations/marketplace-plugin.ts +48 -0
  273. package/src/integrations/oauth-client.ts +1356 -459
  274. package/src/integrations/oauth-profiles.ts +537 -0
  275. package/src/integrations/oauth-return-path.ts +18 -0
  276. package/src/integrations/personal-github-repositories.ts +674 -0
  277. package/src/integrations/personal-github.ts +825 -0
  278. package/src/integrations/pr-review-provider.ts +246 -0
  279. package/src/integrations/provider-oauth.ts +250 -22
  280. package/src/integrations/slack-app-home.ts +300 -0
  281. package/src/integrations/slack-bot.ts +963 -239
  282. package/src/integrations/slack-file-upload-flow.ts +147 -0
  283. package/src/integrations/slack-install.ts +83 -0
  284. package/src/integrations/slack-interactions.ts +3167 -474
  285. package/src/integrations/slack-mrkdwn.ts +193 -0
  286. package/src/integrations/slack-routing.ts +337 -0
  287. package/src/integrations/slack-session-defaults.ts +224 -0
  288. package/src/integrations/slack-task-file-upload.ts +323 -0
  289. package/src/integrations/social-oauth.ts +213 -15
  290. package/src/interaction-agent-access.ts +48 -0
  291. package/src/interaction-frame-proxy.ts +409 -0
  292. package/src/interaction-metrics.ts +30 -0
  293. package/src/mcp/action-catalog-types.ts +9 -0
  294. package/src/mcp/action-catalog.gen.ts +6696 -0
  295. package/src/mcp/capability-account-readiness.ts +17 -0
  296. package/src/mcp/child-read-evidence.ts +19 -0
  297. package/src/mcp/command-read.ts +109 -0
  298. package/src/mcp/company-profile-agent-admin.ts +198 -0
  299. package/src/mcp/contract-input.ts +141 -0
  300. package/src/mcp/documents.ts +65 -377
  301. package/src/mcp/editable-artifacts.ts +12 -6
  302. package/src/mcp/files.ts +80 -43
  303. package/src/mcp/first-party-tool-permissions.ts +305 -0
  304. package/src/mcp/knowledge-entries.ts +322 -0
  305. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  306. package/src/mcp/receipts.ts +34 -0
  307. package/src/mcp/request-abort.ts +44 -0
  308. package/src/mcp/scheduled-task-input.ts +76 -0
  309. package/src/mcp/scheduled-task-view.ts +44 -3
  310. package/src/mcp/server.ts +3030 -1267
  311. package/src/mcp/session-event-view.ts +383 -0
  312. package/src/mcp/session-view.ts +84 -3
  313. package/src/mcp/session-wait.ts +665 -0
  314. package/src/mcp-oauth.ts +1123 -0
  315. package/src/model-catalog.ts +91 -333
  316. package/src/observability.ts +15 -1
  317. package/src/organization-mcp.ts +397 -0
  318. package/src/organization-recovery-notifications.ts +103 -0
  319. package/src/routes/analytics-consent.ts +123 -0
  320. package/src/routes/api-integrations.ts +364 -74
  321. package/src/routes/api-keys.ts +336 -10
  322. package/src/routes/artifact-catalog.ts +279 -0
  323. package/src/routes/automations.ts +768 -0
  324. package/src/routes/billing.ts +461 -46
  325. package/src/routes/browser-sessions.ts +780 -112
  326. package/src/routes/canonical-human-identities.ts +90 -14
  327. package/src/routes/capabilities.ts +107 -6
  328. package/src/routes/channels.ts +20 -3
  329. package/src/routes/claude-subscription-accounts.ts +334 -0
  330. package/src/routes/claude-subscription-oauth.ts +142 -0
  331. package/src/routes/client-errors.ts +362 -0
  332. package/src/routes/codex.ts +833 -116
  333. package/src/routes/company-brain.ts +322 -0
  334. package/src/routes/company-profile.ts +65 -1
  335. package/src/routes/computer-sessions.ts +557 -106
  336. package/src/routes/connect.ts +1938 -0
  337. package/src/routes/connections.ts +1039 -349
  338. package/src/routes/documents.ts +488 -287
  339. package/src/routes/editable-artifacts.ts +79 -9
  340. package/src/routes/enrollments.ts +135 -39
  341. package/src/routes/environments.ts +154 -58
  342. package/src/routes/external-identity-links.ts +201 -0
  343. package/src/routes/feedback.ts +113 -0
  344. package/src/routes/files.ts +267 -33
  345. package/src/routes/github-browser-pages.ts +162 -0
  346. package/src/routes/github.ts +446 -144
  347. package/src/routes/insights-response-cache.ts +83 -0
  348. package/src/routes/insights-usage.ts +345 -0
  349. package/src/routes/insights.ts +153 -19
  350. package/src/routes/install.ts +133 -38
  351. package/src/routes/integration-facets.ts +107 -33
  352. package/src/routes/interaction-resources.ts +30 -3
  353. package/src/routes/knowledge.ts +488 -0
  354. package/src/routes/machines.ts +313 -15
  355. package/src/routes/managed-auth-session-sets.ts +1218 -0
  356. package/src/routes/managed-onboarding.ts +337 -0
  357. package/src/routes/managed-sign-in-methods.ts +433 -0
  358. package/src/routes/memory-slack-publications.ts +3 -2
  359. package/src/routes/model-connection-access.ts +208 -0
  360. package/src/routes/organization-integration-policy.ts +116 -0
  361. package/src/routes/organization-integrations.ts +326 -0
  362. package/src/routes/organization-mcp-connections.ts +178 -0
  363. package/src/routes/organization-memberships.ts +1019 -27
  364. package/src/routes/organization-model-providers.ts +293 -0
  365. package/src/routes/organization-recovery.ts +325 -0
  366. package/src/routes/organization-service-accounts.ts +128 -0
  367. package/src/routes/organization-sessions.ts +350 -0
  368. package/src/routes/personal-github-git-broker.ts +785 -0
  369. package/src/routes/personal-github.ts +370 -0
  370. package/src/routes/plugins.ts +251 -22
  371. package/src/routes/pr-review-github.ts +881 -0
  372. package/src/routes/pr-review.ts +622 -0
  373. package/src/routes/preference-registry.ts +12 -93
  374. package/src/routes/rigs.ts +167 -50
  375. package/src/routes/scheduled-tasks.ts +346 -41
  376. package/src/routes/session-artifact-associations.ts +87 -0
  377. package/src/routes/session-history-imports.ts +280 -0
  378. package/src/routes/sessions.ts +2306 -264
  379. package/src/routes/skill-content.ts +297 -0
  380. package/src/routes/skill-install-authority.ts +64 -0
  381. package/src/routes/skills.ts +62 -9
  382. package/src/routes/social.ts +46 -18
  383. package/src/routes/subscription-account-pools.ts +436 -0
  384. package/src/routes/subscription-pool-access.ts +126 -0
  385. package/src/routes/supergrok.ts +219 -303
  386. package/src/routes/transcription-recordings.ts +38 -3
  387. package/src/routes/transcriptions.ts +4 -0
  388. package/src/routes/usage-allowances.ts +378 -0
  389. package/src/routes/user-resource-authorities.ts +138 -0
  390. package/src/routes/video-generation.ts +14 -10
  391. package/src/routes/workspace-artifacts.ts +275 -69
  392. package/src/routes/workspace-capture.ts +21 -0
  393. package/src/routes/workspace-integrations.ts +576 -0
  394. package/src/routes/workspace-learning.ts +159 -0
  395. package/src/routes/workspace-model-providers.ts +148 -0
  396. package/src/routes/workspace-state.ts +38 -25
  397. package/src/routes/workspaces.ts +1004 -127
  398. package/src/sandbox/auth-callout.ts +96 -19
  399. package/src/sandbox/channel-a.ts +323 -24
  400. package/src/sandbox/connection-authority.ts +3 -0
  401. package/src/sandbox/enrollment.ts +81 -6
  402. package/src/sandbox/machines.ts +218 -76
  403. package/src/sandbox/metrics-ingestion.ts +411 -62
  404. package/src/sandbox/rematerialize.ts +121 -19
  405. package/src/sandbox/session-attach-variable-sets.ts +131 -0
  406. package/src/sandbox/viewer.ts +280 -25
  407. package/src/sandbox-file-artifacts.ts +413 -0
  408. package/src/scheduled-task-deletion.ts +138 -0
  409. package/src/session-create-observability.ts +46 -0
  410. package/src/session-realtime-context.ts +6 -3
  411. package/src/site-session-origin.ts +48 -0
  412. package/src/site-uploads.ts +224 -0
  413. package/src/slack-reaction-files.ts +16 -5
  414. package/src/temporal-schedule-cleanup.ts +13 -0
  415. package/src/temporal-schedule-sync.ts +74 -0
  416. package/src/transcription/providers/codex-subscription.ts +2 -0
  417. package/src/transcription/providers/openai.ts +1 -0
  418. package/src/transcription/providers/xai-subscription.ts +49 -3
  419. package/src/transcription/segmenter.ts +8 -2
  420. package/src/transcription/service.ts +43 -6
  421. package/src/work-discovery-observability.ts +121 -0
  422. package/src/workspace-artifact-content.ts +144 -0
  423. package/src/workspace-artifact-provenance.ts +104 -0
  424. package/src/workspace-delete-observability.ts +75 -0
  425. package/src/workspace-deletion.ts +73 -0
  426. package/src/workspace-state-projection.ts +18 -148
  427. package/src/workspace-tool-gateway-observability.ts +100 -0
  428. package/src/workspace-tool-gateway.ts +870 -0
  429. package/src/workspace-webhook-dispatch.ts +190 -0
  430. package/src/xai-subscription-auth.ts +9 -2
  431. package/dist/chunk-WCXHI3FF.js +0 -49920
  432. package/dist/chunk-WCXHI3FF.js.map +0 -1
  433. package/dist/routes/packs.d.ts +0 -3
  434. package/src/routes/packs.ts +0 -729
@@ -0,0 +1,297 @@
1
+ import { z } from "zod";
2
+ import type { Context, Hono } from "hono";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import { SkillFile, SKILL_MAX_FILES, type SkillWriteReceipt } from "@opengeni/contracts";
5
+ import { getSessionForSubject, readSkillRemovalScope } from "@opengeni/db";
6
+ import {
7
+ approveSkill,
8
+ rejectSkill,
9
+ removeSkill,
10
+ listSkills,
11
+ readSkill,
12
+ requireAccessGrant,
13
+ requireAccessGrantAuthorization,
14
+ restoreSkill,
15
+ saveSkill,
16
+ validateSkillFiles,
17
+ type ApiRouteDeps,
18
+ } from "@opengeni/core";
19
+ import { applySkillFileChanges, buildPortableSkillArtifact } from "@opengeni/runtime/skill-library";
20
+ import { authorizePreferenceRegistryScopeMutation } from "./preference-registry";
21
+
22
+ const scopeSchema = z.enum(["workspace", "organization", "user"]);
23
+ const saveRequest = z
24
+ .object({
25
+ operationId: z.uuid(),
26
+ skillId: z.uuid(),
27
+ expectedRevisionId: z.uuid().nullable(),
28
+ expectedScopeVersion: z.number().int().nonnegative(),
29
+ scope: scopeSchema.default("workspace"),
30
+ stableKey: z.string().min(1).max(96),
31
+ files: z.array(SkillFile).max(SKILL_MAX_FILES),
32
+ deletions: z.array(z.string().min(1).max(512)).max(SKILL_MAX_FILES).default([]),
33
+ reason: z.string().min(1).max(2000),
34
+ })
35
+ .strict();
36
+ const revisionRequest = z
37
+ .object({
38
+ removalOperationId: z.uuid().optional(),
39
+ operationId: z.uuid(),
40
+ revisionId: z.uuid(),
41
+ expectedRevisionId: z.uuid().nullable(),
42
+ expectedScopeVersion: z.number().int().nonnegative(),
43
+ reason: z.string().min(1).max(2000),
44
+ })
45
+ .strict();
46
+
47
+ const removeRequest = revisionRequest.omit({ revisionId: true, removalOperationId: true });
48
+
49
+ async function parseSkillRequest<S extends z.ZodType>(c: Context, schema: S): Promise<z.infer<S>> {
50
+ const parsed = schema.safeParse(await c.req.json().catch(() => null));
51
+ if (!parsed.success) throw new HTTPException(422, { message: "Invalid Skill request" });
52
+ return parsed.data;
53
+ }
54
+
55
+ function skillIdentifier(value: string): string {
56
+ const parsed = z.uuid().safeParse(value);
57
+ if (!parsed.success)
58
+ throw new HTTPException(422, { message: "Invalid Skill revision or identity" });
59
+ return parsed.data;
60
+ }
61
+
62
+ /** Human folder editing and the legacy Skill admin share scope authorization. */
63
+ export function registerSkillContentRoutes(app: Hono, deps: ApiRouteDeps): void {
64
+ const base = "/v1/workspaces/:workspaceId/skills/content";
65
+ app.get(base, async (c) => {
66
+ const workspaceId = c.req.param("workspaceId");
67
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:read");
68
+ const query = z
69
+ .object({
70
+ limit: z.coerce.number().int().min(1).max(250).default(100),
71
+ cursor: z.string().max(2048).optional(),
72
+ sessionId: z.uuid().optional(),
73
+ })
74
+ .safeParse(c.req.query());
75
+ if (!query.success) throw new HTTPException(422, { message: "Invalid Skill list query" });
76
+ if (query.data.sessionId) {
77
+ await requireAccessGrant(c, deps, workspaceId, "sessions:read");
78
+ const session = await getSessionForSubject(
79
+ deps.db,
80
+ workspaceId,
81
+ query.data.sessionId,
82
+ grant.subjectId,
83
+ );
84
+ if (!session) throw new HTTPException(404, { message: "Session not found" });
85
+ }
86
+ let after: { stableKey: string; id: string } | undefined;
87
+ if (query.data.cursor !== undefined) {
88
+ try {
89
+ after = z
90
+ .object({ stableKey: z.string().min(1).max(128), id: z.uuid() })
91
+ .strict()
92
+ .parse(JSON.parse(Buffer.from(query.data.cursor, "base64url").toString("utf8")));
93
+ } catch {
94
+ throw new HTTPException(422, { message: "Invalid Skill list cursor" });
95
+ }
96
+ }
97
+ const rows = await listSkills(
98
+ deps.db,
99
+ { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
100
+ {
101
+ limit: query.data.limit + 1,
102
+ metadataOnly: true,
103
+ ...(after ? { after } : {}),
104
+ ...(query.data.sessionId ? { sessionId: query.data.sessionId } : {}),
105
+ },
106
+ );
107
+ const page = rows.slice(0, query.data.limit);
108
+ const last = page.at(-1);
109
+ return c.json({
110
+ skills: page.map(({ files: _files, ...summary }) => summary),
111
+ nextCursor:
112
+ rows.length > query.data.limit && last
113
+ ? Buffer.from(JSON.stringify({ stableKey: last.stableKey, id: last.id })).toString(
114
+ "base64url",
115
+ )
116
+ : null,
117
+ });
118
+ });
119
+ app.get(`${base}/:skillId`, async (c) => {
120
+ const workspaceId = c.req.param("workspaceId");
121
+ const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:read");
122
+ const skillId = skillIdentifier(c.req.param("skillId"));
123
+ const revision = c.req.query("revisionId");
124
+ const revisionId = revision === undefined ? undefined : skillIdentifier(revision);
125
+ const skill = await readSkill(
126
+ deps.db,
127
+ { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
128
+ skillId,
129
+ revisionId,
130
+ );
131
+ if (!skill) throw new HTTPException(404, { message: "Skill not found" });
132
+ return c.json(skill);
133
+ });
134
+ app.post(`${base}/save`, async (c) => {
135
+ const workspaceId = c.req.param("workspaceId");
136
+ const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "workspace:read");
137
+ const request = await parseSkillRequest(c, saveRequest);
138
+ authorizePreferenceRegistryScopeMutation(access, request.scope);
139
+ const context = {
140
+ accountId: access.grant.accountId,
141
+ workspaceId,
142
+ subjectId: access.grant.subjectId,
143
+ };
144
+ const skillId = request.skillId;
145
+ const current = await readSkill(
146
+ deps.db,
147
+ context,
148
+ skillId,
149
+ request.expectedRevisionId ?? undefined,
150
+ );
151
+ if (request.expectedRevisionId && !current)
152
+ throw new HTTPException(404, { message: "Skill not found" });
153
+ if (current) {
154
+ authorizePreferenceRegistryScopeMutation(access, current.scope);
155
+ if (current.scope !== request.scope)
156
+ throw new HTTPException(409, {
157
+ message: "Move Skill scope through the existing scope-management workflow.",
158
+ });
159
+ }
160
+ let files;
161
+ try {
162
+ files = validateSkillFiles(
163
+ applySkillFileChanges(
164
+ request.expectedRevisionId ? (current?.files ?? []) : [],
165
+ request.files,
166
+ request.deletions,
167
+ ),
168
+ );
169
+ buildPortableSkillArtifact(files);
170
+ } catch {
171
+ throw new HTTPException(400, {
172
+ message:
173
+ "Invalid Skill folder. SKILL.md requires valid YAML frontmatter with a name and description. Use unique relative text-file paths and respect the file and folder size limits.",
174
+ });
175
+ }
176
+ const receipt = await skillMutation(() =>
177
+ saveSkill(deps.db, {
178
+ accountId: context.accountId,
179
+ workspaceId,
180
+ actor: { kind: "human", principalKind: "human_session", subjectId: context.subjectId },
181
+ operationId: request.operationId,
182
+ skillId,
183
+ expectedRevisionId: request.expectedRevisionId,
184
+ expectedScopeVersion: request.expectedScopeVersion,
185
+ scope: request.scope,
186
+ stableKey: current?.stableKey ?? request.stableKey,
187
+ files,
188
+ reason: request.reason,
189
+ }),
190
+ );
191
+ return c.json(receipt);
192
+ });
193
+ app.post(`${base}/:skillId/remove`, async (c) => {
194
+ const workspaceId = c.req.param("workspaceId");
195
+ const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "workspace:read");
196
+ const skillId = skillIdentifier(c.req.param("skillId"));
197
+ const request = await parseSkillRequest(c, removeRequest);
198
+ const context = {
199
+ accountId: access.grant.accountId,
200
+ workspaceId,
201
+ subjectId: access.grant.subjectId,
202
+ };
203
+ const current = await readSkill(deps.db, context, skillId);
204
+ const scope =
205
+ current?.scope ??
206
+ (await readSkillRemovalScope(deps.db, context, skillId, request.operationId));
207
+ if (!scope) throw new HTTPException(404, { message: "Skill not found" });
208
+ authorizePreferenceRegistryScopeMutation(access, scope);
209
+ if (scope === "organization")
210
+ throw new HTTPException(403, { message: "Organization skills cannot be removed here." });
211
+ return c.json(
212
+ await skillMutation(() =>
213
+ removeSkill(deps.db, {
214
+ ...request,
215
+ skillId,
216
+ accountId: context.accountId,
217
+ workspaceId,
218
+ actor: { kind: "human", principalKind: "human_session", subjectId: context.subjectId },
219
+ }),
220
+ ),
221
+ );
222
+ });
223
+ for (const operation of ["approve", "reject", "restore"] as const) {
224
+ app.post(`${base}/:skillId/${operation}`, async (c) => {
225
+ const workspaceId = c.req.param("workspaceId");
226
+ const access = await requireAccessGrantAuthorization(c, deps, workspaceId, "workspace:read");
227
+ const skillId = skillIdentifier(c.req.param("skillId"));
228
+ const request = await parseSkillRequest(c, revisionRequest);
229
+ const current = await readSkill(
230
+ deps.db,
231
+ { accountId: access.grant.accountId, workspaceId, subjectId: access.grant.subjectId },
232
+ skillId,
233
+ );
234
+ const scope =
235
+ current?.scope ??
236
+ (operation === "approve" && request.removalOperationId
237
+ ? await readSkillRemovalScope(
238
+ deps.db,
239
+ {
240
+ accountId: access.grant.accountId,
241
+ workspaceId,
242
+ subjectId: access.grant.subjectId,
243
+ },
244
+ skillId,
245
+ request.operationId,
246
+ )
247
+ : null);
248
+ if (!scope) throw new HTTPException(404, { message: "Skill not found" });
249
+ authorizePreferenceRegistryScopeMutation(access, scope);
250
+ const apply =
251
+ operation === "approve"
252
+ ? approveSkill
253
+ : operation === "reject"
254
+ ? rejectSkill
255
+ : restoreSkill;
256
+ return c.json(
257
+ await skillMutation(() =>
258
+ apply(deps.db, {
259
+ ...request,
260
+ skillId,
261
+ accountId: access.grant.accountId,
262
+ workspaceId,
263
+ actor: {
264
+ kind: "human",
265
+ principalKind: "human_session",
266
+ subjectId: access.grant.subjectId,
267
+ },
268
+ }),
269
+ ),
270
+ );
271
+ });
272
+ }
273
+ }
274
+
275
+ async function skillMutation(run: () => Promise<SkillWriteReceipt>): Promise<SkillWriteReceipt> {
276
+ try {
277
+ return await run();
278
+ } catch (error) {
279
+ let current: unknown = error;
280
+ for (let depth = 0; depth < 4 && current && typeof current === "object"; depth++) {
281
+ const candidate = current as { code?: unknown; cause?: unknown };
282
+ if (candidate.code === "23505" || candidate.code === "40001" || candidate.code === "55P03")
283
+ throw new HTTPException(409, {
284
+ message:
285
+ "Skill changed or this operation key was reused. Reload and reconcile the change.",
286
+ });
287
+ if (candidate.code === "42501")
288
+ throw new HTTPException(403, {
289
+ message: "Skill change is not permitted for this actor or scope.",
290
+ });
291
+ if (candidate.code === "22023" || candidate.code === "23514")
292
+ throw new HTTPException(400, { message: "Skill content or revision request is invalid." });
293
+ current = candidate.cause;
294
+ }
295
+ throw error;
296
+ }
297
+ }
@@ -0,0 +1,64 @@
1
+ import { SkillActor, type SkillActor as SkillActorType } from "@opengeni/contracts";
2
+ import {
3
+ requireResolvedAccessGrantAuthorization,
4
+ type AccessGrantAuthorization,
5
+ } from "@opengeni/core";
6
+ import { HTTPException } from "hono/http-exception";
7
+
8
+ /** Use authenticated request provenance, never a caller-supplied actor or subject alone. */
9
+ export function skillInstallerActor(access: AccessGrantAuthorization): SkillActorType {
10
+ const grant = requireResolvedAccessGrantAuthorization(access, access.grant.workspaceId);
11
+ const metadata = grant.metadata ?? {};
12
+ const hasAttempt = ["sessionId", "turnId", "attemptId", "executionGeneration"].some(
13
+ (key) => metadata[key] !== undefined,
14
+ );
15
+ if (hasAttempt || grant.principalKind === "agent_attempt") {
16
+ const parsed = SkillActor.safeParse({
17
+ kind: "agent",
18
+ sessionId: metadata.sessionId,
19
+ turnId: metadata.turnId,
20
+ attemptId: metadata.attemptId,
21
+ executionGeneration: metadata.executionGeneration,
22
+ });
23
+ if (!parsed.success)
24
+ throw new HTTPException(403, {
25
+ message: "Skill installation requires complete signed attempt authority.",
26
+ });
27
+ return parsed.data;
28
+ }
29
+ if (
30
+ grant.principalKind === "human_session" &&
31
+ !grant.serviceInitiator &&
32
+ !grant.serviceInitiatorContext &&
33
+ !grant.subjectId.startsWith("api_key:")
34
+ ) {
35
+ return { kind: "human", principalKind: "human_session", subjectId: grant.subjectId };
36
+ }
37
+ if (
38
+ grant.principalKind === "service" ||
39
+ grant.principalKind === "api_key" ||
40
+ grant.principalKind === "configured_key"
41
+ ) {
42
+ return { kind: "service", principalKind: grant.principalKind, subjectId: grant.subjectId };
43
+ }
44
+ throw new HTTPException(403, {
45
+ message:
46
+ "Skill installation requires authenticated human, service, or complete live-attempt authority.",
47
+ });
48
+ }
49
+
50
+ /** Non-Skill component removals remain available to their existing callers. */
51
+ export function skillRemovalActor(access: AccessGrantAuthorization): SkillActorType | undefined {
52
+ const grant = requireResolvedAccessGrantAuthorization(access, access.grant.workspaceId);
53
+ if (
54
+ grant.principalKind !== "human_session" ||
55
+ grant.serviceInitiator ||
56
+ grant.serviceInitiatorContext ||
57
+ grant.subjectId.startsWith("api_key:") ||
58
+ ["sessionId", "turnId", "attemptId", "executionGeneration"].some(
59
+ (key) => grant.metadata?.[key] !== undefined,
60
+ )
61
+ )
62
+ return undefined;
63
+ return skillInstallerActor(access);
64
+ }
@@ -11,8 +11,11 @@ import {
11
11
  } from "@opengeni/contracts";
12
12
  import {
13
13
  portableSkillCapabilityId,
14
+ createPublicSkillSearchClient,
15
+ PublicSkillSearchError,
14
16
  portableSkillPluginKey,
15
17
  requireAccessGrant,
18
+ requireAccessGrantAuthorization,
16
19
  resolveSkillImport,
17
20
  type ApiRouteDeps,
18
21
  type GitHubSkillSourceClient,
@@ -23,7 +26,9 @@ import {
23
26
  listInstalledSkills,
24
27
  PortableSkillInstallationVersionConflictError,
25
28
  PortableSkillInstallationVersionRequiredError,
29
+ PortableSkillSourcePathConflictError,
26
30
  uninstallPortableSkill,
31
+ SkillSourceRemovalAuthorityError,
27
32
  } from "@opengeni/db";
28
33
  import { loadSkillLibrarySkill, skillLibraryRepositoryUrl } from "@opengeni/runtime/skill-library";
29
34
  import { createHash } from "node:crypto";
@@ -31,6 +36,9 @@ import { HTTPException } from "hono/http-exception";
31
36
  import type { Hono } from "hono";
32
37
 
33
38
  import { createGitHubSkillSourceClient } from "../integrations/github-skill-source";
39
+ import { registerSkillContentRoutes } from "./skill-content";
40
+ import { skillInstallerActor, skillRemovalActor } from "./skill-install-authority";
41
+ import { parseRequestJson } from "../http/request-body";
34
42
 
35
43
  export type SkillRouteOverrides = Readonly<{
36
44
  github?: GitHubSkillSourceClient;
@@ -41,7 +49,24 @@ export function registerSkillRoutes(
41
49
  deps: ApiRouteDeps,
42
50
  overrides: SkillRouteOverrides = {},
43
51
  ): void {
52
+ registerSkillContentRoutes(app, deps);
44
53
  const github = overrides.github ?? createGitHubSkillSourceClient(deps.settings);
54
+ const discovery = createPublicSkillSearchClient(deps.settings);
55
+ app.get("/v1/workspaces/:workspaceId/skills/search", async (c) => {
56
+ await requireAccessGrant(c, deps, c.req.param("workspaceId"), "workspace:read");
57
+ try {
58
+ return c.json(await discovery.search({ query: c.req.query("q") ?? "", limit: 20 }));
59
+ } catch (error) {
60
+ if (!(error instanceof PublicSkillSearchError)) throw error;
61
+ if (error.retryAfterSeconds) c.header("Retry-After", String(error.retryAfterSeconds));
62
+ throw new HTTPException(
63
+ error.code === "invalid_query" ? 422 : error.code === "rate_limited" ? 429 : 503,
64
+ {
65
+ message: "Skill search is unavailable. Please try again.",
66
+ },
67
+ );
68
+ }
69
+ });
45
70
 
46
71
  app.get("/v1/workspaces/:workspaceId/skills", async (c) => {
47
72
  const workspaceId = c.req.param("workspaceId");
@@ -55,9 +80,16 @@ export function registerSkillRoutes(
55
80
 
56
81
  app.post("/v1/workspaces/:workspaceId/skills/library/:libraryId/install", async (c) => {
57
82
  const workspaceId = c.req.param("workspaceId");
58
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
83
+ const access = await requireAccessGrantAuthorization(
84
+ c,
85
+ deps,
86
+ workspaceId,
87
+ "capabilities:manage",
88
+ );
89
+ const { grant } = access;
90
+ const skillActor = skillInstallerActor(access);
59
91
  const libraryId = decodeURIComponent(c.req.param("libraryId"));
60
- const payload = InstallLibrarySkillRequest.parse(await c.req.json());
92
+ const payload = await parseRequestJson(c, InstallLibrarySkillRequest);
61
93
  let loaded: ReturnType<typeof loadSkillLibrarySkill>;
62
94
  try {
63
95
  loaded = loadSkillLibrarySkill(libraryId, payload.expectedVersion);
@@ -81,6 +113,7 @@ export function registerSkillRoutes(
81
113
  });
82
114
  try {
83
115
  const installed = await installPortableSkill(deps.db, {
116
+ skillActor,
84
117
  accountId: grant.accountId,
85
118
  workspaceId,
86
119
  subjectId: grant.subjectId,
@@ -118,7 +151,7 @@ export function registerSkillRoutes(
118
151
  app.post("/v1/workspaces/:workspaceId/skills/preview", async (c) => {
119
152
  const workspaceId = c.req.param("workspaceId");
120
153
  await requireAccessGrant(c, deps, workspaceId, "workspace:read");
121
- const payload = PreviewSkillImportRequest.parse(await c.req.json());
154
+ const payload = await parseRequestJson(c, PreviewSkillImportRequest);
122
155
  const resolved = await resolveForRoute(payload.url, github);
123
156
  const installed = await getPortableSkillUninstallPreview(
124
157
  deps.db,
@@ -136,16 +169,22 @@ export function registerSkillRoutes(
136
169
 
137
170
  app.post("/v1/workspaces/:workspaceId/skills/install", async (c) => {
138
171
  const workspaceId = c.req.param("workspaceId");
139
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
140
- const payload = InstallSkillRequest.parse(await c.req.json());
172
+ const access = await requireAccessGrantAuthorization(
173
+ c,
174
+ deps,
175
+ workspaceId,
176
+ "capabilities:manage",
177
+ );
178
+ const { grant } = access;
179
+ const skillActor = skillInstallerActor(access);
180
+ const payload = await parseRequestJson(c, InstallSkillRequest);
141
181
  const resolved = await resolveForRoute(payload.url, github);
142
182
  if (
143
183
  resolved.preview.sourceCommit !== payload.expectedSourceCommit ||
144
184
  resolved.preview.contentSha256 !== payload.expectedContentSha256
145
185
  ) {
146
186
  throw new HTTPException(409, {
147
- message:
148
- "The Skill source changed after preview. Review the new commit and contents before installing.",
187
+ message: "The skill changed after preview. Review its contents again before installing.",
149
188
  });
150
189
  }
151
190
  const fileSummaryByPath = new Map(
@@ -153,6 +192,7 @@ export function registerSkillRoutes(
153
192
  );
154
193
  try {
155
194
  const installed = await installPortableSkill(deps.db, {
195
+ skillActor,
156
196
  accountId: grant.accountId,
157
197
  workspaceId,
158
198
  subjectId: grant.subjectId,
@@ -204,13 +244,21 @@ export function registerSkillRoutes(
204
244
 
205
245
  app.delete("/v1/workspaces/:workspaceId/skills/:capabilityId", async (c) => {
206
246
  const workspaceId = c.req.param("workspaceId");
207
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
247
+ const access = await requireAccessGrantAuthorization(
248
+ c,
249
+ deps,
250
+ workspaceId,
251
+ "capabilities:manage",
252
+ );
253
+ const { grant } = access;
254
+ const skillActor = skillRemovalActor(access);
208
255
  const capabilityId = decodeURIComponent(c.req.param("capabilityId"));
209
- const payload = UninstallSkillRequest.parse(await c.req.json());
256
+ const payload = await parseRequestJson(c, UninstallSkillRequest);
210
257
  try {
211
258
  return c.json(
212
259
  UninstallSkillResult.parse(
213
260
  await uninstallPortableSkill(deps.db, {
261
+ ...(skillActor ? { skillActor } : {}),
214
262
  accountId: grant.accountId,
215
263
  workspaceId,
216
264
  capabilityId,
@@ -224,12 +272,17 @@ export function registerSkillRoutes(
224
272
  message: "The Skill changed after preview. Review uninstall impact again.",
225
273
  });
226
274
  }
275
+ if (error instanceof SkillSourceRemovalAuthorityError)
276
+ throw new HTTPException(403, { message: error.message });
227
277
  throw error;
228
278
  }
229
279
  });
230
280
  }
231
281
 
232
282
  function portableSkillMutationHttpError(error: unknown): Error {
283
+ if (error instanceof PortableSkillSourcePathConflictError) {
284
+ return new HTTPException(409, { message: error.message });
285
+ }
233
286
  if (error instanceof PortableSkillInstallationVersionRequiredError) {
234
287
  return new HTTPException(400, { message: error.message });
235
288
  }
@@ -2,8 +2,10 @@ import {
2
2
  CreateSocialConnectionRequest,
3
3
  CreateSocialPostRequest,
4
4
  OAuthStartResponse,
5
+ OrganizationIntegrationDeniedError,
5
6
  SocialOAuthStartRequest,
6
7
  } from "@opengeni/contracts";
8
+ import { withOrganizationIntegrationAcquisition } from "@opengeni/db/organization-integration-policy";
7
9
  import {
8
10
  createSocialConnection,
9
11
  createSocialPost,
@@ -15,10 +17,16 @@ import {
15
17
  import type { Hono } from "hono";
16
18
  import { HTTPException } from "hono/http-exception";
17
19
  import { z } from "zod";
18
- import { requireAccessGrant } from "@opengeni/core";
20
+ import { requireAccessGrant, requireAccessGrantAuthorization } from "@opengeni/core";
21
+ import {
22
+ assertPersonalConnectionOwnerPrincipal,
23
+ isPersonalConnectionOwnerPrincipal,
24
+ requireLegacyOAuthActor,
25
+ } from "../connection-ownership";
19
26
  import type { ApiRouteDeps } from "@opengeni/core";
20
27
  import { boundedLimit } from "../http/common";
21
28
  import { completeSocialOAuthCallback, startSocialOAuth } from "../integrations/social-oauth";
29
+ import { parseRequestJson } from "../http/request-body";
22
30
 
23
31
  export function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {
24
32
  const { db, settings, observability } = deps;
@@ -39,22 +47,28 @@ export function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {
39
47
  app.post("/v1/workspaces/:workspaceId/social/connections", async (c) => {
40
48
  const workspaceId = c.req.param("workspaceId");
41
49
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
42
- const payload = CreateSocialConnectionRequest.parse(await c.req.json());
50
+ const payload = await parseRequestJson(c, CreateSocialConnectionRequest);
43
51
  try {
44
52
  return c.json(
45
- await createSocialConnection(db, {
46
- accountId: grant.accountId,
47
- workspaceId,
48
- provider: payload.provider,
49
- accountHandle: payload.accountHandle,
50
- accountName: payload.accountName ?? null,
51
- externalAccountId: payload.externalAccountId ?? null,
52
- status: payload.status,
53
- scopes: payload.scopes,
54
- credentialRef: payload.credentialRef ?? null,
55
- tokenMetadata: payload.tokenMetadata,
56
- metadata: payload.metadata,
57
- }),
53
+ await withOrganizationIntegrationAcquisition(
54
+ db,
55
+ { accountId: grant.accountId, workspaceId },
56
+ [payload.provider === "x" || payload.provider === "reddit" ? payload.provider : null],
57
+ (tx) =>
58
+ createSocialConnection(tx, {
59
+ accountId: grant.accountId,
60
+ workspaceId,
61
+ provider: payload.provider,
62
+ accountHandle: payload.accountHandle,
63
+ accountName: payload.accountName ?? null,
64
+ externalAccountId: payload.externalAccountId ?? null,
65
+ status: payload.status,
66
+ scopes: payload.scopes,
67
+ credentialRef: payload.credentialRef ?? null,
68
+ tokenMetadata: payload.tokenMetadata,
69
+ metadata: payload.metadata,
70
+ }),
71
+ ),
58
72
  201,
59
73
  );
60
74
  } catch (error) {
@@ -99,18 +113,27 @@ export function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {
99
113
  });
100
114
  }
101
115
  const payload = parsed.data;
102
- const grant = await requireAccessGrant(
116
+ const access = await requireAccessGrantAuthorization(
103
117
  c,
104
118
  deps,
105
119
  workspaceId,
106
120
  payload.ownership === "workspace" ? "workspace:admin" : "workspace:read",
107
121
  );
122
+ // The contract already defaults this request to workspace ownership; an
123
+ // explicit personal choice still needs a human who can own it.
124
+ requireLegacyOAuthActor(access);
125
+ const personalOwnershipAllowed = isPersonalConnectionOwnerPrincipal(access);
126
+ if (payload.ownership === "personal") {
127
+ assertPersonalConnectionOwnerPrincipal(access);
128
+ }
129
+ const grant = access.grant;
108
130
  const result = await startSocialOAuth(
109
131
  { db, settings, observability },
110
132
  {
111
133
  accountId: grant.accountId,
112
134
  workspaceId,
113
135
  subjectId: grant.subjectId,
136
+ personalOwnershipAllowed,
114
137
  requestUrl: c.req.url,
115
138
  payload,
116
139
  },
@@ -128,7 +151,9 @@ export function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {
128
151
  requestUrl: c.req.url,
129
152
  },
130
153
  );
131
- return c.redirect(result.redirectTo, 302);
154
+ return result.exactReturn
155
+ ? new Response(null, { status: 302, headers: { location: result.redirectTo } })
156
+ : c.redirect(result.redirectTo, 302);
132
157
  });
133
158
 
134
159
  app.get("/v1/workspaces/:workspaceId/social/posts", async (c) => {
@@ -152,7 +177,7 @@ export function registerSocialRoutes(app: Hono, deps: ApiRouteDeps): void {
152
177
  app.post("/v1/workspaces/:workspaceId/social/posts", async (c) => {
153
178
  const workspaceId = c.req.param("workspaceId");
154
179
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
155
- const payload = CreateSocialPostRequest.parse(await c.req.json());
180
+ const payload = await parseRequestJson(c, CreateSocialPostRequest);
156
181
  try {
157
182
  return c.json(
158
183
  await createSocialPost(db, {
@@ -206,6 +231,9 @@ function parseConnectionIds(raw: string | undefined): string[] | undefined {
206
231
  }
207
232
 
208
233
  function socialHttpException(error: unknown): HTTPException {
234
+ if (error instanceof OrganizationIntegrationDeniedError) {
235
+ return new HTTPException(403, { message: error.message });
236
+ }
209
237
  const message = error instanceof Error ? error.message : String(error);
210
238
  if (message.includes("not found")) {
211
239
  return new HTTPException(404, { message });