@howells/motif-sdk 7.2.0 → 7.3.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/dist/index.d.cts CHANGED
@@ -253,6 +253,11 @@ export declare function validateCreativeDirection(creative: CreativeDirection |
253
253
  * are joined with `". "` and closed with a period. With no clauses the sanitized prompt is returned unchanged.
254
254
  */
255
255
  export declare function enrichPrompt(options: EnrichPromptOptions): CreativePromptResult;
256
+ /** The upscale factors an endpoint takes, from its fal OpenAPI schema. */
257
+ interface ScaleRange {
258
+ max: number;
259
+ min: number;
260
+ }
256
261
  /** ─── Model Types ─────────────────────────────────────────────── */
257
262
  type AspectRatio = "auto" | "8:1" | "4:1" | "21:9" | "16:9" | "3:2" | "4:3" | "5:4" | "1:1" | "4:5" | "3:4" | "2:3" | "9:16" | "1:4" | "1:8";
258
263
  type Resolution = "0.5K" | "1K" | "2K" | "4K";
@@ -273,6 +278,7 @@ interface ImageSizeBounds {
273
278
  /** Both edges must be multiples of this. Motif uses 16 when absent. */
274
279
  multipleOf?: number;
275
280
  }
281
+ /** ─── Queue Types ────────────────────────────────────────────── */
276
282
  interface JobStatus {
277
283
  error?: string;
278
284
  logs?: {
@@ -298,20 +304,20 @@ export declare class ImageSizeBoundsError extends Error {
298
304
  readonly bounds: ImageSizeBounds;
299
305
  constructor(model: string, aspect: AspectRatio, bounds: ImageSizeBounds);
300
306
  }
301
- /** Format presets that set aspect + resolution in one click */
302
- export declare const FORMAT_PRESETS: Record<string, {
303
- label: string;
304
- aspect: AspectRatio;
305
- resolution?: Resolution;
306
- }>;
307
- /**
308
- * `MotifError` and its coercion helper.
309
- *
310
- * Extracted from `server.ts` so `server-tools.ts` can construct errors without
311
- * importing back into `server.ts` — that import was a dependency cycle. Both
312
- * `server.ts` and `server-tools.ts` import from here, and `server.ts`
313
- * re-exports `MotifError` so the package's public surface is unchanged.
314
- */
307
+ /** A named output format: an aspect, and a resolution when the format needs one. */
308
+ interface FormatPreset {
309
+ readonly aspect: AspectRatio;
310
+ /** What the format is for, in a few words. */
311
+ readonly description: string;
312
+ /** Short title for pickers. */
313
+ readonly label: string;
314
+ readonly resolution?: Resolution;
315
+ /** The platform's pixel spec, such as "1080×1920", when it publishes one. */
316
+ readonly size?: string;
317
+ }
318
+ /** Format presets that set aspect + resolution in one click, in display order. */
319
+ export declare const FORMAT_PRESETS: Record<string, FormatPreset>;
320
+ /** `MotifError` and the fal HTTP error mapping. */
315
321
  export declare class MotifError extends Error {
316
322
  readonly status: number;
317
323
  readonly code?: string;
@@ -324,6 +330,14 @@ export declare class MotifError extends Error {
324
330
  }
325
331
  /** Error code for a fal account that is locked, usually for lack of credit. */
326
332
  export declare const ACCOUNT_LOCKED = "ACCOUNT_LOCKED";
333
+ /**
334
+ * Error code for a request that can bill a job and got no clear answer (a
335
+ * connection error, a timeout, a 5xx, or an unreadable acceptance). fal may
336
+ * have accepted it, so the caller must not blindly submit it again.
337
+ */
338
+ export declare const SUBMISSION_UNCERTAIN = "SUBMISSION_UNCERTAIN";
339
+ /** Error code for a fal response whose body was not the JSON it should be. */
340
+ export declare const INVALID_RESPONSE = "INVALID_RESPONSE";
327
341
  /**
328
342
  * Whether a fal HTTP failure means the account is locked.
329
343
  *
@@ -331,6 +345,12 @@ export declare const ACCOUNT_LOCKED = "ACCOUNT_LOCKED";
331
345
  * account has run out of credit. Retrying cannot help, and the key is valid.
332
346
  */
333
347
  export declare function isFalAccountLocked(status: number, body: string): boolean;
348
+ /**
349
+ * The option a refused request was stopped on, from either refusal shape: an
350
+ * `INVALID_OPTION` names it as `details.field`, a `NO_MODEL_AVAILABLE` as
351
+ * `details.blockedBy`. Undefined when the error names no option.
352
+ */
353
+ export declare function refusedOption(error: MotifError): string | undefined;
334
354
  /**
335
355
  * Arena image scores captured for MOT-72 on October 29, 2026. Flare and
336
356
  * Sunburst route to GPT Image 2.6 but keep 2.5's scores until 2.6 is
@@ -398,6 +418,23 @@ interface TaskDefinition {
398
418
  readonly modes?: readonly TaskMode[];
399
419
  /** Best first. Order within a Tier is the order tried at that Tier. */
400
420
  readonly models: readonly RankedModel[];
421
+ /**
422
+ * Its ranked Models are generation Models: a Look can fix one.
423
+ */
424
+ readonly generationModels?: true;
425
+ /** Refuses a request with no prompt. */
426
+ readonly needsPrompt?: true;
427
+ /** Takes its Source and exactly one Reference. */
428
+ readonly oneReference?: true;
429
+ /** The ranking used when the request carries References, in place of `models`. */
430
+ readonly referenceModels?: readonly RankedModel[];
431
+ /** Makes a new image, so there is no Source to ask for. */
432
+ readonly sourceless?: true;
433
+ /**
434
+ * A Mood is joined to the prompt on its tool Models, and the plain job needs
435
+ * a prompt or a Mood to say what light to make.
436
+ */
437
+ readonly takesMood?: true;
401
438
  }
402
439
  /** Every Task with a Model. A Task is absent here until it has at least one Model. */
403
440
  export declare const TASKS: {
@@ -538,10 +575,14 @@ export declare const TASKS: {
538
575
  };
539
576
  readonly generate: {
540
577
  readonly basis: "Order follows Arena text-to-image (https://arena.ai/leaderboard/image/text-to-image), read 2026-10-29. Sunburst and Flare lead quality at the benchmarked max setting. Balanced starts with Nano Banana 2.1 on preliminary Arena evidence, then Grok Image 2 and Muse Image. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Fast keeps Nano Banana 2 Lite, budget Grok and Seedream 5 Lite, and FLUX.2 Turbo. Design, transparency and tuned Look routes are capability exceptions. Generation with References uses the separate editing ranking. Older routes are explicit/pin/Look-only.";
578
+ readonly generationModels: true;
541
579
  readonly models: readonly RankedModel[];
580
+ readonly needsPrompt: true;
542
581
  readonly notFor: "Variations of an image you already have (vary), or a consistent set of images (series run).";
543
582
  readonly rankedAt: "2026-10-29";
544
583
  readonly rankedFrom: "hand";
584
+ readonly referenceModels: readonly RankedModel[];
585
+ readonly sourceless: true;
545
586
  readonly summary: "Make an image from a prompt, or change an image you pass as a reference.";
546
587
  };
547
588
  readonly layers: {
@@ -754,6 +795,7 @@ export declare const TASKS: {
754
795
  readonly rankedAt: "2026-09-16";
755
796
  readonly rankedFrom: "hand";
756
797
  readonly summary: "Change the light in a photo without regenerating it.";
798
+ readonly takesMood: true;
757
799
  };
758
800
  readonly restore: {
759
801
  readonly basis: "Hand-ranked. Each mode names what is wrong and has the Model that fixes it; the plain job is Topaz's general restoration. Control Light, a FLUX.2 klein fine-tune at $0.03/MP, brightens dark photos.";
@@ -816,6 +858,7 @@ export declare const TASKS: {
816
858
  readonly tier: "balanced";
817
859
  }];
818
860
  readonly notFor: "A house style kept across images (generate with a look).";
861
+ readonly oneReference: true;
819
862
  readonly rankedAt: "2026-09-16";
820
863
  readonly rankedFrom: "hand";
821
864
  readonly summary: "Redraw an image in the style of a reference image.";
@@ -886,6 +929,7 @@ export declare const TASKS: {
886
929
  readonly tier: "balanced";
887
930
  }];
888
931
  readonly notFor: "Changing clothes by description (generate with a reference).";
932
+ readonly oneReference: true;
889
933
  readonly rankedAt: "2026-09-16";
890
934
  readonly rankedFrom: "hand";
891
935
  readonly summary: "Dress a person in a garment from another image.";
@@ -951,6 +995,7 @@ export declare const TASKS: {
951
995
  };
952
996
  readonly vary: {
953
997
  readonly basis: "Order follows Arena image edit, single image (https://arena.ai/leaderboard/image/image-edit/single-image), read 2026-10-29. Editing order differs from generation: Grok Image 2 leads balanced, ahead of Nano Banana 2.1, and Seedream 5 Pro outranks GPT Image 1.5. Arena scores Krea 3 only at its Ultra setting, which costs $0.27 to $0.35 an edit and ignores the requested size, so krea3, which runs fal's default setting, is ranked as unscored. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Sunburst and Flare use max quality. vary reuses a previously chosen Model when explicitly supplied, including retained legacy routes.";
998
+ readonly generationModels: true;
954
999
  readonly models: readonly RankedModel[];
955
1000
  readonly notFor: "A specific change to an image described in words (generate with a reference), or a set of different scenes in one style (series run).";
956
1001
  readonly rankedAt: "2026-10-29";
@@ -999,6 +1044,13 @@ interface TaskEnvironment {
999
1044
  readonly keys: readonly string[];
1000
1045
  /** Models pinned per Task in config (`tasks.<task>.model`). */
1001
1046
  readonly pins?: Readonly<Partial<Record<TaskId, string>>>;
1047
+ /**
1048
+ * Whether the Model can carry the whole request: the refusal when it cannot.
1049
+ * The planner supplies it, so a ranked Model it would refuse is skipped
1050
+ * rather than chosen and then refused. Absent when only the capability
1051
+ * profiles are asked, as in `--describe`.
1052
+ */
1053
+ readonly refusal?: (model: string) => MotifError | undefined;
1002
1054
  }
1003
1055
  /** Which rule fixed the Model. */
1004
1056
  type ChosenBy = "look" | "model" | "pin" | "ranking";
@@ -1027,29 +1079,13 @@ interface TaskUnresolved {
1027
1079
  readonly unblockedBy: readonly Unblocker[];
1028
1080
  /** The key variable that would unblock a Model, when one would. */
1029
1081
  readonly missingKey?: string;
1082
+ /** The values the Model takes, when it refused an aspect, resolution or output format. */
1083
+ readonly accepts?: readonly string[];
1084
+ /** The most images the Model makes, when it refused a count. */
1085
+ readonly limit?: number;
1030
1086
  readonly message: string;
1031
1087
  }
1032
1088
  type TaskResolution = TaskResolved | TaskUnresolved;
1033
- interface ModelProfile {
1034
- /** The Model's display name, e.g. "GPT Image 2.6 Sunburst". */
1035
- readonly name: string;
1036
- readonly capabilities: ReadonlySet<Capability>;
1037
- /** Key variables the Model needs for any request. */
1038
- readonly keys: readonly string[];
1039
- readonly maxReferences: number;
1040
- /** Aspect ratios the Model accepts. Absent means any. */
1041
- readonly aspects?: readonly string[];
1042
- /** Resolutions the Model accepts. Absent means any. */
1043
- readonly resolutions?: readonly string[];
1044
- /** Output formats the Model returns, for a text request and an edit.
1045
- * Absent means any. */
1046
- readonly outputFormats?: {
1047
- readonly edit: readonly string[];
1048
- readonly text: readonly string[];
1049
- };
1050
- }
1051
- /** The profile for a Model id, or undefined when no registry knows it. */
1052
- export declare function modelProfile(model: string): ModelProfile | undefined;
1053
1089
  /**
1054
1090
  * Choose the Model for a Task. Pure: no I/O, and the prompt is never read.
1055
1091
  *
@@ -1250,13 +1286,23 @@ interface TaskPlan {
1250
1286
  body: Record<string, unknown>;
1251
1287
  /** Prompt after Look and Mood enrichment, when the Task takes one. */
1252
1288
  prompt?: string;
1289
+ /**
1290
+ * The exact pixel size the request asks for. Null when the size is the
1291
+ * Model's own to decide (a ratio with a resolution tier), the source's
1292
+ * (`auto`), or not set by the request.
1293
+ */
1294
+ size: CustomImageSize | null;
1295
+ /**
1296
+ * The Model always runs through fal's queue. `run` also queues a run whose
1297
+ * caller passes `onSubmitted`, or `onProgress` on a generation Model.
1298
+ */
1253
1299
  queued: boolean;
1254
1300
  /** Projected: before the call. */
1255
1301
  cost: ResolvedCost;
1256
1302
  /** The provider carrying the request. */
1257
1303
  provider: "fal";
1258
1304
  }
1259
- interface TaskOutput extends Omit<TaskPlan, "body" | "cost" | "queued"> {
1305
+ interface TaskOutput extends Omit<TaskPlan, "body" | "cost" | "queued" | "size"> {
1260
1306
  files: TaskFile[];
1261
1307
  /** Non-file result fields: answer, reasoning, boxes, scores, points, rle, text... */
1262
1308
  data: Record<string, unknown>;
@@ -1350,4 +1396,54 @@ export declare const motifEnvSchema: import("@howells/envy").EnvSchema<{
1350
1396
  type MotifEnv = ReturnType<typeof motifEnvSchema.parse>;
1351
1397
  export declare function parseMotifEnv(input?: Record<string, unknown>): MotifEnv;
1352
1398
  export declare function getFalKeyFromEnv(input?: Record<string, unknown>): string | undefined;
1353
- export { type AspectRatio, type Blocker, type Capability, type ChosenBy, type CreativeDirection, type CreativeField, type CreativeOption, type CreativeOptionErrorDetails, type CreativePromptResult, type CustomImageSize, type EnrichPromptOptions, type FalFetch, type ImageSizeBounds, type LookId, type LookOption, type ModelProfile, type MoodId, type MotifClient, type MotifClientConfig, type MotifEnv, type OutputDimensions, type PlanOptions, type PromptWarning, type PromptWarningRule, type RankedFrom, type RankedModel, type Resolution, type ResolvedCost, type Result, type ResultAsync, type TaskDefinition, type TaskEnvironment, type TaskFile, type TaskFunction, type TaskId, type TaskInput, type TaskMode, type TaskOutput, type TaskPlan, type TaskRequest, type TaskResolution, type TaskResolved, type TaskStream, type TaskStreamEvent, type TaskStreamOptions, type TaskUnresolved, type Tier, type Unblocker, err, ok };
1399
+ /**
1400
+ * How a Model is sized:
1401
+ * - `exact`: Motif asks for an exact width and height from the ratio and tier.
1402
+ * - `preset`: a fal named size (about 1024 px on the long edge); no tier.
1403
+ * - `fixed`: a few fixed sizes chosen by ratio; no tier.
1404
+ * - `model`: Motif sends the ratio and tier and the Model decides the pixels.
1405
+ * - `source`: the output keeps the source's size, scaled by the tier where the
1406
+ * endpoint takes one.
1407
+ * - `none`: the Model takes no size.
1408
+ */
1409
+ type SizingKind = "exact" | "preset" | "fixed" | "model" | "source" | "none";
1410
+ interface ModelSizing {
1411
+ readonly kind: SizingKind;
1412
+ /** Tiers the Model accepts. Empty when it takes none. */
1413
+ readonly resolutions: readonly Resolution[];
1414
+ /** The tier sent when none is asked for, where the Model takes tiers. */
1415
+ readonly defaultResolution?: Resolution;
1416
+ /** At ratio 1:1, the size each tier asks for (`exact` Models). */
1417
+ readonly tierSizes?: Readonly<Partial<Record<Resolution, CustomImageSize>>>;
1418
+ /** At ratio 1:1, the size asked for with no tier (`preset` and `fixed`). */
1419
+ readonly size?: CustomImageSize;
1420
+ }
1421
+ interface ModelProfile {
1422
+ /** The Model's display name, e.g. "GPT Image 2.6 Sunburst". */
1423
+ readonly name: string;
1424
+ readonly capabilities: ReadonlySet<Capability>;
1425
+ /** Key variables the Model needs for any request. */
1426
+ readonly keys: readonly string[];
1427
+ /** Most images one request makes. Absent where no maximum is known. */
1428
+ readonly maxCount?: number;
1429
+ readonly maxReferences: number;
1430
+ /** Aspect ratios the Model accepts. Absent means any. */
1431
+ readonly aspects?: readonly string[];
1432
+ /** Resolutions the Model accepts. Absent means any. */
1433
+ readonly resolutions?: readonly string[];
1434
+ /** Upscale factors the Model takes. Absent where its endpoint states no range. */
1435
+ readonly scaleRange?: ScaleRange;
1436
+ /** Output formats that make one image whatever the count. */
1437
+ readonly singleImageFormats?: readonly string[];
1438
+ /** What a resolution tier means for the Model, in pixels where known. */
1439
+ readonly sizing: ModelSizing;
1440
+ /** Output formats the Model returns, for a text request and an edit.
1441
+ * Absent means any. */
1442
+ readonly outputFormats?: {
1443
+ readonly edit: readonly string[];
1444
+ readonly text: readonly string[];
1445
+ };
1446
+ }
1447
+ /** The profile for a Model id, or undefined when no registry knows it. */
1448
+ export declare function modelProfile(model: string): ModelProfile | undefined;
1449
+ export { type AspectRatio, type Blocker, type Capability, type ChosenBy, type CreativeDirection, type CreativeField, type CreativeOption, type CreativeOptionErrorDetails, type CreativePromptResult, type CustomImageSize, type EnrichPromptOptions, type FalFetch, type FormatPreset, type ImageSizeBounds, type LookId, type LookOption, type ModelProfile, type ModelSizing, type MoodId, type MotifClient, type MotifClientConfig, type MotifEnv, type OutputDimensions, type PlanOptions, type PromptWarning, type PromptWarningRule, type RankedFrom, type RankedModel, type Resolution, type ResolvedCost, type Result, type ResultAsync, type SizingKind, type TaskDefinition, type TaskEnvironment, type TaskFile, type TaskFunction, type TaskId, type TaskInput, type TaskMode, type TaskOutput, type TaskPlan, type TaskRequest, type TaskResolution, type TaskResolved, type TaskStream, type TaskStreamEvent, type TaskStreamOptions, type TaskUnresolved, type Tier, type Unblocker, err, ok };
package/dist/index.d.ts CHANGED
@@ -253,6 +253,11 @@ export declare function validateCreativeDirection(creative: CreativeDirection |
253
253
  * are joined with `". "` and closed with a period. With no clauses the sanitized prompt is returned unchanged.
254
254
  */
255
255
  export declare function enrichPrompt(options: EnrichPromptOptions): CreativePromptResult;
256
+ /** The upscale factors an endpoint takes, from its fal OpenAPI schema. */
257
+ interface ScaleRange {
258
+ max: number;
259
+ min: number;
260
+ }
256
261
  /** ─── Model Types ─────────────────────────────────────────────── */
257
262
  type AspectRatio = "auto" | "8:1" | "4:1" | "21:9" | "16:9" | "3:2" | "4:3" | "5:4" | "1:1" | "4:5" | "3:4" | "2:3" | "9:16" | "1:4" | "1:8";
258
263
  type Resolution = "0.5K" | "1K" | "2K" | "4K";
@@ -273,6 +278,7 @@ interface ImageSizeBounds {
273
278
  /** Both edges must be multiples of this. Motif uses 16 when absent. */
274
279
  multipleOf?: number;
275
280
  }
281
+ /** ─── Queue Types ────────────────────────────────────────────── */
276
282
  interface JobStatus {
277
283
  error?: string;
278
284
  logs?: {
@@ -298,20 +304,20 @@ export declare class ImageSizeBoundsError extends Error {
298
304
  readonly bounds: ImageSizeBounds;
299
305
  constructor(model: string, aspect: AspectRatio, bounds: ImageSizeBounds);
300
306
  }
301
- /** Format presets that set aspect + resolution in one click */
302
- export declare const FORMAT_PRESETS: Record<string, {
303
- label: string;
304
- aspect: AspectRatio;
305
- resolution?: Resolution;
306
- }>;
307
- /**
308
- * `MotifError` and its coercion helper.
309
- *
310
- * Extracted from `server.ts` so `server-tools.ts` can construct errors without
311
- * importing back into `server.ts` — that import was a dependency cycle. Both
312
- * `server.ts` and `server-tools.ts` import from here, and `server.ts`
313
- * re-exports `MotifError` so the package's public surface is unchanged.
314
- */
307
+ /** A named output format: an aspect, and a resolution when the format needs one. */
308
+ interface FormatPreset {
309
+ readonly aspect: AspectRatio;
310
+ /** What the format is for, in a few words. */
311
+ readonly description: string;
312
+ /** Short title for pickers. */
313
+ readonly label: string;
314
+ readonly resolution?: Resolution;
315
+ /** The platform's pixel spec, such as "1080×1920", when it publishes one. */
316
+ readonly size?: string;
317
+ }
318
+ /** Format presets that set aspect + resolution in one click, in display order. */
319
+ export declare const FORMAT_PRESETS: Record<string, FormatPreset>;
320
+ /** `MotifError` and the fal HTTP error mapping. */
315
321
  export declare class MotifError extends Error {
316
322
  readonly status: number;
317
323
  readonly code?: string;
@@ -324,6 +330,14 @@ export declare class MotifError extends Error {
324
330
  }
325
331
  /** Error code for a fal account that is locked, usually for lack of credit. */
326
332
  export declare const ACCOUNT_LOCKED = "ACCOUNT_LOCKED";
333
+ /**
334
+ * Error code for a request that can bill a job and got no clear answer (a
335
+ * connection error, a timeout, a 5xx, or an unreadable acceptance). fal may
336
+ * have accepted it, so the caller must not blindly submit it again.
337
+ */
338
+ export declare const SUBMISSION_UNCERTAIN = "SUBMISSION_UNCERTAIN";
339
+ /** Error code for a fal response whose body was not the JSON it should be. */
340
+ export declare const INVALID_RESPONSE = "INVALID_RESPONSE";
327
341
  /**
328
342
  * Whether a fal HTTP failure means the account is locked.
329
343
  *
@@ -331,6 +345,12 @@ export declare const ACCOUNT_LOCKED = "ACCOUNT_LOCKED";
331
345
  * account has run out of credit. Retrying cannot help, and the key is valid.
332
346
  */
333
347
  export declare function isFalAccountLocked(status: number, body: string): boolean;
348
+ /**
349
+ * The option a refused request was stopped on, from either refusal shape: an
350
+ * `INVALID_OPTION` names it as `details.field`, a `NO_MODEL_AVAILABLE` as
351
+ * `details.blockedBy`. Undefined when the error names no option.
352
+ */
353
+ export declare function refusedOption(error: MotifError): string | undefined;
334
354
  /**
335
355
  * Arena image scores captured for MOT-72 on October 29, 2026. Flare and
336
356
  * Sunburst route to GPT Image 2.6 but keep 2.5's scores until 2.6 is
@@ -398,6 +418,23 @@ interface TaskDefinition {
398
418
  readonly modes?: readonly TaskMode[];
399
419
  /** Best first. Order within a Tier is the order tried at that Tier. */
400
420
  readonly models: readonly RankedModel[];
421
+ /**
422
+ * Its ranked Models are generation Models: a Look can fix one.
423
+ */
424
+ readonly generationModels?: true;
425
+ /** Refuses a request with no prompt. */
426
+ readonly needsPrompt?: true;
427
+ /** Takes its Source and exactly one Reference. */
428
+ readonly oneReference?: true;
429
+ /** The ranking used when the request carries References, in place of `models`. */
430
+ readonly referenceModels?: readonly RankedModel[];
431
+ /** Makes a new image, so there is no Source to ask for. */
432
+ readonly sourceless?: true;
433
+ /**
434
+ * A Mood is joined to the prompt on its tool Models, and the plain job needs
435
+ * a prompt or a Mood to say what light to make.
436
+ */
437
+ readonly takesMood?: true;
401
438
  }
402
439
  /** Every Task with a Model. A Task is absent here until it has at least one Model. */
403
440
  export declare const TASKS: {
@@ -538,10 +575,14 @@ export declare const TASKS: {
538
575
  };
539
576
  readonly generate: {
540
577
  readonly basis: "Order follows Arena text-to-image (https://arena.ai/leaderboard/image/text-to-image), read 2026-10-29. Sunburst and Flare lead quality at the benchmarked max setting. Balanced starts with Nano Banana 2.1 on preliminary Arena evidence, then Grok Image 2 and Muse Image. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Fast keeps Nano Banana 2 Lite, budget Grok and Seedream 5 Lite, and FLUX.2 Turbo. Design, transparency and tuned Look routes are capability exceptions. Generation with References uses the separate editing ranking. Older routes are explicit/pin/Look-only.";
578
+ readonly generationModels: true;
541
579
  readonly models: readonly RankedModel[];
580
+ readonly needsPrompt: true;
542
581
  readonly notFor: "Variations of an image you already have (vary), or a consistent set of images (series run).";
543
582
  readonly rankedAt: "2026-10-29";
544
583
  readonly rankedFrom: "hand";
584
+ readonly referenceModels: readonly RankedModel[];
585
+ readonly sourceless: true;
545
586
  readonly summary: "Make an image from a prompt, or change an image you pass as a reference.";
546
587
  };
547
588
  readonly layers: {
@@ -754,6 +795,7 @@ export declare const TASKS: {
754
795
  readonly rankedAt: "2026-09-16";
755
796
  readonly rankedFrom: "hand";
756
797
  readonly summary: "Change the light in a photo without regenerating it.";
798
+ readonly takesMood: true;
757
799
  };
758
800
  readonly restore: {
759
801
  readonly basis: "Hand-ranked. Each mode names what is wrong and has the Model that fixes it; the plain job is Topaz's general restoration. Control Light, a FLUX.2 klein fine-tune at $0.03/MP, brightens dark photos.";
@@ -816,6 +858,7 @@ export declare const TASKS: {
816
858
  readonly tier: "balanced";
817
859
  }];
818
860
  readonly notFor: "A house style kept across images (generate with a look).";
861
+ readonly oneReference: true;
819
862
  readonly rankedAt: "2026-09-16";
820
863
  readonly rankedFrom: "hand";
821
864
  readonly summary: "Redraw an image in the style of a reference image.";
@@ -886,6 +929,7 @@ export declare const TASKS: {
886
929
  readonly tier: "balanced";
887
930
  }];
888
931
  readonly notFor: "Changing clothes by description (generate with a reference).";
932
+ readonly oneReference: true;
889
933
  readonly rankedAt: "2026-09-16";
890
934
  readonly rankedFrom: "hand";
891
935
  readonly summary: "Dress a person in a garment from another image.";
@@ -951,6 +995,7 @@ export declare const TASKS: {
951
995
  };
952
996
  readonly vary: {
953
997
  readonly basis: "Order follows Arena image edit, single image (https://arena.ai/leaderboard/image/image-edit/single-image), read 2026-10-29. Editing order differs from generation: Grok Image 2 leads balanced, ahead of Nano Banana 2.1, and Seedream 5 Pro outranks GPT Image 1.5. Arena scores Krea 3 only at its Ultra setting, which costs $0.27 to $0.35 an edit and ignores the requested size, so krea3, which runs fal's default setting, is ranked as unscored. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Sunburst and Flare use max quality. vary reuses a previously chosen Model when explicitly supplied, including retained legacy routes.";
998
+ readonly generationModels: true;
954
999
  readonly models: readonly RankedModel[];
955
1000
  readonly notFor: "A specific change to an image described in words (generate with a reference), or a set of different scenes in one style (series run).";
956
1001
  readonly rankedAt: "2026-10-29";
@@ -999,6 +1044,13 @@ interface TaskEnvironment {
999
1044
  readonly keys: readonly string[];
1000
1045
  /** Models pinned per Task in config (`tasks.<task>.model`). */
1001
1046
  readonly pins?: Readonly<Partial<Record<TaskId, string>>>;
1047
+ /**
1048
+ * Whether the Model can carry the whole request: the refusal when it cannot.
1049
+ * The planner supplies it, so a ranked Model it would refuse is skipped
1050
+ * rather than chosen and then refused. Absent when only the capability
1051
+ * profiles are asked, as in `--describe`.
1052
+ */
1053
+ readonly refusal?: (model: string) => MotifError | undefined;
1002
1054
  }
1003
1055
  /** Which rule fixed the Model. */
1004
1056
  type ChosenBy = "look" | "model" | "pin" | "ranking";
@@ -1027,29 +1079,13 @@ interface TaskUnresolved {
1027
1079
  readonly unblockedBy: readonly Unblocker[];
1028
1080
  /** The key variable that would unblock a Model, when one would. */
1029
1081
  readonly missingKey?: string;
1082
+ /** The values the Model takes, when it refused an aspect, resolution or output format. */
1083
+ readonly accepts?: readonly string[];
1084
+ /** The most images the Model makes, when it refused a count. */
1085
+ readonly limit?: number;
1030
1086
  readonly message: string;
1031
1087
  }
1032
1088
  type TaskResolution = TaskResolved | TaskUnresolved;
1033
- interface ModelProfile {
1034
- /** The Model's display name, e.g. "GPT Image 2.6 Sunburst". */
1035
- readonly name: string;
1036
- readonly capabilities: ReadonlySet<Capability>;
1037
- /** Key variables the Model needs for any request. */
1038
- readonly keys: readonly string[];
1039
- readonly maxReferences: number;
1040
- /** Aspect ratios the Model accepts. Absent means any. */
1041
- readonly aspects?: readonly string[];
1042
- /** Resolutions the Model accepts. Absent means any. */
1043
- readonly resolutions?: readonly string[];
1044
- /** Output formats the Model returns, for a text request and an edit.
1045
- * Absent means any. */
1046
- readonly outputFormats?: {
1047
- readonly edit: readonly string[];
1048
- readonly text: readonly string[];
1049
- };
1050
- }
1051
- /** The profile for a Model id, or undefined when no registry knows it. */
1052
- export declare function modelProfile(model: string): ModelProfile | undefined;
1053
1089
  /**
1054
1090
  * Choose the Model for a Task. Pure: no I/O, and the prompt is never read.
1055
1091
  *
@@ -1250,13 +1286,23 @@ interface TaskPlan {
1250
1286
  body: Record<string, unknown>;
1251
1287
  /** Prompt after Look and Mood enrichment, when the Task takes one. */
1252
1288
  prompt?: string;
1289
+ /**
1290
+ * The exact pixel size the request asks for. Null when the size is the
1291
+ * Model's own to decide (a ratio with a resolution tier), the source's
1292
+ * (`auto`), or not set by the request.
1293
+ */
1294
+ size: CustomImageSize | null;
1295
+ /**
1296
+ * The Model always runs through fal's queue. `run` also queues a run whose
1297
+ * caller passes `onSubmitted`, or `onProgress` on a generation Model.
1298
+ */
1253
1299
  queued: boolean;
1254
1300
  /** Projected: before the call. */
1255
1301
  cost: ResolvedCost;
1256
1302
  /** The provider carrying the request. */
1257
1303
  provider: "fal";
1258
1304
  }
1259
- interface TaskOutput extends Omit<TaskPlan, "body" | "cost" | "queued"> {
1305
+ interface TaskOutput extends Omit<TaskPlan, "body" | "cost" | "queued" | "size"> {
1260
1306
  files: TaskFile[];
1261
1307
  /** Non-file result fields: answer, reasoning, boxes, scores, points, rle, text... */
1262
1308
  data: Record<string, unknown>;
@@ -1350,4 +1396,54 @@ export declare const motifEnvSchema: import("@howells/envy").EnvSchema<{
1350
1396
  type MotifEnv = ReturnType<typeof motifEnvSchema.parse>;
1351
1397
  export declare function parseMotifEnv(input?: Record<string, unknown>): MotifEnv;
1352
1398
  export declare function getFalKeyFromEnv(input?: Record<string, unknown>): string | undefined;
1353
- export { type AspectRatio, type Blocker, type Capability, type ChosenBy, type CreativeDirection, type CreativeField, type CreativeOption, type CreativeOptionErrorDetails, type CreativePromptResult, type CustomImageSize, type EnrichPromptOptions, type FalFetch, type ImageSizeBounds, type LookId, type LookOption, type ModelProfile, type MoodId, type MotifClient, type MotifClientConfig, type MotifEnv, type OutputDimensions, type PlanOptions, type PromptWarning, type PromptWarningRule, type RankedFrom, type RankedModel, type Resolution, type ResolvedCost, type Result, type ResultAsync, type TaskDefinition, type TaskEnvironment, type TaskFile, type TaskFunction, type TaskId, type TaskInput, type TaskMode, type TaskOutput, type TaskPlan, type TaskRequest, type TaskResolution, type TaskResolved, type TaskStream, type TaskStreamEvent, type TaskStreamOptions, type TaskUnresolved, type Tier, type Unblocker, err, ok };
1399
+ /**
1400
+ * How a Model is sized:
1401
+ * - `exact`: Motif asks for an exact width and height from the ratio and tier.
1402
+ * - `preset`: a fal named size (about 1024 px on the long edge); no tier.
1403
+ * - `fixed`: a few fixed sizes chosen by ratio; no tier.
1404
+ * - `model`: Motif sends the ratio and tier and the Model decides the pixels.
1405
+ * - `source`: the output keeps the source's size, scaled by the tier where the
1406
+ * endpoint takes one.
1407
+ * - `none`: the Model takes no size.
1408
+ */
1409
+ type SizingKind = "exact" | "preset" | "fixed" | "model" | "source" | "none";
1410
+ interface ModelSizing {
1411
+ readonly kind: SizingKind;
1412
+ /** Tiers the Model accepts. Empty when it takes none. */
1413
+ readonly resolutions: readonly Resolution[];
1414
+ /** The tier sent when none is asked for, where the Model takes tiers. */
1415
+ readonly defaultResolution?: Resolution;
1416
+ /** At ratio 1:1, the size each tier asks for (`exact` Models). */
1417
+ readonly tierSizes?: Readonly<Partial<Record<Resolution, CustomImageSize>>>;
1418
+ /** At ratio 1:1, the size asked for with no tier (`preset` and `fixed`). */
1419
+ readonly size?: CustomImageSize;
1420
+ }
1421
+ interface ModelProfile {
1422
+ /** The Model's display name, e.g. "GPT Image 2.6 Sunburst". */
1423
+ readonly name: string;
1424
+ readonly capabilities: ReadonlySet<Capability>;
1425
+ /** Key variables the Model needs for any request. */
1426
+ readonly keys: readonly string[];
1427
+ /** Most images one request makes. Absent where no maximum is known. */
1428
+ readonly maxCount?: number;
1429
+ readonly maxReferences: number;
1430
+ /** Aspect ratios the Model accepts. Absent means any. */
1431
+ readonly aspects?: readonly string[];
1432
+ /** Resolutions the Model accepts. Absent means any. */
1433
+ readonly resolutions?: readonly string[];
1434
+ /** Upscale factors the Model takes. Absent where its endpoint states no range. */
1435
+ readonly scaleRange?: ScaleRange;
1436
+ /** Output formats that make one image whatever the count. */
1437
+ readonly singleImageFormats?: readonly string[];
1438
+ /** What a resolution tier means for the Model, in pixels where known. */
1439
+ readonly sizing: ModelSizing;
1440
+ /** Output formats the Model returns, for a text request and an edit.
1441
+ * Absent means any. */
1442
+ readonly outputFormats?: {
1443
+ readonly edit: readonly string[];
1444
+ readonly text: readonly string[];
1445
+ };
1446
+ }
1447
+ /** The profile for a Model id, or undefined when no registry knows it. */
1448
+ export declare function modelProfile(model: string): ModelProfile | undefined;
1449
+ export { type AspectRatio, type Blocker, type Capability, type ChosenBy, type CreativeDirection, type CreativeField, type CreativeOption, type CreativeOptionErrorDetails, type CreativePromptResult, type CustomImageSize, type EnrichPromptOptions, type FalFetch, type FormatPreset, type ImageSizeBounds, type LookId, type LookOption, type ModelProfile, type ModelSizing, type MoodId, type MotifClient, type MotifClientConfig, type MotifEnv, type OutputDimensions, type PlanOptions, type PromptWarning, type PromptWarningRule, type RankedFrom, type RankedModel, type Resolution, type ResolvedCost, type Result, type ResultAsync, type SizingKind, type TaskDefinition, type TaskEnvironment, type TaskFile, type TaskFunction, type TaskId, type TaskInput, type TaskMode, type TaskOutput, type TaskPlan, type TaskRequest, type TaskResolution, type TaskResolved, type TaskStream, type TaskStreamEvent, type TaskStreamOptions, type TaskUnresolved, type Tier, type Unblocker, err, ok };