@opengeni/db 0.28.1 → 0.31.1
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.
- package/dist/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +91 -0
- package/dist/browser-identities.d.ts +128 -0
- package/dist/browser-sessions.d.ts +272 -0
- package/dist/browser-state-artifacts.d.ts +17 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +192 -0
- package/dist/chunk-F5FT7BEZ.js +3535 -0
- package/dist/chunk-F5FT7BEZ.js.map +1 -0
- package/dist/chunk-ISI2TIUG.js +639 -0
- package/dist/chunk-ISI2TIUG.js.map +1 -0
- package/dist/chunk-MWDVXQOL.js +11639 -0
- package/dist/chunk-MWDVXQOL.js.map +1 -0
- package/dist/chunk-NBE6CHRI.js +955 -0
- package/dist/chunk-NBE6CHRI.js.map +1 -0
- package/dist/{chunk-DBS5HPEW.js → chunk-YHZXQ7HP.js} +760 -11
- package/dist/chunk-YHZXQ7HP.js.map +1 -0
- package/dist/codemode-operations.d.ts +117 -0
- package/dist/codex-token-resolver.d.ts +2 -2
- package/dist/company-profile-schema.d.ts +798 -0
- package/dist/company-profile.d.ts +268 -0
- package/dist/computer-sessions.d.ts +109 -0
- package/dist/connection-token-resolver.d.ts +9 -4
- package/dist/database.d.ts +52 -3
- package/dist/editable-artifact-durable-export.d.ts +153 -0
- package/dist/editable-artifact-durable-export.js +788 -0
- package/dist/editable-artifact-durable-export.js.map +1 -0
- package/dist/editable-artifact-materialization.d.ts +123 -0
- package/dist/editable-artifacts-schema.d.ts +4152 -0
- package/dist/editable-artifacts.d.ts +610 -0
- package/dist/editable-artifacts.js +26 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/environment-crypto.d.ts +8 -4
- package/dist/generated-images.d.ts +139 -0
- package/dist/index.d.ts +958 -126
- package/dist/index.js +31204 -12172
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +51 -4
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-features.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +3 -0
- package/dist/interaction-schema.d.ts +5372 -0
- package/dist/knowledge-source-sync-schema.d.ts +1679 -0
- package/dist/knowledge-source-sync.d.ts +274 -0
- package/dist/lossless-columns.d.ts +58 -0
- package/dist/lossless-json.d.ts +39 -0
- package/dist/memory-domain.d.ts +3 -6
- package/dist/memory-governance.d.ts +2 -2
- package/dist/memory-slack-delivery.d.ts +101 -0
- package/dist/new-session-drafts.d.ts +2 -2
- package/dist/pack-components.d.ts +76 -0
- package/dist/pack-installations.d.ts +86 -0
- package/dist/persistence-errors.d.ts +7 -14
- package/dist/plugin-packages.d.ts +126 -0
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +9 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +18 -6
- package/dist/schema.d.ts +19122 -8731
- package/dist/schema.js +173 -1
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +4 -4
- package/dist/session-queue-commands.d.ts +19 -11
- package/dist/session-realtime-context.d.ts +2 -2
- package/dist/session-realtime-ledger.d.ts +4 -4
- package/dist/session-realtime-mirror.d.ts +2 -2
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +8 -1
- package/dist/session-realtime.d.ts +7 -7
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/transcription-recordings-schema.d.ts +44 -6
- package/dist/transcription-recordings.d.ts +3 -3
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +216 -0
- package/dist/video-generation.js +66 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/dist/workspace-learning-policy-schema.d.ts +932 -0
- package/dist/workspace-learning-policy.d.ts +161 -0
- package/drizzle/0065_enrollment_credential_generation.sql +10 -0
- package/drizzle/0140_retained_screenshot_artifacts.sql +192 -0
- package/drizzle/0176_lossless_canonical_json.sql +975 -0
- package/drizzle/0177_session_events_workspace_turn_type_index.sql +5 -0
- package/drizzle/0178_permissioned_secret_reads.sql +14 -0
- package/drizzle/0179_slack_private_shortcut_delivery_gate.sql +85 -0
- package/drizzle/0180_retained_screenshot_lifecycle_fences.sql +273 -0
- package/drizzle/0181_connected_machine_removal.sql +52 -0
- package/drizzle/0182_connected_machine_remove_session_default.sql +77 -0
- package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
- package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
- package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
- package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
- package/drizzle/0187_generated_image_artifacts.sql +173 -0
- package/drizzle/0188_image_generation_retention_failure.sql +25 -0
- package/drizzle/0189_retained_session_image_formats.sql +13 -0
- package/drizzle/0190_timeline_annotations.sql +31 -0
- package/drizzle/0191_editable_artifact_engine.sql +3853 -0
- package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
- package/drizzle/0193_editable_artifact_authorization.sql +190 -0
- package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
- package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
- package/drizzle/0196_rig_provider_images.sql +152 -0
- package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
- package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
- package/drizzle/0199_workspace_learning_policy.sql +980 -0
- package/drizzle/0201_company_profile_authority.sql +849 -0
- package/drizzle/0202_document_index_checkpoints.sql +85 -0
- package/drizzle/0203_durable_video_generation.sql +353 -0
- package/drizzle/0204_video_generation_funding.sql +44 -0
- package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
- package/drizzle/0206_browser_sessions.sql +646 -0
- package/drizzle/0207_browser_identities.sql +379 -0
- package/drizzle/0208_attached_browser_devices.sql +165 -0
- package/drizzle/0209_computer_sessions.sql +575 -0
- package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
- package/drizzle/0211_editable_artifact_session_links.sql +58 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/package.json +18 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +1896 -0
- package/src/browser-identities.ts +1168 -0
- package/src/browser-sessions.ts +2005 -0
- package/src/browser-state-artifacts.ts +59 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1663 -0
- package/src/codemode-operations.ts +674 -0
- package/src/company-profile-schema.ts +143 -0
- package/src/company-profile.ts +968 -0
- package/src/computer-sessions.ts +1130 -0
- package/src/connection-token-resolver.ts +270 -25
- package/src/database.ts +434 -24
- package/src/editable-artifact-durable-export.ts +1201 -0
- package/src/editable-artifact-materialization.ts +893 -0
- package/src/editable-artifacts-schema.ts +879 -0
- package/src/editable-artifacts.ts +5404 -0
- package/src/environment-crypto.ts +22 -9
- package/src/generated-images.ts +740 -0
- package/src/index.ts +10117 -2730
- package/src/insights.ts +160 -13
- package/src/integration-bindings.ts +455 -0
- package/src/integration-features.ts +859 -0
- package/src/interaction-revisions.ts +40 -0
- package/src/interaction-schema.ts +1303 -0
- package/src/knowledge-source-sync-schema.ts +192 -0
- package/src/knowledge-source-sync.ts +1922 -0
- package/src/lossless-columns.ts +35 -0
- package/src/lossless-json.ts +322 -0
- package/src/memory-domain.ts +5 -44
- package/src/memory-slack-delivery.ts +801 -0
- package/src/pack-components.ts +954 -0
- package/src/pack-installations.ts +929 -0
- package/src/persistence-errors.ts +26 -34
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +302 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +514 -5
- package/src/schema.ts +5177 -2957
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +133 -81
- package/src/session-queue-commands.ts +511 -247
- package/src/session-realtime-context.ts +20 -7
- package/src/session-realtime-ledger.ts +32 -10
- package/src/session-realtime-mirror.ts +4 -2
- package/src/session-realtime-terminal.ts +89 -21
- package/src/session-realtime.ts +10 -9
- package/src/session-tool-call-settlement.ts +38 -15
- package/src/slack-user-link-access.ts +702 -0
- package/src/transcription-recordings-schema.ts +5 -2
- package/src/transcription-recordings.ts +70 -40
- package/src/video-generation.ts +1452 -0
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/dist/chunk-DBS5HPEW.js.map +0 -1
- package/dist/chunk-HT5T62CC.js +0 -6803
- package/dist/chunk-HT5T62CC.js.map +0 -1
- package/dist/event-payload-sanitizer.d.ts +0 -32
- package/src/event-payload-sanitizer.ts +0 -377
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
import type { FileAsset, FileStatus, FileUploadStatus } from "@opengeni/contracts";
|
|
2
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
3
|
+
import type { Database } from "./database";
|
|
4
|
+
import { withRlsContext, withWorkspaceRls } from "./database";
|
|
5
|
+
import * as schema from "./schema";
|
|
6
|
+
|
|
7
|
+
export type GeneratedImageSourceStrategy = "native_hosted" | "provider_adapter";
|
|
8
|
+
export type GeneratedImageArtifactStatus = "pending" | "ready";
|
|
9
|
+
export type ImageGenerationOperationStatus =
|
|
10
|
+
| "prepared"
|
|
11
|
+
| "provider_started"
|
|
12
|
+
| "completed"
|
|
13
|
+
| "outcome_unknown"
|
|
14
|
+
| "retention_failed";
|
|
15
|
+
|
|
16
|
+
export type ImageGenerationOperation = {
|
|
17
|
+
id: string;
|
|
18
|
+
accountId: string;
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
sessionId: string | null;
|
|
21
|
+
turnId: string | null;
|
|
22
|
+
attemptId: string | null;
|
|
23
|
+
operationKey: string;
|
|
24
|
+
toolCallId: string;
|
|
25
|
+
providerId: string;
|
|
26
|
+
providerBindingHash: string;
|
|
27
|
+
modelId: string;
|
|
28
|
+
requestDigest: string;
|
|
29
|
+
expectedArtifactId: string;
|
|
30
|
+
status: ImageGenerationOperationStatus;
|
|
31
|
+
providerStartedAt: Date | null;
|
|
32
|
+
completedAt: Date | null;
|
|
33
|
+
lastError: string | null;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
updatedAt: Date;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type GeneratedImageArtifact = {
|
|
39
|
+
artifactId: string;
|
|
40
|
+
accountId: string;
|
|
41
|
+
workspaceId: string;
|
|
42
|
+
sessionId: string | null;
|
|
43
|
+
turnId: string | null;
|
|
44
|
+
attemptId: string | null;
|
|
45
|
+
uploadId: string | null;
|
|
46
|
+
settlementKey: string;
|
|
47
|
+
toolCallId: string;
|
|
48
|
+
sourceStrategy: GeneratedImageSourceStrategy;
|
|
49
|
+
providerId: string;
|
|
50
|
+
providerBindingHash: string;
|
|
51
|
+
providerItemId: string | null;
|
|
52
|
+
status: GeneratedImageArtifactStatus;
|
|
53
|
+
mediaType: string;
|
|
54
|
+
sizeBytes: number;
|
|
55
|
+
sha256: string;
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
sandboxPath: string;
|
|
59
|
+
readyAt: Date | null;
|
|
60
|
+
lastError: string | null;
|
|
61
|
+
createdAt: Date;
|
|
62
|
+
updatedAt: Date;
|
|
63
|
+
uploadStatus: FileUploadStatus | null;
|
|
64
|
+
file: FileAsset;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type PrepareGeneratedImageArtifactInput = {
|
|
68
|
+
artifactId: string;
|
|
69
|
+
uploadId: string;
|
|
70
|
+
accountId: string;
|
|
71
|
+
workspaceId: string;
|
|
72
|
+
sessionId: string;
|
|
73
|
+
turnId: string;
|
|
74
|
+
attemptId: string;
|
|
75
|
+
settlementKey: string;
|
|
76
|
+
toolCallId: string;
|
|
77
|
+
sourceStrategy: GeneratedImageSourceStrategy;
|
|
78
|
+
providerId: string;
|
|
79
|
+
providerBindingHash: string;
|
|
80
|
+
providerItemId: string | null;
|
|
81
|
+
mediaType: string;
|
|
82
|
+
sizeBytes: number;
|
|
83
|
+
sha256: string;
|
|
84
|
+
width: number;
|
|
85
|
+
height: number;
|
|
86
|
+
sandboxPath: string;
|
|
87
|
+
filename: string;
|
|
88
|
+
safeFilename: string;
|
|
89
|
+
bucket: string;
|
|
90
|
+
objectKey: string;
|
|
91
|
+
uploadExpiresAt: Date;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export type PrepareImageGenerationOperationInput = Omit<
|
|
95
|
+
ImageGenerationOperation,
|
|
96
|
+
"status" | "providerStartedAt" | "completedAt" | "lastError" | "createdAt" | "updatedAt"
|
|
97
|
+
>;
|
|
98
|
+
|
|
99
|
+
export async function prepareImageGenerationOperation(
|
|
100
|
+
db: Database,
|
|
101
|
+
input: PrepareImageGenerationOperationInput,
|
|
102
|
+
): Promise<{ operation: ImageGenerationOperation; created: boolean }> {
|
|
103
|
+
return await withRlsContext(
|
|
104
|
+
db,
|
|
105
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
106
|
+
async (scopedDb) =>
|
|
107
|
+
await scopedDb.transaction(async (tx) => {
|
|
108
|
+
await tx.execute(
|
|
109
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`image-generation-operation:${input.operationKey}`}, 0))`,
|
|
110
|
+
);
|
|
111
|
+
const existing = await imageGenerationOperationByKeyTx(
|
|
112
|
+
tx,
|
|
113
|
+
input.workspaceId,
|
|
114
|
+
input.operationKey,
|
|
115
|
+
);
|
|
116
|
+
if (existing) {
|
|
117
|
+
assertImageGenerationOperationMatches(existing, input);
|
|
118
|
+
return { operation: existing, created: false };
|
|
119
|
+
}
|
|
120
|
+
const [row] = await tx
|
|
121
|
+
.insert(schema.imageGenerationOperations)
|
|
122
|
+
.values({ ...input, status: "prepared" })
|
|
123
|
+
.returning();
|
|
124
|
+
if (!row) throw new Error("Failed to prepare image generation operation");
|
|
125
|
+
return { operation: mapImageGenerationOperation(row), created: true };
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function getImageGenerationOperation(
|
|
131
|
+
db: Database,
|
|
132
|
+
workspaceId: string,
|
|
133
|
+
operationId: string,
|
|
134
|
+
): Promise<ImageGenerationOperation | null> {
|
|
135
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
136
|
+
const [row] = await scopedDb
|
|
137
|
+
.select()
|
|
138
|
+
.from(schema.imageGenerationOperations)
|
|
139
|
+
.where(
|
|
140
|
+
and(
|
|
141
|
+
eq(schema.imageGenerationOperations.workspaceId, workspaceId),
|
|
142
|
+
eq(schema.imageGenerationOperations.id, operationId),
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
.limit(1);
|
|
146
|
+
return row ? mapImageGenerationOperation(row) : null;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export async function beginImageGenerationOperation(
|
|
151
|
+
db: Database,
|
|
152
|
+
input: {
|
|
153
|
+
accountId: string;
|
|
154
|
+
workspaceId: string;
|
|
155
|
+
operationId: string;
|
|
156
|
+
operationKey: string;
|
|
157
|
+
},
|
|
158
|
+
): Promise<{ operation: ImageGenerationOperation; started: boolean }> {
|
|
159
|
+
let started = false;
|
|
160
|
+
const operation = await mutateImageGenerationOperation(db, input, async (tx, current) => {
|
|
161
|
+
if (current.status !== "prepared") return current;
|
|
162
|
+
const now = new Date();
|
|
163
|
+
const [row] = await tx
|
|
164
|
+
.update(schema.imageGenerationOperations)
|
|
165
|
+
.set({
|
|
166
|
+
status: "provider_started",
|
|
167
|
+
providerStartedAt: now,
|
|
168
|
+
updatedAt: now,
|
|
169
|
+
})
|
|
170
|
+
.where(eq(schema.imageGenerationOperations.id, current.id))
|
|
171
|
+
.returning();
|
|
172
|
+
if (!row) throw new Error("Failed to begin image generation operation");
|
|
173
|
+
started = true;
|
|
174
|
+
return mapImageGenerationOperation(row);
|
|
175
|
+
});
|
|
176
|
+
return { operation, started };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function markImageGenerationOperationOutcomeUnknown(
|
|
180
|
+
db: Database,
|
|
181
|
+
input: {
|
|
182
|
+
accountId: string;
|
|
183
|
+
workspaceId: string;
|
|
184
|
+
operationId: string;
|
|
185
|
+
operationKey: string;
|
|
186
|
+
error: string;
|
|
187
|
+
},
|
|
188
|
+
): Promise<ImageGenerationOperation> {
|
|
189
|
+
return await mutateImageGenerationOperation(db, input, async (tx, current) => {
|
|
190
|
+
if (current.status !== "provider_started") return current;
|
|
191
|
+
const [row] = await tx
|
|
192
|
+
.update(schema.imageGenerationOperations)
|
|
193
|
+
.set({
|
|
194
|
+
status: "outcome_unknown",
|
|
195
|
+
lastError: input.error.slice(0, 4_096),
|
|
196
|
+
updatedAt: new Date(),
|
|
197
|
+
})
|
|
198
|
+
.where(eq(schema.imageGenerationOperations.id, current.id))
|
|
199
|
+
.returning();
|
|
200
|
+
if (!row) throw new Error("Failed to fence ambiguous image generation operation");
|
|
201
|
+
return mapImageGenerationOperation(row);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Provider completed, but its bytes could not be durably retained. Never replay the paid call. */
|
|
206
|
+
export async function markImageGenerationOperationRetentionFailed(
|
|
207
|
+
db: Database,
|
|
208
|
+
input: {
|
|
209
|
+
accountId: string;
|
|
210
|
+
workspaceId: string;
|
|
211
|
+
operationId: string;
|
|
212
|
+
operationKey: string;
|
|
213
|
+
error: string;
|
|
214
|
+
},
|
|
215
|
+
): Promise<ImageGenerationOperation> {
|
|
216
|
+
return await mutateImageGenerationOperation(db, input, async (tx, current) => {
|
|
217
|
+
if (current.status !== "provider_started") return current;
|
|
218
|
+
const [row] = await tx
|
|
219
|
+
.update(schema.imageGenerationOperations)
|
|
220
|
+
.set({
|
|
221
|
+
status: "retention_failed",
|
|
222
|
+
lastError: input.error.slice(0, 4_096),
|
|
223
|
+
updatedAt: new Date(),
|
|
224
|
+
})
|
|
225
|
+
.where(eq(schema.imageGenerationOperations.id, current.id))
|
|
226
|
+
.returning();
|
|
227
|
+
if (!row) throw new Error("Failed to record generated image retention failure");
|
|
228
|
+
return mapImageGenerationOperation(row);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function completeImageGenerationOperation(
|
|
233
|
+
db: Database,
|
|
234
|
+
input: {
|
|
235
|
+
accountId: string;
|
|
236
|
+
workspaceId: string;
|
|
237
|
+
operationId: string;
|
|
238
|
+
operationKey: string;
|
|
239
|
+
},
|
|
240
|
+
): Promise<ImageGenerationOperation> {
|
|
241
|
+
return await mutateImageGenerationOperation(db, input, async (tx, current) => {
|
|
242
|
+
if (current.status === "completed") return current;
|
|
243
|
+
if (
|
|
244
|
+
current.status !== "provider_started" &&
|
|
245
|
+
current.status !== "outcome_unknown" &&
|
|
246
|
+
current.status !== "retention_failed"
|
|
247
|
+
) {
|
|
248
|
+
throw new Error("Image generation operation did not reach the provider");
|
|
249
|
+
}
|
|
250
|
+
const [artifact] = await tx
|
|
251
|
+
.select({
|
|
252
|
+
artifact: schema.generatedImageArtifacts,
|
|
253
|
+
file: schema.files,
|
|
254
|
+
})
|
|
255
|
+
.from(schema.generatedImageArtifacts)
|
|
256
|
+
.innerJoin(schema.files, eq(schema.files.id, schema.generatedImageArtifacts.artifactId))
|
|
257
|
+
.where(
|
|
258
|
+
and(
|
|
259
|
+
eq(schema.generatedImageArtifacts.workspaceId, input.workspaceId),
|
|
260
|
+
eq(schema.generatedImageArtifacts.artifactId, current.expectedArtifactId),
|
|
261
|
+
),
|
|
262
|
+
)
|
|
263
|
+
.limit(1);
|
|
264
|
+
if (
|
|
265
|
+
!artifact ||
|
|
266
|
+
artifact.artifact.status !== "ready" ||
|
|
267
|
+
artifact.file.status !== "ready" ||
|
|
268
|
+
artifact.artifact.sourceStrategy !== "provider_adapter" ||
|
|
269
|
+
artifact.artifact.sessionId !== current.sessionId ||
|
|
270
|
+
artifact.artifact.turnId !== current.turnId ||
|
|
271
|
+
artifact.artifact.toolCallId !== current.toolCallId ||
|
|
272
|
+
artifact.artifact.providerId !== current.providerId ||
|
|
273
|
+
artifact.artifact.providerBindingHash !== current.providerBindingHash ||
|
|
274
|
+
artifact.artifact.providerItemId !== null
|
|
275
|
+
) {
|
|
276
|
+
throw new Error("Image generation operation has no durable ready artifact");
|
|
277
|
+
}
|
|
278
|
+
const now = new Date();
|
|
279
|
+
const [row] = await tx
|
|
280
|
+
.update(schema.imageGenerationOperations)
|
|
281
|
+
.set({
|
|
282
|
+
status: "completed",
|
|
283
|
+
completedAt: now,
|
|
284
|
+
lastError: null,
|
|
285
|
+
updatedAt: now,
|
|
286
|
+
})
|
|
287
|
+
.where(eq(schema.imageGenerationOperations.id, current.id))
|
|
288
|
+
.returning();
|
|
289
|
+
if (!row) throw new Error("Failed to complete image generation operation");
|
|
290
|
+
return mapImageGenerationOperation(row);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Reserve one deterministic file/upload/correlation triple. Concurrent retries
|
|
296
|
+
* converge on the same identities and must agree on every immutable byte fact.
|
|
297
|
+
*/
|
|
298
|
+
export async function prepareGeneratedImageArtifact(
|
|
299
|
+
db: Database,
|
|
300
|
+
input: PrepareGeneratedImageArtifactInput,
|
|
301
|
+
): Promise<{ artifact: GeneratedImageArtifact; created: boolean }> {
|
|
302
|
+
return await withRlsContext(
|
|
303
|
+
db,
|
|
304
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
305
|
+
async (scopedDb) =>
|
|
306
|
+
await scopedDb.transaction(async (tx) => {
|
|
307
|
+
// `settlementKey` is the durable provider-operation identity. Serializing
|
|
308
|
+
// only that key makes concurrent Temporal/activity retries converge
|
|
309
|
+
// without relying on unique-violation recovery across three tables.
|
|
310
|
+
await tx.execute(
|
|
311
|
+
sql`select pg_advisory_xact_lock(hashtextextended(${`generated-image:${input.settlementKey}`}, 0))`,
|
|
312
|
+
);
|
|
313
|
+
const existing = await generatedImageArtifactBySettlementTx(
|
|
314
|
+
tx,
|
|
315
|
+
input.workspaceId,
|
|
316
|
+
input.settlementKey,
|
|
317
|
+
);
|
|
318
|
+
if (existing) {
|
|
319
|
+
assertPreparedArtifactMatches(existing, input);
|
|
320
|
+
return { artifact: existing, created: false };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const [file] = await tx
|
|
324
|
+
.insert(schema.files)
|
|
325
|
+
.values({
|
|
326
|
+
id: input.artifactId,
|
|
327
|
+
accountId: input.accountId,
|
|
328
|
+
workspaceId: input.workspaceId,
|
|
329
|
+
filename: input.filename,
|
|
330
|
+
safeFilename: input.safeFilename,
|
|
331
|
+
contentType: input.mediaType,
|
|
332
|
+
sizeBytes: input.sizeBytes,
|
|
333
|
+
sha256: input.sha256,
|
|
334
|
+
bucket: input.bucket,
|
|
335
|
+
objectKey: input.objectKey,
|
|
336
|
+
status: "pending_upload",
|
|
337
|
+
})
|
|
338
|
+
.returning();
|
|
339
|
+
if (!file) throw new Error("Failed to create generated image file");
|
|
340
|
+
|
|
341
|
+
const [upload] = await tx
|
|
342
|
+
.insert(schema.fileUploads)
|
|
343
|
+
.values({
|
|
344
|
+
id: input.uploadId,
|
|
345
|
+
accountId: input.accountId,
|
|
346
|
+
workspaceId: input.workspaceId,
|
|
347
|
+
fileId: input.artifactId,
|
|
348
|
+
status: "pending",
|
|
349
|
+
expiresAt: input.uploadExpiresAt,
|
|
350
|
+
})
|
|
351
|
+
.returning();
|
|
352
|
+
if (!upload) throw new Error("Failed to create generated image upload");
|
|
353
|
+
|
|
354
|
+
const [artifactRow] = await tx
|
|
355
|
+
.insert(schema.generatedImageArtifacts)
|
|
356
|
+
.values({
|
|
357
|
+
artifactId: input.artifactId,
|
|
358
|
+
accountId: input.accountId,
|
|
359
|
+
workspaceId: input.workspaceId,
|
|
360
|
+
sessionId: input.sessionId,
|
|
361
|
+
turnId: input.turnId,
|
|
362
|
+
attemptId: input.attemptId,
|
|
363
|
+
uploadId: input.uploadId,
|
|
364
|
+
settlementKey: input.settlementKey,
|
|
365
|
+
toolCallId: input.toolCallId,
|
|
366
|
+
sourceStrategy: input.sourceStrategy,
|
|
367
|
+
providerId: input.providerId,
|
|
368
|
+
providerBindingHash: input.providerBindingHash,
|
|
369
|
+
providerItemId: input.providerItemId,
|
|
370
|
+
status: "pending",
|
|
371
|
+
mediaType: input.mediaType,
|
|
372
|
+
sizeBytes: input.sizeBytes,
|
|
373
|
+
sha256: input.sha256,
|
|
374
|
+
width: input.width,
|
|
375
|
+
height: input.height,
|
|
376
|
+
sandboxPath: input.sandboxPath,
|
|
377
|
+
})
|
|
378
|
+
.returning();
|
|
379
|
+
if (!artifactRow) throw new Error("Failed to create generated image artifact");
|
|
380
|
+
return {
|
|
381
|
+
artifact: mapGeneratedImageArtifact(artifactRow, file, upload.status),
|
|
382
|
+
created: true,
|
|
383
|
+
};
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export async function getGeneratedImageArtifact(
|
|
389
|
+
db: Database,
|
|
390
|
+
workspaceId: string,
|
|
391
|
+
artifactId: string,
|
|
392
|
+
): Promise<GeneratedImageArtifact | null> {
|
|
393
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
394
|
+
return await generatedImageArtifactByIdTx(scopedDb, workspaceId, artifactId);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export async function settleGeneratedImageArtifactReady(
|
|
399
|
+
db: Database,
|
|
400
|
+
input: {
|
|
401
|
+
accountId: string;
|
|
402
|
+
workspaceId: string;
|
|
403
|
+
artifactId: string;
|
|
404
|
+
settlementKey: string;
|
|
405
|
+
},
|
|
406
|
+
): Promise<GeneratedImageArtifact> {
|
|
407
|
+
return await withRlsContext(
|
|
408
|
+
db,
|
|
409
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
410
|
+
async (scopedDb) =>
|
|
411
|
+
await scopedDb.transaction(async (tx) => {
|
|
412
|
+
const [locked] = await tx
|
|
413
|
+
.select()
|
|
414
|
+
.from(schema.generatedImageArtifacts)
|
|
415
|
+
.where(
|
|
416
|
+
and(
|
|
417
|
+
eq(schema.generatedImageArtifacts.workspaceId, input.workspaceId),
|
|
418
|
+
eq(schema.generatedImageArtifacts.artifactId, input.artifactId),
|
|
419
|
+
),
|
|
420
|
+
)
|
|
421
|
+
.for("update")
|
|
422
|
+
.limit(1);
|
|
423
|
+
if (!locked || locked.settlementKey !== input.settlementKey) {
|
|
424
|
+
throw new Error("Generated image settlement identity does not match");
|
|
425
|
+
}
|
|
426
|
+
const existing = await generatedImageArtifactByIdTx(
|
|
427
|
+
tx,
|
|
428
|
+
input.workspaceId,
|
|
429
|
+
input.artifactId,
|
|
430
|
+
);
|
|
431
|
+
if (!existing) throw new Error("Generated image settlement file is missing");
|
|
432
|
+
if (existing.status === "ready") return existing;
|
|
433
|
+
if (existing.file.status !== "ready" || existing.uploadStatus !== "completed") {
|
|
434
|
+
throw new Error("Generated image file is not durably ready");
|
|
435
|
+
}
|
|
436
|
+
const now = new Date();
|
|
437
|
+
await tx
|
|
438
|
+
.update(schema.generatedImageArtifacts)
|
|
439
|
+
.set({
|
|
440
|
+
status: "ready",
|
|
441
|
+
readyAt: now,
|
|
442
|
+
lastError: null,
|
|
443
|
+
updatedAt: now,
|
|
444
|
+
})
|
|
445
|
+
.where(
|
|
446
|
+
and(
|
|
447
|
+
eq(schema.generatedImageArtifacts.workspaceId, input.workspaceId),
|
|
448
|
+
eq(schema.generatedImageArtifacts.artifactId, input.artifactId),
|
|
449
|
+
eq(schema.generatedImageArtifacts.status, "pending"),
|
|
450
|
+
),
|
|
451
|
+
);
|
|
452
|
+
const settled = await generatedImageArtifactByIdTx(tx, input.workspaceId, input.artifactId);
|
|
453
|
+
if (!settled || settled.status !== "ready") {
|
|
454
|
+
throw new Error("Generated image settlement was superseded");
|
|
455
|
+
}
|
|
456
|
+
return settled;
|
|
457
|
+
}),
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export async function recordGeneratedImageArtifactError(
|
|
462
|
+
db: Database,
|
|
463
|
+
input: {
|
|
464
|
+
accountId: string;
|
|
465
|
+
workspaceId: string;
|
|
466
|
+
artifactId: string;
|
|
467
|
+
settlementKey: string;
|
|
468
|
+
error: string;
|
|
469
|
+
},
|
|
470
|
+
): Promise<void> {
|
|
471
|
+
await withRlsContext(
|
|
472
|
+
db,
|
|
473
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
474
|
+
async (scopedDb) => {
|
|
475
|
+
await scopedDb
|
|
476
|
+
.update(schema.generatedImageArtifacts)
|
|
477
|
+
.set({ lastError: input.error.slice(0, 4_096), updatedAt: new Date() })
|
|
478
|
+
.where(
|
|
479
|
+
and(
|
|
480
|
+
eq(schema.generatedImageArtifacts.workspaceId, input.workspaceId),
|
|
481
|
+
eq(schema.generatedImageArtifacts.artifactId, input.artifactId),
|
|
482
|
+
eq(schema.generatedImageArtifacts.settlementKey, input.settlementKey),
|
|
483
|
+
eq(schema.generatedImageArtifacts.status, "pending"),
|
|
484
|
+
),
|
|
485
|
+
);
|
|
486
|
+
},
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export async function listReadyGeneratedImageArtifactsForSession(
|
|
491
|
+
db: Database,
|
|
492
|
+
workspaceId: string,
|
|
493
|
+
sessionId: string,
|
|
494
|
+
): Promise<GeneratedImageArtifact[]> {
|
|
495
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
496
|
+
const rows = await scopedDb
|
|
497
|
+
.select({
|
|
498
|
+
artifact: schema.generatedImageArtifacts,
|
|
499
|
+
file: schema.files,
|
|
500
|
+
uploadStatus: schema.fileUploads.status,
|
|
501
|
+
})
|
|
502
|
+
.from(schema.generatedImageArtifacts)
|
|
503
|
+
.innerJoin(
|
|
504
|
+
schema.files,
|
|
505
|
+
and(
|
|
506
|
+
eq(schema.files.workspaceId, schema.generatedImageArtifacts.workspaceId),
|
|
507
|
+
eq(schema.files.id, schema.generatedImageArtifacts.artifactId),
|
|
508
|
+
),
|
|
509
|
+
)
|
|
510
|
+
.leftJoin(
|
|
511
|
+
schema.fileUploads,
|
|
512
|
+
eq(schema.fileUploads.id, schema.generatedImageArtifacts.uploadId),
|
|
513
|
+
)
|
|
514
|
+
.where(
|
|
515
|
+
and(
|
|
516
|
+
eq(schema.generatedImageArtifacts.workspaceId, workspaceId),
|
|
517
|
+
eq(schema.generatedImageArtifacts.sessionId, sessionId),
|
|
518
|
+
eq(schema.generatedImageArtifacts.status, "ready"),
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
return rows.map((row) => mapGeneratedImageArtifact(row.artifact, row.file, row.uploadStatus));
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function generatedImageArtifactBySettlementTx(
|
|
526
|
+
tx: Database,
|
|
527
|
+
workspaceId: string,
|
|
528
|
+
settlementKey: string,
|
|
529
|
+
): Promise<GeneratedImageArtifact | null> {
|
|
530
|
+
const [row] = await tx
|
|
531
|
+
.select({
|
|
532
|
+
artifact: schema.generatedImageArtifacts,
|
|
533
|
+
file: schema.files,
|
|
534
|
+
uploadStatus: schema.fileUploads.status,
|
|
535
|
+
})
|
|
536
|
+
.from(schema.generatedImageArtifacts)
|
|
537
|
+
.innerJoin(
|
|
538
|
+
schema.files,
|
|
539
|
+
and(
|
|
540
|
+
eq(schema.files.workspaceId, schema.generatedImageArtifacts.workspaceId),
|
|
541
|
+
eq(schema.files.id, schema.generatedImageArtifacts.artifactId),
|
|
542
|
+
),
|
|
543
|
+
)
|
|
544
|
+
.leftJoin(
|
|
545
|
+
schema.fileUploads,
|
|
546
|
+
eq(schema.fileUploads.id, schema.generatedImageArtifacts.uploadId),
|
|
547
|
+
)
|
|
548
|
+
.where(
|
|
549
|
+
and(
|
|
550
|
+
eq(schema.generatedImageArtifacts.workspaceId, workspaceId),
|
|
551
|
+
eq(schema.generatedImageArtifacts.settlementKey, settlementKey),
|
|
552
|
+
),
|
|
553
|
+
)
|
|
554
|
+
.limit(1);
|
|
555
|
+
return row ? mapGeneratedImageArtifact(row.artifact, row.file, row.uploadStatus) : null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async function generatedImageArtifactByIdTx(
|
|
559
|
+
tx: Database,
|
|
560
|
+
workspaceId: string,
|
|
561
|
+
artifactId: string,
|
|
562
|
+
): Promise<GeneratedImageArtifact | null> {
|
|
563
|
+
const [row] = await tx
|
|
564
|
+
.select({
|
|
565
|
+
artifact: schema.generatedImageArtifacts,
|
|
566
|
+
file: schema.files,
|
|
567
|
+
uploadStatus: schema.fileUploads.status,
|
|
568
|
+
})
|
|
569
|
+
.from(schema.generatedImageArtifacts)
|
|
570
|
+
.innerJoin(
|
|
571
|
+
schema.files,
|
|
572
|
+
and(
|
|
573
|
+
eq(schema.files.workspaceId, schema.generatedImageArtifacts.workspaceId),
|
|
574
|
+
eq(schema.files.id, schema.generatedImageArtifacts.artifactId),
|
|
575
|
+
),
|
|
576
|
+
)
|
|
577
|
+
.leftJoin(
|
|
578
|
+
schema.fileUploads,
|
|
579
|
+
eq(schema.fileUploads.id, schema.generatedImageArtifacts.uploadId),
|
|
580
|
+
)
|
|
581
|
+
.where(
|
|
582
|
+
and(
|
|
583
|
+
eq(schema.generatedImageArtifacts.workspaceId, workspaceId),
|
|
584
|
+
eq(schema.generatedImageArtifacts.artifactId, artifactId),
|
|
585
|
+
),
|
|
586
|
+
)
|
|
587
|
+
.limit(1);
|
|
588
|
+
return row ? mapGeneratedImageArtifact(row.artifact, row.file, row.uploadStatus) : null;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function mapGeneratedImageArtifact(
|
|
592
|
+
artifact: typeof schema.generatedImageArtifacts.$inferSelect,
|
|
593
|
+
file: typeof schema.files.$inferSelect,
|
|
594
|
+
uploadStatus: string | null,
|
|
595
|
+
): GeneratedImageArtifact {
|
|
596
|
+
return {
|
|
597
|
+
...artifact,
|
|
598
|
+
sourceStrategy: artifact.sourceStrategy as GeneratedImageSourceStrategy,
|
|
599
|
+
status: artifact.status as GeneratedImageArtifactStatus,
|
|
600
|
+
uploadStatus: uploadStatus as FileUploadStatus | null,
|
|
601
|
+
file: mapFile(file),
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function mapFile(row: typeof schema.files.$inferSelect): FileAsset {
|
|
606
|
+
return {
|
|
607
|
+
id: row.id,
|
|
608
|
+
workspaceId: row.workspaceId,
|
|
609
|
+
status: row.status as FileStatus,
|
|
610
|
+
filename: row.filename,
|
|
611
|
+
safeFilename: row.safeFilename,
|
|
612
|
+
contentType: row.contentType,
|
|
613
|
+
sizeBytes: row.sizeBytes,
|
|
614
|
+
sha256: row.sha256,
|
|
615
|
+
bucket: row.bucket,
|
|
616
|
+
objectKey: row.objectKey,
|
|
617
|
+
createdAt: row.createdAt.toISOString(),
|
|
618
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function assertPreparedArtifactMatches(
|
|
623
|
+
artifact: GeneratedImageArtifact,
|
|
624
|
+
input: PrepareGeneratedImageArtifactInput,
|
|
625
|
+
): void {
|
|
626
|
+
const adapterProvenanceMismatch =
|
|
627
|
+
artifact.sourceStrategy === "provider_adapter" &&
|
|
628
|
+
(artifact.sessionId !== input.sessionId ||
|
|
629
|
+
artifact.turnId !== input.turnId ||
|
|
630
|
+
artifact.toolCallId !== input.toolCallId);
|
|
631
|
+
const mismatched =
|
|
632
|
+
artifact.artifactId !== input.artifactId ||
|
|
633
|
+
artifact.accountId !== input.accountId ||
|
|
634
|
+
artifact.workspaceId !== input.workspaceId ||
|
|
635
|
+
artifact.uploadId !== input.uploadId ||
|
|
636
|
+
adapterProvenanceMismatch ||
|
|
637
|
+
artifact.sourceStrategy !== input.sourceStrategy ||
|
|
638
|
+
artifact.providerId !== input.providerId ||
|
|
639
|
+
artifact.providerBindingHash !== input.providerBindingHash ||
|
|
640
|
+
artifact.providerItemId !== input.providerItemId ||
|
|
641
|
+
artifact.mediaType !== input.mediaType ||
|
|
642
|
+
artifact.sizeBytes !== input.sizeBytes ||
|
|
643
|
+
artifact.sha256 !== input.sha256 ||
|
|
644
|
+
artifact.width !== input.width ||
|
|
645
|
+
artifact.height !== input.height ||
|
|
646
|
+
artifact.sandboxPath !== input.sandboxPath ||
|
|
647
|
+
artifact.file.filename !== input.filename ||
|
|
648
|
+
artifact.file.safeFilename !== input.safeFilename ||
|
|
649
|
+
artifact.file.contentType !== input.mediaType ||
|
|
650
|
+
artifact.file.sizeBytes !== input.sizeBytes ||
|
|
651
|
+
artifact.file.sha256 !== input.sha256 ||
|
|
652
|
+
artifact.file.bucket !== input.bucket ||
|
|
653
|
+
artifact.file.objectKey !== input.objectKey;
|
|
654
|
+
if (mismatched) {
|
|
655
|
+
throw new Error("Generated image retry does not match the reserved immutable artifact");
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async function imageGenerationOperationByKeyTx(
|
|
660
|
+
tx: Database,
|
|
661
|
+
workspaceId: string,
|
|
662
|
+
operationKey: string,
|
|
663
|
+
): Promise<ImageGenerationOperation | null> {
|
|
664
|
+
const [row] = await tx
|
|
665
|
+
.select()
|
|
666
|
+
.from(schema.imageGenerationOperations)
|
|
667
|
+
.where(
|
|
668
|
+
and(
|
|
669
|
+
eq(schema.imageGenerationOperations.workspaceId, workspaceId),
|
|
670
|
+
eq(schema.imageGenerationOperations.operationKey, operationKey),
|
|
671
|
+
),
|
|
672
|
+
)
|
|
673
|
+
.limit(1);
|
|
674
|
+
return row ? mapImageGenerationOperation(row) : null;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
async function mutateImageGenerationOperation(
|
|
678
|
+
db: Database,
|
|
679
|
+
input: {
|
|
680
|
+
accountId: string;
|
|
681
|
+
workspaceId: string;
|
|
682
|
+
operationId: string;
|
|
683
|
+
operationKey: string;
|
|
684
|
+
},
|
|
685
|
+
mutate: (tx: Database, current: ImageGenerationOperation) => Promise<ImageGenerationOperation>,
|
|
686
|
+
): Promise<ImageGenerationOperation> {
|
|
687
|
+
return await withRlsContext(
|
|
688
|
+
db,
|
|
689
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
690
|
+
async (scopedDb) =>
|
|
691
|
+
await scopedDb.transaction(async (tx) => {
|
|
692
|
+
const [row] = await tx
|
|
693
|
+
.select()
|
|
694
|
+
.from(schema.imageGenerationOperations)
|
|
695
|
+
.where(
|
|
696
|
+
and(
|
|
697
|
+
eq(schema.imageGenerationOperations.workspaceId, input.workspaceId),
|
|
698
|
+
eq(schema.imageGenerationOperations.id, input.operationId),
|
|
699
|
+
),
|
|
700
|
+
)
|
|
701
|
+
.for("update")
|
|
702
|
+
.limit(1);
|
|
703
|
+
if (!row || row.operationKey !== input.operationKey) {
|
|
704
|
+
throw new Error("Image generation operation identity does not match");
|
|
705
|
+
}
|
|
706
|
+
return await mutate(tx, mapImageGenerationOperation(row));
|
|
707
|
+
}),
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function mapImageGenerationOperation(
|
|
712
|
+
row: typeof schema.imageGenerationOperations.$inferSelect,
|
|
713
|
+
): ImageGenerationOperation {
|
|
714
|
+
return {
|
|
715
|
+
...row,
|
|
716
|
+
status: row.status as ImageGenerationOperationStatus,
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function assertImageGenerationOperationMatches(
|
|
721
|
+
operation: ImageGenerationOperation,
|
|
722
|
+
input: PrepareImageGenerationOperationInput,
|
|
723
|
+
): void {
|
|
724
|
+
if (
|
|
725
|
+
operation.id !== input.id ||
|
|
726
|
+
operation.accountId !== input.accountId ||
|
|
727
|
+
operation.workspaceId !== input.workspaceId ||
|
|
728
|
+
operation.sessionId !== input.sessionId ||
|
|
729
|
+
operation.turnId !== input.turnId ||
|
|
730
|
+
operation.operationKey !== input.operationKey ||
|
|
731
|
+
operation.toolCallId !== input.toolCallId ||
|
|
732
|
+
operation.providerId !== input.providerId ||
|
|
733
|
+
operation.providerBindingHash !== input.providerBindingHash ||
|
|
734
|
+
operation.modelId !== input.modelId ||
|
|
735
|
+
operation.requestDigest !== input.requestDigest ||
|
|
736
|
+
operation.expectedArtifactId !== input.expectedArtifactId
|
|
737
|
+
) {
|
|
738
|
+
throw new Error("Image generation retry does not match the reserved operation");
|
|
739
|
+
}
|
|
740
|
+
}
|