@rslsp1/fa-app-tools 2.0.82 → 2.0.84

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/szenen.js CHANGED
@@ -38,6 +38,7 @@ __export(szenen_exports, {
38
38
  ContractViolation: () => ContractViolation,
39
39
  DERIVABLE_TYPES: () => DERIVABLE_TYPES,
40
40
  ELEMENT_VERSION_SCHEMA_FILE: () => ELEMENT_VERSION_SCHEMA_FILE,
41
+ FLOW_MODEL_DISPLAY_NAMES: () => FLOW_MODEL_DISPLAY_NAMES,
41
42
  PROBE_VERSION_ID: () => PROBE_VERSION_ID,
42
43
  REFERENCE_FIELD: () => REFERENCE_FIELD,
43
44
  RULES_WITHOUT_CAPABILITY_EFFECT: () => RULES_WITHOUT_CAPABILITY_EFFECT,
@@ -57,6 +58,7 @@ __export(szenen_exports, {
57
58
  VersionDagError: () => VersionDagError,
58
59
  areasFor: () => areasFor,
59
60
  assessDerivationResponse: () => assessDerivationResponse,
61
+ batchesForPromptRun: () => batchesForPromptRun,
60
62
  buildDerivationSystemInstruction: () => buildSystemInstruction2,
61
63
  buildDerivationUserPrompt: () => buildUserPrompt2,
62
64
  buildSystemInstruction: () => buildSystemInstruction,
@@ -89,6 +91,7 @@ __export(szenen_exports, {
89
91
  checkSzenenBatchOrigin: () => checkSzenenBatchOrigin,
90
92
  checkTitle: () => checkTitle,
91
93
  checkWriteDraftInput: () => checkWriteDraftInput,
94
+ collectImageBatches: () => collectImageBatches,
92
95
  collectImageGenerationOrders: () => collectImageGenerationOrders,
93
96
  collectPromptRuns: () => collectPromptRuns,
94
97
  collectProviderChecks: () => collectProviderChecks,
@@ -102,10 +105,14 @@ __export(szenen_exports, {
102
105
  createEventLogDerivationPersistence: () => createEventLogDerivationPersistence,
103
106
  createEventLogRunPersistence: () => createEventLogRunPersistence,
104
107
  createFakeTextGenerationPort: () => createFakeTextGenerationPort,
108
+ createFlowImageProvider: () => createFlowImageProvider,
105
109
  createFlowSzenenViewPort: () => createFlowSzenenViewPort,
106
110
  createFlowTextGenerationPort: () => createFlowTextGenerationPort,
107
111
  createHfElementStore: () => createHfElementStore,
108
112
  createHfEventChannel: () => createHfEventChannel,
113
+ createHfImageBatchHistory: () => createHfImageBatchHistory,
114
+ createHfImageBatchWrite: () => createHfImageBatchWrite,
115
+ createHfImageGenerationBatchPersistence: () => createHfImageGenerationBatchPersistence,
109
116
  createHfImageLibrary: () => createHfImageLibrary,
110
117
  createImageGenerationPort: () => createImageGenerationPort,
111
118
  createInMemoryEventChannel: () => createInMemoryEventChannel,
@@ -119,12 +126,14 @@ __export(szenen_exports, {
119
126
  differentialSteps: () => differentialSteps,
120
127
  draftRevisionOf: () => draftRevisionOf,
121
128
  elementStoreContractChecks: () => elementStoreContractChecks,
129
+ findImageBatch: () => findImageBatch,
122
130
  findImageGenerationOrder: () => findImageGenerationOrder,
123
131
  findPromptRun: () => findPromptRun,
124
132
  findProviderCheck: () => findProviderCheck,
125
133
  findSceneDerivation: () => findSceneDerivation,
126
134
  findSceneDerivationRecovery: () => findSceneDerivationRecovery,
127
135
  firstError: () => firstError,
136
+ flowImageProviderHasAccess: () => flowImageProviderHasAccess,
128
137
  formatSchemaErrors: () => formatSchemaErrors,
129
138
  freezeDraftChain: () => freezeDraftChain,
130
139
  getSzenenSchemas: () => getSzenenSchemas,
@@ -5327,7 +5336,30 @@ var SZENEN_EVENT_TYPES = {
5327
5336
  * ebenfalls keine Bilddaten — nur Status, Zaehlung und die Item-Kennung,
5328
5337
  * falls eine entstand.
5329
5338
  */
5330
- imageGenerationFinished: "szenen.image_generation_finished"
5339
+ imageGenerationFinished: "szenen.image_generation_finished",
5340
+ /**
5341
+ * Ein Bild-Batch ist kanonisch gespeichert — Stufe 6.9.2, Gate D
5342
+ * (D-6.9.2-6).
5343
+ *
5344
+ * Erscheint erst, **nachdem** jedes Bild des Batches im Bildbestand liegt
5345
+ * (`hfUploadImage` + `image_added`, dieselbe Reihenfolge wie
5346
+ * `hfImageLibrary.putImage`) — kein Bild gilt als vorhanden, bevor sein
5347
+ * Upload bestätigt ist (R3). Trägt bewusst **keine Bilddaten**, nur den
5348
+ * vollständigen Metadatennachweis, den `listBatchesForPromptRun()` /
5349
+ * `getBatch()` ohne MySQL rekonstruieren können müssen: `item_id`,
5350
+ * `prompt_run_id`, `generation_batch_id`, `source_kind`, je Bild
5351
+ * `position`, `media_ref`, `asset_id`, `model`, `seed`, `width`/`height`.
5352
+ *
5353
+ * Additiv wie jedes Ereignis dieser Domain: ein zweiter Batch desselben
5354
+ * Laufs ist ein **weiteres** Ereignis mit derselben `item_id`, nicht eine
5355
+ * Änderung am ersten (D-113/D-121 — ein Lauf bleibt ein Item). Gilt für
5356
+ * `generated_in_app` **und** `external_import` — nur `source_kind`
5357
+ * unterscheidet die beiden Schreiber (D-116/D-117/D-120).
5358
+ *
5359
+ * Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
5360
+ * wird mit `collectImageBatches` (`imageBatchStore.ts`).
5361
+ */
5362
+ imageBatchPersisted: "szenen.image_batch_persisted"
5331
5363
  };
5332
5364
  function sortForReplay(events) {
5333
5365
  return [...events].sort((a, b) => {
@@ -9415,6 +9447,32 @@ var IMAGE_MODEL_CATALOGUE = {
9415
9447
  max_images: 4,
9416
9448
  aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
9417
9449
  supports_seed: true
9450
+ },
9451
+ /**
9452
+ * Stufe 6.9.2, Nachtrag — real gegen Flow geprüft (2026-08-17): Der
9453
+ * Modellwähler im Flow-„Generieren"-Tab zeigt aktuell nur "Nano Banana Pro"
9454
+ * und "Nano Banana 2" als wählbar an, "Imagen 4" erscheint dort ausgegraut.
9455
+ * `provider: 'flow'` statt `'google'` ist bewusst: Diese Modelle sind nur
9456
+ * über `Flow.generate.image` erreichbar, nicht über die direkte
9457
+ * Google-Imagen-REST-Route, die der React-Host benutzt (`reactImageProvider.ts`)
9458
+ * — React meldet sie deshalb korrekt als `unsupported`, ohne eine eigene
9459
+ * Host-Verzweigung zu brauchen (D-115/R-1: `hasAccess` prüft `provider`).
9460
+ */
9461
+ nano_banana_pro: {
9462
+ callable_model_id: "flow-nano-banana-pro",
9463
+ provider: "flow",
9464
+ max_images: 4,
9465
+ // Flow.generate.image dokumentiert nur '1:1' | '16:9' | '9:16' (howto-flow-sdk.md).
9466
+ aspect_ratios: ["1:1", "16:9", "9:16"],
9467
+ // Weder als Eingabe- noch als Ausgabeparameter dokumentiert — nichts erfunden (D-107).
9468
+ supports_seed: false
9469
+ },
9470
+ nano_banana_2: {
9471
+ callable_model_id: "flow-nano-banana-2",
9472
+ provider: "flow",
9473
+ max_images: 4,
9474
+ aspect_ratios: ["1:1", "16:9", "9:16"],
9475
+ supports_seed: false
9418
9476
  }
9419
9477
  };
9420
9478
  function callableImageModelFor(targetModelKey) {
@@ -10996,6 +11054,299 @@ function createClientRunPort(options) {
10996
11054
  };
10997
11055
  }
10998
11056
 
11057
+ // src/lib/szenen/flowImageProvider.ts
11058
+ var FLOW_MODEL_DISPLAY_NAMES = {
11059
+ "imagen-4.0-generate-001": "Imagen 4",
11060
+ "flow-nano-banana-pro": "\u{1F34C} Nano Banana Pro",
11061
+ "flow-nano-banana-2": "\u{1F34C} Nano Banana 2"
11062
+ };
11063
+ function flowImageProviderHasAccess(model) {
11064
+ return model.callable_model_id in FLOW_MODEL_DISPLAY_NAMES;
11065
+ }
11066
+ function istNichtleererText(wert) {
11067
+ return typeof wert === "string" && wert.trim().length > 0;
11068
+ }
11069
+ function createFlowImageProvider(generateImage) {
11070
+ return {
11071
+ async generate(input) {
11072
+ const modelDisplayName = FLOW_MODEL_DISPLAY_NAMES[input.callableModelId];
11073
+ if (!modelDisplayName) {
11074
+ throw new Error(
11075
+ `Der Flow-Host kennt keinen Flow-Display-Namen zu "${input.callableModelId}". Bekannt sind: ${Object.keys(FLOW_MODEL_DISPLAY_NAMES).join(", ")}. Es wird kein anderes Modell ersatzweise gerufen (D-115/D-6.9.2-3).`
11076
+ );
11077
+ }
11078
+ if (istNichtleererText(input.negativePrompt)) {
11079
+ throw new Error(
11080
+ "Flow.generate.image kennt keinen dokumentierten Parameter f\xFCr einen negativen Prompt. Der Auftrag wird deshalb als fehlgeschlagen protokolliert, statt den negativen Prompt still zu verlieren (D-6.9.2-3)."
11081
+ );
11082
+ }
11083
+ const erzeugt = [];
11084
+ let ersterFehler = null;
11085
+ for (let position = 0; position < input.imageCount; position++) {
11086
+ try {
11087
+ const antwort = await generateImage({
11088
+ prompt: input.positivePrompt,
11089
+ modelDisplayName,
11090
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}
11091
+ });
11092
+ if (!antwort || typeof antwort.base64 !== "string" || antwort.base64.length === 0) {
11093
+ throw new Error("Flow.generate.image hat kein Bild geliefert.");
11094
+ }
11095
+ erzeugt.push({
11096
+ imageBase64: antwort.base64,
11097
+ // Das tatsaechlich gerufene Modell, nicht der Flow-Display-Name —
11098
+ // im Bestand soll stehen, was bezahlt wurde (wie reactImageProvider.ts).
11099
+ model: input.callableModelId
11100
+ // Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
11101
+ // geliefert — nichts wird erfunden (D-107).
11102
+ });
11103
+ } catch (fehler4) {
11104
+ ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
11105
+ break;
11106
+ }
11107
+ }
11108
+ if (erzeugt.length === 0) {
11109
+ throw new Error(
11110
+ ersterFehler ?? `Flow.generate.image hat zu ${input.callableModelId} kein verwertbares Bild geliefert.`
11111
+ );
11112
+ }
11113
+ return { images: erzeugt };
11114
+ }
11115
+ };
11116
+ }
11117
+
11118
+ // src/lib/szenen/imageBatchStore.ts
11119
+ var istText4 = (wert) => typeof wert === "string" && wert.trim().length > 0;
11120
+ function alsBilder(rohe) {
11121
+ if (!Array.isArray(rohe)) return [];
11122
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText4(b.media_ref) && typeof b.position === "number").map((b) => ({
11123
+ media_ref: b.media_ref,
11124
+ position: b.position,
11125
+ ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
11126
+ model: b.model ?? null,
11127
+ seed: b.seed ?? null,
11128
+ width: b.width ?? null,
11129
+ height: b.height ?? null
11130
+ }));
11131
+ }
11132
+ function collectImageBatches(events) {
11133
+ const byItemId = /* @__PURE__ */ new Map();
11134
+ for (const event of events) {
11135
+ if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
11136
+ const payload = event.payload;
11137
+ if (!payload || !istText4(payload.item_id) || !istText4(payload.prompt_run_id) || !istText4(payload.generation_batch_id)) continue;
11138
+ if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
11139
+ const bestehend = byItemId.get(payload.item_id);
11140
+ if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
11141
+ const bilder = alsBilder(payload.images);
11142
+ if (!bestehend) {
11143
+ byItemId.set(payload.item_id, {
11144
+ item_id: payload.item_id,
11145
+ prompt_run_id: payload.prompt_run_id,
11146
+ generation_batch_ids: [payload.generation_batch_id],
11147
+ source_kind: payload.source_kind,
11148
+ title: istText4(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
11149
+ images: bilder
11150
+ });
11151
+ continue;
11152
+ }
11153
+ byItemId.set(payload.item_id, {
11154
+ ...bestehend,
11155
+ generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
11156
+ images: [...bestehend.images, ...bilder]
11157
+ });
11158
+ }
11159
+ return [...byItemId.values()];
11160
+ }
11161
+ function batchesForPromptRun(events, runId) {
11162
+ return collectImageBatches(events).filter((b) => b.prompt_run_id === runId);
11163
+ }
11164
+ function findImageBatch(events, batchId) {
11165
+ return collectImageBatches(events).find((b) => b.generation_batch_ids.includes(batchId)) ?? null;
11166
+ }
11167
+
11168
+ // src/lib/szenen/hfImageBatchHistory.ts
11169
+ function lesefehler(aktion, error) {
11170
+ return {
11171
+ kind: "error",
11172
+ message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`,
11173
+ retryable: true
11174
+ };
11175
+ }
11176
+ function createHfImageBatchHistory(options) {
11177
+ const { channel } = options;
11178
+ return {
11179
+ async listBatchesForPromptRun(runId) {
11180
+ try {
11181
+ const events = await channel.readEvents();
11182
+ return { kind: "ok", value: batchesForPromptRun(events, runId) };
11183
+ } catch (error) {
11184
+ return lesefehler(`Batches fuer Lauf ${runId} konnten nicht gelesen werden`, error);
11185
+ }
11186
+ },
11187
+ async getBatch(batchId) {
11188
+ try {
11189
+ const events = await channel.readEvents();
11190
+ return { kind: "ok", value: findImageBatch(events, batchId) };
11191
+ } catch (error) {
11192
+ return lesefehler(`Batch ${batchId} konnte nicht gelesen werden`, error);
11193
+ }
11194
+ },
11195
+ async listOrdersForPromptRun(runId) {
11196
+ try {
11197
+ const events = await channel.readEvents();
11198
+ return { kind: "ok", value: ordersForPromptRun(events, runId) };
11199
+ } catch (error) {
11200
+ return lesefehler(`Bildgenerierungsauftraege fuer Lauf ${runId} konnten nicht gelesen werden`, error);
11201
+ }
11202
+ }
11203
+ };
11204
+ }
11205
+
11206
+ // src/lib/szenen/hfImageBatchPersistence.ts
11207
+ function fehler3(nachricht, retryable = true) {
11208
+ return { kind: "error", message: nachricht, retryable };
11209
+ }
11210
+ async function legeBatchAb(options, input) {
11211
+ const { namespace, token, channel } = options;
11212
+ const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
11213
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
11214
+ const itemId = itemIdForRun(input.runId);
11215
+ let vorhandeneEvents;
11216
+ try {
11217
+ vorhandeneEvents = await channel.readEvents();
11218
+ } catch (error) {
11219
+ return fehler3(
11220
+ `Bild-Batches von Lauf ${input.runId} konnten vor dem Ablegen nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`
11221
+ );
11222
+ }
11223
+ const bestehend = batchesForPromptRun(vorhandeneEvents, input.runId)[0] ?? null;
11224
+ if (bestehend?.generation_batch_ids.includes(input.generationBatchId)) {
11225
+ return { kind: "ok", value: bestehend };
11226
+ }
11227
+ const startPosition = bestehend?.images.length ?? 0;
11228
+ const abgelegt = [];
11229
+ for (let offset = 0; offset < input.images.length; offset++) {
11230
+ const bild = input.images[offset];
11231
+ const id = newImageId(input.generationBatchId, startPosition + offset);
11232
+ try {
11233
+ await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
11234
+ await writeHFEvent(namespace, token, "image_added", {
11235
+ id,
11236
+ prompt: input.title ?? `Batch ${input.generationBatchId}`,
11237
+ tags: [],
11238
+ timestamp: now().getTime(),
11239
+ mimeType: bild.mimeType
11240
+ }, []);
11241
+ } catch (error) {
11242
+ return fehler3(
11243
+ `Bild ${offset + 1}/${input.images.length} des Batches ${input.generationBatchId} konnte nicht im Bildbestand abgelegt werden: ${error instanceof Error ? error.message : String(error)}. Bereits abgelegte Bilder dieses Versuchs (${abgelegt.length}) bleiben im Bestand, geh\xF6ren aber zu keinem Batch, solange das Batch-Ereignis nicht geschrieben ist.`
11244
+ );
11245
+ }
11246
+ abgelegt.push({
11247
+ position: startPosition + offset,
11248
+ media_ref: id,
11249
+ asset_id: id,
11250
+ model: bild.model ?? null,
11251
+ seed: bild.seed ?? null,
11252
+ width: bild.width ?? null,
11253
+ height: bild.height ?? null
11254
+ });
11255
+ }
11256
+ const titel = bestehend?.title ?? (input.title?.trim() || `Batch aus Lauf ${input.runId}`);
11257
+ const payload = {
11258
+ item_id: itemId,
11259
+ prompt_run_id: input.runId,
11260
+ generation_batch_id: input.generationBatchId,
11261
+ source_kind: input.sourceKind,
11262
+ title: titel,
11263
+ images: abgelegt,
11264
+ created_at: now().toISOString()
11265
+ };
11266
+ try {
11267
+ await channel.appendEvent(SZENEN_EVENT_TYPES.imageBatchPersisted, payload);
11268
+ } catch (error) {
11269
+ return fehler3(
11270
+ `Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
11271
+ );
11272
+ }
11273
+ const zusammengefuehrt = {
11274
+ item_id: itemId,
11275
+ prompt_run_id: input.runId,
11276
+ generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
11277
+ source_kind: bestehend?.source_kind ?? input.sourceKind,
11278
+ title: titel,
11279
+ images: [...bestehend?.images ?? [], ...abgelegt]
11280
+ };
11281
+ return { kind: "ok", value: zusammengefuehrt };
11282
+ }
11283
+ function createHfImageGenerationBatchPersistence(options) {
11284
+ return {
11285
+ async persist(input) {
11286
+ return legeBatchAb(options, {
11287
+ runId: input.runId,
11288
+ generationBatchId: input.generationBatchId,
11289
+ sourceKind: "generated_in_app",
11290
+ images: input.images.map((bild) => ({
11291
+ imageBase64: bild.imageBase64,
11292
+ // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
11293
+ // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
11294
+ // keinen MIME-Typ je Bild.
11295
+ mimeType: "image/png",
11296
+ model: bild.model ?? null,
11297
+ seed: bild.seed ?? null,
11298
+ width: bild.width ?? null,
11299
+ height: bild.height ?? null
11300
+ }))
11301
+ });
11302
+ }
11303
+ };
11304
+ }
11305
+ function createHfImageBatchWrite(options) {
11306
+ return {
11307
+ async importBatch(input) {
11308
+ return legeBatchAb(options, {
11309
+ runId: input.runId,
11310
+ generationBatchId: input.generationBatchId,
11311
+ sourceKind: "external_import",
11312
+ title: input.title,
11313
+ images: input.images.map((bild) => ({
11314
+ imageBase64: bild.imageBase64,
11315
+ mimeType: "image/png",
11316
+ model: bild.model ?? null,
11317
+ seed: bild.seed ?? null,
11318
+ width: bild.width ?? null,
11319
+ height: bild.height ?? null
11320
+ }))
11321
+ });
11322
+ },
11323
+ async attachLibraryItem(input) {
11324
+ let events;
11325
+ try {
11326
+ events = await options.channel.readEvents();
11327
+ } catch (error) {
11328
+ return {
11329
+ kind: "error",
11330
+ message: `Batches konnten nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
11331
+ retryable: true
11332
+ };
11333
+ }
11334
+ const item = collectImageBatches(events).find((b) => b.item_id === input.itemId);
11335
+ if (!item) {
11336
+ return { kind: "error", message: `Item ${input.itemId} ist unbekannt.`, retryable: false };
11337
+ }
11338
+ if (item.prompt_run_id !== input.runId) {
11339
+ return {
11340
+ kind: "conflict",
11341
+ message: `Item ${input.itemId} geh\xF6rt bereits zu Lauf ${item.prompt_run_id}.`,
11342
+ existingPromptRunId: item.prompt_run_id
11343
+ };
11344
+ }
11345
+ return { kind: "ok", value: item };
11346
+ }
11347
+ };
11348
+ }
11349
+
10999
11350
  // src/lib/szenen/flowViewPort.ts
11000
11351
  var defaultContext = {
11001
11352
  async chainFor(contextId) {
@@ -11043,8 +11394,36 @@ function createFlowSzenenViewPort(options) {
11043
11394
  ...options.newRunId ? { newRunId: options.newRunId } : {},
11044
11395
  ...options.now ? { now: options.now } : {}
11045
11396
  });
11046
- const port = { elements, context, runs, derivation };
11397
+ const imageBatchHistory = createHfImageBatchHistory({ channel });
11398
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11399
+ const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
11400
+ namespace: options.namespace,
11401
+ token: options.token,
11402
+ channel,
11403
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11404
+ ...options.now ? { now: options.now } : {}
11405
+ }) : void 0;
11406
+ const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
11407
+ const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
11408
+ runs: { async getRun(runId) {
11409
+ return runs.getRun(runId);
11410
+ } },
11411
+ provider: bildProvider,
11412
+ batches: createHfImageGenerationBatchPersistence({
11413
+ namespace: options.namespace,
11414
+ token: options.token,
11415
+ channel,
11416
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11417
+ ...options.now ? { now: options.now } : {}
11418
+ }),
11419
+ events: channel,
11420
+ hasAccess: options.imageProviderHasAccess ?? flowImageProviderHasAccess,
11421
+ ...options.now ? { now: options.now } : {}
11422
+ }) : void 0;
11423
+ const port = { elements, context, runs, derivation, imageBatchHistory };
11047
11424
  if (imageLibrary) port.imageLibrary = imageLibrary;
11425
+ if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
11426
+ if (imageGeneration) port.imageGeneration = imageGeneration;
11048
11427
  return port;
11049
11428
  }
11050
11429
 
@@ -11172,7 +11551,7 @@ var PFLICHTFELDER = [
11172
11551
  "system_prompt",
11173
11552
  "created_at"
11174
11553
  ];
11175
- function istText4(wert) {
11554
+ function istText5(wert) {
11176
11555
  return typeof wert === "string" && wert.trim().length > 0;
11177
11556
  }
11178
11557
  function isObject10(wert) {
@@ -11186,7 +11565,7 @@ function pruefeLaufherkunft(quelle) {
11186
11565
  }
11187
11566
  if (quelle.kind === "published") {
11188
11567
  for (const feld of VERSIONSFELDER) {
11189
- if (!istText4(quelle[feld])) {
11568
+ if (!istText5(quelle[feld])) {
11190
11569
  return { ok: false, message: `"source.${feld}" fehlt oder ist leer.` };
11191
11570
  }
11192
11571
  }
@@ -11198,7 +11577,7 @@ function pruefeLaufherkunft(quelle) {
11198
11577
  message: `"source.kind" ist unbekannt: ${JSON.stringify(quelle.kind)}. Erlaubt: published, draft_chain.`
11199
11578
  };
11200
11579
  }
11201
- if (!istText4(quelle.chain_id)) {
11580
+ if (!istText5(quelle.chain_id)) {
11202
11581
  return { ok: false, message: '"source.chain_id" fehlt oder ist leer.' };
11203
11582
  }
11204
11583
  if (!Array.isArray(quelle.nodes) || quelle.nodes.length === 0) {
@@ -11217,7 +11596,7 @@ function pruefeLaufherkunft(quelle) {
11217
11596
  message: `"source.nodes[${index}].type" ist unbekannt: ${JSON.stringify(knoten.type)}.`
11218
11597
  };
11219
11598
  }
11220
- if (!istText4(knoten.element_id) || !istText4(knoten.draft_id)) {
11599
+ if (!istText5(knoten.element_id) || !istText5(knoten.draft_id)) {
11221
11600
  return {
11222
11601
  ok: false,
11223
11602
  message: `"source.nodes[${index}]" braucht element_id und draft_id.`
@@ -11230,10 +11609,10 @@ function pruefeLaufherkunft(quelle) {
11230
11609
  };
11231
11610
  }
11232
11611
  }
11233
- if (quelle.derivation_run_id !== void 0 && !istText4(quelle.derivation_run_id)) {
11612
+ if (quelle.derivation_run_id !== void 0 && !istText5(quelle.derivation_run_id)) {
11234
11613
  return { ok: false, message: '"source.derivation_run_id" ist gesetzt, aber leer.' };
11235
11614
  }
11236
- if (quelle.derivation_recovery_id !== void 0 && !istText4(quelle.derivation_recovery_id)) {
11615
+ if (quelle.derivation_recovery_id !== void 0 && !istText5(quelle.derivation_recovery_id)) {
11237
11616
  return { ok: false, message: '"source.derivation_recovery_id" ist gesetzt, aber leer.' };
11238
11617
  }
11239
11618
  return { ok: true };
@@ -11254,13 +11633,13 @@ function checkPromptRunRecord(candidate) {
11254
11633
  }
11255
11634
  const pflicht = ausKette ? PFLICHTFELDER.filter((f) => !VERSIONSFELDER.includes(f)) : PFLICHTFELDER;
11256
11635
  for (const feld of pflicht) {
11257
- if (!istText4(record[feld])) {
11636
+ if (!istText5(record[feld])) {
11258
11637
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
11259
11638
  }
11260
11639
  }
11261
11640
  if (ausKette) {
11262
11641
  for (const feld of VERSIONSFELDER) {
11263
- if (istText4(record[feld])) {
11642
+ if (istText5(record[feld])) {
11264
11643
  return {
11265
11644
  ok: false,
11266
11645
  message: `"${feld}" ist bei einem Lauf aus einer Draft-Kette gesetzt. Dort gibt es keine Versionen, und erfundene sind unzulaessig (D-98).`
@@ -11298,7 +11677,7 @@ function checkPromptRunRecord(candidate) {
11298
11677
  message: `"failure.class" ist unbekannt: ${JSON.stringify(failure.class)}. Erlaubt sind ${FAILURE_CLASSES.join(", ")}.`
11299
11678
  };
11300
11679
  }
11301
- if (!istText4(failure.message)) {
11680
+ if (!istText5(failure.message)) {
11302
11681
  return { ok: false, message: '"failure.message" fehlt oder ist leer.' };
11303
11682
  }
11304
11683
  return { ok: true };
@@ -11340,7 +11719,7 @@ function checkSceneDerivationRecord(candidate) {
11340
11719
  }
11341
11720
  const record = candidate;
11342
11721
  for (const feld of DERIVATION_PFLICHTFELDER) {
11343
- if (!istText4(record[feld])) {
11722
+ if (!istText5(record[feld])) {
11344
11723
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
11345
11724
  }
11346
11725
  }
@@ -11395,13 +11774,13 @@ function checkSceneDerivationRecord(candidate) {
11395
11774
  return { ok: false, message: '"follows" muss ein Objekt sein.' };
11396
11775
  }
11397
11776
  const f = folgt;
11398
- if (!istText4(f.run_id)) {
11777
+ if (!istText5(f.run_id)) {
11399
11778
  return { ok: false, message: '"follows.run_id" fehlt oder ist leer.' };
11400
11779
  }
11401
11780
  if (f.run_id === record.run_id) {
11402
11781
  return { ok: false, message: '"follows.run_id" zeigt auf den Lauf selbst.' };
11403
11782
  }
11404
- if (!istText4(f.note)) {
11783
+ if (!istText5(f.note)) {
11405
11784
  return {
11406
11785
  ok: false,
11407
11786
  message: '"follows.note" fehlt oder ist leer \u2014 ohne Beanstandung ist ein Folgelauf nicht nachvollziehbar (D-89).'
@@ -11415,7 +11794,7 @@ function checkSceneDerivationRecord(candidate) {
11415
11794
  }
11416
11795
  }
11417
11796
  const reply = record.reply;
11418
- if (reply !== void 0 && !istText4(reply)) {
11797
+ if (reply !== void 0 && !istText5(reply)) {
11419
11798
  return {
11420
11799
  ok: false,
11421
11800
  message: `"reply" ist unbrauchbar: erwartet wird eine nicht leere Zeichenkette, angekommen ist ${JSON.stringify(reply)}. Ohne "reply" einreichen ist zulaessig.`
@@ -11435,7 +11814,7 @@ function checkSceneDerivationRecord(candidate) {
11435
11814
  message: `"failure.class" ist unbekannt: ${JSON.stringify(failure.class)}. Erlaubt sind ${FAILURE_CLASSES.join(", ")}.`
11436
11815
  };
11437
11816
  }
11438
- if (!istText4(failure.message)) {
11817
+ if (!istText5(failure.message)) {
11439
11818
  return { ok: false, message: '"failure.message" fehlt oder ist leer.' };
11440
11819
  }
11441
11820
  if (Array.isArray(record.proposals) && record.proposals.length > 0) {
@@ -11490,7 +11869,7 @@ function checkSceneDerivationRecord(candidate) {
11490
11869
  };
11491
11870
  }
11492
11871
  for (const [gapIndex, luecke] of gaps.entries()) {
11493
- if (!luecke || typeof luecke !== "object" || !istText4(luecke.pointer) || !istText4(luecke.reason)) {
11872
+ if (!luecke || typeof luecke !== "object" || !istText5(luecke.pointer) || !istText5(luecke.reason)) {
11494
11873
  return {
11495
11874
  ok: false,
11496
11875
  message: `"proposals[${index}].gaps[${gapIndex}]" braucht pointer und reason.`
@@ -11516,7 +11895,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
11516
11895
  }
11517
11896
  const record = candidate;
11518
11897
  for (const feld of ["recovery_id", "run_id", "created_at"]) {
11519
- if (!istText4(record[feld])) {
11898
+ if (!istText5(record[feld])) {
11520
11899
  return { ok: false, message: `"${feld}" fehlt oder ist leer.` };
11521
11900
  }
11522
11901
  }
@@ -11543,7 +11922,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
11543
11922
  return { ok: false, message: '"diagnostics" muss ein Array sein (auch ein leeres).' };
11544
11923
  }
11545
11924
  for (const [index, befund] of record.diagnostics.entries()) {
11546
- if (!befund || typeof befund !== "object" || !istText4(befund.code) || !istText4(befund.message)) {
11925
+ if (!befund || typeof befund !== "object" || !istText5(befund.code) || !istText5(befund.message)) {
11547
11926
  return {
11548
11927
  ok: false,
11549
11928
  message: `"diagnostics[${index}]" braucht code und message.`
@@ -11595,7 +11974,7 @@ function checkSceneDerivationRecoveryRecord(candidate) {
11595
11974
  }
11596
11975
  if (Array.isArray(gaps)) {
11597
11976
  for (const [gapIndex, luecke] of gaps.entries()) {
11598
- if (!luecke || typeof luecke !== "object" || !istText4(luecke.pointer) || !istText4(luecke.reason)) {
11977
+ if (!luecke || typeof luecke !== "object" || !istText5(luecke.pointer) || !istText5(luecke.reason)) {
11599
11978
  return {
11600
11979
  ok: false,
11601
11980
  message: `"proposals[${index}].gaps[${gapIndex}]" braucht pointer und reason.`
@@ -11692,13 +12071,13 @@ function noticeFor(ergebnis, was) {
11692
12071
  };
11693
12072
  }
11694
12073
  case "rejected": {
11695
- const fehler3 = ergebnis.errors ?? [];
12074
+ const fehler4 = ergebnis.errors ?? [];
11696
12075
  const ungeprueft = ergebnis.unchecked ?? [];
11697
12076
  return {
11698
12077
  tone: "rejected",
11699
- text: `${was}: abgelehnt \u2014 ${fehler3.length} Befund${fehler3.length === 1 ? "" : "e"}.`,
12078
+ text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
11700
12079
  details: [
11701
- ...fehler3.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
12080
+ ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
11702
12081
  // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
11703
12082
  // sondern ungeprüft.
11704
12083
  ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
@@ -12204,7 +12583,7 @@ var ERKLAERUNG = {
12204
12583
  };
12205
12584
  var CapabilityPanel = ({ port, elementId, setupPayload }) => {
12206
12585
  const [bericht, setBericht] = (0, import_react4.useState)(null);
12207
- const [fehler3, setFehler] = (0, import_react4.useState)(null);
12586
+ const [fehler4, setFehler] = (0, import_react4.useState)(null);
12208
12587
  (0, import_react4.useEffect)(() => {
12209
12588
  let abgemeldet = false;
12210
12589
  setBericht(null);
@@ -12226,10 +12605,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
12226
12605
  abgemeldet = true;
12227
12606
  };
12228
12607
  }, [port, elementId, setupPayload]);
12229
- if (fehler3) {
12608
+ if (fehler4) {
12230
12609
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { "data-testid": "capability-fehler", role: "alert", children: [
12231
12610
  "F\xE4higkeitsbericht nicht erstellbar: ",
12232
- fehler3
12611
+ fehler4
12233
12612
  ] });
12234
12613
  }
12235
12614
  if (!bericht) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
@@ -12731,10 +13110,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
12731
13110
  setNotiz(
12732
13111
  ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
12733
13112
  );
12734
- } catch (fehler3) {
13113
+ } catch (fehler4) {
12735
13114
  setNotiz({
12736
13115
  tone: "error",
12737
- text: `Externe Bilder importieren: ${fehler3 instanceof Error ? fehler3.message : String(fehler3)}`
13116
+ text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
12738
13117
  });
12739
13118
  }
12740
13119
  setLaeuft(false);
@@ -14542,6 +14921,7 @@ var SzenenView = ({
14542
14921
  ContractViolation,
14543
14922
  DERIVABLE_TYPES,
14544
14923
  ELEMENT_VERSION_SCHEMA_FILE,
14924
+ FLOW_MODEL_DISPLAY_NAMES,
14545
14925
  PROBE_VERSION_ID,
14546
14926
  REFERENCE_FIELD,
14547
14927
  RULES_WITHOUT_CAPABILITY_EFFECT,
@@ -14561,6 +14941,7 @@ var SzenenView = ({
14561
14941
  VersionDagError,
14562
14942
  areasFor,
14563
14943
  assessDerivationResponse,
14944
+ batchesForPromptRun,
14564
14945
  buildDerivationSystemInstruction,
14565
14946
  buildDerivationUserPrompt,
14566
14947
  buildSystemInstruction,
@@ -14593,6 +14974,7 @@ var SzenenView = ({
14593
14974
  checkSzenenBatchOrigin,
14594
14975
  checkTitle,
14595
14976
  checkWriteDraftInput,
14977
+ collectImageBatches,
14596
14978
  collectImageGenerationOrders,
14597
14979
  collectPromptRuns,
14598
14980
  collectProviderChecks,
@@ -14606,10 +14988,14 @@ var SzenenView = ({
14606
14988
  createEventLogDerivationPersistence,
14607
14989
  createEventLogRunPersistence,
14608
14990
  createFakeTextGenerationPort,
14991
+ createFlowImageProvider,
14609
14992
  createFlowSzenenViewPort,
14610
14993
  createFlowTextGenerationPort,
14611
14994
  createHfElementStore,
14612
14995
  createHfEventChannel,
14996
+ createHfImageBatchHistory,
14997
+ createHfImageBatchWrite,
14998
+ createHfImageGenerationBatchPersistence,
14613
14999
  createHfImageLibrary,
14614
15000
  createImageGenerationPort,
14615
15001
  createInMemoryEventChannel,
@@ -14623,12 +15009,14 @@ var SzenenView = ({
14623
15009
  differentialSteps,
14624
15010
  draftRevisionOf,
14625
15011
  elementStoreContractChecks,
15012
+ findImageBatch,
14626
15013
  findImageGenerationOrder,
14627
15014
  findPromptRun,
14628
15015
  findProviderCheck,
14629
15016
  findSceneDerivation,
14630
15017
  findSceneDerivationRecovery,
14631
15018
  firstError,
15019
+ flowImageProviderHasAccess,
14632
15020
  formatSchemaErrors,
14633
15021
  freezeDraftChain,
14634
15022
  getSzenenSchemas,