@opengeni/core 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 (257) hide show
  1. package/README.md +4 -4
  2. package/dist/access/index.d.ts +131 -7
  3. package/dist/access/service-initiator.d.ts +3 -0
  4. package/dist/application/archived-session-imports.d.ts +6 -0
  5. package/dist/application/composer-submit.d.ts +1 -1
  6. package/dist/application/connect-operation.d.ts +6 -0
  7. package/dist/application/external-workspace-members.d.ts +13 -1
  8. package/dist/application/modal-native-live-declaration-join.d.ts +37 -0
  9. package/dist/application/modal-native-original-configuration.d.ts +79 -0
  10. package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
  11. package/dist/application/new-session-drafts.d.ts +29 -2
  12. package/dist/application/organization-privacy.d.ts +57 -0
  13. package/dist/application/prepared-mcp-connection.d.ts +31 -0
  14. package/dist/application/sandbox-recovery.d.ts +58 -0
  15. package/dist/application/session-commands.d.ts +3 -0
  16. package/dist/application/session-tenancy.d.ts +13 -4
  17. package/dist/billing/agent-run-admission.d.ts +21 -0
  18. package/dist/billing/limits.d.ts +27 -1
  19. package/dist/billing/model-call-admission.d.ts +34 -0
  20. package/dist/billing/model-usage-settlement.d.ts +112 -0
  21. package/dist/canonical-human-identities.js +1 -1
  22. package/dist/{chunk-VCZFFEPB.js → chunk-7T2SZ7BI.js} +16 -2
  23. package/dist/chunk-7T2SZ7BI.js.map +1 -0
  24. package/dist/{chunk-ZYCNLYGH.js → chunk-OMJFIIWK.js} +178 -12
  25. package/dist/chunk-OMJFIIWK.js.map +1 -0
  26. package/dist/{chunk-H7X52RI2.js → chunk-QI2M2B2K.js} +83 -8
  27. package/dist/chunk-QI2M2B2K.js.map +1 -0
  28. package/dist/{chunk-OF65T3PM.js → chunk-TTWTXQB2.js} +94 -6
  29. package/dist/chunk-TTWTXQB2.js.map +1 -0
  30. package/dist/codex-model-availability.d.ts +55 -0
  31. package/dist/default-session-model.d.ts +192 -0
  32. package/dist/dependencies.d.ts +41 -7
  33. package/dist/developer-setup-credential-ceiling.d.ts +10 -0
  34. package/dist/domain/agent-config-resolution.d.ts +49 -0
  35. package/dist/domain/automations.d.ts +8 -1
  36. package/dist/domain/capabilities.d.ts +112 -8
  37. package/dist/domain/connection-account-selection-error.d.ts +8 -0
  38. package/dist/domain/connector-tool-permissions.d.ts +62 -0
  39. package/dist/domain/direct-model-provider.d.ts +3 -0
  40. package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
  41. package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
  42. package/dist/domain/editable-artifacts/ports.d.ts +10 -0
  43. package/dist/domain/editable-artifacts/service.d.ts +3 -1
  44. package/dist/domain/editable-artifacts/types.d.ts +1 -1
  45. package/dist/domain/encryption-key.d.ts +12 -0
  46. package/dist/domain/insights-timing.d.ts +16 -0
  47. package/dist/domain/insights-usage.d.ts +27 -0
  48. package/dist/domain/insights.d.ts +21 -3
  49. package/dist/domain/knowledge-files.d.ts +1 -1
  50. package/dist/domain/knowledge-preparation.d.ts +10 -0
  51. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  52. package/dist/domain/knowledge-search.d.ts +15 -76
  53. package/dist/domain/mcp-account-bindings.d.ts +17 -0
  54. package/dist/domain/mcp-account-routes.d.ts +21 -0
  55. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  56. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  57. package/dist/domain/personal-connection-delegations.d.ts +73 -12
  58. package/dist/domain/pr-review.d.ts +121 -6
  59. package/dist/domain/realtime-voice-billing.d.ts +103 -0
  60. package/dist/domain/resources.d.ts +17 -1
  61. package/dist/domain/scheduled-task-access.d.ts +265 -0
  62. package/dist/domain/scheduled-task-account-audit.d.ts +27 -0
  63. package/dist/domain/scheduled-task-audience.d.ts +70 -0
  64. package/dist/domain/scheduled-tasks.d.ts +61 -18
  65. package/dist/domain/session-connection-accounts.d.ts +53 -0
  66. package/dist/domain/session-file-access.d.ts +4 -0
  67. package/dist/domain/session-native-connection-replacement.d.ts +14 -0
  68. package/dist/domain/session-start-timing.d.ts +7 -0
  69. package/dist/domain/session-tool-policy.d.ts +102 -4
  70. package/dist/domain/sessions.d.ts +179 -15
  71. package/dist/domain/site-audience.d.ts +52 -0
  72. package/dist/domain/skills.d.ts +29 -1
  73. package/dist/domain/slack-bot.d.ts +46 -1
  74. package/dist/domain/slack-publication-secret-safety.d.ts +1 -1
  75. package/dist/domain/vaults/index.d.ts +7 -0
  76. package/dist/domain/vaults/mapping.d.ts +25 -0
  77. package/dist/domain/vaults/provider-registry.d.ts +72 -0
  78. package/dist/domain/vaults/provider-sync.d.ts +176 -0
  79. package/dist/domain/vaults/provider-writes.d.ts +57 -0
  80. package/dist/domain/vaults/secret-resolver.d.ts +34 -0
  81. package/dist/domain/vaults/types.d.ts +24 -0
  82. package/dist/domain/vaults/vault-service.d.ts +35 -0
  83. package/dist/domain/voice-input-billing.d.ts +77 -0
  84. package/dist/domain/web-search-billing.d.ts +51 -0
  85. package/dist/domain/workspace-credits.d.ts +7 -0
  86. package/dist/editable-artifact-live/application.d.ts +2 -1
  87. package/dist/editable-artifact-live/server.d.ts +5 -1
  88. package/dist/editable-artifact-live/ticket.d.ts +2 -1
  89. package/dist/editable-artifact-live/types.d.ts +9 -0
  90. package/dist/editable-artifact-live.js +2 -2
  91. package/dist/editable-artifacts.js +10 -2
  92. package/dist/goal-admission.d.ts +627 -0
  93. package/dist/host-session-usage-limit.d.ts +15 -0
  94. package/dist/index.d.ts +43 -8
  95. package/dist/index.js +22682 -11743
  96. package/dist/index.js.map +1 -1
  97. package/dist/managed-session.d.ts +110 -7
  98. package/dist/model-call.d.ts +58 -0
  99. package/dist/model-catalog.d.ts +29 -3
  100. package/dist/model-provider-credentials.d.ts +43 -0
  101. package/dist/product-usage-metrics.d.ts +30 -0
  102. package/dist/rigs/index.d.ts +1 -13
  103. package/dist/sandbox/fleet.d.ts +58 -4
  104. package/dist/sandbox/routing.d.ts +42 -2
  105. package/dist/sandbox/runtime-settings.d.ts +27 -17
  106. package/dist/session-authorization.d.ts +10 -0
  107. package/dist/session-usage-limit-metrics.d.ts +60 -0
  108. package/dist/session-usage-limits.d.ts +89 -0
  109. package/dist/site-agent-clamp.d.ts +71 -0
  110. package/dist/site-session-origin.d.ts +39 -2
  111. package/dist/transcription.d.ts +120 -3
  112. package/dist/turn-surface.d.ts +20 -0
  113. package/dist/user-presence.d.ts +32 -0
  114. package/dist/workflow-wake-contract.d.ts +13 -1
  115. package/package.json +15 -22
  116. package/src/access/index.ts +961 -38
  117. package/src/access/service-initiator.ts +50 -0
  118. package/src/application/api-integration-servers.ts +9 -0
  119. package/src/application/archived-session-imports.ts +161 -0
  120. package/src/application/composer-submit.ts +3 -2
  121. package/src/application/connect-authority.ts +3 -3
  122. package/src/application/connect-operation.ts +37 -3
  123. package/src/application/external-continuation.ts +20 -3
  124. package/src/application/external-link-work-admission.ts +3 -0
  125. package/src/application/external-workspace-members.ts +51 -3
  126. package/src/application/modal-native-live-declaration-join.ts +196 -0
  127. package/src/application/modal-native-original-configuration.ts +261 -0
  128. package/src/application/modal-native-worker-host-transport.ts +263 -0
  129. package/src/application/new-session-drafts.ts +172 -55
  130. package/src/application/organization-privacy.ts +161 -0
  131. package/src/application/prepared-mcp-connection.ts +133 -0
  132. package/src/application/sandbox-recovery.ts +78 -0
  133. package/src/application/session-commands.ts +73 -1
  134. package/src/application/session-mcp-credential-rotation.ts +61 -18
  135. package/src/application/session-tenancy.ts +262 -44
  136. package/src/application/user-resource-grants.ts +1 -29
  137. package/src/billing/agent-run-admission.ts +117 -0
  138. package/src/billing/limits.ts +138 -14
  139. package/src/billing/model-call-admission.ts +118 -0
  140. package/src/billing/model-usage-settlement.ts +472 -0
  141. package/src/codex-model-availability.ts +264 -0
  142. package/src/default-session-model.ts +692 -0
  143. package/src/dependencies.ts +48 -6
  144. package/src/developer-setup-credential-ceiling.ts +60 -0
  145. package/src/domain/agent-config-resolution.ts +148 -0
  146. package/src/domain/automations.ts +76 -2
  147. package/src/domain/capabilities.ts +830 -181
  148. package/src/domain/connection-account-selection-error.ts +19 -0
  149. package/src/domain/connector-tool-permissions.ts +572 -0
  150. package/src/domain/direct-model-provider.ts +62 -0
  151. package/src/domain/editable-artifacts/agent-application.ts +44 -6
  152. package/src/domain/editable-artifacts/durable-export.ts +100 -5
  153. package/src/domain/editable-artifacts/in-memory.ts +6 -0
  154. package/src/domain/editable-artifacts/ports.ts +11 -0
  155. package/src/domain/editable-artifacts/service.ts +20 -0
  156. package/src/domain/editable-artifacts/types.ts +1 -1
  157. package/src/domain/encryption-key.ts +26 -0
  158. package/src/domain/file-owner.ts +16 -1
  159. package/src/domain/github-action-policies.ts +1 -1
  160. package/src/domain/github-skill-source.ts +3 -1
  161. package/src/domain/insights-timing.ts +95 -0
  162. package/src/domain/insights-usage.ts +67 -0
  163. package/src/domain/insights.ts +167 -43
  164. package/src/domain/knowledge-files.ts +10 -3
  165. package/src/domain/knowledge-messages.ts +1 -0
  166. package/src/domain/knowledge-preparation.test.ts +276 -0
  167. package/src/domain/knowledge-preparation.ts +147 -0
  168. package/src/domain/knowledge-search.test.ts +617 -5
  169. package/src/domain/knowledge-search.ts +240 -23
  170. package/src/domain/mcp-account-bindings.ts +169 -0
  171. package/src/domain/mcp-account-routes.ts +152 -0
  172. package/src/domain/native-mcp-connection-admission.ts +14 -0
  173. package/src/domain/organization-integration-catalog.ts +88 -0
  174. package/src/domain/personal-connection-delegations.ts +399 -257
  175. package/src/domain/pr-review.ts +24 -8
  176. package/src/domain/realtime-voice-billing.ts +425 -0
  177. package/src/domain/resources.ts +50 -4
  178. package/src/domain/scheduled-task-access.ts +1089 -0
  179. package/src/domain/scheduled-task-account-audit.ts +90 -0
  180. package/src/domain/scheduled-task-audience.ts +249 -0
  181. package/src/domain/scheduled-tasks.ts +1313 -374
  182. package/src/domain/session-connection-accounts.ts +239 -0
  183. package/src/domain/session-file-access.ts +47 -0
  184. package/src/domain/session-native-connection-replacement.ts +64 -0
  185. package/src/domain/session-start-timing.ts +60 -0
  186. package/src/domain/session-tool-policy.ts +489 -14
  187. package/src/domain/sessions.ts +2254 -717
  188. package/src/domain/site-audience.ts +130 -0
  189. package/src/domain/skills.ts +9 -0
  190. package/src/domain/slack-bot.ts +141 -4
  191. package/src/domain/slack-publication-secret-safety.ts +1 -1
  192. package/src/domain/vaults/index.ts +19 -0
  193. package/src/domain/vaults/mapping.ts +190 -0
  194. package/src/domain/vaults/provider-registry.ts +201 -0
  195. package/src/domain/vaults/provider-sync.ts +531 -0
  196. package/src/domain/vaults/provider-writes.ts +288 -0
  197. package/src/domain/vaults/secret-resolver.ts +464 -0
  198. package/src/domain/vaults/types.ts +26 -0
  199. package/src/domain/vaults/vault-service.ts +1316 -0
  200. package/src/domain/voice-input-billing.ts +330 -0
  201. package/src/domain/web-search-billing.ts +169 -0
  202. package/src/domain/workspace-credits.ts +13 -0
  203. package/src/editable-artifact-live/application.ts +8 -1
  204. package/src/editable-artifact-live/server.ts +154 -6
  205. package/src/editable-artifact-live/ticket.ts +86 -1
  206. package/src/editable-artifact-live/types.ts +10 -0
  207. package/src/goal-admission.ts +220 -0
  208. package/src/host-session-usage-limit.ts +25 -0
  209. package/src/index.ts +75 -11
  210. package/src/managed-session.ts +137 -6
  211. package/src/model-call.ts +79 -0
  212. package/src/model-catalog.ts +345 -91
  213. package/src/model-provider-credentials.ts +394 -0
  214. package/src/product-usage-metrics.ts +101 -0
  215. package/src/rigs/index.ts +23 -111
  216. package/src/sandbox/fleet.ts +253 -28
  217. package/src/sandbox/routing.ts +204 -43
  218. package/src/sandbox/runtime-settings.ts +159 -85
  219. package/src/session-authorization.ts +172 -23
  220. package/src/session-usage-limit-metrics.ts +95 -0
  221. package/src/session-usage-limits.ts +415 -0
  222. package/src/site-agent-clamp.ts +226 -0
  223. package/src/site-session-origin.ts +84 -5
  224. package/src/transcription.ts +123 -4
  225. package/src/turn-surface.ts +47 -0
  226. package/src/user-presence.ts +134 -0
  227. package/src/workflow-wake-contract.ts +14 -1
  228. package/dist/application/host-mcp-owner.d.ts +0 -6
  229. package/dist/application/host-mcp-resolvers.d.ts +0 -13
  230. package/dist/chunk-5WZYFL45.js +0 -266
  231. package/dist/chunk-5WZYFL45.js.map +0 -1
  232. package/dist/chunk-H7X52RI2.js.map +0 -1
  233. package/dist/chunk-OF65T3PM.js.map +0 -1
  234. package/dist/chunk-VCZFFEPB.js.map +0 -1
  235. package/dist/chunk-ZYCNLYGH.js.map +0 -1
  236. package/dist/domain/connection-authority.d.ts +0 -97
  237. package/dist/domain/connection-authority.js +0 -169
  238. package/dist/domain/connection-authority.js.map +0 -1
  239. package/dist/domain/environments.d.ts +0 -33
  240. package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
  241. package/dist/domain/host-mcp-task-admission.d.ts +0 -18
  242. package/dist/domain/packs.d.ts +0 -59
  243. package/dist/domain/product-integration-pack.d.ts +0 -44
  244. package/dist/domain/product-integration-skill.gen.d.ts +0 -5
  245. package/dist/remote-mcp-credentials.d.ts +0 -23
  246. package/dist/remote-mcp-credentials.js +0 -11
  247. package/dist/remote-mcp-credentials.js.map +0 -1
  248. package/src/application/host-mcp-owner.ts +0 -41
  249. package/src/application/host-mcp-resolvers.ts +0 -84
  250. package/src/domain/connection-authority.ts +0 -329
  251. package/src/domain/environments.ts +0 -120
  252. package/src/domain/host-mcp-authority-source-admission.ts +0 -25
  253. package/src/domain/host-mcp-task-admission.ts +0 -121
  254. package/src/domain/packs.ts +0 -699
  255. package/src/domain/product-integration-pack.ts +0 -42
  256. package/src/domain/product-integration-skill.gen.ts +0 -52
  257. package/src/remote-mcp-credentials.ts +0 -361
@@ -0,0 +1,394 @@
1
+ import {
2
+ CLAUDE_CONNECTION_KINDS,
3
+ claudeProviderId,
4
+ withClaudeConnectionCatalog,
5
+ withClaudeConnectionCredential,
6
+ withDirectModelProviders,
7
+ type Settings,
8
+ WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
9
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
10
+ withCodexCatalogProvider,
11
+ withWorkspaceGatewayCatalogProvider,
12
+ withWorkspaceGatewayCredential,
13
+ withWorkspaceOpenRouterCatalogProvider,
14
+ withWorkspaceOpenRouterCredential,
15
+ withOrganizationGatewayCatalogProvider,
16
+ withOrganizationGatewayCredential,
17
+ withOrganizationOpenRouterCatalogProvider,
18
+ withOrganizationOpenRouterCredential,
19
+ ORGANIZATION_GATEWAY_MODEL_ID_PREFIX,
20
+ ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX,
21
+ ORGANIZATION_OPPER_MODEL_ID_PREFIX,
22
+ WORKSPACE_OPPER_MODEL_ID_PREFIX,
23
+ withOrganizationOpperCatalogProvider,
24
+ withOrganizationOpperCredential,
25
+ withWorkspaceOpperCatalogProvider,
26
+ withWorkspaceOpperCredential,
27
+ withXaiSubscriptionCatalogProvider,
28
+ } from "@opengeni/config";
29
+ import {
30
+ getWorkspaceGatewayCustomModelForExecution,
31
+ getWorkspaceOpenRouterCustomModelForExecution,
32
+ listWorkspaceGatewayCustomModels,
33
+ listWorkspaceOpenRouterCustomModels,
34
+ loadDirectModelProviderConnection,
35
+ workspaceCodexSubscriptionActive,
36
+ loadWorkspaceVercelAiGatewayApiKey,
37
+ loadWorkspaceOpenRouterApiKey,
38
+ loadWorkspaceOpperApiKey,
39
+ listOrganizationModelProviderCustomModelsForWorkspace,
40
+ getOrganizationModelProviderCustomModelForExecution,
41
+ loadOrganizationModelProviderApiKey,
42
+ listWorkspaceProviderCustomModels,
43
+ getWorkspaceProviderCustomModelForExecution,
44
+ loadWorkspaceProviderApiKey,
45
+ type Database,
46
+ } from "@opengeni/db";
47
+
48
+ /**
49
+ * Execution-time provider overlays for one workspace: subscription catalog
50
+ * providers plus the decrypted workspace and organization provider keys a
51
+ * model call needs. Agent turns (worker claim) and stateless single model
52
+ * calls (API) apply the same overlays, so both resolve a model to the same
53
+ * provider and credential.
54
+ */
55
+
56
+ /**
57
+ * When the workspace has an active Codex subscription connected and the feature
58
+ * is enabled, inject a synthetic "codex-subscription" registry provider so a
59
+ * `codex/<slug>` model id routes through the ChatGPT backend. No secrets touch
60
+ * this overlay (metadata-only read); the per-request bearer is resolved later via
61
+ * codexRequestStorage. Idempotent and a no-op when not applicable.
62
+ */
63
+ export async function settingsWithCodexCredential(
64
+ db: Database,
65
+ workspaceId: string,
66
+ settings: Settings,
67
+ activeOverride?: boolean,
68
+ ): Promise<Settings> {
69
+ // Same active-credential predicate the billing bypass uses, so provider
70
+ // injection and billing can never disagree on what an "active codex" turn is.
71
+ // The caller may pass `activeOverride` (a single, shared read; P2-b) so routing
72
+ // and billing decide from the exact same observation, immune to a concurrent
73
+ // disconnect/reconnect landing between two independent reads.
74
+ const active =
75
+ activeOverride ?? (await workspaceCodexSubscriptionActive(db, settings, workspaceId));
76
+ if (!active) {
77
+ return settings; // disabled / not connected / needs_relogin / error -> leave settings unchanged
78
+ }
79
+ const withProvider = withCodexProvider(settings);
80
+ return withProvider;
81
+ }
82
+
83
+ /** Pure: append the synthetic codex-subscription provider, idempotently. */
84
+ export function withCodexProvider(settings: Settings): Settings {
85
+ return withCodexCatalogProvider(settings);
86
+ }
87
+
88
+ /**
89
+ * Pure: append the synthetic SuperGrok/xAI subscription provider,
90
+ * idempotently. The overlay is catalogue metadata only; the exact account,
91
+ * authority snapshot, and bearer remain frozen later at turn admission.
92
+ */
93
+ export function withXaiSubscriptionProvider(settings: Settings): Settings {
94
+ return withXaiSubscriptionCatalogProvider(settings);
95
+ }
96
+
97
+ export async function settingsWithWorkspaceGatewayCredential(
98
+ db: Database,
99
+ accountId: string,
100
+ workspaceId: string,
101
+ settings: Settings,
102
+ retainedProductModelId?: string | null,
103
+ ): Promise<Settings> {
104
+ const activeCustomModels = await listWorkspaceGatewayCustomModels(db, {
105
+ accountId,
106
+ workspaceId,
107
+ });
108
+ const retainedUpstreamModelId = retainedProductModelId?.startsWith(
109
+ WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
110
+ )
111
+ ? retainedProductModelId.slice(WORKSPACE_GATEWAY_MODEL_ID_PREFIX.length)
112
+ : null;
113
+ const retainedCustomModel = retainedUpstreamModelId
114
+ ? await getWorkspaceGatewayCustomModelForExecution(db, {
115
+ accountId,
116
+ workspaceId,
117
+ upstreamModelId: retainedUpstreamModelId,
118
+ })
119
+ : null;
120
+ const customModels =
121
+ retainedCustomModel &&
122
+ !activeCustomModels.some(
123
+ (model) => model.upstreamModelId === retainedCustomModel.upstreamModelId,
124
+ )
125
+ ? [...activeCustomModels, retainedCustomModel]
126
+ : activeCustomModels;
127
+ const catalogSettings = withWorkspaceGatewayCatalogProvider(settings, customModels);
128
+ const apiKey = await loadWorkspaceVercelAiGatewayApiKey(
129
+ db,
130
+ settings,
131
+ workspaceId,
132
+ retainedProductModelId,
133
+ );
134
+ return apiKey
135
+ ? withWorkspaceGatewayCredential(catalogSettings, apiKey, customModels)
136
+ : catalogSettings;
137
+ }
138
+
139
+ export async function settingsWithWorkspaceOpenRouterCredential(
140
+ db: Database,
141
+ accountId: string,
142
+ workspaceId: string,
143
+ settings: Settings,
144
+ retainedProductModelId?: string | null,
145
+ ): Promise<Settings> {
146
+ const activeCustomModels = await listWorkspaceOpenRouterCustomModels(db, {
147
+ accountId,
148
+ workspaceId,
149
+ });
150
+ const retainedUpstreamModelId = retainedProductModelId?.startsWith(
151
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
152
+ )
153
+ ? retainedProductModelId.slice(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX.length)
154
+ : null;
155
+ const retainedCustomModel = retainedUpstreamModelId
156
+ ? await getWorkspaceOpenRouterCustomModelForExecution(db, {
157
+ accountId,
158
+ workspaceId,
159
+ upstreamModelId: retainedUpstreamModelId,
160
+ })
161
+ : null;
162
+ const customModels =
163
+ retainedCustomModel &&
164
+ !activeCustomModels.some(
165
+ (model) => model.upstreamModelId === retainedCustomModel.upstreamModelId,
166
+ )
167
+ ? [...activeCustomModels, retainedCustomModel]
168
+ : activeCustomModels;
169
+ const catalogSettings = withWorkspaceOpenRouterCatalogProvider(settings, customModels);
170
+ const apiKey = await loadWorkspaceOpenRouterApiKey(
171
+ db,
172
+ settings,
173
+ workspaceId,
174
+ retainedProductModelId,
175
+ );
176
+ return apiKey
177
+ ? withWorkspaceOpenRouterCredential(catalogSettings, apiKey, customModels)
178
+ : catalogSettings;
179
+ }
180
+
181
+ /** Overlay the workspace Opper catalog and, when active, its decrypted key. */
182
+ export async function settingsWithWorkspaceOpperCredential(
183
+ db: Database,
184
+ accountId: string,
185
+ workspaceId: string,
186
+ settings: Settings,
187
+ retainedProductModelId?: string | null,
188
+ ): Promise<Settings> {
189
+ const activeCustomModels = await listWorkspaceProviderCustomModels(db, {
190
+ accountId,
191
+ workspaceId,
192
+ providerKind: "opper",
193
+ });
194
+ const retainedUpstreamModelId = retainedProductModelId?.startsWith(
195
+ WORKSPACE_OPPER_MODEL_ID_PREFIX,
196
+ )
197
+ ? retainedProductModelId.slice(WORKSPACE_OPPER_MODEL_ID_PREFIX.length)
198
+ : null;
199
+ const retainedCustomModel = retainedUpstreamModelId
200
+ ? await getWorkspaceProviderCustomModelForExecution(db, {
201
+ accountId,
202
+ workspaceId,
203
+ providerKind: "opper",
204
+ upstreamModelId: retainedUpstreamModelId,
205
+ })
206
+ : null;
207
+ const customModels =
208
+ retainedCustomModel &&
209
+ !activeCustomModels.some(
210
+ (model) => model.upstreamModelId === retainedCustomModel.upstreamModelId,
211
+ )
212
+ ? [...activeCustomModels, retainedCustomModel]
213
+ : activeCustomModels;
214
+ const catalogSettings = withWorkspaceOpperCatalogProvider(settings, customModels);
215
+ const apiKey = await loadWorkspaceOpperApiKey(db, settings, workspaceId, retainedProductModelId);
216
+ return apiKey
217
+ ? withWorkspaceOpperCredential(catalogSettings, apiKey, customModels)
218
+ : catalogSettings;
219
+ }
220
+
221
+ export async function settingsWithOrganizationProviderCredentials(
222
+ db: Database,
223
+ accountId: string,
224
+ workspaceId: string,
225
+ settings: Settings,
226
+ retainedProductModelId?: string | null,
227
+ ): Promise<Settings> {
228
+ const buildModels = async (
229
+ providerKind: "vercel_gateway" | "openrouter" | "anthropic" | "claude_subscription" | "opper",
230
+ prefix: string,
231
+ ) => {
232
+ const active = await listOrganizationModelProviderCustomModelsForWorkspace(db, {
233
+ accountId,
234
+ workspaceId,
235
+ providerKind,
236
+ });
237
+ const upstreamModelId = retainedProductModelId?.startsWith(prefix)
238
+ ? retainedProductModelId.slice(prefix.length)
239
+ : null;
240
+ const retained = upstreamModelId
241
+ ? await getOrganizationModelProviderCustomModelForExecution(db, {
242
+ accountId,
243
+ workspaceId,
244
+ providerKind,
245
+ upstreamModelId,
246
+ })
247
+ : null;
248
+ return retained && !active.some((model) => model.id === retained.id)
249
+ ? [...active, retained]
250
+ : active;
251
+ };
252
+ const gatewayModels = await buildModels("vercel_gateway", ORGANIZATION_GATEWAY_MODEL_ID_PREFIX);
253
+ const openRouterModels = await buildModels("openrouter", ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX);
254
+ const gatewayKey = await loadOrganizationModelProviderApiKey(db, settings, {
255
+ accountId,
256
+ workspaceId,
257
+ providerKind: "vercel_gateway",
258
+ });
259
+ const gatewaySettings = gatewayKey
260
+ ? withOrganizationGatewayCredential(settings, gatewayKey, gatewayModels)
261
+ : withOrganizationGatewayCatalogProvider(settings, gatewayModels);
262
+ const openRouterKey = await loadOrganizationModelProviderApiKey(db, settings, {
263
+ accountId,
264
+ workspaceId,
265
+ providerKind: "openrouter",
266
+ });
267
+ const openRouterSettings = openRouterKey
268
+ ? withOrganizationOpenRouterCredential(gatewaySettings, openRouterKey, openRouterModels)
269
+ : withOrganizationOpenRouterCatalogProvider(gatewaySettings, openRouterModels);
270
+ const opperModels = await buildModels("opper", ORGANIZATION_OPPER_MODEL_ID_PREFIX);
271
+ const opperKey = await loadOrganizationModelProviderApiKey(db, settings, {
272
+ accountId,
273
+ workspaceId,
274
+ providerKind: "opper",
275
+ });
276
+ let result = opperKey
277
+ ? withOrganizationOpperCredential(openRouterSettings, opperKey, opperModels)
278
+ : withOrganizationOpperCatalogProvider(openRouterSettings, opperModels);
279
+ for (const kind of CLAUDE_CONNECTION_KINDS) {
280
+ if (kind === "claude_subscription" && !settings.claudeSubscriptionEnabled) continue;
281
+ const models = await buildModels(kind, claudeProviderId(kind) + "/");
282
+ result = withClaudeConnectionCatalog(result, { [kind]: { models } });
283
+ const credential =
284
+ kind === "claude_subscription"
285
+ ? null
286
+ : await loadOrganizationModelProviderApiKey(db, settings, {
287
+ accountId,
288
+ workspaceId,
289
+ providerKind: kind,
290
+ });
291
+ if (credential)
292
+ result = withClaudeConnectionCredential(result, kind, credential, "organization", undefined);
293
+ const workspaceModels = await listWorkspaceProviderCustomModels(db, {
294
+ accountId,
295
+ workspaceId,
296
+ providerKind: kind,
297
+ });
298
+ const workspacePrefix = claudeProviderId(kind, "workspace") + "/";
299
+ const workspaceModelId = retainedProductModelId?.startsWith(workspacePrefix)
300
+ ? retainedProductModelId
301
+ : null;
302
+ if (workspaceModelId) {
303
+ const retained = await getWorkspaceProviderCustomModelForExecution(db, {
304
+ accountId,
305
+ workspaceId,
306
+ providerKind: kind,
307
+ upstreamModelId: workspaceModelId.slice(workspacePrefix.length),
308
+ });
309
+ if (retained && !workspaceModels.some((model) => model.id === retained.id))
310
+ workspaceModels.push(retained);
311
+ }
312
+ result = withClaudeConnectionCatalog(
313
+ result,
314
+ { [kind]: { models: workspaceModels } },
315
+ "workspace",
316
+ );
317
+ const workspaceCredential =
318
+ kind === "claude_subscription"
319
+ ? null
320
+ : await loadWorkspaceProviderApiKey(db, settings, workspaceId, kind, workspaceModelId);
321
+ if (workspaceCredential)
322
+ result = withClaudeConnectionCredential(
323
+ result,
324
+ kind,
325
+ workspaceCredential,
326
+ "workspace",
327
+ undefined,
328
+ );
329
+ }
330
+ return result;
331
+ }
332
+
333
+ /**
334
+ * Apply every execution overlay in the order the worker claim uses, then
335
+ * install the selected customer direct connection when the model names one.
336
+ * `codexSubscriptionActive` is the caller's single readiness observation, so
337
+ * routing and billing decide from the same read.
338
+ */
339
+ export async function settingsWithModelProviderCredentials(
340
+ db: Database,
341
+ input: {
342
+ accountId: string;
343
+ workspaceId: string;
344
+ settings: Settings;
345
+ productModelId: string | null | undefined;
346
+ codexSubscriptionActive: boolean;
347
+ },
348
+ ): Promise<Settings> {
349
+ const { accountId, workspaceId, productModelId } = input;
350
+ const codexSettings = await settingsWithCodexCredential(
351
+ db,
352
+ workspaceId,
353
+ input.settings,
354
+ input.codexSubscriptionActive,
355
+ );
356
+ const xaiSettings = codexSettings.supergrokSubscriptionEnabled
357
+ ? withXaiSubscriptionProvider(codexSettings)
358
+ : codexSettings;
359
+ const gatewaySettings = await settingsWithWorkspaceGatewayCredential(
360
+ db,
361
+ accountId,
362
+ workspaceId,
363
+ xaiSettings,
364
+ productModelId,
365
+ );
366
+ const openRouterSettings = await settingsWithWorkspaceOpenRouterCredential(
367
+ db,
368
+ accountId,
369
+ workspaceId,
370
+ gatewaySettings,
371
+ productModelId,
372
+ );
373
+ const workspaceProviderSettings = await settingsWithWorkspaceOpperCredential(
374
+ db,
375
+ accountId,
376
+ workspaceId,
377
+ openRouterSettings,
378
+ productModelId,
379
+ );
380
+ const capabilitySettings = await settingsWithOrganizationProviderCredentials(
381
+ db,
382
+ accountId,
383
+ workspaceId,
384
+ workspaceProviderSettings,
385
+ productModelId,
386
+ );
387
+ const direct = await loadDirectModelProviderConnection(
388
+ db,
389
+ capabilitySettings,
390
+ workspaceId,
391
+ productModelId ?? "",
392
+ );
393
+ return direct ? withDirectModelProviders(capabilitySettings, [direct]) : capabilitySettings;
394
+ }
@@ -0,0 +1,101 @@
1
+ import { SESSION_TURN_SURFACES, type SessionTurnSurface } from "@opengeni/contracts";
2
+ import type { Observability } from "@opengeni/observability";
3
+
4
+ /**
5
+ * Live, content-free product usage counters recorded where work is accepted.
6
+ * Every label is a closed set: the product surface (or `unknown` when no
7
+ * surface was captured), who created a session (`subject` or `service`), and
8
+ * whether the session is a root. No workspace, user, model or text value ever
9
+ * becomes a label.
10
+ */
11
+ export type ProductUsageMetricsSink = Pick<Observability, "incrementCounter">;
12
+
13
+ export const PRODUCT_USAGE_SURFACE_LABELS = [...SESSION_TURN_SURFACES, "unknown"] as const;
14
+ export type ProductUsageSurfaceLabel = (typeof PRODUCT_USAGE_SURFACE_LABELS)[number];
15
+ export const SESSION_CREATED_BY_KINDS = ["subject", "service"] as const;
16
+ export type SessionCreatedByKind = (typeof SESSION_CREATED_BY_KINDS)[number];
17
+
18
+ const SESSIONS_CREATED_METRIC = {
19
+ name: "opengeni_sessions_created_total",
20
+ help: "Sessions created by product surface, creator kind, and whether they are root sessions.",
21
+ } as const;
22
+ const USER_MESSAGES_METRIC = {
23
+ name: "opengeni_user_messages_total",
24
+ help: "Accepted user messages and scheduled prompts by product surface.",
25
+ } as const;
26
+
27
+ export function productUsageSurfaceLabel(
28
+ surface: SessionTurnSurface | null | undefined,
29
+ ): ProductUsageSurfaceLabel {
30
+ return surface && (SESSION_TURN_SURFACES as readonly string[]).includes(surface)
31
+ ? surface
32
+ : "unknown";
33
+ }
34
+
35
+ function sessionCreatedByKind(kind: string | null | undefined): SessionCreatedByKind {
36
+ return kind === "subject" ? "subject" : "service";
37
+ }
38
+
39
+ /** Count one newly created session. Never throws; replays must not call it. */
40
+ export function recordSessionCreated(
41
+ sink: ProductUsageMetricsSink | null | undefined,
42
+ input: {
43
+ surface: SessionTurnSurface | null | undefined;
44
+ createdByKind: string | null | undefined;
45
+ parentSessionId: string | null | undefined;
46
+ },
47
+ ): void {
48
+ try {
49
+ sink?.incrementCounter({
50
+ ...SESSIONS_CREATED_METRIC,
51
+ labels: {
52
+ surface: productUsageSurfaceLabel(input.surface),
53
+ created_by_kind: sessionCreatedByKind(input.createdByKind),
54
+ root: input.parentSessionId ? "false" : "true",
55
+ },
56
+ });
57
+ } catch {
58
+ // Telemetry must never change the create outcome.
59
+ }
60
+ }
61
+
62
+ /** Count one newly accepted user message. Never throws; replays must not call it. */
63
+ export function recordUserMessageAccepted(
64
+ sink: ProductUsageMetricsSink | null | undefined,
65
+ input: { surface: SessionTurnSurface | null | undefined },
66
+ ): void {
67
+ try {
68
+ sink?.incrementCounter({
69
+ ...USER_MESSAGES_METRIC,
70
+ labels: { surface: productUsageSurfaceLabel(input.surface) },
71
+ });
72
+ } catch {
73
+ // Telemetry must never change the send outcome.
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Publish every closed series at zero so dashboards can tell a quiet surface
79
+ * from missing instrumentation. Never throws.
80
+ */
81
+ export function registerProductUsageMetricBaselines(
82
+ sink: ProductUsageMetricsSink | null | undefined,
83
+ ): void {
84
+ if (!sink) return;
85
+ try {
86
+ for (const surface of PRODUCT_USAGE_SURFACE_LABELS) {
87
+ sink.incrementCounter({ ...USER_MESSAGES_METRIC, labels: { surface }, amount: 0 });
88
+ for (const createdByKind of SESSION_CREATED_BY_KINDS) {
89
+ for (const root of ["true", "false"]) {
90
+ sink.incrementCounter({
91
+ ...SESSIONS_CREATED_METRIC,
92
+ labels: { surface, created_by_kind: createdByKind, root },
93
+ amount: 0,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ } catch {
99
+ // Telemetry only; series appear on their first real increment.
100
+ }
101
+ }