@omnicross/core 0.1.9 → 0.2.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 (176) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +57 -57
  3. package/README.md +22 -15
  4. package/dist/{CompletionService-kNA17cfI.d.cts → CompletionService-BiJqftc-.d.cts} +1 -1
  5. package/dist/{CompletionService-C3BX0Ovy.d.ts → CompletionService-NXN_Av5W.d.ts} +1 -1
  6. package/dist/{ProviderProxy-QdwNV6l1.d.cts → ProviderProxy-CgIcCVwk.d.cts} +1 -1
  7. package/dist/{ProviderProxy--ROec6VI.d.ts → ProviderProxy-DYoY5XoT.d.ts} +1 -1
  8. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +6 -6
  9. package/dist/auth/GeminiCodeAssistProjectResolver.js +4 -4
  10. package/dist/chunk-32SRZFFP.cjs +153 -0
  11. package/dist/chunk-4JOU54M5.js +200 -0
  12. package/dist/{chunk-2UTGX6FM.js → chunk-4VVCXVB7.js} +1 -1
  13. package/dist/{chunk-EPN67EGP.cjs → chunk-5UK3KLE2.cjs} +9 -1
  14. package/dist/chunk-6DDYNS2B.cjs +384 -0
  15. package/dist/{chunk-HKB656B4.js → chunk-6OC3ORW6.js} +55 -287
  16. package/dist/chunk-6OKXH2TW.js +320 -0
  17. package/dist/{chunk-XELFM7KL.cjs → chunk-7B56AJBB.cjs} +6275 -2421
  18. package/dist/{chunk-YWL7YZWS.cjs → chunk-7LRTPNFL.cjs} +5 -0
  19. package/dist/{chunk-CCVRRJOX.js → chunk-7PIUVCKK.js} +9 -2
  20. package/dist/{chunk-IIRPUVPH.cjs → chunk-7ZHBN75N.cjs} +3 -1
  21. package/dist/{chunk-UAPBLNN2.js → chunk-AQDON6MB.js} +8 -0
  22. package/dist/{chunk-RJ5ZGNI3.js → chunk-CFSGYFOZ.js} +1 -1
  23. package/dist/chunk-CTKM773J.cjs +3919 -0
  24. package/dist/chunk-DEIUR2TH.js +3919 -0
  25. package/dist/{chunk-WNKWAEUR.js → chunk-EYGBM2VC.js} +3 -1
  26. package/dist/{chunk-NGYOO5TO.js → chunk-FJNKXOWE.js} +9 -9
  27. package/dist/chunk-FX325I5Q.cjs +320 -0
  28. package/dist/chunk-G4CZIUIB.cjs +200 -0
  29. package/dist/chunk-HCOQQAZE.cjs +123 -0
  30. package/dist/{chunk-55FMGZ3L.js → chunk-HN74IGEO.js} +1 -1
  31. package/dist/{chunk-ZY6KU6P3.js → chunk-HOMUGKHR.js} +8 -5
  32. package/dist/{chunk-AOFWNWLT.cjs → chunk-IUL4VWNU.cjs} +22 -1
  33. package/dist/{chunk-5LNYGCIW.cjs → chunk-KIQ2HA2A.cjs} +5 -5
  34. package/dist/{chunk-MUHWHQVM.cjs → chunk-KQBLZMP6.cjs} +4 -3
  35. package/dist/chunk-LFEH7CEO.js +384 -0
  36. package/dist/chunk-N76KU6F5.js +123 -0
  37. package/dist/{chunk-CMVXX7ON.cjs → chunk-NOITAVHA.cjs} +114 -58
  38. package/dist/{chunk-Q27JY5RG.cjs → chunk-OIKREZVQ.cjs} +7 -4
  39. package/dist/{chunk-RJUONLUQ.cjs → chunk-OST5RJOJ.cjs} +1 -1
  40. package/dist/chunk-QIIAMBTM.js +153 -0
  41. package/dist/{chunk-56QD3YW6.cjs → chunk-QWNUOR7F.cjs} +11 -4
  42. package/dist/{chunk-S3DYFEBE.js → chunk-R4VCO2CB.js} +10 -1
  43. package/dist/{chunk-ZQQUD66M.cjs → chunk-SQMAEDNE.cjs} +63 -295
  44. package/dist/{chunk-4VZX5F4T.cjs → chunk-T6NWPIYZ.cjs} +5 -2
  45. package/dist/{chunk-P7EM6BND.cjs → chunk-TVYA5JSQ.cjs} +10 -10
  46. package/dist/{chunk-EBLFCNPY.js → chunk-U47LQXPB.js} +5 -2
  47. package/dist/{chunk-N7OOJI5V.js → chunk-V2UKUTJ3.js} +5 -0
  48. package/dist/{chunk-45FHEOKW.js → chunk-V53ZEFXC.js} +92 -36
  49. package/dist/{chunk-OC6DOOFX.cjs → chunk-VXRTQZF2.cjs} +5 -5
  50. package/dist/{chunk-CQIJCPMF.js → chunk-WSJE42SG.js} +5149 -1295
  51. package/dist/{chunk-RICPANBY.cjs → chunk-WX2E2G7H.cjs} +13 -4
  52. package/dist/{chunk-DE2SRBFZ.js → chunk-X335TJNS.js} +4 -3
  53. package/dist/{chunk-N2EIM2XJ.js → chunk-ZLNHPUFA.js} +22 -1
  54. package/dist/completion/CompletionService.cjs +30 -27
  55. package/dist/completion/CompletionService.d.cts +3 -2
  56. package/dist/completion/CompletionService.d.ts +3 -2
  57. package/dist/completion/CompletionService.js +29 -26
  58. package/dist/completion.cjs +38 -35
  59. package/dist/completion.d.cts +3 -2
  60. package/dist/completion.d.ts +3 -2
  61. package/dist/completion.js +37 -34
  62. package/dist/image-generation.cjs +52 -0
  63. package/dist/image-generation.d.cts +93 -0
  64. package/dist/image-generation.d.ts +93 -0
  65. package/dist/image-generation.js +52 -0
  66. package/dist/index.cjs +126 -31
  67. package/dist/index.d.cts +11 -7
  68. package/dist/index.d.ts +11 -7
  69. package/dist/index.js +143 -48
  70. package/dist/openai-operation.cjs +18 -0
  71. package/dist/openai-operation.d.cts +19 -0
  72. package/dist/openai-operation.d.ts +19 -0
  73. package/dist/openai-operation.js +18 -0
  74. package/dist/outbound-api/auditCapture.cjs +3 -3
  75. package/dist/outbound-api/auditCapture.d.cts +19 -2
  76. package/dist/outbound-api/auditCapture.d.ts +19 -2
  77. package/dist/outbound-api/auditCapture.js +2 -2
  78. package/dist/outbound-api/auditRedact.cjs +2 -2
  79. package/dist/outbound-api/auditRedact.js +1 -1
  80. package/dist/outbound-api/routeResolver.cjs +2 -2
  81. package/dist/outbound-api/routeResolver.d.cts +7 -4
  82. package/dist/outbound-api/routeResolver.d.ts +7 -4
  83. package/dist/outbound-api/routeResolver.js +1 -1
  84. package/dist/outbound-api/subscriptionRegistryPort.d.cts +2 -1
  85. package/dist/outbound-api/subscriptionRegistryPort.d.ts +2 -1
  86. package/dist/outbound-api/types.d.cts +6 -3
  87. package/dist/outbound-api/types.d.ts +6 -3
  88. package/dist/outbound-api.cjs +49 -32
  89. package/dist/outbound-api.d.cts +97 -10
  90. package/dist/outbound-api.d.ts +97 -10
  91. package/dist/outbound-api.js +48 -31
  92. package/dist/pipeline/AccountAllowanceScheduling.d.cts +18 -15
  93. package/dist/pipeline/AccountAllowanceScheduling.d.ts +18 -15
  94. package/dist/pipeline/LlmConfigProviderAuth.cjs +3 -3
  95. package/dist/pipeline/LlmConfigProviderAuth.js +2 -2
  96. package/dist/pipeline/SubscriptionAccountHealth.cjs +2 -2
  97. package/dist/pipeline/SubscriptionAccountHealth.d.cts +7 -0
  98. package/dist/pipeline/SubscriptionAccountHealth.d.ts +7 -0
  99. package/dist/pipeline/SubscriptionAccountHealth.js +1 -1
  100. package/dist/pipeline/executeProviderCall.cjs +2 -2
  101. package/dist/pipeline/executeProviderCall.d.cts +11 -6
  102. package/dist/pipeline/executeProviderCall.d.ts +11 -6
  103. package/dist/pipeline/executeProviderCall.js +1 -1
  104. package/dist/pipeline/upstreamFetch.cjs +2 -2
  105. package/dist/pipeline/upstreamFetch.d.cts +5 -0
  106. package/dist/pipeline/upstreamFetch.d.ts +5 -0
  107. package/dist/pipeline/upstreamFetch.js +1 -1
  108. package/dist/ports.d.cts +6 -3
  109. package/dist/ports.d.ts +6 -3
  110. package/dist/provider-proxy/ProviderProxy.cjs +30 -27
  111. package/dist/provider-proxy/ProviderProxy.d.cts +3 -2
  112. package/dist/provider-proxy/ProviderProxy.d.ts +3 -2
  113. package/dist/provider-proxy/ProviderProxy.js +29 -26
  114. package/dist/provider-proxy/identity/codexCliHeaders.cjs +4 -2
  115. package/dist/provider-proxy/identity/codexCliHeaders.d.cts +26 -6
  116. package/dist/provider-proxy/identity/codexCliHeaders.d.ts +26 -6
  117. package/dist/provider-proxy/identity/codexCliHeaders.js +5 -3
  118. package/dist/provider-proxy/ingress/providerProxyShared.cjs +32 -27
  119. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +21 -8
  120. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +21 -8
  121. package/dist/provider-proxy/ingress/providerProxyShared.js +31 -26
  122. package/dist/provider-proxy/types.d.cts +14 -483
  123. package/dist/provider-proxy/types.d.ts +14 -483
  124. package/dist/provider-proxy.cjs +44 -27
  125. package/dist/provider-proxy.d.cts +30 -8
  126. package/dist/provider-proxy.d.ts +30 -8
  127. package/dist/provider-proxy.js +43 -26
  128. package/dist/{routeResolver-C0mkkZaM.d.ts → routeResolver-CTCcSHWu.d.ts} +4 -2
  129. package/dist/{routeResolver-CVFE8KKe.d.cts → routeResolver-rpKQLyDO.d.cts} +4 -2
  130. package/dist/transformer/TransformerChainExecutor.cjs +2 -2
  131. package/dist/transformer/TransformerChainExecutor.d.cts +2 -2
  132. package/dist/transformer/TransformerChainExecutor.d.ts +2 -2
  133. package/dist/transformer/TransformerChainExecutor.js +1 -1
  134. package/dist/transformer/transformers/AnthropicOpenAIToAnthropicStream.cjs +16 -0
  135. package/dist/transformer/transformers/AnthropicOpenAIToAnthropicStream.d.cts +53 -0
  136. package/dist/transformer/transformers/AnthropicOpenAIToAnthropicStream.d.ts +53 -0
  137. package/dist/transformer/transformers/AnthropicOpenAIToAnthropicStream.js +16 -0
  138. package/dist/transformer/transformers/AnthropicTransformer.cjs +4 -3
  139. package/dist/transformer/transformers/AnthropicTransformer.js +3 -2
  140. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +4 -4
  141. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +3 -3
  142. package/dist/transformer/transformers/GeminiTransformer.cjs +4 -4
  143. package/dist/transformer/transformers/GeminiTransformer.js +3 -3
  144. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +3 -3
  145. package/dist/transformer/transformers/OpenAIResponseTransformer.js +2 -2
  146. package/dist/transformer/transformers/OpenAITransformer.cjs +3 -3
  147. package/dist/transformer/transformers/OpenAITransformer.js +2 -2
  148. package/dist/transformer/transformers.cjs +10 -9
  149. package/dist/transformer/transformers.js +13 -12
  150. package/dist/transformer/types.d.cts +39 -4
  151. package/dist/transformer/types.d.ts +39 -4
  152. package/dist/transformer.cjs +14 -13
  153. package/dist/transformer.js +14 -13
  154. package/dist/{types-BB8pxk_3.d.ts → types-5T223WYQ.d.ts} +183 -23
  155. package/dist/types-Bd72H4SB.d.ts +196 -0
  156. package/dist/types-CVRge76p.d.ts +1115 -0
  157. package/dist/types-CbglB73l.d.cts +196 -0
  158. package/dist/types-DsHBBk3h.d.cts +1115 -0
  159. package/dist/{types-C2MwkLah.d.cts → types-jjU24eYG.d.cts} +183 -23
  160. package/dist/usage/usage-recorder.cjs +2 -2
  161. package/dist/usage/usage-recorder.d.cts +12 -2
  162. package/dist/usage/usage-recorder.d.ts +12 -2
  163. package/dist/usage/usage-recorder.js +1 -1
  164. package/dist/usage.cjs +20 -3
  165. package/dist/usage.d.cts +154 -0
  166. package/dist/usage.d.ts +154 -0
  167. package/dist/usage.js +20 -3
  168. package/package.json +69 -64
  169. package/dist/chunk-7C7DET6S.js +0 -154
  170. package/dist/chunk-7VU7V2E4.js +0 -0
  171. package/dist/chunk-EYZYXJTJ.cjs +0 -1
  172. package/dist/chunk-FCR77GYM.cjs +0 -33
  173. package/dist/chunk-K5NM7VAH.js +0 -33
  174. package/dist/chunk-XPIOJQS2.cjs +0 -154
  175. /package/dist/{chunk-XXFYLL7R.js → chunk-ECZ3ZE2C.js} +0 -0
  176. /package/dist/{chunk-3SCOLIPT.cjs → chunk-MXI2PWIC.cjs} +0 -0
@@ -0,0 +1,3919 @@
1
+ // src/image-generation/capabilities.ts
2
+ var BOOLEAN_KEYS = [
3
+ "available",
4
+ "generate",
5
+ "edit",
6
+ "maskEdit",
7
+ "streaming",
8
+ "transparentBackground",
9
+ "flexibleSizes",
10
+ "responsesTool",
11
+ "multiTurnEdit",
12
+ "supportsFileId",
13
+ "supportsImageUrl"
14
+ ];
15
+ var NUMERIC_KEYS = [
16
+ "maxInputImages",
17
+ "maxOutputImages",
18
+ "maxPartialImages"
19
+ ];
20
+ var OUTPUT_COMPRESSION_FORMATS = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
21
+ function isValidOutputCompressionCapability(value) {
22
+ if (!value || value.supported !== true || !Array.isArray(value.formats) || value.formats.length === 0 || value.formats.some((format) => !OUTPUT_COMPRESSION_FORMATS.has(format)) || new Set(value.formats).size !== value.formats.length || !Number.isInteger(value.min) || value.min < 0 || value.min > 100 || !Number.isInteger(value.max) || value.max < 0 || value.max > 100 || value.min > value.max) {
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+ function intersectStrings(layers, key) {
28
+ const allowed = key === "outputFormats" ? /* @__PURE__ */ new Set(["png", "jpeg", "webp"]) : key === "qualityLevels" ? /* @__PURE__ */ new Set(["auto", "low", "medium", "high"]) : key === "moderationModes" ? /* @__PURE__ */ new Set(["auto", "low"]) : void 0;
29
+ const sets = layers.map((layer) => {
30
+ const value = layer.values?.[key];
31
+ return new Set(Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0 && item.length <= 256 && (!allowed || allowed.has(item))) : []);
32
+ });
33
+ if (sets.length === 0) return [];
34
+ return [...sets[0]].filter((value) => sets.slice(1).every((set) => set.has(value))).sort();
35
+ }
36
+ function intersectOutputCompression(layers) {
37
+ const values = layers.map((layer) => layer.values?.outputCompression);
38
+ if (!values.every(isValidOutputCompressionCapability)) {
39
+ return { supported: false };
40
+ }
41
+ const formats = values[0].formats.filter((format) => values.slice(1).every((value) => value.formats.includes(format))).sort();
42
+ const min = Math.max(...values.map((value) => value.min));
43
+ const max = Math.min(...values.map((value) => value.max));
44
+ if (formats.length === 0 || min > max) {
45
+ return { supported: false };
46
+ }
47
+ return { supported: true, formats, min, max };
48
+ }
49
+ function unavailableReason(layers, now) {
50
+ for (const layer of layers) {
51
+ if (layer.kind !== "adapter" && layer.kind !== "account" && layer.kind !== "upstream") {
52
+ return "missing_evidence";
53
+ }
54
+ if (!Number.isFinite(layer.verifiedAt) || !layer.values) {
55
+ if (layer.kind === "account") return "account_unverified";
56
+ if (layer.kind === "upstream") return "protocol_unverified";
57
+ return "missing_evidence";
58
+ }
59
+ if (layer.expiresAt != null && (!Number.isFinite(layer.expiresAt) || layer.expiresAt <= now)) {
60
+ return "stale_evidence";
61
+ }
62
+ }
63
+ const availability = layers.map((layer) => layer.values?.available);
64
+ if (availability.some((value) => value === true) && availability.some((value) => value === false)) {
65
+ return "contradictory_evidence";
66
+ }
67
+ if (availability.some((value) => value !== true)) {
68
+ const unavailable = layers.find((layer) => layer.values?.available === false);
69
+ if (unavailable?.kind === "adapter") return "adapter_unavailable";
70
+ if (unavailable?.kind === "account") return "account_unverified";
71
+ if (unavailable?.kind === "upstream") return "protocol_unverified";
72
+ return "missing_evidence";
73
+ }
74
+ return void 0;
75
+ }
76
+ function resolveImageCapabilities(evidence, now = Date.now()) {
77
+ const layers = [evidence.adapter, evidence.account, evidence.upstream];
78
+ let reason = unavailableReason(layers, now);
79
+ const booleans = Object.fromEntries(
80
+ BOOLEAN_KEYS.map((key) => [key, layers.every((layer) => layer.values?.[key] === true)])
81
+ );
82
+ const numbers = Object.fromEntries(
83
+ NUMERIC_KEYS.map((key) => {
84
+ const values = layers.map((layer) => layer.values?.[key]);
85
+ const valid = values.every((value) => Number.isInteger(value) && value >= 0);
86
+ return [key, valid ? Math.min(...values) : 0];
87
+ })
88
+ );
89
+ const models = intersectStrings(layers, "models");
90
+ const outputFormats = intersectStrings(layers, "outputFormats");
91
+ const qualityLevels = intersectStrings(layers, "qualityLevels");
92
+ const moderationModes = intersectStrings(layers, "moderationModes");
93
+ const outputCompression = intersectOutputCompression(layers);
94
+ if (!reason && models.length === 0) reason = "no_common_models";
95
+ if (!reason && outputFormats.length === 0) reason = "no_common_output_formats";
96
+ if (!reason && qualityLevels.length === 0) reason = "no_common_quality_levels";
97
+ if (!reason && moderationModes.length === 0) reason = "no_common_moderation_modes";
98
+ const verifiedAt = layers.map((layer) => layer.verifiedAt).filter((value) => typeof value === "number" && Number.isFinite(value));
99
+ return {
100
+ ...booleans,
101
+ ...numbers,
102
+ available: !reason && booleans.available && models.length > 0 && outputFormats.length > 0 && qualityLevels.length > 0 && moderationModes.length > 0,
103
+ models,
104
+ outputFormats,
105
+ qualityLevels,
106
+ moderationModes,
107
+ outputCompression,
108
+ reason,
109
+ resolvedAt: now,
110
+ oldestEvidenceAt: verifiedAt.length === layers.length ? Math.min(...verifiedAt) : void 0
111
+ };
112
+ }
113
+
114
+ // src/image-generation/errors.ts
115
+ import {
116
+ IMAGE_GENERATION_ERROR_CODES
117
+ } from "@omnicross/contracts/image-generation-types";
118
+ var DEFAULT_MESSAGES = {
119
+ invalid_image_request: "The image request is invalid.",
120
+ unsupported_model: "The requested image model is not supported.",
121
+ unsupported_capability: "The requested image capability is not available.",
122
+ invalid_api_key: "The API key is invalid.",
123
+ upstream_auth_required: "The upstream image provider requires authentication.",
124
+ image_reference_not_found: "The image reference was not found.",
125
+ image_reference_expired: "The image reference has expired.",
126
+ image_too_large: "The image exceeds the configured size limit.",
127
+ unsupported_image_type: "The image type is not supported.",
128
+ moderation_blocked: "The image request was blocked by moderation.",
129
+ upstream_rate_limited: "The upstream image provider is rate limited.",
130
+ subscription_usage_limit_reached: "The subscription image usage limit has been reached.",
131
+ image_queue_full: "The image execution queue is full.",
132
+ image_queue_timeout: "The image execution queue timed out.",
133
+ request_cancelled: "The image request was cancelled.",
134
+ upstream_protocol_changed: "The upstream image protocol could not be recognized.",
135
+ image_generation_failed: "The upstream image generation failed.",
136
+ image_generation_timeout: "The upstream image generation timed out."
137
+ };
138
+ var DEFAULT_HTTP_STATUS = {
139
+ invalid_image_request: 400,
140
+ unsupported_model: 400,
141
+ unsupported_capability: 422,
142
+ invalid_api_key: 401,
143
+ upstream_auth_required: 503,
144
+ image_reference_not_found: 404,
145
+ image_reference_expired: 410,
146
+ image_too_large: 413,
147
+ unsupported_image_type: 415,
148
+ moderation_blocked: 422,
149
+ upstream_rate_limited: 429,
150
+ subscription_usage_limit_reached: 429,
151
+ image_queue_full: 429,
152
+ image_queue_timeout: 504,
153
+ request_cancelled: 499,
154
+ upstream_protocol_changed: 502,
155
+ image_generation_failed: 502,
156
+ image_generation_timeout: 504
157
+ };
158
+ function safeParam(value) {
159
+ return typeof value === "string" && /^[a-z][a-z0-9_.-]{0,63}$/i.test(value) ? value : void 0;
160
+ }
161
+ function safeModerationDetails(value) {
162
+ if (!value) return void 0;
163
+ const stage = value.stage === "input" || value.stage === "output" ? value.stage : void 0;
164
+ const categories = value.categories?.filter((item) => /^[a-z][a-z0-9_.-]{0,63}$/i.test(item)).slice(0, 16);
165
+ return stage || categories?.length ? { stage, categories: categories?.length ? categories : void 0 } : void 0;
166
+ }
167
+ var ImageGenerationError = class extends Error {
168
+ code;
169
+ param;
170
+ httpStatus;
171
+ retryAfterSeconds;
172
+ retrySafety;
173
+ moderationDetails;
174
+ constructor(code, options = {}) {
175
+ super(DEFAULT_MESSAGES[code]);
176
+ this.name = "ImageGenerationError";
177
+ this.code = code;
178
+ this.param = safeParam(options.param);
179
+ this.httpStatus = Number.isInteger(options.httpStatus) ? Math.min(599, Math.max(400, options.httpStatus)) : DEFAULT_HTTP_STATUS[code];
180
+ this.retryAfterSeconds = typeof options.retryAfterSeconds === "number" && Number.isFinite(options.retryAfterSeconds) && options.retryAfterSeconds >= 0 ? Math.min(604800, Math.floor(options.retryAfterSeconds)) : void 0;
181
+ this.retrySafety = options.retrySafety;
182
+ this.moderationDetails = safeModerationDetails(options.moderationDetails);
183
+ if (options.cause !== void 0) {
184
+ Object.defineProperty(this, "cause", {
185
+ value: options.cause,
186
+ configurable: true,
187
+ enumerable: false,
188
+ writable: false
189
+ });
190
+ }
191
+ }
192
+ toJSON() {
193
+ return serializeImageGenerationError(this);
194
+ }
195
+ };
196
+ function isImageGenerationError(error) {
197
+ return error instanceof ImageGenerationError;
198
+ }
199
+ function serializeImageGenerationError(error) {
200
+ return {
201
+ type: error.code === "moderation_blocked" ? "image_generation_user_error" : "image_generation_error",
202
+ code: error.code,
203
+ message: DEFAULT_MESSAGES[error.code],
204
+ httpStatus: error.httpStatus,
205
+ ...error.param !== void 0 ? { param: error.param } : {},
206
+ ...error.retryAfterSeconds !== void 0 ? { retryAfterSeconds: error.retryAfterSeconds } : {},
207
+ ...error.retrySafety !== void 0 ? { retrySafety: error.retrySafety } : {},
208
+ ...error.moderationDetails !== void 0 ? { moderationDetails: error.moderationDetails } : {}
209
+ };
210
+ }
211
+ function normalizeImageGenerationError(error, fallbackCode = "image_generation_failed", options = {}) {
212
+ if (isImageGenerationError(error)) return error;
213
+ return new ImageGenerationError(fallbackCode, { ...options, cause: error });
214
+ }
215
+ function imageGenerationErrorFromPublic(value) {
216
+ if (!value || typeof value !== "object") return new ImageGenerationError("image_generation_failed");
217
+ const record2 = value;
218
+ const code = IMAGE_GENERATION_ERROR_CODES.includes(record2.code) ? record2.code : "image_generation_failed";
219
+ return new ImageGenerationError(code, {
220
+ param: safeParam(record2.param),
221
+ httpStatus: typeof record2.httpStatus === "number" ? record2.httpStatus : void 0,
222
+ retryAfterSeconds: typeof record2.retryAfterSeconds === "number" ? record2.retryAfterSeconds : void 0,
223
+ retrySafety: record2.retrySafety === "before_acceptance" || record2.retrySafety === "after_acceptance" || record2.retrySafety === "unknown" ? record2.retrySafety : void 0,
224
+ moderationDetails: record2.moderationDetails && typeof record2.moderationDetails === "object" ? record2.moderationDetails : void 0
225
+ });
226
+ }
227
+
228
+ // src/image-generation/ports.ts
229
+ import { randomUUID } from "crypto";
230
+ var InMemoryImageAsset = class {
231
+ artifactId;
232
+ mimeType;
233
+ byteLength;
234
+ width;
235
+ height;
236
+ hasAlpha;
237
+ sha256;
238
+ independentlyDecodable = true;
239
+ #bytes;
240
+ constructor(bytes, metadata) {
241
+ if (bytes.byteLength === 0) throw new TypeError("Image asset bytes must not be empty.");
242
+ this.#bytes = bytes.slice();
243
+ this.artifactId = metadata.artifactId ?? randomUUID();
244
+ this.mimeType = metadata.mimeType;
245
+ this.byteLength = bytes.byteLength;
246
+ this.width = metadata.width;
247
+ this.height = metadata.height;
248
+ this.hasAlpha = metadata.hasAlpha;
249
+ this.sha256 = metadata.sha256;
250
+ }
251
+ async open(options = {}) {
252
+ if (options.signal?.aborted) throw options.signal.reason;
253
+ const bytes = this.#bytes.slice();
254
+ return new ReadableStream({
255
+ start(controller) {
256
+ if (options.signal?.aborted) {
257
+ controller.error(options.signal.reason);
258
+ return;
259
+ }
260
+ controller.enqueue(bytes);
261
+ controller.close();
262
+ }
263
+ });
264
+ }
265
+ /** Best-effort zeroization for short-lived verified/probe artifacts. */
266
+ dispose() {
267
+ this.#bytes.fill(0);
268
+ }
269
+ };
270
+ async function readImageAssetBytes(asset, maxBytes, signal) {
271
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0 || asset.byteLength > maxBytes) {
272
+ throw new RangeError("Image asset exceeds the bounded read limit.");
273
+ }
274
+ if (signal?.aborted) throw signal.reason;
275
+ const reader = (await asset.open({ signal })).getReader();
276
+ const chunks = [];
277
+ let total = 0;
278
+ try {
279
+ while (true) {
280
+ if (signal?.aborted) throw signal.reason;
281
+ const next = await reader.read();
282
+ if (next.done) break;
283
+ total += next.value.byteLength;
284
+ if (total > maxBytes || total > asset.byteLength) {
285
+ await reader.cancel();
286
+ throw new RangeError("Image asset stream exceeded its declared bound.");
287
+ }
288
+ chunks.push(next.value);
289
+ }
290
+ } finally {
291
+ reader.releaseLock();
292
+ }
293
+ if (total !== asset.byteLength) throw new RangeError("Image asset stream length did not match metadata.");
294
+ const output = new Uint8Array(total);
295
+ let offset = 0;
296
+ for (const chunk of chunks) {
297
+ output.set(chunk, offset);
298
+ offset += chunk.byteLength;
299
+ }
300
+ return output;
301
+ }
302
+ var InMemoryImageReferenceStore = class {
303
+ #entries = /* @__PURE__ */ new Map();
304
+ #now;
305
+ constructor(now = Date.now) {
306
+ this.#now = now;
307
+ }
308
+ async save(input) {
309
+ if (!input.artifact && !input.providerReference) {
310
+ throw new TypeError("A retained image reference requires content or an opaque provider reference.");
311
+ }
312
+ if (!Number.isSafeInteger(input.ttlMs) || input.ttlMs <= 0) {
313
+ throw new RangeError("Image reference TTL must be a positive integer.");
314
+ }
315
+ const now = this.#now();
316
+ const metadata = {
317
+ ...input.metadata,
318
+ referenceId: randomUUID(),
319
+ createdAt: now,
320
+ expiresAt: now + input.ttlMs
321
+ };
322
+ this.#entries.set(metadata.referenceId, {
323
+ tenantId: input.tenantId,
324
+ metadata,
325
+ value: { artifact: input.artifact, providerReference: input.providerReference },
326
+ activeLeases: 0,
327
+ deleted: false
328
+ });
329
+ return metadata;
330
+ }
331
+ async resolve(tenantId, referenceId) {
332
+ const entry = this.#entries.get(referenceId);
333
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) return { status: "not_found" };
334
+ if (entry.metadata.expiresAt <= this.#now()) return { status: "expired" };
335
+ entry.activeLeases += 1;
336
+ let released = false;
337
+ return {
338
+ status: "found",
339
+ lease: {
340
+ metadata: entry.metadata,
341
+ value: entry.value,
342
+ async release() {
343
+ if (released) return;
344
+ released = true;
345
+ entry.activeLeases = Math.max(0, entry.activeLeases - 1);
346
+ }
347
+ }
348
+ };
349
+ }
350
+ async delete(tenantId, referenceId) {
351
+ const entry = this.#entries.get(referenceId);
352
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) return false;
353
+ entry.deleted = true;
354
+ if (entry.activeLeases === 0) this.#entries.delete(referenceId);
355
+ return true;
356
+ }
357
+ async cleanup(now = this.#now()) {
358
+ let removed = 0;
359
+ for (const [referenceId, entry] of this.#entries) {
360
+ if ((entry.deleted || entry.metadata.expiresAt <= now) && entry.activeLeases === 0) {
361
+ this.#entries.delete(referenceId);
362
+ removed += 1;
363
+ }
364
+ }
365
+ return removed;
366
+ }
367
+ };
368
+ async function emitImageTelemetry(sink, record2) {
369
+ try {
370
+ await sink?.record(record2);
371
+ } catch {
372
+ }
373
+ }
374
+
375
+ // src/image-generation/ImageOrchestrator.ts
376
+ function safeJobObservability(job) {
377
+ try {
378
+ const value = job?.observability?.snapshot();
379
+ if (!value) return void 0;
380
+ const finite = (input) => typeof input === "number" && Number.isFinite(input) && input >= 0 ? Math.min(Number.MAX_SAFE_INTEGER, input) : void 0;
381
+ return {
382
+ queueWaitMs: finite(value.queueWaitMs),
383
+ generationStartedAt: finite(value.generationStartedAt),
384
+ retryCount: finite(value.retryCount),
385
+ authRefreshCount: finite(value.authRefreshCount)
386
+ };
387
+ } catch {
388
+ return void 0;
389
+ }
390
+ }
391
+ function unsupported(param) {
392
+ throw new ImageGenerationError("unsupported_capability", { param });
393
+ }
394
+ function assertRequestSupported(request2, capabilities) {
395
+ if (typeof request2.prompt !== "string" || !request2.prompt.trim() || request2.prompt.length > 32e3) {
396
+ throw new ImageGenerationError("invalid_image_request", { param: "prompt" });
397
+ }
398
+ if (!capabilities.available) unsupported();
399
+ if (!capabilities.models.includes(request2.model)) {
400
+ throw new ImageGenerationError("unsupported_model", { param: "model" });
401
+ }
402
+ if (!Number.isInteger(request2.n) || request2.n <= 0) {
403
+ throw new ImageGenerationError("invalid_image_request", { param: "n" });
404
+ }
405
+ if (request2.n > capabilities.maxOutputImages) unsupported("n");
406
+ if (!capabilities.outputFormats.includes(request2.outputFormat)) unsupported("output_format");
407
+ if (!Array.isArray(capabilities.qualityLevels) || !capabilities.qualityLevels.includes(request2.quality)) {
408
+ unsupported("quality");
409
+ }
410
+ if (!Array.isArray(capabilities.moderationModes) || !capabilities.moderationModes.includes(request2.moderation)) {
411
+ unsupported("moderation");
412
+ }
413
+ if (request2.outputCompression !== void 0 && (!Number.isInteger(request2.outputCompression) || request2.outputCompression < 0 || request2.outputCompression > 100)) {
414
+ throw new ImageGenerationError("invalid_image_request", { param: "output_compression" });
415
+ }
416
+ if (request2.outputCompression !== void 0) {
417
+ const compression = capabilities.outputCompression;
418
+ if (!compression || compression.supported !== true || !compression.formats.includes(request2.outputFormat) || request2.outputCompression < compression.min || request2.outputCompression > compression.max) {
419
+ unsupported("output_compression");
420
+ }
421
+ }
422
+ if (request2.background === "transparent" && !capabilities.transparentBackground) {
423
+ unsupported("background");
424
+ }
425
+ if (request2.size.kind === "pixels" && !capabilities.flexibleSizes) unsupported("size");
426
+ if (request2.stream && !capabilities.streaming) unsupported("stream");
427
+ if (!Number.isInteger(request2.partialImages) || request2.partialImages < 0) {
428
+ throw new ImageGenerationError("invalid_image_request", { param: "partial_images" });
429
+ }
430
+ if (request2.partialImages > 0 && !request2.stream) {
431
+ throw new ImageGenerationError("invalid_image_request", { param: "partial_images" });
432
+ }
433
+ if (request2.partialImages > capabilities.maxPartialImages) unsupported("partial_images");
434
+ if (request2.action === "generate") {
435
+ if (!capabilities.generate) unsupported("action");
436
+ return;
437
+ }
438
+ if (!capabilities.edit) unsupported("action");
439
+ if (request2.images.length === 0) {
440
+ throw new ImageGenerationError("invalid_image_request", { param: "images" });
441
+ }
442
+ if (request2.images.length > capabilities.maxInputImages) unsupported("images");
443
+ if (request2.mask && !capabilities.maskEdit) unsupported("mask");
444
+ }
445
+ function sanitizeUsage(value) {
446
+ if (value === void 0) return void 0;
447
+ const keys = [
448
+ "totalTokens",
449
+ "inputTokens",
450
+ "outputTokens",
451
+ "inputTextTokens",
452
+ "inputImageTokens",
453
+ "outputImageTokens",
454
+ "generatedImages"
455
+ ];
456
+ const output = {};
457
+ for (const key of keys) {
458
+ const item = value[key];
459
+ if (item === void 0) continue;
460
+ if (!Number.isSafeInteger(item) || item < 0) {
461
+ throw new ImageGenerationError("upstream_protocol_changed");
462
+ }
463
+ output[key] = item;
464
+ }
465
+ return Object.keys(output).length ? output : void 0;
466
+ }
467
+ function assertArtifactMetadata(asset) {
468
+ if (asset.independentlyDecodable !== true || !asset.mimeType.startsWith("image/") || !Number.isSafeInteger(asset.byteLength) || asset.byteLength <= 0 || !Number.isSafeInteger(asset.width) || asset.width <= 0 || !Number.isSafeInteger(asset.height) || asset.height <= 0 || typeof asset.open !== "function") {
469
+ throw new ImageGenerationError("upstream_protocol_changed");
470
+ }
471
+ }
472
+ function sanitizeProviderOutput(value) {
473
+ if (!value || typeof value !== "object") {
474
+ throw new ImageGenerationError("upstream_protocol_changed");
475
+ }
476
+ assertArtifactMetadata(value.artifact);
477
+ if (value.providerReference !== void 0 && (typeof value.providerReference !== "string" || value.providerReference.length === 0 || value.providerReference.length > 4096)) {
478
+ throw new ImageGenerationError("upstream_protocol_changed");
479
+ }
480
+ if (value.revisedPrompt !== void 0 && (typeof value.revisedPrompt !== "string" || value.revisedPrompt.length > 32e3)) {
481
+ throw new ImageGenerationError("upstream_protocol_changed");
482
+ }
483
+ return {
484
+ artifact: value.artifact,
485
+ ...value.providerReference !== void 0 ? { providerReference: value.providerReference } : {},
486
+ ...value.revisedPrompt !== void 0 ? { revisedPrompt: value.revisedPrompt } : {}
487
+ };
488
+ }
489
+ var OUTPUT_MIME_TYPES = {
490
+ png: "image/png",
491
+ jpeg: "image/jpeg",
492
+ webp: "image/webp"
493
+ };
494
+ function assertFinalOutputMatchesRequest(output, request2) {
495
+ const artifact = output.artifact;
496
+ if (artifact.mimeType !== OUTPUT_MIME_TYPES[request2.outputFormat]) {
497
+ throw new ImageGenerationError("upstream_protocol_changed");
498
+ }
499
+ if (request2.size.kind === "pixels" && (artifact.width !== request2.size.width || artifact.height !== request2.size.height)) {
500
+ throw new ImageGenerationError("upstream_protocol_changed");
501
+ }
502
+ if (request2.background === "transparent" && artifact.hasAlpha !== true) {
503
+ throw new ImageGenerationError("upstream_protocol_changed");
504
+ }
505
+ }
506
+ function cancellationError(cause) {
507
+ return new ImageGenerationError("request_cancelled", { cause });
508
+ }
509
+ var ImageOrchestrator = class {
510
+ #registry;
511
+ #referenceStore;
512
+ #telemetrySink;
513
+ #now;
514
+ constructor(options) {
515
+ this.#registry = options.registry;
516
+ this.#referenceStore = options.referenceStore;
517
+ this.#telemetrySink = options.telemetrySink;
518
+ this.#now = options.now ?? Date.now;
519
+ }
520
+ async getCapabilities(providerId, context) {
521
+ let lease;
522
+ try {
523
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
524
+ lease = await this.#registry.require(providerId).acquire(context);
525
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
526
+ return lease.capabilities;
527
+ } catch (error) {
528
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
529
+ throw normalizeImageGenerationError(error);
530
+ } finally {
531
+ await lease?.release();
532
+ }
533
+ }
534
+ async *run(request2, context, options) {
535
+ const startedAt = this.#now();
536
+ let acceptedAt;
537
+ let firstPartialAt;
538
+ let lease;
539
+ let job;
540
+ let iterator;
541
+ let releaseStarted = false;
542
+ let cancelled5 = false;
543
+ let cancelPromise;
544
+ let telemetrySent = false;
545
+ let terminalCode;
546
+ let terminalKind = "failed";
547
+ let finalImages = [];
548
+ let finalUsage;
549
+ let accepted = false;
550
+ let providerTerminal = false;
551
+ let completionExposed = false;
552
+ const retentionAttempt = {
553
+ references: [],
554
+ rollbackFailures: 0
555
+ };
556
+ const releaseOnce = async () => {
557
+ if (!lease || releaseStarted) return;
558
+ releaseStarted = true;
559
+ await lease.release();
560
+ };
561
+ const cancelOnce = (reason = "request_cancelled") => {
562
+ if (!job || cancelled5) return cancelPromise ?? Promise.resolve();
563
+ cancelled5 = true;
564
+ cancelPromise = Promise.resolve(job.cancel(reason)).catch(() => void 0);
565
+ return cancelPromise;
566
+ };
567
+ const cancelStartedNonterminalOnce = (reason = "request_cancelled") => {
568
+ if (providerTerminal) return cancelPromise ?? Promise.resolve();
569
+ return cancelOnce(reason);
570
+ };
571
+ const sendTelemetry = async () => {
572
+ if (telemetrySent) return;
573
+ telemetrySent = true;
574
+ const editInputs = request2.action === "edit" ? request2.images : [];
575
+ const jobObservability = safeJobObservability(job);
576
+ await emitImageTelemetry(this.#telemetrySink, {
577
+ requestId: context.requestId,
578
+ providerId: options.providerId,
579
+ model: request2.model,
580
+ action: request2.action,
581
+ quality: request2.quality,
582
+ background: request2.background,
583
+ outputFormat: request2.outputFormat,
584
+ streaming: request2.stream,
585
+ inputCount: editInputs.length,
586
+ inputBytes: editInputs.reduce((sum, asset) => sum + asset.byteLength, 0),
587
+ requestedOutputCount: request2.n,
588
+ outputs: finalImages.map((asset) => ({
589
+ mimeType: asset.mimeType,
590
+ byteLength: asset.byteLength,
591
+ width: asset.width,
592
+ height: asset.height,
593
+ hasAlpha: asset.hasAlpha
594
+ })),
595
+ startedAt,
596
+ acceptedAt,
597
+ firstPartialAt,
598
+ ...jobObservability?.generationStartedAt !== void 0 ? { generationStartedAt: jobObservability.generationStartedAt } : {},
599
+ finishedAt: this.#now(),
600
+ terminal: terminalKind,
601
+ errorCode: terminalCode,
602
+ usage: finalUsage,
603
+ usageUnavailable: finalUsage === void 0,
604
+ ...jobObservability?.queueWaitMs !== void 0 ? { queueWaitMs: jobObservability.queueWaitMs } : {},
605
+ ...jobObservability?.retryCount !== void 0 ? { retryCount: jobObservability.retryCount } : {},
606
+ ...jobObservability?.authRefreshCount !== void 0 ? { authRefreshCount: jobObservability.authRefreshCount } : {},
607
+ ...options.retention?.enabled ? { referenceSaveCount: retentionAttempt.references.length } : {},
608
+ ...retentionAttempt.rollbackFailures > 0 ? { retentionRollbackFailures: retentionAttempt.rollbackFailures } : {}
609
+ });
610
+ };
611
+ const rollbackRetainedOnce = () => this.#rollbackRetained(
612
+ context.tenantId,
613
+ retentionAttempt
614
+ );
615
+ const onAbort = () => {
616
+ void cancelStartedNonterminalOnce();
617
+ };
618
+ context.signal.addEventListener("abort", onAbort, { once: true });
619
+ try {
620
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
621
+ lease = await this.#registry.require(options.providerId).acquire(context);
622
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
623
+ assertRequestSupported(request2, lease.capabilities);
624
+ if (options.requireResponsesTool === true && lease.capabilities.responsesTool !== true) {
625
+ unsupported("tools");
626
+ }
627
+ job = await lease.start(request2);
628
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
629
+ iterator = job.events[Symbol.asyncIterator]();
630
+ let pendingTerminal;
631
+ const partialIndexes = /* @__PURE__ */ new Map();
632
+ const partialCounts = /* @__PURE__ */ new Map();
633
+ while (true) {
634
+ let next;
635
+ try {
636
+ const read = iterator.next();
637
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
638
+ next = await Promise.race([
639
+ read,
640
+ new Promise((_resolve, reject) => {
641
+ const abort = () => reject(cancellationError(context.signal.reason));
642
+ context.signal.addEventListener("abort", abort, { once: true });
643
+ void read.then(
644
+ () => context.signal.removeEventListener("abort", abort),
645
+ () => context.signal.removeEventListener("abort", abort)
646
+ );
647
+ })
648
+ ]);
649
+ } catch (error) {
650
+ if (context.signal.aborted) throw cancellationError(context.signal.reason);
651
+ throw normalizeImageGenerationError(error, "image_generation_failed", {
652
+ retrySafety: accepted ? "after_acceptance" : "unknown"
653
+ });
654
+ }
655
+ if (next.done) {
656
+ if (!pendingTerminal) throw new ImageGenerationError("upstream_protocol_changed");
657
+ if (pendingTerminal.type === "completed") {
658
+ const { usage: providerUsage, ...terminalWithoutUsage } = pendingTerminal;
659
+ const safeUsage = sanitizeUsage(providerUsage);
660
+ const references = await this.#retainCompleted(
661
+ pendingTerminal,
662
+ context,
663
+ options.retention,
664
+ retentionAttempt
665
+ );
666
+ const completed = {
667
+ ...terminalWithoutUsage,
668
+ ...safeUsage ? { usage: safeUsage } : {},
669
+ ...references.length ? { references } : {}
670
+ };
671
+ finalImages = completed.images.map((image) => image.artifact);
672
+ finalUsage = completed.usage;
673
+ terminalKind = "completed";
674
+ if (context.signal.aborted) {
675
+ await rollbackRetainedOnce();
676
+ throw cancellationError(context.signal.reason);
677
+ }
678
+ completionExposed = true;
679
+ yield completed;
680
+ } else {
681
+ const safeError = imageGenerationErrorFromPublic(pendingTerminal.error);
682
+ terminalCode = safeError.code;
683
+ terminalKind = safeError.code === "request_cancelled" ? "cancelled" : "failed";
684
+ await sendTelemetry();
685
+ yield { type: "failed", error: serializeImageGenerationError(safeError) };
686
+ }
687
+ return;
688
+ }
689
+ const event = next.value;
690
+ if (pendingTerminal) {
691
+ throw new ImageGenerationError("upstream_protocol_changed", {
692
+ retrySafety: accepted ? "after_acceptance" : "unknown"
693
+ });
694
+ }
695
+ switch (event.type) {
696
+ case "accepted":
697
+ if (accepted || !Number.isFinite(event.acceptedAt)) {
698
+ throw new ImageGenerationError("upstream_protocol_changed");
699
+ }
700
+ accepted = true;
701
+ acceptedAt = event.acceptedAt;
702
+ yield { type: "accepted", acceptedAt: event.acceptedAt };
703
+ break;
704
+ case "partial_image": {
705
+ if (!accepted) throw new ImageGenerationError("upstream_protocol_changed");
706
+ firstPartialAt ??= this.#now();
707
+ const image = sanitizeProviderOutput(event.image);
708
+ if (!Number.isInteger(event.outputIndex) || event.outputIndex < 0 || event.outputIndex >= request2.n || !Number.isInteger(event.partialImageIndex) || event.partialImageIndex < 0) {
709
+ throw new ImageGenerationError("upstream_protocol_changed");
710
+ }
711
+ const previous = partialIndexes.get(event.outputIndex);
712
+ if (previous != null && event.partialImageIndex <= previous) {
713
+ throw new ImageGenerationError("upstream_protocol_changed");
714
+ }
715
+ const nextCount = (partialCounts.get(event.outputIndex) ?? 0) + 1;
716
+ if (nextCount > request2.partialImages || event.partialImageIndex >= request2.partialImages) {
717
+ throw new ImageGenerationError("upstream_protocol_changed");
718
+ }
719
+ partialIndexes.set(event.outputIndex, event.partialImageIndex);
720
+ partialCounts.set(event.outputIndex, nextCount);
721
+ yield {
722
+ type: "partial_image",
723
+ outputIndex: event.outputIndex,
724
+ partialImageIndex: event.partialImageIndex,
725
+ image
726
+ };
727
+ break;
728
+ }
729
+ case "completed": {
730
+ if (!accepted || !Array.isArray(event.images) || event.images.length !== request2.n) {
731
+ throw new ImageGenerationError("upstream_protocol_changed", {
732
+ retrySafety: accepted ? "after_acceptance" : "unknown"
733
+ });
734
+ }
735
+ const images = event.images.map((image) => {
736
+ const output = sanitizeProviderOutput(image);
737
+ assertFinalOutputMatchesRequest(output, request2);
738
+ return output;
739
+ });
740
+ pendingTerminal = {
741
+ type: "completed",
742
+ images,
743
+ ...event.usage !== void 0 ? { usage: event.usage } : {}
744
+ };
745
+ providerTerminal = true;
746
+ break;
747
+ }
748
+ case "failed":
749
+ pendingTerminal = event;
750
+ providerTerminal = true;
751
+ break;
752
+ default:
753
+ throw new ImageGenerationError("upstream_protocol_changed");
754
+ }
755
+ }
756
+ } catch (error) {
757
+ if (completionExposed) throw error;
758
+ const normalized = context.signal.aborted ? cancellationError(context.signal.reason) : normalizeImageGenerationError(error, "image_generation_failed", {
759
+ retrySafety: acceptedAt == null ? "unknown" : "after_acceptance"
760
+ });
761
+ if (normalized.code === "request_cancelled") await rollbackRetainedOnce();
762
+ terminalCode = normalized.code;
763
+ terminalKind = normalized.code === "request_cancelled" ? "cancelled" : "failed";
764
+ await sendTelemetry();
765
+ throw normalized;
766
+ } finally {
767
+ if (completionExposed) await sendTelemetry();
768
+ context.signal.removeEventListener("abort", onAbort);
769
+ await cancelStartedNonterminalOnce();
770
+ try {
771
+ await iterator?.return?.();
772
+ } catch {
773
+ }
774
+ await cancelPromise;
775
+ await releaseOnce();
776
+ }
777
+ }
778
+ async #retainCompleted(event, context, retention, attempt) {
779
+ if (!retention?.enabled) return [];
780
+ if (!this.#referenceStore) {
781
+ throw new ImageGenerationError("image_generation_failed", { retrySafety: "after_acceptance" });
782
+ }
783
+ if (!Number.isSafeInteger(retention.ttlMs) || retention.ttlMs <= 0) {
784
+ throw new ImageGenerationError("invalid_image_request", { param: "reference_ttl" });
785
+ }
786
+ const references = [];
787
+ for (const image of event.images) {
788
+ const asset = image.artifact;
789
+ const reference = await this.#referenceStore.save({
790
+ tenantId: context.tenantId,
791
+ ttlMs: retention.ttlMs,
792
+ artifact: asset,
793
+ providerReference: image.providerReference,
794
+ metadata: {
795
+ mimeType: asset.mimeType,
796
+ byteLength: asset.byteLength,
797
+ width: asset.width,
798
+ height: asset.height
799
+ }
800
+ });
801
+ references.push(reference);
802
+ attempt.references.push(reference);
803
+ if (context.signal.aborted) {
804
+ await this.#rollbackRetained(context.tenantId, attempt);
805
+ throw cancellationError(context.signal.reason);
806
+ }
807
+ }
808
+ return references;
809
+ }
810
+ async #rollbackRetained(tenantId, attempt) {
811
+ attempt.rollbackPromise ??= (async () => {
812
+ const store = this.#referenceStore;
813
+ if (!store) return;
814
+ for (const reference of attempt.references) {
815
+ try {
816
+ if (!await store.delete(tenantId, reference.referenceId)) {
817
+ attempt.rollbackFailures += 1;
818
+ }
819
+ } catch {
820
+ attempt.rollbackFailures += 1;
821
+ }
822
+ }
823
+ })();
824
+ await attempt.rollbackPromise;
825
+ }
826
+ };
827
+
828
+ // src/image-generation/ImageProviderRegistry.ts
829
+ var ImageProviderRegistry = class {
830
+ #providers = /* @__PURE__ */ new Map();
831
+ constructor(providers = []) {
832
+ for (const provider of providers) this.register(provider);
833
+ }
834
+ register(provider) {
835
+ const id = provider.id.trim();
836
+ if (!id) throw new TypeError("Image provider ID must not be empty.");
837
+ if (this.#providers.has(id)) throw new Error(`Duplicate image provider ID: ${id}`);
838
+ this.#providers.set(id, provider);
839
+ }
840
+ resolve(id) {
841
+ return this.#providers.get(id);
842
+ }
843
+ require(id) {
844
+ const provider = this.resolve(id);
845
+ if (!provider) throw new Error(`Image provider is not registered: ${id}`);
846
+ return provider;
847
+ }
848
+ list() {
849
+ return [...this.#providers.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([, provider]) => provider);
850
+ }
851
+ };
852
+
853
+ // src/image-generation/openai-images/TemporaryImageAsset.ts
854
+ import { createReadStream } from "fs";
855
+ import {
856
+ mkdir,
857
+ mkdtemp,
858
+ open,
859
+ chmod,
860
+ realpath,
861
+ rm,
862
+ stat,
863
+ unlink
864
+ } from "fs/promises";
865
+ import { tmpdir } from "os";
866
+ import { basename, dirname, join, resolve } from "path";
867
+ import { Readable } from "stream";
868
+ import { randomUUID as randomUUID2 } from "crypto";
869
+ var IMAGE_REQUEST_DIRECTORY_MARKER_NAME = ".omnicross-images-owner.json";
870
+ var IMAGE_REQUEST_DIRECTORY_MARKER_CONTENT = '{"schema":"omnicross-images-temporary","version":1}\n';
871
+ function cancelled(signal) {
872
+ throw new ImageGenerationError("request_cancelled", { cause: signal.reason });
873
+ }
874
+ function tooLarge() {
875
+ throw new ImageGenerationError("image_too_large");
876
+ }
877
+ function assertPositiveLimit(value) {
878
+ if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError("Temporary file limits must be positive safe integers.");
879
+ }
880
+ var StagedTemporaryFile = class {
881
+ byteLength;
882
+ #path;
883
+ #scope;
884
+ #kind;
885
+ #disposed = false;
886
+ constructor(scope, path, byteLength2, kind) {
887
+ this.#scope = scope;
888
+ this.#path = path;
889
+ this.byteLength = byteLength2;
890
+ this.#kind = kind;
891
+ }
892
+ async open(signal) {
893
+ if (this.#disposed) throw new ImageGenerationError("request_cancelled");
894
+ this.#scope.assertActive();
895
+ if (signal?.aborted) cancelled(signal);
896
+ const source = createReadStream(this.#path, {
897
+ flags: "r",
898
+ mode: 384,
899
+ start: 0,
900
+ end: this.byteLength - 1,
901
+ autoClose: true,
902
+ ...signal ? { signal } : {}
903
+ });
904
+ return Readable.toWeb(source);
905
+ }
906
+ /** Internal validator seam; no path is returned or retained by the caller. */
907
+ async inspect(callback) {
908
+ if (this.#disposed) throw new ImageGenerationError("request_cancelled");
909
+ this.#scope.assertActive();
910
+ return callback(this.#path);
911
+ }
912
+ async dispose() {
913
+ if (this.#disposed || this.#kind !== "spool") return;
914
+ this.#disposed = true;
915
+ await this.#scope.releaseSpool(this.#path, this.byteLength);
916
+ }
917
+ toImageAsset(metadata) {
918
+ return new TemporaryImageAsset(this, metadata);
919
+ }
920
+ };
921
+ var TemporaryImageAsset = class {
922
+ artifactId;
923
+ mimeType;
924
+ byteLength;
925
+ width;
926
+ height;
927
+ hasAlpha;
928
+ sha256;
929
+ independentlyDecodable = true;
930
+ #file;
931
+ constructor(file, metadata) {
932
+ if (file.byteLength <= 0) throw new TypeError("Temporary image assets must not be empty.");
933
+ this.#file = file;
934
+ this.artifactId = randomUUID2();
935
+ this.mimeType = metadata.mimeType;
936
+ this.byteLength = file.byteLength;
937
+ this.width = metadata.width;
938
+ this.height = metadata.height;
939
+ this.hasAlpha = metadata.hasAlpha;
940
+ this.sha256 = metadata.sha256;
941
+ }
942
+ open(options = {}) {
943
+ return this.#file.open(options.signal);
944
+ }
945
+ };
946
+ var RequestFileWriter = class {
947
+ #scope;
948
+ #handle;
949
+ #path;
950
+ #kind;
951
+ #maxBytes;
952
+ #declaredBytes;
953
+ #byteLength = 0;
954
+ #done = false;
955
+ constructor(options) {
956
+ this.#scope = options.scope;
957
+ this.#handle = options.handle;
958
+ this.#path = options.path;
959
+ this.#kind = options.kind;
960
+ this.#maxBytes = options.maxBytes;
961
+ this.#declaredBytes = options.declaredBytes;
962
+ }
963
+ get byteLength() {
964
+ return this.#byteLength;
965
+ }
966
+ async write(chunk, signal) {
967
+ if (this.#done) throw new TypeError("Temporary writer is already closed.");
968
+ if (signal?.aborted) cancelled(signal);
969
+ if (!(chunk instanceof Uint8Array) || chunk.byteLength === 0) return;
970
+ const next = this.#byteLength + chunk.byteLength;
971
+ if (!Number.isSafeInteger(next) || next > this.#maxBytes || this.#declaredBytes !== void 0 && next > this.#declaredBytes) {
972
+ tooLarge();
973
+ }
974
+ this.#scope.reserve(this.#kind, chunk.byteLength);
975
+ try {
976
+ let offset = 0;
977
+ while (offset < chunk.byteLength) {
978
+ if (signal?.aborted) cancelled(signal);
979
+ const result = await this.#handle.write(chunk, offset, chunk.byteLength - offset, null);
980
+ if (result.bytesWritten <= 0) throw new ImageGenerationError("image_generation_failed");
981
+ offset += result.bytesWritten;
982
+ }
983
+ this.#byteLength = next;
984
+ } catch (error) {
985
+ this.#scope.unreserve(this.#kind, chunk.byteLength);
986
+ throw error;
987
+ }
988
+ }
989
+ async finish() {
990
+ if (this.#done) throw new TypeError("Temporary writer is already closed.");
991
+ this.#done = true;
992
+ this.#scope.writerFinished(this);
993
+ try {
994
+ await this.#handle.sync();
995
+ } finally {
996
+ await this.#handle.close().catch(() => void 0);
997
+ }
998
+ if (this.#byteLength === 0 || this.#declaredBytes !== void 0 && this.#byteLength !== this.#declaredBytes) {
999
+ throw new ImageGenerationError("invalid_image_request");
1000
+ }
1001
+ const info = await stat(this.#path);
1002
+ if (!info.isFile() || info.size !== this.#byteLength) throw new ImageGenerationError("image_generation_failed");
1003
+ return new StagedTemporaryFile(this.#scope, this.#path, this.#byteLength, this.#kind);
1004
+ }
1005
+ async abort() {
1006
+ if (this.#done) return;
1007
+ this.#done = true;
1008
+ this.#scope.writerFinished(this);
1009
+ await this.#handle.close().catch(() => void 0);
1010
+ }
1011
+ };
1012
+ var ImageRequestResourceScope = class _ImageRequestResourceScope {
1013
+ #limits;
1014
+ #signal;
1015
+ #safeRoot;
1016
+ #requestDirectory;
1017
+ #budgetLease;
1018
+ #releaseDirectoryLease;
1019
+ #writers = /* @__PURE__ */ new Set();
1020
+ #leases = /* @__PURE__ */ new Set();
1021
+ #inputBytes = 0;
1022
+ #spoolBytes = 0;
1023
+ #cleaned = false;
1024
+ #cleanupPromise;
1025
+ constructor(options) {
1026
+ this.#limits = options.limits;
1027
+ this.#signal = options.signal;
1028
+ this.#safeRoot = options.safeRoot;
1029
+ this.#requestDirectory = options.requestDirectory;
1030
+ this.#budgetLease = options.budgetLease;
1031
+ this.#releaseDirectoryLease = options.releaseDirectoryLease;
1032
+ }
1033
+ static async create(options) {
1034
+ if (options.signal.aborted) cancelled(options.signal);
1035
+ const tenantId = options.tenantId?.trim();
1036
+ if (options.sharedBudget && !tenantId) {
1037
+ throw new TypeError("A shared image temporary budget requires a trusted tenant id.");
1038
+ }
1039
+ const budgetLease = options.sharedBudget?.acquireScope(tenantId);
1040
+ let createdDirectory;
1041
+ let requestDirectory;
1042
+ try {
1043
+ const requestedRoot = resolve(options.tempRoot ?? tmpdir());
1044
+ await mkdir(requestedRoot, { recursive: true, mode: 448 });
1045
+ const safeRoot = await realpath(requestedRoot);
1046
+ createdDirectory = await mkdtemp(join(safeRoot, "omnicross-images-"));
1047
+ await chmod(createdDirectory, 448);
1048
+ requestDirectory = await realpath(createdDirectory);
1049
+ if (dirname(requestDirectory) !== safeRoot || !basename(requestDirectory).startsWith("omnicross-images-")) {
1050
+ throw new TypeError("Temporary image request directory escaped the injected safe root.");
1051
+ }
1052
+ if (options.ownedDirectoryMarker) {
1053
+ const marker = await open(
1054
+ join(requestDirectory, IMAGE_REQUEST_DIRECTORY_MARKER_NAME),
1055
+ "wx",
1056
+ 384
1057
+ );
1058
+ try {
1059
+ await marker.writeFile(IMAGE_REQUEST_DIRECTORY_MARKER_CONTENT, { encoding: "utf8" });
1060
+ await marker.sync();
1061
+ } finally {
1062
+ await marker.close().catch(() => void 0);
1063
+ }
1064
+ }
1065
+ const releaseDirectoryLease = options.onDirectoryActive?.(requestDirectory);
1066
+ return new _ImageRequestResourceScope({
1067
+ ...options,
1068
+ safeRoot,
1069
+ requestDirectory,
1070
+ ...budgetLease ? { budgetLease } : {},
1071
+ ...releaseDirectoryLease ? { releaseDirectoryLease } : {}
1072
+ });
1073
+ } catch (error) {
1074
+ if (createdDirectory) {
1075
+ await rm(createdDirectory, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }).catch(() => void 0);
1076
+ }
1077
+ budgetLease?.releaseScope();
1078
+ throw error;
1079
+ }
1080
+ }
1081
+ assertActive() {
1082
+ if (this.#cleaned) throw new ImageGenerationError("request_cancelled");
1083
+ if (this.#signal.aborted) cancelled(this.#signal);
1084
+ }
1085
+ reserve(kind, bytes) {
1086
+ this.assertActive();
1087
+ const current = kind === "input" ? this.#inputBytes : this.#spoolBytes;
1088
+ const max = kind === "input" ? this.#limits.maxTotalInputBytes : this.#limits.maxSpoolBytes;
1089
+ if (!Number.isSafeInteger(current + bytes) || current + bytes > max) tooLarge();
1090
+ this.#budgetLease?.reserve(bytes);
1091
+ if (kind === "input") this.#inputBytes += bytes;
1092
+ else this.#spoolBytes += bytes;
1093
+ }
1094
+ unreserve(kind, bytes) {
1095
+ const current = kind === "input" ? this.#inputBytes : this.#spoolBytes;
1096
+ const released = Math.min(current, Math.max(0, bytes));
1097
+ if (kind === "input") this.#inputBytes -= released;
1098
+ else this.#spoolBytes -= released;
1099
+ if (released > 0) this.#budgetLease?.release(released);
1100
+ }
1101
+ writerFinished(writer) {
1102
+ this.#writers.delete(writer);
1103
+ }
1104
+ async releaseSpool(privatePath, bytes) {
1105
+ const target = resolve(privatePath);
1106
+ if (dirname(target) !== this.#requestDirectory || basename(target).length < 16) {
1107
+ throw new TypeError("Refusing to remove an unverified image spool.");
1108
+ }
1109
+ try {
1110
+ await unlink(target).catch((error) => {
1111
+ if (error.code !== "ENOENT") throw error;
1112
+ });
1113
+ } finally {
1114
+ this.unreserve("spool", bytes);
1115
+ }
1116
+ }
1117
+ async createWriter(options) {
1118
+ this.assertActive();
1119
+ assertPositiveLimit(options.maxBytes);
1120
+ if (options.declaredBytes !== void 0) {
1121
+ if (!Number.isSafeInteger(options.declaredBytes) || options.declaredBytes <= 0) tooLarge();
1122
+ if (options.declaredBytes > options.maxBytes) tooLarge();
1123
+ }
1124
+ const privatePath = join(this.#requestDirectory, randomUUID2());
1125
+ const handle = await open(privatePath, "wx", 384);
1126
+ const writer = new RequestFileWriter({
1127
+ scope: this,
1128
+ handle,
1129
+ path: privatePath,
1130
+ ...options
1131
+ });
1132
+ this.#writers.add(writer);
1133
+ return writer;
1134
+ }
1135
+ async materialize(source, options = {}) {
1136
+ const signal = options.signal ?? this.#signal;
1137
+ const writer = await this.createWriter({
1138
+ kind: options.kind ?? "input",
1139
+ maxBytes: options.maxBytes ?? this.#limits.maxFileBytes,
1140
+ ...options.declaredBytes !== void 0 ? { declaredBytes: options.declaredBytes } : {}
1141
+ });
1142
+ try {
1143
+ for await (const chunk of source) await writer.write(chunk, signal);
1144
+ return await writer.finish();
1145
+ } catch (error) {
1146
+ await writer.abort();
1147
+ throw error;
1148
+ }
1149
+ }
1150
+ addLease(lease) {
1151
+ this.assertActive();
1152
+ this.#leases.add(lease);
1153
+ }
1154
+ async cleanup() {
1155
+ if (this.#cleanupPromise) return this.#cleanupPromise;
1156
+ this.#cleaned = true;
1157
+ this.#cleanupPromise = (async () => {
1158
+ try {
1159
+ await Promise.allSettled([...this.#writers].map((writer) => writer.abort()));
1160
+ this.#writers.clear();
1161
+ await Promise.allSettled([...this.#leases].map((lease) => lease.release()));
1162
+ this.#leases.clear();
1163
+ const target = resolve(this.#requestDirectory);
1164
+ if (dirname(target) !== this.#safeRoot || !basename(target).startsWith("omnicross-images-")) {
1165
+ throw new TypeError("Refusing to clean an unverified temporary image directory.");
1166
+ }
1167
+ await rm(target, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
1168
+ } finally {
1169
+ this.unreserve("input", this.#inputBytes);
1170
+ this.unreserve("spool", this.#spoolBytes);
1171
+ this.#budgetLease?.releaseScope();
1172
+ this.#releaseDirectoryLease?.();
1173
+ }
1174
+ })();
1175
+ return this.#cleanupPromise;
1176
+ }
1177
+ };
1178
+ function createImageRequestResourceScope(limits, signal, tempRoot, ownership) {
1179
+ return ImageRequestResourceScope.create({
1180
+ limits,
1181
+ signal,
1182
+ ...tempRoot ? { tempRoot } : {},
1183
+ ...ownership ? ownership : {}
1184
+ });
1185
+ }
1186
+
1187
+ // src/image-generation/openai-images/imageApiErrors.ts
1188
+ import { randomUUID as randomUUID3 } from "crypto";
1189
+ var SAFE_STATUS = {
1190
+ invalid_image_request: 400,
1191
+ unsupported_model: 400,
1192
+ unsupported_capability: 422,
1193
+ invalid_api_key: 401,
1194
+ upstream_auth_required: 503,
1195
+ image_reference_not_found: 404,
1196
+ image_reference_expired: 410,
1197
+ image_too_large: 413,
1198
+ unsupported_image_type: 415,
1199
+ moderation_blocked: 422,
1200
+ upstream_rate_limited: 429,
1201
+ subscription_usage_limit_reached: 429,
1202
+ image_queue_full: 429,
1203
+ image_queue_timeout: 504,
1204
+ request_cancelled: 499,
1205
+ upstream_protocol_changed: 502,
1206
+ image_generation_failed: 502,
1207
+ image_generation_timeout: 504
1208
+ };
1209
+ function safeImageRequestId(value) {
1210
+ return typeof value === "string" && /^[A-Za-z0-9_.:-]{1,128}$/.test(value) ? value : randomUUID3();
1211
+ }
1212
+ function imageApiErrorFromUnknown(error) {
1213
+ return error instanceof ImageGenerationError ? error : normalizeImageGenerationError(error, "image_generation_failed");
1214
+ }
1215
+ function imageApiErrorFromProvider(value) {
1216
+ return imageGenerationErrorFromPublic(value);
1217
+ }
1218
+ function serializeImageApiError(error) {
1219
+ const domainError = imageApiErrorFromUnknown(error);
1220
+ const safe = serializeImageGenerationError(domainError);
1221
+ const headers = {};
1222
+ if (safe.retryAfterSeconds !== void 0) headers["Retry-After"] = String(safe.retryAfterSeconds);
1223
+ return {
1224
+ status: SAFE_STATUS[safe.code],
1225
+ headers,
1226
+ domainError,
1227
+ body: {
1228
+ error: {
1229
+ message: safe.message,
1230
+ type: safe.type,
1231
+ code: safe.code,
1232
+ ...safe.param !== void 0 ? { param: safe.param } : {},
1233
+ ...safe.retryAfterSeconds !== void 0 ? { retry_after_seconds: safe.retryAfterSeconds } : {},
1234
+ ...safe.moderationDetails !== void 0 ? {
1235
+ moderation_details: {
1236
+ ...safe.moderationDetails.stage ? { stage: safe.moderationDetails.stage } : {},
1237
+ ...safe.moderationDetails.categories?.length ? { categories: safe.moderationDetails.categories } : {}
1238
+ }
1239
+ } : {}
1240
+ }
1241
+ }
1242
+ };
1243
+ }
1244
+ function writeImageApiError(response, error, requestId) {
1245
+ const mapped = serializeImageApiError(error);
1246
+ if (!response.headersSent && !response.writableEnded && !response.destroyed) {
1247
+ response.writeHead(mapped.status, {
1248
+ "Content-Type": "application/json; charset=utf-8",
1249
+ "x-request-id": safeImageRequestId(requestId),
1250
+ ...mapped.headers
1251
+ });
1252
+ response.end(JSON.stringify(mapped.body));
1253
+ }
1254
+ return mapped.domainError;
1255
+ }
1256
+
1257
+ // src/image-generation/openai-images/imageApiResponse.ts
1258
+ function cancelled2(signal) {
1259
+ throw new ImageGenerationError("request_cancelled", { cause: signal.reason });
1260
+ }
1261
+ async function writeWithBackpressure(response, chunk, signal) {
1262
+ if (signal.aborted || response.destroyed || response.writableEnded) cancelled2(signal);
1263
+ if (response.write(chunk)) return;
1264
+ await new Promise((resolve2, reject) => {
1265
+ const cleanup = () => {
1266
+ response.removeListener("drain", onDrain);
1267
+ response.removeListener("close", onClose);
1268
+ response.removeListener("error", onError);
1269
+ signal.removeEventListener("abort", onAbort);
1270
+ };
1271
+ const onDrain = () => {
1272
+ cleanup();
1273
+ resolve2();
1274
+ };
1275
+ const onClose = () => {
1276
+ cleanup();
1277
+ reject(new ImageGenerationError("request_cancelled"));
1278
+ };
1279
+ const onError = () => {
1280
+ cleanup();
1281
+ reject(new ImageGenerationError("request_cancelled"));
1282
+ };
1283
+ const onAbort = () => {
1284
+ cleanup();
1285
+ reject(new ImageGenerationError("request_cancelled", { cause: signal.reason }));
1286
+ };
1287
+ response.once("drain", onDrain);
1288
+ response.once("close", onClose);
1289
+ response.once("error", onError);
1290
+ signal.addEventListener("abort", onAbort, { once: true });
1291
+ });
1292
+ }
1293
+ async function* encodeAssetBase64Chunks(asset, limit, signal) {
1294
+ if (asset.byteLength <= 0 || asset.byteLength > limit) throw new ImageGenerationError("image_too_large");
1295
+ const reader = (await asset.open({ signal })).getReader();
1296
+ let carry = Buffer.alloc(0);
1297
+ let observed = 0;
1298
+ try {
1299
+ while (true) {
1300
+ if (signal.aborted) cancelled2(signal);
1301
+ const next = await reader.read();
1302
+ if (next.done) break;
1303
+ observed += next.value.byteLength;
1304
+ if (observed > limit || observed > asset.byteLength) throw new ImageGenerationError("image_too_large");
1305
+ const source = carry.byteLength ? Buffer.concat([carry, Buffer.from(next.value)]) : Buffer.from(next.value);
1306
+ const complete = source.byteLength - source.byteLength % 3;
1307
+ if (complete) yield Buffer.from(source.subarray(0, complete).toString("base64"), "ascii");
1308
+ carry = Buffer.from(source.subarray(complete));
1309
+ }
1310
+ if (observed !== asset.byteLength) throw new ImageGenerationError("upstream_protocol_changed");
1311
+ if (carry.byteLength) yield Buffer.from(carry.toString("base64"), "ascii");
1312
+ } finally {
1313
+ await reader.cancel().catch(() => void 0);
1314
+ reader.releaseLock();
1315
+ }
1316
+ }
1317
+ function outputUsage(usage) {
1318
+ if (!usage) return void 0;
1319
+ return {
1320
+ ...usage.totalTokens !== void 0 ? { total_tokens: usage.totalTokens } : {},
1321
+ ...usage.inputTokens !== void 0 ? { input_tokens: usage.inputTokens } : {},
1322
+ ...usage.outputTokens !== void 0 ? { output_tokens: usage.outputTokens } : {},
1323
+ ...usage.generatedImages !== void 0 ? { generated_images: usage.generatedImages } : {},
1324
+ ...usage.inputTextTokens !== void 0 || usage.inputImageTokens !== void 0 ? {
1325
+ input_tokens_details: {
1326
+ ...usage.inputTextTokens !== void 0 ? { text_tokens: usage.inputTextTokens } : {},
1327
+ ...usage.inputImageTokens !== void 0 ? { image_tokens: usage.inputImageTokens } : {}
1328
+ }
1329
+ } : {},
1330
+ ...usage.outputImageTokens !== void 0 ? { output_tokens_details: { image_tokens: usage.outputImageTokens } } : {}
1331
+ };
1332
+ }
1333
+ async function stageOutputs(event, count, scope, limits, signal) {
1334
+ if (event.images.length !== count) throw new ImageGenerationError("upstream_protocol_changed");
1335
+ const total = event.images.reduce((sum, image) => sum + image.artifact.byteLength, 0);
1336
+ if (!Number.isSafeInteger(total) || total > limits.maxTotalOutputBytes) {
1337
+ throw new ImageGenerationError("image_too_large");
1338
+ }
1339
+ const outputs = [];
1340
+ for (const image of event.images) {
1341
+ const encodedLength = Math.ceil(image.artifact.byteLength / 3) * 4;
1342
+ const base64 = await scope.materialize(
1343
+ encodeAssetBase64Chunks(image.artifact, limits.maxOutputBytes, signal),
1344
+ {
1345
+ kind: "spool",
1346
+ maxBytes: encodedLength,
1347
+ declaredBytes: encodedLength,
1348
+ signal
1349
+ }
1350
+ );
1351
+ outputs.push({
1352
+ base64,
1353
+ ...image.revisedPrompt !== void 0 ? { revisedPrompt: image.revisedPrompt } : {}
1354
+ });
1355
+ }
1356
+ return outputs;
1357
+ }
1358
+ async function terminalEvent(events, signal) {
1359
+ let accepted = false;
1360
+ let completed;
1361
+ for await (const event of events) {
1362
+ if (signal.aborted) cancelled2(signal);
1363
+ if (event.type === "accepted") {
1364
+ if (accepted || completed) throw new ImageGenerationError("upstream_protocol_changed");
1365
+ accepted = true;
1366
+ } else if (event.type === "partial_image") {
1367
+ throw new ImageGenerationError("upstream_protocol_changed");
1368
+ } else if (event.type === "failed") {
1369
+ throw imageApiErrorFromProvider(event.error);
1370
+ } else {
1371
+ if (!accepted || completed) throw new ImageGenerationError("upstream_protocol_changed");
1372
+ completed = event;
1373
+ }
1374
+ }
1375
+ if (!completed) throw new ImageGenerationError("upstream_protocol_changed");
1376
+ return completed;
1377
+ }
1378
+ async function writeImageApiResponse(options) {
1379
+ const completed = await terminalEvent(options.events, options.signal);
1380
+ const outputs = await stageOutputs(
1381
+ completed,
1382
+ options.requestedCount,
1383
+ options.scope,
1384
+ options.limits,
1385
+ options.signal
1386
+ );
1387
+ const usage = outputUsage(completed.usage);
1388
+ options.response.writeHead(200, {
1389
+ "Content-Type": "application/json; charset=utf-8",
1390
+ "x-request-id": safeImageRequestId(options.requestId)
1391
+ });
1392
+ await writeWithBackpressure(
1393
+ options.response,
1394
+ `{"created":${Math.floor(options.createdAt / 1e3)},"data":[`,
1395
+ options.signal
1396
+ );
1397
+ for (let index = 0; index < outputs.length; index += 1) {
1398
+ const output = outputs[index];
1399
+ await writeWithBackpressure(options.response, `${index ? "," : ""}{"b64_json":"`, options.signal);
1400
+ const reader = (await output.base64.open(options.signal)).getReader();
1401
+ try {
1402
+ while (true) {
1403
+ const next = await reader.read();
1404
+ if (next.done) break;
1405
+ await writeWithBackpressure(options.response, next.value, options.signal);
1406
+ }
1407
+ } finally {
1408
+ await reader.cancel().catch(() => void 0);
1409
+ reader.releaseLock();
1410
+ }
1411
+ await writeWithBackpressure(
1412
+ options.response,
1413
+ `"${output.revisedPrompt !== void 0 ? `,"revised_prompt":${JSON.stringify(output.revisedPrompt)}` : ""}}`,
1414
+ options.signal
1415
+ );
1416
+ }
1417
+ await writeWithBackpressure(
1418
+ options.response,
1419
+ `]${usage ? `,"usage":${JSON.stringify(usage)}` : ""}}`,
1420
+ options.signal
1421
+ );
1422
+ options.response.end();
1423
+ await Promise.all(outputs.map((output) => output.base64.dispose()));
1424
+ }
1425
+
1426
+ // src/image-generation/openai-images/imageApiSse.ts
1427
+ function cancelled3(signal) {
1428
+ throw new ImageGenerationError("request_cancelled", { cause: signal.reason });
1429
+ }
1430
+ async function stageAsset(asset, scope, limits, signal) {
1431
+ const encodedLength = Math.ceil(asset.byteLength / 3) * 4;
1432
+ return scope.materialize(encodeAssetBase64Chunks(asset, limits.maxOutputBytes, signal), {
1433
+ kind: "spool",
1434
+ maxBytes: encodedLength,
1435
+ declaredBytes: encodedLength,
1436
+ signal
1437
+ });
1438
+ }
1439
+ async function writeAssetFrame(options) {
1440
+ const prefix = JSON.stringify({ type: options.type, ...options.beforeImage, b64_json: "__IMAGE__" });
1441
+ const marker = '"__IMAGE__"';
1442
+ const markerAt = prefix.indexOf(marker);
1443
+ await writeWithBackpressure(
1444
+ options.response,
1445
+ `event: ${options.type}
1446
+ data: ${prefix.slice(0, markerAt)}"`,
1447
+ options.signal
1448
+ );
1449
+ const reader = (await options.spool.open(options.signal)).getReader();
1450
+ try {
1451
+ while (true) {
1452
+ const next = await reader.read();
1453
+ if (next.done) break;
1454
+ await writeWithBackpressure(options.response, next.value, options.signal);
1455
+ }
1456
+ } finally {
1457
+ await reader.cancel().catch(() => void 0);
1458
+ reader.releaseLock();
1459
+ }
1460
+ const suffixObject = options.afterImage ?? {};
1461
+ const suffix = JSON.stringify(suffixObject);
1462
+ await writeWithBackpressure(
1463
+ options.response,
1464
+ `"${prefix.slice(markerAt + marker.length, -1)}${suffix === "{}" ? "" : `,${suffix.slice(1, -1)}`}}
1465
+
1466
+ `,
1467
+ options.signal
1468
+ );
1469
+ }
1470
+ async function emitOutput(options) {
1471
+ const spool = await stageAsset(options.output.artifact, options.scope, options.limits, options.signal);
1472
+ try {
1473
+ await writeAssetFrame({
1474
+ response: options.response,
1475
+ spool,
1476
+ type: options.type,
1477
+ beforeImage: {
1478
+ output_index: options.outputIndex,
1479
+ ...options.partialImageIndex !== void 0 ? { partial_image_index: options.partialImageIndex } : {}
1480
+ },
1481
+ afterImage: {
1482
+ ...options.output.revisedPrompt !== void 0 ? { revised_prompt: options.output.revisedPrompt } : {},
1483
+ ...options.finalFields ?? {}
1484
+ },
1485
+ signal: options.signal
1486
+ });
1487
+ } finally {
1488
+ await spool.dispose();
1489
+ }
1490
+ }
1491
+ async function writeImageApiSse(options) {
1492
+ const prefix = options.action === "generation" ? "image_generation" : "image_edit";
1493
+ let accepted = false;
1494
+ let completed = false;
1495
+ try {
1496
+ for await (const event of options.events) {
1497
+ if (options.signal.aborted) cancelled3(options.signal);
1498
+ if (event.type === "accepted") {
1499
+ if (accepted) throw new ImageGenerationError("upstream_protocol_changed");
1500
+ accepted = true;
1501
+ options.response.writeHead(200, {
1502
+ "Content-Type": "text/event-stream; charset=utf-8",
1503
+ "Cache-Control": "no-cache, no-transform",
1504
+ Connection: "keep-alive",
1505
+ "x-request-id": safeImageRequestId(options.requestId)
1506
+ });
1507
+ options.response.flushHeaders();
1508
+ continue;
1509
+ }
1510
+ if (event.type === "failed") throw imageApiErrorFromProvider(event.error);
1511
+ if (!accepted) throw new ImageGenerationError("upstream_protocol_changed");
1512
+ if (event.type === "partial_image") {
1513
+ await emitOutput({
1514
+ response: options.response,
1515
+ output: event.image,
1516
+ type: `${prefix}.partial_image`,
1517
+ outputIndex: event.outputIndex,
1518
+ partialImageIndex: event.partialImageIndex,
1519
+ finalFields: {
1520
+ background: options.metadata.background,
1521
+ created_at: Math.floor(options.createdAt / 1e3),
1522
+ output_format: options.metadata.outputFormat,
1523
+ quality: options.metadata.quality,
1524
+ size: options.metadata.size
1525
+ },
1526
+ scope: options.scope,
1527
+ limits: options.limits,
1528
+ signal: options.signal
1529
+ });
1530
+ continue;
1531
+ }
1532
+ if (event.images.length !== options.requestedCount) {
1533
+ throw new ImageGenerationError("upstream_protocol_changed");
1534
+ }
1535
+ const total = event.images.reduce((sum, output) => sum + output.artifact.byteLength, 0);
1536
+ if (!Number.isSafeInteger(total) || total > options.limits.maxTotalOutputBytes) {
1537
+ throw new ImageGenerationError("image_too_large");
1538
+ }
1539
+ const usage = outputUsage(event.usage);
1540
+ for (let index = 0; index < event.images.length; index += 1) {
1541
+ await emitOutput({
1542
+ response: options.response,
1543
+ output: event.images[index],
1544
+ type: `${prefix}.completed`,
1545
+ outputIndex: index,
1546
+ finalFields: {
1547
+ background: options.metadata.background,
1548
+ created_at: Math.floor(options.createdAt / 1e3),
1549
+ output_format: options.metadata.outputFormat,
1550
+ quality: options.metadata.quality,
1551
+ size: options.metadata.size,
1552
+ ...index === event.images.length - 1 && usage ? { usage } : {}
1553
+ },
1554
+ scope: options.scope,
1555
+ limits: options.limits,
1556
+ signal: options.signal
1557
+ });
1558
+ }
1559
+ completed = true;
1560
+ }
1561
+ if (!completed) throw new ImageGenerationError("upstream_protocol_changed");
1562
+ if (!options.response.writableEnded) options.response.end();
1563
+ } catch (error) {
1564
+ const mapped = serializeImageApiError(error);
1565
+ if (!accepted) throw mapped.domainError;
1566
+ if (mapped.domainError.code !== "request_cancelled" && !options.signal.aborted && !options.response.destroyed && !options.response.writableEnded) {
1567
+ const body = JSON.stringify({ type: "error", error: mapped.body.error });
1568
+ await writeWithBackpressure(options.response, `event: error
1569
+ data: ${body}
1570
+
1571
+ `, options.signal).catch(() => void 0);
1572
+ options.response.end();
1573
+ }
1574
+ throw mapped.domainError;
1575
+ }
1576
+ }
1577
+
1578
+ // src/image-generation/openai-images/normalizeOptions.ts
1579
+ var OPTION_KEYS = /* @__PURE__ */ new Set([
1580
+ "prompt",
1581
+ "model",
1582
+ "n",
1583
+ "quality",
1584
+ "size",
1585
+ "background",
1586
+ "output_format",
1587
+ "output_compression",
1588
+ "moderation",
1589
+ "stream",
1590
+ "partial_images",
1591
+ "user"
1592
+ ]);
1593
+ function invalid(param) {
1594
+ throw new ImageGenerationError("invalid_image_request", { param });
1595
+ }
1596
+ function recordOf(value) {
1597
+ if (!value || typeof value !== "object" || Array.isArray(value)) invalid();
1598
+ return value;
1599
+ }
1600
+ function scalar(value, multipart) {
1601
+ if (!multipart || typeof value !== "string") return value;
1602
+ return value.trim();
1603
+ }
1604
+ function integer(value, fallback, param, min, max, multipart) {
1605
+ const item = scalar(value, multipart);
1606
+ const parsed = typeof item === "string" && /^\d+$/.test(item) ? Number(item) : item;
1607
+ if (value === void 0) return fallback;
1608
+ if (!Number.isSafeInteger(parsed) || parsed < min || parsed > max) invalid(param);
1609
+ return parsed;
1610
+ }
1611
+ function booleanValue(value, fallback, param, multipart) {
1612
+ if (value === void 0) return fallback;
1613
+ const item = scalar(value, multipart);
1614
+ if (item === true || item === "true") return true;
1615
+ if (item === false || item === "false") return false;
1616
+ return invalid(param);
1617
+ }
1618
+ function enumValue(value, fallback, allowed, param, multipart) {
1619
+ if (value === void 0) return fallback;
1620
+ const item = scalar(value, multipart);
1621
+ if (typeof item !== "string" || !allowed.includes(item)) invalid(param);
1622
+ return item;
1623
+ }
1624
+ function parseSize(value, multipart) {
1625
+ if (value === void 0 || scalar(value, multipart) === "auto") return { kind: "auto" };
1626
+ const item = scalar(value, multipart);
1627
+ if (typeof item !== "string") invalid("size");
1628
+ const match = /^(\d+)x(\d+)$/.exec(item);
1629
+ if (!match) invalid("size");
1630
+ const width = Number(match[1]);
1631
+ const height = Number(match[2]);
1632
+ const pixels = width * height;
1633
+ const long = Math.max(width, height);
1634
+ const short = Math.min(width, height);
1635
+ if (!Number.isSafeInteger(pixels) || width % 16 !== 0 || height % 16 !== 0 || width <= 0 || height <= 0 || long > 3840 || long / short > 3 || pixels < 655360 || pixels > 8294400) invalid("size");
1636
+ return { kind: "pixels", width, height };
1637
+ }
1638
+ function normalizeImageOptions(value, runtime, input) {
1639
+ const record2 = recordOf(value);
1640
+ const allowed = /* @__PURE__ */ new Set([...OPTION_KEYS, ...input.extraKeys ?? []]);
1641
+ for (const key of Object.keys(record2)) {
1642
+ if (!allowed.has(key)) invalid(key);
1643
+ }
1644
+ const multipart = input.multipart === true;
1645
+ const promptValue = scalar(record2.prompt, multipart);
1646
+ if (typeof promptValue !== "string" || !promptValue.trim() || promptValue.length > 32e3) {
1647
+ invalid("prompt");
1648
+ }
1649
+ const requestedModel = record2.model === void 0 ? runtime.defaultModel : scalar(record2.model, multipart);
1650
+ if (typeof requestedModel !== "string" || !requestedModel.trim() || requestedModel.length > 128) {
1651
+ invalid("model");
1652
+ }
1653
+ const model = runtime.modelAliases.get(requestedModel) ?? requestedModel;
1654
+ const n = integer(record2.n, 1, "n", 1, 10, multipart);
1655
+ const quality = enumValue(record2.quality, "auto", ["auto", "low", "medium", "high"], "quality", multipart);
1656
+ const size = parseSize(record2.size, multipart);
1657
+ const background = enumValue(record2.background, "auto", ["auto", "opaque", "transparent"], "background", multipart);
1658
+ const outputFormat = enumValue(record2.output_format, "png", ["png", "jpeg", "webp"], "output_format", multipart);
1659
+ const outputCompression = record2.output_compression === void 0 ? void 0 : integer(record2.output_compression, 0, "output_compression", 0, 100, multipart);
1660
+ const moderation = enumValue(record2.moderation, "auto", ["auto", "low"], "moderation", multipart);
1661
+ const stream = booleanValue(record2.stream, false, "stream", multipart);
1662
+ const partialImages = integer(record2.partial_images, 0, "partial_images", 0, 3, multipart);
1663
+ if (background === "transparent" && outputFormat === "jpeg") invalid("background");
1664
+ if (outputCompression !== void 0 && outputFormat === "png") invalid("output_compression");
1665
+ if (partialImages > 0 && !stream) invalid("partial_images");
1666
+ let userFingerprint;
1667
+ if (record2.user !== void 0) {
1668
+ const user = scalar(record2.user, multipart);
1669
+ if (typeof user !== "string" || !user || user.length > 256) invalid("user");
1670
+ if (runtime.fingerprintUser) {
1671
+ const fingerprint = runtime.fingerprintUser(user);
1672
+ if (typeof fingerprint !== "string" || !/^[A-Za-z0-9:_-]{1,128}$/.test(fingerprint)) {
1673
+ throw new TypeError("The injected image user fingerprint is not a safe identifier.");
1674
+ }
1675
+ userFingerprint = fingerprint;
1676
+ }
1677
+ }
1678
+ return {
1679
+ model,
1680
+ prompt: promptValue,
1681
+ n,
1682
+ quality,
1683
+ size,
1684
+ background,
1685
+ outputFormat,
1686
+ ...outputCompression !== void 0 ? { outputCompression } : {},
1687
+ moderation,
1688
+ stream,
1689
+ partialImages,
1690
+ ...userFingerprint !== void 0 ? { userFingerprint } : {}
1691
+ };
1692
+ }
1693
+ function normalizeGenerateRequest(value, runtime) {
1694
+ return { action: "generate", ...normalizeImageOptions(value, runtime, { action: "generate" }) };
1695
+ }
1696
+
1697
+ // src/image-generation/openai-images/readJsonBody.ts
1698
+ function invalid2(param) {
1699
+ throw new ImageGenerationError("invalid_image_request", { param });
1700
+ }
1701
+ function tooLarge2() {
1702
+ throw new ImageGenerationError("image_too_large");
1703
+ }
1704
+ function mediaType(value) {
1705
+ return (value ?? "").split(";", 1)[0].trim().toLowerCase();
1706
+ }
1707
+ function declaredLength(value, limit) {
1708
+ if (value === void 0) return void 0;
1709
+ if (Array.isArray(value) || !/^(0|[1-9]\d*)$/.test(value)) tooLarge2();
1710
+ const parsed = Number(value);
1711
+ if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > limit) tooLarge2();
1712
+ return parsed;
1713
+ }
1714
+ async function readJsonBody(request2, options) {
1715
+ if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes <= 0) {
1716
+ throw new TypeError("The JSON body limit must be a positive safe integer.");
1717
+ }
1718
+ if (options.signal.aborted) {
1719
+ throw new ImageGenerationError("request_cancelled", { cause: options.signal.reason });
1720
+ }
1721
+ if (mediaType(request2.headers["content-type"]) !== "application/json") invalid2("content_type");
1722
+ const encoding = request2.headers["content-encoding"];
1723
+ if (Array.isArray(encoding) || encoding !== void 0 && encoding.trim().toLowerCase() !== "identity") {
1724
+ invalid2("content_encoding");
1725
+ }
1726
+ const expected = declaredLength(request2.headers["content-length"], options.maxBytes);
1727
+ const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
1728
+ const pieces = [];
1729
+ let observed = 0;
1730
+ const abort = () => {
1731
+ request2.destroy(options.signal.reason instanceof Error ? options.signal.reason : void 0);
1732
+ };
1733
+ options.signal.addEventListener("abort", abort, { once: true });
1734
+ try {
1735
+ try {
1736
+ for await (const raw of request2) {
1737
+ if (options.signal.aborted) {
1738
+ throw new ImageGenerationError("request_cancelled", { cause: options.signal.reason });
1739
+ }
1740
+ const chunk = raw instanceof Uint8Array ? raw : Buffer.from(raw);
1741
+ observed += chunk.byteLength;
1742
+ if (!Number.isSafeInteger(observed) || observed > options.maxBytes || expected !== void 0 && observed > expected) {
1743
+ tooLarge2();
1744
+ }
1745
+ pieces.push(decoder.decode(chunk, { stream: true }));
1746
+ }
1747
+ pieces.push(decoder.decode());
1748
+ } catch (error) {
1749
+ if (options.signal.aborted) {
1750
+ throw new ImageGenerationError("request_cancelled", { cause: options.signal.reason });
1751
+ }
1752
+ if (error instanceof ImageGenerationError) throw error;
1753
+ invalid2();
1754
+ }
1755
+ if (observed === 0 || expected !== void 0 && observed !== expected) invalid2();
1756
+ try {
1757
+ return JSON.parse(pieces.join(""));
1758
+ } catch {
1759
+ invalid2();
1760
+ }
1761
+ } finally {
1762
+ options.signal.removeEventListener("abort", abort);
1763
+ }
1764
+ }
1765
+
1766
+ // src/image-generation/openai-images/types.ts
1767
+ var DEFAULT_IMAGE_API_LIMITS = Object.freeze({
1768
+ maxJsonBytes: 1024 * 1024,
1769
+ maxMultipartBytes: 64 * 1024 * 1024,
1770
+ maxFileBytes: 50 * 1024 * 1024,
1771
+ maxTotalInputBytes: 64 * 1024 * 1024,
1772
+ maxFiles: 17,
1773
+ maxFields: 32,
1774
+ maxParts: 49,
1775
+ maxHeaderPairs: 64,
1776
+ maxFieldNameBytes: 128,
1777
+ maxFieldValueBytes: 32 * 1024,
1778
+ maxPixels: 8294400,
1779
+ maxRawBytes: 8294400 * 4,
1780
+ maxOutputBytes: 50 * 1024 * 1024,
1781
+ maxTotalOutputBytes: 100 * 1024 * 1024,
1782
+ maxSpoolBytes: 140 * 1024 * 1024,
1783
+ maxRedirects: 5,
1784
+ maxRemoteUrlBytes: 8 * 1024,
1785
+ maxRemoteHeaderBytes: 32 * 1024,
1786
+ remoteConnectTimeoutMs: 1e4,
1787
+ remoteTotalTimeoutMs: 3e4
1788
+ });
1789
+ function assertFiniteImageApiLimits(limits) {
1790
+ for (const [name, value] of Object.entries(limits)) {
1791
+ if (!Number.isSafeInteger(value) || value <= 0) {
1792
+ throw new TypeError(`Image API limit '${name}' must be a positive safe integer.`);
1793
+ }
1794
+ }
1795
+ if (limits.maxFileBytes > 50 * 1024 * 1024) {
1796
+ throw new TypeError("Image API per-file limit exceeds the 50 MiB hard ceiling.");
1797
+ }
1798
+ if (limits.maxFiles > 17) {
1799
+ throw new TypeError("Image API file count exceeds sixteen images plus one mask.");
1800
+ }
1801
+ if (limits.maxPixels > 8294400) {
1802
+ throw new TypeError("Image API pixel limit exceeds the GPT Image hard ceiling.");
1803
+ }
1804
+ if (limits.maxTotalInputBytes < limits.maxFileBytes) {
1805
+ throw new TypeError("Image API aggregate input limit must cover one file.");
1806
+ }
1807
+ if (limits.maxTotalOutputBytes < limits.maxOutputBytes) {
1808
+ throw new TypeError("Image API aggregate output limit must cover one output.");
1809
+ }
1810
+ }
1811
+
1812
+ // src/image-generation/openai-images/generateHandler.ts
1813
+ function validateRuntime(runtime) {
1814
+ if (typeof runtime.tenantId !== "string" || !runtime.tenantId || runtime.tenantId.length > 256 || typeof runtime.providerId !== "string" || !runtime.providerId || runtime.providerId.length > 128 || typeof runtime.defaultModel !== "string" || !runtime.defaultModel || runtime.defaultModel.length > 128 || !runtime.modelAliases || typeof runtime.modelAliases.get !== "function") {
1815
+ throw new ImageGenerationError("invalid_api_key");
1816
+ }
1817
+ for (const hint of [runtime.preferredAccountId, runtime.preferredAccountGroup]) {
1818
+ if (hint !== void 0 && (typeof hint !== "string" || !hint.trim() || hint.length > 256)) {
1819
+ throw new ImageGenerationError("invalid_api_key");
1820
+ }
1821
+ }
1822
+ if (runtime.boundAccountFallbackPolicy !== void 0 && runtime.boundAccountFallbackPolicy !== "strict" && runtime.boundAccountFallbackPolicy !== "pool") {
1823
+ throw new ImageGenerationError("invalid_api_key");
1824
+ }
1825
+ assertFiniteImageApiLimits(runtime.limits);
1826
+ }
1827
+ async function bestEffortAudit(deps, record2) {
1828
+ try {
1829
+ await deps.audit?.(record2);
1830
+ } catch {
1831
+ }
1832
+ }
1833
+ function createImageGenerateHandler(deps) {
1834
+ return async (context) => {
1835
+ const requestId = safeImageRequestId(deps.createRequestId?.());
1836
+ const createdAt = (deps.now ?? Date.now)();
1837
+ let runtime;
1838
+ let request2;
1839
+ let scope;
1840
+ let cleanupOutcome;
1841
+ let terminal = "failed";
1842
+ let terminalCode;
1843
+ try {
1844
+ runtime = await deps.resolveRuntime(context);
1845
+ validateRuntime(runtime);
1846
+ scope = deps.createResourceScope ? await deps.createResourceScope(runtime.limits, context.signal, runtime.tenantId) : await createImageRequestResourceScope(runtime.limits, context.signal);
1847
+ const body = await readJsonBody(context.request, {
1848
+ maxBytes: runtime.limits.maxJsonBytes,
1849
+ signal: context.signal
1850
+ });
1851
+ request2 = normalizeGenerateRequest(body, runtime);
1852
+ const events = deps.orchestrator.run(
1853
+ request2,
1854
+ {
1855
+ requestId,
1856
+ tenantId: runtime.tenantId,
1857
+ signal: context.signal,
1858
+ ...context.route.sessionId ? { sessionKey: context.route.sessionId } : {},
1859
+ ...runtime.preferredAccountId ? { preferredAccountId: runtime.preferredAccountId } : {},
1860
+ ...runtime.preferredAccountGroup ? { preferredAccountGroup: runtime.preferredAccountGroup } : {},
1861
+ ...runtime.boundAccountFallbackPolicy ? { boundAccountFallbackPolicy: runtime.boundAccountFallbackPolicy } : {}
1862
+ },
1863
+ { providerId: runtime.providerId, ...runtime.retention ? { retention: runtime.retention } : {} }
1864
+ );
1865
+ if (request2.stream) {
1866
+ await writeImageApiSse({
1867
+ action: "generation",
1868
+ response: context.response,
1869
+ events,
1870
+ requestedCount: request2.n,
1871
+ requestId,
1872
+ createdAt,
1873
+ metadata: {
1874
+ background: request2.background,
1875
+ outputFormat: request2.outputFormat,
1876
+ quality: request2.quality,
1877
+ size: request2.size.kind === "auto" ? "auto" : `${request2.size.width}x${request2.size.height}`
1878
+ },
1879
+ scope,
1880
+ limits: runtime.limits,
1881
+ signal: context.signal
1882
+ });
1883
+ } else {
1884
+ await writeImageApiResponse({
1885
+ response: context.response,
1886
+ events,
1887
+ requestedCount: request2.n,
1888
+ requestId,
1889
+ createdAt,
1890
+ scope,
1891
+ limits: runtime.limits,
1892
+ signal: context.signal
1893
+ });
1894
+ }
1895
+ terminal = "completed";
1896
+ } catch (error) {
1897
+ const domainError = imageApiErrorFromUnknown(error);
1898
+ terminal = domainError.code === "request_cancelled" ? "cancelled" : "failed";
1899
+ terminalCode = domainError.code;
1900
+ writeImageApiError(context.response, domainError, requestId);
1901
+ } finally {
1902
+ if (scope) {
1903
+ try {
1904
+ await scope.cleanup();
1905
+ cleanupOutcome = "completed";
1906
+ } catch {
1907
+ cleanupOutcome = "failed";
1908
+ }
1909
+ }
1910
+ await bestEffortAudit(deps, {
1911
+ requestId,
1912
+ operationId: "images.generate",
1913
+ ...runtime ? { providerId: runtime.providerId } : {},
1914
+ ...request2 ? {
1915
+ model: request2.model,
1916
+ options: {
1917
+ n: request2.n,
1918
+ quality: request2.quality,
1919
+ background: request2.background,
1920
+ outputFormat: request2.outputFormat,
1921
+ stream: request2.stream,
1922
+ partialImages: request2.partialImages
1923
+ }
1924
+ } : {},
1925
+ terminal,
1926
+ ...cleanupOutcome ? { cleanupOutcome } : {},
1927
+ ...terminalCode ? { errorCode: terminalCode } : {}
1928
+ });
1929
+ }
1930
+ };
1931
+ }
1932
+
1933
+ // src/image-generation/openai-images/readMultipartEdit.ts
1934
+ import Busboy from "busboy";
1935
+
1936
+ // src/image-generation/openai-images/validateRaster.ts
1937
+ import { createHash } from "crypto";
1938
+ import { open as open2, readFile } from "fs/promises";
1939
+ import sharp from "sharp";
1940
+ function unsupported2() {
1941
+ throw new ImageGenerationError("unsupported_image_type");
1942
+ }
1943
+ function tooLarge3() {
1944
+ throw new ImageGenerationError("image_too_large");
1945
+ }
1946
+ function hasPrefix(bytes, signature) {
1947
+ return signature.every((value, index) => bytes[index] === value);
1948
+ }
1949
+ function signatureFormat(header) {
1950
+ if (hasPrefix(header, [137, 80, 78, 71, 13, 10, 26, 10])) return "png";
1951
+ if (hasPrefix(header, [255, 216, 255])) return "jpeg";
1952
+ if (hasPrefix(header, [82, 73, 70, 70]) && header[8] === 87 && header[9] === 69 && header[10] === 66 && header[11] === 80) return "webp";
1953
+ unsupported2();
1954
+ }
1955
+ async function inspectContainer(privatePath, byteLength2) {
1956
+ if (byteLength2 < 12) unsupported2();
1957
+ const handle = await open2(privatePath, "r");
1958
+ try {
1959
+ const header = Buffer.allocUnsafe(12);
1960
+ const headerRead = await handle.read(header, 0, header.byteLength, 0);
1961
+ if (headerRead.bytesRead !== header.byteLength) unsupported2();
1962
+ const format = signatureFormat(header);
1963
+ if (format === "png") {
1964
+ if (byteLength2 < 20) unsupported2();
1965
+ const trailer = Buffer.allocUnsafe(12);
1966
+ const result = await handle.read(trailer, 0, trailer.byteLength, byteLength2 - trailer.byteLength);
1967
+ if (result.bytesRead !== trailer.byteLength || trailer.readUInt32BE(0) !== 0 || trailer.toString("ascii", 4, 8) !== "IEND" || !hasPrefix(trailer.subarray(8), [174, 66, 96, 130])) unsupported2();
1968
+ } else if (format === "jpeg") {
1969
+ const trailer = Buffer.allocUnsafe(2);
1970
+ const result = await handle.read(trailer, 0, 2, byteLength2 - 2);
1971
+ if (result.bytesRead !== 2 || trailer[0] !== 255 || trailer[1] !== 217) unsupported2();
1972
+ } else {
1973
+ const declared = header.readUInt32LE(4) + 8;
1974
+ if (declared !== byteLength2) unsupported2();
1975
+ }
1976
+ return format;
1977
+ } finally {
1978
+ await handle.close();
1979
+ }
1980
+ }
1981
+ function mapSharpFailure(error) {
1982
+ const message = error instanceof Error ? error.message.toLowerCase() : "";
1983
+ if (message.includes("pixel limit") || message.includes("memory") || message.includes("too large")) tooLarge3();
1984
+ unsupported2();
1985
+ }
1986
+ async function validateRaster(file, limits) {
1987
+ if (file.byteLength > limits.maxFileBytes) tooLarge3();
1988
+ const metadata = await file.inspect(async (privatePath) => {
1989
+ const format = await inspectContainer(privatePath, file.byteLength);
1990
+ const encoded = await readFile(privatePath);
1991
+ if (encoded.byteLength !== file.byteLength) unsupported2();
1992
+ try {
1993
+ const image = sharp(encoded, {
1994
+ failOn: "warning",
1995
+ sequentialRead: true,
1996
+ limitInputPixels: limits.maxPixels,
1997
+ pages: 1,
1998
+ animated: false
1999
+ });
2000
+ const info = await image.metadata();
2001
+ image.destroy();
2002
+ if (info.format !== format) unsupported2();
2003
+ if ((info.pages ?? 1) !== 1 || info.pageHeight !== void 0 && info.height !== info.pageHeight) unsupported2();
2004
+ const width = info.width;
2005
+ const height = info.height;
2006
+ const channels = info.channels;
2007
+ if (!width || !height || !channels || !Number.isSafeInteger(width) || !Number.isSafeInteger(height) || !Number.isSafeInteger(channels)) unsupported2();
2008
+ const pixels = width * height;
2009
+ const rawBytes = pixels * channels;
2010
+ if (!Number.isSafeInteger(pixels) || pixels <= 0 || pixels > limits.maxPixels || !Number.isSafeInteger(rawBytes) || rawBytes <= 0 || rawBytes > limits.maxRawBytes) tooLarge3();
2011
+ const decoder = sharp(encoded, {
2012
+ failOn: "warning",
2013
+ sequentialRead: true,
2014
+ limitInputPixels: limits.maxPixels,
2015
+ pages: 1,
2016
+ animated: false
2017
+ }).raw();
2018
+ const decoded = await decoder.toBuffer({ resolveWithObject: true });
2019
+ decoder.destroy();
2020
+ if (decoded.info.width !== width || decoded.info.height !== height || decoded.info.channels !== channels || decoded.data.byteLength !== rawBytes) unsupported2();
2021
+ return {
2022
+ format,
2023
+ mimeType: `image/${format}`,
2024
+ width,
2025
+ height,
2026
+ hasAlpha: info.hasAlpha === true,
2027
+ sha256: createHash("sha256").update(encoded).digest("hex")
2028
+ };
2029
+ } catch (error) {
2030
+ if (error instanceof ImageGenerationError) throw error;
2031
+ mapSharpFailure(error);
2032
+ }
2033
+ });
2034
+ return file.toImageAsset(metadata);
2035
+ }
2036
+ function assertCompatibleMask(primary, mask) {
2037
+ if (mask.mimeType !== primary.mimeType || mask.width !== primary.width || mask.height !== primary.height || mask.hasAlpha !== true) {
2038
+ throw new ImageGenerationError("invalid_image_request", { param: "mask" });
2039
+ }
2040
+ }
2041
+
2042
+ // src/image-generation/openai-images/readMultipartEdit.ts
2043
+ function invalid3(param) {
2044
+ return new ImageGenerationError("invalid_image_request", { param });
2045
+ }
2046
+ function tooLarge4() {
2047
+ return new ImageGenerationError("image_too_large");
2048
+ }
2049
+ function declaredLength2(value, limit) {
2050
+ if (value === void 0) return void 0;
2051
+ if (Array.isArray(value) || !/^(0|[1-9]\d*)$/.test(value)) throw tooLarge4();
2052
+ const parsed = Number(value);
2053
+ if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > limit) throw tooLarge4();
2054
+ return parsed;
2055
+ }
2056
+ function byteLength(value) {
2057
+ return Buffer.byteLength(value, "utf8");
2058
+ }
2059
+ var MAX_MULTIPART_HEADER_BYTES = 16 * 1024;
2060
+ function multipartBoundary(contentType) {
2061
+ const match = /(?:^|;)\s*boundary=(?:"([^"]*)"|([^;\s]*))/i.exec(contentType);
2062
+ const boundary = match?.[1] ?? match?.[2];
2063
+ if (!boundary || boundary.length > 200 || !/^[\x20-\x7e]+$/.test(boundary) || boundary.endsWith(" ")) {
2064
+ throw invalid3("content_type");
2065
+ }
2066
+ return boundary;
2067
+ }
2068
+ var MultipartHeaderPairGuard = class {
2069
+ #opening;
2070
+ #betweenParts;
2071
+ #maxPairs;
2072
+ #state = "preamble";
2073
+ #pending = Buffer.alloc(0);
2074
+ constructor(boundary, maxPairs) {
2075
+ this.#opening = Buffer.from(`--${boundary}`, "latin1");
2076
+ this.#betweenParts = Buffer.from(`\r
2077
+ --${boundary}`, "latin1");
2078
+ this.#maxPairs = maxPairs;
2079
+ }
2080
+ push(chunk) {
2081
+ if (this.#state === "done") return;
2082
+ let data = this.#pending.length === 0 ? Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength) : Buffer.concat([this.#pending, Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)]);
2083
+ this.#pending = Buffer.alloc(0);
2084
+ while (data.length > 0) {
2085
+ if (this.#state === "preamble") {
2086
+ const start = data.indexOf(this.#opening);
2087
+ if (start < 0) {
2088
+ if (data.length > MAX_MULTIPART_HEADER_BYTES) throw tooLarge4();
2089
+ this.#pending = Buffer.from(data);
2090
+ return;
2091
+ }
2092
+ data = data.subarray(start + this.#opening.length);
2093
+ if (data.length < 2) {
2094
+ this.#pending = Buffer.concat([this.#opening, data]);
2095
+ return;
2096
+ }
2097
+ if (data[0] === 45 && data[1] === 45) {
2098
+ this.#state = "done";
2099
+ return;
2100
+ }
2101
+ if (data[0] !== 13 || data[1] !== 10) throw invalid3("content_type");
2102
+ data = data.subarray(2);
2103
+ this.#state = "headers";
2104
+ continue;
2105
+ }
2106
+ if (this.#state === "headers") {
2107
+ const end = data.indexOf("\r\n\r\n");
2108
+ if (end < 0) {
2109
+ if (data.length > MAX_MULTIPART_HEADER_BYTES) throw tooLarge4();
2110
+ this.#pending = Buffer.from(data);
2111
+ return;
2112
+ }
2113
+ const block = data.subarray(0, end);
2114
+ const pairs = block.length === 0 ? 0 : block.toString("latin1").split("\r\n").length;
2115
+ if (pairs > this.#maxPairs) throw tooLarge4();
2116
+ data = data.subarray(end + 4);
2117
+ this.#state = "body";
2118
+ continue;
2119
+ }
2120
+ let searchFrom = 0;
2121
+ let boundaryAt = -1;
2122
+ while ((boundaryAt = data.indexOf(this.#betweenParts, searchFrom)) >= 0) {
2123
+ const suffix2 = boundaryAt + this.#betweenParts.length;
2124
+ if (data.length < suffix2 + 2) {
2125
+ this.#pending = Buffer.from(data.subarray(boundaryAt));
2126
+ return;
2127
+ }
2128
+ if (data[suffix2] === 45 && data[suffix2 + 1] === 45 || data[suffix2] === 13 && data[suffix2 + 1] === 10) break;
2129
+ searchFrom = boundaryAt + 1;
2130
+ }
2131
+ if (boundaryAt < 0) {
2132
+ const keep = Math.min(data.length, this.#betweenParts.length - 1);
2133
+ this.#pending = Buffer.from(data.subarray(data.length - keep));
2134
+ return;
2135
+ }
2136
+ const suffix = boundaryAt + this.#betweenParts.length;
2137
+ if (data[suffix] === 45 && data[suffix + 1] === 45) {
2138
+ this.#state = "done";
2139
+ return;
2140
+ }
2141
+ data = data.subarray(suffix + 2);
2142
+ this.#state = "headers";
2143
+ }
2144
+ }
2145
+ finish() {
2146
+ if (this.#state !== "done") throw invalid3();
2147
+ }
2148
+ };
2149
+ async function readMultipartEdit(request2, scope, limits, signal) {
2150
+ if (signal.aborted) throw new ImageGenerationError("request_cancelled", { cause: signal.reason });
2151
+ const contentType = request2.headers["content-type"];
2152
+ if (typeof contentType !== "string" || !/^multipart\/form-data(?:\s*;|$)/i.test(contentType)) {
2153
+ throw invalid3("content_type");
2154
+ }
2155
+ const encoding = request2.headers["content-encoding"];
2156
+ if (Array.isArray(encoding) || encoding !== void 0 && encoding.trim().toLowerCase() !== "identity") {
2157
+ throw invalid3("content_encoding");
2158
+ }
2159
+ const expected = declaredLength2(request2.headers["content-length"], limits.maxMultipartBytes);
2160
+ const headerGuard = new MultipartHeaderPairGuard(
2161
+ multipartBoundary(contentType),
2162
+ limits.maxHeaderPairs
2163
+ );
2164
+ const fields = /* @__PURE__ */ Object.create(null);
2165
+ const pending = [];
2166
+ let imageCount = 0;
2167
+ let maskCount = 0;
2168
+ let observed = 0;
2169
+ let settled = false;
2170
+ let parser;
2171
+ try {
2172
+ parser = Busboy({
2173
+ headers: request2.headers,
2174
+ limits: {
2175
+ fileSize: limits.maxFileBytes,
2176
+ files: limits.maxFiles,
2177
+ fields: limits.maxFields,
2178
+ parts: limits.maxParts,
2179
+ headerPairs: limits.maxHeaderPairs,
2180
+ fieldNameSize: limits.maxFieldNameBytes,
2181
+ fieldSize: limits.maxFieldValueBytes
2182
+ }
2183
+ });
2184
+ } catch {
2185
+ throw invalid3("content_type");
2186
+ }
2187
+ const parsed = new Promise((resolve2, reject) => {
2188
+ const fail = (error) => {
2189
+ if (settled) return;
2190
+ settled = true;
2191
+ request2.unpipe(parser);
2192
+ request2.pause();
2193
+ parser.destroy(error instanceof Error ? error : invalid3());
2194
+ reject(error);
2195
+ };
2196
+ const onAbort = () => fail(new ImageGenerationError("request_cancelled", { cause: signal.reason }));
2197
+ let requestEnded = false;
2198
+ const onEnd = () => {
2199
+ requestEnded = true;
2200
+ };
2201
+ const onClose = () => {
2202
+ if (!requestEnded) fail(invalid3());
2203
+ };
2204
+ const onRequestError = () => fail(invalid3());
2205
+ const onData = (chunk) => {
2206
+ observed += chunk.byteLength;
2207
+ if (!Number.isSafeInteger(observed) || observed > limits.maxMultipartBytes || expected !== void 0 && observed > expected) {
2208
+ fail(tooLarge4());
2209
+ return;
2210
+ }
2211
+ try {
2212
+ headerGuard.push(chunk);
2213
+ } catch (error) {
2214
+ fail(error);
2215
+ }
2216
+ };
2217
+ signal.addEventListener("abort", onAbort, { once: true });
2218
+ request2.on("data", onData);
2219
+ request2.once("aborted", onAbort);
2220
+ request2.once("end", onEnd);
2221
+ request2.once("close", onClose);
2222
+ request2.once("error", onRequestError);
2223
+ parser.on("file", (name, file) => {
2224
+ if (settled) {
2225
+ file.resume();
2226
+ return;
2227
+ }
2228
+ if (byteLength(name) > limits.maxFieldNameBytes) {
2229
+ file.resume();
2230
+ fail(tooLarge4());
2231
+ return;
2232
+ }
2233
+ let kind;
2234
+ if (name === "image" || name === "image[]") {
2235
+ imageCount += 1;
2236
+ if (imageCount > 16) {
2237
+ file.resume();
2238
+ fail(invalid3("image"));
2239
+ return;
2240
+ }
2241
+ kind = "image";
2242
+ } else if (name === "mask") {
2243
+ maskCount += 1;
2244
+ if (maskCount > 1) {
2245
+ file.resume();
2246
+ fail(invalid3("mask"));
2247
+ return;
2248
+ }
2249
+ kind = "mask";
2250
+ } else {
2251
+ file.resume();
2252
+ fail(invalid3(name.length <= 128 ? name : void 0));
2253
+ return;
2254
+ }
2255
+ let truncated = false;
2256
+ file.once("limit", () => {
2257
+ truncated = true;
2258
+ });
2259
+ const staged2 = scope.materialize(file, {
2260
+ maxBytes: limits.maxFileBytes,
2261
+ signal
2262
+ }).then((value) => {
2263
+ if (truncated || file.truncated) throw tooLarge4();
2264
+ return value;
2265
+ });
2266
+ staged2.catch(fail);
2267
+ pending.push({ kind, staged: staged2 });
2268
+ });
2269
+ parser.on("field", (name, value, info) => {
2270
+ if (settled) return;
2271
+ if (info.nameTruncated || info.valueTruncated || byteLength(name) > limits.maxFieldNameBytes || byteLength(value) > limits.maxFieldValueBytes) {
2272
+ fail(tooLarge4());
2273
+ return;
2274
+ }
2275
+ if (Object.hasOwn(fields, name)) {
2276
+ fail(invalid3(name.length <= 128 ? name : void 0));
2277
+ return;
2278
+ }
2279
+ fields[name] = value;
2280
+ });
2281
+ parser.once("filesLimit", () => fail(tooLarge4()));
2282
+ parser.once("fieldsLimit", () => fail(tooLarge4()));
2283
+ parser.once("partsLimit", () => fail(tooLarge4()));
2284
+ parser.once("error", (error) => fail(error instanceof ImageGenerationError ? error : invalid3()));
2285
+ parser.once("finish", () => {
2286
+ if (settled) return;
2287
+ try {
2288
+ headerGuard.finish();
2289
+ } catch (error) {
2290
+ fail(error);
2291
+ return;
2292
+ }
2293
+ if (expected !== void 0 && observed !== expected) {
2294
+ fail(invalid3());
2295
+ return;
2296
+ }
2297
+ settled = true;
2298
+ resolve2();
2299
+ });
2300
+ request2.pipe(parser);
2301
+ const cleanup = () => {
2302
+ signal.removeEventListener("abort", onAbort);
2303
+ request2.removeListener("data", onData);
2304
+ request2.removeListener("aborted", onAbort);
2305
+ request2.removeListener("end", onEnd);
2306
+ request2.removeListener("close", onClose);
2307
+ request2.removeListener("error", onRequestError);
2308
+ };
2309
+ parser.once("close", cleanup);
2310
+ });
2311
+ await parsed;
2312
+ if (imageCount < 1 || imageCount > 16) throw invalid3("image");
2313
+ const staged = await Promise.all(pending.map((item) => item.staged));
2314
+ const images = [];
2315
+ let mask;
2316
+ for (let index = 0; index < staged.length; index += 1) {
2317
+ const asset = await validateRaster(staged[index], limits);
2318
+ if (pending[index].kind === "mask") mask = asset;
2319
+ else images.push(asset);
2320
+ }
2321
+ if (mask) assertCompatibleMask(images[0], mask);
2322
+ return { fields, images, ...mask ? { mask } : {} };
2323
+ }
2324
+
2325
+ // src/image-generation/openai-images/resolveImageInput.ts
2326
+ function invalid4(param = "image") {
2327
+ throw new ImageGenerationError("invalid_image_request", { param });
2328
+ }
2329
+ function closedInput(value, maxRemoteUrlBytes) {
2330
+ if (!value || typeof value !== "object" || Array.isArray(value)) invalid4();
2331
+ const record2 = value;
2332
+ const keys = Object.keys(record2);
2333
+ if (keys.length !== 1) invalid4();
2334
+ const key = keys[0];
2335
+ if (key !== "file_id" && key !== "image_url") invalid4();
2336
+ const item = record2[key];
2337
+ const maxBytes = key === "file_id" ? 256 : maxRemoteUrlBytes;
2338
+ if (typeof item !== "string" || !item || Buffer.byteLength(item, "utf8") > maxBytes) invalid4();
2339
+ return { [key]: item };
2340
+ }
2341
+ function decodedBase64Length(value) {
2342
+ if (value.length === 0 || value.length % 4 !== 0) invalid4();
2343
+ let padding = 0;
2344
+ if (value.charCodeAt(value.length - 1) === 61) padding += 1;
2345
+ if (value.charCodeAt(value.length - 2) === 61) padding += 1;
2346
+ const decoded = value.length / 4 * 3 - padding;
2347
+ if (!Number.isSafeInteger(decoded) || decoded <= 0) invalid4();
2348
+ return decoded;
2349
+ }
2350
+ function validateBase64(value, padding) {
2351
+ const alphabetEnd = value.length - padding;
2352
+ for (let index = 0; index < value.length; index += 1) {
2353
+ const code = value.charCodeAt(index);
2354
+ const alphabet = code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47;
2355
+ if (index < alphabetEnd ? !alphabet : code !== 61) invalid4();
2356
+ }
2357
+ const alphabetValue = (code) => {
2358
+ if (code >= 65 && code <= 90) return code - 65;
2359
+ if (code >= 97 && code <= 122) return code - 97 + 26;
2360
+ if (code >= 48 && code <= 57) return code - 48 + 52;
2361
+ return code === 43 ? 62 : 63;
2362
+ };
2363
+ if (padding === 1 && (alphabetValue(value.charCodeAt(value.length - 2)) & 3) !== 0) invalid4();
2364
+ if (padding === 2 && (alphabetValue(value.charCodeAt(value.length - 3)) & 15) !== 0) invalid4();
2365
+ }
2366
+ async function* decodeBase64Chunks(value) {
2367
+ const blockCharacters = 64 * 1024;
2368
+ for (let offset = 0; offset < value.length; offset += blockCharacters) {
2369
+ const end = Math.min(value.length, offset + blockCharacters);
2370
+ yield Buffer.from(value.slice(offset, end), "base64");
2371
+ }
2372
+ }
2373
+ async function resolveDataUrl(value, runtime, scope, signal) {
2374
+ const comma = value.indexOf(",");
2375
+ if (comma < 0) invalid4();
2376
+ const descriptor = value.slice(0, comma).toLowerCase();
2377
+ if (!["data:image/png;base64", "data:image/jpeg;base64", "data:image/webp;base64"].includes(descriptor)) {
2378
+ invalid4();
2379
+ }
2380
+ const encoded = value.slice(comma + 1);
2381
+ const decoded = decodedBase64Length(encoded);
2382
+ if (decoded > runtime.limits.maxFileBytes) throw new ImageGenerationError("image_too_large");
2383
+ const padding = encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0;
2384
+ validateBase64(encoded, padding);
2385
+ const file = await scope.materialize(decodeBase64Chunks(encoded), {
2386
+ declaredBytes: decoded,
2387
+ maxBytes: runtime.limits.maxFileBytes,
2388
+ signal
2389
+ });
2390
+ return validateRaster(file, runtime.limits);
2391
+ }
2392
+ async function resolveReference(referenceId, runtime, scope) {
2393
+ if (!runtime.referenceStore) throw new ImageGenerationError("unsupported_capability", { param: "file_id" });
2394
+ const result = await runtime.referenceStore.resolve(runtime.tenantId, referenceId);
2395
+ if (result.status === "not_found") throw new ImageGenerationError("image_reference_not_found", { param: "file_id" });
2396
+ if (result.status === "expired") throw new ImageGenerationError("image_reference_expired", { param: "file_id" });
2397
+ const asset = result.lease.value.artifact;
2398
+ if (!asset) {
2399
+ await result.lease.release();
2400
+ throw new ImageGenerationError("unsupported_capability", { param: "file_id" });
2401
+ }
2402
+ if (asset.byteLength !== result.lease.metadata.byteLength || asset.width !== result.lease.metadata.width || asset.height !== result.lease.metadata.height || asset.mimeType !== result.lease.metadata.mimeType) {
2403
+ await result.lease.release();
2404
+ throw new ImageGenerationError("upstream_protocol_changed");
2405
+ }
2406
+ scope.addLease(result.lease);
2407
+ return asset;
2408
+ }
2409
+ async function resolveImageInput(value, runtime, scope, signal) {
2410
+ const input = closedInput(value, runtime.limits.maxRemoteUrlBytes);
2411
+ if ("file_id" in input) return resolveReference(input.file_id, runtime, scope);
2412
+ if (input.image_url.toLowerCase().startsWith("data:")) {
2413
+ return resolveDataUrl(input.image_url, runtime, scope, signal);
2414
+ }
2415
+ if (!runtime.remoteResolver) {
2416
+ throw new ImageGenerationError("unsupported_capability", { param: "image_url" });
2417
+ }
2418
+ const materializer = {
2419
+ async materialize(source, declaredBytes, remoteSignal) {
2420
+ const file = await scope.materialize(source, {
2421
+ maxBytes: runtime.limits.maxFileBytes,
2422
+ ...declaredBytes !== void 0 ? { declaredBytes } : {},
2423
+ signal: remoteSignal
2424
+ });
2425
+ return validateRaster(file, runtime.limits);
2426
+ }
2427
+ };
2428
+ return runtime.remoteResolver.resolve({
2429
+ url: input.image_url,
2430
+ signal,
2431
+ limits: runtime.limits,
2432
+ materializer
2433
+ });
2434
+ }
2435
+ function parseImageInputList(value, maxRemoteUrlBytes) {
2436
+ const list = Array.isArray(value) ? value : [value];
2437
+ if (list.length < 1 || list.length > 16) invalid4();
2438
+ for (const item of list) closedInput(item, maxRemoteUrlBytes);
2439
+ return list;
2440
+ }
2441
+
2442
+ // src/image-generation/openai-images/editHandler.ts
2443
+ function mediaType2(value) {
2444
+ return (value ?? "").split(";", 1)[0].trim().toLowerCase();
2445
+ }
2446
+ function isReferenceInput(value) {
2447
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, "file_id");
2448
+ }
2449
+ function createImageEditHandler(deps) {
2450
+ return async (context) => {
2451
+ const requestId = safeImageRequestId(deps.createRequestId?.());
2452
+ const createdAt = (deps.now ?? Date.now)();
2453
+ let runtime;
2454
+ let request2;
2455
+ let scope;
2456
+ let cleanupOutcome;
2457
+ let observedReference = false;
2458
+ const referenceOutcomes = { hits: 0, notFound: 0, expired: 0, failed: 0 };
2459
+ let terminal = "failed";
2460
+ let terminalCode;
2461
+ try {
2462
+ runtime = await deps.resolveRuntime(context);
2463
+ validateRuntime(runtime);
2464
+ scope = deps.createResourceScope ? await deps.createResourceScope(runtime.limits, context.signal, runtime.tenantId) : await createImageRequestResourceScope(runtime.limits, context.signal);
2465
+ const type = mediaType2(context.request.headers["content-type"]);
2466
+ if (type === "application/json") {
2467
+ const value = await readJsonBody(context.request, {
2468
+ maxBytes: runtime.limits.maxJsonBytes,
2469
+ signal: context.signal
2470
+ });
2471
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2472
+ throw new ImageGenerationError("invalid_image_request");
2473
+ }
2474
+ const record2 = value;
2475
+ if (record2.image !== void 0 && record2.images !== void 0) {
2476
+ throw new ImageGenerationError("invalid_image_request", { param: "image" });
2477
+ }
2478
+ const options = normalizeImageOptions(record2, runtime, {
2479
+ action: "edit",
2480
+ extraKeys: ["image", "images", "mask"]
2481
+ });
2482
+ const inputs = parseImageInputList(
2483
+ record2.images ?? record2.image,
2484
+ runtime.limits.maxRemoteUrlBytes
2485
+ );
2486
+ const resolveObservedInput = async (input) => {
2487
+ const reference = isReferenceInput(input);
2488
+ if (reference) observedReference = true;
2489
+ try {
2490
+ const asset = await resolveImageInput(input, runtime, scope, context.signal);
2491
+ if (reference) referenceOutcomes.hits += 1;
2492
+ return asset;
2493
+ } catch (error) {
2494
+ if (reference) {
2495
+ if (error instanceof ImageGenerationError && error.code === "image_reference_not_found") {
2496
+ referenceOutcomes.notFound += 1;
2497
+ } else if (error instanceof ImageGenerationError && error.code === "image_reference_expired") {
2498
+ referenceOutcomes.expired += 1;
2499
+ } else {
2500
+ referenceOutcomes.failed += 1;
2501
+ }
2502
+ }
2503
+ throw error;
2504
+ }
2505
+ };
2506
+ const images = [];
2507
+ for (const input of inputs) images.push(await resolveObservedInput(input));
2508
+ const mask = record2.mask === void 0 ? void 0 : await resolveObservedInput(record2.mask);
2509
+ if (mask) assertCompatibleMask(images[0], mask);
2510
+ request2 = { action: "edit", ...options, images, ...mask ? { mask } : {} };
2511
+ } else {
2512
+ const multipart = await readMultipartEdit(context.request, scope, runtime.limits, context.signal);
2513
+ const options = normalizeImageOptions(multipart.fields, runtime, {
2514
+ action: "edit",
2515
+ multipart: true
2516
+ });
2517
+ request2 = {
2518
+ action: "edit",
2519
+ ...options,
2520
+ images: multipart.images,
2521
+ ...multipart.mask ? { mask: multipart.mask } : {}
2522
+ };
2523
+ }
2524
+ const events = deps.orchestrator.run(
2525
+ request2,
2526
+ {
2527
+ requestId,
2528
+ tenantId: runtime.tenantId,
2529
+ signal: context.signal,
2530
+ ...context.route.sessionId ? { sessionKey: context.route.sessionId } : {},
2531
+ ...runtime.preferredAccountId ? { preferredAccountId: runtime.preferredAccountId } : {},
2532
+ ...runtime.preferredAccountGroup ? { preferredAccountGroup: runtime.preferredAccountGroup } : {},
2533
+ ...runtime.boundAccountFallbackPolicy ? { boundAccountFallbackPolicy: runtime.boundAccountFallbackPolicy } : {}
2534
+ },
2535
+ { providerId: runtime.providerId, ...runtime.retention ? { retention: runtime.retention } : {} }
2536
+ );
2537
+ if (request2.stream) {
2538
+ await writeImageApiSse({
2539
+ action: "edit",
2540
+ response: context.response,
2541
+ events,
2542
+ requestedCount: request2.n,
2543
+ requestId,
2544
+ createdAt,
2545
+ metadata: {
2546
+ background: request2.background,
2547
+ outputFormat: request2.outputFormat,
2548
+ quality: request2.quality,
2549
+ size: request2.size.kind === "auto" ? "auto" : `${request2.size.width}x${request2.size.height}`
2550
+ },
2551
+ scope,
2552
+ limits: runtime.limits,
2553
+ signal: context.signal
2554
+ });
2555
+ } else {
2556
+ await writeImageApiResponse({
2557
+ response: context.response,
2558
+ events,
2559
+ requestedCount: request2.n,
2560
+ requestId,
2561
+ createdAt,
2562
+ scope,
2563
+ limits: runtime.limits,
2564
+ signal: context.signal
2565
+ });
2566
+ }
2567
+ terminal = "completed";
2568
+ } catch (error) {
2569
+ const domainError = imageApiErrorFromUnknown(error);
2570
+ terminal = domainError.code === "request_cancelled" ? "cancelled" : "failed";
2571
+ terminalCode = domainError.code;
2572
+ writeImageApiError(context.response, domainError, requestId);
2573
+ } finally {
2574
+ if (scope) {
2575
+ try {
2576
+ await scope.cleanup();
2577
+ cleanupOutcome = "completed";
2578
+ } catch {
2579
+ cleanupOutcome = "failed";
2580
+ }
2581
+ }
2582
+ await bestEffortAudit(deps, {
2583
+ requestId,
2584
+ operationId: "images.edit",
2585
+ ...runtime ? { providerId: runtime.providerId } : {},
2586
+ ...request2 ? {
2587
+ model: request2.model,
2588
+ options: {
2589
+ n: request2.n,
2590
+ quality: request2.quality,
2591
+ background: request2.background,
2592
+ outputFormat: request2.outputFormat,
2593
+ stream: request2.stream,
2594
+ partialImages: request2.partialImages
2595
+ },
2596
+ inputCount: request2.images.length,
2597
+ inputBytes: request2.images.reduce((sum, asset) => sum + asset.byteLength, 0)
2598
+ } : {},
2599
+ terminal,
2600
+ ...observedReference ? { referenceOutcomes } : {},
2601
+ ...cleanupOutcome ? { cleanupOutcome } : {},
2602
+ ...terminalCode ? { errorCode: terminalCode } : {}
2603
+ });
2604
+ }
2605
+ };
2606
+ }
2607
+
2608
+ // src/image-generation/openai-images/contributions.ts
2609
+ function createImageApiContributions(deps) {
2610
+ if (!deps || typeof deps !== "object" || !deps.orchestrator || typeof deps.resolveRuntime !== "function") {
2611
+ throw new TypeError("Image API contributions require an orchestrator and runtime resolver.");
2612
+ }
2613
+ const generate = Object.freeze({
2614
+ operationId: "images.generate",
2615
+ handler: createImageGenerateHandler(deps)
2616
+ });
2617
+ const edit = Object.freeze({
2618
+ operationId: "images.edit",
2619
+ handler: createImageEditHandler(deps)
2620
+ });
2621
+ return Object.freeze({ generate, edit, all: Object.freeze([generate, edit]) });
2622
+ }
2623
+
2624
+ // src/image-generation/openai-images/safeRemoteImageResolver.ts
2625
+ import { lookup as dnsLookup } from "dns/promises";
2626
+ import { isIP } from "net";
2627
+ import { Agent, request } from "undici";
2628
+ function ipv4Number(value) {
2629
+ const parts = value.split(".");
2630
+ if (parts.length !== 4) return void 0;
2631
+ let output = 0;
2632
+ for (const part of parts) {
2633
+ if (!/^\d{1,3}$/.test(part)) return void 0;
2634
+ const octet = Number(part);
2635
+ if (octet > 255 || String(octet) !== part) return void 0;
2636
+ output = output * 256 + octet;
2637
+ }
2638
+ return output >>> 0;
2639
+ }
2640
+ function blockedIpv4(value) {
2641
+ const address = ipv4Number(value);
2642
+ if (address === void 0) return true;
2643
+ const first = address >>> 24;
2644
+ const second = address >>> 16 & 255;
2645
+ if (first === 0 || first === 10 || first === 127) return true;
2646
+ if (first === 100 && second >= 64 && second <= 127) return true;
2647
+ if (first === 169 && second === 254) return true;
2648
+ if (first === 172 && second >= 16 && second <= 31) return true;
2649
+ if (first === 192 && second === 168) return true;
2650
+ if (first === 192 && second === 0) return true;
2651
+ if (first === 192 && second === 2) return true;
2652
+ if (first === 198 && (second === 18 || second === 19 || second === 51)) return true;
2653
+ if (first === 203 && second === 0) return true;
2654
+ if (first >= 224) return true;
2655
+ return false;
2656
+ }
2657
+ function ipv6Bytes(input) {
2658
+ const zone = input.indexOf("%");
2659
+ const value = (zone >= 0 ? input.slice(0, zone) : input).toLowerCase();
2660
+ const halves = value.split("::");
2661
+ if (halves.length > 2) return void 0;
2662
+ const parseHalf = (half) => {
2663
+ if (!half) return [];
2664
+ const words2 = [];
2665
+ for (const item of half.split(":")) {
2666
+ if (item.includes(".")) {
2667
+ const ipv4 = ipv4Number(item);
2668
+ if (ipv4 === void 0) return void 0;
2669
+ words2.push(ipv4 >>> 16 & 65535, ipv4 & 65535);
2670
+ } else {
2671
+ if (!/^[0-9a-f]{1,4}$/.test(item)) return void 0;
2672
+ words2.push(Number.parseInt(item, 16));
2673
+ }
2674
+ }
2675
+ return words2;
2676
+ };
2677
+ const left = parseHalf(halves[0]);
2678
+ const right = parseHalf(halves[1] ?? "");
2679
+ if (!left || !right) return void 0;
2680
+ const missing = 8 - left.length - right.length;
2681
+ if (halves.length === 1 && missing !== 0 || halves.length === 2 && missing < 1) return void 0;
2682
+ const words = [...left, ...Array.from({ length: missing }, () => 0), ...right];
2683
+ if (words.length !== 8) return void 0;
2684
+ const bytes = new Uint8Array(16);
2685
+ words.forEach((word, index) => {
2686
+ bytes[index * 2] = word >>> 8;
2687
+ bytes[index * 2 + 1] = word & 255;
2688
+ });
2689
+ return bytes;
2690
+ }
2691
+ function blockedIpv6(value) {
2692
+ const bytes = ipv6Bytes(value);
2693
+ if (!bytes) return true;
2694
+ if (bytes.every((item) => item === 0)) return true;
2695
+ if (bytes.slice(0, 15).every((item) => item === 0) && bytes[15] === 1) return true;
2696
+ if ((bytes[0] & 254) === 252) return true;
2697
+ if (bytes[0] === 254 && (bytes[1] & 192) === 128) return true;
2698
+ if (bytes[0] === 255) return true;
2699
+ const mapped = bytes.slice(0, 10).every((item) => item === 0) && bytes[10] === 255 && bytes[11] === 255;
2700
+ if (mapped) return blockedIpv4(`${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`);
2701
+ const compatible = bytes.slice(0, 12).every((item) => item === 0);
2702
+ if (compatible) return blockedIpv4(`${bytes[12]}.${bytes[13]}.${bytes[14]}.${bytes[15]}`);
2703
+ return false;
2704
+ }
2705
+ function isBlockedRemoteAddress(address) {
2706
+ const family = isIP(address);
2707
+ return family === 4 ? blockedIpv4(address) : family === 6 ? blockedIpv6(address) : true;
2708
+ }
2709
+ function safeUrl(value) {
2710
+ let url;
2711
+ try {
2712
+ url = new URL(value);
2713
+ } catch {
2714
+ throw new ImageGenerationError("invalid_image_request", { param: "image_url" });
2715
+ }
2716
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password) {
2717
+ throw new ImageGenerationError("invalid_image_request", { param: "image_url" });
2718
+ }
2719
+ return url;
2720
+ }
2721
+ function combinedSignal(parent, timeoutMs) {
2722
+ return AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
2723
+ }
2724
+ function pinnedLookup(address) {
2725
+ return ((_hostname, options, callback) => {
2726
+ if (typeof options === "object" && options.all) {
2727
+ callback(null, [{ address: address.address, family: address.family }]);
2728
+ return;
2729
+ }
2730
+ callback(null, address.address, address.family);
2731
+ });
2732
+ }
2733
+ async function defaultResolveAll(hostname, signal) {
2734
+ const lookup = dnsLookup(hostname, { all: true, verbatim: true });
2735
+ let abort;
2736
+ try {
2737
+ const results = await Promise.race([
2738
+ lookup,
2739
+ new Promise((_resolve, reject) => {
2740
+ abort = () => reject(signal.reason);
2741
+ if (signal.aborted) abort();
2742
+ else signal.addEventListener("abort", abort, { once: true });
2743
+ })
2744
+ ]);
2745
+ return results.filter((item) => item.family === 4 || item.family === 6);
2746
+ } finally {
2747
+ if (abort) signal.removeEventListener("abort", abort);
2748
+ }
2749
+ }
2750
+ function headerBytes(headers) {
2751
+ let total = 0;
2752
+ for (const [name, value] of Object.entries(headers)) {
2753
+ total += Buffer.byteLength(name);
2754
+ for (const item of Array.isArray(value) ? value : [value ?? ""]) total += Buffer.byteLength(item);
2755
+ }
2756
+ return total;
2757
+ }
2758
+ function contentLength(value, maxBytes) {
2759
+ if (value === void 0) return void 0;
2760
+ if (Array.isArray(value) || !/^(0|[1-9]\d*)$/.test(value)) {
2761
+ throw new ImageGenerationError("image_generation_failed");
2762
+ }
2763
+ const parsed = Number(value);
2764
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) throw new ImageGenerationError("image_generation_failed");
2765
+ if (parsed > maxBytes) throw new ImageGenerationError("image_too_large");
2766
+ return parsed;
2767
+ }
2768
+ async function defaultRequestPinned(input) {
2769
+ const dispatcher = new Agent({
2770
+ maxHeaderSize: input.maxHeaderBytes,
2771
+ connect: {
2772
+ lookup: pinnedLookup(input.address),
2773
+ timeout: input.connectTimeoutMs
2774
+ }
2775
+ });
2776
+ try {
2777
+ const response = await request(input.url, {
2778
+ method: "GET",
2779
+ dispatcher,
2780
+ signal: input.signal,
2781
+ maxRedirections: 0,
2782
+ headersTimeout: input.connectTimeoutMs,
2783
+ bodyTimeout: input.totalTimeoutMs,
2784
+ headers: { accept: "image/png,image/jpeg,image/webp" }
2785
+ });
2786
+ return {
2787
+ statusCode: response.statusCode,
2788
+ headers: response.headers,
2789
+ body: response.body,
2790
+ close: () => dispatcher.close()
2791
+ };
2792
+ } catch (error) {
2793
+ await dispatcher.close().catch(() => void 0);
2794
+ throw error;
2795
+ }
2796
+ }
2797
+ function createSafeRemoteImageResolver(deps = {}) {
2798
+ const resolveAll = deps.resolveAll ?? defaultResolveAll;
2799
+ const requestPinned = deps.requestPinned ?? defaultRequestPinned;
2800
+ return {
2801
+ async resolve(input) {
2802
+ const totalSignal = combinedSignal(input.signal, input.limits.remoteTotalTimeoutMs);
2803
+ let current = safeUrl(input.url);
2804
+ try {
2805
+ for (let redirects = 0; redirects <= input.limits.maxRedirects; redirects += 1) {
2806
+ if (totalSignal.aborted) throw totalSignal.reason;
2807
+ const literal = current.hostname.startsWith("[") && current.hostname.endsWith("]") ? current.hostname.slice(1, -1) : current.hostname;
2808
+ const family = isIP(literal);
2809
+ const answers = family ? [{ address: literal, family }] : await resolveAll(literal, totalSignal);
2810
+ if (answers.length === 0 || answers.some((answer) => isBlockedRemoteAddress(answer.address))) {
2811
+ throw new ImageGenerationError("invalid_image_request", { param: "image_url" });
2812
+ }
2813
+ const selected = answers[0];
2814
+ const response = await requestPinned({
2815
+ url: current,
2816
+ address: selected,
2817
+ signal: totalSignal,
2818
+ connectTimeoutMs: input.limits.remoteConnectTimeoutMs,
2819
+ totalTimeoutMs: input.limits.remoteTotalTimeoutMs,
2820
+ maxHeaderBytes: input.limits.maxRemoteHeaderBytes
2821
+ });
2822
+ try {
2823
+ if (headerBytes(response.headers) > input.limits.maxRemoteHeaderBytes) {
2824
+ await response.body.dump();
2825
+ throw new ImageGenerationError("image_too_large");
2826
+ }
2827
+ if ([301, 302, 303, 307, 308].includes(response.statusCode)) {
2828
+ const location = response.headers.location;
2829
+ await response.body.dump();
2830
+ if (redirects >= input.limits.maxRedirects || typeof location !== "string") {
2831
+ throw new ImageGenerationError("invalid_image_request", { param: "image_url" });
2832
+ }
2833
+ current = safeUrl(new URL(location, current).toString());
2834
+ continue;
2835
+ }
2836
+ if (response.statusCode < 200 || response.statusCode >= 300) {
2837
+ await response.body.dump();
2838
+ throw new ImageGenerationError("image_generation_failed");
2839
+ }
2840
+ const declared = contentLength(response.headers["content-length"], input.limits.maxFileBytes);
2841
+ return await input.materializer.materialize(response.body, declared, totalSignal);
2842
+ } finally {
2843
+ await response.close().catch(() => void 0);
2844
+ }
2845
+ }
2846
+ throw new ImageGenerationError("invalid_image_request", { param: "image_url" });
2847
+ } catch (error) {
2848
+ if (error instanceof ImageGenerationError) throw error;
2849
+ if (input.signal.aborted) {
2850
+ throw new ImageGenerationError("request_cancelled", { cause: input.signal.reason });
2851
+ }
2852
+ if (totalSignal.aborted) throw new ImageGenerationError("image_generation_timeout");
2853
+ throw new ImageGenerationError("image_generation_failed");
2854
+ }
2855
+ }
2856
+ };
2857
+ }
2858
+
2859
+ // src/image-generation/responses/ResponsesImageGenerationContribution.ts
2860
+ import { randomUUID as randomUUID4 } from "crypto";
2861
+
2862
+ // src/image-generation/responses/normalizeResponsesImageTool.ts
2863
+ var IMAGE_DECLARATION_KEYS = /* @__PURE__ */ new Set([
2864
+ "type",
2865
+ "action",
2866
+ "size",
2867
+ "quality",
2868
+ "output_format",
2869
+ "output_compression",
2870
+ "background",
2871
+ "partial_images"
2872
+ ]);
2873
+ var CALL_ID_PATTERN = /^ig_[A-Za-z0-9_-]{16,128}$/;
2874
+ var RESPONSE_ID_PATTERN = /^resp_[A-Za-z0-9_-]{1,240}$/;
2875
+ var TOOL_IDENTITY_KEYS = /* @__PURE__ */ new Set(["declarationIndex", "type", "name"]);
2876
+ var MAX_SELECTED_TOOL_CALLS = 1024;
2877
+ var NORMALIZATION_RUNTIME = {
2878
+ tenantId: "responses-image-inspection",
2879
+ providerId: "responses-image-inspection",
2880
+ defaultModel: "responses-image-model",
2881
+ modelAliases: /* @__PURE__ */ new Map(),
2882
+ limits: DEFAULT_IMAGE_API_LIMITS
2883
+ };
2884
+ function invalid5(param) {
2885
+ throw new ImageGenerationError("invalid_image_request", { param });
2886
+ }
2887
+ function protocolFailure() {
2888
+ throw new ImageGenerationError("upstream_protocol_changed");
2889
+ }
2890
+ function record(value, param) {
2891
+ if (!value || typeof value !== "object" || Array.isArray(value)) invalid5(param);
2892
+ return value;
2893
+ }
2894
+ function parseSelectionPolicy(value) {
2895
+ if (value === void 0 || value === "auto") return { kind: "auto" };
2896
+ if (value === "required") return { kind: "required" };
2897
+ if (value === "none") return { kind: "none" };
2898
+ const choice = record(value, "tool_choice");
2899
+ if (typeof choice.type !== "string" || !choice.type.trim() || choice.type.length > 128) {
2900
+ invalid5("tool_choice");
2901
+ }
2902
+ if (choice.type === "image_generation") {
2903
+ if (Object.keys(choice).some((key) => key !== "type")) invalid5("tool_choice");
2904
+ return { kind: "forced_image" };
2905
+ }
2906
+ if (Object.keys(choice).some((key) => key !== "type" && key !== "name")) {
2907
+ invalid5("tool_choice");
2908
+ }
2909
+ if (choice.name !== void 0 && (typeof choice.name !== "string" || !choice.name.trim() || choice.name.length > 128)) {
2910
+ invalid5("tool_choice");
2911
+ }
2912
+ return {
2913
+ kind: "forced_other",
2914
+ toolType: choice.type,
2915
+ ...choice.name !== void 0 ? { toolName: choice.name } : {}
2916
+ };
2917
+ }
2918
+ function declaredOtherToolIdentity(value, declarationIndex) {
2919
+ if (typeof value.type !== "string" || !value.type.trim() || value.type.length > 128) {
2920
+ return void 0;
2921
+ }
2922
+ if (value.name !== void 0 && (typeof value.name !== "string" || !value.name.trim() || value.name.length > 128)) {
2923
+ return void 0;
2924
+ }
2925
+ return Object.freeze({
2926
+ declarationIndex,
2927
+ type: value.type,
2928
+ ...value.name !== void 0 ? { name: value.name } : {}
2929
+ });
2930
+ }
2931
+ function sameToolIdentity(left, right) {
2932
+ return left.declarationIndex === right.declarationIndex && left.type === right.type && left.name === right.name;
2933
+ }
2934
+ function forcedOtherMatches(policy, identity) {
2935
+ return identity.type === policy.toolType && identity.name === policy.toolName;
2936
+ }
2937
+ function parsePreviousResponseId(value) {
2938
+ if (value === void 0) return void 0;
2939
+ if (typeof value !== "string" || !RESPONSE_ID_PATTERN.test(value)) {
2940
+ invalid5("previous_response_id");
2941
+ }
2942
+ return value;
2943
+ }
2944
+ function parseExplicitCallIds(value) {
2945
+ if (value === void 0 || typeof value === "string") return Object.freeze([]);
2946
+ if (!Array.isArray(value)) invalid5("input");
2947
+ const ids = [];
2948
+ for (const item of value) {
2949
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
2950
+ const candidate = item;
2951
+ if (candidate.type !== "image_generation_call") continue;
2952
+ if (Object.keys(candidate).some((key) => key !== "type" && key !== "id")) invalid5("input");
2953
+ if (typeof candidate.id !== "string" || !CALL_ID_PATTERN.test(candidate.id)) invalid5("input");
2954
+ ids.push(candidate.id);
2955
+ }
2956
+ return Object.freeze(ids);
2957
+ }
2958
+ function parseDeclaration(value, stream) {
2959
+ for (const key of Object.keys(value)) {
2960
+ if (!IMAGE_DECLARATION_KEYS.has(key)) invalid5(`tools.${key}`);
2961
+ }
2962
+ const action = value.action === void 0 ? "auto" : value.action;
2963
+ if (action !== "auto" && action !== "generate" && action !== "edit") invalid5("tools.action");
2964
+ const options = normalizeImageOptions(
2965
+ {
2966
+ prompt: "responses image selection",
2967
+ model: NORMALIZATION_RUNTIME.defaultModel,
2968
+ n: 1,
2969
+ moderation: "auto",
2970
+ stream,
2971
+ ...value.size !== void 0 ? { size: value.size } : {},
2972
+ ...value.quality !== void 0 ? { quality: value.quality } : {},
2973
+ ...value.output_format !== void 0 ? { output_format: value.output_format } : {},
2974
+ ...value.output_compression !== void 0 ? { output_compression: value.output_compression } : {},
2975
+ ...value.background !== void 0 ? { background: value.background } : {},
2976
+ ...value.partial_images !== void 0 ? { partial_images: value.partial_images } : {}
2977
+ },
2978
+ NORMALIZATION_RUNTIME,
2979
+ { action: "generate" }
2980
+ );
2981
+ return Object.freeze({
2982
+ action,
2983
+ quality: options.quality,
2984
+ size: options.size,
2985
+ background: options.background,
2986
+ outputFormat: options.outputFormat,
2987
+ ...options.outputCompression !== void 0 ? { outputCompression: options.outputCompression } : {},
2988
+ partialImages: options.partialImages
2989
+ });
2990
+ }
2991
+ function inspectResponsesImageRequest(input) {
2992
+ if (!input || typeof input !== "object") invalid5();
2993
+ const stream = input.stream === void 0 ? false : input.stream;
2994
+ if (typeof stream !== "boolean") invalid5("stream");
2995
+ const tools = input.tools === void 0 ? [] : input.tools;
2996
+ if (!Array.isArray(tools)) invalid5("tools");
2997
+ let imageToolIndex;
2998
+ let declaration;
2999
+ let otherToolCount = 0;
3000
+ const otherTools = [];
3001
+ for (let index = 0; index < tools.length; index += 1) {
3002
+ const tool = tools[index];
3003
+ if (!tool || typeof tool !== "object" || Array.isArray(tool)) {
3004
+ otherToolCount += 1;
3005
+ continue;
3006
+ }
3007
+ const candidate = tool;
3008
+ if (candidate.type !== "image_generation") {
3009
+ otherToolCount += 1;
3010
+ const identity = declaredOtherToolIdentity(candidate, index);
3011
+ if (identity) otherTools.push(identity);
3012
+ continue;
3013
+ }
3014
+ if (imageToolIndex !== void 0) invalid5("tools");
3015
+ imageToolIndex = index;
3016
+ declaration = parseDeclaration(candidate, stream);
3017
+ }
3018
+ const selectionPolicy = parseSelectionPolicy(input.tool_choice);
3019
+ if (selectionPolicy.kind === "forced_image" && imageToolIndex === void 0) {
3020
+ invalid5("tool_choice");
3021
+ }
3022
+ if (selectionPolicy.kind === "forced_other" && !otherTools.some((identity) => forcedOtherMatches(selectionPolicy, identity))) {
3023
+ invalid5("tool_choice");
3024
+ }
3025
+ const previousResponseId = parsePreviousResponseId(input.previous_response_id);
3026
+ return Object.freeze({
3027
+ declared: imageToolIndex !== void 0,
3028
+ ...imageToolIndex !== void 0 ? { imageToolIndex } : {},
3029
+ otherToolCount,
3030
+ otherTools: Object.freeze(otherTools),
3031
+ stream,
3032
+ ...previousResponseId !== void 0 ? { previousResponseId } : {},
3033
+ explicitCallIds: parseExplicitCallIds(input.input),
3034
+ selectionPolicy,
3035
+ ...declaration ? { options: declaration } : {}
3036
+ });
3037
+ }
3038
+ function validateSelectedCall(call) {
3039
+ if (!call || typeof call !== "object" || typeof call.prompt !== "string") protocolFailure();
3040
+ if (!call.prompt.trim() || call.prompt.length > 32e3) protocolFailure();
3041
+ if (Object.keys(call).some((key) => key !== "prompt")) {
3042
+ protocolFailure();
3043
+ }
3044
+ }
3045
+ function validateSelectedOtherTool(value) {
3046
+ if (!value || typeof value !== "object" || Array.isArray(value)) protocolFailure();
3047
+ const candidate = value;
3048
+ if (Object.keys(candidate).some((key) => !TOOL_IDENTITY_KEYS.has(key))) protocolFailure();
3049
+ if (!Number.isSafeInteger(value.declarationIndex) || value.declarationIndex < 0 || typeof value.type !== "string" || !value.type.trim() || value.type.length > 128 || value.name !== void 0 && (typeof value.name !== "string" || !value.name.trim() || value.name.length > 128)) {
3050
+ protocolFailure();
3051
+ }
3052
+ }
3053
+ function validateResponsesImageSelection(admission, selection) {
3054
+ if (!selection || typeof selection !== "object" || Array.isArray(selection) || !Array.isArray(selection.imageCalls) || !Array.isArray(selection.otherTools) || Object.keys(selection).some((key) => key !== "imageCalls" && key !== "otherToolCount" && key !== "otherTools")) {
3055
+ protocolFailure();
3056
+ }
3057
+ if (!Number.isSafeInteger(selection.otherToolCount) || selection.otherToolCount < 0 || selection.otherToolCount !== selection.otherTools.length || selection.imageCalls.length + selection.otherToolCount > MAX_SELECTED_TOOL_CALLS) {
3058
+ protocolFailure();
3059
+ }
3060
+ selection.imageCalls.forEach(validateSelectedCall);
3061
+ selection.otherTools.forEach(validateSelectedOtherTool);
3062
+ if (selection.imageCalls.length > 0 && !admission.declared) protocolFailure();
3063
+ for (const selected of selection.otherTools) {
3064
+ if (!admission.otherTools.some((declared) => sameToolIdentity(declared, selected))) {
3065
+ protocolFailure();
3066
+ }
3067
+ }
3068
+ const selectedAny = selection.imageCalls.length + selection.otherToolCount > 0;
3069
+ switch (admission.selectionPolicy.kind) {
3070
+ case "auto":
3071
+ return;
3072
+ case "required":
3073
+ if (!selectedAny) protocolFailure();
3074
+ return;
3075
+ case "none":
3076
+ if (selectedAny) protocolFailure();
3077
+ return;
3078
+ case "forced_image":
3079
+ if (selection.imageCalls.length === 0 || selection.otherToolCount !== 0) protocolFailure();
3080
+ return;
3081
+ case "forced_other": {
3082
+ const forcedOtherPolicy = admission.selectionPolicy;
3083
+ if (selection.imageCalls.length > 0 || selection.otherTools.length === 0 || selection.otherTools.some(
3084
+ (selected) => !forcedOtherMatches(forcedOtherPolicy, selected)
3085
+ )) {
3086
+ protocolFailure();
3087
+ }
3088
+ return;
3089
+ }
3090
+ default:
3091
+ protocolFailure();
3092
+ }
3093
+ }
3094
+
3095
+ // src/image-generation/responses/ResponsesImageGenerationContribution.ts
3096
+ var CALL_ID_PATTERN2 = /^ig_[A-Za-z0-9_-]{16,128}$/;
3097
+ var RESPONSE_ID_PATTERN2 = /^resp_[A-Za-z0-9_-]{1,240}$/;
3098
+ function nonempty(value, max = 512) {
3099
+ return typeof value === "string" && value.trim().length > 0 && value.length <= max;
3100
+ }
3101
+ function cancelled4(signal) {
3102
+ throw new ImageGenerationError("request_cancelled", { cause: signal.reason });
3103
+ }
3104
+ function assertNotAborted(signal) {
3105
+ if (signal.aborted) cancelled4(signal);
3106
+ }
3107
+ function stableError(error, signal) {
3108
+ if (signal.aborted) return new ImageGenerationError("request_cancelled", { cause: signal.reason });
3109
+ return normalizeImageGenerationError(error);
3110
+ }
3111
+ function assertRuntime(runtime) {
3112
+ if (!runtime || typeof runtime !== "object") {
3113
+ throw new ImageGenerationError("invalid_image_request");
3114
+ }
3115
+ if (!nonempty(runtime.tenantId) || !nonempty(runtime.requestId)) {
3116
+ throw new ImageGenerationError("invalid_image_request");
3117
+ }
3118
+ if (!nonempty(runtime.providerId, 128)) {
3119
+ throw new ImageGenerationError("invalid_image_request", { param: "provider" });
3120
+ }
3121
+ if (!nonempty(runtime.imageModel, 128)) {
3122
+ throw new ImageGenerationError("invalid_image_request", { param: "model" });
3123
+ }
3124
+ if (!Number.isSafeInteger(runtime.referenceTtlMs) || runtime.referenceTtlMs <= 0 || !Number.isSafeInteger(runtime.maxOutputBytes) || runtime.maxOutputBytes <= 0 || !Number.isSafeInteger(runtime.maxTotalOutputBytes) || runtime.maxTotalOutputBytes < runtime.maxOutputBytes || !runtime.signal || typeof runtime.signal.aborted !== "boolean" || typeof runtime.signal.addEventListener !== "function" || typeof runtime.signal.removeEventListener !== "function") {
3125
+ throw new ImageGenerationError("invalid_image_request");
3126
+ }
3127
+ for (const hint of [runtime.sessionKey, runtime.preferredAccountId, runtime.preferredAccountGroup]) {
3128
+ if (hint !== void 0 && !nonempty(hint)) {
3129
+ throw new ImageGenerationError("invalid_image_request");
3130
+ }
3131
+ }
3132
+ if (runtime.boundAccountFallbackPolicy !== void 0 && runtime.boundAccountFallbackPolicy !== "strict" && runtime.boundAccountFallbackPolicy !== "pool") {
3133
+ throw new ImageGenerationError("invalid_image_request");
3134
+ }
3135
+ }
3136
+ function linkAbortSignal(signal) {
3137
+ const controller = new AbortController();
3138
+ const onAbort = () => {
3139
+ if (!controller.signal.aborted) controller.abort(signal.reason);
3140
+ };
3141
+ signal.addEventListener("abort", onAbort, { once: true });
3142
+ if (signal.aborted) onAbort();
3143
+ return {
3144
+ controller,
3145
+ removeListener: () => signal.removeEventListener("abort", onAbort)
3146
+ };
3147
+ }
3148
+ function referenceFailure(status) {
3149
+ throw new ImageGenerationError(
3150
+ status === "expired" ? "image_reference_expired" : "image_reference_not_found"
3151
+ );
3152
+ }
3153
+ function assertAssetAgreement(lease) {
3154
+ const asset = lease.value.artifact;
3155
+ const metadata = lease.metadata;
3156
+ if (!asset || asset.independentlyDecodable !== true || asset.mimeType !== metadata.mimeType || asset.byteLength !== metadata.byteLength || asset.width !== metadata.width || asset.height !== metadata.height || asset.artifactId.length === 0) {
3157
+ throw new ImageGenerationError("upstream_protocol_changed");
3158
+ }
3159
+ return asset;
3160
+ }
3161
+ async function releaseQuietly(leases) {
3162
+ for (const lease of [...leases].reverse()) {
3163
+ try {
3164
+ await lease.release();
3165
+ } catch {
3166
+ }
3167
+ }
3168
+ }
3169
+ async function createScope(deps, now, input) {
3170
+ assertRuntime(input.runtime);
3171
+ const { admission, runtime } = input;
3172
+ const linked = linkAbortSignal(runtime.signal);
3173
+ const responseLeases = [];
3174
+ const stateLeases = [];
3175
+ const imageLeases = [];
3176
+ const callIds = [];
3177
+ const seen = /* @__PURE__ */ new Set();
3178
+ const addCallId = (callId) => {
3179
+ if (!seen.has(callId)) {
3180
+ seen.add(callId);
3181
+ callIds.push(callId);
3182
+ }
3183
+ };
3184
+ try {
3185
+ assertNotAborted(linked.controller.signal);
3186
+ if (!admission || typeof admission !== "object") {
3187
+ throw new ImageGenerationError("invalid_image_request");
3188
+ }
3189
+ if (admission.previousResponseId !== void 0) {
3190
+ if (input.authorizedPreviousResponseId !== admission.previousResponseId) {
3191
+ throw new ImageGenerationError("image_reference_not_found");
3192
+ }
3193
+ } else if (input.authorizedPreviousResponseId !== void 0) {
3194
+ throw new ImageGenerationError("invalid_image_request", { param: "previous_response_id" });
3195
+ }
3196
+ if (input.authorizedPreviousResponseKnownEmpty !== void 0 && (typeof input.authorizedPreviousResponseKnownEmpty !== "boolean" || input.authorizedPreviousResponseId === void 0)) {
3197
+ throw new ImageGenerationError("invalid_image_request", { param: "previous_response_id" });
3198
+ }
3199
+ admission.explicitCallIds.forEach(addCallId);
3200
+ if (input.authorizedPreviousResponseId !== void 0) {
3201
+ const response = await deps.stateStore.resolveResponse(
3202
+ runtime.tenantId,
3203
+ input.authorizedPreviousResponseId
3204
+ );
3205
+ if (response.status === "found") responseLeases.push(response.lease);
3206
+ assertNotAborted(linked.controller.signal);
3207
+ if (response.status === "expired") referenceFailure(response.status);
3208
+ if (response.status === "not_found" && input.authorizedPreviousResponseKnownEmpty !== true) {
3209
+ referenceFailure(response.status);
3210
+ }
3211
+ if (response.status === "found") response.lease.callIds.forEach(addCallId);
3212
+ }
3213
+ const resolvedInputs = [];
3214
+ for (const callId of callIds) {
3215
+ assertNotAborted(linked.controller.signal);
3216
+ const state = await deps.stateStore.resolveCall(runtime.tenantId, callId);
3217
+ if (state.status === "found") stateLeases.push(state.lease);
3218
+ assertNotAborted(linked.controller.signal);
3219
+ if (state.status !== "found") referenceFailure(state.status);
3220
+ const image = await deps.referenceStore.resolve(
3221
+ runtime.tenantId,
3222
+ state.lease.binding.referenceId
3223
+ );
3224
+ if (image.status === "found") imageLeases.push(image.lease);
3225
+ assertNotAborted(linked.controller.signal);
3226
+ if (image.status !== "found") referenceFailure(image.status);
3227
+ if (image.lease.metadata.referenceId !== state.lease.binding.referenceId) {
3228
+ throw new ImageGenerationError("upstream_protocol_changed");
3229
+ }
3230
+ resolvedInputs.push({
3231
+ binding: state.lease.binding,
3232
+ asset: assertAssetAgreement(image.lease),
3233
+ stateLease: state.lease,
3234
+ imageLease: image.lease
3235
+ });
3236
+ }
3237
+ assertNotAborted(linked.controller.signal);
3238
+ return new RequestScope(
3239
+ deps,
3240
+ now,
3241
+ input,
3242
+ resolvedInputs,
3243
+ responseLeases,
3244
+ linked.controller,
3245
+ linked.removeListener
3246
+ );
3247
+ } catch (error) {
3248
+ await releaseQuietly([...imageLeases, ...stateLeases, ...responseLeases]);
3249
+ linked.removeListener();
3250
+ throw stableError(error, linked.controller.signal);
3251
+ }
3252
+ }
3253
+ var RequestScope = class {
3254
+ #deps;
3255
+ #now;
3256
+ #admission;
3257
+ #runtime;
3258
+ #resolvedInputs;
3259
+ #responseLeases;
3260
+ #pendingBindings = [];
3261
+ #newReferenceIds = /* @__PURE__ */ new Set();
3262
+ #newReferenceLeases = [];
3263
+ #allocatedCallIds = /* @__PURE__ */ new Set();
3264
+ #scopeController;
3265
+ #removeRuntimeAbortListener;
3266
+ #usedBytes = 0;
3267
+ #lastOutputIndex = -1;
3268
+ #lastSequenceNumber = -1;
3269
+ #state = "open";
3270
+ #disposeRequested = false;
3271
+ #executionDone;
3272
+ #resolveExecutionDone;
3273
+ #activeExecutionIterator;
3274
+ #activeIteratorClosePromise;
3275
+ #commitResponseId;
3276
+ #commitPromise;
3277
+ #committedResponseId;
3278
+ #disposePromise;
3279
+ constructor(deps, now, input, resolvedInputs, responseLeases, scopeController, removeRuntimeAbortListener) {
3280
+ this.#deps = deps;
3281
+ this.#now = now;
3282
+ this.#admission = input.admission;
3283
+ this.#runtime = input.runtime;
3284
+ this.#resolvedInputs = Object.freeze([...resolvedInputs]);
3285
+ this.#responseLeases = Object.freeze([...responseLeases]);
3286
+ this.#scopeController = scopeController;
3287
+ this.#removeRuntimeAbortListener = removeRuntimeAbortListener;
3288
+ for (const item of resolvedInputs) this.#allocatedCallIds.add(item.binding.callId);
3289
+ }
3290
+ executeSelectedCall(call, allocator) {
3291
+ const generator = this.#executeSelectedCall(call, allocator);
3292
+ let tracked;
3293
+ const clearIfCurrent = () => {
3294
+ if (this.#activeExecutionIterator === tracked) {
3295
+ this.#activeExecutionIterator = void 0;
3296
+ this.#activeIteratorClosePromise = void 0;
3297
+ }
3298
+ };
3299
+ tracked = {
3300
+ next: async () => {
3301
+ if (this.#activeExecutionIterator === void 0 || this.#activeExecutionIterator === tracked) {
3302
+ this.#activeExecutionIterator = tracked;
3303
+ }
3304
+ const result = await generator.next();
3305
+ if (result.done) clearIfCurrent();
3306
+ return result;
3307
+ },
3308
+ return: async () => {
3309
+ try {
3310
+ return await generator.return(void 0);
3311
+ } finally {
3312
+ clearIfCurrent();
3313
+ }
3314
+ },
3315
+ throw: async (error) => {
3316
+ try {
3317
+ return await generator.throw(error);
3318
+ } finally {
3319
+ clearIfCurrent();
3320
+ }
3321
+ }
3322
+ };
3323
+ return Object.freeze({
3324
+ [Symbol.asyncIterator]: () => tracked
3325
+ });
3326
+ }
3327
+ async *#executeSelectedCall(call, allocator) {
3328
+ let callId;
3329
+ let outputIndex;
3330
+ let started = false;
3331
+ let providerIterator;
3332
+ try {
3333
+ this.#beginExecution();
3334
+ started = true;
3335
+ if (!this.#admission.declared || !this.#admission.options) {
3336
+ throw new ImageGenerationError("upstream_protocol_changed");
3337
+ }
3338
+ if (!call || typeof call !== "object" || !nonempty(call.prompt, 32e3)) {
3339
+ throw new ImageGenerationError("upstream_protocol_changed");
3340
+ }
3341
+ if (!allocator || typeof allocator.reserveOutputIndex !== "function" || typeof allocator.nextSequenceNumber !== "function") {
3342
+ throw new ImageGenerationError("image_generation_failed");
3343
+ }
3344
+ assertNotAborted(this.#scopeController.signal);
3345
+ const candidateCallId = (this.#deps.createCallId ?? (() => `ig_${randomUUID4().replaceAll("-", "")}`))();
3346
+ if (!CALL_ID_PATTERN2.test(candidateCallId) || this.#allocatedCallIds.has(candidateCallId)) {
3347
+ throw new ImageGenerationError("image_generation_failed");
3348
+ }
3349
+ callId = candidateCallId;
3350
+ this.#allocatedCallIds.add(callId);
3351
+ outputIndex = allocator.reserveOutputIndex();
3352
+ if (!Number.isSafeInteger(outputIndex) || outputIndex < 0 || outputIndex <= this.#lastOutputIndex) {
3353
+ throw new ImageGenerationError("image_generation_failed");
3354
+ }
3355
+ this.#lastOutputIndex = outputIndex;
3356
+ const request2 = this.#buildRequest(call);
3357
+ const context = {
3358
+ requestId: this.#runtime.requestId,
3359
+ tenantId: this.#runtime.tenantId,
3360
+ signal: this.#scopeController.signal,
3361
+ ...this.#runtime.sessionKey !== void 0 ? { sessionKey: this.#runtime.sessionKey } : {},
3362
+ ...this.#runtime.preferredAccountId !== void 0 ? { preferredAccountId: this.#runtime.preferredAccountId } : {},
3363
+ ...this.#runtime.preferredAccountGroup !== void 0 ? { preferredAccountGroup: this.#runtime.preferredAccountGroup } : {},
3364
+ ...this.#runtime.boundAccountFallbackPolicy !== void 0 ? { boundAccountFallbackPolicy: this.#runtime.boundAccountFallbackPolicy } : {}
3365
+ };
3366
+ const iterator = this.#deps.orchestrator.run(request2, context, {
3367
+ providerId: this.#runtime.providerId,
3368
+ retention: { enabled: true, ttlMs: this.#runtime.referenceTtlMs },
3369
+ requireResponsesTool: true
3370
+ })[Symbol.asyncIterator]();
3371
+ providerIterator = iterator;
3372
+ while (true) {
3373
+ const next = await iterator.next();
3374
+ if (!next.done && next.value.type === "completed" && next.value.images.length === 1 && next.value.references?.length === 1) {
3375
+ this.#newReferenceIds.add(next.value.references[0].referenceId);
3376
+ }
3377
+ if (next.done) throw new ImageGenerationError("upstream_protocol_changed");
3378
+ const event = next.value;
3379
+ assertNotAborted(this.#scopeController.signal);
3380
+ if (event.type === "accepted") {
3381
+ yield Object.freeze({
3382
+ kind: "started",
3383
+ outputIndex,
3384
+ item: Object.freeze({
3385
+ id: callId,
3386
+ type: "image_generation_call",
3387
+ status: "in_progress"
3388
+ })
3389
+ });
3390
+ continue;
3391
+ }
3392
+ if (event.type === "partial_image") {
3393
+ const sequence = allocator.nextSequenceNumber();
3394
+ if (!Number.isSafeInteger(sequence) || sequence < 0 || sequence <= this.#lastSequenceNumber) {
3395
+ throw new ImageGenerationError("image_generation_failed");
3396
+ }
3397
+ this.#lastSequenceNumber = sequence;
3398
+ const partial = await this.#readBase64(event.image.artifact);
3399
+ assertNotAborted(this.#scopeController.signal);
3400
+ yield Object.freeze({
3401
+ type: "response.image_generation_call.partial_image",
3402
+ output_index: outputIndex,
3403
+ item_id: callId,
3404
+ sequence_number: sequence,
3405
+ partial_image_index: event.partialImageIndex,
3406
+ partial_image_b64: partial
3407
+ });
3408
+ continue;
3409
+ }
3410
+ if (event.type === "failed") {
3411
+ yield Object.freeze({
3412
+ kind: "failed",
3413
+ outputIndex,
3414
+ callId,
3415
+ error: serializeImageGenerationError(imageGenerationErrorFromPublic(event.error))
3416
+ });
3417
+ return;
3418
+ }
3419
+ if (event.images.length !== 1 || !event.references || event.references.length !== 1) {
3420
+ throw new ImageGenerationError("upstream_protocol_changed");
3421
+ }
3422
+ const image = event.images[0];
3423
+ const reference = event.references[0];
3424
+ const retained = await this.#deps.referenceStore.resolve(
3425
+ this.#runtime.tenantId,
3426
+ reference.referenceId
3427
+ );
3428
+ if (retained.status !== "found") throw new ImageGenerationError("upstream_protocol_changed");
3429
+ this.#newReferenceLeases.push(retained.lease);
3430
+ const retainedAsset = assertAssetAgreement(retained.lease);
3431
+ if (retained.lease.metadata.referenceId !== reference.referenceId || retainedAsset.artifactId !== image.artifact.artifactId || reference.mimeType !== image.artifact.mimeType || reference.byteLength !== image.artifact.byteLength || reference.width !== image.artifact.width || reference.height !== image.artifact.height) {
3432
+ throw new ImageGenerationError("upstream_protocol_changed");
3433
+ }
3434
+ const result = await this.#readBase64(image.artifact);
3435
+ assertNotAborted(this.#scopeController.signal);
3436
+ const binding = Object.freeze({
3437
+ callId,
3438
+ referenceId: reference.referenceId,
3439
+ expiresAt: reference.expiresAt
3440
+ });
3441
+ this.#pendingBindings.push(binding);
3442
+ yield Object.freeze({
3443
+ kind: "completed",
3444
+ outputIndex,
3445
+ item: Object.freeze({
3446
+ id: callId,
3447
+ type: "image_generation_call",
3448
+ status: "completed",
3449
+ result,
3450
+ ...image.revisedPrompt !== void 0 ? { revised_prompt: image.revisedPrompt } : {}
3451
+ })
3452
+ });
3453
+ return;
3454
+ }
3455
+ } catch (error) {
3456
+ const safe = stableError(error, this.#scopeController.signal);
3457
+ yield Object.freeze({
3458
+ kind: "failed",
3459
+ ...outputIndex !== void 0 ? { outputIndex } : {},
3460
+ ...callId !== void 0 ? { callId } : {},
3461
+ error: serializeImageGenerationError(safe)
3462
+ });
3463
+ } finally {
3464
+ try {
3465
+ await providerIterator?.return?.();
3466
+ } catch {
3467
+ }
3468
+ if (started) this.#finishExecution();
3469
+ }
3470
+ }
3471
+ commit(responseId) {
3472
+ if (!RESPONSE_ID_PATTERN2.test(responseId)) return this.#commitFailure();
3473
+ if (this.#state === "committing") {
3474
+ return this.#commitResponseId === responseId ? this.#commitPromise : this.#commitFailure();
3475
+ }
3476
+ if (this.#state === "committed") {
3477
+ return this.#committedResponseId === responseId ? this.#commitPromise ?? Promise.resolve() : this.#commitFailure();
3478
+ }
3479
+ if (this.#state !== "open" || this.#disposeRequested) return this.#commitFailure();
3480
+ try {
3481
+ assertNotAborted(this.#scopeController.signal);
3482
+ } catch (error) {
3483
+ return Promise.reject(error);
3484
+ }
3485
+ this.#state = "committing";
3486
+ this.#commitResponseId = responseId;
3487
+ this.#commitPromise = Promise.resolve().then(() => this.#performCommit(responseId));
3488
+ return this.#commitPromise;
3489
+ }
3490
+ async #performCommit(responseId) {
3491
+ const bindings = [...this.#resolvedInputs.map((item) => item.binding), ...this.#pendingBindings];
3492
+ try {
3493
+ const evicted = await this.#deps.stateStore.commit({
3494
+ tenantId: this.#runtime.tenantId,
3495
+ responseId,
3496
+ bindings,
3497
+ responseExpiresAt: this.#now() + this.#runtime.referenceTtlMs
3498
+ });
3499
+ this.#committedResponseId = responseId;
3500
+ this.#state = "committed";
3501
+ for (const binding of evicted) {
3502
+ try {
3503
+ await this.#deps.referenceStore.delete(this.#runtime.tenantId, binding.referenceId);
3504
+ } catch {
3505
+ }
3506
+ }
3507
+ } catch (error) {
3508
+ if (this.#state === "committing") this.#state = "open";
3509
+ this.#commitResponseId = void 0;
3510
+ throw stableError(error, this.#scopeController.signal);
3511
+ }
3512
+ }
3513
+ dispose() {
3514
+ if (this.#disposePromise) return this.#disposePromise;
3515
+ this.#disposeRequested = true;
3516
+ this.#disposePromise = this.#performDispose();
3517
+ return this.#disposePromise;
3518
+ }
3519
+ async waitForIdle() {
3520
+ const operations = [this.#executionDone, this.#commitPromise].filter((value) => value !== void 0);
3521
+ await Promise.allSettled(operations);
3522
+ }
3523
+ #buildRequest(call) {
3524
+ const options = this.#admission.options;
3525
+ const common = {
3526
+ model: this.#runtime.imageModel,
3527
+ prompt: call.prompt,
3528
+ n: 1,
3529
+ quality: options.quality,
3530
+ size: options.size,
3531
+ background: options.background,
3532
+ outputFormat: options.outputFormat,
3533
+ ...options.outputCompression !== void 0 ? { outputCompression: options.outputCompression } : {},
3534
+ moderation: "auto",
3535
+ stream: options.partialImages > 0,
3536
+ partialImages: options.partialImages
3537
+ };
3538
+ const action = options.action === "auto" ? this.#resolvedInputs.length > 0 ? "edit" : "generate" : options.action;
3539
+ if (action === "generate") return { action, ...common };
3540
+ if (this.#resolvedInputs.length === 0) {
3541
+ throw new ImageGenerationError("invalid_image_request", { param: "input" });
3542
+ }
3543
+ return { action, ...common, images: this.#resolvedInputs.map((item) => item.asset) };
3544
+ }
3545
+ async #readBase64(asset) {
3546
+ assertNotAborted(this.#scopeController.signal);
3547
+ if (!Number.isSafeInteger(asset.byteLength) || asset.byteLength <= 0 || asset.byteLength > this.#runtime.maxOutputBytes || this.#usedBytes > this.#runtime.maxTotalOutputBytes - asset.byteLength) {
3548
+ throw new ImageGenerationError("image_too_large");
3549
+ }
3550
+ this.#usedBytes += asset.byteLength;
3551
+ let bytes;
3552
+ try {
3553
+ bytes = await readImageAssetBytes(
3554
+ asset,
3555
+ this.#runtime.maxOutputBytes,
3556
+ this.#scopeController.signal
3557
+ );
3558
+ } catch (error) {
3559
+ if (this.#scopeController.signal.aborted) cancelled4(this.#scopeController.signal);
3560
+ if (error instanceof RangeError) {
3561
+ throw new ImageGenerationError("upstream_protocol_changed");
3562
+ }
3563
+ throw error;
3564
+ }
3565
+ assertNotAborted(this.#scopeController.signal);
3566
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("base64");
3567
+ }
3568
+ #beginExecution() {
3569
+ if (this.#state !== "open" || this.#disposeRequested) {
3570
+ throw new ImageGenerationError("invalid_image_request");
3571
+ }
3572
+ this.#state = "executing";
3573
+ this.#executionDone = new Promise((resolve2) => {
3574
+ this.#resolveExecutionDone = resolve2;
3575
+ });
3576
+ }
3577
+ #finishExecution() {
3578
+ if (this.#state === "executing") this.#state = "open";
3579
+ this.#resolveExecutionDone?.();
3580
+ this.#resolveExecutionDone = void 0;
3581
+ }
3582
+ #commitFailure() {
3583
+ return Promise.reject(new ImageGenerationError("image_generation_failed"));
3584
+ }
3585
+ #closeActiveIterator() {
3586
+ if (this.#activeIteratorClosePromise) return this.#activeIteratorClosePromise;
3587
+ const iterator = this.#activeExecutionIterator;
3588
+ if (!iterator?.return) return Promise.resolve();
3589
+ this.#activeIteratorClosePromise = Promise.resolve(iterator.return()).then(
3590
+ () => void 0,
3591
+ () => void 0
3592
+ );
3593
+ return this.#activeIteratorClosePromise;
3594
+ }
3595
+ async #performDispose() {
3596
+ if (this.#state === "executing" && !this.#scopeController.signal.aborted) {
3597
+ this.#scopeController.abort(new Error("Responses image request scope disposed."));
3598
+ }
3599
+ const executionDone = this.#executionDone;
3600
+ await Promise.allSettled([
3601
+ this.#closeActiveIterator(),
3602
+ ...executionDone ? [executionDone] : []
3603
+ ]);
3604
+ if (this.#commitPromise) {
3605
+ await this.#commitPromise.catch(() => void 0);
3606
+ }
3607
+ if (this.#committedResponseId === void 0) {
3608
+ for (const referenceId of this.#newReferenceIds) {
3609
+ try {
3610
+ await this.#deps.referenceStore.delete(this.#runtime.tenantId, referenceId);
3611
+ } catch {
3612
+ }
3613
+ }
3614
+ }
3615
+ await releaseQuietly([
3616
+ ...this.#newReferenceLeases,
3617
+ ...this.#resolvedInputs.map((item) => item.imageLease),
3618
+ ...this.#resolvedInputs.map((item) => item.stateLease),
3619
+ ...this.#responseLeases
3620
+ ]);
3621
+ if (!this.#scopeController.signal.aborted) {
3622
+ this.#scopeController.abort(new Error("Responses image request scope disposed."));
3623
+ }
3624
+ this.#removeRuntimeAbortListener();
3625
+ this.#state = "disposed";
3626
+ }
3627
+ };
3628
+ function createResponsesImageGenerationContribution(deps) {
3629
+ if (!deps || typeof deps !== "object" || !deps.orchestrator || !deps.stateStore || !deps.referenceStore || deps.createCallId !== void 0 && typeof deps.createCallId !== "function" || deps.now !== void 0 && typeof deps.now !== "function") {
3630
+ throw new TypeError("Responses image generation requires orchestrator and state/reference stores.");
3631
+ }
3632
+ const now = deps.now ?? Date.now;
3633
+ return Object.freeze({
3634
+ toolType: "image_generation",
3635
+ inspectRequest: inspectResponsesImageRequest,
3636
+ validateSelection: validateResponsesImageSelection,
3637
+ createRequestScope: (input) => createScope(deps, now, input)
3638
+ });
3639
+ }
3640
+
3641
+ // src/image-generation/responses/ResponsesImageStateStore.ts
3642
+ var CALL_ID_PATTERN3 = /^ig_[A-Za-z0-9_-]{16,128}$/;
3643
+ var RESPONSE_ID_PATTERN3 = /^resp_[A-Za-z0-9_-]{1,240}$/;
3644
+ function positiveInteger(value, name) {
3645
+ if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be positive.`);
3646
+ return value;
3647
+ }
3648
+ function safeTenant(value) {
3649
+ return typeof value === "string" && value.trim().length > 0 && value.length <= 512;
3650
+ }
3651
+ function sameBinding(a, b) {
3652
+ return a.callId === b.callId && a.referenceId === b.referenceId && a.expiresAt === b.expiresAt;
3653
+ }
3654
+ function sameIds(a, b) {
3655
+ return a.length === b.length && a.every((value, index) => value === b[index]);
3656
+ }
3657
+ var InMemoryResponsesImageStateStore = class {
3658
+ #calls = /* @__PURE__ */ new Map();
3659
+ #responses = /* @__PURE__ */ new Map();
3660
+ #tombstones = /* @__PURE__ */ new Map();
3661
+ #maxCalls;
3662
+ #maxResponses;
3663
+ #maxTombstones;
3664
+ #tombstoneTtlMs;
3665
+ #now;
3666
+ constructor(options = {}) {
3667
+ this.#maxCalls = positiveInteger(options.maxCalls ?? 1e4, "maxCalls");
3668
+ this.#maxResponses = positiveInteger(options.maxResponses ?? 1e4, "maxResponses");
3669
+ this.#maxTombstones = positiveInteger(options.maxTombstones ?? 2e3, "maxTombstones");
3670
+ this.#tombstoneTtlMs = positiveInteger(
3671
+ options.tombstoneTtlMs ?? 60 * 6e4,
3672
+ "tombstoneTtlMs"
3673
+ );
3674
+ this.#now = options.now ?? Date.now;
3675
+ }
3676
+ async commit(input) {
3677
+ const now = this.#now();
3678
+ this.#pruneTombstones(now);
3679
+ this.#assertCommit(input, now);
3680
+ const unique = new Set(input.bindings.map((binding) => binding.callId));
3681
+ if (unique.size !== input.bindings.length) {
3682
+ throw new ImageGenerationError("image_generation_failed");
3683
+ }
3684
+ for (const binding of input.bindings) {
3685
+ const current = this.#calls.get(binding.callId);
3686
+ if (current && (current.deleted || current.tenantId !== input.tenantId || !sameBinding(current.binding, binding))) {
3687
+ throw new ImageGenerationError("image_generation_failed");
3688
+ }
3689
+ }
3690
+ const currentResponse = this.#responses.get(input.responseId);
3691
+ const callIds = Object.freeze(input.bindings.map((binding) => binding.callId));
3692
+ if (currentResponse && (currentResponse.deleted || currentResponse.tenantId !== input.tenantId || currentResponse.expiresAt !== input.responseExpiresAt || !sameIds(currentResponse.callIds, callIds))) {
3693
+ throw new ImageGenerationError("image_generation_failed");
3694
+ }
3695
+ const protectedCalls = new Set(callIds);
3696
+ const additionalCalls = input.bindings.filter((binding) => !this.#calls.has(binding.callId)).length;
3697
+ const unleasedCalls = [...this.#calls.entries()].filter(
3698
+ ([id, entry]) => entry.activeLeases === 0 && !protectedCalls.has(id)
3699
+ );
3700
+ const evictableCalls = [
3701
+ ...unleasedCalls.filter(([, entry]) => entry.binding.expiresAt <= now),
3702
+ ...unleasedCalls.filter(([, entry]) => entry.binding.expiresAt > now)
3703
+ ];
3704
+ const callsToRemove = Math.max(0, this.#calls.size + additionalCalls - this.#maxCalls);
3705
+ if (evictableCalls.length < callsToRemove) {
3706
+ throw new ImageGenerationError("image_generation_failed");
3707
+ }
3708
+ const additionalResponse = currentResponse ? 0 : 1;
3709
+ const unleasedResponses = [...this.#responses.entries()].filter(
3710
+ ([id, entry]) => entry.activeLeases === 0 && id !== input.responseId
3711
+ );
3712
+ const evictableResponses = [
3713
+ ...unleasedResponses.filter(([, entry]) => entry.expiresAt <= now),
3714
+ ...unleasedResponses.filter(([, entry]) => entry.expiresAt > now)
3715
+ ];
3716
+ const responsesToRemove = Math.max(
3717
+ 0,
3718
+ this.#responses.size + additionalResponse - this.#maxResponses
3719
+ );
3720
+ if (evictableResponses.length < responsesToRemove) {
3721
+ throw new ImageGenerationError("image_generation_failed");
3722
+ }
3723
+ const evicted = [];
3724
+ for (const [id, entry] of evictableCalls.slice(0, callsToRemove)) {
3725
+ if (entry.binding.expiresAt <= now) {
3726
+ this.#rememberTombstone("call", entry.tenantId, id, now);
3727
+ }
3728
+ this.#calls.delete(id);
3729
+ evicted.push(entry.binding);
3730
+ }
3731
+ for (const [id, entry] of evictableResponses.slice(0, responsesToRemove)) {
3732
+ if (entry.expiresAt <= now) {
3733
+ this.#rememberTombstone("response", entry.tenantId, id, now);
3734
+ }
3735
+ this.#responses.delete(id);
3736
+ }
3737
+ for (const binding of input.bindings) {
3738
+ const current = this.#calls.get(binding.callId);
3739
+ if (current) {
3740
+ this.#touch(this.#calls, binding.callId, current);
3741
+ } else {
3742
+ this.#calls.set(binding.callId, {
3743
+ tenantId: input.tenantId,
3744
+ binding: Object.freeze({ ...binding }),
3745
+ activeLeases: 0,
3746
+ deleted: false
3747
+ });
3748
+ }
3749
+ }
3750
+ if (currentResponse) {
3751
+ this.#touch(this.#responses, input.responseId, currentResponse);
3752
+ } else {
3753
+ this.#responses.set(input.responseId, {
3754
+ tenantId: input.tenantId,
3755
+ responseId: input.responseId,
3756
+ callIds,
3757
+ expiresAt: input.responseExpiresAt,
3758
+ activeLeases: 0,
3759
+ deleted: false
3760
+ });
3761
+ }
3762
+ return Object.freeze(evicted);
3763
+ }
3764
+ async resolveCall(tenantId, callId) {
3765
+ if (!safeTenant(tenantId) || !CALL_ID_PATTERN3.test(callId)) return { status: "not_found" };
3766
+ const now = this.#now();
3767
+ this.#pruneTombstones(now);
3768
+ const entry = this.#calls.get(callId);
3769
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) {
3770
+ return this.#hasTombstone("call", tenantId, callId) ? { status: "expired" } : { status: "not_found" };
3771
+ }
3772
+ if (entry.binding.expiresAt <= now) {
3773
+ this.#rememberTombstone("call", tenantId, callId, now);
3774
+ return { status: "expired" };
3775
+ }
3776
+ entry.activeLeases += 1;
3777
+ this.#touch(this.#calls, callId, entry);
3778
+ let released = false;
3779
+ return {
3780
+ status: "found",
3781
+ lease: {
3782
+ binding: entry.binding,
3783
+ async release() {
3784
+ if (released) return;
3785
+ released = true;
3786
+ entry.activeLeases = Math.max(0, entry.activeLeases - 1);
3787
+ }
3788
+ }
3789
+ };
3790
+ }
3791
+ async resolveResponse(tenantId, responseId) {
3792
+ if (!safeTenant(tenantId) || !RESPONSE_ID_PATTERN3.test(responseId)) return { status: "not_found" };
3793
+ const now = this.#now();
3794
+ this.#pruneTombstones(now);
3795
+ const entry = this.#responses.get(responseId);
3796
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) {
3797
+ return this.#hasTombstone("response", tenantId, responseId) ? { status: "expired" } : { status: "not_found" };
3798
+ }
3799
+ if (entry.expiresAt <= now) {
3800
+ this.#rememberTombstone("response", tenantId, responseId, now);
3801
+ return { status: "expired" };
3802
+ }
3803
+ entry.activeLeases += 1;
3804
+ this.#touch(this.#responses, responseId, entry);
3805
+ let released = false;
3806
+ return {
3807
+ status: "found",
3808
+ lease: {
3809
+ responseId,
3810
+ callIds: entry.callIds,
3811
+ expiresAt: entry.expiresAt,
3812
+ async release() {
3813
+ if (released) return;
3814
+ released = true;
3815
+ entry.activeLeases = Math.max(0, entry.activeLeases - 1);
3816
+ }
3817
+ }
3818
+ };
3819
+ }
3820
+ async deleteCall(tenantId, callId) {
3821
+ const entry = this.#calls.get(callId);
3822
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) return void 0;
3823
+ entry.deleted = true;
3824
+ if (entry.activeLeases === 0) this.#calls.delete(callId);
3825
+ return entry.binding;
3826
+ }
3827
+ async deleteResponse(tenantId, responseId) {
3828
+ const entry = this.#responses.get(responseId);
3829
+ if (!entry || entry.tenantId !== tenantId || entry.deleted) return false;
3830
+ entry.deleted = true;
3831
+ if (entry.activeLeases === 0) this.#responses.delete(responseId);
3832
+ return true;
3833
+ }
3834
+ async cleanup(now = this.#now()) {
3835
+ if (!Number.isFinite(now)) throw new RangeError("cleanup time must be finite.");
3836
+ this.#pruneTombstones(now);
3837
+ const removed = [];
3838
+ for (const [callId, entry] of this.#calls) {
3839
+ const expired = entry.binding.expiresAt <= now;
3840
+ if (expired) this.#rememberTombstone("call", entry.tenantId, callId, now);
3841
+ if ((expired || entry.deleted) && entry.activeLeases === 0) {
3842
+ this.#calls.delete(callId);
3843
+ removed.push(entry.binding);
3844
+ }
3845
+ }
3846
+ for (const [responseId, entry] of this.#responses) {
3847
+ const expired = entry.expiresAt <= now;
3848
+ if (expired) this.#rememberTombstone("response", entry.tenantId, responseId, now);
3849
+ if ((expired || entry.deleted) && entry.activeLeases === 0) {
3850
+ this.#responses.delete(responseId);
3851
+ }
3852
+ }
3853
+ return Object.freeze(removed);
3854
+ }
3855
+ #assertCommit(input, now) {
3856
+ if (!safeTenant(input.tenantId)) throw new ImageGenerationError("image_generation_failed");
3857
+ if (!RESPONSE_ID_PATTERN3.test(input.responseId)) {
3858
+ throw new ImageGenerationError("image_generation_failed");
3859
+ }
3860
+ if (!Number.isSafeInteger(input.responseExpiresAt) || input.responseExpiresAt <= now) {
3861
+ throw new ImageGenerationError("image_generation_failed");
3862
+ }
3863
+ if (!Array.isArray(input.bindings)) throw new ImageGenerationError("image_generation_failed");
3864
+ for (const binding of input.bindings) {
3865
+ if (!binding || !CALL_ID_PATTERN3.test(binding.callId) || typeof binding.referenceId !== "string" || binding.referenceId.length === 0 || !Number.isSafeInteger(binding.expiresAt) || binding.expiresAt <= now) {
3866
+ throw new ImageGenerationError("image_generation_failed");
3867
+ }
3868
+ }
3869
+ }
3870
+ #rememberTombstone(kind, tenantId, id, now) {
3871
+ const key = `${kind}\0${tenantId}\0${id}`;
3872
+ this.#tombstones.delete(key);
3873
+ this.#tombstones.set(key, now + this.#tombstoneTtlMs);
3874
+ while (this.#tombstones.size > this.#maxTombstones) {
3875
+ const oldest = this.#tombstones.keys().next().value;
3876
+ if (oldest === void 0) break;
3877
+ this.#tombstones.delete(oldest);
3878
+ }
3879
+ }
3880
+ #hasTombstone(kind, tenantId, id) {
3881
+ return this.#tombstones.has(`${kind}\0${tenantId}\0${id}`);
3882
+ }
3883
+ #pruneTombstones(now) {
3884
+ for (const [key, expiresAt] of this.#tombstones) {
3885
+ if (expiresAt <= now) this.#tombstones.delete(key);
3886
+ }
3887
+ }
3888
+ #touch(map, key, value) {
3889
+ map.delete(key);
3890
+ map.set(key, value);
3891
+ }
3892
+ };
3893
+
3894
+ export {
3895
+ resolveImageCapabilities,
3896
+ ImageGenerationError,
3897
+ isImageGenerationError,
3898
+ serializeImageGenerationError,
3899
+ normalizeImageGenerationError,
3900
+ imageGenerationErrorFromPublic,
3901
+ InMemoryImageAsset,
3902
+ readImageAssetBytes,
3903
+ InMemoryImageReferenceStore,
3904
+ emitImageTelemetry,
3905
+ ImageOrchestrator,
3906
+ ImageProviderRegistry,
3907
+ IMAGE_REQUEST_DIRECTORY_MARKER_NAME,
3908
+ IMAGE_REQUEST_DIRECTORY_MARKER_CONTENT,
3909
+ ImageRequestResourceScope,
3910
+ createImageRequestResourceScope,
3911
+ DEFAULT_IMAGE_API_LIMITS,
3912
+ assertFiniteImageApiLimits,
3913
+ createImageApiContributions,
3914
+ createSafeRemoteImageResolver,
3915
+ inspectResponsesImageRequest,
3916
+ validateResponsesImageSelection,
3917
+ createResponsesImageGenerationContribution,
3918
+ InMemoryResponsesImageStateStore
3919
+ };