@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
@@ -1,14 +1,21 @@
1
1
  import {
2
2
  OPENROUTER_PROVIDER_ID,
3
+ OPPER_PROVIDER_ID,
3
4
  WORKSPACE_OPENROUTER_PROVIDER_ID,
5
+ WORKSPACE_OPPER_PROVIDER_ID,
4
6
  type ConfiguredModel,
5
7
  } from "@opengeni/config";
6
8
  import {
7
9
  ClientModel,
8
10
  WorkspaceModelCatalogResponse,
11
+ type DefaultModelSelection,
9
12
  type WorkspaceModelCatalogResponse as WorkspaceModelCatalogResponseType,
10
13
  } from "@opengeni/contracts";
11
- import { resolveWorkspaceModelSelection, type WorkspaceModelSelectionInput } from "@opengeni/core";
14
+ import {
15
+ resolveWorkspaceModelSelection,
16
+ type WorkspaceModelSelection,
17
+ type WorkspaceModelSelectionInput,
18
+ } from "@opengeni/core";
12
19
 
13
20
  export {
14
21
  MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS,
@@ -20,13 +27,24 @@ export {
20
27
  export function projectClientModel(model: ConfiguredModel): ClientModel {
21
28
  const anonymousProvider =
22
29
  model.credentialSource.kind === "deployment" && model.credentialSource.mechanism === "none";
30
+ const directProvider =
31
+ model.providerId.startsWith("workspace-openai-") ||
32
+ model.providerId.startsWith("workspace-azure-openai-");
33
+ const organizationProvider = model.credentialSource.kind === "organization_connection";
23
34
  // Keep the established closed `source` enum compatible for older same-major
24
35
  // clients. OpenRouter remains truthfully identified by its public provider
25
36
  // id/label and billing metadata; omitting this optional legacy grouping field
26
37
  // lets tolerant older contracts parse the additive provider.
27
38
  const source =
39
+ directProvider ||
40
+ organizationProvider ||
41
+ model.providerId === "workspace-anthropic" ||
42
+ model.providerId === "workspace-claude-subscription" ||
43
+ model.providerId === "organization-claude-subscription" ||
28
44
  model.providerId === OPENROUTER_PROVIDER_ID ||
29
- model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID
45
+ model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID ||
46
+ model.providerId === OPPER_PROVIDER_ID ||
47
+ model.providerId === WORKSPACE_OPPER_PROVIDER_ID
30
48
  ? undefined
31
49
  : model.credentialSource.kind === "connected_subscription"
32
50
  ? model.credentialSource.provider === "xai"
@@ -37,23 +55,34 @@ export function projectClientModel(model: ConfiguredModel): ClientModel {
37
55
  : anonymousProvider
38
56
  ? undefined
39
57
  : "opengeni";
40
- const publicProvider = anonymousProvider
41
- ? { provider: model.providerId, providerLabel: model.providerLabel }
42
- : model.providerId === OPENROUTER_PROVIDER_ID
43
- ? { provider: "openrouter", providerLabel: "OpenRouter" }
44
- : model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID
45
- ? { provider: "workspace-openrouter", providerLabel: "Your OpenRouter" }
46
- : source === "codex"
47
- ? { provider: "codex", providerLabel: "Codex" }
48
- : source === "supergrok"
49
- ? { provider: "supergrok", providerLabel: "SuperGrok" }
50
- : source === "workspace_gateway"
51
- ? { provider: "workspace-gateway", providerLabel: "Your Gateway" }
52
- : { provider: "opengeni", providerLabel: "OpenGeni" };
58
+ const publicProvider =
59
+ directProvider ||
60
+ anonymousProvider ||
61
+ organizationProvider ||
62
+ model.providerId === "workspace-anthropic" ||
63
+ model.providerId === "workspace-claude-subscription" ||
64
+ model.providerId === "organization-claude-subscription"
65
+ ? { provider: model.providerId, providerLabel: model.providerLabel }
66
+ : model.providerId === OPENROUTER_PROVIDER_ID
67
+ ? { provider: "openrouter", providerLabel: "OpenRouter" }
68
+ : model.providerId === WORKSPACE_OPENROUTER_PROVIDER_ID
69
+ ? { provider: "workspace-openrouter", providerLabel: "Your OpenRouter" }
70
+ : model.providerId === OPPER_PROVIDER_ID
71
+ ? { provider: "opper", providerLabel: "Opper" }
72
+ : model.providerId === WORKSPACE_OPPER_PROVIDER_ID
73
+ ? { provider: "workspace-opper", providerLabel: "Your Opper" }
74
+ : source === "codex"
75
+ ? { provider: "codex", providerLabel: "Codex" }
76
+ : source === "supergrok"
77
+ ? { provider: "supergrok", providerLabel: "SuperGrok" }
78
+ : source === "workspace_gateway"
79
+ ? { provider: "workspace-gateway", providerLabel: "Your Gateway" }
80
+ : { provider: "opengeni", providerLabel: "Opengeni" };
53
81
  return ClientModel.parse({
54
82
  id: model.id,
55
83
  label: model.label,
56
84
  ...(model.shortLabel ? { shortLabel: model.shortLabel } : {}),
85
+ ...(model.logoUrl ? { logoUrl: model.logoUrl } : {}),
57
86
  ...publicProvider,
58
87
  ...(source === undefined ? {} : { source }),
59
88
  api: model.api,
@@ -81,12 +110,34 @@ export function projectClientModel(model: ConfiguredModel): ClientModel {
81
110
  */
82
111
  export function buildWorkspaceModelCatalog(
83
112
  input: WorkspaceModelSelectionInput,
113
+ defaults?: WorkspaceModelCatalogDefaults,
84
114
  ): WorkspaceModelCatalogResponseType {
85
- const models = resolveWorkspaceModelSelection(input).map((selection) => ({
115
+ return projectWorkspaceModelCatalog(resolveWorkspaceModelSelection(input), defaults);
116
+ }
117
+
118
+ /** Resolved new-chat defaults published beside the catalog rows. */
119
+ export type WorkspaceModelCatalogDefaults = {
120
+ defaultSelection: DefaultModelSelection;
121
+ creditsSelection: DefaultModelSelection | null;
122
+ };
123
+
124
+ export function projectWorkspaceModelCatalog(
125
+ selections: readonly WorkspaceModelSelection[],
126
+ defaults?: WorkspaceModelCatalogDefaults,
127
+ ): WorkspaceModelCatalogResponseType {
128
+ const models = selections.map((selection) => ({
86
129
  ...projectClientModel(selection.model),
87
130
  credentialReadiness: selection.credentialReadiness,
88
131
  policyAllowed: selection.policyAllowed,
89
132
  availability: selection.availability,
90
133
  }));
91
- return WorkspaceModelCatalogResponse.parse({ models });
134
+ return WorkspaceModelCatalogResponse.parse({
135
+ models,
136
+ ...(defaults
137
+ ? {
138
+ defaultSelection: defaults.defaultSelection,
139
+ creditsSelection: defaults.creditsSelection,
140
+ }
141
+ : {}),
142
+ });
92
143
  }
@@ -0,0 +1,457 @@
1
+ import { createSign, randomUUID } from "node:crypto";
2
+ import { connect, type ClientHttp2Session } from "node:http2";
3
+ import type { Settings } from "@opengeni/config";
4
+ import {
5
+ claimNativePushDeliveries,
6
+ listSessionHumanInputRequests,
7
+ pruneNativePushDeliveries,
8
+ settleNativePushDelivery,
9
+ type ClaimedNativePushDelivery,
10
+ type Database,
11
+ type NativePushOutcome,
12
+ } from "@opengeni/db";
13
+ import type { Observability } from "@opengeni/observability";
14
+
15
+ // Sends queued native app pushes (0638) through APNs (token auth over HTTP/2)
16
+ // and FCM HTTP v1. At-least-once: a delivery is settled only after the
17
+ // provider answered; an unknown token removes the registration.
18
+
19
+ const POLL_INTERVAL_MS = 1_000;
20
+ const CLAIM_SECONDS = 60;
21
+ const MAX_ATTEMPTS = 6;
22
+ const PRUNE_EVERY_TICKS = 600;
23
+
24
+ export type NativePushSendResult = { outcome: NativePushOutcome; error?: string };
25
+
26
+ export type NativePushSender = (
27
+ delivery: ClaimedNativePushDelivery,
28
+ ) => Promise<NativePushSendResult>;
29
+
30
+ /** A claimed payload, marked when its question asks for a secret. */
31
+ export type NativePushPayload = ClaimedNativePushDelivery["payload"] & { secretQuestion?: boolean };
32
+
33
+ const SECRET_QUESTION_PUSH_BODY = "Open Opengeni to answer. The answer is saved to a vault.";
34
+
35
+ /**
36
+ * Mark question pushes whose pending request asks for a secret, so they carry
37
+ * no inline answer action and point to the app instead.
38
+ */
39
+ async function markSecretQuestionDeliveries(
40
+ db: Database,
41
+ deliveries: ClaimedNativePushDelivery[],
42
+ ): Promise<ClaimedNativePushDelivery[]> {
43
+ const secretSessions = new Map<string, Promise<boolean>>();
44
+ return await Promise.all(
45
+ deliveries.map(async (delivery) => {
46
+ if (delivery.payload.eventType !== "session.humanInput.requested") return delivery;
47
+ const key = `${delivery.payload.workspaceId}:${delivery.payload.sessionId}`;
48
+ let lookup = secretSessions.get(key);
49
+ if (!lookup) {
50
+ lookup = listSessionHumanInputRequests(
51
+ db,
52
+ delivery.payload.workspaceId,
53
+ delivery.payload.sessionId,
54
+ { status: "pending", limit: 20 },
55
+ )
56
+ .then((requests) =>
57
+ requests.some((request) =>
58
+ request.questions.some((question) => question.kind === "secret"),
59
+ ),
60
+ )
61
+ .catch(() => false);
62
+ secretSessions.set(key, lookup);
63
+ }
64
+ if (!(await lookup)) return delivery;
65
+ const payload: NativePushPayload = { ...delivery.payload, secretQuestion: true };
66
+ return { ...delivery, payload };
67
+ }),
68
+ );
69
+ }
70
+
71
+ export type NativePushDispatchDeps = {
72
+ db: Database;
73
+ settings: Settings;
74
+ observability?: Pick<Observability, "info" | "warn" | "error">;
75
+ senders?: Partial<Record<"ios" | "android", NativePushSender>>;
76
+ };
77
+
78
+ /**
79
+ * A notification message as plain text for the lock screen: links keep their
80
+ * text, emphasis and code marks go, and "- " bullets become "•".
81
+ */
82
+ export function plainNotificationText(markdown: string): string {
83
+ return markdown
84
+ .replace(/\[([^\]]+)\]\((?:[^)]+)\)/gu, "$1")
85
+ .replace(/(\*\*|__)(.+?)\1/gu, "$2")
86
+ .replace(/`([^`]+)`/gu, "$1")
87
+ .replace(/^[ \t]*[-*][ \t]+/gmu, "• ")
88
+ .replace(/\n{3,}/gu, "\n\n")
89
+ .trim();
90
+ }
91
+
92
+ /** The visible text of a push: title, optional subtitle, and the message with its facts. */
93
+ export function nativePushAlert(payload: NativePushPayload): {
94
+ title: string;
95
+ subtitle?: string;
96
+ body?: string;
97
+ } {
98
+ if (payload.secretQuestion) {
99
+ // A secret is answered only in the app's secure card (AC-5): no inline reply.
100
+ return { title: payload.title ?? "The agent needs a secret", body: SECRET_QUESTION_PUSH_BODY };
101
+ }
102
+ const fallback =
103
+ payload.rule === "needs_input"
104
+ ? "The agent needs you"
105
+ : payload.rule === "failed"
106
+ ? "A turn failed"
107
+ : payload.rule === "reply_ready"
108
+ ? "Reply ready"
109
+ : "Opengeni";
110
+ const facts = (payload.facts ?? [])
111
+ .slice(0, 4)
112
+ .map((fact) => `${fact.label}: ${fact.value}`)
113
+ .join("\n");
114
+ const message = payload.body ? plainNotificationText(payload.body) : "";
115
+ // A notification with no message of its own carries its title as the body;
116
+ // show the title once.
117
+ const lines = [message === payload.title ? "" : message, facts].filter(Boolean);
118
+ const body = lines.join("\n\n");
119
+ return {
120
+ title: payload.title ?? fallback,
121
+ ...(payload.subtitle ? { subtitle: payload.subtitle } : {}),
122
+ ...(body || payload.rule !== "agent" ? { body: body || fallback } : {}),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * What breaks through Focus: an agent notification the agent marked
128
+ * time-sensitive, and questions and approvals for a person who asked for them
129
+ * to (their setting puts the urgency on the delivery). Everything else, and
130
+ * replies and failures always, arrive as ordinary alerts, so Focus and the
131
+ * person's own notification settings decide when they show.
132
+ */
133
+ export function nativePushInterruptionLevel(
134
+ payload: ClaimedNativePushDelivery["payload"],
135
+ ): "time-sensitive" | "active" {
136
+ if (
137
+ (payload.rule === "agent" || payload.rule === "needs_input") &&
138
+ payload.urgency === "time_sensitive"
139
+ ) {
140
+ return "time-sensitive";
141
+ }
142
+ return "active";
143
+ }
144
+
145
+ /** What the app reads on tap: enough to open the session in the right account. */
146
+ export function nativePushData(payload: ClaimedNativePushDelivery["payload"]) {
147
+ return {
148
+ sessionId: payload.sessionId,
149
+ workspaceId: payload.workspaceId,
150
+ subjectId: payload.subjectId,
151
+ rule: payload.rule,
152
+ ...(payload.eventType ? { eventType: payload.eventType } : {}),
153
+ ...(payload.sequence ? { sequence: payload.sequence } : {}),
154
+ };
155
+ }
156
+
157
+ /**
158
+ * The notification category the app registers actions for: an approval can be
159
+ * approved (behind device unlock) or denied, a question answered inline.
160
+ */
161
+ export function nativePushCategory(
162
+ payload: NativePushPayload,
163
+ ): "og.approval" | "og.question" | undefined {
164
+ if (payload.secretQuestion) return undefined;
165
+ if (payload.eventType === "session.requiresAction") return "og.approval";
166
+ if (payload.eventType === "session.humanInput.requested") return "og.question";
167
+ return undefined;
168
+ }
169
+
170
+ function base64Url(input: Buffer | string): string {
171
+ return Buffer.from(input).toString("base64url");
172
+ }
173
+
174
+ function signJwt(
175
+ header: Record<string, string>,
176
+ claims: Record<string, unknown>,
177
+ privateKey: string,
178
+ algorithm: "ES256" | "RS256",
179
+ ): string {
180
+ const unsigned = `${base64Url(JSON.stringify(header))}.${base64Url(JSON.stringify(claims))}`;
181
+ const signer = createSign("SHA256");
182
+ signer.update(unsigned);
183
+ const signature =
184
+ algorithm === "ES256"
185
+ ? signer.sign({ key: privateKey, dsaEncoding: "ieee-p1363" })
186
+ : signer.sign(privateKey);
187
+ return `${unsigned}.${base64Url(signature)}`;
188
+ }
189
+
190
+ function pem(value: string): string {
191
+ return value.includes("\\n") ? value.replace(/\\n/gu, "\n") : value;
192
+ }
193
+
194
+ /** APNs with a token-based (.p8) key; one HTTP/2 connection per environment. */
195
+ export function createApnsSender(input: {
196
+ keyId: string;
197
+ teamId: string;
198
+ privateKey: string;
199
+ }): NativePushSender {
200
+ const key = pem(input.privateKey);
201
+ let token: { value: string; issuedAt: number } | null = null;
202
+ const sessions = new Map<string, ClientHttp2Session>();
203
+ const bearer = () => {
204
+ const now = Math.floor(Date.now() / 1000);
205
+ // Apple accepts a provider token for an hour and rejects refreshes more
206
+ // often than every 20 minutes.
207
+ if (!token || now - token.issuedAt > 40 * 60) {
208
+ token = {
209
+ value: signJwt(
210
+ { alg: "ES256", kid: input.keyId },
211
+ { iss: input.teamId, iat: now },
212
+ key,
213
+ "ES256",
214
+ ),
215
+ issuedAt: now,
216
+ };
217
+ }
218
+ return token.value;
219
+ };
220
+ const session = (host: string) => {
221
+ const existing = sessions.get(host);
222
+ if (existing && !existing.closed && !existing.destroyed) return existing;
223
+ const created = connect(`https://${host}`);
224
+ created.on("error", () => sessions.delete(host));
225
+ created.on("close", () => sessions.delete(host));
226
+ sessions.set(host, created);
227
+ return created;
228
+ };
229
+ return async (delivery) => {
230
+ const host =
231
+ delivery.environment === "development" ? "api.sandbox.push.apple.com" : "api.push.apple.com";
232
+ const alert = nativePushAlert(delivery.payload);
233
+ const category = nativePushCategory(delivery.payload);
234
+ const body = JSON.stringify({
235
+ aps: {
236
+ alert,
237
+ sound: "default",
238
+ "thread-id": delivery.payload.sessionId,
239
+ "interruption-level": nativePushInterruptionLevel(delivery.payload),
240
+ ...(category ? { category } : {}),
241
+ },
242
+ body: nativePushData(delivery.payload),
243
+ });
244
+ return await new Promise<NativePushSendResult>((resolve) => {
245
+ const request = session(host).request({
246
+ ":method": "POST",
247
+ ":path": `/3/device/${delivery.token}`,
248
+ authorization: `bearer ${bearer()}`,
249
+ "apns-topic": delivery.appId,
250
+ "apns-push-type": "alert",
251
+ "apns-priority": "10",
252
+ "apns-collapse-id": `${delivery.payload.sessionId}:${delivery.payload.rule}`.slice(0, 64),
253
+ "content-type": "application/json",
254
+ });
255
+ let status = 0;
256
+ let text = "";
257
+ request.setTimeout(10_000, () => request.close());
258
+ request.on("response", (headers) => {
259
+ status = Number(headers[":status"] ?? 0);
260
+ });
261
+ request.on("data", (chunk: Buffer) => {
262
+ text += chunk.toString("utf8");
263
+ });
264
+ request.on("error", (error) => resolve({ outcome: "retry", error: String(error) }));
265
+ request.on("close", () => {
266
+ if (status === 200) return resolve({ outcome: "delivered" });
267
+ const reason = (() => {
268
+ try {
269
+ return (JSON.parse(text) as { reason?: string }).reason ?? text;
270
+ } catch {
271
+ return text || `status ${status}`;
272
+ }
273
+ })();
274
+ if (status === 410 || reason === "BadDeviceToken" || reason === "Unregistered") {
275
+ return resolve({ outcome: "unregistered", error: `${status} ${reason}` });
276
+ }
277
+ resolve({
278
+ outcome: status === 429 || status >= 500 || status === 0 ? "retry" : "failed",
279
+ error: `${status} ${reason}`,
280
+ });
281
+ });
282
+ request.end(body);
283
+ });
284
+ };
285
+ }
286
+
287
+ /** FCM HTTP v1 with a service account; data-only, so the app presents it. */
288
+ export function createFcmSender(input: {
289
+ serviceAccountJson: string;
290
+ fetch?: typeof fetch;
291
+ }): NativePushSender {
292
+ const account = JSON.parse(input.serviceAccountJson) as {
293
+ project_id: string;
294
+ client_email: string;
295
+ private_key: string;
296
+ token_uri?: string;
297
+ };
298
+ const http = input.fetch ?? fetch;
299
+ let access: { token: string; expiresAt: number } | null = null;
300
+ const accessToken = async () => {
301
+ if (access && access.expiresAt - 60_000 > Date.now()) return access.token;
302
+ const now = Math.floor(Date.now() / 1000);
303
+ const tokenUri = account.token_uri ?? "https://oauth2.googleapis.com/token";
304
+ const assertion = signJwt(
305
+ { alg: "RS256", typ: "JWT" },
306
+ {
307
+ iss: account.client_email,
308
+ scope: "https://www.googleapis.com/auth/firebase.messaging",
309
+ aud: tokenUri,
310
+ iat: now,
311
+ exp: now + 3600,
312
+ },
313
+ pem(account.private_key),
314
+ "RS256",
315
+ );
316
+ const response = await http(tokenUri, {
317
+ method: "POST",
318
+ headers: { "content-type": "application/x-www-form-urlencoded" },
319
+ body: new URLSearchParams({
320
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
321
+ assertion,
322
+ }),
323
+ });
324
+ if (!response.ok) throw new Error(`FCM token exchange failed: ${response.status}`);
325
+ const json = (await response.json()) as { access_token: string; expires_in: number };
326
+ access = { token: json.access_token, expiresAt: Date.now() + json.expires_in * 1000 };
327
+ return access.token;
328
+ };
329
+ return async (delivery) => {
330
+ try {
331
+ const alert = nativePushAlert(delivery.payload);
332
+ const response = await http(
333
+ `https://fcm.googleapis.com/v1/projects/${account.project_id}/messages:send`,
334
+ {
335
+ method: "POST",
336
+ headers: {
337
+ authorization: `Bearer ${await accessToken()}`,
338
+ "content-type": "application/json",
339
+ },
340
+ body: JSON.stringify({
341
+ message: {
342
+ token: delivery.token,
343
+ android: { priority: "HIGH", collapse_key: delivery.payload.sessionId },
344
+ data: {
345
+ title: alert.title,
346
+ message: [alert.subtitle, alert.body].filter(Boolean).join("\n") || alert.title,
347
+ body: JSON.stringify(nativePushData(delivery.payload)),
348
+ },
349
+ },
350
+ }),
351
+ signal: AbortSignal.timeout(10_000),
352
+ },
353
+ );
354
+ if (response.ok) return { outcome: "delivered" };
355
+ const text = await response.text();
356
+ if (response.status === 404 || text.includes("UNREGISTERED")) {
357
+ return { outcome: "unregistered", error: `${response.status} ${text.slice(0, 200)}` };
358
+ }
359
+ return {
360
+ outcome: response.status === 429 || response.status >= 500 ? "retry" : "failed",
361
+ error: `${response.status} ${text.slice(0, 200)}`,
362
+ };
363
+ } catch (error) {
364
+ return { outcome: "retry", error: String(error).slice(0, 300) };
365
+ }
366
+ };
367
+ }
368
+
369
+ /** The senders this deployment has credentials for. */
370
+ export function configuredNativePushSenders(
371
+ settings: Settings,
372
+ ): Partial<Record<"ios" | "android", NativePushSender>> {
373
+ const senders: Partial<Record<"ios" | "android", NativePushSender>> = {};
374
+ if (settings.apnsKeyId && settings.apnsTeamId && settings.apnsPrivateKey) {
375
+ senders.ios = createApnsSender({
376
+ keyId: settings.apnsKeyId,
377
+ teamId: settings.apnsTeamId,
378
+ privateKey: settings.apnsPrivateKey,
379
+ });
380
+ }
381
+ if (settings.fcmServiceAccountJson) {
382
+ senders.android = createFcmSender({ serviceAccountJson: settings.fcmServiceAccountJson });
383
+ }
384
+ return senders;
385
+ }
386
+
387
+ export async function drainNativePushDeliveries(
388
+ deps: NativePushDispatchDeps,
389
+ ): Promise<{ claimed: number; delivered: number }> {
390
+ const senders = deps.senders ?? configuredNativePushSenders(deps.settings);
391
+ const claimId = randomUUID();
392
+ const claimed = await markSecretQuestionDeliveries(
393
+ deps.db,
394
+ await claimNativePushDeliveries(deps.db, {
395
+ claimId,
396
+ limit: 32,
397
+ claimSeconds: CLAIM_SECONDS,
398
+ }),
399
+ );
400
+ let delivered = 0;
401
+ await Promise.all(
402
+ claimed.map(async (delivery) => {
403
+ const sender = senders[delivery.platform];
404
+ const result: NativePushSendResult = sender
405
+ ? await sender(delivery).catch((error: unknown) => ({
406
+ outcome: "retry" as const,
407
+ error: String(error),
408
+ }))
409
+ : { outcome: "failed", error: `No ${delivery.platform} push credentials configured` };
410
+ const outcome =
411
+ result.outcome === "retry" && delivery.attempts >= MAX_ATTEMPTS ? "failed" : result.outcome;
412
+ if (outcome === "delivered") delivered += 1;
413
+ else
414
+ deps.observability?.warn("Native push not delivered", {
415
+ platform: delivery.platform,
416
+ outcome,
417
+ error: result.error,
418
+ });
419
+ await settleNativePushDelivery(deps.db, {
420
+ claimId,
421
+ deliveryId: delivery.deliveryId,
422
+ outcome,
423
+ error: result.error ?? null,
424
+ retrySeconds: Math.min(3600, 5 * 2 ** delivery.attempts),
425
+ });
426
+ }),
427
+ );
428
+ return { claimed: claimed.length, delivered };
429
+ }
430
+
431
+ export function startNativePushDispatchPump(deps: NativePushDispatchDeps): () => Promise<void> {
432
+ const senders = deps.senders ?? configuredNativePushSenders(deps.settings);
433
+ let stopped = false;
434
+ let ticks = 0;
435
+ let running: Promise<void> | null = null;
436
+ const tick = async () => {
437
+ ticks += 1;
438
+ try {
439
+ const { claimed } = await drainNativePushDeliveries({ ...deps, senders });
440
+ if (claimed > 0) deps.observability?.info("Native push batch", { claimed });
441
+ if (ticks % PRUNE_EVERY_TICKS === 0) await pruneNativePushDeliveries(deps.db);
442
+ } catch (error) {
443
+ deps.observability?.error("Native push dispatch failed", { error: String(error) });
444
+ }
445
+ };
446
+ const timer = setInterval(() => {
447
+ if (stopped || running) return;
448
+ running = tick().finally(() => {
449
+ running = null;
450
+ });
451
+ }, POLL_INTERVAL_MS);
452
+ return async () => {
453
+ stopped = true;
454
+ clearInterval(timer);
455
+ await running;
456
+ };
457
+ }
@@ -1,6 +1,20 @@
1
- import type { EventLogger } from "@opengeni/events";
1
+ import {
2
+ natsSubscriptionTerminationCounter,
3
+ type EventBusOptions,
4
+ type EventLogger,
5
+ } from "@opengeni/events";
2
6
  import type { Attributes, AttributeValue, Observability } from "@opengeni/observability";
3
7
 
8
+ /** Logger plus the closed-label subscription-termination counter for NATS connections. */
9
+ export function observabilityEventBusOptions(
10
+ observability: Observability,
11
+ ): Pick<EventBusOptions, "logger" | "onSubscriptionTerminated"> {
12
+ return {
13
+ logger: observabilityEventLogger(observability),
14
+ onSubscriptionTerminated: natsSubscriptionTerminationCounter(observability),
15
+ };
16
+ }
17
+
4
18
  export function observabilityEventLogger(observability: Observability): EventLogger {
5
19
  return {
6
20
  debug: (message, attributes) => observability.debug(message, eventAttributes(attributes)),