@fungi.computer/darkroom 0.3.1 → 0.4.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.
package/README.md CHANGED
@@ -162,12 +162,26 @@ representations and do not decode as these outcomes.
162
162
 
163
163
  Provider facts use Agar's canonical Media grammar: an allowlisted error category
164
164
  and bounded numeric code, or `result_download` and a native HTTP status. The
165
- durable owner retains those facts through interruption and replay, including
166
- safety refusals, without retaining the original error message or cause in the
167
- failure record. A terminal provider failure never redispatches the paid attempt.
168
- The fresh pre-release schema replaces the safety-only refusal table; old preview
169
- stores require an explicit host reset, not an automatic migration or dual
170
- reader.
165
+ provider boundary classifies code `2021` as the optional, closed
166
+ `provider.reason: "credits_exhausted"`. On 2026-10-27, a real GPT Image 2 call
167
+ from the Prints UI returned
168
+ `AiGatewayError: 2021: Insufficient AI Gateway credits`. The original evidence
169
+ is `~/Botanical-cto/receipts/prints-e2e/REPORT.md:51` ("Follow-up 2"; no charge
170
+ recorded at line 53) and
171
+ `~/Botanical-cto/receipts/prints-e2e/gwfix/results.json` (02:01 local; GPT Image
172
+ 2 call at `2026-10-27T01:01:04.296Z`). The error object contained only
173
+ `name: "AiGatewayError"` and `message: "2021: Insufficient AI Gateway credits"`;
174
+ no HTTP status was recorded. This is observed provider wording from a real UI
175
+ call, not a published guarantee that every Cloudflare `2021` means a zero
176
+ balance. Apps consume the typed reason without interpreting provider numbers or
177
+ public error prose, and should direct the owner to check AI Gateway credits and
178
+ billing rather than infer the account balance. It is separate from the host's
179
+ `INSUFFICIENT_CREDIT` Team billing refusal. The durable owner retains those
180
+ facts through interruption and replay, including safety refusals, without
181
+ retaining the original error message or cause in the failure record. A terminal
182
+ provider failure never redispatches the paid attempt. The fresh pre-release
183
+ schema replaces the safety-only refusal table; old preview stores require an
184
+ explicit host reset, not an automatic migration or dual reader.
171
185
 
172
186
  See the [generated API](docs/api/README.md) for signatures and host ports. The
173
187
  Cloudflare entry needs the native Workers type declarations in a TypeScript
@@ -177,8 +191,8 @@ host. The other entries use ordinary platform web types.
177
191
 
178
192
  For Workers AI, use `createWorkersAiMediaProvider(env.AI)` from `cloudflare` and
179
193
  add an `AI` binding to your host. `createImagesReferenceFitter` prepares bounded
180
- reference images through an Images binding. Your host authorizes each reference
181
- read.
194
+ reference images through an Images binding, using the selected catalog model
195
+ passed to the fitter. Your host authorizes each reference read.
182
196
 
183
197
  `stripPrivateProvenance(bytes)` from `provenance` removes prompt and lineage
184
198
  metadata while preserving the AI marker, print ID and image encoding. It returns
@@ -187,9 +201,20 @@ bytes, MIME type and filename extension. The public XMP format uses
187
201
 
188
202
  ## Limits
189
203
 
190
- References are bounded to four files, 512 pixels per side and 8 MiB per image.
191
- Still-image output and public downloads are bounded to 32 MiB. Catalog models
192
- may impose tighter limits. Video output has its own 64 MiB provider bound.
204
+ References are bounded to four files and 8 MiB per image. Image-model references
205
+ keep a maximum 512-pixel long edge. Video-model frames and references preserve
206
+ their aspect ratio and are fitted by the short edge to 320 pixels, growing
207
+ smaller inputs when necessary; resolved video references must have a short edge
208
+ of at least 320 pixels. Still-image output and public downloads are bounded to
209
+ 32 MiB. Catalog models may impose tighter limits. Video output has its own 64
210
+ MiB provider bound.
211
+
212
+ The 8 MiB reference budget also applies after transformation: extreme panoramas
213
+ can still exceed the encoded-byte budget, even though video references have no
214
+ 512-pixel long-edge cap. Celld Hub reference fitting requires a
215
+ [paired Hub and edge release](../../services/hub-edge/README.md#paired-hub-and-edge-release);
216
+ the linked section includes the draft Fungi 0.7.1 release note and mixed-version
217
+ failure window.
193
218
 
194
219
  Private-provenance export supports PNG, JPEG and WebP. GIF without XMP remains
195
220
  index-only. Signed, malformed and unsupported images return a typed refusal. The
@@ -1,4 +1,211 @@
1
1
  import * as z from "zod";
2
+ /** A labelled estimate for one explicitly described generation, never a receipt. */
3
+ export declare const mediaModelPriceEstimateSchema: z.ZodObject<{
4
+ kind: z.ZodLiteral<"estimate">;
5
+ basis: z.ZodEnum<{
6
+ /** Provider cost before the host's billing policy. */
7
+ readonly "provider-cost": "provider-cost";
8
+ /** Team charge estimate after the host's billing policy. */
9
+ readonly "team-price": "team-price";
10
+ }>;
11
+ costMilliunits: z.ZodNumber;
12
+ settings: z.ZodObject<{
13
+ model: z.ZodString;
14
+ prompt: z.ZodString;
15
+ width: z.ZodNumber;
16
+ height: z.ZodNumber;
17
+ steps: z.ZodOptional<z.ZodNumber>;
18
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
19
+ resolution: z.ZodOptional<z.ZodEnum<{
20
+ readonly p480: "480p";
21
+ readonly p720: "720p";
22
+ readonly p768: "768p";
23
+ readonly p1080: "1080p";
24
+ readonly p4k: "4k";
25
+ }>>;
26
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
27
+ readonly wide: "16:9";
28
+ readonly landscape: "4:3";
29
+ readonly square: "1:1";
30
+ readonly portrait: "3:4";
31
+ readonly tall: "9:16";
32
+ readonly ultrawide: "21:9";
33
+ readonly ultratall: "9:21";
34
+ }>>;
35
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
36
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
37
+ computerId: z.ZodString;
38
+ path: z.ZodString;
39
+ role: z.ZodOptional<z.ZodEnum<{
40
+ readonly first: "first-frame";
41
+ readonly last: "last-frame";
42
+ readonly reference: "reference";
43
+ }>>;
44
+ }, z.core.$strict>>>;
45
+ }, z.core.$strict>;
46
+ }, z.core.$strict>;
47
+ /** Host catalog projection; provider metadata remains owned by the sole catalog. */
48
+ export declare const mediaCatalogModelSchema: z.ZodReadonly<z.ZodObject<{
49
+ id: z.ZodString;
50
+ label: z.ZodString;
51
+ kind: z.ZodEnum<{
52
+ readonly image: "image";
53
+ readonly video: "video";
54
+ }>;
55
+ tier: z.ZodEnum<{
56
+ readonly draft: "draft";
57
+ readonly balanced: "balanced";
58
+ readonly premium: "premium";
59
+ readonly max: "max";
60
+ }>;
61
+ outputFormats: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
62
+ readonly png: "png";
63
+ readonly jpeg: "jpeg";
64
+ readonly webp: "webp";
65
+ }>>>;
66
+ maxPromptLength: z.ZodNumber;
67
+ input: z.ZodLazy<z.ZodReadonly<z.ZodObject<{
68
+ transport: z.ZodEnum<{
69
+ readonly json: "json";
70
+ readonly multipart: "multipart";
71
+ }>;
72
+ dimensions: z.ZodEnum<{
73
+ readonly none: "none";
74
+ readonly own: "own";
75
+ }>;
76
+ maxDimension: z.ZodNullable<z.ZodNumber>;
77
+ maxDimensionDocumented: z.ZodBoolean;
78
+ defaultWidth: z.ZodNumber;
79
+ defaultHeight: z.ZodNumber;
80
+ steps: z.ZodEnum<{
81
+ readonly steps: "steps";
82
+ readonly num_steps: "num_steps";
83
+ readonly fixed: "fixed";
84
+ }>;
85
+ maxSteps: z.ZodNullable<z.ZodNumber>;
86
+ defaultSteps: z.ZodNullable<z.ZodNumber>;
87
+ referenceField: z.ZodNullable<z.ZodString>;
88
+ maxReferences: z.ZodNumber;
89
+ }, z.core.$strict>>>;
90
+ sizes: z.ZodNullable<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
91
+ video: z.ZodNullable<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
92
+ minSeconds: z.ZodNumber;
93
+ maxSeconds: z.ZodNumber;
94
+ defaultSeconds: z.ZodNumber;
95
+ durations: z.ZodReadonly<z.ZodArray<z.ZodNumber>>;
96
+ provider: z.ZodEnum<{
97
+ readonly seedance: "seedance";
98
+ readonly veo: "veo";
99
+ readonly minimax: "minimax";
100
+ }>;
101
+ formats: z.ZodReadonly<z.ZodArray<z.ZodLazy<z.ZodReadonly<z.ZodObject<{
102
+ resolution: z.ZodEnum<{
103
+ readonly p480: "480p";
104
+ readonly p720: "720p";
105
+ readonly p768: "768p";
106
+ readonly p1080: "1080p";
107
+ readonly p4k: "4k";
108
+ }>;
109
+ aspectRatio: z.ZodEnum<{
110
+ readonly wide: "16:9";
111
+ readonly landscape: "4:3";
112
+ readonly square: "1:1";
113
+ readonly portrait: "3:4";
114
+ readonly tall: "9:16";
115
+ readonly ultrawide: "21:9";
116
+ readonly ultratall: "9:21";
117
+ }>;
118
+ width: z.ZodNumber;
119
+ height: z.ZodNumber;
120
+ }, z.core.$strict>>>>>;
121
+ defaultResolution: z.ZodEnum<{
122
+ readonly p480: "480p";
123
+ readonly p720: "720p";
124
+ readonly p768: "768p";
125
+ readonly p1080: "1080p";
126
+ readonly p4k: "4k";
127
+ }>;
128
+ defaultAspectRatio: z.ZodEnum<{
129
+ readonly wide: "16:9";
130
+ readonly landscape: "4:3";
131
+ readonly square: "1:1";
132
+ readonly portrait: "3:4";
133
+ readonly tall: "9:16";
134
+ readonly ultrawide: "21:9";
135
+ readonly ultratall: "9:21";
136
+ }>;
137
+ supportsAudio: z.ZodBoolean;
138
+ defaultGenerateAudio: z.ZodNullable<z.ZodBoolean>;
139
+ referenceRoles: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
140
+ readonly first: "first-frame";
141
+ readonly last: "last-frame";
142
+ readonly reference: "reference";
143
+ }>>>;
144
+ maxReferenceImages: z.ZodNumber;
145
+ referenceResolutions: z.ZodReadonly<z.ZodArray<z.ZodEnum<{
146
+ readonly p480: "480p";
147
+ readonly p720: "720p";
148
+ readonly p768: "768p";
149
+ readonly p1080: "1080p";
150
+ readonly p4k: "4k";
151
+ }>>>;
152
+ prices: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
153
+ resolution: z.ZodEnum<{
154
+ readonly p480: "480p";
155
+ readonly p720: "720p";
156
+ readonly p768: "768p";
157
+ readonly p1080: "1080p";
158
+ readonly p4k: "4k";
159
+ }>;
160
+ silentMilliunitsPerSecond: z.ZodNumber;
161
+ audioMilliunitsPerSecond: z.ZodNumber;
162
+ }, z.core.$strict>>>>;
163
+ }, z.core.$strict>>>>;
164
+ priceEstimate: z.ZodOptional<z.ZodObject<{
165
+ kind: z.ZodLiteral<"estimate">;
166
+ basis: z.ZodEnum<{
167
+ /** Provider cost before the host's billing policy. */
168
+ readonly "provider-cost": "provider-cost";
169
+ /** Team charge estimate after the host's billing policy. */
170
+ readonly "team-price": "team-price";
171
+ }>;
172
+ costMilliunits: z.ZodNumber;
173
+ settings: z.ZodObject<{
174
+ model: z.ZodString;
175
+ prompt: z.ZodString;
176
+ width: z.ZodNumber;
177
+ height: z.ZodNumber;
178
+ steps: z.ZodOptional<z.ZodNumber>;
179
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
180
+ resolution: z.ZodOptional<z.ZodEnum<{
181
+ readonly p480: "480p";
182
+ readonly p720: "720p";
183
+ readonly p768: "768p";
184
+ readonly p1080: "1080p";
185
+ readonly p4k: "4k";
186
+ }>>;
187
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
188
+ readonly wide: "16:9";
189
+ readonly landscape: "4:3";
190
+ readonly square: "1:1";
191
+ readonly portrait: "3:4";
192
+ readonly tall: "9:16";
193
+ readonly ultrawide: "21:9";
194
+ readonly ultratall: "9:21";
195
+ }>>;
196
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
197
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
198
+ computerId: z.ZodString;
199
+ path: z.ZodString;
200
+ role: z.ZodOptional<z.ZodEnum<{
201
+ readonly first: "first-frame";
202
+ readonly last: "last-frame";
203
+ readonly reference: "reference";
204
+ }>>;
205
+ }, z.core.$strict>>>;
206
+ }, z.core.$strict>;
207
+ }, z.core.$strict>>;
208
+ }, z.core.$strict>>;
2
209
  /** Bounded catalog decoded by the App media broker before host filtering. */
3
210
  export declare const mediaModelsSchema: z.ZodLazy<z.ZodArray<z.ZodReadonly<z.ZodObject<{
4
211
  id: z.ZodString;
@@ -116,6 +323,50 @@ export declare const mediaModelsSchema: z.ZodLazy<z.ZodArray<z.ZodReadonly<z.Zod
116
323
  audioMilliunitsPerSecond: z.ZodNumber;
117
324
  }, z.core.$strict>>>>;
118
325
  }, z.core.$strict>>>>;
326
+ priceEstimate: z.ZodOptional<z.ZodObject<{
327
+ kind: z.ZodLiteral<"estimate">;
328
+ basis: z.ZodEnum<{
329
+ /** Provider cost before the host's billing policy. */
330
+ readonly "provider-cost": "provider-cost";
331
+ /** Team charge estimate after the host's billing policy. */
332
+ readonly "team-price": "team-price";
333
+ }>;
334
+ costMilliunits: z.ZodNumber;
335
+ settings: z.ZodObject<{
336
+ model: z.ZodString;
337
+ prompt: z.ZodString;
338
+ width: z.ZodNumber;
339
+ height: z.ZodNumber;
340
+ steps: z.ZodOptional<z.ZodNumber>;
341
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
342
+ resolution: z.ZodOptional<z.ZodEnum<{
343
+ readonly p480: "480p";
344
+ readonly p720: "720p";
345
+ readonly p768: "768p";
346
+ readonly p1080: "1080p";
347
+ readonly p4k: "4k";
348
+ }>>;
349
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
350
+ readonly wide: "16:9";
351
+ readonly landscape: "4:3";
352
+ readonly square: "1:1";
353
+ readonly portrait: "3:4";
354
+ readonly tall: "9:16";
355
+ readonly ultrawide: "21:9";
356
+ readonly ultratall: "9:21";
357
+ }>>;
358
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
359
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
360
+ computerId: z.ZodString;
361
+ path: z.ZodString;
362
+ role: z.ZodOptional<z.ZodEnum<{
363
+ readonly first: "first-frame";
364
+ readonly last: "last-frame";
365
+ readonly reference: "reference";
366
+ }>>;
367
+ }, z.core.$strict>>>;
368
+ }, z.core.$strict>;
369
+ }, z.core.$strict>>;
119
370
  }, z.core.$strict>>>>;
120
371
  /** An absolute output path on the Computer admitted by the App host. */
121
372
  export declare const destinationSchema: z.ZodObject<{
@@ -206,6 +457,8 @@ export declare const appMediaBrokerFailureSchema: z.ZodObject<{
206
457
  readonly INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
207
458
  readonly HOST_FAILURE: "HOST_FAILURE";
208
459
  readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
460
+ readonly MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
461
+ readonly MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
209
462
  readonly SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
210
463
  }>;
211
464
  message: z.ZodString;
@@ -223,13 +476,7 @@ export declare const appMediaBrokerFailureSchema: z.ZodObject<{
223
476
  readonly "destination-write": "destination-write";
224
477
  }>;
225
478
  retryable: z.ZodBoolean;
226
- provider: z.ZodOptional<z.ZodPipe<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodTransform<Readonly<{
227
- category: "InferenceUpstreamError" | "AiInternalError" | "TypeError" | "AbortError" | "TimeoutError" | "Error";
228
- code: number | null;
229
- }> | Readonly<{
230
- category: "result_download";
231
- status: number;
232
- }>, unknown>>>;
479
+ provider: z.ZodOptional<z.ZodType<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown, z.core.$ZodTypeInternals<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown>>>;
233
480
  }, z.core.$strict>;
234
481
  /** Refusal payloads emitted by the App media broker, separate from transport errors. */
235
482
  export declare const appMediaRefusalSchema: z.ZodUnion<readonly [z.ZodObject<{
@@ -260,6 +507,8 @@ export declare const appMediaRefusalSchema: z.ZodUnion<readonly [z.ZodObject<{
260
507
  readonly INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
261
508
  readonly HOST_FAILURE: "HOST_FAILURE";
262
509
  readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
510
+ readonly MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
511
+ readonly MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
263
512
  readonly SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
264
513
  }>;
265
514
  message: z.ZodString;
@@ -277,13 +526,7 @@ export declare const appMediaRefusalSchema: z.ZodUnion<readonly [z.ZodObject<{
277
526
  readonly "destination-write": "destination-write";
278
527
  }>;
279
528
  retryable: z.ZodBoolean;
280
- provider: z.ZodOptional<z.ZodPipe<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodTransform<Readonly<{
281
- category: "InferenceUpstreamError" | "AiInternalError" | "TypeError" | "AbortError" | "TimeoutError" | "Error";
282
- code: number | null;
283
- }> | Readonly<{
284
- category: "result_download";
285
- status: number;
286
- }>, unknown>>>;
529
+ provider: z.ZodOptional<z.ZodType<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown, z.core.$ZodTypeInternals<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown>>>;
287
530
  }, z.core.$strict>]>;
288
531
  /** Model-list payload or an existing broker refusal. */
289
532
  export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy<z.ZodArray<z.ZodReadonly<z.ZodObject<{
@@ -402,6 +645,50 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
402
645
  audioMilliunitsPerSecond: z.ZodNumber;
403
646
  }, z.core.$strict>>>>;
404
647
  }, z.core.$strict>>>>;
648
+ priceEstimate: z.ZodOptional<z.ZodObject<{
649
+ kind: z.ZodLiteral<"estimate">;
650
+ basis: z.ZodEnum<{
651
+ /** Provider cost before the host's billing policy. */
652
+ readonly "provider-cost": "provider-cost";
653
+ /** Team charge estimate after the host's billing policy. */
654
+ readonly "team-price": "team-price";
655
+ }>;
656
+ costMilliunits: z.ZodNumber;
657
+ settings: z.ZodObject<{
658
+ model: z.ZodString;
659
+ prompt: z.ZodString;
660
+ width: z.ZodNumber;
661
+ height: z.ZodNumber;
662
+ steps: z.ZodOptional<z.ZodNumber>;
663
+ durationSeconds: z.ZodOptional<z.ZodNumber>;
664
+ resolution: z.ZodOptional<z.ZodEnum<{
665
+ readonly p480: "480p";
666
+ readonly p720: "720p";
667
+ readonly p768: "768p";
668
+ readonly p1080: "1080p";
669
+ readonly p4k: "4k";
670
+ }>>;
671
+ aspectRatio: z.ZodOptional<z.ZodEnum<{
672
+ readonly wide: "16:9";
673
+ readonly landscape: "4:3";
674
+ readonly square: "1:1";
675
+ readonly portrait: "3:4";
676
+ readonly tall: "9:16";
677
+ readonly ultrawide: "21:9";
678
+ readonly ultratall: "9:21";
679
+ }>>;
680
+ generateAudio: z.ZodOptional<z.ZodBoolean>;
681
+ references: z.ZodOptional<z.ZodArray<z.ZodObject<{
682
+ computerId: z.ZodString;
683
+ path: z.ZodString;
684
+ role: z.ZodOptional<z.ZodEnum<{
685
+ readonly first: "first-frame";
686
+ readonly last: "last-frame";
687
+ readonly reference: "reference";
688
+ }>>;
689
+ }, z.core.$strict>>>;
690
+ }, z.core.$strict>;
691
+ }, z.core.$strict>>;
405
692
  }, z.core.$strict>>>>, z.ZodUnion<readonly [z.ZodObject<{
406
693
  error: z.ZodEnum<{
407
694
  /** The broker refused the input representation or selection. */
@@ -430,6 +717,8 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
430
717
  readonly INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
431
718
  readonly HOST_FAILURE: "HOST_FAILURE";
432
719
  readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
720
+ readonly MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
721
+ readonly MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
433
722
  readonly SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
434
723
  }>;
435
724
  message: z.ZodString;
@@ -447,13 +736,7 @@ export declare const appMediaModelsOutcomeSchema: z.ZodUnion<readonly [z.ZodLazy
447
736
  readonly "destination-write": "destination-write";
448
737
  }>;
449
738
  retryable: z.ZodBoolean;
450
- provider: z.ZodOptional<z.ZodPipe<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodTransform<Readonly<{
451
- category: "InferenceUpstreamError" | "AiInternalError" | "TypeError" | "AbortError" | "TimeoutError" | "Error";
452
- code: number | null;
453
- }> | Readonly<{
454
- category: "result_download";
455
- status: number;
456
- }>, unknown>>>;
739
+ provider: z.ZodOptional<z.ZodType<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown, z.core.$ZodTypeInternals<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown>>>;
457
740
  }, z.core.$strict>]>]>;
458
741
  /** Completed generation, Agar polling result, or an existing broker refusal. */
459
742
  export declare const appMediaGenerationOutcomeSchema: z.ZodUnion<readonly [z.ZodObject<{
@@ -504,6 +787,8 @@ export declare const appMediaGenerationOutcomeSchema: z.ZodUnion<readonly [z.Zod
504
787
  readonly INSUFFICIENT_CREDIT: "INSUFFICIENT_CREDIT";
505
788
  readonly HOST_FAILURE: "HOST_FAILURE";
506
789
  readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
790
+ readonly MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
791
+ readonly MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
507
792
  readonly SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
508
793
  }>;
509
794
  message: z.ZodString;
@@ -521,11 +806,5 @@ export declare const appMediaGenerationOutcomeSchema: z.ZodUnion<readonly [z.Zod
521
806
  readonly "destination-write": "destination-write";
522
807
  }>;
523
808
  retryable: z.ZodBoolean;
524
- provider: z.ZodOptional<z.ZodPipe<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodTransform<Readonly<{
525
- category: "InferenceUpstreamError" | "AiInternalError" | "TypeError" | "AbortError" | "TimeoutError" | "Error";
526
- code: number | null;
527
- }> | Readonly<{
528
- category: "result_download";
529
- status: number;
530
- }>, unknown>>>;
809
+ provider: z.ZodOptional<z.ZodType<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown, z.core.$ZodTypeInternals<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown>>>;
531
810
  }, z.core.$strict>]>]>;
@@ -3,8 +3,33 @@ import { listMediaModels, mediaModelSchema } from "./model-catalog.js";
3
3
  import { mediaResultSchema } from "./media-operation-contract.js";
4
4
  import { pendingSchema } from "@fungi.computer/agar/backend-definition";
5
5
  import { mediaFailureSchema } from "./browser-failure.js";
6
+ import { mediaGenerationSettingsSchema } from "./index.js";
7
+ /** A labelled estimate for one explicitly described generation, never a receipt. */
8
+ export const mediaModelPriceEstimateSchema = z.strictObject({
9
+ /** Quotes are indicative; the admitted generation owns its charge. */
10
+ kind: z.literal("estimate"),
11
+ /** Unmetered hosts pay provider cost; funded hosts apply their billing policy. */
12
+ basis: z.enum({
13
+ /** Provider cost before the host's billing policy. */
14
+ "provider-cost": "provider-cost",
15
+ /** Team charge estimate after the host's billing policy. */
16
+ "team-price": "team-price",
17
+ }),
18
+ /** Milliunits of a cent, at the quoted settings. */
19
+ costMilliunits: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
20
+ /** Defaults, a one-character prompt and no references; varying these changes cost. */
21
+ settings: mediaGenerationSettingsSchema,
22
+ });
23
+ /** Host catalog projection; provider metadata remains owned by the sole catalog. */
24
+ export const mediaCatalogModelSchema = mediaModelSchema
25
+ .unwrap()
26
+ .extend({
27
+ /** Optional host estimate with explicit assumptions and funding basis. */
28
+ priceEstimate: mediaModelPriceEstimateSchema.optional(),
29
+ })
30
+ .readonly();
6
31
  /** Bounded catalog decoded by the App media broker before host filtering. */
7
- export const mediaModelsSchema = z.lazy(() => z.array(mediaModelSchema).max(listMediaModels().length));
32
+ export const mediaModelsSchema = z.lazy(() => z.array(mediaCatalogModelSchema).max(listMediaModels().length));
8
33
  /** An absolute output path on the Computer admitted by the App host. */
9
34
  export const destinationSchema = z.strictObject({
10
35
  /** Computer admitted by the host. */
@@ -29,17 +29,15 @@ export declare const mediaFailureSchema: z.ZodObject<{
29
29
  readonly HOST_FAILURE: "HOST_FAILURE";
30
30
  /** The host refused the selected input. */
31
31
  readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
32
+ /** The inference provider did not complete generation. */
33
+ readonly MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE";
34
+ /** The host confirmed zero-unit customer settlement after provider failure. */
35
+ readonly MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED";
32
36
  /** The model's safety filter refused generation. */
33
37
  readonly SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED";
34
38
  }>;
35
39
  retryable: z.ZodBoolean;
36
- provider: z.ZodOptional<z.ZodPipe<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodTransform<Readonly<{
37
- category: "InferenceUpstreamError" | "AiInternalError" | "TypeError" | "AbortError" | "TimeoutError" | "Error";
38
- code: number | null;
39
- }> | Readonly<{
40
- category: "result_download";
41
- status: number;
42
- }>, unknown>>>;
40
+ provider: z.ZodOptional<z.ZodType<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown, z.core.$ZodTypeInternals<import("@fungi.computer/agar/errors").MediaProviderDiagnostic, unknown>>>;
43
41
  }, z.core.$strict>;
44
42
  /** Sanitized Media failure carried from the authenticated host to Agar. */
45
43
  export type MediaFailure = z.output<typeof mediaFailureSchema>;
@@ -19,6 +19,10 @@ export const mediaFailureSchema = z.strictObject({
19
19
  HOST_FAILURE: "HOST_FAILURE",
20
20
  /** The host refused the selected input. */
21
21
  INVALID_ARGUMENT: "INVALID_ARGUMENT",
22
+ /** The inference provider did not complete generation. */
23
+ MEDIA_PROVIDER_FAILURE: "MEDIA_PROVIDER_FAILURE",
24
+ /** The host confirmed zero-unit customer settlement after provider failure. */
25
+ MEDIA_PROVIDER_FAILURE_UNCHARGED: "MEDIA_PROVIDER_FAILURE_UNCHARGED",
22
26
  /** The model's safety filter refused generation. */
23
27
  SAFETY_FILTER_REFUSED: "SAFETY_FILTER_REFUSED",
24
28
  }),
@@ -27,8 +31,9 @@ export const mediaFailureSchema = z.strictObject({
27
31
  /** Allowlisted provider facts only; the original cause never crosses HTTP. */
28
32
  provider: mediaProviderDiagnosticWireSchema.optional(),
29
33
  });
30
- const decodeJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
31
34
  function parseMediaFailureJson(json) {
35
+ // Schema-only App consumers must not retain the browser's JSON decoder.
36
+ const decodeJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
32
37
  const parsed = mediaFailureSchema.safeParse(Option.getOrUndefined(decodeJson(json)));
33
38
  return parsed.success ? parsed.data : null;
34
39
  }
@@ -1,4 +1,26 @@
1
- import { type MediaAiResult, type MediaGenerationSettings, type MediaProvider, type MediaReferenceBytes } from "../index.js";
1
+ import { type MediaModel, type MediaAiResult, type MediaGenerationSettings, type MediaProvider, type MediaReferenceBytes } from "../index.js";
2
+ /** Quote the catalog defaults through the exact estimator used before dispatch. */
3
+ export declare function estimateMediaModel(model: MediaModel): {
4
+ /** An indicative quote, not an admitted generation receipt. */
5
+ kind: "estimate";
6
+ /** Provider cost before the host's billing policy. */
7
+ basis: "provider-cost";
8
+ /** Catalog defaults, a one-character prompt and no references. */
9
+ settings: Readonly<{
10
+ model: string;
11
+ prompt: string;
12
+ width: number;
13
+ height: number;
14
+ steps?: number;
15
+ durationSeconds?: number;
16
+ resolution?: import("../model-catalog.js").MediaVideoResolution;
17
+ aspectRatio?: import("../model-catalog.js").MediaVideoAspectRatio;
18
+ generateAudio?: boolean;
19
+ references?: readonly import("../index.js").MediaReference[];
20
+ }>;
21
+ /** Estimated provider milliunits of a cent for these exact settings. */
22
+ costMilliunits: number;
23
+ };
2
24
  /** Workers AI results vary by model; the provider decodes them at this boundary. */
3
25
  export type MediaAiPort = {
4
26
  /** Dispatch to the native Workers AI binding. */