@opengeni/db 0.31.1 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/browser-auth.d.ts +209 -1
  2. package/dist/browser-downloads.d.ts +51 -0
  3. package/dist/browser-identities.d.ts +11 -2
  4. package/dist/browser-sessions.d.ts +83 -1
  5. package/dist/browser-state-artifacts.d.ts +75 -1
  6. package/dist/canonical-human-identities.d.ts +48 -0
  7. package/dist/canonical-human-identities.js +28 -0
  8. package/dist/canonical-human-identities.js.map +1 -0
  9. package/dist/capability-integrations.d.ts +8 -6
  10. package/dist/{chunk-MWDVXQOL.js → chunk-5KTIEDX7.js} +5773 -4212
  11. package/dist/chunk-5KTIEDX7.js.map +1 -0
  12. package/dist/{chunk-NBE6CHRI.js → chunk-CQZ4QSP5.js} +49 -9
  13. package/dist/chunk-CQZ4QSP5.js.map +1 -0
  14. package/dist/chunk-F32YT7MP.js +117 -0
  15. package/dist/chunk-F32YT7MP.js.map +1 -0
  16. package/dist/{chunk-ISI2TIUG.js → chunk-JC5QPLUM.js} +30 -3
  17. package/dist/chunk-JC5QPLUM.js.map +1 -0
  18. package/dist/{chunk-F5FT7BEZ.js → chunk-P3RE4D2B.js} +2 -2
  19. package/dist/chunk-TAUTWOS5.js +259 -0
  20. package/dist/chunk-TAUTWOS5.js.map +1 -0
  21. package/dist/{chunk-YHZXQ7HP.js → chunk-VKLB2ZGI.js} +446 -19
  22. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  23. package/dist/database.d.ts +5 -0
  24. package/dist/editable-artifact-durable-export.js +2 -2
  25. package/dist/editable-artifacts.js +3 -3
  26. package/dist/index.d.ts +438 -29
  27. package/dist/index.js +35640 -28969
  28. package/dist/index.js.map +1 -1
  29. package/dist/insights.d.ts +23 -7
  30. package/dist/integration-bindings.d.ts +24 -24
  31. package/dist/integration-facets.d.ts +99 -0
  32. package/dist/interaction-revisions.d.ts +11 -0
  33. package/dist/interaction-schema.d.ts +698 -34
  34. package/dist/provision-roles.js +1 -1
  35. package/dist/runtime-posture.d.ts +8 -7
  36. package/dist/schema.d.ts +9464 -5412
  37. package/dist/schema.js +51 -9
  38. package/dist/session-control.d.ts +29 -3
  39. package/dist/session-tenancy.d.ts +44 -0
  40. package/dist/session-tenancy.js +16 -0
  41. package/dist/session-tenancy.js.map +1 -0
  42. package/dist/slack-task-policy-schema.d.ts +847 -0
  43. package/dist/slack-task-policy.d.ts +65 -0
  44. package/dist/task-notes-schema.d.ts +924 -0
  45. package/dist/task-notes.d.ts +85 -0
  46. package/dist/video-generation.d.ts +9 -0
  47. package/dist/video-generation.js +5 -3
  48. package/dist/xai-subscription.d.ts +326 -0
  49. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  50. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  51. package/drizzle/0214_browser_download_saves.sql +34 -0
  52. package/drizzle/0215_browser_controller_host.sql +385 -0
  53. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  54. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  55. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  56. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  57. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  58. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  59. package/drizzle/0220_session_channels.sql +54 -0
  60. package/drizzle/0221_sessions_channel_index.sql +5 -0
  61. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  62. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  63. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  64. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  65. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  66. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  67. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  68. package/drizzle/0227_slack_native_actions.sql +145 -0
  69. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  70. package/drizzle/0228_slack_task_policy.sql +400 -0
  71. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  72. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  73. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  74. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  75. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  76. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  77. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  78. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  79. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  80. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  81. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  82. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  83. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  84. package/drizzle/0239_task_tree_notes.sql +990 -0
  85. package/package.json +13 -5
  86. package/src/browser-auth.ts +2600 -123
  87. package/src/browser-downloads.ts +517 -0
  88. package/src/browser-identities.ts +220 -3
  89. package/src/browser-sessions.ts +334 -45
  90. package/src/browser-state-artifacts.ts +402 -12
  91. package/src/canonical-human-identities.ts +317 -0
  92. package/src/capability-integrations.ts +121 -213
  93. package/src/database.ts +41 -1
  94. package/src/index.ts +4157 -394
  95. package/src/insights.ts +109 -28
  96. package/src/integration-bindings.ts +101 -101
  97. package/src/{integration-features.ts → integration-facets.ts} +187 -184
  98. package/src/interaction-revisions.ts +33 -3
  99. package/src/interaction-schema.ts +311 -8
  100. package/src/pack-components.ts +43 -43
  101. package/src/pack-installations.ts +0 -43
  102. package/src/plugin-packages.ts +9 -9
  103. package/src/provision-roles.ts +232 -0
  104. package/src/runtime-posture.ts +227 -14
  105. package/src/schema.ts +1188 -75
  106. package/src/session-control.ts +122 -37
  107. package/src/session-queue-commands.ts +78 -1
  108. package/src/session-tenancy.ts +188 -0
  109. package/src/session-tool-call-settlement.ts +9 -3
  110. package/src/slack-task-policy-schema.ts +130 -0
  111. package/src/slack-task-policy.ts +341 -0
  112. package/src/task-notes-schema.ts +135 -0
  113. package/src/task-notes.ts +178 -0
  114. package/src/video-generation.ts +90 -12
  115. package/src/workspace-artifacts.ts +1 -1
  116. package/src/xai-subscription.ts +1887 -0
  117. package/dist/chunk-ISI2TIUG.js.map +0 -1
  118. package/dist/chunk-MWDVXQOL.js.map +0 -1
  119. package/dist/chunk-NBE6CHRI.js.map +0 -1
  120. package/dist/chunk-YHZXQ7HP.js.map +0 -1
  121. package/dist/integration-features.d.ts +0 -99
  122. /package/dist/{chunk-F5FT7BEZ.js.map → chunk-P3RE4D2B.js.map} +0 -0
@@ -0,0 +1,1887 @@
1
+ import {
2
+ WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1,
3
+ XaiProviderAccountAuthoritySnapshotV1,
4
+ type XaiProviderAccountAuthoritySnapshotV1 as XaiAuthoritySnapshot,
5
+ } from "@opengeni/contracts";
6
+ import { and, asc, eq, gt, isNull, lte, sql } from "drizzle-orm";
7
+ import type { Database } from "./database";
8
+ import { rawRows, withWorkspaceSubjectRls } from "./database";
9
+ import { decryptEnvironmentValue, encryptEnvironmentValue } from "./environment-crypto";
10
+ import * as schema from "./schema";
11
+
12
+ export type XaiAccountAuthorityScope = "workspace" | "user";
13
+ export type XaiCredentialStatus = "active" | "needs_relogin" | "error" | "disabled";
14
+
15
+ export type XaiCredentialSecretV1 = {
16
+ version: 1;
17
+ accessToken?: string;
18
+ refreshToken?: string;
19
+ sessionToken?: string;
20
+ cookie?: string;
21
+ };
22
+
23
+ export type XaiSubscriptionAccountMetadata = {
24
+ id: string;
25
+ scope: XaiAccountAuthorityScope;
26
+ providerAccountId: string | null;
27
+ label: string | null;
28
+ accountEmail: string | null;
29
+ planType: string | null;
30
+ status: XaiCredentialStatus;
31
+ allocatorEnabled: boolean;
32
+ version: number;
33
+ allocatorVersion: number;
34
+ allocatorUpdatedAt: Date | null;
35
+ expiresAt: Date | null;
36
+ lastRefreshAt: Date | null;
37
+ lastError: string | null;
38
+ quotaUsedPercent: number | null;
39
+ quotaResetAt: Date | null;
40
+ quotaCheckedAt: Date | null;
41
+ exhaustedUntil: Date | null;
42
+ selectionCount: number;
43
+ lastSelectedAt: Date | null;
44
+ connectedBySubjectId: string | null;
45
+ };
46
+
47
+ export type XaiCredentialForRun = XaiSubscriptionAccountMetadata & {
48
+ secret: XaiCredentialSecretV1;
49
+ authoritySnapshot: XaiAuthoritySnapshot;
50
+ };
51
+
52
+ export type XaiCredentialLeaseResult = {
53
+ credentialId: string | null;
54
+ rotationEnabled: boolean;
55
+ reused: boolean;
56
+ holderId: string | null;
57
+ generation: number | null;
58
+ leasedUntil: Date | null;
59
+ accounts: XaiSubscriptionAccountMetadata[];
60
+ };
61
+
62
+ export const XAI_CREDENTIAL_LEASE_TTL_MS = 5 * 60_000;
63
+
64
+ /** Stable session sharding, matching Codex's cache-affinity contract. */
65
+ export function xaiCredentialShardIndex(sessionId: string, candidateCount: number): number {
66
+ if (!Number.isSafeInteger(candidateCount) || candidateCount <= 0) {
67
+ throw new Error("xAI shard candidate count must be positive");
68
+ }
69
+ let hash = 0x811c9dc5;
70
+ for (let index = 0; index < sessionId.length; index += 1) {
71
+ hash ^= sessionId.charCodeAt(index);
72
+ hash = Math.imul(hash, 0x01000193);
73
+ }
74
+ return (hash >>> 0) % candidateCount;
75
+ }
76
+
77
+ type XaiCredentialMetadataRow = Pick<
78
+ typeof schema.xaiSubscriptionCredentials.$inferSelect,
79
+ | "id"
80
+ | "authorityScope"
81
+ | "providerAccountId"
82
+ | "label"
83
+ | "accountEmail"
84
+ | "planType"
85
+ | "status"
86
+ | "allocatorEnabled"
87
+ | "version"
88
+ | "allocatorVersion"
89
+ | "allocatorUpdatedAt"
90
+ | "expiresAt"
91
+ | "lastRefreshAt"
92
+ | "lastError"
93
+ | "quotaUsedPercent"
94
+ | "quotaResetAt"
95
+ | "quotaCheckedAt"
96
+ | "exhaustedUntil"
97
+ | "selectionCount"
98
+ | "lastSelectedAt"
99
+ | "connectedBySubjectId"
100
+ >;
101
+
102
+ const xaiCredentialMetadataColumns = {
103
+ id: schema.xaiSubscriptionCredentials.id,
104
+ authorityScope: schema.xaiSubscriptionCredentials.authorityScope,
105
+ providerAccountId: schema.xaiSubscriptionCredentials.providerAccountId,
106
+ label: schema.xaiSubscriptionCredentials.label,
107
+ accountEmail: schema.xaiSubscriptionCredentials.accountEmail,
108
+ planType: schema.xaiSubscriptionCredentials.planType,
109
+ status: schema.xaiSubscriptionCredentials.status,
110
+ allocatorEnabled: schema.xaiSubscriptionCredentials.allocatorEnabled,
111
+ version: schema.xaiSubscriptionCredentials.version,
112
+ allocatorVersion: schema.xaiSubscriptionCredentials.allocatorVersion,
113
+ allocatorUpdatedAt: schema.xaiSubscriptionCredentials.allocatorUpdatedAt,
114
+ expiresAt: schema.xaiSubscriptionCredentials.expiresAt,
115
+ lastRefreshAt: schema.xaiSubscriptionCredentials.lastRefreshAt,
116
+ lastError: schema.xaiSubscriptionCredentials.lastError,
117
+ quotaUsedPercent: schema.xaiSubscriptionCredentials.quotaUsedPercent,
118
+ quotaResetAt: schema.xaiSubscriptionCredentials.quotaResetAt,
119
+ quotaCheckedAt: schema.xaiSubscriptionCredentials.quotaCheckedAt,
120
+ exhaustedUntil: schema.xaiSubscriptionCredentials.exhaustedUntil,
121
+ selectionCount: schema.xaiSubscriptionCredentials.selectionCount,
122
+ lastSelectedAt: schema.xaiSubscriptionCredentials.lastSelectedAt,
123
+ connectedBySubjectId: schema.xaiSubscriptionCredentials.connectedBySubjectId,
124
+ } as const;
125
+
126
+ const xaiCredentialAllocationColumns = {
127
+ ...xaiCredentialMetadataColumns,
128
+ accountId: schema.xaiSubscriptionCredentials.accountId,
129
+ workspaceId: schema.xaiSubscriptionCredentials.workspaceId,
130
+ ownerOrganizationMembershipId: schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId,
131
+ createdAt: schema.xaiSubscriptionCredentials.createdAt,
132
+ } as const;
133
+
134
+ function assertSecret(secret: XaiCredentialSecretV1): void {
135
+ if (secret.version !== 1) throw new Error("Unsupported xAI credential secret version");
136
+ const values = [secret.accessToken, secret.refreshToken, secret.sessionToken, secret.cookie];
137
+ if (!values.some((value) => typeof value === "string" && value.length > 0)) {
138
+ throw new Error("An xAI credential must contain at least one secret value");
139
+ }
140
+ }
141
+
142
+ function parseSecret(value: string): XaiCredentialSecretV1 {
143
+ const parsed = JSON.parse(value) as Partial<XaiCredentialSecretV1>;
144
+ const secret: XaiCredentialSecretV1 = {
145
+ version: 1,
146
+ ...(typeof parsed.accessToken === "string" ? { accessToken: parsed.accessToken } : {}),
147
+ ...(typeof parsed.refreshToken === "string" ? { refreshToken: parsed.refreshToken } : {}),
148
+ ...(typeof parsed.sessionToken === "string" ? { sessionToken: parsed.sessionToken } : {}),
149
+ ...(typeof parsed.cookie === "string" ? { cookie: parsed.cookie } : {}),
150
+ };
151
+ assertSecret(secret);
152
+ return secret;
153
+ }
154
+
155
+ function metadataFromRow(row: XaiCredentialMetadataRow): XaiSubscriptionAccountMetadata {
156
+ return {
157
+ id: row.id,
158
+ scope: row.authorityScope as XaiAccountAuthorityScope,
159
+ providerAccountId: row.providerAccountId,
160
+ label: row.label,
161
+ accountEmail: row.accountEmail,
162
+ planType: row.planType,
163
+ status: row.status as XaiCredentialStatus,
164
+ allocatorEnabled: row.allocatorEnabled,
165
+ version: row.version,
166
+ allocatorVersion: row.allocatorVersion,
167
+ allocatorUpdatedAt: row.allocatorUpdatedAt,
168
+ expiresAt: row.expiresAt,
169
+ lastRefreshAt: row.lastRefreshAt,
170
+ lastError: row.lastError,
171
+ quotaUsedPercent: row.quotaUsedPercent,
172
+ quotaResetAt: row.quotaResetAt,
173
+ quotaCheckedAt: row.quotaCheckedAt,
174
+ exhaustedUntil: row.exhaustedUntil,
175
+ selectionCount: row.selectionCount,
176
+ lastSelectedAt: row.lastSelectedAt,
177
+ connectedBySubjectId: row.connectedBySubjectId,
178
+ };
179
+ }
180
+
181
+ async function resolveXaiPoolOwnerMembershipId(
182
+ db: Database,
183
+ input: {
184
+ workspaceId: string;
185
+ subjectId: string;
186
+ authoritySnapshot: XaiAuthoritySnapshot;
187
+ },
188
+ ): Promise<string | null> {
189
+ if (input.authoritySnapshot.scope === "workspace") return null;
190
+ const rows = await rawRows<{ membership_id: string }>(
191
+ db,
192
+ sql`select organization_membership_id as membership_id
193
+ from resolve_xai_authority_pool(
194
+ current_setting('opengeni.account_id')::uuid,
195
+ ${input.workspaceId}::uuid,
196
+ ${input.subjectId},
197
+ ${JSON.stringify(input.authoritySnapshot)}::jsonb
198
+ )`,
199
+ );
200
+ const ownerMembershipId = rows[0]?.membership_id ?? null;
201
+ if (!ownerMembershipId) {
202
+ throw new Error("xAI user authority pool is no longer active");
203
+ }
204
+ return ownerMembershipId;
205
+ }
206
+
207
+ async function assertXaiTurnAuthoritySnapshot(
208
+ db: Database,
209
+ input: {
210
+ accountId: string;
211
+ workspaceId: string;
212
+ turnId: string;
213
+ sessionId?: string;
214
+ executionGeneration?: number;
215
+ authoritySnapshot: XaiAuthoritySnapshot;
216
+ },
217
+ ): Promise<void> {
218
+ const rows = await rawRows<{ id: string }>(
219
+ db,
220
+ sql`select id
221
+ from session_turns
222
+ where account_id = ${input.accountId}::uuid
223
+ and workspace_id = ${input.workspaceId}::uuid
224
+ and id = ${input.turnId}::uuid
225
+ ${input.sessionId ? sql`and session_id = ${input.sessionId}::uuid` : sql``}
226
+ ${input.executionGeneration !== undefined ? sql`and execution_generation = ${input.executionGeneration}` : sql``}
227
+ and xai_provider_account_authority_snapshot =
228
+ ${JSON.stringify(input.authoritySnapshot)}::jsonb
229
+ for share`,
230
+ );
231
+ if (!rows[0]) {
232
+ throw new Error("xAI logical turn authority snapshot is unavailable");
233
+ }
234
+ }
235
+
236
+ async function assertXaiCredentialInPool(
237
+ db: Database,
238
+ input: {
239
+ accountId: string;
240
+ workspaceId: string;
241
+ credentialId: string;
242
+ authorityScope: XaiAccountAuthorityScope;
243
+ ownerMembershipId: string | null;
244
+ },
245
+ ): Promise<void> {
246
+ const [row] = await db
247
+ .select({ id: schema.xaiSubscriptionCredentials.id })
248
+ .from(schema.xaiSubscriptionCredentials)
249
+ .where(
250
+ and(
251
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
252
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
253
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
254
+ eq(schema.xaiSubscriptionCredentials.authorityScope, input.authorityScope),
255
+ input.ownerMembershipId === null
256
+ ? isNull(schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId)
257
+ : eq(
258
+ schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId,
259
+ input.ownerMembershipId,
260
+ ),
261
+ ),
262
+ )
263
+ .limit(1);
264
+ if (!row) throw new Error("xAI credential is outside the authorized account pool");
265
+ }
266
+
267
+ export async function createXaiSubscriptionCredential(
268
+ db: Database,
269
+ input: {
270
+ accountId: string;
271
+ workspaceId: string;
272
+ subjectId: string;
273
+ scope?: XaiAccountAuthorityScope;
274
+ encryptionKey: Uint8Array;
275
+ secret: XaiCredentialSecretV1;
276
+ providerAccountId?: string | null;
277
+ label?: string | null;
278
+ accountEmail?: string | null;
279
+ planType?: string | null;
280
+ expiresAt?: Date | null;
281
+ },
282
+ ): Promise<{ account: XaiSubscriptionAccountMetadata; authoritySnapshot: XaiAuthoritySnapshot }> {
283
+ assertSecret(input.secret);
284
+ const scope = input.scope ?? "workspace";
285
+ const encrypted = encryptEnvironmentValue(input.encryptionKey, JSON.stringify(input.secret));
286
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
287
+ const rows = await rawRows<{
288
+ credential_id: string;
289
+ authority_generation: number | string | null;
290
+ }>(
291
+ scopedDb,
292
+ sql`select * from create_xai_subscription_credential(
293
+ ${input.accountId}::uuid,
294
+ ${input.workspaceId}::uuid,
295
+ ${input.subjectId},
296
+ ${scope},
297
+ ${encrypted},
298
+ ${input.providerAccountId ?? null},
299
+ ${input.label ?? null},
300
+ ${input.accountEmail ?? null},
301
+ ${input.planType ?? null},
302
+ ${input.expiresAt?.toISOString() ?? null}::timestamptz
303
+ )`,
304
+ );
305
+ const created = rows[0];
306
+ if (!created) throw new Error("xAI credential lifecycle returned no row");
307
+ const [row] = await scopedDb
308
+ .select(xaiCredentialMetadataColumns)
309
+ .from(schema.xaiSubscriptionCredentials)
310
+ .where(eq(schema.xaiSubscriptionCredentials.id, created.credential_id))
311
+ .limit(1);
312
+ if (!row) throw new Error("xAI credential lifecycle result is not visible");
313
+ const authoritySnapshot =
314
+ scope === "workspace"
315
+ ? WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1
316
+ : XaiProviderAccountAuthoritySnapshotV1.parse({
317
+ version: 1,
318
+ scope: "user",
319
+ authorityGeneration: Number(created.authority_generation),
320
+ });
321
+ return { account: metadataFromRow(row), authoritySnapshot };
322
+ });
323
+ }
324
+
325
+ export async function upsertXaiSubscriptionCredential(
326
+ db: Database,
327
+ input: {
328
+ accountId: string;
329
+ workspaceId: string;
330
+ subjectId: string;
331
+ credentialId?: string | null;
332
+ authoritySnapshot?: XaiAuthoritySnapshot;
333
+ scope?: XaiAccountAuthorityScope;
334
+ encryptionKey: Uint8Array;
335
+ secret: XaiCredentialSecretV1;
336
+ providerAccountId?: string | null;
337
+ label?: string | null;
338
+ accountEmail?: string | null;
339
+ planType?: string | null;
340
+ expiresAt?: Date | null;
341
+ },
342
+ ): Promise<{ account: XaiSubscriptionAccountMetadata; authoritySnapshot: XaiAuthoritySnapshot }> {
343
+ if (!input.credentialId) {
344
+ if (input.providerAccountId) {
345
+ const existing = await findXaiCredentialByProviderIdentity(db, {
346
+ workspaceId: input.workspaceId,
347
+ subjectId: input.subjectId,
348
+ scope: input.scope ?? "workspace",
349
+ providerAccountId: input.providerAccountId,
350
+ });
351
+ if (existing) {
352
+ return await upsertXaiSubscriptionCredential(db, {
353
+ ...input,
354
+ credentialId: existing.credentialId,
355
+ authoritySnapshot: existing.authoritySnapshot,
356
+ });
357
+ }
358
+ }
359
+ return await createXaiSubscriptionCredential(db, input);
360
+ }
361
+ if (!input.authoritySnapshot) {
362
+ throw new Error("Existing xAI credentials require their frozen authority snapshot");
363
+ }
364
+ const credentialId = input.credentialId;
365
+ assertSecret(input.secret);
366
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
367
+ const encrypted = encryptEnvironmentValue(input.encryptionKey, JSON.stringify(input.secret));
368
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
369
+ const authorized = await rawRows<{ id: string }>(
370
+ scopedDb,
371
+ sql`select id from revalidate_xai_subscription_authority(
372
+ ${input.workspaceId}::uuid, ${input.subjectId}, ${credentialId}::uuid,
373
+ ${JSON.stringify(snapshot)}::jsonb
374
+ )`,
375
+ );
376
+ if (!authorized[0]) throw new Error("xAI provider-account authority is no longer active");
377
+ const [row] = await scopedDb
378
+ .update(schema.xaiSubscriptionCredentials)
379
+ .set({
380
+ credentialEncrypted: encrypted,
381
+ providerAccountId: input.providerAccountId ?? null,
382
+ label: input.label ?? null,
383
+ accountEmail: input.accountEmail ?? null,
384
+ planType: input.planType ?? null,
385
+ expiresAt: input.expiresAt ?? null,
386
+ lastRefreshAt: new Date(),
387
+ status: "active",
388
+ lastError: null,
389
+ version: sql`${schema.xaiSubscriptionCredentials.version} + 1`,
390
+ updatedAt: new Date(),
391
+ })
392
+ .where(eq(schema.xaiSubscriptionCredentials.id, credentialId))
393
+ .returning(xaiCredentialMetadataColumns);
394
+ if (!row) throw new Error("xAI credential update lost its authority fence");
395
+ return { account: metadataFromRow(row), authoritySnapshot: snapshot };
396
+ });
397
+ }
398
+
399
+ async function findXaiCredentialByProviderIdentity(
400
+ db: Database,
401
+ input: {
402
+ workspaceId: string;
403
+ subjectId: string;
404
+ scope: XaiAccountAuthorityScope;
405
+ providerAccountId: string;
406
+ },
407
+ ): Promise<{ credentialId: string; authoritySnapshot: XaiAuthoritySnapshot } | null> {
408
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
409
+ const [row] = await scopedDb
410
+ .select({
411
+ id: schema.xaiSubscriptionCredentials.id,
412
+ authorityScope: schema.xaiSubscriptionCredentials.authorityScope,
413
+ authorityGeneration:
414
+ schema.xaiSubscriptionCredentials.organizationUserResourceAuthorityGeneration,
415
+ })
416
+ .from(schema.xaiSubscriptionCredentials)
417
+ .where(
418
+ and(
419
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
420
+ eq(schema.xaiSubscriptionCredentials.authorityScope, input.scope),
421
+ eq(schema.xaiSubscriptionCredentials.providerAccountId, input.providerAccountId),
422
+ ),
423
+ )
424
+ .limit(1);
425
+ if (!row) return null;
426
+ return {
427
+ credentialId: row.id,
428
+ authoritySnapshot:
429
+ row.authorityScope === "workspace"
430
+ ? WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1
431
+ : XaiProviderAccountAuthoritySnapshotV1.parse({
432
+ version: 1,
433
+ scope: "user",
434
+ authorityGeneration: row.authorityGeneration,
435
+ }),
436
+ };
437
+ });
438
+ }
439
+
440
+ export async function listXaiSubscriptionAccountsMetadata(
441
+ db: Database,
442
+ input: { workspaceId: string; subjectId: string },
443
+ ): Promise<XaiSubscriptionAccountMetadata[]> {
444
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
445
+ const rows = await scopedDb
446
+ .select(xaiCredentialMetadataColumns)
447
+ .from(schema.xaiSubscriptionCredentials)
448
+ .where(eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId))
449
+ .orderBy(
450
+ asc(schema.xaiSubscriptionCredentials.createdAt),
451
+ asc(schema.xaiSubscriptionCredentials.id),
452
+ );
453
+ return rows.map(metadataFromRow);
454
+ });
455
+ }
456
+
457
+ /**
458
+ * Metadata-only readiness check for the SuperGrok model catalog.
459
+ *
460
+ * Like Codex, allocator eligibility is runtime scheduling state, not connection
461
+ * readiness. With rotation enabled any healthy account makes the rail ready;
462
+ * with rotation disabled the explicit active account is authoritative.
463
+ */
464
+ export async function workspaceXaiSubscriptionActive(
465
+ db: Database,
466
+ settings: { readonly supergrokSubscriptionEnabled: boolean },
467
+ workspaceId: string,
468
+ subjectId: string,
469
+ ): Promise<boolean> {
470
+ if (!settings.supergrokSubscriptionEnabled) return false;
471
+ const authoritySnapshot = await resolveXaiProviderAccountAuthoritySnapshotForAcceptance(db, {
472
+ workspaceId,
473
+ subjectId,
474
+ });
475
+ const [accounts, rotation] = await Promise.all([
476
+ listXaiSubscriptionAccountsMetadata(db, { workspaceId, subjectId }),
477
+ getXaiRotationSettings(db, { workspaceId, subjectId, authoritySnapshot }),
478
+ ]);
479
+ const activeCredentialId = rotation?.activeCredentialId ?? null;
480
+ const now = new Date();
481
+ const eligible = (account: XaiSubscriptionAccountMetadata) =>
482
+ account.status === "active" &&
483
+ account.allocatorEnabled &&
484
+ (!account.exhaustedUntil || account.exhaustedUntil <= now);
485
+ if (rotation?.rotationEnabled !== false) {
486
+ return accounts.some(eligible);
487
+ }
488
+ return accounts.some((account) => account.id === activeCredentialId && eligible(account));
489
+ }
490
+
491
+ export async function getXaiSubscriptionAccountMetadata(
492
+ db: Database,
493
+ input: { workspaceId: string; subjectId: string; credentialId: string },
494
+ ): Promise<XaiSubscriptionAccountMetadata | null> {
495
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
496
+ const [row] = await scopedDb
497
+ .select(xaiCredentialMetadataColumns)
498
+ .from(schema.xaiSubscriptionCredentials)
499
+ .where(
500
+ and(
501
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
502
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
503
+ ),
504
+ )
505
+ .limit(1);
506
+ return row ? metadataFromRow(row) : null;
507
+ });
508
+ }
509
+
510
+ export async function getXaiSubscriptionAccountAuthoritySnapshot(
511
+ db: Database,
512
+ input: { workspaceId: string; subjectId: string; credentialId: string },
513
+ ): Promise<XaiAuthoritySnapshot | null> {
514
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
515
+ const [row] = await scopedDb
516
+ .select({
517
+ authorityScope: schema.xaiSubscriptionCredentials.authorityScope,
518
+ authorityGeneration:
519
+ schema.xaiSubscriptionCredentials.organizationUserResourceAuthorityGeneration,
520
+ })
521
+ .from(schema.xaiSubscriptionCredentials)
522
+ .where(
523
+ and(
524
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
525
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
526
+ ),
527
+ )
528
+ .limit(1);
529
+ if (!row) return null;
530
+ return row.authorityScope === "workspace"
531
+ ? WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1
532
+ : XaiProviderAccountAuthoritySnapshotV1.parse({
533
+ version: 1,
534
+ scope: "user",
535
+ authorityGeneration: row.authorityGeneration,
536
+ });
537
+ });
538
+ }
539
+
540
+ /**
541
+ * Resolve the provider-account authority frozen on a newly accepted human turn.
542
+ * Workspace authority is the default. A caller's private pool becomes effective
543
+ * only after that exact pool has an active credential pointer, which is set by
544
+ * the caller's explicit private connection/activation action.
545
+ */
546
+ export async function resolveXaiProviderAccountAuthoritySnapshotForAcceptance(
547
+ db: Database,
548
+ input: { workspaceId: string; subjectId: string },
549
+ ): Promise<XaiAuthoritySnapshot> {
550
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
551
+ return await resolveXaiProviderAccountAuthoritySnapshotForAcceptanceInTransaction(scopedDb, {
552
+ workspaceId: input.workspaceId,
553
+ });
554
+ });
555
+ }
556
+
557
+ /** Transaction-local acceptance resolver. The caller must already have set the
558
+ * exact authenticated subject GUC on this same transaction. */
559
+ export async function resolveXaiProviderAccountAuthoritySnapshotForAcceptanceInTransaction(
560
+ db: Database,
561
+ input: { workspaceId: string },
562
+ ): Promise<XaiAuthoritySnapshot> {
563
+ const [row] = await db
564
+ .select({
565
+ authorityGeneration:
566
+ schema.xaiSubscriptionCredentials.organizationUserResourceAuthorityGeneration,
567
+ })
568
+ .from(schema.xaiRotationSettings)
569
+ .innerJoin(
570
+ schema.xaiSubscriptionCredentials,
571
+ and(
572
+ eq(schema.xaiSubscriptionCredentials.id, schema.xaiRotationSettings.activeCredentialId),
573
+ eq(schema.xaiSubscriptionCredentials.workspaceId, schema.xaiRotationSettings.workspaceId),
574
+ eq(schema.xaiSubscriptionCredentials.authorityScope, "user"),
575
+ eq(schema.xaiSubscriptionCredentials.status, "active"),
576
+ ),
577
+ )
578
+ .where(
579
+ and(
580
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
581
+ eq(schema.xaiRotationSettings.authorityScope, "user"),
582
+ ),
583
+ )
584
+ .limit(1);
585
+ if (!row) return WORKSPACE_XAI_PROVIDER_ACCOUNT_AUTHORITY_SNAPSHOT_V1;
586
+ return XaiProviderAccountAuthoritySnapshotV1.parse({
587
+ version: 1,
588
+ scope: "user",
589
+ authorityGeneration: row.authorityGeneration,
590
+ });
591
+ }
592
+
593
+ export async function updateXaiSubscriptionAccountSettings(
594
+ db: Database,
595
+ input: {
596
+ workspaceId: string;
597
+ subjectId: string;
598
+ credentialId: string;
599
+ expectedVersion: number;
600
+ label?: string | null;
601
+ allocatorEnabled?: boolean;
602
+ },
603
+ ): Promise<XaiSubscriptionAccountMetadata> {
604
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
605
+ const [row] = await scopedDb
606
+ .update(schema.xaiSubscriptionCredentials)
607
+ .set({
608
+ ...(input.label !== undefined ? { label: input.label } : {}),
609
+ ...(input.allocatorEnabled !== undefined
610
+ ? {
611
+ allocatorEnabled: input.allocatorEnabled,
612
+ allocatorVersion: sql`${schema.xaiSubscriptionCredentials.allocatorVersion} + 1`,
613
+ }
614
+ : {}),
615
+ version: sql`${schema.xaiSubscriptionCredentials.version} + 1`,
616
+ updatedAt: new Date(),
617
+ })
618
+ .where(
619
+ and(
620
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
621
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
622
+ eq(schema.xaiSubscriptionCredentials.version, input.expectedVersion),
623
+ ),
624
+ )
625
+ .returning(xaiCredentialMetadataColumns);
626
+ if (!row) throw new Error("xAI subscription account settings changed");
627
+ return metadataFromRow(row);
628
+ });
629
+ }
630
+
631
+ export type XaiAllocatorUpdateResult =
632
+ | {
633
+ kind: "updated" | "unchanged" | "conflict";
634
+ allocatorEnabled: boolean;
635
+ allocatorVersion: number;
636
+ allocatorUpdatedAt: Date | null;
637
+ }
638
+ | { kind: "not_found" };
639
+
640
+ export async function updateXaiAllocatorEligibility(
641
+ db: Database,
642
+ input: {
643
+ workspaceId: string;
644
+ subjectId: string;
645
+ credentialId: string;
646
+ enabled: boolean;
647
+ expectedVersion: number;
648
+ },
649
+ ): Promise<XaiAllocatorUpdateResult> {
650
+ return await withWorkspaceSubjectRls(
651
+ db,
652
+ input.workspaceId,
653
+ input.subjectId,
654
+ async (scopedDb) =>
655
+ await scopedDb.transaction(async (tx) => {
656
+ const [row] = await tx
657
+ .select({
658
+ allocatorEnabled: schema.xaiSubscriptionCredentials.allocatorEnabled,
659
+ allocatorVersion: schema.xaiSubscriptionCredentials.allocatorVersion,
660
+ allocatorUpdatedAt: schema.xaiSubscriptionCredentials.allocatorUpdatedAt,
661
+ })
662
+ .from(schema.xaiSubscriptionCredentials)
663
+ .where(
664
+ and(
665
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
666
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
667
+ ),
668
+ )
669
+ .for("update")
670
+ .limit(1);
671
+ if (!row) return { kind: "not_found" } as const;
672
+ const current = {
673
+ allocatorEnabled: row.allocatorEnabled,
674
+ allocatorVersion: row.allocatorVersion,
675
+ allocatorUpdatedAt: row.allocatorUpdatedAt,
676
+ };
677
+ if (row.allocatorEnabled === input.enabled) {
678
+ return { kind: "unchanged", ...current } as const;
679
+ }
680
+ if (row.allocatorVersion !== input.expectedVersion) {
681
+ return { kind: "conflict", ...current } as const;
682
+ }
683
+ const changedAt = new Date();
684
+ const [updated] = await tx
685
+ .update(schema.xaiSubscriptionCredentials)
686
+ .set({
687
+ allocatorEnabled: input.enabled,
688
+ allocatorVersion: sql`${schema.xaiSubscriptionCredentials.allocatorVersion} + 1`,
689
+ allocatorUpdatedAt: changedAt,
690
+ })
691
+ .where(
692
+ and(
693
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
694
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
695
+ eq(schema.xaiSubscriptionCredentials.allocatorVersion, input.expectedVersion),
696
+ ),
697
+ )
698
+ .returning({
699
+ allocatorEnabled: schema.xaiSubscriptionCredentials.allocatorEnabled,
700
+ allocatorVersion: schema.xaiSubscriptionCredentials.allocatorVersion,
701
+ allocatorUpdatedAt: schema.xaiSubscriptionCredentials.allocatorUpdatedAt,
702
+ });
703
+ if (!updated) throw new Error("xAI allocator row changed while locked");
704
+ return { kind: "updated", ...updated } as const;
705
+ }),
706
+ );
707
+ }
708
+
709
+ export async function renameXaiSubscriptionAccount(
710
+ db: Database,
711
+ input: {
712
+ workspaceId: string;
713
+ subjectId: string;
714
+ credentialId: string;
715
+ label: string | null;
716
+ },
717
+ ): Promise<XaiSubscriptionAccountMetadata | null> {
718
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
719
+ const [row] = await scopedDb
720
+ .update(schema.xaiSubscriptionCredentials)
721
+ .set({
722
+ label: input.label,
723
+ version: sql`${schema.xaiSubscriptionCredentials.version} + 1`,
724
+ updatedAt: new Date(),
725
+ })
726
+ .where(
727
+ and(
728
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
729
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
730
+ ),
731
+ )
732
+ .returning(xaiCredentialMetadataColumns);
733
+ return row ? metadataFromRow(row) : null;
734
+ });
735
+ }
736
+
737
+ export async function disconnectXaiSubscriptionCredential(
738
+ db: Database,
739
+ input: {
740
+ accountId: string;
741
+ workspaceId: string;
742
+ subjectId: string;
743
+ credentialId: string;
744
+ authoritySnapshot: XaiAuthoritySnapshot;
745
+ },
746
+ ): Promise<boolean> {
747
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
748
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
749
+ const rows = await rawRows<{ disconnected: boolean }>(
750
+ scopedDb,
751
+ sql`select disconnect_xai_subscription_credential(
752
+ ${input.accountId}::uuid, ${input.workspaceId}::uuid,
753
+ ${input.subjectId}, ${input.credentialId}::uuid,
754
+ ${JSON.stringify(snapshot)}::jsonb
755
+ ) as disconnected`,
756
+ );
757
+ return rows[0]?.disconnected === true;
758
+ });
759
+ }
760
+
761
+ export async function materializeXaiCredentialForRun(
762
+ db: Database,
763
+ input: {
764
+ workspaceId: string;
765
+ subjectId: string;
766
+ credentialId: string;
767
+ authoritySnapshot: XaiAuthoritySnapshot;
768
+ encryptionKey: Uint8Array;
769
+ },
770
+ ): Promise<XaiCredentialForRun> {
771
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
772
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
773
+ const rows = await rawRows<{ id: string }>(
774
+ scopedDb,
775
+ sql`select id from revalidate_xai_subscription_authority(
776
+ ${input.workspaceId}::uuid,
777
+ ${input.subjectId},
778
+ ${input.credentialId}::uuid,
779
+ ${JSON.stringify(snapshot)}::jsonb
780
+ )`,
781
+ );
782
+ if (!rows[0]) throw new Error("xAI provider-account authority is no longer active");
783
+ const [row] = await scopedDb
784
+ .select({
785
+ ...xaiCredentialMetadataColumns,
786
+ credentialEncrypted: schema.xaiSubscriptionCredentials.credentialEncrypted,
787
+ })
788
+ .from(schema.xaiSubscriptionCredentials)
789
+ .where(
790
+ and(
791
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
792
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
793
+ eq(schema.xaiSubscriptionCredentials.status, "active"),
794
+ ),
795
+ )
796
+ .limit(1);
797
+ if (!row) throw new Error("xAI credential is unavailable");
798
+ return {
799
+ ...metadataFromRow(row),
800
+ secret: parseSecret(decryptEnvironmentValue(input.encryptionKey, row.credentialEncrypted)),
801
+ authoritySnapshot: snapshot,
802
+ };
803
+ });
804
+ }
805
+
806
+ export type XaiSerializedCredentialRefreshResult = {
807
+ credential: XaiCredentialForRun;
808
+ refreshed: boolean;
809
+ };
810
+
811
+ /**
812
+ * Refresh one connected xAI account under a database row lock.
813
+ *
814
+ * OAuth refresh tokens may rotate. Multiple sessions are allowed to share the
815
+ * same account, so an unlocked read-refresh-write sequence can make one
816
+ * successful refresh invalidate every other in-flight refresh. This operation
817
+ * re-reads the secret after acquiring the lock and skips the provider call when
818
+ * another request already installed a newer token pair.
819
+ */
820
+ export async function refreshXaiSubscriptionCredentialSerialized(
821
+ db: Database,
822
+ input: {
823
+ accountId: string;
824
+ workspaceId: string;
825
+ subjectId: string;
826
+ credentialId: string;
827
+ authoritySnapshot: XaiAuthoritySnapshot;
828
+ encryptionKey: Uint8Array;
829
+ observedAccessToken: string | undefined;
830
+ observedRefreshToken: string | undefined;
831
+ refresh: (current: XaiCredentialForRun) => Promise<{
832
+ secret: XaiCredentialSecretV1;
833
+ expiresAt: Date | null;
834
+ }>;
835
+ },
836
+ ): Promise<XaiSerializedCredentialRefreshResult> {
837
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
838
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
839
+ const authorized = await rawRows<{ id: string }>(
840
+ scopedDb,
841
+ sql`select id from revalidate_xai_subscription_authority(
842
+ ${input.workspaceId}::uuid,
843
+ ${input.subjectId},
844
+ ${input.credentialId}::uuid,
845
+ ${JSON.stringify(snapshot)}::jsonb
846
+ )`,
847
+ );
848
+ if (!authorized[0]) throw new Error("xAI provider-account authority is no longer active");
849
+
850
+ const [row] = await scopedDb
851
+ .select({
852
+ ...xaiCredentialMetadataColumns,
853
+ credentialEncrypted: schema.xaiSubscriptionCredentials.credentialEncrypted,
854
+ })
855
+ .from(schema.xaiSubscriptionCredentials)
856
+ .where(
857
+ and(
858
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
859
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
860
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
861
+ eq(schema.xaiSubscriptionCredentials.status, "active"),
862
+ ),
863
+ )
864
+ .for("update")
865
+ .limit(1);
866
+ if (!row) throw new Error("xAI credential is unavailable");
867
+
868
+ const currentSecret = parseSecret(
869
+ decryptEnvironmentValue(input.encryptionKey, row.credentialEncrypted),
870
+ );
871
+ const current: XaiCredentialForRun = {
872
+ ...metadataFromRow(row),
873
+ secret: currentSecret,
874
+ authoritySnapshot: snapshot,
875
+ };
876
+ if (
877
+ currentSecret.accessToken !== input.observedAccessToken ||
878
+ currentSecret.refreshToken !== input.observedRefreshToken
879
+ ) {
880
+ return { credential: current, refreshed: false };
881
+ }
882
+
883
+ const next = await input.refresh(current);
884
+ assertSecret(next.secret);
885
+ const credentialEncrypted = encryptEnvironmentValue(
886
+ input.encryptionKey,
887
+ JSON.stringify(next.secret),
888
+ );
889
+ const [updated] = await scopedDb
890
+ .update(schema.xaiSubscriptionCredentials)
891
+ .set({
892
+ credentialEncrypted,
893
+ expiresAt: next.expiresAt,
894
+ lastRefreshAt: new Date(),
895
+ status: "active",
896
+ lastError: null,
897
+ version: sql`${schema.xaiSubscriptionCredentials.version} + 1`,
898
+ updatedAt: new Date(),
899
+ })
900
+ .where(
901
+ and(
902
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
903
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
904
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
905
+ ),
906
+ )
907
+ .returning(xaiCredentialMetadataColumns);
908
+ if (!updated) throw new Error("xAI credential refresh lost its authority fence");
909
+ return {
910
+ credential: {
911
+ ...metadataFromRow(updated),
912
+ secret: next.secret,
913
+ authoritySnapshot: snapshot,
914
+ },
915
+ refreshed: true,
916
+ };
917
+ });
918
+ }
919
+
920
+ export async function acquireXaiCredentialLease(
921
+ db: Database,
922
+ input: {
923
+ accountId: string;
924
+ workspaceId: string;
925
+ subjectId: string;
926
+ sessionId: string;
927
+ turnId: string;
928
+ holderId: string;
929
+ authoritySnapshot: XaiAuthoritySnapshot;
930
+ pinnedCredentialId?: string | null;
931
+ pinSource?: "manual" | "policy" | null;
932
+ now?: Date;
933
+ leaseTtlMs?: number;
934
+ },
935
+ ): Promise<XaiCredentialLeaseResult> {
936
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
937
+ const now = input.now ?? new Date();
938
+ const leaseTtlMs = input.leaseTtlMs ?? XAI_CREDENTIAL_LEASE_TTL_MS;
939
+ if (!Number.isFinite(leaseTtlMs) || leaseTtlMs <= 0) {
940
+ throw new Error("xAI credential lease TTL must be positive");
941
+ }
942
+ if (!input.holderId.trim()) {
943
+ throw new Error("xAI credential lease holder id is required");
944
+ }
945
+ const leasedUntil = new Date(now.getTime() + leaseTtlMs);
946
+ return await withWorkspaceSubjectRls(
947
+ db,
948
+ input.workspaceId,
949
+ input.subjectId,
950
+ async (scopedDb) =>
951
+ await scopedDb.transaction(async (tx) => {
952
+ await assertXaiTurnAuthoritySnapshot(tx, {
953
+ accountId: input.accountId,
954
+ workspaceId: input.workspaceId,
955
+ turnId: input.turnId,
956
+ authoritySnapshot: snapshot,
957
+ });
958
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(tx, {
959
+ workspaceId: input.workspaceId,
960
+ subjectId: input.subjectId,
961
+ authoritySnapshot: snapshot,
962
+ });
963
+
964
+ await tx
965
+ .insert(schema.xaiRotationSettings)
966
+ .values({
967
+ accountId: input.accountId,
968
+ workspaceId: input.workspaceId,
969
+ authorityScope: snapshot.scope,
970
+ ownerOrganizationMembershipId: ownerMembershipId,
971
+ })
972
+ .onConflictDoNothing();
973
+ const [settings] = await tx
974
+ .select()
975
+ .from(schema.xaiRotationSettings)
976
+ .where(
977
+ and(
978
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
979
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
980
+ ownerMembershipId === null
981
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
982
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
983
+ ),
984
+ )
985
+ .for("update")
986
+ .limit(1);
987
+ if (!settings) throw new Error("xAI rotation settings are unavailable");
988
+
989
+ await tx
990
+ .delete(schema.xaiCredentialLeases)
991
+ .where(
992
+ and(
993
+ eq(schema.xaiCredentialLeases.workspaceId, input.workspaceId),
994
+ lte(schema.xaiCredentialLeases.leasedUntil, now),
995
+ ),
996
+ );
997
+
998
+ const [existing] = await tx
999
+ .select()
1000
+ .from(schema.xaiCredentialLeases)
1001
+ .where(
1002
+ and(
1003
+ eq(schema.xaiCredentialLeases.workspaceId, input.workspaceId),
1004
+ eq(schema.xaiCredentialLeases.turnId, input.turnId),
1005
+ gt(schema.xaiCredentialLeases.leasedUntil, now),
1006
+ ),
1007
+ )
1008
+ .for("update")
1009
+ .limit(1);
1010
+ if (existing) {
1011
+ const [updated] = await tx
1012
+ .update(schema.xaiCredentialLeases)
1013
+ .set({
1014
+ holderId: input.holderId,
1015
+ generation:
1016
+ existing.holderId === input.holderId
1017
+ ? existing.generation
1018
+ : existing.generation + 1,
1019
+ leasedUntil,
1020
+ updatedAt: now,
1021
+ })
1022
+ .where(eq(schema.xaiCredentialLeases.id, existing.id))
1023
+ .returning();
1024
+ const accounts = await tx
1025
+ .select(xaiCredentialMetadataColumns)
1026
+ .from(schema.xaiSubscriptionCredentials)
1027
+ .where(eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId));
1028
+ return {
1029
+ credentialId: updated!.credentialId,
1030
+ rotationEnabled: settings.rotationEnabled,
1031
+ reused: true,
1032
+ holderId: updated!.holderId,
1033
+ generation: updated!.generation,
1034
+ leasedUntil: updated!.leasedUntil,
1035
+ accounts: accounts.map(metadataFromRow),
1036
+ };
1037
+ }
1038
+
1039
+ const candidates = await tx
1040
+ .select(xaiCredentialAllocationColumns)
1041
+ .from(schema.xaiSubscriptionCredentials)
1042
+ .where(
1043
+ and(
1044
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
1045
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
1046
+ eq(schema.xaiSubscriptionCredentials.authorityScope, snapshot.scope),
1047
+ ownerMembershipId === null
1048
+ ? isNull(schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId)
1049
+ : eq(
1050
+ schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId,
1051
+ ownerMembershipId,
1052
+ ),
1053
+ ),
1054
+ )
1055
+ .orderBy(
1056
+ asc(schema.xaiSubscriptionCredentials.createdAt),
1057
+ asc(schema.xaiSubscriptionCredentials.id),
1058
+ );
1059
+ const eligible = candidates.filter(
1060
+ (candidate) =>
1061
+ candidate.status === "active" &&
1062
+ candidate.allocatorEnabled &&
1063
+ (!candidate.exhaustedUntil || candidate.exhaustedUntil <= now),
1064
+ );
1065
+ const pinned = input.pinnedCredentialId
1066
+ ? eligible.find((candidate) => candidate.id === input.pinnedCredentialId)
1067
+ : undefined;
1068
+ const pinIsManual = input.pinnedCredentialId && input.pinSource !== "policy";
1069
+ const selected = pinIsManual
1070
+ ? pinned
1071
+ : settings.rotationEnabled
1072
+ ? (pinned ??
1073
+ (eligible.length > 0
1074
+ ? eligible[xaiCredentialShardIndex(input.sessionId, eligible.length)]
1075
+ : undefined))
1076
+ : eligible.find((candidate) => candidate.id === settings.activeCredentialId);
1077
+ if (!selected) {
1078
+ return {
1079
+ credentialId: null,
1080
+ rotationEnabled: settings.rotationEnabled,
1081
+ reused: false,
1082
+ holderId: null,
1083
+ generation: null,
1084
+ leasedUntil: null,
1085
+ accounts: candidates.map(metadataFromRow),
1086
+ };
1087
+ }
1088
+ const [lease] = await tx
1089
+ .insert(schema.xaiCredentialLeases)
1090
+ .values({
1091
+ accountId: input.accountId,
1092
+ workspaceId: input.workspaceId,
1093
+ authorityScope: snapshot.scope,
1094
+ ownerOrganizationMembershipId: ownerMembershipId,
1095
+ credentialId: selected.id,
1096
+ turnId: input.turnId,
1097
+ holderId: input.holderId,
1098
+ leasedUntil,
1099
+ })
1100
+ .returning();
1101
+ await tx
1102
+ .update(schema.xaiSubscriptionCredentials)
1103
+ .set({
1104
+ selectionCount: sql`${schema.xaiSubscriptionCredentials.selectionCount} + 1`,
1105
+ lastSelectedAt: now,
1106
+ updatedAt: now,
1107
+ })
1108
+ .where(eq(schema.xaiSubscriptionCredentials.id, selected.id));
1109
+ // A session policy/manual home must never move the workspace-global
1110
+ // active pointer. A missing pointer is bootstrapped once for rotation-off
1111
+ // fallback and UI state, but healthy sharded turns never churn it.
1112
+ if (settings.activeCredentialId === null) {
1113
+ await tx
1114
+ .update(schema.xaiRotationSettings)
1115
+ .set({
1116
+ activeCredentialId: selected.id,
1117
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1118
+ updatedAt: now,
1119
+ })
1120
+ .where(eq(schema.xaiRotationSettings.id, settings.id));
1121
+ }
1122
+ return {
1123
+ credentialId: selected.id,
1124
+ rotationEnabled: settings.rotationEnabled,
1125
+ reused: false,
1126
+ holderId: lease!.holderId,
1127
+ generation: lease!.generation,
1128
+ leasedUntil: lease!.leasedUntil,
1129
+ accounts: candidates.map(metadataFromRow),
1130
+ };
1131
+ }),
1132
+ );
1133
+ }
1134
+
1135
+ /**
1136
+ * Select an authorized connected account for a non-turn operation (voice,
1137
+ * transcription, media). Rotation uses the same stable session/request shard
1138
+ * as turns, but does not create a capacity lease: one account may serve many
1139
+ * concurrent upstream sessions just like Codex subscriptions.
1140
+ */
1141
+ export async function selectXaiCredentialForUse(
1142
+ db: Database,
1143
+ input: {
1144
+ accountId: string;
1145
+ workspaceId: string;
1146
+ subjectId: string;
1147
+ authoritySnapshot: XaiAuthoritySnapshot;
1148
+ shardKey: string;
1149
+ pinnedCredentialId?: string | null;
1150
+ pinSource?: "manual" | "policy" | null;
1151
+ now?: Date;
1152
+ },
1153
+ ): Promise<{
1154
+ credentialId: string | null;
1155
+ rotationEnabled: boolean;
1156
+ accounts: XaiSubscriptionAccountMetadata[];
1157
+ }> {
1158
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1159
+ const now = input.now ?? new Date();
1160
+ return await withWorkspaceSubjectRls(
1161
+ db,
1162
+ input.workspaceId,
1163
+ input.subjectId,
1164
+ async (scopedDb) =>
1165
+ await scopedDb.transaction(async (tx) => {
1166
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(tx, {
1167
+ workspaceId: input.workspaceId,
1168
+ subjectId: input.subjectId,
1169
+ authoritySnapshot: snapshot,
1170
+ });
1171
+ await tx
1172
+ .insert(schema.xaiRotationSettings)
1173
+ .values({
1174
+ accountId: input.accountId,
1175
+ workspaceId: input.workspaceId,
1176
+ authorityScope: snapshot.scope,
1177
+ ownerOrganizationMembershipId: ownerMembershipId,
1178
+ })
1179
+ .onConflictDoNothing();
1180
+ const [settings] = await tx
1181
+ .select()
1182
+ .from(schema.xaiRotationSettings)
1183
+ .where(
1184
+ and(
1185
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1186
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
1187
+ ownerMembershipId === null
1188
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
1189
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
1190
+ ),
1191
+ )
1192
+ .limit(1);
1193
+ if (!settings) throw new Error("xAI rotation settings are unavailable");
1194
+ const candidates = await tx
1195
+ .select(xaiCredentialAllocationColumns)
1196
+ .from(schema.xaiSubscriptionCredentials)
1197
+ .where(
1198
+ and(
1199
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
1200
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
1201
+ eq(schema.xaiSubscriptionCredentials.authorityScope, snapshot.scope),
1202
+ ownerMembershipId === null
1203
+ ? isNull(schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId)
1204
+ : eq(
1205
+ schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId,
1206
+ ownerMembershipId,
1207
+ ),
1208
+ ),
1209
+ )
1210
+ .orderBy(
1211
+ asc(schema.xaiSubscriptionCredentials.createdAt),
1212
+ asc(schema.xaiSubscriptionCredentials.id),
1213
+ );
1214
+ const eligible = candidates.filter(
1215
+ (candidate) =>
1216
+ candidate.status === "active" &&
1217
+ candidate.allocatorEnabled &&
1218
+ (!candidate.exhaustedUntil || candidate.exhaustedUntil <= now),
1219
+ );
1220
+ const pinned = input.pinnedCredentialId
1221
+ ? eligible.find((candidate) => candidate.id === input.pinnedCredentialId)
1222
+ : undefined;
1223
+ const selected =
1224
+ input.pinnedCredentialId && input.pinSource !== "policy"
1225
+ ? pinned
1226
+ : settings.rotationEnabled
1227
+ ? (pinned ??
1228
+ (eligible.length > 0
1229
+ ? eligible[xaiCredentialShardIndex(input.shardKey, eligible.length)]
1230
+ : undefined))
1231
+ : eligible.find((candidate) => candidate.id === settings.activeCredentialId);
1232
+ return {
1233
+ credentialId: selected?.id ?? null,
1234
+ rotationEnabled: settings.rotationEnabled,
1235
+ accounts: candidates.map(metadataFromRow),
1236
+ };
1237
+ }),
1238
+ );
1239
+ }
1240
+
1241
+ export async function releaseXaiCredentialLease(
1242
+ db: Database,
1243
+ input: {
1244
+ workspaceId: string;
1245
+ subjectId: string;
1246
+ turnId: string;
1247
+ holderId: string;
1248
+ generation: number;
1249
+ },
1250
+ ): Promise<boolean> {
1251
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1252
+ const deleted = await scopedDb
1253
+ .delete(schema.xaiCredentialLeases)
1254
+ .where(
1255
+ and(
1256
+ eq(schema.xaiCredentialLeases.workspaceId, input.workspaceId),
1257
+ eq(schema.xaiCredentialLeases.turnId, input.turnId),
1258
+ eq(schema.xaiCredentialLeases.holderId, input.holderId),
1259
+ eq(schema.xaiCredentialLeases.generation, input.generation),
1260
+ ),
1261
+ )
1262
+ .returning({ id: schema.xaiCredentialLeases.id });
1263
+ return deleted.length === 1;
1264
+ });
1265
+ }
1266
+
1267
+ export async function heartbeatXaiCredentialLeaseUntil(
1268
+ db: Database,
1269
+ input: {
1270
+ workspaceId: string;
1271
+ subjectId: string;
1272
+ turnId: string;
1273
+ holderId: string;
1274
+ generation: number;
1275
+ leaseTtlMs?: number;
1276
+ now?: Date;
1277
+ },
1278
+ ): Promise<Date | null> {
1279
+ const now = input.now ?? new Date();
1280
+ const leaseTtlMs = input.leaseTtlMs ?? XAI_CREDENTIAL_LEASE_TTL_MS;
1281
+ if (!Number.isFinite(leaseTtlMs) || leaseTtlMs <= 0) {
1282
+ throw new Error("xAI credential lease TTL must be positive");
1283
+ }
1284
+ const leasedUntil = new Date(now.getTime() + leaseTtlMs);
1285
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1286
+ const [row] = await scopedDb
1287
+ .update(schema.xaiCredentialLeases)
1288
+ .set({ leasedUntil, updatedAt: now })
1289
+ .where(
1290
+ and(
1291
+ eq(schema.xaiCredentialLeases.workspaceId, input.workspaceId),
1292
+ eq(schema.xaiCredentialLeases.turnId, input.turnId),
1293
+ eq(schema.xaiCredentialLeases.holderId, input.holderId),
1294
+ eq(schema.xaiCredentialLeases.generation, input.generation),
1295
+ gt(schema.xaiCredentialLeases.leasedUntil, now),
1296
+ ),
1297
+ )
1298
+ .returning({ leasedUntil: schema.xaiCredentialLeases.leasedUntil });
1299
+ return row?.leasedUntil ?? null;
1300
+ });
1301
+ }
1302
+
1303
+ export async function getXaiRotationSettings(
1304
+ db: Database,
1305
+ input: {
1306
+ workspaceId: string;
1307
+ subjectId: string;
1308
+ authoritySnapshot: XaiAuthoritySnapshot;
1309
+ },
1310
+ ): Promise<typeof schema.xaiRotationSettings.$inferSelect | null> {
1311
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1312
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1313
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1314
+ workspaceId: input.workspaceId,
1315
+ subjectId: input.subjectId,
1316
+ authoritySnapshot: snapshot,
1317
+ });
1318
+ const rows = await scopedDb
1319
+ .select()
1320
+ .from(schema.xaiRotationSettings)
1321
+ .where(
1322
+ and(
1323
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1324
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
1325
+ ownerMembershipId === null
1326
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
1327
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
1328
+ ),
1329
+ );
1330
+ return rows[0] ?? null;
1331
+ });
1332
+ }
1333
+
1334
+ export async function ensureXaiRotationSettings(
1335
+ db: Database,
1336
+ input: {
1337
+ accountId: string;
1338
+ workspaceId: string;
1339
+ subjectId: string;
1340
+ authoritySnapshot: XaiAuthoritySnapshot;
1341
+ },
1342
+ ): Promise<typeof schema.xaiRotationSettings.$inferSelect> {
1343
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1344
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1345
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1346
+ workspaceId: input.workspaceId,
1347
+ subjectId: input.subjectId,
1348
+ authoritySnapshot: snapshot,
1349
+ });
1350
+ const [row] = await scopedDb
1351
+ .insert(schema.xaiRotationSettings)
1352
+ .values({
1353
+ accountId: input.accountId,
1354
+ workspaceId: input.workspaceId,
1355
+ authorityScope: snapshot.scope,
1356
+ ownerOrganizationMembershipId: ownerMembershipId,
1357
+ })
1358
+ .onConflictDoNothing()
1359
+ .returning();
1360
+ if (row) return row;
1361
+ const [current] = await scopedDb
1362
+ .select()
1363
+ .from(schema.xaiRotationSettings)
1364
+ .where(
1365
+ and(
1366
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1367
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
1368
+ ownerMembershipId === null
1369
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
1370
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
1371
+ ),
1372
+ )
1373
+ .limit(1);
1374
+ if (!current) throw new Error("xAI rotation settings are unavailable");
1375
+ return current;
1376
+ });
1377
+ }
1378
+
1379
+ export async function setActiveXaiCredential(
1380
+ db: Database,
1381
+ input: {
1382
+ accountId: string;
1383
+ workspaceId: string;
1384
+ subjectId: string;
1385
+ authoritySnapshot: XaiAuthoritySnapshot;
1386
+ credentialId: string;
1387
+ },
1388
+ ): Promise<boolean> {
1389
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1390
+ return await withWorkspaceSubjectRls(
1391
+ db,
1392
+ input.workspaceId,
1393
+ input.subjectId,
1394
+ async (scopedDb) =>
1395
+ await scopedDb.transaction(async (tx) => {
1396
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(tx, {
1397
+ workspaceId: input.workspaceId,
1398
+ subjectId: input.subjectId,
1399
+ authoritySnapshot: snapshot,
1400
+ });
1401
+ await assertXaiCredentialInPool(tx, {
1402
+ accountId: input.accountId,
1403
+ workspaceId: input.workspaceId,
1404
+ credentialId: input.credentialId,
1405
+ authorityScope: snapshot.scope,
1406
+ ownerMembershipId,
1407
+ });
1408
+ const [credential] = await tx
1409
+ .select({ status: schema.xaiSubscriptionCredentials.status })
1410
+ .from(schema.xaiSubscriptionCredentials)
1411
+ .where(eq(schema.xaiSubscriptionCredentials.id, input.credentialId))
1412
+ .limit(1);
1413
+ if (!credential || credential.status !== "active") return false;
1414
+ await tx
1415
+ .insert(schema.xaiRotationSettings)
1416
+ .values({
1417
+ accountId: input.accountId,
1418
+ workspaceId: input.workspaceId,
1419
+ authorityScope: snapshot.scope,
1420
+ ownerOrganizationMembershipId: ownerMembershipId,
1421
+ activeCredentialId: input.credentialId,
1422
+ })
1423
+ .onConflictDoUpdate({
1424
+ target: [
1425
+ schema.xaiRotationSettings.workspaceId,
1426
+ schema.xaiRotationSettings.authorityScope,
1427
+ schema.xaiRotationSettings.ownerOrganizationMembershipId,
1428
+ ],
1429
+ set: {
1430
+ activeCredentialId: input.credentialId,
1431
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1432
+ updatedAt: new Date(),
1433
+ },
1434
+ });
1435
+ if (snapshot.scope === "workspace") {
1436
+ // Workspace is the deliberate default. Selecting a workspace account
1437
+ // also opts the current subject out of their private pool; FORCE RLS
1438
+ // limits this update to that subject's visible user-scoped row.
1439
+ await tx
1440
+ .update(schema.xaiRotationSettings)
1441
+ .set({
1442
+ activeCredentialId: null,
1443
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1444
+ updatedAt: new Date(),
1445
+ })
1446
+ .where(
1447
+ and(
1448
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1449
+ eq(schema.xaiRotationSettings.authorityScope, "user"),
1450
+ ),
1451
+ );
1452
+ }
1453
+ return true;
1454
+ }),
1455
+ );
1456
+ }
1457
+
1458
+ export async function setInitialActiveXaiCredential(
1459
+ db: Database,
1460
+ input: {
1461
+ accountId: string;
1462
+ workspaceId: string;
1463
+ subjectId: string;
1464
+ authoritySnapshot: XaiAuthoritySnapshot;
1465
+ credentialId: string;
1466
+ },
1467
+ ): Promise<boolean> {
1468
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1469
+ return await withWorkspaceSubjectRls(
1470
+ db,
1471
+ input.workspaceId,
1472
+ input.subjectId,
1473
+ async (scopedDb) =>
1474
+ await scopedDb.transaction(async (tx) => {
1475
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(tx, {
1476
+ workspaceId: input.workspaceId,
1477
+ subjectId: input.subjectId,
1478
+ authoritySnapshot: snapshot,
1479
+ });
1480
+ await assertXaiCredentialInPool(tx, {
1481
+ accountId: input.accountId,
1482
+ workspaceId: input.workspaceId,
1483
+ credentialId: input.credentialId,
1484
+ authorityScope: snapshot.scope,
1485
+ ownerMembershipId,
1486
+ });
1487
+ await tx
1488
+ .insert(schema.xaiRotationSettings)
1489
+ .values({
1490
+ accountId: input.accountId,
1491
+ workspaceId: input.workspaceId,
1492
+ authorityScope: snapshot.scope,
1493
+ ownerOrganizationMembershipId: ownerMembershipId,
1494
+ })
1495
+ .onConflictDoNothing();
1496
+ const [updated] = await tx
1497
+ .update(schema.xaiRotationSettings)
1498
+ .set({
1499
+ activeCredentialId: input.credentialId,
1500
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1501
+ updatedAt: new Date(),
1502
+ })
1503
+ .where(
1504
+ and(
1505
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1506
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
1507
+ ownerMembershipId === null
1508
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
1509
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
1510
+ isNull(schema.xaiRotationSettings.activeCredentialId),
1511
+ ),
1512
+ )
1513
+ .returning({ id: schema.xaiRotationSettings.id });
1514
+ return updated !== undefined;
1515
+ }),
1516
+ );
1517
+ }
1518
+
1519
+ export async function disconnectXaiSubscriptionCredentialAndRepick(
1520
+ db: Database,
1521
+ input: {
1522
+ accountId: string;
1523
+ workspaceId: string;
1524
+ subjectId: string;
1525
+ credentialId: string;
1526
+ authoritySnapshot: XaiAuthoritySnapshot;
1527
+ },
1528
+ ): Promise<{ disconnected: boolean; newActiveCredentialId: string | null }> {
1529
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1530
+ return await withWorkspaceSubjectRls(
1531
+ db,
1532
+ input.workspaceId,
1533
+ input.subjectId,
1534
+ async (scopedDb) =>
1535
+ await scopedDb.transaction(async (tx) => {
1536
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(tx, {
1537
+ workspaceId: input.workspaceId,
1538
+ subjectId: input.subjectId,
1539
+ authoritySnapshot: snapshot,
1540
+ });
1541
+ const disconnected = await disconnectXaiSubscriptionCredential(tx, input);
1542
+ if (!disconnected) return { disconnected: false, newActiveCredentialId: null };
1543
+ const [settings] = await tx
1544
+ .select()
1545
+ .from(schema.xaiRotationSettings)
1546
+ .where(
1547
+ and(
1548
+ eq(schema.xaiRotationSettings.workspaceId, input.workspaceId),
1549
+ eq(schema.xaiRotationSettings.authorityScope, snapshot.scope),
1550
+ ownerMembershipId === null
1551
+ ? isNull(schema.xaiRotationSettings.ownerOrganizationMembershipId)
1552
+ : eq(schema.xaiRotationSettings.ownerOrganizationMembershipId, ownerMembershipId),
1553
+ ),
1554
+ )
1555
+ .for("update")
1556
+ .limit(1);
1557
+ if (!settings) return { disconnected: true, newActiveCredentialId: null };
1558
+ if (settings.activeCredentialId !== null) {
1559
+ return { disconnected: true, newActiveCredentialId: settings.activeCredentialId };
1560
+ }
1561
+ const [replacement] = await tx
1562
+ .select({ id: schema.xaiSubscriptionCredentials.id })
1563
+ .from(schema.xaiSubscriptionCredentials)
1564
+ .where(
1565
+ and(
1566
+ eq(schema.xaiSubscriptionCredentials.accountId, input.accountId),
1567
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
1568
+ eq(schema.xaiSubscriptionCredentials.authorityScope, snapshot.scope),
1569
+ ownerMembershipId === null
1570
+ ? isNull(schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId)
1571
+ : eq(
1572
+ schema.xaiSubscriptionCredentials.ownerOrganizationMembershipId,
1573
+ ownerMembershipId,
1574
+ ),
1575
+ eq(schema.xaiSubscriptionCredentials.status, "active"),
1576
+ ),
1577
+ )
1578
+ .orderBy(
1579
+ asc(schema.xaiSubscriptionCredentials.createdAt),
1580
+ asc(schema.xaiSubscriptionCredentials.id),
1581
+ )
1582
+ .limit(1);
1583
+ await tx
1584
+ .update(schema.xaiRotationSettings)
1585
+ .set({
1586
+ activeCredentialId: replacement?.id ?? null,
1587
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1588
+ updatedAt: new Date(),
1589
+ })
1590
+ .where(eq(schema.xaiRotationSettings.id, settings.id));
1591
+ return { disconnected: true, newActiveCredentialId: replacement?.id ?? null };
1592
+ }),
1593
+ );
1594
+ }
1595
+
1596
+ export async function updateXaiRotationSettings(
1597
+ db: Database,
1598
+ input: {
1599
+ workspaceId: string;
1600
+ subjectId: string;
1601
+ authoritySnapshot: XaiAuthoritySnapshot;
1602
+ expectedVersion: number;
1603
+ rotationEnabled: boolean;
1604
+ },
1605
+ ): Promise<typeof schema.xaiRotationSettings.$inferSelect> {
1606
+ const current = await getXaiRotationSettings(db, input);
1607
+ if (!current || current.version !== input.expectedVersion) {
1608
+ throw new Error("xAI rotation settings changed");
1609
+ }
1610
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1611
+ const [row] = await scopedDb
1612
+ .update(schema.xaiRotationSettings)
1613
+ .set({
1614
+ rotationEnabled: input.rotationEnabled,
1615
+ version: sql`${schema.xaiRotationSettings.version} + 1`,
1616
+ updatedAt: new Date(),
1617
+ })
1618
+ .where(
1619
+ and(
1620
+ eq(schema.xaiRotationSettings.id, current.id),
1621
+ eq(schema.xaiRotationSettings.version, input.expectedVersion),
1622
+ ),
1623
+ )
1624
+ .returning();
1625
+ if (!row) throw new Error("xAI rotation settings changed");
1626
+ return row;
1627
+ });
1628
+ }
1629
+
1630
+ export async function setXaiSessionAccountPin(
1631
+ db: Database,
1632
+ input: {
1633
+ accountId: string;
1634
+ workspaceId: string;
1635
+ subjectId: string;
1636
+ sessionId: string;
1637
+ authoritySnapshot: XaiAuthoritySnapshot;
1638
+ credentialId: string | null;
1639
+ pinSource: "manual" | "policy" | null;
1640
+ /** undefined = unconditional human write; null = row must not exist. */
1641
+ expectedVersion?: number | null;
1642
+ },
1643
+ ): Promise<typeof schema.xaiSessionAccountPins.$inferSelect> {
1644
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1645
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1646
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1647
+ workspaceId: input.workspaceId,
1648
+ subjectId: input.subjectId,
1649
+ authoritySnapshot: snapshot,
1650
+ });
1651
+ if (input.credentialId) {
1652
+ await assertXaiCredentialInPool(scopedDb, {
1653
+ accountId: input.accountId,
1654
+ workspaceId: input.workspaceId,
1655
+ credentialId: input.credentialId,
1656
+ authorityScope: snapshot.scope,
1657
+ ownerMembershipId,
1658
+ });
1659
+ }
1660
+ const [row] = await scopedDb
1661
+ .insert(schema.xaiSessionAccountPins)
1662
+ .values({
1663
+ accountId: input.accountId,
1664
+ workspaceId: input.workspaceId,
1665
+ sessionId: input.sessionId,
1666
+ authorityScope: snapshot.scope,
1667
+ ownerOrganizationMembershipId: ownerMembershipId,
1668
+ pinnedCredentialId: input.credentialId,
1669
+ pinSource: input.credentialId ? input.pinSource : null,
1670
+ })
1671
+ .onConflictDoUpdate({
1672
+ target: [
1673
+ schema.xaiSessionAccountPins.workspaceId,
1674
+ schema.xaiSessionAccountPins.sessionId,
1675
+ schema.xaiSessionAccountPins.authorityScope,
1676
+ schema.xaiSessionAccountPins.ownerOrganizationMembershipId,
1677
+ ],
1678
+ set: {
1679
+ pinnedCredentialId: input.credentialId,
1680
+ pinSource: input.credentialId ? input.pinSource : null,
1681
+ version: sql`${schema.xaiSessionAccountPins.version} + 1`,
1682
+ updatedAt: new Date(),
1683
+ },
1684
+ ...(input.expectedVersion !== undefined
1685
+ ? {
1686
+ setWhere:
1687
+ input.expectedVersion === null
1688
+ ? sql`false`
1689
+ : eq(schema.xaiSessionAccountPins.version, input.expectedVersion),
1690
+ }
1691
+ : {}),
1692
+ })
1693
+ .returning();
1694
+ if (!row) throw new Error("xAI session pin changed");
1695
+ return row;
1696
+ });
1697
+ }
1698
+
1699
+ export async function getXaiSessionAccountPin(
1700
+ db: Database,
1701
+ input: {
1702
+ workspaceId: string;
1703
+ subjectId: string;
1704
+ sessionId: string;
1705
+ authoritySnapshot: XaiAuthoritySnapshot;
1706
+ },
1707
+ ): Promise<typeof schema.xaiSessionAccountPins.$inferSelect | null> {
1708
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1709
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1710
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1711
+ workspaceId: input.workspaceId,
1712
+ subjectId: input.subjectId,
1713
+ authoritySnapshot: snapshot,
1714
+ });
1715
+ const [row] = await scopedDb
1716
+ .select()
1717
+ .from(schema.xaiSessionAccountPins)
1718
+ .where(
1719
+ and(
1720
+ eq(schema.xaiSessionAccountPins.workspaceId, input.workspaceId),
1721
+ eq(schema.xaiSessionAccountPins.sessionId, input.sessionId),
1722
+ eq(schema.xaiSessionAccountPins.authorityScope, snapshot.scope),
1723
+ ownerMembershipId === null
1724
+ ? isNull(schema.xaiSessionAccountPins.ownerOrganizationMembershipId)
1725
+ : eq(schema.xaiSessionAccountPins.ownerOrganizationMembershipId, ownerMembershipId),
1726
+ ),
1727
+ )
1728
+ .limit(1);
1729
+ return row ?? null;
1730
+ });
1731
+ }
1732
+
1733
+ export async function recordXaiSessionLastAccount(
1734
+ db: Database,
1735
+ input: {
1736
+ accountId: string;
1737
+ workspaceId: string;
1738
+ subjectId: string;
1739
+ sessionId: string;
1740
+ authoritySnapshot: XaiAuthoritySnapshot;
1741
+ credentialId: string;
1742
+ },
1743
+ ): Promise<typeof schema.xaiSessionAccountPins.$inferSelect> {
1744
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1745
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1746
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1747
+ workspaceId: input.workspaceId,
1748
+ subjectId: input.subjectId,
1749
+ authoritySnapshot: snapshot,
1750
+ });
1751
+ await assertXaiCredentialInPool(scopedDb, {
1752
+ accountId: input.accountId,
1753
+ workspaceId: input.workspaceId,
1754
+ credentialId: input.credentialId,
1755
+ authorityScope: snapshot.scope,
1756
+ ownerMembershipId,
1757
+ });
1758
+ const [row] = await scopedDb
1759
+ .insert(schema.xaiSessionAccountPins)
1760
+ .values({
1761
+ accountId: input.accountId,
1762
+ workspaceId: input.workspaceId,
1763
+ sessionId: input.sessionId,
1764
+ authorityScope: snapshot.scope,
1765
+ ownerOrganizationMembershipId: ownerMembershipId,
1766
+ lastCredentialId: input.credentialId,
1767
+ })
1768
+ .onConflictDoUpdate({
1769
+ target: [
1770
+ schema.xaiSessionAccountPins.workspaceId,
1771
+ schema.xaiSessionAccountPins.sessionId,
1772
+ schema.xaiSessionAccountPins.authorityScope,
1773
+ schema.xaiSessionAccountPins.ownerOrganizationMembershipId,
1774
+ ],
1775
+ set: {
1776
+ lastCredentialId: input.credentialId,
1777
+ version: sql`${schema.xaiSessionAccountPins.version} + 1`,
1778
+ updatedAt: new Date(),
1779
+ },
1780
+ })
1781
+ .returning();
1782
+ return row!;
1783
+ });
1784
+ }
1785
+
1786
+ export async function updateXaiQuotaMetadata(
1787
+ db: Database,
1788
+ input: {
1789
+ workspaceId: string;
1790
+ subjectId: string;
1791
+ credentialId: string;
1792
+ quotaUsedPercent: number | null;
1793
+ quotaResetAt: Date | null;
1794
+ quotaCheckedAt: Date;
1795
+ exhaustedUntil: Date | null;
1796
+ },
1797
+ ): Promise<boolean> {
1798
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1799
+ const updated = await scopedDb
1800
+ .update(schema.xaiSubscriptionCredentials)
1801
+ .set({
1802
+ quotaUsedPercent: input.quotaUsedPercent,
1803
+ quotaResetAt: input.quotaResetAt,
1804
+ quotaCheckedAt: input.quotaCheckedAt,
1805
+ exhaustedUntil: input.exhaustedUntil,
1806
+ updatedAt: input.quotaCheckedAt,
1807
+ })
1808
+ .where(
1809
+ and(
1810
+ eq(schema.xaiSubscriptionCredentials.workspaceId, input.workspaceId),
1811
+ eq(schema.xaiSubscriptionCredentials.id, input.credentialId),
1812
+ ),
1813
+ )
1814
+ .returning({ id: schema.xaiSubscriptionCredentials.id });
1815
+ return updated.length === 1;
1816
+ });
1817
+ }
1818
+
1819
+ export async function wakeXaiCapacityWaiters(
1820
+ db: Database,
1821
+ input: {
1822
+ workspaceId: string;
1823
+ subjectId: string;
1824
+ authoritySnapshot: XaiAuthoritySnapshot;
1825
+ reason: string;
1826
+ now?: Date;
1827
+ },
1828
+ ): Promise<number> {
1829
+ const snapshot = XaiProviderAccountAuthoritySnapshotV1.parse(input.authoritySnapshot);
1830
+ const now = input.now ?? new Date();
1831
+ return await withWorkspaceSubjectRls(db, input.workspaceId, input.subjectId, async (scopedDb) => {
1832
+ const ownerMembershipId = await resolveXaiPoolOwnerMembershipId(scopedDb, {
1833
+ workspaceId: input.workspaceId,
1834
+ subjectId: input.subjectId,
1835
+ authoritySnapshot: snapshot,
1836
+ });
1837
+ const rows = await scopedDb
1838
+ .update(schema.xaiCapacityWaiters)
1839
+ .set({
1840
+ wakeRevision: sql`${schema.xaiCapacityWaiters.wakeRevision} + 1`,
1841
+ lastWakeReason: input.reason,
1842
+ nextCheckAt: now,
1843
+ updatedAt: now,
1844
+ })
1845
+ .where(
1846
+ and(
1847
+ eq(schema.xaiCapacityWaiters.workspaceId, input.workspaceId),
1848
+ eq(schema.xaiCapacityWaiters.status, "waiting"),
1849
+ eq(schema.xaiCapacityWaiters.authorityScope, snapshot.scope),
1850
+ ownerMembershipId === null
1851
+ ? isNull(schema.xaiCapacityWaiters.ownerOrganizationMembershipId)
1852
+ : eq(schema.xaiCapacityWaiters.ownerOrganizationMembershipId, ownerMembershipId),
1853
+ ),
1854
+ )
1855
+ .returning({
1856
+ id: schema.xaiCapacityWaiters.id,
1857
+ accountId: schema.xaiCapacityWaiters.accountId,
1858
+ sessionId: schema.xaiCapacityWaiters.sessionId,
1859
+ workflowId: schema.xaiCapacityWaiters.workflowId,
1860
+ });
1861
+ for (const row of rows) {
1862
+ await scopedDb
1863
+ .insert(schema.sessionWorkflowWakeOutbox)
1864
+ .values({
1865
+ accountId: row.accountId,
1866
+ workspaceId: input.workspaceId,
1867
+ sessionId: row.sessionId,
1868
+ temporalWorkflowId: row.workflowId,
1869
+ reason: "xai_capacity",
1870
+ nextAttemptAt: now,
1871
+ })
1872
+ .onConflictDoUpdate({
1873
+ target: schema.sessionWorkflowWakeOutbox.sessionId,
1874
+ set: {
1875
+ temporalWorkflowId: row.workflowId,
1876
+ wakeRevision: sql`${schema.sessionWorkflowWakeOutbox.wakeRevision} + 1`,
1877
+ reason: "xai_capacity",
1878
+ attempts: 0,
1879
+ nextAttemptAt: sql`least(${schema.sessionWorkflowWakeOutbox.nextAttemptAt}, ${now.toISOString()}::timestamptz)`,
1880
+ lastError: null,
1881
+ updatedAt: now,
1882
+ },
1883
+ });
1884
+ }
1885
+ return rows.length;
1886
+ });
1887
+ }