@civitai/app-sdk 0.56.1 → 0.57.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.
@@ -1146,17 +1146,30 @@ export type WorkflowBodyStep = {
1146
1146
  * 3. **No resource policy / `urn:air:` scan.** AIR resources are ALLOWED here.
1147
1147
  * Spend, not entitlement, is the binding control.
1148
1148
  * 4. **No `billingMode` and no load-time price invariant.** {@link maxBuzz}
1149
- * replaces them, exactly as on {@link WorkflowBodyCustomComfyInline}.
1149
+ * and a per-request orchestrator `whatif` quote replace them — see
1150
+ * `maxBuzz` below for how the two combine.
1150
1151
  *
1151
1152
  * WHAT STILL BOUNDS IT, all server-side:
1152
1153
  * - A **denylist** of platform-internal `$type`s (scanners, moderation
1153
1154
  * classifiers, hashing/model-ingestion, web egress) is refused by the host
1154
1155
  * router before any spend reservation or orchestrator call. It is a DENYLIST,
1155
1156
  * not an allowlist: a `$type` the host has never heard of is allowed through
1156
- * by construction, which is the point of this arm.
1157
+ * by construction, which is the point of this arm. `training` and
1158
+ * `imageResourceTraining` are deliberately NOT on it (a host operator
1159
+ * decision): both may be submitted here, on the same terms as every other
1160
+ * `$type`. If the orchestrator quotes the run, the host reserves the quote
1161
+ * (or `maxBuzz`, if larger), gated by the token's per-call budget, and
1162
+ * stamps no timeout; if it does
1163
+ * not, `maxBuzz` is both the reservation and the timeout — see `maxBuzz`
1164
+ * below. Whether a given training input is quoted, and at what price, is
1165
+ * the orchestrator's answer, not this package's: read it from `estimate`.
1166
+ * A host that supports it reports the trained epochs on
1167
+ * {@link BlockWorkflowSnapshot.trainedEpochs} without exposing the
1168
+ * checkpoint itself.
1157
1169
  * - `$type` is bounded to **1…64 characters** and `input` to **262144 bytes**
1158
1170
  * (256 KB) serialized. Both REJECT; neither truncates.
1159
- * - `maxBuzz` is the single spend knob — see its own note below.
1171
+ * - `maxBuzz` is the app's spend knob, and the host's quote can raise the
1172
+ * reservation above it — see its own note below.
1160
1173
  *
1161
1174
  * @example A pass-through body for an orchestrator step the registry does not
1162
1175
  * carry. Note there is no `step` key.
@@ -1205,23 +1218,34 @@ export type WorkflowBodyPassThroughStep = {
1205
1218
  */
1206
1219
  input: Record<string, unknown>;
1207
1220
  /**
1208
- * The per-job Buzz ceiling. Required, an integer in **1…250**.
1209
- *
1210
- * 🔴 **IT IS ALSO THE STEP TIMEOUT, IN SECONDS** — identical mechanism to
1211
- * {@link WorkflowBodyCustomComfyInline.maxBuzz}. The host stamps
1212
- * `stepTimeoutSeconds = maxBuzz`; there is only one number, which is what makes
1213
- * the ceiling physically enforceable rather than merely asserted. So
1214
- * `maxBuzz: 10` does not buy a cheap job; it buys one that is KILLED after 10
1215
- * seconds and comes back `expired`. Size it to the wall-clock time the step
1216
- * actually needs.
1221
+ * The app's declared per-job Buzz amount: the least the host reserves, and —
1222
+ * only when unquoted — the step timeout in seconds. NOT a spend ceiling (see
1223
+ * below). Required, an integer in **1…250**.
1217
1224
  *
1218
- * You are billed the REAL cost: post-paid against measured GPU seconds,
1219
- * refunding the unused remainder of the ceiling, so a generous `maxBuzz` costs
1220
- * nothing extra when the job finishes early. `estimate` on a pass-through body
1221
- * echoes this number back as `cost.total` — an upper bound, not a price;
1222
- * surface it as "up to N Buzz".
1225
+ * 🔴 **WHAT IT BOUNDS DEPENDS ON WHETHER THE HOST GOT A QUOTE** — this is NOT
1226
+ * the mechanism of {@link WorkflowBodyCustomComfyInline.maxBuzz}, which is
1227
+ * always the timeout. On both `estimate` and `submit` the host first asks the
1228
+ * orchestrator for a `whatif` quote of this exact step, then:
1229
+ * - **Quoted** — the host reserves `max(maxBuzz, quote)` and stamps NO step
1230
+ * timeout. That reservation can exceed 250: it is gated against the token's
1231
+ * per-call budget (`token.buzzBudget` — the app's per-generation budget,
1232
+ * capped at 1000 when the host mints a production token), and a submit
1233
+ * whose reservation exceeds that budget comes back `failed` with an
1234
+ * `insufficient buzz budget` error before any Buzz is reserved.
1235
+ * - **Unquoted** (the orchestrator returned no price, or the quote could not
1236
+ * be had) — the host reserves `maxBuzz` and stamps it as the step timeout,
1237
+ * in seconds. `maxBuzz: 10` then buys a job that is KILLED after 10
1238
+ * seconds and comes back `expired`, so size it to the wall-clock time the
1239
+ * step actually needs. The same per-call budget gate applies. The timeout
1240
+ * bounds wall-clock time; it bounds spend only for a step billed by
1241
+ * compute time. A step priced per unit can bill ABOVE `maxBuzz`.
1223
1242
  *
1224
- * The host additionally requires `maxBuzz <= token.buzzBudget` before submit.
1243
+ * `estimate` on a pass-through body returns, as `cost.total`, the number the
1244
+ * submit would reserve: `max(maxBuzz, quote)`, or `maxBuzz` when unquoted.
1245
+ * It is a reservation, NOT a guaranteed ceiling: the terminal settle refunds
1246
+ * any unused remainder, but nothing on this arm stops a job billing above
1247
+ * the reservation (see the unquoted case above). Do not present it to the
1248
+ * viewer as a maximum.
1225
1249
  */
1226
1250
  maxBuzz: number;
1227
1251
  };
@@ -1310,6 +1334,226 @@ export interface BlockWorkflowSnapshot {
1310
1334
  amount: number;
1311
1335
  accountType: 'yellow' | 'blue' | 'red' | 'green';
1312
1336
  };
1337
+ /**
1338
+ * Checkpoint versions the host SILENTLY replaced before running the
1339
+ * generation. `requested` is the version id the block asked for, `applied` is
1340
+ * the one that actually ran (and was billed).
1341
+ *
1342
+ * On an ecosystem whose model is locked to a workflow, the host swaps a
1343
+ * version id that is not offered for the current workflow for that workflow's
1344
+ * default and still succeeds — graceful degradation that keeps an app pinned
1345
+ * to a since-retired version working. This field only REPORTS the swap; the
1346
+ * behaviour is unchanged.
1347
+ *
1348
+ * WHERE IT APPEARS: on the `kind: 'textToImage'` path only — the submit reply,
1349
+ * the estimate reply, every submit reply that quotes a cost without
1350
+ * submitting, and every later poll/cancel of that workflow (the record is
1351
+ * persisted with it, so it sits next to the `imageUrls` it describes). An
1352
+ * estimate creates no persisted workflow, so there it exists only on that
1353
+ * reply. It does NOT appear for `customComfy` or `step` bodies: neither
1354
+ * resolves a checkpoint, so there is nothing to substitute.
1355
+ *
1356
+ * OMITTED entirely when nothing was substituted. Absent on hosts that predate
1357
+ * it.
1358
+ */
1359
+ modelSubstitutions?: BlockModelSubstitution[];
1360
+ /**
1361
+ * Generated FREE TEXT produced by a registered text-output step (e.g.
1362
+ * `chat-completion`) — every string here has PASSED the host's output
1363
+ * moderation scan.
1364
+ *
1365
+ * WHERE IT APPEARS: the poll and cancel replies (`poll()` / `cancel()` /
1366
+ * `watch()` on `useBuzzWorkflow`). NOT on the submit reply — a submit returns
1367
+ * a freshly-queued workflow with no output yet, so poll for the text. Also
1368
+ * never on {@link AppWorkflow}, which has no text field.
1369
+ *
1370
+ * OMITTED entirely when there is no text. Independent of
1371
+ * {@link BlockWorkflowSnapshot.textOutputWithheld}: a workflow with two text
1372
+ * steps can release one and withhold the other, and both fields then appear.
1373
+ */
1374
+ textOutputs?: string[];
1375
+ /**
1376
+ * Set when a text-output step produced nothing the block can be given, with a
1377
+ * user-facing sentence saying why. TWO distinct causes land here:
1378
+ *
1379
+ * 1. WITHHELD — text WAS produced and is being kept back: a content-policy
1380
+ * hit, or any other fail-CLOSED scan outcome (scanner error or timeout,
1381
+ * no verdict at all, an over-cap payload, or requested labels that came
1382
+ * back missing or errored). Every one of those paths returns the SAME
1383
+ * message, so this cause is one string with several origins.
1384
+ * 2. UNPUBLISHABLE — the step SUCCEEDED and the scan RELEASED, but none of
1385
+ * what came back could be published (e.g. a provider tool-call `id`
1386
+ * outside the published charset). Nothing was moderated in this case,
1387
+ * and the message says so explicitly.
1388
+ *
1389
+ * `reason` is deliberately generic: it does not name the labels that
1390
+ * triggered.
1391
+ *
1392
+ * 🔴 THE TWO CAUSES ARE NOT MACHINE-SEPARABLE without matching on the string,
1393
+ * which is NOT a contract. Do not branch on the text of `reason`.
1394
+ *
1395
+ * 🔴 ONLY CAUSE 2 IS STATUS-GATED, and that gate reads the STEP's own
1396
+ * `succeeded` status — never the workflow's. Cause 1 is not gated at all, so a
1397
+ * CANCELLED generation CAN still set this field: the orchestrator returns
1398
+ * whatever text it had already produced, that text is still scanned, and a
1399
+ * hit still withholds it. A block that skips rendering `reason` on cancel
1400
+ * would suppress a real content-policy explanation.
1401
+ *
1402
+ * A block polling a healthy in-flight generation sees the field absent — not
1403
+ * because of its status, but because a step that has produced no text yet can
1404
+ * reach neither cause.
1405
+ *
1406
+ * Independent of {@link BlockWorkflowSnapshot.textOutputs} rather than
1407
+ * mutually exclusive, so `reason` is scoped to "a step in this response",
1408
+ * never to the response as a whole. Same appearance rules as `textOutputs`.
1409
+ */
1410
+ textOutputWithheld?: {
1411
+ reason: string;
1412
+ };
1413
+ /**
1414
+ * Structured tool calls the model requested on a registered text-output step
1415
+ * (e.g. `chat-completion` with tools), which have PASSED the same output
1416
+ * moderation scan as {@link BlockWorkflowSnapshot.textOutputs}: every
1417
+ * `arguments` string here was part of the text the scan released. The tool
1418
+ * `name` is restricted to `[A-Za-z0-9_-]`, so it cannot carry prose.
1419
+ *
1420
+ * Each entry is fully populated — the host drops a call it cannot fill
1421
+ * completely rather than publishing a partial one.
1422
+ *
1423
+ * Same appearance rules as `textOutputs` (poll and cancel replies only).
1424
+ * OMITTED entirely when there are none.
1425
+ */
1426
+ toolCalls?: BlockStepToolCall[];
1427
+ /**
1428
+ * The raw orchestrator output of each PASS-THROUGH step
1429
+ * ({@link WorkflowBodyPassThroughStep}: `kind: 'step'` with a `$type`), as
1430
+ * `{ $type, output }`, forwarded as-is — minus its blobs.
1431
+ *
1432
+ * Blob-SHAPED values are removed from `output`; their available urls go to
1433
+ * {@link BlockWorkflowSnapshot.imageUrls} (and to {@link AppWorkflow.images})
1434
+ * instead, so a pass-through step does not open a second image channel.
1435
+ * "Blob-shaped" is the exact scope of that: a url held in a plain string
1436
+ * field of the output is not a blob and can still appear here.
1437
+ *
1438
+ * 🔴 UNSCANNED. Unlike `textOutputs`, no moderation scan runs over this
1439
+ * field; moderation for the pass-through arm happens at the publish boundary.
1440
+ * Treat any text in it accordingly.
1441
+ *
1442
+ * 🔴 A `$type` that is ALSO a registered step's orchestrator type (e.g.
1443
+ * `chatCompletion`, which `chat-completion` uses) is handled by that
1444
+ * registered step instead — its text arrives on `textOutputs`, scanned, and
1445
+ * it does not appear here. Which channel a pass-through `$type` lands on can
1446
+ * therefore change the day the host registers a step for it.
1447
+ *
1448
+ * `output` is `unknown`: its shape is whatever the orchestrator returns for
1449
+ * that `$type`. OMITTED entirely when the workflow has no pass-through step
1450
+ * with an unregistered `$type`.
1451
+ */
1452
+ stepOutputs?: Array<{
1453
+ $type: string;
1454
+ output: unknown;
1455
+ }>;
1456
+ /**
1457
+ * The epochs of a pass-through `training` / `imageResourceTraining` step
1458
+ * (see {@link WorkflowBodyPassThroughStep}) that PRODUCED A CHECKPOINT — one
1459
+ * entry per epoch, each naming the step's `$type` and its `epochNumber`.
1460
+ * Only runs whose training moderation status is approved are listed; an
1461
+ * unmoderated or refused run reports no epochs.
1462
+ *
1463
+ * 🔴 THE CHECKPOINT ITSELF IS DELIBERATELY NOT EXPOSED — this field carries
1464
+ * no download url. (An older host may still surface checkpoint urls in
1465
+ * `imageUrls` or `stepOutputs` for a training step; that is a host defect,
1466
+ * not a contract — never treat an `imageUrls` entry as a checkpoint.) To
1467
+ * publish a trained epoch, navigate the viewer to Civitai's own model wizard:
1468
+ *
1469
+ * ```ts
1470
+ * const { navigate } = useCivitaiNavigate();
1471
+ * navigate(
1472
+ * `models/train/from-orchestrator?workflowId=${snapshot.workflowId}&epoch=${epoch.epochNumber}`,
1473
+ * { scope: 'site' },
1474
+ * );
1475
+ * ```
1476
+ *
1477
+ * The viewer completes (or abandons) the publish there, on Civitai's surface;
1478
+ * {@link BlockWorkflowSnapshot.publishedModel} then reports the result.
1479
+ *
1480
+ * 🔴 REQUIRES A HOST VERSION THAT EMITS IT. Absent on older hosts — treat
1481
+ * absence as "unknown", not as "no checkpoint". The run's spend and timeout
1482
+ * follow the pass-through arm's rules — see
1483
+ * {@link WorkflowBodyPassThroughStep.maxBuzz}.
1484
+ */
1485
+ trainedEpochs?: BlockTrainedEpoch[];
1486
+ /**
1487
+ * The Civitai model this run has been published as — present once the viewer
1488
+ * has STARTED or FINISHED publishing it through the model wizard reached from
1489
+ * {@link BlockWorkflowSnapshot.trainedEpochs}. `published` is `false` while the
1490
+ * model is still a draft and `true` once it is published.
1491
+ *
1492
+ * 🔴 REQUIRES A HOST VERSION THAT EMITS IT. Absent on older hosts, and absent
1493
+ * on any run nobody has started publishing.
1494
+ */
1495
+ publishedModel?: BlockPublishedModel;
1496
+ }
1497
+ /**
1498
+ * Why the host substituted a checkpoint version — see
1499
+ * {@link BlockWorkflowSnapshot.modelSubstitutions}.
1500
+ *
1501
+ * - `'wrong-workflow'` — the id IS one of this ecosystem's workflow-scoped
1502
+ * versions, but for a DIFFERENT workflow (e.g. an edit-only version sent to
1503
+ * text-to-image).
1504
+ * - `'unrecognized'` — the id is in no scoped list for this ecosystem: a
1505
+ * community checkpoint, or a version retired since the app shipped.
1506
+ * - `'gated'` — the id IS offered for this workflow, but a gate rule hid it
1507
+ * from THIS viewer. The app is not wrong; the viewer may not use it.
1508
+ *
1509
+ * The host may add a reason in a later version, and the SDK's inbound
1510
+ * validator does not reject an unrecognised one — handle an unknown value as a
1511
+ * generic substitution rather than assuming the list is closed.
1512
+ */
1513
+ export type ModelSubstitutionReason = 'wrong-workflow' | 'unrecognized' | 'gated';
1514
+ /** One entry of {@link BlockWorkflowSnapshot.modelSubstitutions}. */
1515
+ export interface BlockModelSubstitution {
1516
+ /** The checkpoint version id the block asked for. */
1517
+ requested: number;
1518
+ /** The checkpoint version id that actually ran. */
1519
+ applied: number;
1520
+ reason: ModelSubstitutionReason;
1521
+ }
1522
+ /** One entry of {@link BlockWorkflowSnapshot.toolCalls}. */
1523
+ export interface BlockStepToolCall {
1524
+ id: string;
1525
+ /**
1526
+ * The SDK's inbound validator checks only that it is a string, so a value
1527
+ * the host adds later can arrive — do not assume `'function'` exhaustively.
1528
+ */
1529
+ type: 'function';
1530
+ function: {
1531
+ /** Restricted to `[A-Za-z0-9_-]`. */
1532
+ name: string;
1533
+ /** The model's arguments, as the JSON string it produced. Scanned. */
1534
+ arguments: string;
1535
+ };
1536
+ }
1537
+ /** One entry of {@link BlockWorkflowSnapshot.trainedEpochs}. */
1538
+ export interface BlockTrainedEpoch {
1539
+ /**
1540
+ * Which pass-through training `$type` produced this epoch. The SDK's inbound
1541
+ * validator checks only that it is a string, so a value the host adds later
1542
+ * can arrive — give an exhaustive `switch` a default branch.
1543
+ */
1544
+ $type: 'training' | 'imageResourceTraining';
1545
+ /** Pass as `epoch` to the `/models/train/from-orchestrator` wizard route. */
1546
+ epochNumber: number;
1547
+ }
1548
+ /**
1549
+ * The model a training run was published as —
1550
+ * {@link BlockWorkflowSnapshot.publishedModel} / {@link AppWorkflow.publishedModel}.
1551
+ */
1552
+ export interface BlockPublishedModel {
1553
+ modelId: number;
1554
+ modelVersionId: number;
1555
+ /** `false` while the model is still a draft in the wizard; `true` once published. */
1556
+ published: boolean;
1313
1557
  }
1314
1558
  /**
1315
1559
  * Which side of the settings split a field lives on.
@@ -1827,6 +2071,16 @@ export interface AppWorkflow {
1827
2071
  cost: number | null;
1828
2072
  /** ISO-8601. */
1829
2073
  createdAt: string;
2074
+ /**
2075
+ * The Civitai model a training run in this workflow was published as —
2076
+ * present once the viewer has started or finished publishing it through the
2077
+ * model wizard. Same shape and meaning as
2078
+ * {@link BlockWorkflowSnapshot.publishedModel}.
2079
+ *
2080
+ * 🔴 REQUIRES A HOST VERSION THAT EMITS IT. Absent on older hosts, and on
2081
+ * every workflow nobody has published.
2082
+ */
2083
+ publishedModel?: BlockPublishedModel;
1830
2084
  }
1831
2085
  /**
1832
2086
  * Per-viewer gated projection of ONE image returned by `GET_IMAGES_BY_IDS`
package/dist/index.d.ts CHANGED
@@ -25,10 +25,16 @@
25
25
  *
26
26
  * - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
27
27
  * (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
28
- * 119 symbols and that side effect into the root would make every OAuth app
28
+ * 135 symbols and that side effect into the root would make every OAuth app
29
29
  * that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
30
30
  * Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
31
31
  * audiences; see this package's README.
32
+ * 🔴 That 135 is ASSERTED, not merely quoted — by
33
+ * `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
34
+ * `surface.get('./blocks').size`. It said 119 here against a real 125 until
35
+ * 2026-10-04, because the existing checks pin the root as an exact union and
36
+ * each subpath's set relations, none of which can see a subpath GROW. Update
37
+ * both this line and the ledger `reason` in that file together.
32
38
  * - **`./safe-storage`** — the same side effect, and its whole point is being
33
39
  * imported FOR it (`import '@civitai/app-sdk/safe-storage'`). A side effect
34
40
  * on the root barrel is not something a consumer can opt out of.
package/dist/index.js CHANGED
@@ -25,10 +25,16 @@
25
25
  *
26
26
  * - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
27
27
  * (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
28
- * 119 symbols and that side effect into the root would make every OAuth app
28
+ * 135 symbols and that side effect into the root would make every OAuth app
29
29
  * that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
30
30
  * Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
31
31
  * audiences; see this package's README.
32
+ * 🔴 That 135 is ASSERTED, not merely quoted — by
33
+ * `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
34
+ * `surface.get('./blocks').size`. It said 119 here against a real 125 until
35
+ * 2026-10-04, because the existing checks pin the root as an exact union and
36
+ * each subpath's set relations, none of which can see a subpath GROW. Update
37
+ * both this line and the ledger `reason` in that file together.
32
38
  * - **`./safe-storage`** — the same side effect, and its whole point is being
33
39
  * imported FOR it (`import '@civitai/app-sdk/safe-storage'`). A side effect
34
40
  * on the root barrel is not something a consumer can opt out of.
@@ -75,7 +75,12 @@ export declare const WORKFLOW_STEP_TYPES: {
75
75
  readonly imageBackgroundRemoval: "Image background removal (BiRefNet)";
76
76
  /** Vectorize a raster image with a local StarVector / OmniSVG model. */
77
77
  readonly imageToSvg: "Raster image → SVG vectorization";
78
- /** LoRA / DoRA / embedding training. Long-running. */
78
+ /**
79
+ * LoRA / DoRA / embedding training. Long-running. Allowed on an App Block's
80
+ * pass-through arm, where `maxBuzz` (1–250) is its timeout in seconds only
81
+ * when the orchestrator returns no quote — see `WorkflowBodyPassThroughStep`
82
+ * in `@civitai/app-sdk/blocks`.
83
+ */
79
84
  readonly imageResourceTraining: "Train a LoRA / DoRA / embedding from a dataset";
80
85
  /** Pre-process an image (resize, ControlNet preprocessor, etc.). */
81
86
  readonly preprocessImage: "Image preprocessing (resize, ControlNet preprocessors, …)";
@@ -152,7 +157,8 @@ export declare const WORKFLOW_STEP_TYPES: {
152
157
  /**
153
158
  * Generic training step. Engines: `ai-toolkit`, `comfy`. Distinct from
154
159
  * {@link WORKFLOW_STEP_TYPES.imageResourceTraining}, which is the older
155
- * kohya/musubi/flux-dev-fast LoRA path.
160
+ * kohya/musubi/flux-dev-fast LoRA path. Allowed on an App Block's
161
+ * pass-through arm on the same terms as `imageResourceTraining`.
156
162
  */
157
163
  readonly training: "Model training (ai-toolkit / comfy engines)";
158
164
  /** Hash an image / video / model for dedup or lookup. */
@@ -213,6 +219,8 @@ export declare const WORKFLOW_STEP_TYPES: {
213
219
  readonly comfyNodepackSnapshot: "Snapshot a worker’s installed ComfyUI node packs (internal)";
214
220
  /** Qwen image benchmarking harness. */
215
221
  readonly qwenImageBench: "Qwen image benchmarking (internal)";
222
+ readonly liveTranscription: "Live speech-to-text from audio streamed while the user talks";
223
+ readonly merge: "Merge — combine fields from earlier steps' outputs into one result";
216
224
  };
217
225
  export type WorkflowStepType = keyof typeof WORKFLOW_STEP_TYPES;
218
226
  /**
@@ -77,7 +77,12 @@ export const WORKFLOW_STEP_TYPES = {
77
77
  imageBackgroundRemoval: 'Image background removal (BiRefNet)',
78
78
  /** Vectorize a raster image with a local StarVector / OmniSVG model. */
79
79
  imageToSvg: 'Raster image → SVG vectorization',
80
- /** LoRA / DoRA / embedding training. Long-running. */
80
+ /**
81
+ * LoRA / DoRA / embedding training. Long-running. Allowed on an App Block's
82
+ * pass-through arm, where `maxBuzz` (1–250) is its timeout in seconds only
83
+ * when the orchestrator returns no quote — see `WorkflowBodyPassThroughStep`
84
+ * in `@civitai/app-sdk/blocks`.
85
+ */
81
86
  imageResourceTraining: 'Train a LoRA / DoRA / embedding from a dataset',
82
87
  /** Pre-process an image (resize, ControlNet preprocessor, etc.). */
83
88
  preprocessImage: 'Image preprocessing (resize, ControlNet preprocessors, …)',
@@ -159,7 +164,8 @@ export const WORKFLOW_STEP_TYPES = {
159
164
  /**
160
165
  * Generic training step. Engines: `ai-toolkit`, `comfy`. Distinct from
161
166
  * {@link WORKFLOW_STEP_TYPES.imageResourceTraining}, which is the older
162
- * kohya/musubi/flux-dev-fast LoRA path.
167
+ * kohya/musubi/flux-dev-fast LoRA path. Allowed on an App Block's
168
+ * pass-through arm on the same terms as `imageResourceTraining`.
163
169
  */
164
170
  training: 'Model training (ai-toolkit / comfy engines)',
165
171
  // ----- Classification / tagging / moderation ----------------------------
@@ -228,6 +234,15 @@ export const WORKFLOW_STEP_TYPES = {
228
234
  comfyNodepackSnapshot: 'Snapshot a worker’s installed ComfyUI node packs (internal)',
229
235
  /** Qwen image benchmarking harness. */
230
236
  qwenImageBench: 'Qwen image benchmarking (internal)',
237
+ // ----- Auto-added 2026-10-06 from the orchestrator spec ----------------------
238
+ // Added by scripts/sync-orchestrator-catalogs.mjs: the live spec accepts
239
+ // these and this catalog did not list them. MOVE each entry into the right
240
+ // section above, and replace any TODO(catalog) line with a real one-line
241
+ // description — `pnpm check:catalogs` names every placeholder that is left.
242
+ // (That check is advisory, so it will not stop this merging. It is a
243
+ // reminder, not a gate.)
244
+ liveTranscription: 'Live speech-to-text from audio streamed while the user talks',
245
+ merge: "Merge — combine fields from earlier steps' outputs into one result",
231
246
  };
232
247
  /**
233
248
  * Engines that the `imageGen` step accepts. Each one has its own input shape;
@@ -3,7 +3,7 @@
3
3
  * own generated client (`@civitai/client`).
4
4
  *
5
5
  * The sibling `@civitai/app-sdk/orchestrator` module gives you the *catalog*
6
- * (`WORKFLOW_STEP_TYPES` — 51 `$type` names and what each one does) and the
6
+ * (`WORKFLOW_STEP_TYPES` — 53 `$type` names and what each one does) and the
7
7
  * fetch helpers (`submitWorkflow`, `estimateWorkflow`, …), but its body
8
8
  * builders take `input: unknown`. This module is the missing half: the actual
9
9
  * per-step input shapes, tracked against the orchestrator's OpenAPI spec by
@@ -65,12 +65,20 @@
65
65
  *
66
66
  * The practical consequence: a number of the 47 step types below exist to serve
67
67
  * Civitai's own pipelines rather than third-party apps — `modelPickleScan`,
68
- * `xGuardModeration`, `training`, `comfyNodepackSnapshot`, `qwenImageBench`,
68
+ * `xGuardModeration`, `comfyNodepackSnapshot`, `qwenImageBench`,
69
69
  * the `model*` / `media*` hashing and classification steps. They are in the
70
70
  * consumer spec, so they are typed here. They are not an invitation.
71
71
  *
72
+ * `training` and `imageResourceTraining` are the exception: an App Block's host
73
+ * explicitly ALLOWS both on the pass-through arm (`WorkflowBodyPassThroughStep`
74
+ * in `blocks/types.ts`), with the same spend rules as any other `$type` there
75
+ * (see `WorkflowBodyPassThroughStep.maxBuzz`). The trained checkpoint is not
76
+ * part of the block contract; publishing goes through Civitai's model wizard
77
+ * (`BlockWorkflowSnapshot.trainedEpochs`). For a standalone app the
78
+ * orchestrator's own authorization still decides.
79
+ *
72
80
  * ⚠️ `WORKFLOW_STEP_TYPES` does NOT mark most of them. Counted at this commit:
73
- * of its 51 entries, exactly TWO sit under its "Platform internals" heading —
81
+ * of its 53 entries, exactly TWO sit under its "Platform internals" heading —
74
82
  * `comfyNodepackSnapshot` and `qwenImageBench`. `training`, `webScrape`,
75
83
  * `xGuardModeration`, `modelPickleScan` and the `model*` / `media*` steps are
76
84
  * ordinary documented entries under ordinary headings, and `webScrape` carries
@@ -89,10 +97,10 @@
89
97
  * (#315) without anything going red. The measured state, derived rather than
90
98
  * typed:
91
99
  *
92
- * `WORKFLOW_STEP_TYPES` documents 51 `$type`s; this map covers 47. The 4 with
100
+ * `WORKFLOW_STEP_TYPES` documents 53 `$type`s; this map covers 47. The 6 with
93
101
  * no generated template in the pinned `@civitai/client` are `imageScanning`,
94
- * `preprocessVideo`, `soniloAudioGen`, `yuE2`, and `WorkflowStepTemplateFor<…>` is a compile
95
- * error for each of them.
102
+ * `liveTranscription`, `merge`, `preprocessVideo`, `soniloAudioGen`, `yuE2`, and
103
+ * `WorkflowStepTemplateFor<…>` is a compile error for each of them.
96
104
  *
97
105
  * That gap is EXPECTED and is not a defect in either surface. The catalog
98
106
  * tracks the LIVE orchestrator spec (`pnpm check:catalogs` and the daily
@@ -300,7 +308,7 @@ interface StepTemplateMap {
300
308
  xGuardModeration: XGuardModerationStepTemplate;
301
309
  }
302
310
  /**
303
- * `$type` → its step-template type, for 47 of the catalog's 51 step types.
311
+ * `$type` → its step-template type, for 47 of the catalog's 53 step types.
304
312
  *
305
313
  * Keyed by the WIRE name rather than the generated type name, because the wire
306
314
  * name is what you actually have in hand and the generator does not always
@@ -3,7 +3,7 @@
3
3
  * own generated client (`@civitai/client`).
4
4
  *
5
5
  * The sibling `@civitai/app-sdk/orchestrator` module gives you the *catalog*
6
- * (`WORKFLOW_STEP_TYPES` — 51 `$type` names and what each one does) and the
6
+ * (`WORKFLOW_STEP_TYPES` — 53 `$type` names and what each one does) and the
7
7
  * fetch helpers (`submitWorkflow`, `estimateWorkflow`, …), but its body
8
8
  * builders take `input: unknown`. This module is the missing half: the actual
9
9
  * per-step input shapes, tracked against the orchestrator's OpenAPI spec by
@@ -65,12 +65,20 @@
65
65
  *
66
66
  * The practical consequence: a number of the 47 step types below exist to serve
67
67
  * Civitai's own pipelines rather than third-party apps — `modelPickleScan`,
68
- * `xGuardModeration`, `training`, `comfyNodepackSnapshot`, `qwenImageBench`,
68
+ * `xGuardModeration`, `comfyNodepackSnapshot`, `qwenImageBench`,
69
69
  * the `model*` / `media*` hashing and classification steps. They are in the
70
70
  * consumer spec, so they are typed here. They are not an invitation.
71
71
  *
72
+ * `training` and `imageResourceTraining` are the exception: an App Block's host
73
+ * explicitly ALLOWS both on the pass-through arm (`WorkflowBodyPassThroughStep`
74
+ * in `blocks/types.ts`), with the same spend rules as any other `$type` there
75
+ * (see `WorkflowBodyPassThroughStep.maxBuzz`). The trained checkpoint is not
76
+ * part of the block contract; publishing goes through Civitai's model wizard
77
+ * (`BlockWorkflowSnapshot.trainedEpochs`). For a standalone app the
78
+ * orchestrator's own authorization still decides.
79
+ *
72
80
  * ⚠️ `WORKFLOW_STEP_TYPES` does NOT mark most of them. Counted at this commit:
73
- * of its 51 entries, exactly TWO sit under its "Platform internals" heading —
81
+ * of its 53 entries, exactly TWO sit under its "Platform internals" heading —
74
82
  * `comfyNodepackSnapshot` and `qwenImageBench`. `training`, `webScrape`,
75
83
  * `xGuardModeration`, `modelPickleScan` and the `model*` / `media*` steps are
76
84
  * ordinary documented entries under ordinary headings, and `webScrape` carries
@@ -89,10 +97,10 @@
89
97
  * (#315) without anything going red. The measured state, derived rather than
90
98
  * typed:
91
99
  *
92
- * `WORKFLOW_STEP_TYPES` documents 51 `$type`s; this map covers 47. The 4 with
100
+ * `WORKFLOW_STEP_TYPES` documents 53 `$type`s; this map covers 47. The 6 with
93
101
  * no generated template in the pinned `@civitai/client` are `imageScanning`,
94
- * `preprocessVideo`, `soniloAudioGen`, `yuE2`, and `WorkflowStepTemplateFor<…>` is a compile
95
- * error for each of them.
102
+ * `liveTranscription`, `merge`, `preprocessVideo`, `soniloAudioGen`, `yuE2`, and
103
+ * `WorkflowStepTemplateFor<…>` is a compile error for each of them.
96
104
  *
97
105
  * That gap is EXPECTED and is not a defect in either surface. The catalog
98
106
  * tracks the LIVE orchestrator spec (`pnpm check:catalogs` and the daily
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civitai/app-sdk",
3
- "version": "0.56.1",
3
+ "version": "0.57.0",
4
4
  "description": "OAuth + PKCE, encrypted-cookie sessions, scopes, and orchestrator helpers for building third-party Civitai apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",