@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
@@ -0,0 +1,120 @@
1
+ import { createHash } from "node:crypto";
2
+ import { ErrorCode, Permission } from "@opengeni/contracts";
3
+
4
+ /**
5
+ * Bounded, content-free classification of a rejected (4xx/5xx) API response,
6
+ * so a refused request can be diagnosed from logs and counted per route without
7
+ * copying the response message, identifiers, or user content into telemetry.
8
+ *
9
+ * - `code` is the public `ErrorEnvelope.code` (a closed enum).
10
+ * - `reason` is `permission:<name>` when the refusal names a missing permission
11
+ * from the closed permission catalog, else the envelope's typed
12
+ * `details.code` when it is a short identifier, else `control:<code>` for a
13
+ * Connected Machine control failure, else `unclassified`.
14
+ * - `fingerprint` is a one-way hash of the message with identifiers and
15
+ * numbers normalized away. It groups identical refusals in logs; resolve it
16
+ * back to source text with `bun run scripts/resolve-rejection-fingerprint.ts`.
17
+ * It never becomes a metric label.
18
+ */
19
+ export type HttpRejectionFacts = {
20
+ code: string;
21
+ reason: string;
22
+ fingerprint?: string;
23
+ };
24
+
25
+ const ERROR_CODES: ReadonlySet<string> = new Set(ErrorCode.options);
26
+ const PERMISSIONS: ReadonlySet<string> = new Set(Permission.options);
27
+ const DETAIL_CODE = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
28
+ const MISSING_PERMISSION = /^missing (?:literal )?permission: ([a-z][a-z0-9-]*:[a-z][a-z0-9-]*)/;
29
+ const CONTROL_CODE = /^[a-z][a-z0-9_]{0,31}$/;
30
+ const MAX_ENVELOPE_BYTES = 32 * 1024;
31
+
32
+ export const UNCLASSIFIED_REJECTION_REASON = "unclassified";
33
+
34
+ export function rejectionFingerprint(message: string): string {
35
+ return `m_${createHash("sha256").update(normalizeRejectionMessage(message)).digest("hex").slice(0, 10)}`;
36
+ }
37
+
38
+ /** Identifiers, quoted values, and numbers never distinguish two fingerprints. */
39
+ export function normalizeRejectionMessage(message: string): string {
40
+ return message
41
+ .replace(/[\u0000-\u001f\u007f]+/g, " ")
42
+ .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<id>")
43
+ .replace(/"[^"]*"|'[^']*'|`[^`]*`/g, "<q>")
44
+ .replace(/[0-9]+/g, "0")
45
+ .replace(/\s+/g, " ")
46
+ .trim();
47
+ }
48
+
49
+ export function rejectionReasonFromMessage(message: string | undefined): string | null {
50
+ if (!message) return null;
51
+ const permission = MISSING_PERMISSION.exec(message)?.[1];
52
+ return permission && PERMISSIONS.has(permission) ? `permission:${permission}` : null;
53
+ }
54
+
55
+ /** Classify a parsed `ErrorEnvelope`-shaped body. Unknown shapes still get a code. */
56
+ export function httpRejectionFactsFromEnvelope(
57
+ envelope: unknown,
58
+ fallbackCode: string,
59
+ ): HttpRejectionFacts {
60
+ const error =
61
+ envelope && typeof envelope === "object" && "error" in envelope
62
+ ? (envelope as { error?: unknown }).error
63
+ : undefined;
64
+ const record = error && typeof error === "object" ? (error as Record<string, unknown>) : {};
65
+ const code =
66
+ typeof record.code === "string" && ERROR_CODES.has(record.code) ? record.code : fallbackCode;
67
+ const message = typeof record.message === "string" ? record.message : undefined;
68
+ const details =
69
+ record.details && typeof record.details === "object"
70
+ ? (record.details as Record<string, unknown>)
71
+ : undefined;
72
+ const detailCode =
73
+ typeof details?.code === "string" && DETAIL_CODE.test(details.code) ? details.code : null;
74
+ const controlCode =
75
+ typeof details?.controlFailureCode === "string" && CONTROL_CODE.test(details.controlFailureCode)
76
+ ? `control:${details.controlFailureCode}`
77
+ : null;
78
+ return {
79
+ code,
80
+ reason:
81
+ rejectionReasonFromMessage(message) ??
82
+ detailCode ??
83
+ controlCode ??
84
+ UNCLASSIFIED_REJECTION_REASON,
85
+ ...(message ? { fingerprint: rejectionFingerprint(message) } : {}),
86
+ };
87
+ }
88
+
89
+ /** Classify a thrown error that escaped the central error handler. */
90
+ export function httpRejectionFactsFromError(
91
+ error: unknown,
92
+ fallbackCode: string,
93
+ ): HttpRejectionFacts {
94
+ const message = error instanceof Error ? error.message : undefined;
95
+ return {
96
+ code: fallbackCode,
97
+ reason: rejectionReasonFromMessage(message) ?? UNCLASSIFIED_REJECTION_REASON,
98
+ ...(message ? { fingerprint: rejectionFingerprint(message) } : {}),
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Read a small JSON error body without consuming the response that is sent to
104
+ * the client. Streams, non-JSON bodies, and large bodies are never read.
105
+ */
106
+ export async function readRejectionEnvelope(response: Response): Promise<unknown> {
107
+ if (!response.body) return undefined;
108
+ if (!/^application\/json(?:;|$)/i.test(response.headers.get("content-type") ?? "")) {
109
+ return undefined;
110
+ }
111
+ const length = Number(response.headers.get("content-length") ?? "0");
112
+ if (Number.isFinite(length) && length > MAX_ENVELOPE_BYTES) return undefined;
113
+ try {
114
+ const text = await response.clone().text();
115
+ if (text.length > MAX_ENVELOPE_BYTES) return undefined;
116
+ return JSON.parse(text) as unknown;
117
+ } catch {
118
+ return undefined;
119
+ }
120
+ }
@@ -0,0 +1,127 @@
1
+ import type { Context } from "hono";
2
+ import type { z } from "zod";
3
+ import { ZodError } from "zod";
4
+
5
+ import { ApiHttpError } from "./api-error";
6
+
7
+ /**
8
+ * Validation failures produced by parsing a client-supplied request body.
9
+ *
10
+ * The same `ZodError` instance is rethrown so route-local handlers that
11
+ * already map `instanceof ZodError` keep their exact behavior; only the
12
+ * central `app.onError` consults this set to answer a client error instead of
13
+ * a 500. A ZodError that is NOT registered here (for example, a server-side
14
+ * response projection that no longer matches its contract) remains a genuine
15
+ * server fault.
16
+ */
17
+ const requestBodyValidationErrors = new WeakSet<ZodError>();
18
+ /** Malformed-JSON failures raised by reading a client request body. */
19
+ const requestBodyJsonErrors = new WeakSet<SyntaxError>();
20
+
21
+ const MAX_REPORTED_ISSUES = 10;
22
+
23
+ /** Parse an already-read request body against its public request schema. */
24
+ export function parseRequestBody<Schema extends z.ZodType>(
25
+ schema: Schema,
26
+ body: unknown,
27
+ ): z.output<Schema> {
28
+ const parsed = schema.safeParse(body);
29
+ if (parsed.success) return parsed.data;
30
+ requestBodyValidationErrors.add(parsed.error);
31
+ throw parsed.error;
32
+ }
33
+
34
+ /**
35
+ * Read the request body as JSON. Malformed JSON is a client error (400), never
36
+ * an unhandled `SyntaxError` 500. Other failures (body limit, aborted stream)
37
+ * keep their own mapping.
38
+ */
39
+ export async function readRequestJson(c: Context): Promise<unknown> {
40
+ try {
41
+ return await c.req.json();
42
+ } catch (error) {
43
+ if (error instanceof SyntaxError) {
44
+ throw new ApiHttpError(400, {
45
+ code: "validation_failed",
46
+ message: "Request body must be valid JSON.",
47
+ retryable: false,
48
+ details: { code: "invalid_json" },
49
+ });
50
+ }
51
+ throw error;
52
+ }
53
+ }
54
+
55
+ /** Read and validate a JSON request body in one step. */
56
+ export async function parseRequestJson<Schema extends z.ZodType>(
57
+ c: Context,
58
+ schema: Schema,
59
+ ): Promise<z.output<Schema>> {
60
+ return parseRequestBody(schema, await readRequestJson(c));
61
+ }
62
+
63
+ /**
64
+ * Tag malformed-JSON failures of `c.req.json()` for this request. The same
65
+ * `SyntaxError` instance is rethrown so routes that already catch it keep
66
+ * their own mapping; an uncaught one becomes a 400 in `app.onError` instead of
67
+ * a 500. A `SyntaxError` from parsing stored or upstream data is never tagged.
68
+ */
69
+ export function tagRequestJsonParseErrors(c: Context): void {
70
+ const original = c.req.json.bind(c.req);
71
+ const tagged = async () => {
72
+ try {
73
+ return await original();
74
+ } catch (error) {
75
+ if (error instanceof SyntaxError) requestBodyJsonErrors.add(error);
76
+ throw error;
77
+ }
78
+ };
79
+ (c.req as { json: typeof tagged }).json = tagged;
80
+ }
81
+
82
+ export function isRequestBodyValidationError(error: unknown): error is ZodError | SyntaxError {
83
+ return requestBodyInputError(error) !== null;
84
+ }
85
+
86
+ /** Walk a bounded cause chain for a tagged client-input failure. */
87
+ function requestBodyInputError(error: unknown): ZodError | SyntaxError | null {
88
+ let current: unknown = error;
89
+ for (let depth = 0; depth < 5 && current instanceof Error; depth += 1) {
90
+ if (current instanceof ZodError && requestBodyValidationErrors.has(current)) return current;
91
+ if (current instanceof SyntaxError && requestBodyJsonErrors.has(current)) return current;
92
+ current = current.cause;
93
+ }
94
+ return null;
95
+ }
96
+
97
+ /**
98
+ * Public 400 for a registered request-body validation failure, naming every
99
+ * offending field (bounded) so API callers can correct the request.
100
+ */
101
+ export function requestBodyValidationHttpError(error: unknown): ApiHttpError | null {
102
+ const inputError = requestBodyInputError(error);
103
+ if (!inputError) return null;
104
+ if (inputError instanceof SyntaxError) {
105
+ return new ApiHttpError(400, {
106
+ code: "validation_failed",
107
+ message: "Request body must be valid JSON.",
108
+ retryable: false,
109
+ details: { code: "invalid_json" },
110
+ });
111
+ }
112
+ const zodError = inputError;
113
+ const issues = zodError.issues.slice(0, MAX_REPORTED_ISSUES).map((issue) => ({
114
+ path: issue.path.map(String).join("."),
115
+ message: issue.message,
116
+ }));
117
+ const summary = issues.map((issue) => `${issue.path || "request"}: ${issue.message}`).join("; ");
118
+ const omittedIssueCount = Math.max(0, zodError.issues.length - issues.length);
119
+ return new ApiHttpError(400, {
120
+ code: "validation_failed",
121
+ message: `Invalid request body: ${summary}${
122
+ omittedIssueCount > 0 ? `; and ${omittedIssueCount} more` : ""
123
+ }`,
124
+ retryable: false,
125
+ details: { code: "invalid_request_body", issues, omittedIssueCount },
126
+ });
127
+ }
@@ -1,11 +1,28 @@
1
+ import { BlockList, isIP } from "node:net";
2
+ import { trustedProxyCidrEntries, type Settings } from "@opengeni/config";
1
3
  import type { Context } from "hono";
2
4
 
3
5
  const TRANSPORT_PEER_ADDRESS_BINDING = "opengeniTransportPeerAddress";
4
6
 
7
+ /** Returned when the runtime supplied no transport peer (for example an
8
+ * in-process test request). Callers treat it as one shared bucket. */
9
+ export const UNKNOWN_REQUEST_SOURCE_ADDRESS = "unknown";
10
+
11
+ /**
12
+ * App-owned request header carrying the server-resolved client address into
13
+ * Better Auth, which reads client addresses only from request headers. It is
14
+ * removed from every inbound request and re-stamped on managed-auth routes, so
15
+ * a caller-supplied value never reaches the auth rate limiter or session IP.
16
+ */
17
+ export const TRUSTED_CLIENT_ADDRESS_HEADER = "x-opengeni-trusted-client-address";
18
+
5
19
  type ApiRequestBindings = {
6
20
  [TRANSPORT_PEER_ADDRESS_BINDING]?: string | null;
7
21
  };
8
22
 
23
+ /** The deployment's declared proxy chain (`OPENGENI_API_TRUSTED_PROXY_*`). */
24
+ export type RequestSourceTrust = Pick<Settings, "apiTrustedProxyHops" | "apiTrustedProxyCidrs">;
25
+
9
26
  export function apiRequestBindingsForTransportPeer(
10
27
  address: string | null | undefined,
11
28
  ): ApiRequestBindings {
@@ -15,23 +32,147 @@ export function apiRequestBindingsForTransportPeer(
15
32
  /**
16
33
  * Resolve a quota/audit source from the server-owned transport peer. Forwarded
17
34
  * values are considered only when the operator declares an exact trusted proxy
18
- * hop count; the chain is then walked from the server side so caller-prepended
19
- * values cannot replace the address inserted by the trusted edge.
35
+ * hop count (`OPENGENI_API_TRUSTED_PROXY_HOPS`) and, when
36
+ * `OPENGENI_API_TRUSTED_PROXY_CIDRS` is set, only for a transport peer inside
37
+ * those ranges. Each trusted proxy must append the address of the peer that
38
+ * connected to it, or overwrite the header with the original client address;
39
+ * the chain is then walked from the server side so caller-prepended values
40
+ * cannot replace the address observed by the trusted edge. A missing, short,
41
+ * or malformed chain falls back to the transport peer.
20
42
  */
21
- export function trustedRequestSourceAddress(c: Context, trustedProxyHops: number): string {
43
+ export function trustedRequestSourceAddress(c: Context, trust: RequestSourceTrust): string {
22
44
  const bindings = c.env as ApiRequestBindings | undefined;
23
- const peer = normalizedAddress(bindings?.[TRANSPORT_PEER_ADDRESS_BINDING]) ?? "unknown";
24
- if (trustedProxyHops <= 0 || peer === "unknown") return peer;
25
-
26
- const forwarded = (c.req.header("x-forwarded-for") ?? "")
27
- .split(",")
28
- .map(normalizedAddress)
29
- .filter((value): value is string => value !== null);
45
+ const peer =
46
+ normalizedAddress(bindings?.[TRANSPORT_PEER_ADDRESS_BINDING]) ?? UNKNOWN_REQUEST_SOURCE_ADDRESS;
47
+ const trustedProxyHops = trust.apiTrustedProxyHops;
48
+ if (
49
+ !Number.isInteger(trustedProxyHops) ||
50
+ trustedProxyHops <= 0 ||
51
+ peer === UNKNOWN_REQUEST_SOURCE_ADDRESS ||
52
+ !peerIsTrustedProxy(peer, trust.apiTrustedProxyCidrs)
53
+ ) {
54
+ return peer;
55
+ }
56
+ const header = c.req.header("x-forwarded-for");
57
+ if (!header) return peer;
58
+ // Entries are never filtered: dropping a malformed value would shift the
59
+ // server-side position and could promote a caller-prepended value.
60
+ const forwarded = header.split(",");
30
61
  const sourceIndex = forwarded.length - trustedProxyHops;
31
- return sourceIndex >= 0 ? forwarded[sourceIndex]! : peer;
62
+ if (sourceIndex < 0) return peer;
63
+ return normalizedAddress(forwarded[sourceIndex]) ?? peer;
64
+ }
65
+
66
+ /**
67
+ * Rate-limit key for a trusted source address. An IPv6 address keys on its
68
+ * /64, the block one subscriber normally holds and the granularity Better
69
+ * Auth applies to the same stamped address, so rotating addresses inside one
70
+ * /64 cannot mint fresh buckets. An IPv4-mapped IPv6 address (how a
71
+ * dual-stack listener reports an IPv4 peer) keys on the embedded IPv4 address
72
+ * instead of collapsing every IPv4 client into one /64. Anything else,
73
+ * including {@link UNKNOWN_REQUEST_SOURCE_ADDRESS}, is returned unchanged.
74
+ */
75
+ export function requestSourceRateLimitKey(address: string): string {
76
+ if (isIP(address) !== 6) return address;
77
+ const groups = ipv6Groups(address);
78
+ if (!groups) return address;
79
+ if (groups.slice(0, 5).every((group) => group === 0) && groups[5] === 0xffff) {
80
+ const high = groups[6]!;
81
+ const low = groups[7]!;
82
+ return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`;
83
+ }
84
+ return `${groups
85
+ .slice(0, 4)
86
+ .map((group) => group.toString(16))
87
+ .join(":")}::/64`;
88
+ }
89
+
90
+ /** {@link trustedRequestSourceAddress} as an application rate-limit key. */
91
+ export function trustedRequestSourceRateLimitKey(c: Context, trust: RequestSourceTrust): string {
92
+ return requestSourceRateLimitKey(trustedRequestSourceAddress(c, trust));
32
93
  }
33
94
 
95
+ /**
96
+ * Replace any caller-supplied {@link TRUSTED_CLIENT_ADDRESS_HEADER}. When
97
+ * `stamp` is true the header is set to the trusted source address, so every
98
+ * Better Auth request derived from this request (direct handler calls,
99
+ * isolated provider requests, and `auth.api.*` calls built from its headers)
100
+ * keys rate limits and session records on the real client.
101
+ */
102
+ export function replaceTrustedClientAddressHeader(
103
+ c: Context,
104
+ trust: RequestSourceTrust,
105
+ stamp: boolean,
106
+ ): void {
107
+ const headers = c.req.raw.headers;
108
+ headers.delete(TRUSTED_CLIENT_ADDRESS_HEADER);
109
+ if (!stamp) return;
110
+ const address = trustedRequestSourceAddress(c, trust);
111
+ if (address !== UNKNOWN_REQUEST_SOURCE_ADDRESS) {
112
+ headers.set(TRUSTED_CLIENT_ADDRESS_HEADER, address);
113
+ }
114
+ }
115
+
116
+ const trustedProxyRanges = new Map<string, BlockList | null>();
117
+
118
+ function peerIsTrustedProxy(peer: string, cidrs: string): boolean {
119
+ let ranges = trustedProxyRanges.get(cidrs);
120
+ if (ranges === undefined) {
121
+ // Settings validation already rejected malformed entries at boot.
122
+ const entries = trustedProxyCidrEntries(cidrs);
123
+ ranges = entries.length === 0 ? null : new BlockList();
124
+ for (const entry of entries) ranges?.addSubnet(entry.address, entry.prefix, entry.family);
125
+ trustedProxyRanges.set(cidrs, ranges);
126
+ }
127
+ if (!ranges) return true;
128
+ return ranges.check(peer, isIP(peer) === 6 ? "ipv6" : "ipv4");
129
+ }
130
+
131
+ /**
132
+ * Canonical IP text, or null. Accepts the `ipv4:port` and `[ipv6]:port` forms
133
+ * some load balancers write into X-Forwarded-For, and drops IPv6 zone ids.
134
+ */
34
135
  function normalizedAddress(value: string | null | undefined): string | null {
35
- const normalized = value?.trim();
36
- return normalized ? normalized.slice(0, 128) : null;
136
+ let candidate = value?.trim();
137
+ if (!candidate || candidate.length > 64) return null;
138
+ const bracketed = /^\[([^\]]+)\](?::\d{1,5})?$/u.exec(candidate);
139
+ if (bracketed) {
140
+ candidate = bracketed[1]!;
141
+ } else {
142
+ const ipv4WithPort = /^(\d{1,3}(?:\.\d{1,3}){3}):\d{1,5}$/u.exec(candidate);
143
+ if (ipv4WithPort) candidate = ipv4WithPort[1]!;
144
+ }
145
+ const zone = candidate.indexOf("%");
146
+ if (zone >= 0) candidate = candidate.slice(0, zone);
147
+ const family = isIP(candidate);
148
+ if (family === 4) return candidate;
149
+ if (family === 6) return candidate.toLowerCase();
150
+ return null;
151
+ }
152
+
153
+ /** The eight 16-bit groups of a valid IPv6 address, or null. */
154
+ function ipv6Groups(address: string): number[] | null {
155
+ let text = address;
156
+ const tail: number[] = [];
157
+ const lastColon = text.lastIndexOf(":");
158
+ const last = text.slice(lastColon + 1);
159
+ if (last.includes(".")) {
160
+ const octets = last.split(".").map(Number);
161
+ if (octets.length !== 4 || octets.some((octet) => !Number.isInteger(octet))) return null;
162
+ tail.push((octets[0]! << 8) | octets[1]!, (octets[2]! << 8) | octets[3]!);
163
+ text = text.slice(0, lastColon + 1);
164
+ if (!text.endsWith("::")) text = text.slice(0, -1);
165
+ }
166
+ const halves = text.split("::");
167
+ if (halves.length > 2) return null;
168
+ const parse = (part: string): number[] =>
169
+ part ? part.split(":").map((group) => Number.parseInt(group, 16)) : [];
170
+ const head = parse(halves[0]!);
171
+ const rest = halves.length === 2 ? parse(halves[1]!) : [];
172
+ const fill = halves.length === 2 ? 8 - head.length - rest.length - tail.length : 0;
173
+ if (fill < 0) return null;
174
+ const groups = [...head, ...Array.from({ length: fill }, () => 0), ...rest, ...tail];
175
+ if (groups.length !== 8) return null;
176
+ if (groups.some((group) => !Number.isInteger(group) || group < 0 || group > 0xffff)) return null;
177
+ return groups;
37
178
  }
package/src/http/sse.ts CHANGED
@@ -5,8 +5,9 @@ import {
5
5
  } from "@opengeni/contracts";
6
6
  import {
7
7
  getWorkspaceInteractionRevisionState,
8
- listSessionEvents,
8
+ listSessionEventPage,
9
9
  listWorkspaceControlEvents,
10
+ readSessionEventStorageGapEnd,
10
11
  type Database,
11
12
  } from "@opengeni/db";
12
13
  import {
@@ -21,6 +22,7 @@ import type { Observability } from "@opengeni/observability";
21
22
  import { MANAGED_AUTH_ACTOR_EPOCH_HEADER } from "@opengeni/core/managed-auth-session-sets";
22
23
 
23
24
  const SESSION_REPLAY_PAGE_SIZE = 100;
25
+ const SESSION_REPLAY_PAGE_MAX_BYTES = 8 * 1024 * 1024;
24
26
  const WORKSPACE_CONTROL_REPLAY_PAGE_SIZE = 100;
25
27
  export const SSE_QUEUED_FRAME_MAX_COUNT = 1;
26
28
  export const SSE_WRITE_STALL_TIMEOUT_MS = 30_000;
@@ -149,12 +151,12 @@ export function createByteBoundedSseStream(
149
151
  return {
150
152
  stream,
151
153
  write: async (frame) => {
152
- const chunk = encoder.encode(frame);
153
154
  for (;;) {
154
155
  if (stopped) return false;
155
156
  const desired = controller.desiredSize;
156
157
  if (desired === null) return false;
157
158
  if (desired >= 1 && queuedFrames === 0) {
159
+ const chunk = encoder.encode(frame);
158
160
  controller.enqueue(chunk);
159
161
  queuedFrames = 1;
160
162
  queuedBytes = chunk.byteLength;
@@ -267,6 +269,32 @@ export function createLatestWinsDelivery<T extends { sequence: number }>(
267
269
  };
268
270
  }
269
271
 
272
+ /**
273
+ * Durable coverage for each projected frame. Storage may omit sequences on
274
+ * purpose (an archived session keeps only its readable timeline), and clients
275
+ * treat an uncovered jump as a gap to backfill. Within a page the next stored
276
+ * row bounds the gap; after the last row, `gapEnd` (when given) reads the
277
+ * race-free bound from storage.
278
+ */
279
+ export async function coverStorageGaps(
280
+ projection: { events: SessionEvent[]; coveredThroughBySequence: ReadonlyMap<number, number> },
281
+ gapEnd: ((through: number) => Promise<number>) | null,
282
+ ): Promise<Map<number, number>> {
283
+ const coverage = new Map<number, number>();
284
+ const events = projection.events;
285
+ for (const [index, event] of events.entries()) {
286
+ const own = projection.coveredThroughBySequence.get(event.sequence) ?? event.sequence;
287
+ const next = events[index + 1];
288
+ coverage.set(event.sequence, next ? Math.max(own, next.sequence - 1) : own);
289
+ }
290
+ const last = events.at(-1);
291
+ if (last && gapEnd) {
292
+ const own = coverage.get(last.sequence)!;
293
+ coverage.set(last.sequence, Math.max(own, await gapEnd(own)));
294
+ }
295
+ return coverage;
296
+ }
297
+
270
298
  export async function sseSessionStream(
271
299
  db: Database,
272
300
  bus: EventBus,
@@ -277,18 +305,19 @@ export async function sseSessionStream(
277
305
  options: SessionSseDeliveryOptions = {},
278
306
  ): Promise<Response> {
279
307
  if (isHttp1BrowserBatch(options)) {
280
- const events = await listSessionEvents(db, workspaceId, sessionId, {
308
+ const { events } = await listSessionEventPage(db, workspaceId, sessionId, {
281
309
  after,
282
310
  limit: SESSION_REPLAY_PAGE_SIZE,
311
+ maxBytes: options.finiteResponseMaxBytes ?? HTTP1_BROWSER_SSE_BATCH_MAX_BYTES,
283
312
  });
284
313
  await options.reauthorize?.();
285
314
  const compactProjection = coalesceSessionEventDeltasWithCoverage(events);
315
+ const coverage = await coverStorageGaps(compactProjection, async (through) =>
316
+ readSessionEventStorageGapEnd(db, workspaceId, sessionId, through),
317
+ );
286
318
  return finiteSseBatchResponse(
287
319
  compactProjection.events.map((event) =>
288
- formatSessionEventSse(
289
- event,
290
- compactProjection.coveredThroughBySequence.get(event.sequence) ?? event.sequence,
291
- ),
320
+ formatSessionEventSse(event, coverage.get(event.sequence) ?? event.sequence),
292
321
  ),
293
322
  options,
294
323
  );
@@ -353,10 +382,12 @@ export async function sseSessionStream(
353
382
  targetSequence === undefined
354
383
  ? SESSION_REPLAY_PAGE_SIZE
355
384
  : Math.min(SESSION_REPLAY_PAGE_SIZE, targetSequence - lastSent);
356
- const page = await listSessionEvents(db, workspaceId, sessionId, {
385
+ const replayPage = await listSessionEventPage(db, workspaceId, sessionId, {
357
386
  after: lastSent,
358
387
  limit,
388
+ maxBytes: SESSION_REPLAY_PAGE_MAX_BYTES,
359
389
  });
390
+ const page = replayPage.events;
360
391
  const eligible =
361
392
  targetSequence === undefined
362
393
  ? page
@@ -372,9 +403,16 @@ export async function sseSessionStream(
372
403
  // a long answer cannot create thousands of React renders and starve
373
404
  // command acknowledgements behind its own token stream.
374
405
  const compactProjection = coalesceSessionEventDeltasWithCoverage(eligible);
406
+ // Live delivery stops exactly at a published sequence; replay pages also
407
+ // cover what storage intentionally omits after their last event.
408
+ const coverage = await coverStorageGaps(
409
+ compactProjection,
410
+ targetSequence === undefined
411
+ ? async (through) => readSessionEventStorageGapEnd(db, workspaceId, sessionId, through)
412
+ : null,
413
+ );
375
414
  for (const projected of compactProjection.events) {
376
- const coveredThrough =
377
- compactProjection.coveredThroughBySequence.get(projected.sequence) ?? projected.sequence;
415
+ const coveredThrough = coverage.get(projected.sequence) ?? projected.sequence;
378
416
  await writeFrame(formatSessionEventSse(projected, coveredThrough));
379
417
  lastSent = coveredThrough;
380
418
  }
@@ -382,8 +420,10 @@ export async function sseSessionStream(
382
420
  throw new Error(`Session event replay made no progress after sequence ${lastSent}`);
383
421
  }
384
422
  if (targetSequence !== undefined && lastSent >= targetSequence) return;
423
+ if (targetSequence === undefined && !replayPage.hasMore) return;
385
424
  // A byte-selected page may contain fewer rows than requested, especially
386
- // when one large message travels alone. Only an empty read proves EOF.
425
+ // when one large message travels alone. Use explicit continuation rather
426
+ // than page length; live publication covers appends after the snapshot.
387
427
  }
388
428
  };
389
429
  let durableDeliveryTail = Promise.resolve();
@@ -448,17 +488,28 @@ export async function sseSessionStream(
448
488
  stopReconnectObservation = durableFanout.subscribeRecovery(scheduleReconnectReconciliation);
449
489
 
450
490
  void (async () => {
451
- const release = await bus.subscribe(workspaceId, sessionId, (events) => {
452
- if (bootstrapping) {
453
- for (const event of events) {
454
- if (!newestBuffered || event.sequence > newestBuffered.sequence) {
455
- newestBuffered = event;
491
+ const release = await bus.subscribe(
492
+ workspaceId,
493
+ sessionId,
494
+ (events) => {
495
+ if (bootstrapping) {
496
+ for (const event of events) {
497
+ if (!newestBuffered || event.sequence > newestBuffered.sequence) {
498
+ newestBuffered = event;
499
+ }
456
500
  }
501
+ } else {
502
+ delivery?.publish(events);
457
503
  }
458
- } else {
459
- delivery?.publish(events);
460
- }
461
- });
504
+ },
505
+ {
506
+ // Live fanout stopped while this stream still held it. Heartbeats alone
507
+ // would keep a silently stale timeline open; fail retryably so the
508
+ // client reconnects and replays from the durable Postgres cursor.
509
+ onTerminated: (error) =>
510
+ fail(retryableSseFailure("session live fanout subscription ended", error)),
511
+ },
512
+ );
462
513
  if (channel.stopped()) {
463
514
  release();
464
515
  return;
@@ -632,13 +683,22 @@ export async function sseWorkspaceControlStream(
632
683
  delivery = createLatestWinsDelivery(send, fail);
633
684
 
634
685
  void (async () => {
635
- const release = await bus.subscribeWorkspaceControl(workspaceId, (event) => {
636
- if (bootstrapping) {
637
- if (!newestBuffered || event.sequence > newestBuffered.sequence) newestBuffered = event;
638
- } else {
639
- delivery?.publish([event]);
640
- }
641
- });
686
+ const release = await bus.subscribeWorkspaceControl(
687
+ workspaceId,
688
+ (event) => {
689
+ if (bootstrapping) {
690
+ if (!newestBuffered || event.sequence > newestBuffered.sequence) newestBuffered = event;
691
+ } else {
692
+ delivery?.publish([event]);
693
+ }
694
+ },
695
+ {
696
+ // Same contract as session SSE: a dead live subscription must reconnect
697
+ // and replay the durable control cursor, not idle on heartbeats.
698
+ onTerminated: (error) =>
699
+ fail(retryableSseFailure("workspace control live subscription ended", error)),
700
+ },
701
+ );
642
702
  if (channel.stopped()) {
643
703
  release();
644
704
  return;
@@ -678,7 +738,7 @@ export type WorkspaceInteractionSseOptions = SseDeliveryOptions & {
678
738
 
679
739
  /**
680
740
  * One HTTP connection for the two workspace-wide invalidation domains used by
681
- * every visible OpenGeni surface. Keeping these as separate HTTP/1 streams
741
+ * every visible Opengeni surface. Keeping these as separate HTTP/1 streams
682
742
  * consumes all six per-origin browser connections with only two windows and
683
743
  * starves ordinary mutations/terminal grants. The durable cursors remain
684
744
  * independent; this function only multiplexes their already-bounded SSE frames.