@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/index.js CHANGED
@@ -786,7 +786,7 @@ __export(index_exports, {
786
786
  module.exports = __toCommonJS(index_exports);
787
787
 
788
788
  // package.json
789
- var version = "2.0.82";
789
+ var version = "2.0.84";
790
790
 
791
791
  // src/hooks/useOnClickOutside.ts
792
792
  var import_react = require("react");
@@ -6593,13 +6593,13 @@ function noticeFor(ergebnis, was) {
6593
6593
  };
6594
6594
  }
6595
6595
  case "rejected": {
6596
- const fehler3 = ergebnis.errors ?? [];
6596
+ const fehler4 = ergebnis.errors ?? [];
6597
6597
  const ungeprueft = ergebnis.unchecked ?? [];
6598
6598
  return {
6599
6599
  tone: "rejected",
6600
- text: `${was}: abgelehnt \u2014 ${fehler3.length} Befund${fehler3.length === 1 ? "" : "e"}.`,
6600
+ text: `${was}: abgelehnt \u2014 ${fehler4.length} Befund${fehler4.length === 1 ? "" : "e"}.`,
6601
6601
  details: [
6602
- ...fehler3.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
6602
+ ...fehler4.map((f) => `${f.path ?? "(ohne Pfad)"}: ${f.message ?? "ohne Meldung"}`),
6603
6603
  // Nicht verschweigen: was nicht geprüft wurde, ist nicht in Ordnung,
6604
6604
  // sondern ungeprüft.
6605
6605
  ...ungeprueft.map((u) => `ungepr\xFCft: ${u}`)
@@ -11294,7 +11294,7 @@ var ERKLAERUNG = {
11294
11294
  };
11295
11295
  var CapabilityPanel = ({ port, elementId, setupPayload }) => {
11296
11296
  const [bericht, setBericht] = (0, import_react36.useState)(null);
11297
- const [fehler3, setFehler] = (0, import_react36.useState)(null);
11297
+ const [fehler4, setFehler] = (0, import_react36.useState)(null);
11298
11298
  (0, import_react36.useEffect)(() => {
11299
11299
  let abgemeldet = false;
11300
11300
  setBericht(null);
@@ -11316,10 +11316,10 @@ var CapabilityPanel = ({ port, elementId, setupPayload }) => {
11316
11316
  abgemeldet = true;
11317
11317
  };
11318
11318
  }, [port, elementId, setupPayload]);
11319
- if (fehler3) {
11319
+ if (fehler4) {
11320
11320
  return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { "data-testid": "capability-fehler", role: "alert", children: [
11321
11321
  "F\xE4higkeitsbericht nicht erstellbar: ",
11322
- fehler3
11322
+ fehler4
11323
11323
  ] });
11324
11324
  }
11325
11325
  if (!bericht) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { "data-testid": "capability-laden", children: "F\xE4higkeitsbericht wird erstellt \u2026" });
@@ -11429,6 +11429,10 @@ var ChainPromptAction = ({
11429
11429
  var import_react39 = require("react");
11430
11430
 
11431
11431
  // src/lib/szenen/itemDerivation.ts
11432
+ var SZENEN_ITEM_PREFIX = "szn-";
11433
+ function itemIdForRun(runId) {
11434
+ return `${SZENEN_ITEM_PREFIX}${runId}`;
11435
+ }
11432
11436
  function roundOfRun(run, runsOfShot) {
11433
11437
  return runsOfShot.findIndex((kandidat) => kandidat.run_id === run.run_id) + 1;
11434
11438
  }
@@ -11530,7 +11534,30 @@ var SZENEN_EVENT_TYPES = {
11530
11534
  * ebenfalls keine Bilddaten — nur Status, Zaehlung und die Item-Kennung,
11531
11535
  * falls eine entstand.
11532
11536
  */
11533
- imageGenerationFinished: "szenen.image_generation_finished"
11537
+ imageGenerationFinished: "szenen.image_generation_finished",
11538
+ /**
11539
+ * Ein Bild-Batch ist kanonisch gespeichert — Stufe 6.9.2, Gate D
11540
+ * (D-6.9.2-6).
11541
+ *
11542
+ * Erscheint erst, **nachdem** jedes Bild des Batches im Bildbestand liegt
11543
+ * (`hfUploadImage` + `image_added`, dieselbe Reihenfolge wie
11544
+ * `hfImageLibrary.putImage`) — kein Bild gilt als vorhanden, bevor sein
11545
+ * Upload bestätigt ist (R3). Trägt bewusst **keine Bilddaten**, nur den
11546
+ * vollständigen Metadatennachweis, den `listBatchesForPromptRun()` /
11547
+ * `getBatch()` ohne MySQL rekonstruieren können müssen: `item_id`,
11548
+ * `prompt_run_id`, `generation_batch_id`, `source_kind`, je Bild
11549
+ * `position`, `media_ref`, `asset_id`, `model`, `seed`, `width`/`height`.
11550
+ *
11551
+ * Additiv wie jedes Ereignis dieser Domain: ein zweiter Batch desselben
11552
+ * Laufs ist ein **weiteres** Ereignis mit derselben `item_id`, nicht eine
11553
+ * Änderung am ersten (D-113/D-121 — ein Lauf bleibt ein Item). Gilt für
11554
+ * `generated_in_app` **und** `external_import` — nur `source_kind`
11555
+ * unterscheidet die beiden Schreiber (D-116/D-117/D-120).
11556
+ *
11557
+ * Der Elementreducer ignoriert auch diesen Typ (`default:`-Zweig); gelesen
11558
+ * wird mit `collectImageBatches` (`imageBatchStore.ts`).
11559
+ */
11560
+ imageBatchPersisted: "szenen.image_batch_persisted"
11534
11561
  };
11535
11562
  function sortForReplay(events) {
11536
11563
  return [...events].sort((a, b) => {
@@ -12179,10 +12206,10 @@ var ZuordnenAktion = ({ port, lauf, onFertig }) => {
12179
12206
  setNotiz(
12180
12207
  ergebnis.kind === "ok" ? { tone: "ok", text: `Importiert als Item ${ergebnis.value.item_id} mit ${bilder.length} Bild(ern).` } : noticeFor(ergebnis, "Externe Bilder importieren")
12181
12208
  );
12182
- } catch (fehler3) {
12209
+ } catch (fehler4) {
12183
12210
  setNotiz({
12184
12211
  tone: "error",
12185
- text: `Externe Bilder importieren: ${fehler3 instanceof Error ? fehler3.message : String(fehler3)}`
12212
+ text: `Externe Bilder importieren: ${fehler4 instanceof Error ? fehler4.message : String(fehler4)}`
12186
12213
  });
12187
12214
  }
12188
12215
  setLaeuft(false);
@@ -17492,6 +17519,559 @@ function createHfImageLibrary(options) {
17492
17519
  };
17493
17520
  }
17494
17521
 
17522
+ // src/lib/szenen/flowImageProvider.ts
17523
+ var FLOW_MODEL_DISPLAY_NAMES = {
17524
+ "imagen-4.0-generate-001": "Imagen 4",
17525
+ "flow-nano-banana-pro": "\u{1F34C} Nano Banana Pro",
17526
+ "flow-nano-banana-2": "\u{1F34C} Nano Banana 2"
17527
+ };
17528
+ function flowImageProviderHasAccess(model) {
17529
+ return model.callable_model_id in FLOW_MODEL_DISPLAY_NAMES;
17530
+ }
17531
+ function istNichtleererText(wert) {
17532
+ return typeof wert === "string" && wert.trim().length > 0;
17533
+ }
17534
+ function createFlowImageProvider(generateImage) {
17535
+ return {
17536
+ async generate(input) {
17537
+ const modelDisplayName = FLOW_MODEL_DISPLAY_NAMES[input.callableModelId];
17538
+ if (!modelDisplayName) {
17539
+ throw new Error(
17540
+ `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).`
17541
+ );
17542
+ }
17543
+ if (istNichtleererText(input.negativePrompt)) {
17544
+ throw new Error(
17545
+ "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)."
17546
+ );
17547
+ }
17548
+ const erzeugt = [];
17549
+ let ersterFehler = null;
17550
+ for (let position = 0; position < input.imageCount; position++) {
17551
+ try {
17552
+ const antwort = await generateImage({
17553
+ prompt: input.positivePrompt,
17554
+ modelDisplayName,
17555
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}
17556
+ });
17557
+ if (!antwort || typeof antwort.base64 !== "string" || antwort.base64.length === 0) {
17558
+ throw new Error("Flow.generate.image hat kein Bild geliefert.");
17559
+ }
17560
+ erzeugt.push({
17561
+ imageBase64: antwort.base64,
17562
+ // Das tatsaechlich gerufene Modell, nicht der Flow-Display-Name —
17563
+ // im Bestand soll stehen, was bezahlt wurde (wie reactImageProvider.ts).
17564
+ model: input.callableModelId
17565
+ // Kein Seed: weder als Eingabe dokumentiert noch als Ausgabe
17566
+ // geliefert — nichts wird erfunden (D-107).
17567
+ });
17568
+ } catch (fehler4) {
17569
+ ersterFehler = fehler4 instanceof Error ? fehler4.message : String(fehler4);
17570
+ break;
17571
+ }
17572
+ }
17573
+ if (erzeugt.length === 0) {
17574
+ throw new Error(
17575
+ ersterFehler ?? `Flow.generate.image hat zu ${input.callableModelId} kein verwertbares Bild geliefert.`
17576
+ );
17577
+ }
17578
+ return { images: erzeugt };
17579
+ }
17580
+ };
17581
+ }
17582
+
17583
+ // src/lib/szenen/imageGenerationOrderStore.ts
17584
+ function istText2(wert) {
17585
+ return typeof wert === "string" && wert.trim().length > 0;
17586
+ }
17587
+ function collectImageGenerationOrders(events) {
17588
+ const byBatchId = /* @__PURE__ */ new Map();
17589
+ for (const event of events) {
17590
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationStarted) continue;
17591
+ const payload = event.payload;
17592
+ if (!payload || !istText2(payload.generation_batch_id) || !istText2(payload.prompt_run_id)) continue;
17593
+ if (byBatchId.has(payload.generation_batch_id)) continue;
17594
+ byBatchId.set(payload.generation_batch_id, {
17595
+ generation_batch_id: payload.generation_batch_id,
17596
+ prompt_run_id: payload.prompt_run_id,
17597
+ source_kind: "generated_in_app",
17598
+ request: payload.request,
17599
+ created_at: payload.created_at ?? "",
17600
+ status: "started"
17601
+ });
17602
+ }
17603
+ for (const event of events) {
17604
+ if (event.type !== SZENEN_EVENT_TYPES.imageGenerationFinished) continue;
17605
+ const payload = event.payload;
17606
+ if (!payload || !istText2(payload.generation_batch_id)) continue;
17607
+ const auftrag = byBatchId.get(payload.generation_batch_id);
17608
+ if (!auftrag) continue;
17609
+ if (auftrag.status !== "started") continue;
17610
+ if (payload.status !== "succeeded" && payload.status !== "partial" && payload.status !== "failed") continue;
17611
+ byBatchId.set(payload.generation_batch_id, {
17612
+ ...auftrag,
17613
+ status: payload.status,
17614
+ ...payload.item_id ? { item_id: payload.item_id } : {},
17615
+ produced_count: payload.produced_count ?? 0,
17616
+ ...payload.diagnostic ? { diagnostic: payload.diagnostic } : {},
17617
+ finished_at: payload.finished_at ?? ""
17618
+ });
17619
+ }
17620
+ return [...byBatchId.values()];
17621
+ }
17622
+ function ordersForPromptRun(events, runId) {
17623
+ return collectImageGenerationOrders(events).filter((o) => o.prompt_run_id === runId);
17624
+ }
17625
+
17626
+ // src/lib/szenen/imageBatchStore.ts
17627
+ var istText3 = (wert) => typeof wert === "string" && wert.trim().length > 0;
17628
+ function alsBilder(rohe) {
17629
+ if (!Array.isArray(rohe)) return [];
17630
+ return rohe.filter((b) => !!b && typeof b === "object").filter((b) => istText3(b.media_ref) && typeof b.position === "number").map((b) => ({
17631
+ media_ref: b.media_ref,
17632
+ position: b.position,
17633
+ ...istText3(b.asset_id) ? { asset_id: b.asset_id } : {},
17634
+ model: b.model ?? null,
17635
+ seed: b.seed ?? null,
17636
+ width: b.width ?? null,
17637
+ height: b.height ?? null
17638
+ }));
17639
+ }
17640
+ function collectImageBatches(events) {
17641
+ const byItemId = /* @__PURE__ */ new Map();
17642
+ for (const event of events) {
17643
+ if (event.type !== SZENEN_EVENT_TYPES.imageBatchPersisted) continue;
17644
+ const payload = event.payload;
17645
+ if (!payload || !istText3(payload.item_id) || !istText3(payload.prompt_run_id) || !istText3(payload.generation_batch_id)) continue;
17646
+ if (payload.source_kind !== "generated_in_app" && payload.source_kind !== "external_import") continue;
17647
+ const bestehend = byItemId.get(payload.item_id);
17648
+ if (bestehend?.generation_batch_ids.includes(payload.generation_batch_id)) continue;
17649
+ const bilder = alsBilder(payload.images);
17650
+ if (!bestehend) {
17651
+ byItemId.set(payload.item_id, {
17652
+ item_id: payload.item_id,
17653
+ prompt_run_id: payload.prompt_run_id,
17654
+ generation_batch_ids: [payload.generation_batch_id],
17655
+ source_kind: payload.source_kind,
17656
+ title: istText3(payload.title) ? payload.title : `Batch aus Lauf ${payload.prompt_run_id}`,
17657
+ images: bilder
17658
+ });
17659
+ continue;
17660
+ }
17661
+ byItemId.set(payload.item_id, {
17662
+ ...bestehend,
17663
+ generation_batch_ids: [...bestehend.generation_batch_ids, payload.generation_batch_id],
17664
+ images: [...bestehend.images, ...bilder]
17665
+ });
17666
+ }
17667
+ return [...byItemId.values()];
17668
+ }
17669
+ function batchesForPromptRun(events, runId) {
17670
+ return collectImageBatches(events).filter((b) => b.prompt_run_id === runId);
17671
+ }
17672
+ function findImageBatch(events, batchId) {
17673
+ return collectImageBatches(events).find((b) => b.generation_batch_ids.includes(batchId)) ?? null;
17674
+ }
17675
+
17676
+ // src/lib/szenen/hfImageBatchHistory.ts
17677
+ function lesefehler(aktion, error) {
17678
+ return {
17679
+ kind: "error",
17680
+ message: `${aktion}: ${error instanceof Error ? error.message : String(error)}`,
17681
+ retryable: true
17682
+ };
17683
+ }
17684
+ function createHfImageBatchHistory(options) {
17685
+ const { channel } = options;
17686
+ return {
17687
+ async listBatchesForPromptRun(runId) {
17688
+ try {
17689
+ const events = await channel.readEvents();
17690
+ return { kind: "ok", value: batchesForPromptRun(events, runId) };
17691
+ } catch (error) {
17692
+ return lesefehler(`Batches fuer Lauf ${runId} konnten nicht gelesen werden`, error);
17693
+ }
17694
+ },
17695
+ async getBatch(batchId) {
17696
+ try {
17697
+ const events = await channel.readEvents();
17698
+ return { kind: "ok", value: findImageBatch(events, batchId) };
17699
+ } catch (error) {
17700
+ return lesefehler(`Batch ${batchId} konnte nicht gelesen werden`, error);
17701
+ }
17702
+ },
17703
+ async listOrdersForPromptRun(runId) {
17704
+ try {
17705
+ const events = await channel.readEvents();
17706
+ return { kind: "ok", value: ordersForPromptRun(events, runId) };
17707
+ } catch (error) {
17708
+ return lesefehler(`Bildgenerierungsauftraege fuer Lauf ${runId} konnten nicht gelesen werden`, error);
17709
+ }
17710
+ }
17711
+ };
17712
+ }
17713
+
17714
+ // src/lib/szenen/hfImageBatchPersistence.ts
17715
+ init_hfStateService();
17716
+ init_hfEventLog();
17717
+ function fehler3(nachricht, retryable = true) {
17718
+ return { kind: "error", message: nachricht, retryable };
17719
+ }
17720
+ async function legeBatchAb(options, input) {
17721
+ const { namespace, token, channel } = options;
17722
+ const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
17723
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17724
+ const itemId = itemIdForRun(input.runId);
17725
+ let vorhandeneEvents;
17726
+ try {
17727
+ vorhandeneEvents = await channel.readEvents();
17728
+ } catch (error) {
17729
+ return fehler3(
17730
+ `Bild-Batches von Lauf ${input.runId} konnten vor dem Ablegen nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`
17731
+ );
17732
+ }
17733
+ const bestehend = batchesForPromptRun(vorhandeneEvents, input.runId)[0] ?? null;
17734
+ if (bestehend?.generation_batch_ids.includes(input.generationBatchId)) {
17735
+ return { kind: "ok", value: bestehend };
17736
+ }
17737
+ const startPosition = bestehend?.images.length ?? 0;
17738
+ const abgelegt = [];
17739
+ for (let offset = 0; offset < input.images.length; offset++) {
17740
+ const bild = input.images[offset];
17741
+ const id = newImageId(input.generationBatchId, startPosition + offset);
17742
+ try {
17743
+ await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
17744
+ await writeHFEvent(namespace, token, "image_added", {
17745
+ id,
17746
+ prompt: input.title ?? `Batch ${input.generationBatchId}`,
17747
+ tags: [],
17748
+ timestamp: now().getTime(),
17749
+ mimeType: bild.mimeType
17750
+ }, []);
17751
+ } catch (error) {
17752
+ return fehler3(
17753
+ `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.`
17754
+ );
17755
+ }
17756
+ abgelegt.push({
17757
+ position: startPosition + offset,
17758
+ media_ref: id,
17759
+ asset_id: id,
17760
+ model: bild.model ?? null,
17761
+ seed: bild.seed ?? null,
17762
+ width: bild.width ?? null,
17763
+ height: bild.height ?? null
17764
+ });
17765
+ }
17766
+ const titel = bestehend?.title ?? (input.title?.trim() || `Batch aus Lauf ${input.runId}`);
17767
+ const payload = {
17768
+ item_id: itemId,
17769
+ prompt_run_id: input.runId,
17770
+ generation_batch_id: input.generationBatchId,
17771
+ source_kind: input.sourceKind,
17772
+ title: titel,
17773
+ images: abgelegt,
17774
+ created_at: now().toISOString()
17775
+ };
17776
+ try {
17777
+ await channel.appendEvent(SZENEN_EVENT_TYPES.imageBatchPersisted, payload);
17778
+ } catch (error) {
17779
+ return fehler3(
17780
+ `Die Batch-Metadaten fuer ${input.generationBatchId} konnten nicht protokolliert werden, obwohl die Bilder bereits im Bestand liegen: ${error instanceof Error ? error.message : String(error)}`
17781
+ );
17782
+ }
17783
+ const zusammengefuehrt = {
17784
+ item_id: itemId,
17785
+ prompt_run_id: input.runId,
17786
+ generation_batch_ids: bestehend ? [...bestehend.generation_batch_ids, input.generationBatchId] : [input.generationBatchId],
17787
+ source_kind: bestehend?.source_kind ?? input.sourceKind,
17788
+ title: titel,
17789
+ images: [...bestehend?.images ?? [], ...abgelegt]
17790
+ };
17791
+ return { kind: "ok", value: zusammengefuehrt };
17792
+ }
17793
+ function createHfImageGenerationBatchPersistence(options) {
17794
+ return {
17795
+ async persist(input) {
17796
+ return legeBatchAb(options, {
17797
+ runId: input.runId,
17798
+ generationBatchId: input.generationBatchId,
17799
+ sourceKind: "generated_in_app",
17800
+ images: input.images.map((bild) => ({
17801
+ imageBase64: bild.imageBase64,
17802
+ // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
17803
+ // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
17804
+ // keinen MIME-Typ je Bild.
17805
+ mimeType: "image/png",
17806
+ model: bild.model ?? null,
17807
+ seed: bild.seed ?? null,
17808
+ width: bild.width ?? null,
17809
+ height: bild.height ?? null
17810
+ }))
17811
+ });
17812
+ }
17813
+ };
17814
+ }
17815
+ function createHfImageBatchWrite(options) {
17816
+ return {
17817
+ async importBatch(input) {
17818
+ return legeBatchAb(options, {
17819
+ runId: input.runId,
17820
+ generationBatchId: input.generationBatchId,
17821
+ sourceKind: "external_import",
17822
+ title: input.title,
17823
+ images: input.images.map((bild) => ({
17824
+ imageBase64: bild.imageBase64,
17825
+ mimeType: "image/png",
17826
+ model: bild.model ?? null,
17827
+ seed: bild.seed ?? null,
17828
+ width: bild.width ?? null,
17829
+ height: bild.height ?? null
17830
+ }))
17831
+ });
17832
+ },
17833
+ async attachLibraryItem(input) {
17834
+ let events;
17835
+ try {
17836
+ events = await options.channel.readEvents();
17837
+ } catch (error) {
17838
+ return {
17839
+ kind: "error",
17840
+ message: `Batches konnten nicht gelesen werden: ${error instanceof Error ? error.message : String(error)}`,
17841
+ retryable: true
17842
+ };
17843
+ }
17844
+ const item = collectImageBatches(events).find((b) => b.item_id === input.itemId);
17845
+ if (!item) {
17846
+ return { kind: "error", message: `Item ${input.itemId} ist unbekannt.`, retryable: false };
17847
+ }
17848
+ if (item.prompt_run_id !== input.runId) {
17849
+ return {
17850
+ kind: "conflict",
17851
+ message: `Item ${input.itemId} geh\xF6rt bereits zu Lauf ${item.prompt_run_id}.`,
17852
+ existingPromptRunId: item.prompt_run_id
17853
+ };
17854
+ }
17855
+ return { kind: "ok", value: item };
17856
+ }
17857
+ };
17858
+ }
17859
+
17860
+ // src/lib/szenen/imageGenerationCapability.ts
17861
+ var isObject9 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
17862
+ var IMAGE_MODEL_CATALOGUE = {
17863
+ imagen_4: {
17864
+ callable_model_id: "imagen-4.0-generate-001",
17865
+ provider: "google",
17866
+ max_images: 4,
17867
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
17868
+ supports_seed: true
17869
+ },
17870
+ imagen_3: {
17871
+ callable_model_id: "imagen-3.0-generate-001",
17872
+ provider: "google",
17873
+ max_images: 4,
17874
+ aspect_ratios: ["1:1", "3:4", "4:3", "9:16", "16:9"],
17875
+ supports_seed: true
17876
+ },
17877
+ /**
17878
+ * Stufe 6.9.2, Nachtrag — real gegen Flow geprüft (2026-08-17): Der
17879
+ * Modellwähler im Flow-„Generieren"-Tab zeigt aktuell nur "Nano Banana Pro"
17880
+ * und "Nano Banana 2" als wählbar an, "Imagen 4" erscheint dort ausgegraut.
17881
+ * `provider: 'flow'` statt `'google'` ist bewusst: Diese Modelle sind nur
17882
+ * über `Flow.generate.image` erreichbar, nicht über die direkte
17883
+ * Google-Imagen-REST-Route, die der React-Host benutzt (`reactImageProvider.ts`)
17884
+ * — React meldet sie deshalb korrekt als `unsupported`, ohne eine eigene
17885
+ * Host-Verzweigung zu brauchen (D-115/R-1: `hasAccess` prüft `provider`).
17886
+ */
17887
+ nano_banana_pro: {
17888
+ callable_model_id: "flow-nano-banana-pro",
17889
+ provider: "flow",
17890
+ max_images: 4,
17891
+ // Flow.generate.image dokumentiert nur '1:1' | '16:9' | '9:16' (howto-flow-sdk.md).
17892
+ aspect_ratios: ["1:1", "16:9", "9:16"],
17893
+ // Weder als Eingabe- noch als Ausgabeparameter dokumentiert — nichts erfunden (D-107).
17894
+ supports_seed: false
17895
+ },
17896
+ nano_banana_2: {
17897
+ callable_model_id: "flow-nano-banana-2",
17898
+ provider: "flow",
17899
+ max_images: 4,
17900
+ aspect_ratios: ["1:1", "16:9", "9:16"],
17901
+ supports_seed: false
17902
+ }
17903
+ };
17904
+ function callableImageModelFor(targetModelKey) {
17905
+ return IMAGE_MODEL_CATALOGUE[targetModelKey] ?? null;
17906
+ }
17907
+ function targetModelKeyOf(targetModel) {
17908
+ if (!isObject9(targetModel)) return null;
17909
+ return typeof targetModel.key === "string" && targetModel.key.length > 0 ? targetModel.key : null;
17910
+ }
17911
+ function capabilityForTargetModel(targetModel, hasAccess) {
17912
+ const key = targetModelKeyOf(targetModel);
17913
+ if (!key) {
17914
+ return {
17915
+ kind: "unsupported",
17916
+ target_model_key: "(unbekannt)",
17917
+ reason: 'Der eingefrorene Lauf tr\xE4gt kein "target_model.key" \u2014 es gibt nichts zuzuordnen.'
17918
+ };
17919
+ }
17920
+ const modell = callableImageModelFor(key);
17921
+ if (!modell) {
17922
+ return {
17923
+ kind: "unsupported",
17924
+ target_model_key: key,
17925
+ reason: `F\xFCr das Zielmodell "${key}" ist in diesem Host kein Bildpfad katalogisiert.`
17926
+ };
17927
+ }
17928
+ if (!hasAccess(modell)) {
17929
+ return {
17930
+ kind: "unsupported",
17931
+ target_model_key: key,
17932
+ reason: `Dieser Host hat keinen Zugang zu ${modell.callable_model_id} (${modell.provider}).`
17933
+ };
17934
+ }
17935
+ return {
17936
+ kind: "supported",
17937
+ target_model_key: key,
17938
+ provider: modell.provider,
17939
+ callable_model_id: modell.callable_model_id,
17940
+ max_images: modell.max_images,
17941
+ aspect_ratios: modell.aspect_ratios,
17942
+ supports_seed: modell.supports_seed
17943
+ };
17944
+ }
17945
+
17946
+ // src/lib/szenen/imageGenerationPort.ts
17947
+ function queryError2(error, action) {
17948
+ return {
17949
+ kind: "error",
17950
+ message: `${action}: ${error instanceof Error ? error.message : String(error)}`,
17951
+ retryable: true
17952
+ };
17953
+ }
17954
+ function createImageGenerationPort(options) {
17955
+ const { runs, provider, batches, events, hasAccess } = options;
17956
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17957
+ async function readRun(runId) {
17958
+ try {
17959
+ return await runs.getRun(runId);
17960
+ } catch (error) {
17961
+ return queryError2(error, `Lauf ${runId} konnte nicht gelesen werden`);
17962
+ }
17963
+ }
17964
+ return {
17965
+ async capabilityForRun(runId) {
17966
+ const gelesen = await readRun(runId);
17967
+ if (gelesen.kind !== "ok") return gelesen;
17968
+ if (!gelesen.value) {
17969
+ return { kind: "error", message: `Lauf ${runId} ist unbekannt.`, retryable: false };
17970
+ }
17971
+ const targetModel = gelesen.value.input_payload?.target_model;
17972
+ return { kind: "ok", value: capabilityForTargetModel(targetModel, hasAccess) };
17973
+ },
17974
+ async startFromPromptRun(input) {
17975
+ const gelesen = await readRun(input.runId);
17976
+ if (gelesen.kind !== "ok") return { kind: "rejected", message: gelesen.message };
17977
+ if (!gelesen.value) {
17978
+ return { kind: "rejected", message: `Lauf ${input.runId} ist unbekannt.` };
17979
+ }
17980
+ const run = gelesen.value;
17981
+ if (run.status !== "succeeded" || !run.output) {
17982
+ return {
17983
+ kind: "rejected",
17984
+ message: `Lauf ${input.runId} ist nicht erfolgreich \u2014 ein Bildauftrag braucht einen eingefrorenen Prompt (D-103).`
17985
+ };
17986
+ }
17987
+ const targetModel = run.input_payload?.target_model;
17988
+ const capability = capabilityForTargetModel(targetModel, hasAccess);
17989
+ if (capability.kind !== "supported") {
17990
+ return { kind: "unsupported", targetModelKey: capability.target_model_key, reason: capability.reason };
17991
+ }
17992
+ const startedPayload = {
17993
+ generation_batch_id: input.generationBatchId,
17994
+ prompt_run_id: input.runId,
17995
+ source_kind: "generated_in_app",
17996
+ request: {
17997
+ image_count: input.imageCount,
17998
+ ...input.aspectRatio ? { aspect_ratio: input.aspectRatio } : {},
17999
+ ...input.seeds ? { seeds: input.seeds } : {},
18000
+ callable_model_id: capability.callable_model_id
18001
+ },
18002
+ created_at: now().toISOString()
18003
+ };
18004
+ try {
18005
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationStarted, startedPayload);
18006
+ } catch (error) {
18007
+ return {
18008
+ kind: "failed",
18009
+ message: `Der Auftrag ${input.generationBatchId} konnte nicht protokolliert werden: ${error instanceof Error ? error.message : String(error)}`
18010
+ };
18011
+ }
18012
+ let produced = [];
18013
+ let providerFehler = null;
18014
+ try {
18015
+ const antwort = await provider.generate({
18016
+ callableModelId: capability.callable_model_id,
18017
+ provider: capability.provider,
18018
+ positivePrompt: run.output.positive,
18019
+ negativePrompt: run.output.negative,
18020
+ imageCount: input.imageCount,
18021
+ ...input.aspectRatio ? { aspectRatio: input.aspectRatio } : {},
18022
+ ...input.seeds ? { seeds: input.seeds } : {}
18023
+ });
18024
+ produced = antwort.images;
18025
+ } catch (error) {
18026
+ providerFehler = error instanceof Error ? error.message : String(error);
18027
+ }
18028
+ if (providerFehler || produced.length === 0) {
18029
+ const finishedPayload2 = {
18030
+ generation_batch_id: input.generationBatchId,
18031
+ status: "failed",
18032
+ produced_count: 0,
18033
+ diagnostic: providerFehler ?? "Der Provider hat kein verwertbares Bild geliefert.",
18034
+ finished_at: now().toISOString()
18035
+ };
18036
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
18037
+ });
18038
+ return {
18039
+ kind: "failed",
18040
+ message: finishedPayload2.diagnostic
18041
+ };
18042
+ }
18043
+ const persistiert = await batches.persist({
18044
+ runId: input.runId,
18045
+ generationBatchId: input.generationBatchId,
18046
+ images: produced
18047
+ });
18048
+ if (persistiert.kind !== "ok") {
18049
+ const finishedPayload2 = {
18050
+ generation_batch_id: input.generationBatchId,
18051
+ status: "failed",
18052
+ produced_count: 0,
18053
+ diagnostic: persistiert.kind === "error" ? persistiert.message : `Batch ${input.generationBatchId} konnte nicht gespeichert werden (${persistiert.kind}).`,
18054
+ finished_at: now().toISOString()
18055
+ };
18056
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload2).catch(() => {
18057
+ });
18058
+ return { kind: "failed", message: finishedPayload2.diagnostic };
18059
+ }
18060
+ const istPartial = produced.length < input.imageCount;
18061
+ const finishedPayload = {
18062
+ generation_batch_id: input.generationBatchId,
18063
+ status: istPartial ? "partial" : "succeeded",
18064
+ item_id: persistiert.value.item_id,
18065
+ produced_count: produced.length,
18066
+ finished_at: now().toISOString()
18067
+ };
18068
+ await events.appendEvent(SZENEN_EVENT_TYPES.imageGenerationFinished, finishedPayload).catch(() => {
18069
+ });
18070
+ return istPartial ? { kind: "partial", batch: persistiert.value, requested: input.imageCount, produced: produced.length } : { kind: "succeeded", batch: persistiert.value };
18071
+ }
18072
+ };
18073
+ }
18074
+
17495
18075
  // src/lib/szenen/flowViewPort.ts
17496
18076
  var defaultContext = {
17497
18077
  async chainFor(contextId) {
@@ -17539,8 +18119,36 @@ function createFlowSzenenViewPort(options) {
17539
18119
  ...options.newRunId ? { newRunId: options.newRunId } : {},
17540
18120
  ...options.now ? { now: options.now } : {}
17541
18121
  });
17542
- const port = { elements, context, runs, derivation };
18122
+ const imageBatchHistory = createHfImageBatchHistory({ channel });
18123
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
18124
+ const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
18125
+ namespace: options.namespace,
18126
+ token: options.token,
18127
+ channel,
18128
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18129
+ ...options.now ? { now: options.now } : {}
18130
+ }) : void 0;
18131
+ const bildProvider = options.imageProvider ?? (options.generateImage ? createFlowImageProvider(options.generateImage) : void 0);
18132
+ const imageGeneration = bildProvider && imageBatchWrite ? createImageGenerationPort({
18133
+ runs: { async getRun(runId) {
18134
+ return runs.getRun(runId);
18135
+ } },
18136
+ provider: bildProvider,
18137
+ batches: createHfImageGenerationBatchPersistence({
18138
+ namespace: options.namespace,
18139
+ token: options.token,
18140
+ channel,
18141
+ ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
18142
+ ...options.now ? { now: options.now } : {}
18143
+ }),
18144
+ events: channel,
18145
+ hasAccess: options.imageProviderHasAccess ?? flowImageProviderHasAccess,
18146
+ ...options.now ? { now: options.now } : {}
18147
+ }) : void 0;
18148
+ const port = { elements, context, runs, derivation, imageBatchHistory };
17543
18149
  if (imageLibrary) port.imageLibrary = imageLibrary;
18150
+ if (imageBatchWrite) port.imageBatchWrite = imageBatchWrite;
18151
+ if (imageGeneration) port.imageGeneration = imageGeneration;
17544
18152
  return port;
17545
18153
  }
17546
18154
 
@@ -17650,8 +18258,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
17650
18258
  const szenenPort = (0, import_react47.useMemo)(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
17651
18259
  generateText: onGeneratePrompt,
17652
18260
  token: hfToken,
17653
- namespace: effectiveNamespace
17654
- }) : null, [hfToken, effectiveNamespace, onGeneratePrompt]);
18261
+ namespace: effectiveNamespace,
18262
+ generateImage: onGenerateImage
18263
+ }) : null, [hfToken, effectiveNamespace, onGeneratePrompt, onGenerateImage]);
17655
18264
  const {
17656
18265
  state: hfState,
17657
18266
  isLoading: isHfRefreshing,