@opengeni/db 0.28.9 → 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 (225) hide show
  1. package/dist/attached-browser-devices.d.ts +42 -0
  2. package/dist/attempt-tool-catalogs.d.ts +16 -0
  3. package/dist/browser-auth.d.ts +299 -0
  4. package/dist/browser-downloads.d.ts +51 -0
  5. package/dist/browser-identities.d.ts +137 -0
  6. package/dist/browser-sessions.d.ts +354 -0
  7. package/dist/browser-state-artifacts.d.ts +91 -0
  8. package/dist/canonical-human-identities.d.ts +48 -0
  9. package/dist/canonical-human-identities.js +28 -0
  10. package/dist/canonical-human-identities.js.map +1 -0
  11. package/dist/capability-components.d.ts +37 -0
  12. package/dist/capability-integrations.d.ts +194 -0
  13. package/dist/chunk-5KTIEDX7.js +13200 -0
  14. package/dist/chunk-5KTIEDX7.js.map +1 -0
  15. package/dist/chunk-CQZ4QSP5.js +995 -0
  16. package/dist/chunk-CQZ4QSP5.js.map +1 -0
  17. package/dist/chunk-F32YT7MP.js +117 -0
  18. package/dist/chunk-F32YT7MP.js.map +1 -0
  19. package/dist/chunk-JC5QPLUM.js +666 -0
  20. package/dist/chunk-JC5QPLUM.js.map +1 -0
  21. package/dist/chunk-P3RE4D2B.js +3535 -0
  22. package/dist/chunk-P3RE4D2B.js.map +1 -0
  23. package/dist/chunk-TAUTWOS5.js +259 -0
  24. package/dist/chunk-TAUTWOS5.js.map +1 -0
  25. package/dist/chunk-VKLB2ZGI.js +2389 -0
  26. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  27. package/dist/codemode-operations.d.ts +117 -0
  28. package/dist/codex-token-resolver.d.ts +2 -2
  29. package/dist/company-profile-schema.d.ts +798 -0
  30. package/dist/company-profile.d.ts +268 -0
  31. package/dist/computer-sessions.d.ts +109 -0
  32. package/dist/connection-token-resolver.d.ts +9 -4
  33. package/dist/database.d.ts +57 -3
  34. package/dist/editable-artifact-durable-export.d.ts +153 -0
  35. package/dist/editable-artifact-durable-export.js +788 -0
  36. package/dist/editable-artifact-durable-export.js.map +1 -0
  37. package/dist/editable-artifact-materialization.d.ts +123 -0
  38. package/dist/editable-artifacts-schema.d.ts +4152 -0
  39. package/dist/editable-artifacts.d.ts +610 -0
  40. package/dist/editable-artifacts.js +26 -0
  41. package/dist/editable-artifacts.js.map +1 -0
  42. package/dist/generated-images.d.ts +139 -0
  43. package/dist/index.d.ts +1122 -126
  44. package/dist/index.js +42329 -18407
  45. package/dist/index.js.map +1 -1
  46. package/dist/insights.d.ts +73 -10
  47. package/dist/integration-bindings.d.ts +70 -0
  48. package/dist/integration-facets.d.ts +99 -0
  49. package/dist/interaction-revisions.d.ts +14 -0
  50. package/dist/interaction-schema.d.ts +6036 -0
  51. package/dist/knowledge-source-sync-schema.d.ts +1679 -0
  52. package/dist/knowledge-source-sync.d.ts +274 -0
  53. package/dist/memory-governance.d.ts +2 -2
  54. package/dist/memory-slack-delivery.d.ts +101 -0
  55. package/dist/new-session-drafts.d.ts +2 -2
  56. package/dist/pack-components.d.ts +76 -0
  57. package/dist/pack-installations.d.ts +86 -0
  58. package/dist/persistence-errors.d.ts +3 -3
  59. package/dist/plugin-packages.d.ts +126 -0
  60. package/dist/preference-registry.d.ts +1 -1
  61. package/dist/provision-roles.d.ts +9 -1
  62. package/dist/provision-roles.js +1 -1
  63. package/dist/runtime-posture.d.ts +19 -6
  64. package/dist/schema.d.ts +22654 -9359
  65. package/dist/schema.js +209 -3
  66. package/dist/scoped-knowledge.d.ts +97 -1
  67. package/dist/session-control.d.ts +33 -7
  68. package/dist/session-queue-commands.d.ts +19 -11
  69. package/dist/session-realtime-context.d.ts +2 -2
  70. package/dist/session-realtime-ledger.d.ts +4 -4
  71. package/dist/session-realtime-mirror.d.ts +2 -2
  72. package/dist/session-realtime-state.d.ts +1 -1
  73. package/dist/session-realtime-terminal.d.ts +1 -1
  74. package/dist/session-realtime.d.ts +7 -7
  75. package/dist/session-tenancy.d.ts +44 -0
  76. package/dist/session-tenancy.js +16 -0
  77. package/dist/session-tenancy.js.map +1 -0
  78. package/dist/session-tool-call-settlement.d.ts +1 -1
  79. package/dist/slack-task-policy-schema.d.ts +847 -0
  80. package/dist/slack-task-policy.d.ts +65 -0
  81. package/dist/slack-user-link-access.d.ts +54 -0
  82. package/dist/task-notes-schema.d.ts +924 -0
  83. package/dist/task-notes.d.ts +85 -0
  84. package/dist/transcription-recordings.d.ts +2 -2
  85. package/dist/turn-initiator.d.ts +2 -2
  86. package/dist/video-generation.d.ts +225 -0
  87. package/dist/video-generation.js +68 -0
  88. package/dist/video-generation.js.map +1 -0
  89. package/dist/workspace-artifacts.d.ts +1 -1
  90. package/dist/workspace-instruction-policies.d.ts +1 -1
  91. package/dist/workspace-learning-policy-schema.d.ts +932 -0
  92. package/dist/workspace-learning-policy.d.ts +161 -0
  93. package/dist/xai-subscription.d.ts +326 -0
  94. package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
  95. package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
  96. package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
  97. package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
  98. package/drizzle/0187_generated_image_artifacts.sql +173 -0
  99. package/drizzle/0188_image_generation_retention_failure.sql +25 -0
  100. package/drizzle/0189_retained_session_image_formats.sql +13 -0
  101. package/drizzle/0190_timeline_annotations.sql +31 -0
  102. package/drizzle/0191_editable_artifact_engine.sql +3853 -0
  103. package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
  104. package/drizzle/0193_editable_artifact_authorization.sql +190 -0
  105. package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
  106. package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
  107. package/drizzle/0196_rig_provider_images.sql +152 -0
  108. package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
  109. package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
  110. package/drizzle/0199_workspace_learning_policy.sql +980 -0
  111. package/drizzle/0201_company_profile_authority.sql +849 -0
  112. package/drizzle/0202_document_index_checkpoints.sql +85 -0
  113. package/drizzle/0203_durable_video_generation.sql +353 -0
  114. package/drizzle/0204_video_generation_funding.sql +44 -0
  115. package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
  116. package/drizzle/0206_browser_sessions.sql +646 -0
  117. package/drizzle/0207_browser_identities.sql +379 -0
  118. package/drizzle/0208_attached_browser_devices.sql +165 -0
  119. package/drizzle/0209_computer_sessions.sql +575 -0
  120. package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
  121. package/drizzle/0211_editable_artifact_session_links.sql +58 -0
  122. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  123. package/drizzle/0212_slack_installation_bindings.sql +348 -0
  124. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  125. package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
  126. package/drizzle/0214_browser_download_saves.sql +34 -0
  127. package/drizzle/0214_session_activity_commit_gate.sql +262 -0
  128. package/drizzle/0215_browser_controller_host.sql +385 -0
  129. package/drizzle/0215_capabilities_platform.sql +980 -0
  130. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  131. package/drizzle/0216_pack_component_ownership.sql +235 -0
  132. package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
  133. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  134. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  135. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  136. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  137. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  138. package/drizzle/0220_session_channels.sql +54 -0
  139. package/drizzle/0221_sessions_channel_index.sql +5 -0
  140. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  141. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  142. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  143. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  144. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  145. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  146. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  147. package/drizzle/0227_slack_native_actions.sql +145 -0
  148. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  149. package/drizzle/0228_slack_task_policy.sql +400 -0
  150. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  151. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  152. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  153. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  154. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  155. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  156. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  157. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  158. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  159. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  160. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  161. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  162. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  163. package/drizzle/0239_task_tree_notes.sql +990 -0
  164. package/package.json +26 -5
  165. package/src/attached-browser-devices.ts +371 -0
  166. package/src/attempt-tool-catalogs.ts +131 -0
  167. package/src/browser-auth.ts +4373 -0
  168. package/src/browser-downloads.ts +517 -0
  169. package/src/browser-identities.ts +1385 -0
  170. package/src/browser-sessions.ts +2294 -0
  171. package/src/browser-state-artifacts.ts +449 -0
  172. package/src/canonical-human-identities.ts +317 -0
  173. package/src/capability-components.ts +166 -0
  174. package/src/capability-integrations.ts +1571 -0
  175. package/src/codemode-operations.ts +674 -0
  176. package/src/company-profile-schema.ts +143 -0
  177. package/src/company-profile.ts +968 -0
  178. package/src/computer-sessions.ts +1130 -0
  179. package/src/connection-token-resolver.ts +270 -25
  180. package/src/database.ts +468 -24
  181. package/src/editable-artifact-durable-export.ts +1201 -0
  182. package/src/editable-artifact-materialization.ts +893 -0
  183. package/src/editable-artifacts-schema.ts +879 -0
  184. package/src/editable-artifacts.ts +5404 -0
  185. package/src/generated-images.ts +740 -0
  186. package/src/index.ts +9963 -1465
  187. package/src/insights.ts +251 -23
  188. package/src/integration-bindings.ts +455 -0
  189. package/src/integration-facets.ts +862 -0
  190. package/src/interaction-revisions.ts +70 -0
  191. package/src/interaction-schema.ts +1606 -0
  192. package/src/knowledge-source-sync-schema.ts +192 -0
  193. package/src/knowledge-source-sync.ts +1922 -0
  194. package/src/memory-slack-delivery.ts +801 -0
  195. package/src/pack-components.ts +954 -0
  196. package/src/pack-installations.ts +886 -0
  197. package/src/persistence-errors.ts +4 -7
  198. package/src/plugin-packages.ts +1186 -0
  199. package/src/provision-roles.ts +534 -1
  200. package/src/runtime-posture-cli.ts +9 -0
  201. package/src/runtime-posture.ts +721 -5
  202. package/src/schema.ts +6270 -3159
  203. package/src/scoped-knowledge-schema.ts +1 -1
  204. package/src/scoped-knowledge.ts +655 -3
  205. package/src/session-control.ts +130 -42
  206. package/src/session-queue-commands.ts +265 -15
  207. package/src/session-realtime-context.ts +2 -2
  208. package/src/session-realtime-ledger.ts +3 -3
  209. package/src/session-realtime.ts +7 -7
  210. package/src/session-tenancy.ts +188 -0
  211. package/src/session-tool-call-settlement.ts +18 -3
  212. package/src/slack-task-policy-schema.ts +130 -0
  213. package/src/slack-task-policy.ts +341 -0
  214. package/src/slack-user-link-access.ts +702 -0
  215. package/src/task-notes-schema.ts +135 -0
  216. package/src/task-notes.ts +178 -0
  217. package/src/video-generation.ts +1530 -0
  218. package/src/workspace-artifacts.ts +1 -1
  219. package/src/workspace-learning-policy-schema.ts +139 -0
  220. package/src/workspace-learning-policy.ts +595 -0
  221. package/src/xai-subscription.ts +1887 -0
  222. package/dist/chunk-P6CUHXQZ.js +0 -7248
  223. package/dist/chunk-P6CUHXQZ.js.map +0 -1
  224. package/dist/chunk-VHBFHMPC.js +0 -1219
  225. package/dist/chunk-VHBFHMPC.js.map +0 -1
@@ -0,0 +1,995 @@
1
+ import {
2
+ rawRows,
3
+ withRlsContext,
4
+ withWorkspaceRls
5
+ } from "./chunk-JC5QPLUM.js";
6
+ import {
7
+ connections,
8
+ creditLedgerEntries,
9
+ files,
10
+ generatedVideoArtifacts,
11
+ usageEvents,
12
+ videoGenerationOperations,
13
+ videoGenerationReferences,
14
+ workspaceVideoGenerationPolicies,
15
+ workspaceVideoGenerationQuotas,
16
+ workspaces
17
+ } from "./chunk-5KTIEDX7.js";
18
+
19
+ // src/video-generation.ts
20
+ import {
21
+ GENERATED_VIDEO_MAX_BYTES,
22
+ GeneratedVideoReceipt,
23
+ GROK_IMAGINE_VIDEO_1_5_MODEL_ID,
24
+ SEEDANCE_2_5_MODEL_ID,
25
+ VideoGenerationPolicy,
26
+ VideoGenerationOperationSummary,
27
+ retainedGeneratedVideoReferenceFromFile
28
+ } from "@opengeni/contracts";
29
+ import { and, eq, inArray, sql } from "drizzle-orm";
30
+ var ACTIVE_VIDEO_GENERATION_STATUSES = [
31
+ "preparing",
32
+ "prepared",
33
+ "accepted",
34
+ "submission_uncertain",
35
+ "provider_started",
36
+ "retaining"
37
+ ];
38
+ var VideoGenerationConflictError = class extends Error {
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = "VideoGenerationConflictError";
42
+ }
43
+ };
44
+ var VideoGenerationCapacityError = class extends Error {
45
+ constructor(message) {
46
+ super(message);
47
+ this.name = "VideoGenerationCapacityError";
48
+ }
49
+ };
50
+ var VideoGenerationCreditError = class extends Error {
51
+ constructor(message = "insufficient OpenGeni credits") {
52
+ super(message);
53
+ this.name = "VideoGenerationCreditError";
54
+ }
55
+ };
56
+ async function getWorkspaceVideoGenerationPolicy(db, workspaceId) {
57
+ return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
58
+ const [row] = await scopedDb.select().from(workspaceVideoGenerationPolicies).where(eq(workspaceVideoGenerationPolicies.workspaceId, workspaceId)).limit(1);
59
+ return policyFromRow(row);
60
+ });
61
+ }
62
+ async function updateWorkspaceVideoGenerationPolicy(db, input) {
63
+ const requested = VideoGenerationPolicy.parse({
64
+ schemaVersion: 1,
65
+ revision: input.expectedRevision + 1,
66
+ fundingSource: input.fundingSource,
67
+ enabledModelIds: input.enabledModelIds,
68
+ defaultModelId: input.defaultModelId
69
+ });
70
+ assertSupportedModels(requested.enabledModelIds);
71
+ return await withRlsContext(
72
+ db,
73
+ { accountId: input.accountId, workspaceId: input.workspaceId },
74
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
75
+ const [workspace] = await tx.select({ id: workspaces.id }).from(workspaces).where(
76
+ and(
77
+ eq(workspaces.id, input.workspaceId),
78
+ eq(workspaces.accountId, input.accountId)
79
+ )
80
+ ).for("update").limit(1);
81
+ if (!workspace) throw new Error("Workspace not found");
82
+ const [current] = await tx.select().from(workspaceVideoGenerationPolicies).where(eq(workspaceVideoGenerationPolicies.workspaceId, input.workspaceId)).for("update").limit(1);
83
+ const currentRevision = current?.revision ?? 0;
84
+ if (currentRevision !== input.expectedRevision) {
85
+ throw new VideoGenerationConflictError("Video generation policy changed");
86
+ }
87
+ const now = /* @__PURE__ */ new Date();
88
+ const values = {
89
+ workspaceId: input.workspaceId,
90
+ accountId: input.accountId,
91
+ revision: currentRevision + 1,
92
+ fundingSource: requested.fundingSource,
93
+ enabledModelIds: [...requested.enabledModelIds],
94
+ defaultModelId: requested.defaultModelId,
95
+ updatedBySubjectId: input.subjectId.slice(0, 1024),
96
+ updatedAt: now
97
+ };
98
+ const [row] = current ? await tx.update(workspaceVideoGenerationPolicies).set(values).where(eq(workspaceVideoGenerationPolicies.workspaceId, input.workspaceId)).returning() : await tx.insert(workspaceVideoGenerationPolicies).values(values).returning();
99
+ if (!row) throw new Error("Video generation policy update was not persisted");
100
+ return policyFromRow(row);
101
+ })
102
+ );
103
+ }
104
+ async function admitVideoGenerationOperation(db, input) {
105
+ const reservedBytes = input.reservedBytes ?? GENERATED_VIDEO_MAX_BYTES;
106
+ return await withRlsContext(
107
+ db,
108
+ { accountId: input.accountId, workspaceId: input.workspaceId },
109
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
110
+ await tx.execute(
111
+ sql`select pg_advisory_xact_lock(hashtextextended(${`video-generation:${input.admissionKey}`}, 0))`
112
+ );
113
+ const [existing] = await tx.select().from(videoGenerationOperations).where(
114
+ and(
115
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
116
+ eq(videoGenerationOperations.admissionKey, input.admissionKey)
117
+ )
118
+ ).limit(1);
119
+ if (existing) {
120
+ assertAdmissionMatches(existing, input);
121
+ return { operation: existing, created: false };
122
+ }
123
+ const [policy] = await tx.select().from(workspaceVideoGenerationPolicies).where(eq(workspaceVideoGenerationPolicies.workspaceId, input.workspaceId)).for("update").limit(1);
124
+ if (!policy || policy.revision !== input.policyRevision || policy.fundingSource !== input.fundingSource || !policy.enabledModelIds.includes(input.modelId)) {
125
+ throw new VideoGenerationConflictError("Video generation capability changed");
126
+ }
127
+ const active = await tx.select({ id: videoGenerationOperations.id }).from(videoGenerationOperations).where(
128
+ and(
129
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
130
+ inArray(videoGenerationOperations.status, [
131
+ ...ACTIVE_VIDEO_GENERATION_STATUSES
132
+ ])
133
+ )
134
+ ).for("update");
135
+ if (active.length >= input.maxConcurrentPerWorkspace) {
136
+ throw new VideoGenerationCapacityError("Workspace video generation limit reached");
137
+ }
138
+ await tx.insert(workspaceVideoGenerationQuotas).values({
139
+ workspaceId: input.workspaceId,
140
+ accountId: input.accountId
141
+ }).onConflictDoNothing({
142
+ target: workspaceVideoGenerationQuotas.workspaceId
143
+ });
144
+ const [quota] = await tx.select().from(workspaceVideoGenerationQuotas).where(eq(workspaceVideoGenerationQuotas.workspaceId, input.workspaceId)).for("update").limit(1);
145
+ if (!quota) throw new Error("Video generation quota row disappeared");
146
+ if (quota.reservedBytes + quota.readyBytes + reservedBytes > input.workspaceQuotaBytes) {
147
+ throw new VideoGenerationCapacityError("Workspace video storage quota reached");
148
+ }
149
+ const [operation] = await tx.insert(videoGenerationOperations).values({
150
+ id: input.id,
151
+ accountId: input.accountId,
152
+ workspaceId: input.workspaceId,
153
+ sessionId: input.sessionId,
154
+ turnId: input.turnId,
155
+ attemptId: input.attemptId,
156
+ toolCallId: input.toolCallId,
157
+ admissionKey: input.admissionKey,
158
+ requestDigest: input.requestDigest,
159
+ promptDigest: input.promptDigest,
160
+ requestEncrypted: input.requestEncrypted,
161
+ modelId: input.modelId,
162
+ sourceMode: input.sourceMode,
163
+ capabilityRevision: input.capabilityRevision,
164
+ fundingSource: input.fundingSource,
165
+ pricedCostMicros: input.pricedCostMicros,
166
+ creditState: input.pricedCostMicros > 0 ? "debited" : "not_applicable",
167
+ connectionId: input.connectionId,
168
+ credentialVersion: input.credentialVersion,
169
+ credentialEncrypted: input.credentialEncrypted,
170
+ providerIdempotencyKey: input.providerIdempotencyKey,
171
+ expectedArtifactId: input.expectedArtifactId,
172
+ expectedFileId: input.expectedFileId,
173
+ reservedBytes,
174
+ recoveryDeadlineAt: input.recoveryDeadlineAt,
175
+ status: input.references.length === 0 ? "prepared" : "preparing",
176
+ admissionOutputState: "pending",
177
+ terminalUpdateState: "ineligible"
178
+ }).returning();
179
+ if (!operation) throw new Error("Video generation operation was not admitted");
180
+ if (input.pricedCostMicros > 0) {
181
+ if (input.fundingSource !== "opengeni_credits" || input.connectionId !== null) {
182
+ throw new Error("Only OpenGeni-funded video operations may debit credits");
183
+ }
184
+ await debitVideoGenerationCredits(tx, operation);
185
+ }
186
+ if (input.references.length > 0) {
187
+ await tx.insert(videoGenerationReferences).values(
188
+ input.references.map((reference) => ({
189
+ operationId: operation.id,
190
+ workspaceId: input.workspaceId,
191
+ accountId: input.accountId,
192
+ grantExpiresAt: reference.grantExpiresAt ?? null,
193
+ ...reference
194
+ }))
195
+ );
196
+ }
197
+ await tx.update(workspaceVideoGenerationQuotas).set({
198
+ reservedBytes: sql`${workspaceVideoGenerationQuotas.reservedBytes} + ${reservedBytes}`,
199
+ updatedAt: /* @__PURE__ */ new Date()
200
+ }).where(eq(workspaceVideoGenerationQuotas.workspaceId, input.workspaceId));
201
+ return { operation, created: true };
202
+ })
203
+ );
204
+ }
205
+ async function rotateVideoGenerationCredential(db, input) {
206
+ const [row] = await db.update(videoGenerationOperations).set({
207
+ credentialEncrypted: input.credentialEncrypted,
208
+ updatedAt: /* @__PURE__ */ new Date()
209
+ }).where(
210
+ and(
211
+ eq(videoGenerationOperations.accountId, input.accountId),
212
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
213
+ eq(videoGenerationOperations.id, input.operationId),
214
+ eq(videoGenerationOperations.requestDigest, input.requestDigest),
215
+ eq(videoGenerationOperations.credentialEncrypted, input.expectedCredentialEncrypted),
216
+ inArray(videoGenerationOperations.status, [
217
+ "accepted",
218
+ "submission_uncertain",
219
+ "provider_started",
220
+ "retaining"
221
+ ])
222
+ )
223
+ ).returning();
224
+ if (!row) throw new VideoGenerationConflictError("Video provider credential changed");
225
+ return row;
226
+ }
227
+ async function markVideoGenerationPrepared(db, input) {
228
+ return await mutateOperation(db, input, async (tx, operation) => {
229
+ if (operation.status === "prepared") return operation;
230
+ if (operation.status !== "preparing") throw invalidTransition(operation, "prepared");
231
+ const references = await tx.select({
232
+ stagingObjectKey: videoGenerationReferences.stagingObjectKey,
233
+ cleanedAt: videoGenerationReferences.cleanedAt
234
+ }).from(videoGenerationReferences).where(eq(videoGenerationReferences.operationId, operation.id));
235
+ if (references.length === 0 || references.some((reference) => !reference.stagingObjectKey || reference.cleanedAt)) {
236
+ throw new VideoGenerationConflictError("Video references are not sealed");
237
+ }
238
+ const [updated] = await tx.update(videoGenerationOperations).set({ status: "prepared", updatedAt: /* @__PURE__ */ new Date() }).where(eq(videoGenerationOperations.id, operation.id)).returning();
239
+ return requireRow(updated, "seal video generation references");
240
+ });
241
+ }
242
+ async function markVideoGenerationAccepted(db, input) {
243
+ return await mutateOperation(
244
+ db,
245
+ input,
246
+ async (tx, operation) => await markVideoGenerationAcceptedInTransaction(tx, {
247
+ workspaceId: input.workspaceId,
248
+ operation
249
+ })
250
+ );
251
+ }
252
+ async function markVideoGenerationAcceptedInTransaction(tx, input) {
253
+ const operation = input.operation;
254
+ if (input.toolCallId !== void 0 && operation.toolCallId !== input.toolCallId) {
255
+ throw new VideoGenerationConflictError("Video generation tool-call identity changed");
256
+ }
257
+ if (operation.admissionOutputState === "recorded") return operation;
258
+ if (operation.status !== "prepared") {
259
+ throw new VideoGenerationConflictError("Video generation is no longer awaiting acceptance");
260
+ }
261
+ const [policy] = await tx.select().from(workspaceVideoGenerationPolicies).where(eq(workspaceVideoGenerationPolicies.workspaceId, input.workspaceId)).for("share").limit(1);
262
+ const [connection] = operation.connectionId ? await tx.select({
263
+ id: connections.id,
264
+ version: connections.version,
265
+ status: connections.status,
266
+ metadata: connections.metadata
267
+ }).from(connections).where(
268
+ and(
269
+ eq(connections.workspaceId, input.workspaceId),
270
+ eq(connections.id, operation.connectionId)
271
+ )
272
+ ).for("share").limit(1) : [];
273
+ const workspaceGatewayAuthorized = operation.fundingSource === "workspace_gateway" && connection?.status === "active" && connection.version === operation.credentialVersion && connection.metadata.credentialRole === "vercel_ai_gateway";
274
+ const managedAuthorized = operation.fundingSource === "opengeni_credits" && operation.connectionId === null && (operation.creditState === "debited" || operation.creditState === "not_applicable" && operation.pricedCostMicros === 0);
275
+ const supergrokAuthorized = operation.fundingSource === "supergrok_subscription" && operation.connectionId === null && operation.credentialEncrypted !== null && operation.pricedCostMicros === 0 && operation.creditState === "not_applicable";
276
+ if (!policy || policy.fundingSource !== operation.fundingSource || !policy.enabledModelIds.includes(operation.modelId) || !workspaceGatewayAuthorized && !managedAuthorized && !supergrokAuthorized) {
277
+ throw new VideoGenerationConflictError("Video generation authorization changed");
278
+ }
279
+ const now = /* @__PURE__ */ new Date();
280
+ const [updated] = await tx.update(videoGenerationOperations).set({
281
+ status: "accepted",
282
+ admissionOutputState: "recorded",
283
+ terminalUpdateState: "pending",
284
+ nextReconcileAt: now,
285
+ updatedAt: now
286
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
287
+ return requireRow(updated, "record video generation acceptance");
288
+ }
289
+ async function cancelVideoGenerationBeforeSubmit(db, input) {
290
+ return await mutateOperation(db, input, async (tx, operation) => {
291
+ if (operation.status === "cancelled_before_submit") return operation;
292
+ if (!inPreSubmitState(operation.status) || operation.providerRequestSentAt) {
293
+ throw new VideoGenerationConflictError(
294
+ "Video generation may already have reached the provider"
295
+ );
296
+ }
297
+ const now = /* @__PURE__ */ new Date();
298
+ await releaseReservedQuota(tx, operation, false);
299
+ const creditState = await refundVideoGenerationCredits(tx, operation, now);
300
+ const [updated] = await tx.update(videoGenerationOperations).set({
301
+ status: "cancelled_before_submit",
302
+ creditState,
303
+ quotaState: "released",
304
+ terminalAt: now,
305
+ terminalUpdateState: operation.admissionOutputState === "recorded" ? "pending" : "suppressed",
306
+ credentialEncrypted: null,
307
+ requestEncrypted: null,
308
+ boundedPublicReason: boundPublicReason(input.reason),
309
+ updatedAt: now
310
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
311
+ if (!updated) throw new Error("Video generation cancellation was not recorded");
312
+ return updated;
313
+ });
314
+ }
315
+ async function cancelUnacceptedVideoGenerationsForToolCallsInTransaction(tx, input) {
316
+ const toolCallIds = [...new Set(input.toolCallIds)];
317
+ if (toolCallIds.length === 0) return [];
318
+ const operations = await tx.select().from(videoGenerationOperations).where(
319
+ and(
320
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
321
+ eq(videoGenerationOperations.sessionId, input.sessionId),
322
+ eq(videoGenerationOperations.turnId, input.turnId),
323
+ inArray(videoGenerationOperations.toolCallId, toolCallIds),
324
+ eq(videoGenerationOperations.admissionOutputState, "pending"),
325
+ inArray(videoGenerationOperations.status, ["preparing", "prepared"]),
326
+ sql`${videoGenerationOperations.providerRequestSentAt} is null`
327
+ )
328
+ ).orderBy(videoGenerationOperations.id).for("update");
329
+ const cancelled = [];
330
+ for (const operation of operations) {
331
+ await releaseReservedQuota(tx, operation, false);
332
+ const creditState = await refundVideoGenerationCredits(tx, operation, input.now);
333
+ const [updated] = await tx.update(videoGenerationOperations).set({
334
+ status: "cancelled_before_submit",
335
+ creditState,
336
+ quotaState: "released",
337
+ terminalAt: input.now,
338
+ terminalUpdateState: "suppressed",
339
+ nextReconcileAt: input.now,
340
+ providerRequestEncrypted: null,
341
+ providerRequestExpiresAt: null,
342
+ credentialEncrypted: null,
343
+ requestEncrypted: null,
344
+ privateDataEraseAfter: input.now,
345
+ boundedPublicReason: boundPublicReason(input.reason),
346
+ reconcileLeaseOwner: null,
347
+ reconcileLeaseExpiresAt: null,
348
+ updatedAt: input.now
349
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning({ id: videoGenerationOperations.id });
350
+ if (!updated) throw new Error("Interrupted video generation was not cancelled");
351
+ await tx.update(videoGenerationReferences).set({ cleanupAfter: input.now }).where(eq(videoGenerationReferences.operationId, operation.id));
352
+ cancelled.push(updated.id);
353
+ }
354
+ return cancelled;
355
+ }
356
+ async function claimVideoGenerationOperations(db, input) {
357
+ const rows = await rawRows(
358
+ db,
359
+ sql`select * from opengeni_private.claim_video_generation_operations(
360
+ ${input.owner}, ${input.leaseSeconds}, ${input.limit}
361
+ )`
362
+ );
363
+ return rows.map((row) => ({
364
+ operationId: row.operation_id,
365
+ accountId: row.account_id,
366
+ workspaceId: row.workspace_id
367
+ }));
368
+ }
369
+ async function getVideoGenerationOperation(db, workspaceId, operationId) {
370
+ return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
371
+ const [operation] = await scopedDb.select().from(videoGenerationOperations).where(
372
+ and(
373
+ eq(videoGenerationOperations.workspaceId, workspaceId),
374
+ eq(videoGenerationOperations.id, operationId)
375
+ )
376
+ ).limit(1);
377
+ if (!operation) return null;
378
+ const references = await scopedDb.select().from(videoGenerationReferences).where(eq(videoGenerationReferences.operationId, operationId)).orderBy(videoGenerationReferences.ordinal);
379
+ return { ...operation, references };
380
+ });
381
+ }
382
+ async function markVideoGenerationSubmissionUncertain(db, input) {
383
+ return await mutateOperation(db, input, async (tx, operation) => {
384
+ if (operation.status !== "submission_uncertain" || !operation.providerRequestEncrypted) {
385
+ throw invalidTransition(operation, "submission_uncertain");
386
+ }
387
+ const now = /* @__PURE__ */ new Date();
388
+ const [updated] = await tx.update(videoGenerationOperations).set({
389
+ status: "submission_uncertain",
390
+ providerRequestSentAt: operation.providerRequestSentAt ?? now,
391
+ nextReconcileAt: input.nextReconcileAt,
392
+ lastError: boundPrivateError(input.error),
393
+ reconcileLeaseOwner: null,
394
+ reconcileLeaseExpiresAt: null,
395
+ updatedAt: now
396
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
397
+ return requireRow(updated, "record uncertain video submission");
398
+ });
399
+ }
400
+ async function markVideoGenerationSubmissionIntent(db, input) {
401
+ return await mutateOperation(db, input, async (tx, operation) => {
402
+ if (operation.status === "submission_uncertain") {
403
+ if (!operation.providerRequestEncrypted || !operation.providerRequestExpiresAt) {
404
+ throw new VideoGenerationConflictError("Video provider request is incomplete");
405
+ }
406
+ return operation;
407
+ }
408
+ if (operation.status !== "accepted") {
409
+ throw invalidTransition(operation, "submission_uncertain");
410
+ }
411
+ const now = /* @__PURE__ */ new Date();
412
+ const [updated] = await tx.update(videoGenerationOperations).set({
413
+ status: "submission_uncertain",
414
+ providerRequestEncrypted: input.encryptedProviderRequest,
415
+ providerRequestExpiresAt: input.providerRequestExpiresAt,
416
+ nextReconcileAt: input.nextReconcileAt,
417
+ lastError: null,
418
+ reconcileLeaseOwner: null,
419
+ reconcileLeaseExpiresAt: null,
420
+ updatedAt: now
421
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
422
+ await tx.update(videoGenerationReferences).set({
423
+ grantExpiresAt: input.providerRequestExpiresAt,
424
+ cleanupAfter: new Date(
425
+ Math.max(
426
+ operation.recoveryDeadlineAt.getTime(),
427
+ input.providerRequestExpiresAt.getTime()
428
+ )
429
+ )
430
+ }).where(eq(videoGenerationReferences.operationId, operation.id));
431
+ return requireRow(updated, "record video provider submission intent");
432
+ });
433
+ }
434
+ async function markVideoGenerationProviderStarted(db, input) {
435
+ return await mutateOperation(db, input, async (tx, operation) => {
436
+ if (operation.providerJobId === input.providerJobId && operation.status === "provider_started") {
437
+ return operation;
438
+ }
439
+ if (!inArrayValue(operation.status, ["accepted", "submission_uncertain"])) {
440
+ throw invalidTransition(operation, "provider_started");
441
+ }
442
+ const now = /* @__PURE__ */ new Date();
443
+ const [updated] = await tx.update(videoGenerationOperations).set({
444
+ status: "provider_started",
445
+ providerJobId: input.providerJobId.slice(0, 1024),
446
+ providerRequestEncrypted: null,
447
+ providerRequestExpiresAt: null,
448
+ providerRequestSentAt: operation.providerRequestSentAt ?? now,
449
+ providerStartedAt: operation.providerStartedAt ?? now,
450
+ nextReconcileAt: input.nextReconcileAt,
451
+ lastError: null,
452
+ reconcileLeaseOwner: null,
453
+ reconcileLeaseExpiresAt: null,
454
+ updatedAt: now
455
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
456
+ return requireRow(updated, "record video provider job");
457
+ });
458
+ }
459
+ async function rescheduleVideoGenerationOperation(db, input) {
460
+ return await withRlsContext(
461
+ db,
462
+ { accountId: input.accountId, workspaceId: input.workspaceId },
463
+ async (scopedDb) => {
464
+ const [updated] = await scopedDb.update(videoGenerationOperations).set({
465
+ nextReconcileAt: input.nextReconcileAt,
466
+ ...input.error ? { lastError: boundPrivateError(input.error) } : {},
467
+ reconcileLeaseOwner: null,
468
+ reconcileLeaseExpiresAt: null,
469
+ updatedAt: /* @__PURE__ */ new Date()
470
+ }).where(
471
+ and(
472
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
473
+ eq(videoGenerationOperations.id, input.operationId),
474
+ eq(videoGenerationOperations.requestDigest, input.requestDigest),
475
+ ...input.owner ? [eq(videoGenerationOperations.reconcileLeaseOwner, input.owner)] : []
476
+ )
477
+ ).returning({ id: videoGenerationOperations.id });
478
+ return updated !== void 0;
479
+ }
480
+ );
481
+ }
482
+ async function markVideoGenerationRetaining(db, input) {
483
+ return await mutateOperation(db, input, async (tx, operation) => {
484
+ if (operation.status === "retaining") return operation;
485
+ if (operation.status !== "provider_started") throw invalidTransition(operation, "retaining");
486
+ const [updated] = await tx.update(videoGenerationOperations).set({
487
+ status: "retaining",
488
+ nextReconcileAt: /* @__PURE__ */ new Date(),
489
+ reconcileLeaseOwner: null,
490
+ reconcileLeaseExpiresAt: null,
491
+ updatedAt: /* @__PURE__ */ new Date()
492
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
493
+ return requireRow(updated, "begin video retention");
494
+ });
495
+ }
496
+ async function settleVideoGenerationFailure(db, input) {
497
+ return await mutateOperation(db, input, async (tx, operation) => {
498
+ if (operation.status === input.status) return operation;
499
+ const allowed = input.status === "provider_failed" ? ["accepted", "submission_uncertain", "provider_started"] : input.status === "retention_failed" ? ["retaining"] : ["submission_uncertain", "provider_started"];
500
+ if (!inArrayValue(operation.status, allowed)) throw invalidTransition(operation, input.status);
501
+ await releaseReservedQuota(tx, operation, false);
502
+ const now = /* @__PURE__ */ new Date();
503
+ const creditState = await refundVideoGenerationCredits(tx, operation, now);
504
+ const [updated] = await tx.update(videoGenerationOperations).set({
505
+ status: input.status,
506
+ creditState,
507
+ quotaState: "released",
508
+ terminalAt: now,
509
+ nextReconcileAt: null,
510
+ terminalUpdateState: operation.admissionOutputState === "recorded" ? "pending" : "suppressed",
511
+ providerRequestEncrypted: null,
512
+ providerRequestExpiresAt: null,
513
+ credentialEncrypted: null,
514
+ requestEncrypted: null,
515
+ boundedPublicReason: boundPublicReason(input.publicReason),
516
+ lastError: input.privateError ? boundPrivateError(input.privateError) : null,
517
+ reconcileLeaseOwner: null,
518
+ reconcileLeaseExpiresAt: null,
519
+ privateDataEraseAfter: now,
520
+ updatedAt: now
521
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
522
+ return requireRow(updated, "settle failed video generation");
523
+ });
524
+ }
525
+ async function settleVideoGenerationReady(db, input) {
526
+ return await withRlsContext(
527
+ db,
528
+ { accountId: input.accountId, workspaceId: input.workspaceId },
529
+ async (scopedDb) => await scopedDb.transaction(async (tx) => {
530
+ const [operation] = await tx.select().from(videoGenerationOperations).where(
531
+ and(
532
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
533
+ eq(videoGenerationOperations.id, input.operationId),
534
+ eq(videoGenerationOperations.requestDigest, input.requestDigest)
535
+ )
536
+ ).for("update").limit(1);
537
+ if (!operation) throw new VideoGenerationConflictError("Video operation not found");
538
+ const [existingArtifact] = await tx.select().from(generatedVideoArtifacts).where(eq(generatedVideoArtifacts.operationId, operation.id)).limit(1);
539
+ if (operation.status === "completed" && existingArtifact) {
540
+ return { operation, artifact: existingArtifact };
541
+ }
542
+ if (operation.status !== "retaining") throw invalidTransition(operation, "completed");
543
+ if (operation.expectedFileId !== input.fileId) {
544
+ throw new VideoGenerationConflictError("Video File identity changed during settlement");
545
+ }
546
+ if (operation.expectedArtifactId === operation.expectedFileId) {
547
+ throw new Error("Video artifact and File identities must be distinct");
548
+ }
549
+ const now = /* @__PURE__ */ new Date();
550
+ await tx.insert(files).values({
551
+ id: input.fileId,
552
+ accountId: input.accountId,
553
+ workspaceId: input.workspaceId,
554
+ status: "ready",
555
+ filename: `generated-video-${operation.expectedArtifactId}.mp4`,
556
+ safeFilename: `generated-video-${operation.expectedArtifactId}.mp4`,
557
+ contentType: "video/mp4",
558
+ sizeBytes: input.sizeBytes,
559
+ sha256: input.sha256,
560
+ bucket: input.bucket,
561
+ objectKey: input.objectKey,
562
+ updatedAt: now
563
+ });
564
+ const [artifact] = await tx.insert(generatedVideoArtifacts).values({
565
+ id: operation.expectedArtifactId,
566
+ accountId: input.accountId,
567
+ workspaceId: input.workspaceId,
568
+ primaryFileId: input.fileId,
569
+ operationId: operation.id,
570
+ sessionId: operation.sessionId,
571
+ turnId: operation.turnId,
572
+ attemptId: operation.attemptId,
573
+ modelId: operation.modelId,
574
+ sourceMode: operation.sourceMode,
575
+ promptDigest: operation.promptDigest,
576
+ contentType: "video/mp4",
577
+ sizeBytes: input.sizeBytes,
578
+ sha256: input.sha256,
579
+ durationMillis: Math.round(input.facts.durationSeconds * 1e3),
580
+ width: input.facts.width,
581
+ height: input.facts.height,
582
+ fpsMilli: Math.round(input.facts.fps * 1e3),
583
+ videoCodec: input.facts.videoCodec,
584
+ audioCodec: input.facts.audioCodec,
585
+ hasAudio: input.facts.hasAudio,
586
+ sandboxFilename: `generated-video-${operation.expectedArtifactId}.mp4`,
587
+ readyAt: now
588
+ }).returning();
589
+ if (!artifact) throw new Error("Generated video artifact was not persisted");
590
+ await tx.update(workspaceVideoGenerationQuotas).set({
591
+ reservedBytes: sql`${workspaceVideoGenerationQuotas.reservedBytes} - ${operation.reservedBytes}`,
592
+ readyBytes: sql`${workspaceVideoGenerationQuotas.readyBytes} + ${input.sizeBytes}`,
593
+ updatedAt: now
594
+ }).where(eq(workspaceVideoGenerationQuotas.workspaceId, input.workspaceId));
595
+ const [updated] = await tx.update(videoGenerationOperations).set({
596
+ status: "completed",
597
+ quotaState: "ready",
598
+ terminalAt: now,
599
+ nextReconcileAt: null,
600
+ terminalUpdateState: operation.admissionOutputState === "recorded" ? "pending" : "suppressed",
601
+ providerRequestEncrypted: null,
602
+ providerRequestExpiresAt: null,
603
+ credentialEncrypted: null,
604
+ requestEncrypted: null,
605
+ boundedPublicReason: null,
606
+ lastError: null,
607
+ privateDataEraseAfter: now,
608
+ reconcileLeaseOwner: null,
609
+ reconcileLeaseExpiresAt: null,
610
+ updatedAt: now
611
+ }).where(eq(videoGenerationOperations.id, operation.id)).returning();
612
+ return {
613
+ operation: requireRow(updated, "settle ready video generation"),
614
+ artifact
615
+ };
616
+ })
617
+ );
618
+ }
619
+ async function getGeneratedVideoArtifact(db, workspaceId, artifactId) {
620
+ return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
621
+ const [row] = await scopedDb.select({ artifact: generatedVideoArtifacts, file: files }).from(generatedVideoArtifacts).innerJoin(files, eq(files.id, generatedVideoArtifacts.primaryFileId)).where(
622
+ and(
623
+ eq(generatedVideoArtifacts.workspaceId, workspaceId),
624
+ eq(generatedVideoArtifacts.id, artifactId)
625
+ )
626
+ ).limit(1);
627
+ return row ?? null;
628
+ });
629
+ }
630
+ async function listSessionGeneratedVideoArtifacts(db, workspaceId, sessionId) {
631
+ return await withWorkspaceRls(
632
+ db,
633
+ workspaceId,
634
+ async (scopedDb) => await scopedDb.select({
635
+ artifact: generatedVideoArtifacts,
636
+ file: files
637
+ }).from(generatedVideoArtifacts).innerJoin(files, eq(files.id, generatedVideoArtifacts.primaryFileId)).where(
638
+ and(
639
+ eq(generatedVideoArtifacts.workspaceId, workspaceId),
640
+ eq(generatedVideoArtifacts.sessionId, sessionId),
641
+ sql`${generatedVideoArtifacts.deletedAt} is null`
642
+ )
643
+ ).orderBy(generatedVideoArtifacts.createdAt, generatedVideoArtifacts.id)
644
+ );
645
+ }
646
+ async function markVideoGenerationReferenceCleaned(db, input) {
647
+ return await withRlsContext(
648
+ db,
649
+ { accountId: input.accountId, workspaceId: input.workspaceId },
650
+ async (scopedDb) => {
651
+ const [updated] = await scopedDb.update(videoGenerationReferences).set({
652
+ stagingObjectKey: null,
653
+ grantExpiresAt: null,
654
+ cleanupAfter: null,
655
+ cleanedAt: /* @__PURE__ */ new Date()
656
+ }).where(
657
+ and(
658
+ eq(videoGenerationReferences.workspaceId, input.workspaceId),
659
+ eq(videoGenerationReferences.operationId, input.operationId),
660
+ eq(videoGenerationReferences.ordinal, input.ordinal),
661
+ eq(videoGenerationReferences.stagingObjectKey, input.stagingObjectKey)
662
+ )
663
+ ).returning({
664
+ operationId: videoGenerationReferences.operationId
665
+ });
666
+ if (updated) return true;
667
+ const [existing] = await scopedDb.select({ cleanedAt: videoGenerationReferences.cleanedAt }).from(videoGenerationReferences).where(
668
+ and(
669
+ eq(videoGenerationReferences.workspaceId, input.workspaceId),
670
+ eq(videoGenerationReferences.operationId, input.operationId),
671
+ eq(videoGenerationReferences.ordinal, input.ordinal)
672
+ )
673
+ ).limit(1);
674
+ return existing?.cleanedAt !== null && existing?.cleanedAt !== void 0;
675
+ }
676
+ );
677
+ }
678
+ async function markVideoGenerationTerminalUpdate(db, input) {
679
+ return await withRlsContext(
680
+ db,
681
+ { accountId: input.accountId, workspaceId: input.workspaceId },
682
+ async (scopedDb) => {
683
+ return await markVideoGenerationTerminalUpdateInTransaction(scopedDb, input);
684
+ }
685
+ );
686
+ }
687
+ async function markVideoGenerationTerminalUpdateInTransaction(tx, input) {
688
+ const allowed = input.state === "leased" ? ["pending", "leased"] : input.state === "delivered" ? ["leased"] : ["pending", "leased"];
689
+ const [updated] = await tx.update(videoGenerationOperations).set({
690
+ terminalUpdateState: input.state,
691
+ terminalUpdateId: input.state === "suppressed" ? null : input.updateId ?? null,
692
+ updatedAt: /* @__PURE__ */ new Date()
693
+ }).where(
694
+ and(
695
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
696
+ eq(videoGenerationOperations.id, input.operationId),
697
+ inArray(videoGenerationOperations.terminalUpdateState, allowed),
698
+ ...input.state === "delivered" && input.updateId ? [eq(videoGenerationOperations.terminalUpdateId, input.updateId)] : []
699
+ )
700
+ ).returning({ id: videoGenerationOperations.id });
701
+ return updated !== void 0;
702
+ }
703
+ function mediaGenerationResultForOperation(input) {
704
+ if (input.operation.status === "completed" && input.readyReceipt) {
705
+ return {
706
+ type: "media_generation_result",
707
+ schemaVersion: 1,
708
+ status: "ready",
709
+ operationId: input.operation.id,
710
+ receipt: input.readyReceipt
711
+ };
712
+ }
713
+ const failureStatus = input.operation.status;
714
+ if (!inArrayValue(failureStatus, [
715
+ "provider_failed",
716
+ "retention_failed",
717
+ "cancelled_before_submit",
718
+ "outcome_unknown"
719
+ ])) {
720
+ throw new Error("Video generation operation is not terminal");
721
+ }
722
+ return {
723
+ type: "media_generation_result",
724
+ schemaVersion: 1,
725
+ status: failureStatus,
726
+ operationId: input.operation.id,
727
+ boundedPublicReason: input.operation.boundedPublicReason ?? "Video generation did not complete."
728
+ };
729
+ }
730
+ async function mediaGenerationResultForStoredOperation(db, operation) {
731
+ if (operation.status !== "completed") {
732
+ return mediaGenerationResultForOperation({ operation });
733
+ }
734
+ const retained = await getGeneratedVideoArtifact(
735
+ db,
736
+ operation.workspaceId,
737
+ operation.expectedArtifactId
738
+ );
739
+ if (!retained || retained.artifact.deletedAt) {
740
+ throw new Error("Completed video generation lost its retained artifact");
741
+ }
742
+ const artifact = retainedGeneratedVideoReferenceFromFile({
743
+ id: retained.artifact.id,
744
+ workspaceId: retained.artifact.workspaceId,
745
+ status: retained.file.status,
746
+ contentType: retained.file.contentType,
747
+ sizeBytes: retained.file.sizeBytes,
748
+ sha256: retained.file.sha256,
749
+ updatedAt: retained.artifact.readyAt.toISOString(),
750
+ width: retained.artifact.width,
751
+ height: retained.artifact.height
752
+ });
753
+ if (!artifact) throw new Error("Generated video receipt could not be constructed");
754
+ const receipt = GeneratedVideoReceipt.parse({
755
+ type: "generated_video",
756
+ schemaVersion: 1,
757
+ operationId: operation.id,
758
+ artifact,
759
+ video: {
760
+ durationSeconds: retained.artifact.durationMillis / 1e3,
761
+ width: retained.artifact.width,
762
+ height: retained.artifact.height,
763
+ fps: retained.artifact.fpsMilli / 1e3,
764
+ hasAudio: retained.artifact.hasAudio,
765
+ videoCodec: retained.artifact.videoCodec,
766
+ audioCodec: retained.artifact.audioCodec
767
+ },
768
+ sandboxPath: `/workspace/generated-videos/${retained.artifact.sandboxFilename}`
769
+ });
770
+ return mediaGenerationResultForOperation({
771
+ operation,
772
+ readyReceipt: receipt
773
+ });
774
+ }
775
+ async function getVideoGenerationOperationSummary(db, workspaceId, operationId) {
776
+ const operation = await getVideoGenerationOperation(db, workspaceId, operationId);
777
+ if (!operation) return null;
778
+ const terminal = inArrayValue(operation.status, [
779
+ "completed",
780
+ "provider_failed",
781
+ "cancelled_before_submit",
782
+ "outcome_unknown",
783
+ "retention_failed"
784
+ ]) ? await mediaGenerationResultForStoredOperation(db, operation) : null;
785
+ return VideoGenerationOperationSummary.parse({
786
+ schemaVersion: 1,
787
+ operationId: operation.id,
788
+ modelId: operation.modelId,
789
+ status: operation.status === "submission_uncertain" ? "accepted" : operation.status,
790
+ createdAt: operation.createdAt.toISOString(),
791
+ updatedAt: operation.updatedAt.toISOString(),
792
+ terminal
793
+ });
794
+ }
795
+ async function mutateOperation(db, input, mutate) {
796
+ return await withRlsContext(
797
+ db,
798
+ { accountId: input.accountId, workspaceId: input.workspaceId },
799
+ async (scopedDb) => await scopedDb.transaction(async (txRaw) => {
800
+ const tx = txRaw;
801
+ const [operation] = await tx.select().from(videoGenerationOperations).where(
802
+ and(
803
+ eq(videoGenerationOperations.workspaceId, input.workspaceId),
804
+ eq(videoGenerationOperations.id, input.operationId),
805
+ eq(videoGenerationOperations.requestDigest, input.requestDigest)
806
+ )
807
+ ).for("update").limit(1);
808
+ if (!operation) throw new VideoGenerationConflictError("Video operation not found");
809
+ return await mutate(tx, operation);
810
+ })
811
+ );
812
+ }
813
+ async function releaseReservedQuota(tx, operation, ready) {
814
+ if (operation.quotaState !== "reserved") return;
815
+ await tx.update(workspaceVideoGenerationQuotas).set({
816
+ reservedBytes: sql`${workspaceVideoGenerationQuotas.reservedBytes} - ${operation.reservedBytes}`,
817
+ ...ready ? {
818
+ readyBytes: sql`${workspaceVideoGenerationQuotas.readyBytes} + ${operation.reservedBytes}`
819
+ } : {},
820
+ updatedAt: /* @__PURE__ */ new Date()
821
+ }).where(eq(workspaceVideoGenerationQuotas.workspaceId, operation.workspaceId));
822
+ }
823
+ async function debitVideoGenerationCredits(tx, operation) {
824
+ if (operation.pricedCostMicros <= 0 || operation.fundingSource !== "opengeni_credits") {
825
+ throw new Error("Managed video credit debit has an invalid funding binding");
826
+ }
827
+ await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${operation.accountId}))`);
828
+ const [balanceRow] = await tx.select({
829
+ balanceMicros: sql`coalesce(sum(${creditLedgerEntries.amountMicros}), 0)`
830
+ }).from(creditLedgerEntries).where(eq(creditLedgerEntries.accountId, operation.accountId));
831
+ const balanceMicros = Number(balanceRow?.balanceMicros ?? 0);
832
+ if (!Number.isSafeInteger(balanceMicros) || balanceMicros < operation.pricedCostMicros) {
833
+ throw new VideoGenerationCreditError();
834
+ }
835
+ const now = /* @__PURE__ */ new Date();
836
+ const inserted = await tx.insert(creditLedgerEntries).values({
837
+ accountId: operation.accountId,
838
+ workspaceId: operation.workspaceId,
839
+ type: "video_generation_debit",
840
+ amountMicros: -operation.pricedCostMicros,
841
+ sourceType: "video_generation_operation",
842
+ sourceId: operation.id,
843
+ idempotencyKey: `credit:video_generation_debit:${operation.id}`,
844
+ metadata: {
845
+ modelId: operation.modelId,
846
+ sourceMode: operation.sourceMode,
847
+ pricedCostMicros: operation.pricedCostMicros
848
+ },
849
+ occurredAt: now
850
+ }).onConflictDoNothing({ target: creditLedgerEntries.idempotencyKey }).returning({ id: creditLedgerEntries.id });
851
+ if (inserted.length !== 1) {
852
+ throw new VideoGenerationConflictError("Video generation credit debit already exists");
853
+ }
854
+ await tx.insert(usageEvents).values({
855
+ accountId: operation.accountId,
856
+ workspaceId: operation.workspaceId,
857
+ eventType: "video_generation.cost",
858
+ quantity: operation.pricedCostMicros,
859
+ unit: "usd_micros",
860
+ sourceResourceType: "video_generation_operation",
861
+ sourceResourceId: operation.id,
862
+ sessionId: operation.sessionId,
863
+ turnId: operation.turnId,
864
+ turnAttemptId: operation.attemptId,
865
+ idempotencyKey: `usage:video_generation.cost:${operation.id}`,
866
+ occurredAt: now
867
+ }).onConflictDoNothing({ target: usageEvents.idempotencyKey });
868
+ }
869
+ async function refundVideoGenerationCredits(tx, operation, now) {
870
+ if (operation.creditState !== "debited") return operation.creditState;
871
+ if (operation.fundingSource !== "opengeni_credits" || operation.pricedCostMicros <= 0) {
872
+ throw new Error("Video generation credit refund has an invalid funding binding");
873
+ }
874
+ await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${operation.accountId}))`);
875
+ const inserted = await tx.insert(creditLedgerEntries).values({
876
+ accountId: operation.accountId,
877
+ workspaceId: operation.workspaceId,
878
+ type: "video_generation_refund",
879
+ amountMicros: operation.pricedCostMicros,
880
+ sourceType: "video_generation_operation",
881
+ sourceId: operation.id,
882
+ idempotencyKey: `credit:video_generation_refund:${operation.id}`,
883
+ metadata: {
884
+ modelId: operation.modelId,
885
+ sourceMode: operation.sourceMode,
886
+ pricedCostMicros: operation.pricedCostMicros
887
+ },
888
+ occurredAt: now
889
+ }).onConflictDoNothing({ target: creditLedgerEntries.idempotencyKey }).returning({ id: creditLedgerEntries.id });
890
+ if (inserted.length !== 1) {
891
+ throw new VideoGenerationConflictError("Video generation credit refund already exists");
892
+ }
893
+ await tx.insert(usageEvents).values({
894
+ accountId: operation.accountId,
895
+ workspaceId: operation.workspaceId,
896
+ eventType: "video_generation.refund",
897
+ quantity: operation.pricedCostMicros,
898
+ unit: "usd_micros",
899
+ sourceResourceType: "video_generation_operation",
900
+ sourceResourceId: operation.id,
901
+ sessionId: operation.sessionId,
902
+ turnId: operation.turnId,
903
+ turnAttemptId: operation.attemptId,
904
+ idempotencyKey: `usage:video_generation.refund:${operation.id}`,
905
+ occurredAt: now
906
+ }).onConflictDoNothing({ target: usageEvents.idempotencyKey });
907
+ return "refunded";
908
+ }
909
+ function policyFromRow(row) {
910
+ return VideoGenerationPolicy.parse(
911
+ row ? {
912
+ schemaVersion: 1,
913
+ revision: row.revision,
914
+ fundingSource: row.fundingSource,
915
+ enabledModelIds: row.enabledModelIds,
916
+ defaultModelId: row.defaultModelId
917
+ } : {
918
+ schemaVersion: 1,
919
+ revision: 0,
920
+ fundingSource: "workspace_gateway",
921
+ enabledModelIds: [],
922
+ defaultModelId: null
923
+ }
924
+ );
925
+ }
926
+ function assertSupportedModels(modelIds) {
927
+ for (const modelId of modelIds) {
928
+ if (modelId !== SEEDANCE_2_5_MODEL_ID && modelId !== GROK_IMAGINE_VIDEO_1_5_MODEL_ID) {
929
+ throw new Error(`Unknown video generation model: ${modelId}`);
930
+ }
931
+ }
932
+ }
933
+ function assertAdmissionMatches(operation, input) {
934
+ if (operation.requestDigest !== input.requestDigest || operation.toolCallId !== input.toolCallId || operation.sessionId !== input.sessionId || operation.turnId !== input.turnId || operation.fundingSource !== input.fundingSource || operation.pricedCostMicros !== input.pricedCostMicros) {
935
+ throw new VideoGenerationConflictError(
936
+ "Video generation admission key was reused with different arguments"
937
+ );
938
+ }
939
+ }
940
+ function inPreSubmitState(status) {
941
+ return inArrayValue(status, ["preparing", "prepared", "accepted"]);
942
+ }
943
+ function inArrayValue(value, candidates) {
944
+ return candidates.includes(value);
945
+ }
946
+ function invalidTransition(operation, target) {
947
+ return new VideoGenerationConflictError(
948
+ `Invalid video generation transition ${operation.status} -> ${target}`
949
+ );
950
+ }
951
+ function requireRow(value, action) {
952
+ if (!value) throw new Error(`Failed to ${action}`);
953
+ return value;
954
+ }
955
+ function boundPublicReason(value) {
956
+ const normalized = value.replace(/\s+/gu, " ").trim();
957
+ return (normalized || "Video generation did not complete.").slice(0, 1e3);
958
+ }
959
+ function boundPrivateError(value) {
960
+ return value.replace(/\s+/gu, " ").trim().slice(0, 4096);
961
+ }
962
+
963
+ export {
964
+ ACTIVE_VIDEO_GENERATION_STATUSES,
965
+ VideoGenerationConflictError,
966
+ VideoGenerationCapacityError,
967
+ VideoGenerationCreditError,
968
+ getWorkspaceVideoGenerationPolicy,
969
+ updateWorkspaceVideoGenerationPolicy,
970
+ admitVideoGenerationOperation,
971
+ rotateVideoGenerationCredential,
972
+ markVideoGenerationPrepared,
973
+ markVideoGenerationAccepted,
974
+ markVideoGenerationAcceptedInTransaction,
975
+ cancelVideoGenerationBeforeSubmit,
976
+ cancelUnacceptedVideoGenerationsForToolCallsInTransaction,
977
+ claimVideoGenerationOperations,
978
+ getVideoGenerationOperation,
979
+ markVideoGenerationSubmissionUncertain,
980
+ markVideoGenerationSubmissionIntent,
981
+ markVideoGenerationProviderStarted,
982
+ rescheduleVideoGenerationOperation,
983
+ markVideoGenerationRetaining,
984
+ settleVideoGenerationFailure,
985
+ settleVideoGenerationReady,
986
+ getGeneratedVideoArtifact,
987
+ listSessionGeneratedVideoArtifacts,
988
+ markVideoGenerationReferenceCleaned,
989
+ markVideoGenerationTerminalUpdate,
990
+ markVideoGenerationTerminalUpdateInTransaction,
991
+ mediaGenerationResultForOperation,
992
+ mediaGenerationResultForStoredOperation,
993
+ getVideoGenerationOperationSummary
994
+ };
995
+ //# sourceMappingURL=chunk-CQZ4QSP5.js.map