@fungi.computer/darkroom 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,9 +113,8 @@ save fails. An ambiguous provider execution is never silently dispatched again.
113
113
 
114
114
  Declare `metering: "unmetered"` when your host pays its provider directly. A
115
115
  metered host supplies idempotent `admit` and `settle` functions plus its own
116
- `refusalMessages` for insufficient credit, daily limits and denied spending.
117
- Quotes are estimates. Your metering owner decides the customer price and
118
- settlement.
116
+ `refusalMessages` for insufficient credit and denied spending. Quotes are
117
+ estimates. Your metering owner decides the customer price and settlement.
119
118
 
120
119
  A recognized provider safety refusal returns HTTP 400 with
121
120
  `code: "SAFETY_FILTER_REFUSED"`, `stage: "provider"`, and `retryable: false`.
@@ -162,12 +161,26 @@ representations and do not decode as these outcomes.
162
161
 
163
162
  Provider facts use Agar's canonical Media grammar: an allowlisted error category
164
163
  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.
164
+ provider boundary classifies code `2021` as the optional, closed
165
+ `provider.reason: "credits_exhausted"`. On 2026-10-27, a real GPT Image 2 call
166
+ from the Prints UI returned
167
+ `AiGatewayError: 2021: Insufficient AI Gateway credits`. The original evidence
168
+ is `~/Botanical-cto/receipts/prints-e2e/REPORT.md:51` ("Follow-up 2"; no charge
169
+ recorded at line 53) and
170
+ `~/Botanical-cto/receipts/prints-e2e/gwfix/results.json` (02:01 local; GPT Image
171
+ 2 call at `2026-10-27T01:01:04.296Z`). The error object contained only
172
+ `name: "AiGatewayError"` and `message: "2021: Insufficient AI Gateway credits"`;
173
+ no HTTP status was recorded. This is observed provider wording from a real UI
174
+ call, not a published guarantee that every Cloudflare `2021` means a zero
175
+ balance. Apps consume the typed reason without interpreting provider numbers or
176
+ public error prose, and should direct the owner to check AI Gateway credits and
177
+ billing rather than infer the account balance. It is separate from the host's
178
+ `INSUFFICIENT_CREDIT` Team billing refusal. The durable owner retains those
179
+ facts through interruption and replay, including safety refusals, without
180
+ retaining the original error message or cause in the failure record. A terminal
181
+ provider failure never redispatches the paid attempt. The fresh pre-release
182
+ schema replaces the safety-only refusal table; old preview stores require an
183
+ explicit host reset, not an automatic migration or dual reader.
171
184
 
172
185
  See the [generated API](docs/api/README.md) for signatures and host ports. The
173
186
  Cloudflare entry needs the native Workers type declarations in a TypeScript
@@ -177,8 +190,8 @@ host. The other entries use ordinary platform web types.
177
190
 
178
191
  For Workers AI, use `createWorkersAiMediaProvider(env.AI)` from `cloudflare` and
179
192
  add an `AI` binding to your host. `createImagesReferenceFitter` prepares bounded
180
- reference images through an Images binding. Your host authorizes each reference
181
- read.
193
+ reference images through an Images binding, using the selected catalog model
194
+ passed to the fitter. Your host authorizes each reference read.
182
195
 
183
196
  `stripPrivateProvenance(bytes)` from `provenance` removes prompt and lineage
184
197
  metadata while preserving the AI marker, print ID and image encoding. It returns
@@ -187,9 +200,20 @@ bytes, MIME type and filename extension. The public XMP format uses
187
200
 
188
201
  ## Limits
189
202
 
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.
203
+ References are bounded to four files and 8 MiB per image. Image-model references
204
+ keep a maximum 512-pixel long edge. Video-model frames and references preserve
205
+ their aspect ratio and are fitted by the short edge to 320 pixels, growing
206
+ smaller inputs when necessary; resolved video references must have a short edge
207
+ of at least 320 pixels. Still-image output and public downloads are bounded to
208
+ 32 MiB. Catalog models may impose tighter limits. Video output has its own 64
209
+ MiB provider bound.
210
+
211
+ The 8 MiB reference budget also applies after transformation: extreme panoramas
212
+ can still exceed the encoded-byte budget, even though video references have no
213
+ 512-pixel long-edge cap. Celld Hub reference fitting requires a
214
+ [paired Hub and edge release](../../services/hub-edge/README.md#paired-hub-and-edge-release);
215
+ the linked section includes the draft Fungi 0.7.1 release note and mixed-version
216
+ failure window.
193
217
 
194
218
  Private-provenance export supports PNG, JPEG and WebP. GIF without XMP remains
195
219
  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. */