@opengeni/api-router 3.0.1 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (546) hide show
  1. package/dist/access-grant-rls.d.ts +8 -1
  2. package/dist/admission-trace.d.ts +8 -0
  3. package/dist/app.d.ts +50 -3
  4. package/dist/app.js +10 -1
  5. package/dist/artifact-catalog-cursor.d.ts +1 -0
  6. package/dist/attached-browser-viewer.d.ts +11 -0
  7. package/dist/auth/managed-auth-attempt-context.d.ts +2 -0
  8. package/dist/auth/managed-auth-rate-limits.d.ts +167 -0
  9. package/dist/auth/managed-auth-session-adapter.d.ts +9 -1
  10. package/dist/auth/managed-auth.d.ts +72 -5
  11. package/dist/auth/managed-sign-in-notifications.d.ts +4 -0
  12. package/dist/auth/new-signups-gate.d.ts +23 -0
  13. package/dist/auth/signup-funnel-metrics.d.ts +44 -0
  14. package/dist/azure-live.d.ts +28 -0
  15. package/dist/browser-action-fences.d.ts +13 -0
  16. package/dist/browser-controller-authority.d.ts +1 -61
  17. package/dist/browser-ephemeral.d.ts +5 -0
  18. package/dist/browser-lost-runtime-end.d.ts +24 -0
  19. package/dist/browser-state-authority.d.ts +1 -27
  20. package/dist/chunk-OGQDXVXY.js +18 -0
  21. package/dist/chunk-OGQDXVXY.js.map +1 -0
  22. package/dist/{chunk-AJ62T5QN.js → chunk-OT56OY4V.js} +80503 -39869
  23. package/dist/chunk-OT56OY4V.js.map +1 -0
  24. package/dist/claude-default-models.d.ts +19 -0
  25. package/dist/claude-subscription-core.d.ts +50 -0
  26. package/dist/claude-subscription-oauth.d.ts +36 -0
  27. package/dist/claude-subscription-profile.d.ts +9 -0
  28. package/dist/claude-subscription-usage.d.ts +6 -0
  29. package/dist/claude-workspace-connection.d.ts +15 -0
  30. package/dist/codemode.d.ts +40 -5
  31. package/dist/codex-realtime.d.ts +29 -3
  32. package/dist/codex-session-pointers.d.ts +19 -0
  33. package/dist/connection-ownership.d.ts +6 -3
  34. package/dist/controller-data-plane.d.ts +16 -0
  35. package/dist/credit-promotion-snapshot.d.ts +6 -0
  36. package/dist/editable-artifact-source-session.d.ts +3 -0
  37. package/dist/editable-artifact-websocket.d.ts +5 -3
  38. package/dist/fatal-process-boundary.d.ts +9 -1
  39. package/dist/github-access.d.ts +7 -1
  40. package/dist/github-browser-flow.d.ts +1 -1
  41. package/dist/http/acting-person.d.ts +36 -0
  42. package/dist/http/api-contract-compat.d.ts +19 -0
  43. package/dist/http/api-error.d.ts +50 -1
  44. package/dist/http/auth.d.ts +8 -1
  45. package/dist/http/cors.d.ts +12 -0
  46. package/dist/http/deprecation.d.ts +42 -0
  47. package/dist/http/keyed-admission.d.ts +12 -0
  48. package/dist/http/local-browser-boundary.d.ts +72 -0
  49. package/dist/http/metrics-listener.d.ts +22 -0
  50. package/dist/http/path-identifier.d.ts +17 -0
  51. package/dist/http/registered-route-label.d.ts +16 -0
  52. package/dist/http/rejection-telemetry.d.ts +34 -0
  53. package/dist/http/request-body.d.ts +27 -0
  54. package/dist/http/request-source.d.ts +41 -3
  55. package/dist/http/sse.d.ts +12 -1
  56. package/dist/http/unmatched-route.d.ts +18 -0
  57. package/dist/http/user-content.d.ts +36 -0
  58. package/dist/http/variable-sets-removed.d.ts +41 -0
  59. package/dist/http/websocket-upgrade-dispatch.d.ts +18 -0
  60. package/dist/index.js +1279 -108
  61. package/dist/index.js.map +1 -1
  62. package/dist/integration-connect-metrics.d.ts +35 -0
  63. package/dist/integration-endpoint-test.d.ts +20 -0
  64. package/dist/integrations/api-integrations.d.ts +2 -2
  65. package/dist/integrations/atlassian.d.ts +8 -3
  66. package/dist/integrations/fiken.d.ts +3 -1
  67. package/dist/integrations/github-lens-connect.d.ts +37 -66
  68. package/dist/integrations/google-drive.d.ts +2 -0
  69. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  70. package/dist/integrations/marketplace-plugin.d.ts +43 -43
  71. package/dist/integrations/oauth-client.d.ts +171 -12
  72. package/dist/integrations/oauth-profiles.d.ts +42 -13
  73. package/dist/integrations/onepassword-vault-provider.d.ts +9 -0
  74. package/dist/integrations/slack-bot.d.ts +285 -7
  75. package/dist/integrations/slack-file-upload-flow.d.ts +62 -0
  76. package/dist/integrations/slack-interactions.d.ts +89 -10
  77. package/dist/integrations/slack-mrkdwn.d.ts +41 -0
  78. package/dist/integrations/slack-routing.d.ts +1 -1
  79. package/dist/integrations/slack-session-defaults.d.ts +58 -0
  80. package/dist/integrations/slack-task-file-upload.d.ts +36 -0
  81. package/dist/integrations/slack-tool-review.d.ts +5 -0
  82. package/dist/interaction-agent-access.d.ts +13 -8
  83. package/dist/interaction-agent-version.d.ts +21 -0
  84. package/dist/interaction-holder-heartbeat.d.ts +5 -0
  85. package/dist/interaction-metrics.d.ts +27 -2
  86. package/dist/interaction-route-stages.d.ts +25 -0
  87. package/dist/interaction-secrets-vault.d.ts +20 -0
  88. package/dist/interaction-secrets.d.ts +103 -0
  89. package/dist/mcp/action-catalog-types.d.ts +14 -0
  90. package/dist/mcp/action-catalog.gen.d.ts +2 -0
  91. package/dist/mcp/action-descriptions.d.ts +4 -0
  92. package/dist/mcp/action-schema-imports.gen.d.ts +1 -0
  93. package/dist/mcp/capability-account-readiness.d.ts +7 -0
  94. package/dist/mcp/child-read-evidence.d.ts +9 -0
  95. package/dist/mcp/company-profile-agent-admin.d.ts +12 -0
  96. package/dist/mcp/contract-input.d.ts +13 -0
  97. package/dist/mcp/documents.d.ts +2 -0
  98. package/dist/mcp/first-party-tool-permissions.d.ts +3 -606
  99. package/dist/mcp/notification-tools.d.ts +26 -0
  100. package/dist/mcp/orchestration-failure-diagnostic.d.ts +43 -0
  101. package/dist/mcp/scheduled-task-input.d.ts +1309 -0
  102. package/dist/mcp/scheduled-task-view.d.ts +217 -3
  103. package/dist/mcp/secret-tools.d.ts +14 -0
  104. package/dist/mcp/server.d.ts +108 -5
  105. package/dist/mcp/session-admin-access-tools.d.ts +33 -0
  106. package/dist/mcp/session-event-errors.d.ts +28 -0
  107. package/dist/mcp/session-event-view.d.ts +62 -0
  108. package/dist/mcp/session-view.d.ts +15 -0
  109. package/dist/mcp/session-wait.d.ts +18 -0
  110. package/dist/mcp/site-agent-clamp-tools.d.ts +38 -0
  111. package/dist/mcp/site-data-tools.d.ts +22 -0
  112. package/dist/mcp/site-publication-tools.d.ts +19 -0
  113. package/dist/mcp/site-sql-tools.d.ts +27 -0
  114. package/dist/mcp-oauth.d.ts +37 -4
  115. package/dist/model-calls/chat-completions.d.ts +87 -0
  116. package/dist/model-calls/pending-usage-settlements.d.ts +57 -0
  117. package/dist/model-calls/service.d.ts +36 -0
  118. package/dist/model-catalog.d.ts +9 -3
  119. package/dist/native-push-dispatch.d.ts +69 -0
  120. package/dist/observability.d.ts +3 -1
  121. package/dist/organization-mcp.d.ts +82 -0
  122. package/dist/prepared-mcp-actor.d.ts +77 -0
  123. package/dist/prepared-mcp-permissions.d.ts +8 -0
  124. package/dist/routes/analytics-consent.d.ts +26 -0
  125. package/dist/routes/api-integrations.d.ts +11 -0
  126. package/dist/routes/api-keys.d.ts +7 -7
  127. package/dist/routes/automations.d.ts +2 -0
  128. package/dist/routes/billing.d.ts +59 -0
  129. package/dist/routes/browser-sessions.d.ts +65 -2
  130. package/dist/routes/chat-completions.d.ts +11 -0
  131. package/dist/routes/claude-core-usage.d.ts +20 -0
  132. package/dist/routes/claude-subscription-accounts.d.ts +3 -0
  133. package/dist/routes/claude-subscription-oauth.d.ts +3 -0
  134. package/dist/routes/client-errors.d.ts +182 -0
  135. package/dist/routes/codex-core.d.ts +401 -0
  136. package/dist/routes/codex.d.ts +64 -2
  137. package/dist/routes/computer-sessions.d.ts +6 -1
  138. package/dist/routes/desktop-distribution.d.ts +32 -0
  139. package/dist/routes/github-browser-fonts.gen.d.ts +2 -0
  140. package/dist/routes/github-browser-pages.d.ts +27 -0
  141. package/dist/routes/inbox.d.ts +13 -0
  142. package/dist/routes/insights-response-cache.d.ts +10 -0
  143. package/dist/routes/insights-usage.d.ts +12 -0
  144. package/dist/routes/insights.d.ts +28 -0
  145. package/dist/routes/interaction-placement-message.d.ts +7 -0
  146. package/dist/routes/managed-sign-in-methods.d.ts +6 -0
  147. package/dist/routes/model-account-choice.d.ts +13 -0
  148. package/dist/routes/model-account-settings.d.ts +19 -0
  149. package/dist/routes/native-app-auth.d.ts +12 -0
  150. package/dist/routes/organization-integration-policy.d.ts +8 -0
  151. package/dist/routes/organization-integrations.d.ts +6 -0
  152. package/dist/routes/organization-mcp-connections.d.ts +3 -0
  153. package/dist/routes/organization-model-defaults.d.ts +9 -0
  154. package/dist/routes/organization-service-accounts.d.ts +3 -0
  155. package/dist/routes/personal-api-keys.d.ts +3 -0
  156. package/dist/routes/session-admin-access.d.ts +3 -0
  157. package/dist/routes/session-artifact-associations.d.ts +4 -0
  158. package/dist/routes/session-history-imports.d.ts +20 -0
  159. package/dist/routes/sessions.d.ts +38 -2
  160. package/dist/routes/site-data.d.ts +18 -0
  161. package/dist/routes/site-publishing.d.ts +5 -0
  162. package/dist/routes/site-runtime.d.ts +31 -0
  163. package/dist/routes/site-sql.d.ts +16 -0
  164. package/dist/routes/slack-bot-access.d.ts +3 -0
  165. package/dist/routes/subscription-account-pools.d.ts +21 -0
  166. package/dist/routes/subscription-core-http.d.ts +5 -0
  167. package/dist/routes/subscription-pool-access.d.ts +37 -0
  168. package/dist/routes/subscription-pool-core.d.ts +153 -0
  169. package/dist/routes/supergrok.d.ts +6 -6
  170. package/dist/routes/usage-allowances.d.ts +12 -0
  171. package/dist/routes/variable-sets-removed.d.ts +2 -0
  172. package/dist/routes/vault-providers.d.ts +34 -0
  173. package/dist/routes/vaults.d.ts +3 -0
  174. package/dist/routes/workspace-integrations.d.ts +35 -0
  175. package/dist/routes/workspace-model-providers.d.ts +4 -0
  176. package/dist/routes/workspaces.d.ts +11 -2
  177. package/dist/sandbox/agent-update.d.ts +64 -0
  178. package/dist/sandbox/auth-callout.d.ts +8 -0
  179. package/dist/sandbox/channel-a.d.ts +15 -1
  180. package/dist/sandbox/enrollment.d.ts +26 -4
  181. package/dist/sandbox/machine-audience.d.ts +29 -0
  182. package/dist/sandbox/machines.d.ts +15 -2
  183. package/dist/sandbox/metrics-ingestion.d.ts +6 -2
  184. package/dist/sandbox/screen-control.d.ts +85 -0
  185. package/dist/sandbox/viewer.d.ts +5 -1
  186. package/dist/sandbox-file-artifacts.d.ts +17 -1
  187. package/dist/scheduled-task-blockers.d.ts +39 -0
  188. package/dist/scheduled-task-deletion.d.ts +3 -4
  189. package/dist/scheduled-task-lifecycle.d.ts +14 -0
  190. package/dist/secret-answers.d.ts +40 -0
  191. package/dist/session-create-observability.d.ts +5 -0
  192. package/dist/session-event-view-36GO3IL7.js +528 -0
  193. package/dist/session-event-view-36GO3IL7.js.map +1 -0
  194. package/dist/signed-state-secret.d.ts +18 -0
  195. package/dist/site-agent-guard.d.ts +53 -0
  196. package/dist/site-data/operations.d.ts +103 -0
  197. package/dist/site-data/service.d.ts +203 -0
  198. package/dist/site-frame-guard.d.ts +32 -0
  199. package/dist/site-publishing/agent-data-tools.d.ts +84 -0
  200. package/dist/site-publishing/agent-model.d.ts +58 -0
  201. package/dist/site-publishing/agent-session.d.ts +16 -0
  202. package/dist/site-publishing/content-scanner.d.ts +31 -0
  203. package/dist/site-publishing/edge-sync.d.ts +48 -0
  204. package/dist/site-publishing/gateway-signature.d.ts +56 -0
  205. package/dist/site-publishing/limits.d.ts +63 -0
  206. package/dist/site-publishing/page-tool-authority.d.ts +30 -0
  207. package/dist/site-publishing/platform-usage.d.ts +113 -0
  208. package/dist/site-publishing/platform.d.ts +21 -0
  209. package/dist/site-publishing/readiness.d.ts +62 -0
  210. package/dist/site-publishing/runtime.d.ts +179 -0
  211. package/dist/site-publishing/service.d.ts +264 -0
  212. package/dist/site-publishing/session-events.d.ts +22 -0
  213. package/dist/site-publishing/session-views.d.ts +36 -0
  214. package/dist/site-publishing/viewer.d.ts +40 -0
  215. package/dist/site-session-origin.d.ts +31 -6
  216. package/dist/site-sql/format.d.ts +47 -0
  217. package/dist/site-sql/gateway.d.ts +50 -0
  218. package/dist/site-sql/service.d.ts +213 -0
  219. package/dist/temporal-schedule-sync.d.ts +26 -0
  220. package/dist/transcription/billing-refusal.d.ts +8 -0
  221. package/dist/transcription/normalize.d.ts +23 -0
  222. package/dist/transcription/providers/azure-mai.d.ts +12 -0
  223. package/dist/transcription/providers/azure-openai.d.ts +4 -0
  224. package/dist/transcription/providers/openai.d.ts +16 -0
  225. package/dist/transcription/providers/subscription-core.d.ts +39 -0
  226. package/dist/transcription/service.d.ts +10 -1
  227. package/dist/transcription/wav-duration.d.ts +8 -0
  228. package/dist/workspace-tool-gateway-attestations.d.ts +37 -0
  229. package/dist/workspace-tool-gateway-observability.d.ts +16 -0
  230. package/dist/workspace-tool-gateway-secrets.d.ts +27 -0
  231. package/dist/workspace-tool-gateway.d.ts +102 -6
  232. package/dist/workspace-tool-target-fence.d.ts +116 -0
  233. package/dist/workspace-tool-target.d.ts +225 -0
  234. package/dist/workspace-webhook-dispatch.d.ts +26 -0
  235. package/dist/xai-subscription-core.d.ts +60 -0
  236. package/package.json +25 -20
  237. package/src/access-grant-rls.ts +64 -14
  238. package/src/admission-trace.ts +14 -0
  239. package/src/app.ts +1684 -252
  240. package/src/artifact-catalog-cursor.ts +1 -0
  241. package/src/attached-browser-viewer.ts +26 -0
  242. package/src/auth/managed-auth-attempt-context.ts +9 -0
  243. package/src/auth/managed-auth-rate-limits.ts +252 -0
  244. package/src/auth/managed-auth-session-adapter.ts +64 -29
  245. package/src/auth/managed-auth.ts +303 -35
  246. package/src/auth/managed-email.ts +18 -1
  247. package/src/auth/managed-sign-in-notifications.ts +81 -0
  248. package/src/auth/new-signups-gate.ts +49 -0
  249. package/src/auth/organization-user-setup.ts +3 -3
  250. package/src/auth/signup-funnel-metrics.ts +164 -0
  251. package/src/azure-live.ts +103 -0
  252. package/src/browser-action-fences.ts +45 -0
  253. package/src/browser-controller-authority.ts +1 -186
  254. package/src/browser-ephemeral.ts +39 -0
  255. package/src/browser-lost-runtime-end.ts +71 -0
  256. package/src/browser-state-authority.ts +1 -236
  257. package/src/claude-default-models.ts +37 -0
  258. package/src/claude-subscription-core.ts +154 -0
  259. package/src/claude-subscription-oauth.ts +352 -0
  260. package/src/claude-subscription-profile.ts +73 -0
  261. package/src/claude-subscription-usage.ts +51 -0
  262. package/src/claude-workspace-connection.ts +82 -0
  263. package/src/codemode.ts +236 -39
  264. package/src/codex-realtime.ts +181 -53
  265. package/src/codex-session-pointers.ts +80 -0
  266. package/src/company-brain-okf.ts +1 -1
  267. package/src/connected-machine-computer-access.ts +2 -1
  268. package/src/connection-ownership.ts +19 -13
  269. package/src/controller-data-plane.ts +34 -0
  270. package/src/credit-promotion-snapshot.ts +36 -0
  271. package/src/editable-artifact-production.ts +10 -0
  272. package/src/editable-artifact-source-session.ts +123 -0
  273. package/src/editable-artifact-websocket.ts +16 -3
  274. package/src/fatal-process-boundary.ts +67 -3
  275. package/src/gateway-realtime.ts +12 -8
  276. package/src/github-access.ts +31 -5
  277. package/src/github-browser-flow.ts +1 -1
  278. package/src/http/acting-person.ts +105 -0
  279. package/src/http/api-contract-compat.ts +49 -0
  280. package/src/http/api-error.ts +210 -2
  281. package/src/http/auth.ts +68 -4
  282. package/src/http/cors.ts +37 -5
  283. package/src/http/deprecation.ts +118 -0
  284. package/src/http/keyed-admission.ts +29 -0
  285. package/src/http/local-browser-boundary.ts +343 -0
  286. package/src/http/metrics-listener.ts +57 -0
  287. package/src/http/path-identifier.ts +54 -0
  288. package/src/http/registered-route-label.ts +34 -0
  289. package/src/http/rejection-telemetry.ts +120 -0
  290. package/src/http/request-body.ts +127 -0
  291. package/src/http/request-source.ts +154 -13
  292. package/src/http/sse.ts +88 -28
  293. package/src/http/unmatched-route.ts +60 -0
  294. package/src/http/user-content.ts +158 -0
  295. package/src/http/variable-sets-removed.ts +122 -0
  296. package/src/http/websocket-upgrade-dispatch.ts +29 -0
  297. package/src/index.ts +107 -57
  298. package/src/integration-connect-metrics.ts +228 -0
  299. package/src/integration-endpoint-test.ts +291 -0
  300. package/src/integrations/api-integrations.ts +81 -7
  301. package/src/integrations/atlassian.ts +202 -83
  302. package/src/integrations/fiken.ts +66 -38
  303. package/src/integrations/github-app-connect.ts +64 -18
  304. package/src/integrations/github-lens-connect.ts +8 -29
  305. package/src/integrations/google-drive.ts +448 -152
  306. package/src/integrations/integration-commit-authority.ts +69 -0
  307. package/src/integrations/marketplace-plugin.ts +1 -1
  308. package/src/integrations/oauth-client.ts +733 -193
  309. package/src/integrations/oauth-profiles.ts +116 -45
  310. package/src/integrations/onepassword-vault-provider.ts +16 -0
  311. package/src/integrations/personal-github-repositories.ts +1 -1
  312. package/src/integrations/personal-github.ts +61 -35
  313. package/src/integrations/provider-oauth.ts +67 -85
  314. package/src/integrations/slack-app-home.ts +5 -5
  315. package/src/integrations/slack-bot.ts +893 -201
  316. package/src/integrations/slack-file-upload-flow.ts +147 -0
  317. package/src/integrations/slack-install.ts +7 -7
  318. package/src/integrations/slack-interactions.ts +1589 -299
  319. package/src/integrations/slack-mrkdwn.ts +193 -0
  320. package/src/integrations/slack-routing.ts +2 -2
  321. package/src/integrations/slack-session-defaults.ts +224 -0
  322. package/src/integrations/slack-task-file-upload.ts +323 -0
  323. package/src/integrations/slack-tool-review.ts +65 -0
  324. package/src/integrations/social-oauth.ts +90 -58
  325. package/src/interaction-agent-access.ts +40 -20
  326. package/src/interaction-agent-version.ts +85 -0
  327. package/src/interaction-holder-heartbeat.ts +24 -8
  328. package/src/interaction-metrics.ts +164 -2
  329. package/src/interaction-route-stages.ts +94 -0
  330. package/src/interaction-secrets-vault.ts +213 -0
  331. package/src/interaction-secrets.ts +426 -0
  332. package/src/mcp/action-catalog-types.ts +14 -0
  333. package/src/mcp/action-catalog.gen.ts +8014 -0
  334. package/src/mcp/action-descriptions.ts +299 -0
  335. package/src/mcp/action-schema-imports.gen.ts +304 -0
  336. package/src/mcp/capability-account-readiness.ts +17 -0
  337. package/src/mcp/child-read-evidence.ts +19 -0
  338. package/src/mcp/company-profile-agent-admin.ts +13 -0
  339. package/src/mcp/contract-input.ts +165 -0
  340. package/src/mcp/documents.ts +9 -2
  341. package/src/mcp/editable-artifacts.ts +29 -11
  342. package/src/mcp/files.ts +5 -1
  343. package/src/mcp/first-party-tool-permissions.ts +7 -285
  344. package/src/mcp/knowledge-entries.ts +108 -34
  345. package/src/mcp/notification-tools.ts +480 -0
  346. package/src/mcp/orchestration-failure-diagnostic.ts +43 -0
  347. package/src/mcp/scheduled-task-input.ts +89 -0
  348. package/src/mcp/scheduled-task-view.ts +63 -3
  349. package/src/mcp/secret-tools.ts +185 -0
  350. package/src/mcp/server.ts +2213 -1116
  351. package/src/mcp/session-admin-access-tools.ts +163 -0
  352. package/src/mcp/session-event-errors.ts +123 -0
  353. package/src/mcp/session-event-view.ts +395 -13
  354. package/src/mcp/session-view.ts +3 -0
  355. package/src/mcp/session-wait.ts +67 -7
  356. package/src/mcp/site-agent-clamp-tools.ts +140 -0
  357. package/src/mcp/site-data-tools.ts +339 -0
  358. package/src/mcp/site-publication-tools.ts +201 -0
  359. package/src/mcp/site-sql-tools.ts +427 -0
  360. package/src/mcp-oauth.ts +635 -45
  361. package/src/model-calls/chat-completions.ts +352 -0
  362. package/src/model-calls/pending-usage-settlements.ts +125 -0
  363. package/src/model-calls/service.ts +694 -0
  364. package/src/model-catalog.ts +68 -17
  365. package/src/native-push-dispatch.ts +457 -0
  366. package/src/observability.ts +15 -1
  367. package/src/organization-mcp.ts +485 -0
  368. package/src/prepared-mcp-actor.ts +154 -0
  369. package/src/prepared-mcp-permissions.ts +38 -0
  370. package/src/routes/analytics-consent.ts +123 -0
  371. package/src/routes/api-integrations.ts +268 -9
  372. package/src/routes/api-keys.ts +164 -25
  373. package/src/routes/artifact-catalog.ts +111 -0
  374. package/src/routes/automations.ts +163 -90
  375. package/src/routes/billing.ts +535 -59
  376. package/src/routes/browser-sessions.ts +966 -342
  377. package/src/routes/canonical-human-identities.ts +61 -0
  378. package/src/routes/capabilities.ts +112 -5
  379. package/src/routes/channels.ts +4 -3
  380. package/src/routes/chat-completions.ts +235 -0
  381. package/src/routes/claude-core-usage.ts +147 -0
  382. package/src/routes/claude-subscription-accounts.ts +233 -0
  383. package/src/routes/claude-subscription-oauth.ts +175 -0
  384. package/src/routes/client-errors.ts +394 -0
  385. package/src/routes/codex-core.ts +687 -0
  386. package/src/routes/codex.ts +1277 -1119
  387. package/src/routes/company-brain.ts +4 -0
  388. package/src/routes/computer-sessions.ts +319 -85
  389. package/src/routes/connect.ts +277 -175
  390. package/src/routes/connections.ts +690 -363
  391. package/src/routes/desktop-distribution.ts +217 -0
  392. package/src/routes/documents.ts +13 -7
  393. package/src/routes/editable-artifacts.ts +74 -4
  394. package/src/routes/enrollments.ts +110 -16
  395. package/src/routes/external-identity-links.ts +13 -12
  396. package/src/routes/files.ts +41 -16
  397. package/src/routes/github-browser-fonts.gen.ts +12 -0
  398. package/src/routes/github-browser-pages.ts +258 -0
  399. package/src/routes/github.ts +200 -75
  400. package/src/routes/inbox.ts +353 -0
  401. package/src/routes/insights-response-cache.ts +83 -0
  402. package/src/routes/insights-usage.ts +345 -0
  403. package/src/routes/insights.ts +103 -8
  404. package/src/routes/install.ts +51 -10
  405. package/src/routes/integration-facets.ts +70 -28
  406. package/src/routes/interaction-placement-message.ts +12 -0
  407. package/src/routes/knowledge.ts +136 -27
  408. package/src/routes/machines.ts +189 -135
  409. package/src/routes/managed-auth-session-sets.ts +43 -10
  410. package/src/routes/managed-onboarding.ts +39 -19
  411. package/src/routes/managed-sign-in-methods.ts +433 -0
  412. package/src/routes/memory-slack-publications.ts +3 -2
  413. package/src/routes/model-account-choice.ts +194 -0
  414. package/src/routes/model-account-settings.ts +235 -0
  415. package/src/routes/model-connection-access.ts +210 -48
  416. package/src/routes/native-app-auth.ts +296 -0
  417. package/src/routes/organization-integration-policy.ts +116 -0
  418. package/src/routes/organization-integrations.ts +326 -0
  419. package/src/routes/organization-mcp-connections.ts +183 -0
  420. package/src/routes/organization-memberships.ts +205 -19
  421. package/src/routes/organization-model-defaults.ts +110 -0
  422. package/src/routes/organization-model-providers.ts +74 -3
  423. package/src/routes/organization-service-accounts.ts +128 -0
  424. package/src/routes/organization-sessions.ts +21 -5
  425. package/src/routes/personal-api-keys.ts +184 -0
  426. package/src/routes/personal-github-git-broker.ts +2 -2
  427. package/src/routes/personal-github.ts +9 -4
  428. package/src/routes/plugins.ts +46 -7
  429. package/src/routes/pr-review-github.ts +105 -118
  430. package/src/routes/pr-review.ts +88 -82
  431. package/src/routes/preference-registry.ts +18 -0
  432. package/src/routes/rigs.ts +48 -13
  433. package/src/routes/scheduled-tasks.ts +347 -27
  434. package/src/routes/session-admin-access.ts +271 -0
  435. package/src/routes/session-artifact-associations.ts +164 -0
  436. package/src/routes/session-history-imports.ts +305 -0
  437. package/src/routes/sessions.ts +1682 -363
  438. package/src/routes/site-data.ts +340 -0
  439. package/src/routes/site-publishing.ts +584 -0
  440. package/src/routes/site-runtime.ts +1944 -0
  441. package/src/routes/site-sql.ts +263 -0
  442. package/src/routes/skill-content.ts +72 -7
  443. package/src/routes/skills.ts +5 -4
  444. package/src/routes/slack-bot-access.ts +108 -0
  445. package/src/routes/social.ts +27 -15
  446. package/src/routes/subscription-account-pools.ts +209 -0
  447. package/src/routes/subscription-core-http.ts +18 -0
  448. package/src/routes/subscription-pool-access.ts +180 -0
  449. package/src/routes/subscription-pool-core.ts +552 -0
  450. package/src/routes/supergrok.ts +174 -655
  451. package/src/routes/transcription-recordings.ts +71 -4
  452. package/src/routes/transcriptions.ts +26 -2
  453. package/src/routes/usage-allowances.ts +381 -0
  454. package/src/routes/variable-sets-removed.ts +19 -0
  455. package/src/routes/vault-providers.ts +782 -0
  456. package/src/routes/vaults.ts +421 -0
  457. package/src/routes/video-generation.ts +30 -12
  458. package/src/routes/workspace-artifacts.ts +165 -53
  459. package/src/routes/workspace-integrations.ts +576 -0
  460. package/src/routes/workspace-model-providers.ts +148 -0
  461. package/src/routes/workspace-state.ts +4 -0
  462. package/src/routes/workspaces.ts +625 -117
  463. package/src/sandbox/agent-update.ts +315 -0
  464. package/src/sandbox/auth-callout.ts +104 -21
  465. package/src/sandbox/channel-a.ts +165 -55
  466. package/src/sandbox/enrollment.ts +131 -22
  467. package/src/sandbox/machine-audience.ts +192 -0
  468. package/src/sandbox/machines.ts +75 -31
  469. package/src/sandbox/metrics-ingestion.ts +133 -2
  470. package/src/sandbox/rematerialize.ts +51 -21
  471. package/src/sandbox/screen-control.ts +289 -0
  472. package/src/sandbox/viewer.ts +33 -58
  473. package/src/sandbox-file-artifacts.ts +53 -27
  474. package/src/scheduled-task-blockers.ts +293 -0
  475. package/src/scheduled-task-deletion.ts +13 -3
  476. package/src/scheduled-task-lifecycle.ts +84 -0
  477. package/src/secret-answers.ts +119 -0
  478. package/src/session-create-observability.ts +46 -0
  479. package/src/session-realtime-context.ts +26 -4
  480. package/src/signed-state-secret.ts +58 -0
  481. package/src/site-agent-guard.ts +261 -0
  482. package/src/site-data/operations.ts +777 -0
  483. package/src/site-data/service.ts +1043 -0
  484. package/src/site-frame-guard.ts +107 -0
  485. package/src/site-publishing/agent-data-tools.ts +250 -0
  486. package/src/site-publishing/agent-model.ts +127 -0
  487. package/src/site-publishing/agent-session.ts +148 -0
  488. package/src/site-publishing/content-scanner.ts +93 -0
  489. package/src/site-publishing/edge-sync.ts +148 -0
  490. package/src/site-publishing/gateway-signature.ts +198 -0
  491. package/src/site-publishing/limits.ts +315 -0
  492. package/src/site-publishing/page-tool-authority.ts +111 -0
  493. package/src/site-publishing/platform-usage.ts +479 -0
  494. package/src/site-publishing/platform.ts +98 -0
  495. package/src/site-publishing/readiness.ts +330 -0
  496. package/src/site-publishing/runtime.ts +831 -0
  497. package/src/site-publishing/service.ts +1282 -0
  498. package/src/site-publishing/session-events.ts +262 -0
  499. package/src/site-publishing/session-views.ts +262 -0
  500. package/src/site-publishing/viewer.ts +75 -0
  501. package/src/site-session-origin.ts +91 -6
  502. package/src/site-sql/format.ts +134 -0
  503. package/src/site-sql/gateway.ts +168 -0
  504. package/src/site-sql/service.ts +1636 -0
  505. package/src/site-uploads.ts +27 -5
  506. package/src/temporal-schedule-sync.ts +74 -0
  507. package/src/transcription/billing-refusal.ts +14 -0
  508. package/src/transcription/normalize.ts +77 -0
  509. package/src/transcription/providers/azure-mai.ts +98 -0
  510. package/src/transcription/providers/azure-openai.ts +16 -7
  511. package/src/transcription/providers/codex-subscription.ts +190 -67
  512. package/src/transcription/providers/openai.ts +79 -5
  513. package/src/transcription/providers/subscription-core.ts +125 -0
  514. package/src/transcription/providers/xai-subscription.ts +81 -83
  515. package/src/transcription/segmenter.ts +7 -1
  516. package/src/transcription/service.ts +239 -10
  517. package/src/transcription/wav-duration.ts +39 -0
  518. package/src/workspace-deletion.ts +25 -1
  519. package/src/workspace-tool-gateway-attestations.ts +133 -0
  520. package/src/workspace-tool-gateway-observability.ts +64 -0
  521. package/src/workspace-tool-gateway-secrets.ts +104 -0
  522. package/src/workspace-tool-gateway.ts +921 -103
  523. package/src/workspace-tool-target-fence.ts +380 -0
  524. package/src/workspace-tool-target.ts +554 -0
  525. package/src/workspace-webhook-dispatch.ts +225 -0
  526. package/src/xai-realtime.ts +85 -81
  527. package/src/xai-subscription-core.ts +172 -0
  528. package/dist/browser-auth-broker.d.ts +0 -20
  529. package/dist/chunk-AJ62T5QN.js.map +0 -1
  530. package/dist/connection-authority-owner.d.ts +0 -59
  531. package/dist/routes/connection-authorities.d.ts +0 -3
  532. package/dist/routes/environments.d.ts +0 -5
  533. package/dist/routes/host-mcp-bindings.d.ts +0 -5
  534. package/dist/routes/host-mcp-resolvers.d.ts +0 -3
  535. package/dist/routes/packs.d.ts +0 -3
  536. package/dist/session-event-view-MU3HMNV3.js +0 -264
  537. package/dist/session-event-view-MU3HMNV3.js.map +0 -1
  538. package/dist/xai-subscription-auth.d.ts +0 -23
  539. package/src/browser-auth-broker.ts +0 -155
  540. package/src/connection-authority-owner.ts +0 -61
  541. package/src/routes/connection-authorities.ts +0 -139
  542. package/src/routes/environments.ts +0 -388
  543. package/src/routes/host-mcp-bindings.ts +0 -122
  544. package/src/routes/host-mcp-resolvers.ts +0 -41
  545. package/src/routes/packs.ts +0 -784
  546. package/src/xai-subscription-auth.ts +0 -139
@@ -28,7 +28,7 @@
28
28
  // FLAG GATE: the whole feature is behind sandboxSelfhostedEnabled (default OFF) —
29
29
  // when off the routes 404 (the surface is invisible) and boot is unaffected.
30
30
 
31
- import { randomBytes } from "node:crypto";
31
+ import { createPublicKey, randomBytes, verify } from "node:crypto";
32
32
  import {
33
33
  resolveEnrollmentSigningSecret,
34
34
  resolveRelayTokenSecret,
@@ -36,10 +36,13 @@ import {
36
36
  } from "@opengeni/config";
37
37
  import {
38
38
  DeviceEnrollmentState,
39
+ enrollmentRenewalProof,
39
40
  signEnrollmentBearer,
40
41
  signEnrollToken,
41
42
  signRelayToken,
42
43
  verifyEnrollToken,
44
+ verifyEnrollmentBearer,
45
+ type RenewEnrollmentRequest,
43
46
  type DeviceEnrollmentLookupResponse,
44
47
  type DeviceEnrollmentPollResponse,
45
48
  type DeviceEnrollmentStartResponse,
@@ -52,11 +55,14 @@ import {
52
55
  consumeDeviceEnrollmentRequest,
53
56
  createDeviceEnrollmentRequest,
54
57
  denyDeviceEnrollmentRequest,
58
+ EnrollTokenAlreadyRedeemedError,
59
+ EnrollTokenOwnerUnavailableError,
55
60
  finalizeEnrollmentByToken,
56
61
  getDeviceEnrollmentRequestByDeviceCode,
57
62
  getEnrollment,
58
63
  getPendingDeviceEnrollmentRequestByUserCode,
59
64
  getPendingDeviceEnrollmentRequestByUserCodeGlobal,
65
+ PersonalMachineConnectedElsewhereError,
60
66
  type Database,
61
67
  type DeviceEnrollmentRequestRecord,
62
68
  type EnrollmentOs,
@@ -71,7 +77,8 @@ export const DEVICE_POLL_INTERVAL_SECONDS = 5;
71
77
  // The bearer the agent presents to the NATS auth-callout. A bring-your-own-compute
72
78
  // machine is PERSISTENT (unlike an ephemeral Modal box, whose lifetime ~= an agent
73
79
  // token's hour), so this is long-lived — 30 days, matching the relay token below —
74
- // and re-minted on every poll/re-enroll. The old 1-hour value (sized for a Modal
80
+ // and renewed automatically by install-key proof, or re-minted on poll/re-enroll.
81
+ // The old 1-hour value (sized for a Modal
75
82
  // box) caused a self-hosted agent to drop PERMANENTLY one hour after connecting: the
76
83
  // bearer expired and the auth-callout rejected every reconnect ("re-enroll may be
77
84
  // required"). A long-lived bearer is safe because the auth-callout RE-CHECKS the
@@ -83,10 +90,69 @@ export const ENROLLMENT_BEARER_TTL_SECONDS = 30 * 24 * 3600;
83
90
  // NOT per-stream: the agent presents it on every channel registration for the life
84
91
  // of its run, and the producer side has no per-viewer epoch fence (that is the
85
92
  // VIEWER's `ogs_` token's job). So it is long-lived — 30 days — re-minted on every
86
- // poll/re-enroll. The relay re-verifies it (authenticity + the channel-key ws+agent
93
+ // renewal/poll/re-enroll. The relay re-verifies it (authenticity + the channel-key ws+agent
87
94
  // scope) on every StreamOpen; a revoked enrollment's machine goes offline at the
88
95
  // control plane regardless, so a long-lived relay token cannot reach a dead agent.
89
96
  export const RELAY_TOKEN_TTL_SECONDS = 30 * 24 * 3600;
97
+
98
+ /** Renew an existing grant, including after a long offline interval. The expired
99
+ * bearer is only an authenticated enrollment/generation locator here: current
100
+ * install-key possession and live enrollment authority are both required. Normal
101
+ * NATS/relay authentication continues to reject expired transport credentials.
102
+ * Nothing creates/reactivates an enrollment or changes its consent or scope. */
103
+ export async function renewEnrollmentCredentials(
104
+ services: EnrollmentServices,
105
+ input: RenewEnrollmentRequest,
106
+ nowSeconds = Math.floor(Date.now() / 1000),
107
+ ): Promise<EnrollmentCredentialsResponse | null> {
108
+ if (Math.abs(nowSeconds - input.signedAt) > 120) return null;
109
+ const secret = resolveEnrollmentSigningSecret(services.settings);
110
+ if (!secret) return null;
111
+ const claims = await verifyEnrollmentBearer(secret, input.bearer, 0);
112
+ if (
113
+ !claims ||
114
+ claims.agentId !== claims.enrollmentId ||
115
+ claims.subjectPrefix !== `agent.${claims.workspaceId}.${claims.agentId}`
116
+ )
117
+ return null;
118
+ const enrollment = await getEnrollment(services.db, claims.workspaceId, claims.enrollmentId);
119
+ if (
120
+ !enrollment ||
121
+ enrollment.status !== "active" ||
122
+ enrollment.credentialGeneration !== claims.credentialGeneration
123
+ )
124
+ return null;
125
+ try {
126
+ const rawKey = Buffer.from(enrollment.pubkey, "base64");
127
+ const signature = Buffer.from(input.signature, "base64");
128
+ if (rawKey.length !== 32 || signature.length !== 64) return null;
129
+ const key = createPublicKey({
130
+ key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), rawKey]),
131
+ format: "der",
132
+ type: "spki",
133
+ });
134
+ if (
135
+ !verify(
136
+ null,
137
+ Buffer.from(enrollmentRenewalProof(input.bearer, input.signedAt)),
138
+ key,
139
+ signature,
140
+ )
141
+ )
142
+ return null;
143
+ } catch {
144
+ return null;
145
+ }
146
+ // A concurrent revoke/re-enrollment still wins: issued credentials retain the
147
+ // captured generation, which the control-plane claim rechecks transactionally.
148
+ return buildEnrollmentCredentials(services, {
149
+ secret,
150
+ workspaceId: enrollment.workspaceId,
151
+ agentId: enrollment.id,
152
+ credentialGeneration: enrollment.credentialGeneration,
153
+ consentedScreenControl: enrollment.allowScreenControl,
154
+ });
155
+ }
90
156
  // The headless enroll token (`oget_`; design 11 §A2.1) TTL. 1h: long enough to
91
157
  // script a fleet rollout, short enough to bound exposure of a workspace-scoped
92
158
  // secret that IS the grant (no human approve). Re-mintable by an authorized user.
@@ -294,15 +360,19 @@ export async function denyDeviceEnrollment(
294
360
  }
295
361
 
296
362
  /** MINT a headless enroll token (design 11 §A2.2). Signs an `oget_` token bound to
297
- * the workspace + account + the screen-control consent, with a 1h TTL. Returns
298
- * null when the credential plane is disabled (no signing secret) so the route can
299
- * mirror poll's "disabled" handling. The token value is NEVER logged. */
363
+ * the workspace + account + the screen-control consent, with a 1h TTL. With an
364
+ * `ownerSubjectId` the token is personal (`enroll_user`): the machine it connects
365
+ * belongs to that person. Callers must pass only an authenticated human's own
366
+ * subject. Returns null when the credential plane is disabled (no signing secret)
367
+ * so the route can mirror poll's "disabled" handling. The token value is NEVER
368
+ * logged. */
300
369
  export async function mintEnrollToken(
301
370
  services: EnrollmentServices,
302
371
  input: {
303
372
  accountId: string;
304
373
  workspaceId: string;
305
374
  allowScreenControl: boolean;
375
+ ownerSubjectId?: string | null;
306
376
  },
307
377
  ): Promise<MintEnrollTokenResponse | null> {
308
378
  const { settings } = services;
@@ -312,18 +382,28 @@ export async function mintEnrollToken(
312
382
  }
313
383
  const nowSeconds = Math.floor(Date.now() / 1000);
314
384
  const exp = nowSeconds + ENROLL_TOKEN_TTL_SECONDS;
315
- const token = await signEnrollToken(secret, {
316
- typ: "enroll",
385
+ const claims = {
317
386
  workspaceId: input.workspaceId,
318
387
  accountId: input.accountId,
319
388
  allowScreenControl: input.allowScreenControl,
320
389
  iat: nowSeconds,
321
390
  exp,
322
- });
391
+ // Single-use: the exchange records this id, so the token connects one machine.
392
+ jti: randomBytes(18).toString("base64url"),
393
+ };
394
+ const ownerSubjectId = input.ownerSubjectId ?? null;
395
+ const token = await signEnrollToken(
396
+ secret,
397
+ ownerSubjectId !== null
398
+ ? { typ: "enroll_user", ownerSubjectId, ...claims }
399
+ : { typ: "enroll", ...claims },
400
+ );
323
401
  return {
324
402
  token,
325
403
  expiresAt: new Date(exp * 1000).toISOString(),
326
404
  expiresInSeconds: ENROLL_TOKEN_TTL_SECONDS,
405
+ scope: ownerSubjectId !== null ? "user" : "workspace",
406
+ allowScreenControl: input.allowScreenControl,
327
407
  };
328
408
  }
329
409
 
@@ -331,7 +411,11 @@ export async function mintEnrollToken(
331
411
  export type ExchangeEnrollTokenResult =
332
412
  | { ok: true; credentials: EnrollTokenExchangeResponse["credentials"] }
333
413
  | { ok: false; reason: "disabled" }
334
- | { ok: false; reason: "invalid" };
414
+ | { ok: false; reason: "invalid" }
415
+ | { ok: false; reason: "used" }
416
+ | { ok: false; reason: "owner_unavailable" }
417
+ | { ok: false; reason: "connected_elsewhere" }
418
+ | { ok: false; reason: "busy" };
335
419
 
336
420
  /** EXCHANGE a headless enroll token (design 11 §A2.3) — the UNAUTHENTICATED path
337
421
  * where the token IS the auth. Verifies the `oget_` token, then performs the SAME
@@ -366,18 +450,43 @@ export async function exchangeEnrollToken(
366
450
 
367
451
  // The SAME finalize as approve, but driven by the token's claims (no pending row).
368
452
  const sandboxName = (input.machineName?.trim() || `${input.os} machine`).slice(0, 256);
369
- const { enrollment } = await finalizeEnrollmentByToken(db, {
370
- accountId: claims.accountId,
371
- workspaceId: claims.workspaceId,
372
- pubkey: input.publicKey,
373
- hasDisplay: input.canOfferDisplay,
374
- // The token's allowScreenControl is the AUTHORITATIVE consent (NOT the agent's
375
- // requestsScreenControl) — it was baked in at mint by the authorizing user.
376
- allowScreenControl: claims.allowScreenControl,
377
- os: input.os,
378
- arch: input.arch,
379
- sandboxName,
380
- });
453
+ let enrollment: Awaited<ReturnType<typeof finalizeEnrollmentByToken>>["enrollment"];
454
+ try {
455
+ ({ enrollment } = await finalizeEnrollmentByToken(db, {
456
+ accountId: claims.accountId,
457
+ workspaceId: claims.workspaceId,
458
+ // A personal token connects the signed owner's own machine; the finalizer
459
+ // rechecks that person's live membership and workspace access.
460
+ ...(claims.typ === "enroll_user" ? { ownerSubjectId: claims.ownerSubjectId } : {}),
461
+ pubkey: input.publicKey,
462
+ hasDisplay: input.canOfferDisplay,
463
+ // The token's allowScreenControl is the AUTHORITATIVE consent (NOT the agent's
464
+ // requestsScreenControl) — it was baked in at mint by the authorizing user.
465
+ allowScreenControl: claims.allowScreenControl,
466
+ os: input.os,
467
+ arch: input.arch,
468
+ sandboxName,
469
+ // A token with an id connects one machine; the same machine may retry.
470
+ ...(claims.jti ? { tokenId: claims.jti, tokenExpiresAt: new Date(claims.exp * 1000) } : {}),
471
+ }));
472
+ } catch (error) {
473
+ if (error instanceof EnrollTokenAlreadyRedeemedError) {
474
+ return { ok: false, reason: "used" };
475
+ }
476
+ if (error instanceof EnrollTokenOwnerUnavailableError) {
477
+ return { ok: false, reason: "owner_unavailable" };
478
+ }
479
+ if (error instanceof PersonalMachineConnectedElsewhereError) {
480
+ // The owner's personal enrollment for this machine lives in another
481
+ // workspace; nothing was written and the token was not claimed.
482
+ return { ok: false, reason: "connected_elsewhere" };
483
+ }
484
+ if ((error as { code?: unknown } | null)?.code === "55P03") {
485
+ // The owner's organization membership is changing right now.
486
+ return { ok: false, reason: "busy" };
487
+ }
488
+ throw error;
489
+ }
381
490
 
382
491
  const credentials = await buildEnrollmentCredentials(services, {
383
492
  secret,
@@ -0,0 +1,192 @@
1
+ // Changing who can use a Connected Machine (Workspace or Only me) in place.
2
+ // The routes in ../routes/machines.ts stay thin: this module owns who may ask
3
+ // for which change, and the public shape of the preview and its refusals. The
4
+ // database functions recompute everything under the machine lock, so these
5
+ // checks only decide whether the caller may ask at all.
6
+
7
+ import {
8
+ ChangeMachineAudienceResponse,
9
+ MachineAudiencePreview,
10
+ type MachineAudience,
11
+ } from "@opengeni/contracts";
12
+ import type {
13
+ MachineAudienceChangeRecord,
14
+ MachineAudiencePreviewRecord,
15
+ MachineAudienceRefusal,
16
+ } from "@opengeni/db";
17
+ import { ApiHttpError } from "../http/api-error";
18
+
19
+ type AudienceCaller = {
20
+ subjectId: string;
21
+ permissions: readonly string[];
22
+ };
23
+
24
+ type AudienceMachine = {
25
+ workspaceId: string;
26
+ scope: "organization" | "workspace" | "user";
27
+ };
28
+
29
+ /**
30
+ * Decide whether the caller may ask for this change and who would own the
31
+ * machine. Narrowing to Only me: the person who will own it, or a workspace
32
+ * admin acting for that person. Sharing with the workspace: a workspace admin
33
+ * (the database also requires the caller to be the machine's owner, so nobody
34
+ * can expose another person's Only me machine).
35
+ */
36
+ export function machineAudienceRequestFor(
37
+ caller: AudienceCaller,
38
+ routeWorkspaceId: string,
39
+ machine: AudienceMachine,
40
+ request: { audience: MachineAudience; ownerSubjectId?: string | undefined },
41
+ ): { ownerSubjectId: string | null } {
42
+ if (machine.scope === "organization") {
43
+ throw new ApiHttpError(422, {
44
+ code: "validation_failed",
45
+ message:
46
+ "Organization machines are shared with the whole organization and can't be changed here.",
47
+ retryable: false,
48
+ });
49
+ }
50
+ if (machine.workspaceId !== routeWorkspaceId) {
51
+ throw new ApiHttpError(409, {
52
+ code: "conflict",
53
+ message: "Change who can use this machine from the workspace it was connected to.",
54
+ retryable: false,
55
+ details: { machineWorkspaceId: machine.workspaceId },
56
+ });
57
+ }
58
+ const workspaceAdmin = caller.permissions.includes("workspace:admin");
59
+ if (request.audience === "workspace") {
60
+ if (request.ownerSubjectId !== undefined) {
61
+ throw new ApiHttpError(400, {
62
+ code: "validation_failed",
63
+ message: "A machine shared with the workspace has no single owner.",
64
+ retryable: false,
65
+ });
66
+ }
67
+ if (!workspaceAdmin) {
68
+ throw new ApiHttpError(403, {
69
+ code: "forbidden",
70
+ message: "missing permission: workspace:admin",
71
+ retryable: false,
72
+ });
73
+ }
74
+ return { ownerSubjectId: null };
75
+ }
76
+ const ownerSubjectId = request.ownerSubjectId ?? caller.subjectId;
77
+ if (ownerSubjectId !== caller.subjectId && !workspaceAdmin) {
78
+ throw new ApiHttpError(403, {
79
+ code: "forbidden",
80
+ message: "missing permission: workspace:admin",
81
+ retryable: false,
82
+ });
83
+ }
84
+ return { ownerSubjectId };
85
+ }
86
+
87
+ const REFUSALS: Record<MachineAudienceRefusal, { status: number; message: string }> = {
88
+ machine_not_found: { status: 404, message: "machine not found in this access scope" },
89
+ organization_machine: {
90
+ status: 422,
91
+ message:
92
+ "Organization machines are shared with the whole organization and can't be changed here.",
93
+ },
94
+ invalid_target: { status: 400, message: "Choose Workspace or Only me." },
95
+ already_target: { status: 409, message: "The machine already has this audience." },
96
+ owner_not_member: {
97
+ status: 422,
98
+ message: "The new owner must be an active member of this organization.",
99
+ },
100
+ owner_lacks_workspace_access: {
101
+ status: 422,
102
+ message: "The new owner must be able to use this workspace.",
103
+ },
104
+ not_machine_owner: {
105
+ status: 403,
106
+ message: "Only the machine's owner can share it with the workspace.",
107
+ },
108
+ };
109
+
110
+ export function machineAudienceRefusalError(refusal: MachineAudienceRefusal): ApiHttpError {
111
+ const known = REFUSALS[refusal] ?? { status: 409, message: "The machine can't change now." };
112
+ return new ApiHttpError(known.status, {
113
+ code:
114
+ known.status === 404
115
+ ? "not_found"
116
+ : known.status === 403
117
+ ? "forbidden"
118
+ : known.status === 409
119
+ ? "conflict"
120
+ : "validation_failed",
121
+ message: known.message,
122
+ retryable: false,
123
+ details: { refusal },
124
+ });
125
+ }
126
+
127
+ export function machineAudiencePreview(
128
+ record: MachineAudiencePreviewRecord,
129
+ ): MachineAudiencePreview {
130
+ return MachineAudiencePreview.parse({
131
+ enrollmentId: record.enrollmentId,
132
+ sandboxId: record.sandboxId,
133
+ machineName: record.machineName,
134
+ currentAudience: record.currentAudience,
135
+ targetAudience: record.targetAudience,
136
+ generation: record.generation,
137
+ ownerSubjectId: record.targetAudience === "only_me" ? record.ownerSubjectId : null,
138
+ summary: record.summary,
139
+ ready: record.ready,
140
+ items: record.items.map((item) => ({
141
+ kind: item.kind,
142
+ action: item.action,
143
+ sessionId: item.sessionId ?? null,
144
+ scheduledTaskId: item.scheduledTaskId ?? null,
145
+ enrollmentId: item.enrollmentId ?? null,
146
+ workspaceId: item.workspaceId ?? null,
147
+ relation: item.relation ?? null,
148
+ visibility: item.visibility ?? null,
149
+ title: (item.kind === "schedule" ? item.name : item.title) ?? null,
150
+ ownerSubjectId: item.ownerSubjectId ?? null,
151
+ stopsTurn: item.stopsTurn ?? false,
152
+ stopsCommands: item.stopsCommands ?? 0,
153
+ })),
154
+ });
155
+ }
156
+
157
+ /** Map an applied change, or explain why nothing changed. */
158
+ export function machineAudienceChangeResponse(
159
+ record: MachineAudienceChangeRecord,
160
+ ): ChangeMachineAudienceResponse {
161
+ switch (record.outcome) {
162
+ case "changed":
163
+ return ChangeMachineAudienceResponse.parse({
164
+ enrollmentId: record.enrollmentId,
165
+ sandboxId: record.sandboxId,
166
+ previousAudience: record.previousAudience,
167
+ audience: record.audience,
168
+ ownerSubjectId: record.ownerSubjectId,
169
+ generation: record.generation,
170
+ summary: record.summary,
171
+ detachedSessionIds: record.detachedSessionIds,
172
+ keptSessionIds: record.keptSessionIds,
173
+ });
174
+ case "stale":
175
+ throw new ApiHttpError(409, {
176
+ code: "conflict",
177
+ message: "The machine changed since you reviewed it. Review the list again.",
178
+ retryable: false,
179
+ details: { reason: "stale", generation: record.generation },
180
+ });
181
+ case "blocked":
182
+ throw new ApiHttpError(409, {
183
+ code: "conflict",
184
+ message:
185
+ "This computer is also connected as a separate machine. Remove that copy first, then try again.",
186
+ retryable: false,
187
+ details: { reason: "blocked", preview: machineAudiencePreview(record.preview) },
188
+ });
189
+ case "refused":
190
+ throw machineAudienceRefusalError(record.refusal);
191
+ }
192
+ }
@@ -21,6 +21,7 @@
21
21
 
22
22
  import type { Settings } from "@opengeni/config";
23
23
  import {
24
+ enrollmentMacPermissions,
24
25
  getSession,
25
26
  getLiveEnrollmentConnection,
26
27
  listEnrollments,
@@ -32,8 +33,13 @@ import {
32
33
  type EnrollmentRecord,
33
34
  type MachineMetricsRow,
34
35
  } from "@opengeni/db";
35
- import { MachineView, MetricSample, type MachinesResponse } from "@opengeni/contracts";
36
- import { managedSessionGroupBackend } from "@opengeni/core";
36
+ import {
37
+ MachineView,
38
+ MetricSample,
39
+ type MachineRuntime,
40
+ type MachinesResponse,
41
+ } from "@opengeni/contracts";
42
+ import { managedSessionGroupBackend, sessionGroupMachinePresentation } from "@opengeni/core";
37
43
  import { selfhostedHeartbeatLiveness } from "@opengeni/runtime/sandbox";
38
44
 
39
45
  export type MachinesServices = {
@@ -41,7 +47,7 @@ export type MachinesServices = {
41
47
  settings: Settings;
42
48
  };
43
49
 
44
- const ACTIVE_UPDATE_STATUSES = new Set([
50
+ export const ACTIVE_UPDATE_STATUSES: ReadonlySet<string> = new Set([
45
51
  "requested",
46
52
  "accepted",
47
53
  "waiting_for_idle",
@@ -51,7 +57,7 @@ const ACTIVE_UPDATE_STATUSES = new Set([
51
57
  "restarting",
52
58
  ]);
53
59
 
54
- function compareAgentVersions(left: string, right: string): number | null {
60
+ export function compareAgentVersions(left: string, right: string): number | null {
55
61
  const parse = (value: string): { core: number[]; prerelease: string | null } | null => {
56
62
  const match = /^(?:v)?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(value.trim());
57
63
  if (!match) return null;
@@ -73,28 +79,57 @@ function compareAgentVersions(left: string, right: string): number | null {
73
79
  return a.prerelease.localeCompare(b.prerelease);
74
80
  }
75
81
 
76
- function runtimeFor(settings: Settings, enrollment: EnrollmentRecord): MachineView["runtime"] {
77
- const desiredVersion =
78
- enrollment.agentUpdateChannel === "beta"
79
- ? (settings.agentBetaVersion ?? settings.agentStableVersion)
80
- : settings.agentStableVersion;
81
- const update = enrollment.agentUpdate;
82
- const activeUpdate = update ? ACTIVE_UPDATE_STATUSES.has(update.status) : false;
82
+ /** The build the operator promoted for a machine's update channel. */
83
+ export function promotedAgentVersion(
84
+ settings: Pick<Settings, "agentStableVersion" | "agentBetaVersion">,
85
+ channel: string | null,
86
+ ): string {
87
+ return channel === "beta"
88
+ ? (settings.agentBetaVersion ?? settings.agentStableVersion)
89
+ : settings.agentStableVersion;
90
+ }
91
+
92
+ /** Older Mac updaters replace a single file inside a sealed app. They must
93
+ * bootstrap once through the official whole-app installer, never an RPC swap. */
94
+ export function machineUpdateBlockedReason(os: string, version: string | null): string | null {
95
+ if (os !== "macos") return null;
96
+ const order = version ? compareAgentVersions(version, "0.1.29") : null;
97
+ return order !== null && order >= 0
98
+ ? null
99
+ : "Install Opengeni Agent 0.1.29 or newer with the official Mac installer first. This older updater cannot preserve the signed application.";
100
+ }
101
+
102
+ type AgentUpdateRecord = EnrollmentRecord["agentUpdate"];
103
+
104
+ /** Where the installed agent stands against the promoted build. A failed update
105
+ * stops mattering once its target (or newer) is installed, and a busy machine
106
+ * postponing an update (`update_busy_*`) has not failed: it is still outdated. */
107
+ export function agentVersionState(
108
+ installedVersion: string | null,
109
+ desiredVersion: string | null,
110
+ update: AgentUpdateRecord,
111
+ ): MachineRuntime["versionState"] {
112
+ if (update && ACTIVE_UPDATE_STATUSES.has(update.status)) return "updating";
113
+ if (
114
+ update?.status === "failed" &&
115
+ !update.errorCode?.startsWith("update_busy") &&
116
+ !(installedVersion && (compareAgentVersions(installedVersion, update.targetVersion) ?? -1) >= 0)
117
+ ) {
118
+ return "update_failed";
119
+ }
83
120
  const order =
84
- enrollment.agentVersion && desiredVersion
85
- ? compareAgentVersions(enrollment.agentVersion, desiredVersion)
121
+ installedVersion && desiredVersion
122
+ ? compareAgentVersions(installedVersion, desiredVersion)
86
123
  : null;
87
- const versionState = activeUpdate
88
- ? "updating"
89
- : update?.status === "failed"
90
- ? "update_failed"
91
- : order === null
92
- ? "unknown"
93
- : order === 0
94
- ? "current"
95
- : order < 0
96
- ? "outdated"
97
- : "ahead";
124
+ if (order === null) return "unknown";
125
+ if (order === 0) return "current";
126
+ return order < 0 ? "outdated" : "ahead";
127
+ }
128
+
129
+ function runtimeFor(settings: Settings, enrollment: EnrollmentRecord): MachineView["runtime"] {
130
+ const desiredVersion = promotedAgentVersion(settings, enrollment.agentUpdateChannel);
131
+ const update = enrollment.agentUpdate;
132
+ const versionState = agentVersionState(enrollment.agentVersion, desiredVersion, update);
98
133
  const capability = (name: string): boolean => enrollment.agentCapabilities[name] === true;
99
134
  return {
100
135
  installedVersion: enrollment.agentVersion,
@@ -102,6 +137,7 @@ function runtimeFor(settings: Settings, enrollment: EnrollmentRecord): MachineVi
102
137
  updateChannel: enrollment.agentUpdateChannel,
103
138
  desiredVersion,
104
139
  versionState,
140
+ updateBlockedReason: machineUpdateBlockedReason(enrollment.os, enrollment.agentVersion),
105
141
  capabilities: {
106
142
  exec: capability("exec"),
107
143
  filesystem: capability("filesystem"),
@@ -113,8 +149,15 @@ function runtimeFor(settings: Settings, enrollment: EnrollmentRecord): MachineVi
113
149
  operationResourcePolicy: capability("operationResourcePolicy"),
114
150
  operationCpuQuota: capability("operationCpuQuota"),
115
151
  transactionalFsWrite: capability("transactionalFsWrite"),
152
+ credentialRenew: capability("credentialRenew"),
153
+ screenControl:
154
+ typeof enrollment.agentCapabilities.screenControl === "boolean"
155
+ ? enrollment.agentCapabilities.screenControl
156
+ : null,
116
157
  },
117
158
  update,
159
+ macPermissions:
160
+ enrollment.os === "macos" ? enrollmentMacPermissions(enrollment.agentCapabilities) : null,
118
161
  };
119
162
  }
120
163
 
@@ -258,23 +301,24 @@ export async function listMachines(
258
301
  if (session && groupBackend) {
259
302
  const groupActive = activeSandboxId === null;
260
303
  const groupLease = await readLease(db, workspaceId, session.sandboxGroupId);
304
+ const presentation = sessionGroupMachinePresentation(groupBackend);
305
+ const localHost = groupBackend === "local";
261
306
  machines.push(
262
307
  MachineView.parse({
263
308
  sandboxId: session.sandboxGroupId,
264
309
  enrollmentId: null,
265
- name: "session sandbox",
266
- kind: groupBackend === "opensandbox" ? "opensandbox" : "modal",
310
+ name: presentation.name,
311
+ kind: presentation.kind,
267
312
  state: "online",
268
313
  active: groupActive,
269
314
  isSessionGroup: true,
270
315
  workspaceGeneration: groupLease?.workspaceGeneration ?? null,
271
316
  archiveGeneration: groupLease?.archiveGeneration ?? null,
272
317
  archiveComplete: groupLease?.archiveComplete ?? false,
273
- // The Modal group box is a cloud Linux box; its precise OS/arch is not
274
- // surfaced as a metric, so the dashboard shows the canonical linux/x86_64.
275
- os: "linux",
276
- arch: "x86_64",
277
- hasDisplay: false,
318
+ // A local sandbox is this machine. Other managed providers run a Linux box.
319
+ os: localHost ? process.platform : "linux",
320
+ arch: localHost ? process.arch : "x86_64",
321
+ hasDisplay: localHost,
278
322
  desktopUnavailableReason: null,
279
323
  allowScreenControl: false,
280
324
  sharedSessionCount: 1,