@ykurilov/agent-chat-images 0.80.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @ykurilov/agent-chat-images
2
2
 
3
+ ## 0.81.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 70ad21c: Снимки страниц, картинки провайдера и MCP без своего `fetch` приложения ходят закреплённым `fetch` из нового серверного входа `@ykurilov/agent-chat-web-tools/network`: имя проверяется в момент соединения, подмена DNS во внутреннюю сеть не пускает. Свой `fetch` приложения — граница доверия: закреплять соединение должен он сам. Предел `maxRunningPerUser` держится между копиями сервера и для ответов на подтверждение через новый метод `TaskStore.admitWithinLimit` (память, SQLite, PostgreSQL); своё хранилище без него теперь не запускается — метод надо добавить. Ответ на подтверждение фоновой задачи при занятых местах получает отказ `TurnRefusedError` с кодом `too_many_tasks`: вопрос ждёт, ответить можно, когда место освободится. Отказ проверки адреса в `person_link_view` больше не считается просмотром; битая %-кодировка в адресе экрана админа MCP даёт 404 вместо 500. Подробности и шаги обновления — `docs/audit-network.md`. SQL-миграций нет.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [038b6e2]
12
+ - Updated dependencies [70ad21c]
13
+ - Updated dependencies [58204d2]
14
+ - @ykurilov/agent-chat-react@0.81.0
15
+ - @ykurilov/agent-chat-attachments@0.81.0
16
+ - @ykurilov/agent-chat-web-tools@0.81.0
17
+ - @ykurilov/agent-chat-core@0.81.0
18
+
19
+ ## 0.80.1
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [d59ae4e]
24
+ - @ykurilov/agent-chat-core@0.80.1
25
+ - @ykurilov/agent-chat-attachments@0.80.1
26
+ - @ykurilov/agent-chat-react@0.80.1
27
+
3
28
  ## 0.80.0
4
29
 
5
30
  ### Patch Changes
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ import { SpendingLimitReachedError as SpendingLimitReachedError2, declareToolTim
23
23
 
24
24
  // src/download-image.ts
25
25
  import { detectFileKind } from "@ykurilov/agent-chat-attachments";
26
+ import { dnsHostResolver, sharedPinnedFetch, WebToolError } from "@ykurilov/agent-chat-web-tools/network";
26
27
 
27
28
  // src/download-address.ts
28
29
  var PRIVATE_IPV4 = [
@@ -145,10 +146,15 @@ function checkedAddress(url, base, trustedOrigin) {
145
146
  if (!isAllowedImageAddress(address, trustedOrigin)) throw failure("\u041A\u0430\u0440\u0442\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u0430\u0441\u044C: \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0430\u0434\u0440\u0435\u0441");
146
147
  return address;
147
148
  }
149
+ function fetchFor(input, address) {
150
+ if (input.fetch) return input.fetch;
151
+ if (input.trustedOrigin !== void 0 && address.origin === input.trustedOrigin) return globalThis.fetch;
152
+ return sharedPinnedFetch(input.resolveHost ?? dnsHostResolver);
153
+ }
148
154
  async function fetchImage(input, first, signal) {
149
155
  let address = first;
150
156
  for (let hop = 0; ; hop++) {
151
- const response = await input.fetch(address.href, { signal, redirect: "manual" });
157
+ const response = await fetchFor(input, address)(address.href, { signal, redirect: "manual" });
152
158
  const location = response.status >= 300 && response.status < 400 ? response.headers.get("location") : null;
153
159
  if (location === null) return response;
154
160
  await response.body?.cancel().catch(() => {
@@ -173,6 +179,7 @@ async function downloadGeneratedImage(input) {
173
179
  } catch (error) {
174
180
  if (input.abortSignal?.aborted) throw input.abortSignal.reason ?? error;
175
181
  if (error instanceof ImageGenerationError) throw error;
182
+ if (error instanceof WebToolError && error.kind === "bad_address") throw failure("\u041A\u0430\u0440\u0442\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u0430\u0441\u044C: \u043D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0430\u0434\u0440\u0435\u0441");
176
183
  throw failure(timeout.signal.aborted ? "\u041A\u0430\u0440\u0442\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u0430\u0441\u044C: \u043D\u0435 \u0443\u0441\u043F\u0435\u043B\u0430" : "\u041A\u0430\u0440\u0442\u0438\u043D\u043A\u0430 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043B\u0430\u0441\u044C");
177
184
  } finally {
178
185
  clearTimeout(timer);
@@ -302,7 +309,7 @@ function createImageJobs(options) {
302
309
  const output = input.setup.output;
303
310
  const file = await downloadGeneratedImage({
304
311
  url,
305
- fetch: last.connection.fetch,
312
+ ...last.downloadFetch && { fetch: last.downloadFetch },
306
313
  maxImageBytes: options.maxImageBytes,
307
314
  ...last.trustedOrigin !== void 0 && { trustedOrigin: last.trustedOrigin },
308
315
  ...output && { transform: (bytes, mediaType) => postprocessImage(bytes, mediaType, output) },
@@ -534,6 +541,7 @@ function resolveImageSetups(choice) {
534
541
  sizes,
535
542
  priceUsd: roundUsd(sizePriceUsd * qualityFactor),
536
543
  ...provider.baseURL !== void 0 && { trustedOrigin: new URL(provider.baseURL).origin },
544
+ ...choice.fetch && { downloadFetch: choice.fetch },
537
545
  ...providerSettings && { providerSettings },
538
546
  ...settings.transparentBackground && { outputFormat: "png" }
539
547
  };
@@ -1102,7 +1110,7 @@ function createImageQueue(options) {
1102
1110
  try {
1103
1111
  file = await downloadGeneratedImage({
1104
1112
  url,
1105
- fetch: last.connection.fetch,
1113
+ ...last.downloadFetch && { fetch: last.downloadFetch },
1106
1114
  maxImageBytes,
1107
1115
  ...last.trustedOrigin !== void 0 && { trustedOrigin: last.trustedOrigin },
1108
1116
  abortSignal: rt.controller.signal,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-image-generation.ts","../src/download-image.ts","../src/download-address.ts","../src/postprocess-image.ts","../src/image-jobs.ts","../src/image-queue/create-image-queue.ts","../src/image-setup.ts","../src/image-queue/turn-counter.ts","../src/images-usage.ts","../src/save-images.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport type { AttachmentStorage } from \"@ykurilov/agent-chat-attachments\";\nimport { SpendingLimitReachedError, declareToolTimeout } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage, SpendingLimits } from \"@ykurilov/agent-chat-core\";\nimport { aspectRatios } from \"./image-catalog\";\nimport type { AspectRatio, ImageCatalog, ImageQuality, ImageSize } from \"./image-catalog\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport { createImageJobs } from \"./image-jobs\";\nimport type { ImageJobInput, ImageJobOutcome, LateImage, ReceivedImageFile } from \"./image-jobs\";\nimport { createImageQueue } from \"./image-queue/create-image-queue\";\nimport type { GeneratedImage, ImageDeliveryContext, ImageJob, ImageQueue } from \"./image-queue/create-image-queue\";\nimport type { ImageJobStore } from \"./image-queue/image-job-store\";\nimport { createTurnCounter } from \"./image-queue/turn-counter\";\nimport { resolveImageSetups } from \"./image-setup\";\nimport type { ImagePurpose, ImageSetup } from \"./image-setup\";\nimport { recordImageUsage } from \"./images-usage\";\nimport type { ImageOutputOptions } from \"./postprocess-image\";\nimport { DOWNLOAD_TIMEOUT_MS, saveImageToChat } from \"./save-images\";\n\nconst DEFAULT_MAX_IMAGE_BYTES = 10 * 1024 * 1024;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 150_000;\nconst DEFAULT_LATE_RESULT_WINDOW_MS = 10 * 60 * 1000;\n\n/**\n * Срок вызова `generate_images` при сроке генерации по умолчанию: генерация до 150 с и скачивание до 60 с. Инструмент\n * объявляет ядру свой срок сам (`requestTimeoutMs` + скачивание), настраивать `agent.toolTimeoutMs` не нужно.\n */\nexport const imageToolTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS + DOWNLOAD_TIMEOUT_MS;\n\nexport interface CreateImageGenerationOptions {\n /** Тот же объект `createAttachments`: картинки ложатся в его хранилище и отдаются его маршрутом. */\n attachments: { storage: AttachmentStorage };\n /** Окружение сервера: ключ провайдера берётся только отсюда, по `apiKeyEnv`. */\n env: Record<string, string | undefined>;\n catalog?: ImageCatalog;\n /** Модель картинок из каталога; по умолчанию `gpt-image-2`. С `purposes` не задаётся. */\n model?: string;\n /** Провайдер модели; по умолчанию первый из её `providerModelIds`. */\n provider?: string;\n /** Качество GPT Image (`low`, `medium`, `high`, `auto`); по умолчанию — из профиля (`low`). Для назначений — общее по умолчанию. */\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024 (1:1 — 1024×1024); по умолчанию — размер модели. Для назначений — общий по умолчанию. */\n size?: ImageSize;\n /** Прозрачный фон (GPT Image 2.5 у Runware): файл PNG с альфой. Для назначений — общий по умолчанию. */\n transparentBackground?: boolean;\n /** Постобработка перед сохранением: формат файла и предел большей стороны (sharp). Для назначений — общая по умолчанию. */\n output?: ImageOutputOptions;\n /**\n * Назначения: имя → модель и описание для агента. Агент выбирает назначение параметром `purpose`, по умолчанию —\n * первое. Описание инструмента собирается из них один раз и дальше не меняется (кэш модели).\n */\n purposes?: Record<string, ImagePurpose>;\n /**\n * Расход картинок в лимиты: запись на каждую отправленную задачу и проверка лимита до генерации. Заказанное, но ещё не\n * записанное (вызовы, которые идут прямо сейчас, и задачи в фоне) проверка видит в `pendingUsd`: параллельные вызовы\n * одного шага не проходят лимит все разом.\n */\n spending?: {\n storage: Pick<ChatStorage, \"insertUsageRecord\">;\n /** `afterSpending` (если есть) зовётся после каждой записи расхода: пороги лимитов видят и картинки. */\n limits?: Pick<SpendingLimits, \"assertWithinLimit\" | \"afterSpending\">;\n };\n /** Предел одного файла; по умолчанию 10 МБ. */\n maxImageBytes?: number;\n /** Срок одной генерации; по умолчанию 150 000 мс. */\n requestTimeoutMs?: number;\n /**\n * Провайдер с очередью (KIE): сколько ещё ждать картинку в фоне после срока вызова; по умолчанию 10 минут, 0 — не\n * ждать. Готовая сохраняется в чат и отдаётся повтору того же вызова бесплатно, а приложение узнаёт о ней в `onLateImage`.\n */\n lateResultWindowMs?: number;\n /**\n * Картинка пришла после срока вызова (в фоновом режиме — всегда) и сохранена в чат. Ошибка обработчика — в `logError`,\n * не наружу. Со своим приёмником `deliver` не зовётся: картинку получает приёмник.\n */\n onLateImage?: (image: LateImage) => void | Promise<void>;\n /**\n * Свой приёмник картинки вместо вложений чата: записать файл, обновить экран. Ответ (JSON) получает модель в\n * `результат`; `false` — картинка больше не нужна. В фоновом режиме приёмника зовут хотя бы один раз (после сбоя\n * сервера — ещё раз), поэтому повтор должен быть безопасен. Запись — условная, по `context.isCurrent()`: пока приёмник\n * работает, задачу могут вытеснить или остановить.\n */\n deliver?: (image: GeneratedImage, delivery: ImageDelivery, context: ImageDeliveryContext) => unknown;\n /**\n * `wait` (по умолчанию) — вызов ждёт картинку. `background` — вызов отвечает, как только задача ушла провайдеру, а\n * картинка приходит позже: во вложения (и `onLateImage`) или в `deliver`. Фоновые задачи лежат в `jobStore`.\n */\n mode?: \"wait\" | \"background\";\n /** Фоновый режим: где лежат задачи; по умолчанию в памяти. `sqliteImageJobStore` или `postgresImageJobStore` — переживают перезапуск. */\n jobStore?: ImageJobStore;\n /** Фоновый режим: шаг проверки задачи у провайдера с очередью; по умолчанию 3000 мс. */\n pollIntervalMs?: number;\n /** Фоновый режим: сколько попыток по `requestTimeoutMs`; следующая проверяет ту же задачу без новой оплаты. По умолчанию 1. */\n attempts?: number;\n /** Не больше стольких картинок за ход (`turnId`; без него — за набор `tools()`). Лишние — в ошибках, без заказа. */\n maxImagesPerTurn?: number;\n /** Фоновый режим: поля для ответа модели о заказанной картинке (например, куда она ляжет). */\n onQueued?: (job: ImageJob) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;\n fetch?: typeof globalThis.fetch;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\n/** Чья и какая картинка пришла в `deliver`. */\nexport interface ImageDelivery {\n ownerId: string;\n chatId: string;\n turnId?: string;\n /** Номер картинки в вызове, с 1. */\n number: number;\n prompt: string;\n aspectRatio: AspectRatio;\n purpose?: string;\n /** Модель картинки (первого шага). */\n modelId: string;\n /** Фоновый режим: id задачи из ответа модели. */\n jobId?: string;\n}\n\n/** Выбранная модель хода: ядро передаёт в `agent.tools(model)` того, чей ход, чат и номер хода. */\nexport interface ImageGenerationModel {\n ownerId?: string;\n chatId?: string;\n /** Номер хода: один на все попытки и запасного провайдера. По нему считается `maxImagesPerTurn`. */\n turnId?: string;\n}\n\nexport interface ImageGeneration {\n /** `{ generate_images }` для `agent.tools`; без `ownerId` и `chatId` — пустой набор. */\n tools(model: ImageGenerationModel): ToolSet;\n /** Фоновый режим: подобрать незаконченные задачи. Зовётся само при создании; в режиме `wait` ничего не делает. */\n start(): Promise<void>;\n /** Фоновый режим: остановить опрос и таймеры перед закрытием хранилища; задачи продолжатся следующим `start`. */\n stop(): Promise<void>;\n}\n\n/** Кто заказывает: из `tools(model)`. */\ninterface Caller {\n ownerId: string;\n chatId: string;\n turnId?: string;\n /** Счёт предела за ход: номер хода или свой у набора `tools()`. */\n turnKey: string;\n}\n\ninterface GenerateImagesInput {\n prompt: string;\n purpose?: string;\n count: number;\n aspectRatio: AspectRatio;\n}\n\ninterface SavedImage {\n номер: number;\n /** Вложение чата; со своим приёмником его нет. */\n id?: string;\n ширина: number;\n высота: number;\n /** Ответ своего приёмника. */\n результат?: unknown;\n}\ninterface FailedImage {\n номер: number;\n причина: string;\n /** Картинка ещё готовится у провайдера: повтор того же вызова получит её без новой оплаты. */\n готовится?: true;\n}\n\n/** Основа описания не зависит от человека и времени: от этого зависит кэш модели. */\nconst baseDescription =\n \"Создаёт картинки по описанию и показывает их человеку в чате. \" +\n \"Пиши описание подробно: что изображено, стиль, цвета, композиция. \" +\n \"Человек видит картинки сам: в ответе не пересказывай их и не вставляй ссылки.\";\n/** Фоновый режим: картинка приходит сама, повторять вызов незачем. */\nconst backgroundDescription =\n \"Заказывает картинки по описанию: они готовятся в фоне и появятся у человека сами. \" +\n \"Пиши описание подробно: что изображено, стиль, цвета, композиция. \" +\n \"Ответ приходит сразу, без картинок: не жди их, не вызывай инструмент снова ради них и не вставляй ссылки.\";\nconst queuedMessage = \"Картинки готовятся в фоне и появятся у человека сами. Не жди их и не заказывай заново.\";\n/** Только если есть провайдер с очередью: иначе «готовится» не бывает. */\nconst repeatHint = \" Если у картинки в ошибках «готовится», повтори вызов с теми же параметрами позже: готовая придёт без новой оплаты.\";\n\nconst promptSchema = z.string().min(2).max(2000).describe(\"Описание картинки, от 2 до 2000 знаков.\");\nconst countSchema = z.number().int().min(1).max(8).default(1).describe(\"Сколько вариантов создать, от 1 до 8.\");\nconst aspectRatioSchema = z.enum(aspectRatios as [AspectRatio, ...AspectRatio[]]).default(\"1:1\").describe(\"Пропорции картинки.\");\n\nconst LIMIT_REACHED = \"лимит расходов исчерпан\";\nconst NOT_NEEDED = \"картинка больше не нужна приложению\";\n\n/** Бронь вызова, который идёт: задачи, ещё не отданные реестру задач, по цене назначения. */\ninterface Reservation {\n ownerId: string;\n chatId: string;\n openTasks: number;\n priceUsd: number;\n}\n\n/** Сумма в долларах без хвоста двоичной дроби: 8 × 0,02 — это 0,16. */\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\n\nfunction checkNumbers(options: CreateImageGenerationOptions) {\n for (const [name, value] of [[\"maxImageBytes\", options.maxImageBytes], [\"requestTimeoutMs\", options.requestTimeoutMs]] as const) {\n if (value !== undefined && !(Number.isFinite(value) && value > 0)) throw new Error(`Картинки: ${name} — нужно число больше нуля`);\n }\n const window = options.lateResultWindowMs;\n if (window !== undefined && !(Number.isFinite(window) && window >= 0)) throw new Error(\"Картинки: lateResultWindowMs — число не меньше нуля\");\n const limit = options.maxImagesPerTurn;\n if (limit !== undefined && !(Number.isInteger(limit) && limit >= 1)) throw new Error(\"Картинки: maxImagesPerTurn — целое число от 1\");\n if (options.mode !== \"background\") {\n const backgroundOnly = ([\"jobStore\", \"pollIntervalMs\", \"attempts\", \"onQueued\"] as const).filter((name) => options[name] !== undefined);\n if (backgroundOnly.length > 0) throw new Error(`Картинки: ${backgroundOnly.join(\", \")} — только для mode: \"background\"`);\n }\n}\n\n/**\n * Генерация картинок: инструмент `generate_images` для набора `agent.tools`. Настройка проверяется сразу: нет модели,\n * провайдера или ключа — исключение здесь, а не при первом вызове.\n */\nexport function createImageGeneration(options: CreateImageGenerationOptions): ImageGeneration {\n const setups = resolveImageSetups(options);\n checkNumbers(options);\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const now = options.now ?? (() => new Date());\n const logError = (message: string) => options.logError?.(setups.hideSecrets(message));\n const errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n const all = [...setups.byPurpose.values()];\n const queued = all.some((setup) => setup.steps.some((step) => step.provider.queue !== undefined));\n\n const turns = createTurnCounter(options.maxImagesPerTurn);\n const deliver = options.deliver;\n\n /** Свой приёмник: ответ — модели, `false` и сбой — в ошибки картинки. */\n async function deliverToApp(image: GeneratedImage, delivery: ImageDelivery, context: ImageDeliveryContext): Promise<unknown> {\n let result: unknown;\n try {\n result = await deliver!(image, delivery, context);\n } catch (error) {\n logError(`Картинки: приложение не приняло картинку ${delivery.number} (чат ${delivery.chatId}): ${errorText(error)}`);\n throw new ImageGenerationError(\"Приложение не приняло картинку\", { refusedBeforeWork: false });\n }\n if (result === false) throw new ImageGenerationError(NOT_NEEDED, { refusedBeforeWork: false });\n return result;\n }\n\n const deliveryOf = (input: ImageJobInput, turnId: string | undefined, modelId: string): ImageDelivery => ({\n ownerId: input.ownerId,\n chatId: input.chatId,\n ...(turnId !== undefined && { turnId }),\n number: input.number,\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n ...(input.setup.purpose !== undefined && { purpose: input.setup.purpose }),\n modelId,\n });\n\n /** Номер хода задачи прежнего режима: его ждёт свой приёмник. */\n const turnIds = new WeakMap<ImageJobInput, string>();\n\n async function receive(input: ImageJobInput, file: ReceivedImageFile, signal: AbortSignal): Promise<{ id?: string; result?: unknown }> {\n const modelId = input.setup.steps[0]!.profile.id;\n if (deliver) {\n const context: ImageDeliveryContext = { signal, isCurrent: async () => !signal.aborted };\n const result = await deliverToApp({ ...file, modelId }, deliveryOf(input, turnIds.get(input), modelId), context);\n return result === undefined ? {} : { result };\n }\n const saved = await saveImageToChat({ storage: options.attachments.storage, chatId: input.chatId, ownerId: input.ownerId, number: input.number, file, now });\n return { id: saved.id };\n }\n\n const jobs = createImageJobs({\n receive,\n maxImageBytes: options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES,\n requestTimeoutMs,\n lateResultWindowMs: options.lateResultWindowMs ?? DEFAULT_LATE_RESULT_WINDOW_MS,\n now,\n async record(input, step, costUsd) {\n if (!options.spending) return;\n await recordImageUsage({ storage: options.spending.storage, ownerId: input.ownerId, chatId: input.chatId, modelId: step.profile.id, providerId: step.provider.id, costUsd, now, logError });\n const afterSpending = options.spending.limits?.afterSpending;\n // Пороги — фоном: картинку ход не задерживает.\n if (afterSpending) {\n void afterSpending\n .call(options.spending.limits, { userId: input.ownerId, chatId: input.chatId })\n .catch((error: unknown) => logError(`Картинки: лимиты после расхода (чат ${input.chatId}): ${errorText(error)}`));\n }\n },\n ...(options.onLateImage && { onLateImage: options.onLateImage }),\n logError,\n hideSecrets: setups.hideSecrets,\n });\n\n const reservations = new Set<Reservation>();\n const limitChecks = new Map<string, Promise<unknown>>();\n\n /** Заказанное, но не записанное: всего у человека и в этом чате — брони вызовов и задачи в работе. */\n function pendingUsd(ownerId: string, chatId: string): { user: number; chat: number } {\n const working = jobs.pendingUsd(ownerId, chatId);\n let user = working.user;\n let chat = working.chat;\n for (const reservation of reservations) {\n if (reservation.ownerId !== ownerId) continue;\n user += reservation.openTasks * reservation.priceUsd;\n if (reservation.chatId === chatId) chat += reservation.openTasks * reservation.priceUsd;\n }\n return { user: roundUsd(user), chat: roundUsd(chat) };\n }\n\n async function withinLimit(ownerId: string, chatId: string): Promise<string | undefined> {\n const pending = pendingUsd(ownerId, chatId);\n try {\n await options.spending?.limits?.assertWithinLimit({ userId: ownerId, chatId, ...(pending.user > 0 && { pendingUsd: pending }) });\n return undefined;\n } catch (error) {\n if (error instanceof SpendingLimitReachedError) return LIMIT_REACHED;\n logError(`Картинки: не удалось проверить лимит расходов: ${errorText(error)}`);\n return \"не удалось проверить лимит расходов\";\n }\n }\n\n /**\n * Проверка лимита и бронь `count` задач — по одному на человека: параллельный вызов того же шага проверяется уже\n * с бронью предыдущего. Сама генерация друг друга не ждёт.\n */\n function reserve(ownerId: string, chatId: string, count: number, priceUsd: number): Promise<{ refusal: string } | { reservation: Reservation }> {\n const previous = limitChecks.get(ownerId) ?? Promise.resolve();\n const check = previous.then(async () => {\n const refusal = await withinLimit(ownerId, chatId);\n if (refusal) return { refusal };\n const reservation: Reservation = { ownerId, chatId, openTasks: count, priceUsd };\n reservations.add(reservation);\n return { reservation };\n });\n limitChecks.set(ownerId, check);\n void check.finally(() => {\n if (limitChecks.get(ownerId) === check) limitChecks.delete(ownerId);\n });\n return check;\n }\n\n /** Места хода по номерам картинок: вышедшие за предел — в ошибки, без заказа. */\n function takeTurnPlaces(caller: Caller, count: number): { allowed: number[]; overLimit: FailedImage[] } {\n const allowed: number[] = [];\n const overLimit: FailedImage[] = [];\n for (let number = 1; number <= count; number++) {\n if (turns.take(caller.turnKey)) allowed.push(number);\n else overLimit.push({ номер: number, причина: `не больше ${options.maxImagesPerTurn} картинок за ход` });\n }\n return { allowed, overLimit };\n }\n\n const giveTurnPlaces = (caller: Caller, numbers: readonly number[]) => numbers.forEach(() => turns.give(caller.turnKey));\n\n async function generateImages(input: GenerateImagesInput, caller: Caller, abortSignal: AbortSignal | undefined) {\n const { ownerId, chatId } = caller;\n const setup = setups.byPurpose.get(setups.hasPurposes ? (input.purpose ?? \"\") : \"\")!;\n // «Стоп» до начала: ни проверки, ни генерации, ни расхода.\n const stoppedBeforeWork = { создано: 0, заказано: input.count, картинки: [], остановлено: true };\n if (abortSignal?.aborted) return stoppedBeforeWork;\n const { allowed, overLimit } = takeTurnPlaces(caller, input.count);\n if (allowed.length === 0) return { создано: 0, заказано: input.count, картинки: [], ошибки: overLimit };\n const booked = await reserve(ownerId, chatId, allowed.length, setup.priceUsd);\n if (\"refusal\" in booked) {\n giveTurnPlaces(caller, allowed);\n return { отказ: booked.refusal };\n }\n try {\n if (abortSignal?.aborted) {\n giveTurnPlaces(caller, allowed);\n return stoppedBeforeWork;\n }\n return await runTasks(input, setup, caller, allowed, overLimit, booked.reservation, abortSignal);\n } finally {\n // Вызов закончен: его задачи или оплачены, или в реестре задач (и видны лимиту оттуда), бронь больше не нужна.\n reservations.delete(booked.reservation);\n }\n }\n\n async function runTasks(\n input: GenerateImagesInput,\n setup: ImageSetup,\n caller: Caller,\n numbers: readonly number[],\n overLimit: readonly FailedImage[],\n reservation: Reservation,\n abortSignal: AbortSignal | undefined,\n ) {\n const { ownerId, chatId } = caller;\n const saved: SavedImage[] = [];\n const failed: FailedImage[] = [...overLimit];\n const pendingReason =\n `Картинка не успела за ${Math.round(requestTimeoutMs / 1000)} с и ещё готовится: ` + \"повтори вызов с теми же параметрами позже — готовая придёт без новой оплаты\";\n\n const collect = (number: number, outcome: ImageJobOutcome) => {\n if (outcome.status === \"saved\") {\n const { image } = outcome;\n saved.push({\n номер: number,\n ...(image.id !== undefined && { id: image.id }),\n ширина: image.width,\n высота: image.height,\n ...(image.result !== undefined && { результат: image.result }),\n });\n }\n else if (outcome.status === \"failed\") failed.push({ номер: number, причина: outcome.reason });\n else if (outcome.status === \"pending\") failed.push({ номер: number, причина: pendingReason, готовится: true });\n };\n await Promise.all(\n numbers.map(async (number) => {\n const jobInput: ImageJobInput = { ownerId, chatId, number, prompt: input.prompt, aspectRatio: input.aspectRatio, setup };\n if (caller.turnId !== undefined) turnIds.set(jobInput, caller.turnId);\n // Задача уходит в реестр: дальше лимит видит её там, а не в брони.\n const outcome = jobs.obtain(jobInput, abortSignal);\n reservation.openTasks--;\n collect(number, await outcome);\n }),\n );\n const byNumber = <T extends { номер: number }>(items: T[]) => [...items].sort((a, b) => a.номер - b.номер);\n return {\n создано: saved.length,\n заказано: input.count,\n картинки: byNumber(saved),\n ...(failed.length > 0 && { ошибки: byNumber(failed) }),\n ...(abortSignal?.aborted && { остановлено: true }),\n };\n }\n\n // --- фоновый режим ---\n\n const background = options.mode === \"background\";\n\n /** Готовая фоновая картинка: свой приёмник или вложения чата с `onLateImage`. */\n async function receiveQueued(image: GeneratedImage, job: ImageJob, context: ImageDeliveryContext): Promise<unknown> {\n const number = (job.data as { number?: number } | undefined)?.number ?? 1;\n const delivery: ImageDelivery = {\n ownerId: job.ownerId,\n chatId: job.chatId,\n ...(job.turnId !== undefined && { turnId: job.turnId }),\n number,\n prompt: job.prompt,\n aspectRatio: job.aspectRatio,\n ...(job.purpose !== undefined && { purpose: job.purpose }),\n modelId: image.modelId,\n jobId: job.id,\n };\n if (deliver) return deliver(image, delivery, context);\n const saved = await saveImageToChat({ storage: options.attachments.storage, chatId: job.chatId, ownerId: job.ownerId, number, file: image, now });\n try {\n await options.onLateImage?.({\n ownerId: job.ownerId,\n chatId: job.chatId,\n number,\n attachmentId: saved.id,\n mediaType: image.mediaType,\n width: image.width,\n height: image.height,\n prompt: job.prompt,\n aspectRatio: job.aspectRatio,\n modelId: image.modelId,\n ...(job.purpose !== undefined && { purpose: job.purpose }),\n });\n } catch (error) {\n logError(`Картинки: onLateImage не принял картинку: ${errorText(error)}`);\n }\n return { attachmentId: saved.id };\n }\n\n const queue: ImageQueue | undefined = background\n ? createImageQueue({\n env: options.env,\n ...(options.catalog && { catalog: options.catalog }),\n ...(options.model !== undefined && { model: options.model }),\n ...(options.provider !== undefined && { provider: options.provider }),\n ...(options.quality !== undefined && { quality: options.quality }),\n ...(options.size !== undefined && { size: options.size }),\n ...(options.transparentBackground !== undefined && { transparentBackground: options.transparentBackground }),\n ...(options.output && { output: options.output }),\n ...(options.purposes && { purposes: options.purposes }),\n ...(options.fetch && { fetch: options.fetch }),\n deliver: receiveQueued,\n ...(options.jobStore && { store: options.jobStore }),\n ...(options.spending && { spending: options.spending }),\n ...(options.pollIntervalMs !== undefined && { pollIntervalMs: options.pollIntervalMs }),\n attemptTimeoutMs: requestTimeoutMs,\n ...(options.attempts !== undefined && { attempts: options.attempts }),\n ...(options.lateResultWindowMs !== undefined && { lateResultWindowMs: options.lateResultWindowMs }),\n ...(options.maxImageBytes !== undefined && { maxImageBytes: options.maxImageBytes }),\n ...(options.logError && { logError: options.logError }),\n ...(options.now && { now: options.now }),\n })\n : undefined;\n\n /** Фоновый заказ: ответ модели, как только задачи ушли провайдеру. */\n async function orderImages(queue: ImageQueue, input: GenerateImagesInput, caller: Caller, abortSignal: AbortSignal | undefined) {\n if (abortSignal?.aborted) return { заказано: input.count, принято: 0, задачи: [], остановлено: true };\n const { allowed, overLimit } = takeTurnPlaces(caller, input.count);\n const failed: FailedImage[] = [...overLimit];\n const queuedJobs: Array<Record<string, unknown> & { номер: number }> = [];\n await Promise.all(\n allowed.map(async (number) => {\n let placed: Awaited<ReturnType<ImageQueue[\"order\"]>>;\n try {\n placed = await queue.order({\n ownerId: caller.ownerId,\n chatId: caller.chatId,\n ...(caller.turnId !== undefined && { turnId: caller.turnId }),\n ...(setups.hasPurposes && input.purpose !== undefined && { purpose: input.purpose }),\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n data: { number },\n });\n } catch (error) {\n // Каждый заказ — сам по себе: сбой одного не роняет уже принятые, иначе модель повторит вызов и оплатит их снова.\n turns.give(caller.turnKey);\n logError(`Картинки: заказ ${number} не принят: ${errorText(error)}`);\n failed.push({ номер: number, причина: \"Не удалось заказать картинку\" });\n return;\n }\n if (placed.status === \"refused\") {\n turns.give(caller.turnKey);\n failed.push({ номер: number, причина: placed.reason });\n } else if (placed.status === \"failed\") failed.push({ номер: number, причина: placed.reason ?? \"Провайдер не смог создать картинку\" });\n else queuedJobs.push({ номер: number, id: placed.id, ...(await queuedFields(placed)) });\n }),\n );\n const byNumber = <T extends { номер: number }>(items: T[]) => [...items].sort((a, b) => a.номер - b.номер);\n return {\n заказано: input.count,\n принято: queuedJobs.length,\n задачи: byNumber(queuedJobs),\n ...(failed.length > 0 && { ошибки: byNumber(failed) }),\n ...(queuedJobs.length > 0 && { сообщение: queuedMessage }),\n };\n }\n\n async function queuedFields(job: ImageJob): Promise<Record<string, unknown>> {\n try {\n return (await options.onQueued?.(job)) ?? {};\n } catch (error) {\n logError(`Картинки: onQueued не сработал (задача ${job.id}): ${errorText(error)}`);\n return {};\n }\n }\n\n // Описание и схема собираются один раз: не зависят от человека, чата и времени (кэш модели).\n const purposeList = setups.hasPurposes\n ? \" Назначения (параметр purpose): \" + all.map((setup) => `${setup.purpose} — ${setup.description}`).join(\"; \") + \".\"\n : \"\";\n const description = background ? backgroundDescription + purposeList : baseDescription + purposeList + (queued ? repeatHint : \"\");\n const purposeNames = all.map((setup) => setup.purpose!) as [string, ...string[]];\n const inputSchema = setups.hasPurposes\n ? z.object({\n prompt: promptSchema,\n purpose: z.enum(purposeNames).default(purposeNames[0]).describe(\"Назначение картинки: от него зависит модель.\"),\n count: countSchema,\n aspectRatio: aspectRatioSchema,\n })\n : z.object({ prompt: promptSchema, count: countSchema, aspectRatio: aspectRatioSchema });\n\n // Генерация и скачивание: ядро не обрывает вызов раньше, и модель не заказывает (и не оплачивает) картинки заново.\n const toolTimeoutMs = requestTimeoutMs + DOWNLOAD_TIMEOUT_MS;\n const generateImagesTool = (caller: Caller) =>\n declareToolTimeout(\n tool({\n description,\n inputSchema: inputSchema as z.ZodType<GenerateImagesInput>,\n execute: (input: GenerateImagesInput, { abortSignal }): Promise<Record<string, unknown>> =>\n queue ? orderImages(queue, input, caller, abortSignal) : generateImages(input, caller, abortSignal),\n }),\n toolTimeoutMs,\n );\n\n return {\n tools(model): ToolSet {\n if (!model.ownerId || !model.chatId) return {};\n const caller: Caller = {\n ownerId: model.ownerId,\n chatId: model.chatId,\n ...(model.turnId !== undefined && { turnId: model.turnId }),\n turnKey: model.turnId ?? `tools:${crypto.randomUUID()}`,\n };\n return { generate_images: generateImagesTool(caller) };\n },\n start: () => queue?.start() ?? Promise.resolve(),\n stop: () => queue?.stop() ?? Promise.resolve(),\n };\n}\n","import { detectFileKind } from \"@ykurilov/agent-chat-attachments\";\nimport { isAllowedImageAddress } from \"./download-address\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport { pngSize } from \"./postprocess-image\";\nimport type { ProcessedImage } from \"./postprocess-image\";\n\n/** Скачивание одной картинки — не дольше этого: задача у провайдера уже готова, файл лежит на его складе. */\nexport const DOWNLOAD_TIMEOUT_MS = 60_000;\n/** Переадресаций склада не больше этого: каждая проверяется, как первый адрес. */\nconst MAX_REDIRECTS = 3;\n\n/** Типы картинок, которые принимаются, и их расширения. */\nexport const imageExtensions: Record<string, string> = { \"image/png\": \"png\", \"image/jpeg\": \"jpg\", \"image/webp\": \"webp\" };\n\nexport interface DownloadGeneratedImageInput {\n /** Адрес готовой картинки у провайдера. */\n url: string;\n fetch: typeof globalThis.fetch;\n maxImageBytes: number;\n /** Сайт подключения провайдера из настройки (`https://host:port`): с него можно скачивать и по http, и с localhost. */\n trustedOrigin?: string;\n /** «Стоп»: пришедшее после него не отдаётся. */\n abortSignal?: AbortSignal;\n /** Обработка после скачивания (формат, размер); её итог и отдаётся. */\n transform?: (bytes: Uint8Array, mediaType: string) => Promise<ProcessedImage>;\n}\n\n/** Скачанная картинка; размер — из файла (PNG или после обработки), иначе не известен. */\nexport interface DownloadedImage {\n bytes: Uint8Array;\n mediaType: string;\n width?: number;\n height?: number;\n}\n\nconst failure = (reason: string) => new ImageGenerationError(reason, { refusedBeforeWork: false });\nconst megabytes = (bytes: number) => `${Math.round((bytes / (1024 * 1024)) * 10) / 10} МБ`;\n\n/** Тело ответа, но не больше `maxBytes`: на пределе чтение обрывается, файл не копится в памяти дальше. */\nasync function readWithin(response: Response, maxBytes: number): Promise<Uint8Array | null> {\n if (!response.body) return new Uint8Array();\n const chunks: Uint8Array[] = [];\n let total = 0;\n const reader = response.body.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.length;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n return null;\n }\n chunks.push(value);\n }\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.length;\n }\n return bytes;\n}\n\nfunction checkedAddress(url: string, base: URL | undefined, trustedOrigin: string | undefined): URL {\n let address: URL;\n try {\n address = new URL(url, base);\n } catch {\n throw failure(\"Картинка не скачалась: неверный адрес\");\n }\n if (!isAllowedImageAddress(address, trustedOrigin)) throw failure(\"Картинка не скачалась: неверный адрес\");\n return address;\n}\n\n/** Ответ склада по адресу; переадресации идут вручную, каждый следующий адрес проверяется (download-address). */\nasync function fetchImage(input: DownloadGeneratedImageInput, first: URL, signal: AbortSignal): Promise<Response> {\n let address = first;\n for (let hop = 0; ; hop++) {\n const response = await input.fetch(address.href, { signal, redirect: \"manual\" });\n const location = response.status >= 300 && response.status < 400 ? response.headers.get(\"location\") : null;\n if (location === null) return response;\n await response.body?.cancel().catch(() => {});\n if (hop >= MAX_REDIRECTS) throw failure(\"Картинка не скачалась: слишком много переадресаций\");\n address = checkedAddress(location, address, input.trustedOrigin);\n }\n}\n\n/**\n * Скачивает готовую картинку по адресу провайдера (без заголовков подключения: это чужой склад) и обрабатывает её.\n * Тип — только по содержимому: PNG, JPEG или WebP. Сбой — `ImageGenerationError`; «Стоп» пробрасывается как есть.\n */\nexport async function downloadGeneratedImage(input: DownloadGeneratedImageInput): Promise<DownloadedImage> {\n const address = checkedAddress(input.url, undefined, input.trustedOrigin);\n\n const timeout = new AbortController();\n const timer = setTimeout(() => timeout.abort(new DOMException(\"timeout\", \"TimeoutError\")), DOWNLOAD_TIMEOUT_MS);\n // Срок скачивания не держит процесс: его держит сам запрос.\n (timer as { unref?: () => void }).unref?.();\n const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, timeout.signal]) : timeout.signal;\n let bytes: Uint8Array | null;\n try {\n const response = await fetchImage(input, address, signal);\n if (!response.ok) throw failure(`Картинка не скачалась: ${response.status}`);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > input.maxImageBytes) throw failure(`Картинка больше ${megabytes(input.maxImageBytes)}`);\n bytes = await readWithin(response, input.maxImageBytes);\n } catch (error) {\n if (input.abortSignal?.aborted) throw input.abortSignal.reason ?? error;\n if (error instanceof ImageGenerationError) throw error;\n throw failure(timeout.signal.aborted ? \"Картинка не скачалась: не успела\" : \"Картинка не скачалась\");\n } finally {\n clearTimeout(timer);\n }\n if (input.abortSignal?.aborted) throw input.abortSignal.reason;\n if (!bytes) throw failure(`Картинка больше ${megabytes(input.maxImageBytes)}`);\n\n const detected = detectFileKind(bytes, \"\");\n if (!detected || !imageExtensions[detected.mediaType]) throw failure(\"Провайдер вернул не картинку\");\n let file: DownloadedImage = { bytes, mediaType: detected.mediaType, ...pngSize(bytes) };\n if (input.transform) {\n try {\n file = await input.transform(bytes, detected.mediaType);\n } catch {\n throw failure(\"Картинка не обработалась\");\n }\n }\n if (input.abortSignal?.aborted) throw input.abortSignal.reason;\n return file;\n}\n","/*\n * Куда можно идти за готовой картинкой. Адрес приходит в ответе провайдера; без проверки подменённый ответ (прокси,\n * взломанный склад) отправил бы сервер во внутреннюю сеть: к метаданным облака, базе, админке. Поэтому — только\n * `https:` и не внутренние адреса. Исключение — сайт подключения провайдера из настройки приложения: ему доверяют\n * и так (свой прокси, поддельный провайдер демо на localhost). Имена хостов в адреса здесь не разрешаются.\n */\n\n/** Внутренние диапазоны IPv4: [первый октет, второй от, второй до] или [первый октет]. */\nconst PRIVATE_IPV4: Array<[number, number?, number?]> = [\n [0], [10], [127], [100, 64, 127], [169, 254, 254], [172, 16, 31], [192, 168, 168], [198, 18, 19],\n];\n\nfunction isPrivateIpv4(octets: number[]): boolean {\n const [first = 0, second = 0, third = 0] = octets;\n if (first >= 224) return true; // групповые, зарезервированные, широковещательный\n if (first === 192 && second === 0 && third === 0) return true;\n return PRIVATE_IPV4.some(([a, from, to]) => first === a && (from === undefined || (second >= from && second <= (to ?? from))));\n}\n\n/** Восемь групп IPv6 из записи, которую уже привёл к виду разборщик URL (без скобок). */\nfunction ipv6Groups(host: string): number[] | null {\n const [head = \"\", tail, extra] = host.split(\"::\");\n if (extra !== undefined) return null;\n const part = (text: string) => (text === \"\" ? [] : text.split(\":\").map((group) => Number.parseInt(group, 16)));\n const left = part(head);\n const right = tail === undefined ? [] : part(tail);\n const groups = tail === undefined ? left : [...left, ...Array<number>(8 - left.length - right.length).fill(0), ...right];\n return groups.length === 8 && groups.every((group) => Number.isInteger(group) && group >= 0 && group <= 0xffff) ? groups : null;\n}\n\nfunction isPrivateIpv6(host: string): boolean {\n const groups = ipv6Groups(host);\n if (!groups) return true;\n const [first = 0] = groups;\n if (groups.slice(0, 7).every((group) => group === 0) && (groups[7] ?? 0) <= 1) return true; // :: и ::1\n if ((first & 0xfe00) === 0xfc00 || (first & 0xffc0) === 0xfe80 || (first & 0xff00) === 0xff00) return true;\n // IPv4 внутри IPv6: ::ffff:a.b.c.d и ::a.b.c.d.\n if (groups.slice(0, 5).every((group) => group === 0) && (groups[5] === 0xffff || groups[5] === 0)) {\n const [high = 0, low = 0] = groups.slice(6);\n return isPrivateIpv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);\n }\n return false;\n}\n\nfunction isInternalHost(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, \"\");\n if (host.startsWith(\"[\")) return isPrivateIpv6(host.slice(1, -1));\n const ipv4 = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(host);\n if (ipv4) return isPrivateIpv4(ipv4.slice(1).map(Number));\n return !host.includes(\".\") || /\\.(?:localhost|local|internal|home\\.arpa)$/.test(host);\n}\n\n/** Можно ли скачать картинку по этому адресу. `trustedOrigin` — сайт подключения провайдера из настройки. */\nexport function isAllowedImageAddress(address: URL, trustedOrigin?: string): boolean {\n if (trustedOrigin !== undefined && address.origin === trustedOrigin) return true;\n return address.protocol === \"https:\" && !isInternalHost(address.hostname);\n}\n","import type { ImageFileFormat } from \"./image-catalog\";\n\n/** Постобработка готовой картинки перед сохранением: формат файла и предел большей стороны. */\nexport interface ImageOutputOptions {\n /** Формат файла; не указан — формат провайдера. Прозрачность держат `png` и `webp`. */\n format?: ImageFileFormat;\n /** Большая сторона не длиннее этого, пикселей; меньшая картинка не увеличивается. */\n maxSide?: number;\n}\n\nexport interface ProcessedImage {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n}\n\nexport const imageFileFormats: readonly ImageFileFormat[] = [\"png\", \"jpeg\", \"webp\"];\n\nconst mediaTypes: Record<ImageFileFormat, string> = { png: \"image/png\", jpeg: \"image/jpeg\", webp: \"image/webp\" };\nconst formatOf: Record<string, ImageFileFormat> = { \"image/png\": \"png\", \"image/jpeg\": \"jpeg\", \"image/webp\": \"webp\" };\n\n/** Ошибка настройки `output` — текст с именем поля. */\nexport function checkImageOutput(output: ImageOutputOptions | undefined, where: string): void {\n if (!output) return;\n if (output.format !== undefined && !imageFileFormats.includes(output.format)) {\n throw new Error(`Картинки: ${where}output.format — одно из ${imageFileFormats.join(\", \")}`);\n }\n if (output.maxSide !== undefined && !(Number.isInteger(output.maxSide) && output.maxSide > 0)) {\n throw new Error(`Картинки: ${where}output.maxSide — целое число больше нуля`);\n }\n}\n\n/**\n * Формат и размер через sharp. sharp грузится только здесь и только при первой обработке: приложение без `output` его\n * не загружает. Прозрачность сохраняется: PNG и WebP пишутся с альфой.\n */\nexport async function postprocessImage(bytes: Uint8Array, mediaType: string, output: ImageOutputOptions): Promise<ProcessedImage> {\n const { default: sharp } = await import(\"sharp\");\n const format = output.format ?? formatOf[mediaType] ?? \"png\";\n let image = sharp(bytes);\n if (output.maxSide !== undefined) image = image.resize({ width: output.maxSide, height: output.maxSide, fit: \"inside\", withoutEnlargement: true });\n const { data, info } = await image.toFormat(format).toBuffer({ resolveWithObject: true });\n return { bytes: new Uint8Array(data), mediaType: mediaTypes[format], width: info.width, height: info.height };\n}\n\n/** Размер PNG из заголовка (IHDR), без разбора картинки; не PNG или заголовок битый — `undefined`. */\nexport function pngSize(bytes: Uint8Array): { width: number; height: number } | undefined {\n if (bytes.length < 24) return undefined;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const isHeader = String.fromCharCode(bytes[12]!, bytes[13]!, bytes[14]!, bytes[15]!) === \"IHDR\";\n if (view.getUint32(0) !== 0x89504e47 || !isHeader) return undefined;\n const width = view.getUint32(16);\n const height = view.getUint32(20);\n return width > 0 && height > 0 ? { width, height } : undefined;\n}\n","import { downloadGeneratedImage } from \"./download-image\";\nimport type { AspectRatio, ImageGenerationRequest, ImageGenerationResult } from \"./image-catalog\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport type { ImageSetup, ImageStep } from \"./image-setup\";\nimport { waitForImageTask } from \"./image-task-polling\";\nimport { postprocessImage } from \"./postprocess-image\";\n\n/** Пауза или готовая картинка без вызова, который её ждёт, хранится столько: повтор найдёт её, потом память чистится. */\nconst FORGET_AFTER_MS = 60 * 60 * 1000;\n\n/** Одна картинка одного вызова: кто, где, что и по какой настройке. */\nexport interface ImageJobInput {\n ownerId: string;\n chatId: string;\n /** Номер картинки в вызове, с 1. */\n number: number;\n prompt: string;\n aspectRatio: AspectRatio;\n setup: ImageSetup;\n}\n\n/** Картинка у приложения: во вложениях чата (`id`) или у своего приёмника (`result` — его ответ). */\nexport interface DeliveredImage {\n id?: string;\n result?: unknown;\n mediaType: string;\n width: number;\n height: number;\n}\n\n/** Картинка пришла после срока вызова и уже сохранена в чат: приложение может показать её человеку. */\nexport interface LateImage {\n ownerId: string;\n chatId: string;\n number: number;\n attachmentId: string;\n mediaType: string;\n width: number;\n height: number;\n prompt: string;\n aspectRatio: AspectRatio;\n /** Модель картинки (первого шага). */\n modelId: string;\n /** Назначение, если приложение их задало. */\n purpose?: string;\n}\n\n/** Чем кончилось ожидание одной картинки для вызова. */\nexport type ImageJobOutcome =\n | { status: \"saved\"; image: DeliveredImage }\n | { status: \"failed\"; reason: string }\n /** Срок вызова вышел, задача ещё у провайдера: повтор того же вызова найдёт её. */\n | { status: \"pending\" }\n | { status: \"stopped\" };\n\n/** Скачанная картинка с размером: из файла, а не известен — по пропорции модели. */\nexport interface ReceivedImageFile {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n}\n\nexport interface ImageJobsOptions {\n /** Приёмник скачанной картинки: вложения чата или свой у приложения. */\n /** `signal` — задача остановлена: приёмник не записывает картинку. */\n receive(input: ImageJobInput, image: ReceivedImageFile, signal: AbortSignal): Promise<{ id?: string; result?: unknown }>;\n maxImageBytes: number;\n requestTimeoutMs: number;\n lateResultWindowMs: number;\n now: () => Date;\n /** Запись расхода шага: один раз на задачу провайдера. */\n record(input: ImageJobInput, step: ImageStep, costUsd: number): Promise<void>;\n onLateImage?: (image: LateImage) => void | Promise<void>;\n logError(message: string): void;\n hideSecrets(text: string): string;\n}\n\ninterface StepState {\n step: ImageStep;\n /** Id задачи в очереди провайдера: по нему повтор проверяет старую задачу. */\n taskId?: string;\n /** Задача могла уйти провайдеру: без ответа она всё равно оплачивается по цене модели. */\n sent: boolean;\n charged: boolean;\n /** Провайдер отказал до работы: платить не за что. */\n released: boolean;\n results?: ImageGenerationResult[];\n}\n\ntype Settled = { status: \"saved\"; image: DeliveredImage } | { status: \"failed\"; reason: string } | { status: \"paused\" };\n\ninterface ImageJob {\n /** Ключ повтора; есть только у задач с очередью — синхронную задачу проверить потом нечем. */\n key?: string;\n input: ImageJobInput;\n steps: StepState[];\n state: \"running\" | \"paused\" | \"saved\" | \"finished\";\n /** Задача скачивается и сохраняется: срок вызова её уже не обрывает. */\n saving: boolean;\n controller: AbortController;\n /** Дальше опрос не идёт: срок вызова плюс фоновое окно. */\n hardDeadline: number;\n waiters: number;\n settled: Promise<Settled>;\n settle(result: Settled): void;\n image?: DeliveredImage;\n forgetTimer?: ReturnType<typeof setTimeout>;\n}\n\n/** Срок фонового опроса вышел без результата. */\nconst EXPIRED = Symbol(\"expired\");\nconst STOPPED = Symbol(\"stopped\");\nconst DEADLINE = Symbol(\"deadline\");\n\nfunction whenStopped(signal: AbortSignal | undefined): Promise<typeof STOPPED> {\n return new Promise((resolve) => {\n if (!signal) return;\n if (signal.aborted) resolve(STOPPED);\n else signal.addEventListener(\"abort\", () => resolve(STOPPED), { once: true });\n });\n}\n\n/** Цена ответа провайдера; не сообщил — цена модели. */\nfunction costOf(results: readonly ImageGenerationResult[], fallbackUsd: number): number {\n const known = results.filter((result) => result.costUsd !== undefined);\n return known.length > 0 ? known.reduce((total, result) => total + result.costUsd!, 0) : fallbackUsd;\n}\n\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n\n/**\n * Задачи картинок, которые живут дольше вызова. Задача с очередью (KIE) после срока вызова опрашивается в фоне, а повтор\n * того же вызова (тот же человек, чат, назначение, описание, пропорция и номер) сначала берёт старую задачу: ждёт её,\n * проверяет или отдаёт уже сохранённую картинку. Каждая задача провайдера оплачивается ровно один раз: его ценой, а без\n * ответа («Стоп», срок, сбой) — ценой модели. Реестр — в памяти процесса.\n */\nexport function createImageJobs(options: ImageJobsOptions) {\n const jobs = new Set<ImageJob>();\n const byKey = new Map<string, ImageJob>();\n\n const keyOf = (input: ImageJobInput) =>\n JSON.stringify([input.ownerId, input.chatId, input.setup.purpose ?? input.setup.steps[0]!.profile.id, input.prompt, input.aspectRatio, input.number]);\n\n function forget(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n jobs.delete(job);\n if (job.key !== undefined && byKey.get(job.key) === job) byKey.delete(job.key);\n }\n\n function forgetLater(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n job.forgetTimer = setTimeout(() => forget(job), FORGET_AFTER_MS);\n (job.forgetTimer as { unref?: () => void }).unref?.();\n }\n\n async function charge(job: ImageJob, state: StepState, costUsd: number) {\n if (state.charged) return;\n state.charged = true;\n await options.record(job.input, state.step, costUsd);\n }\n\n /** Ушедшие и не оплаченные задачи — по цене модели: лучше записать лишнее, чем потерять. */\n const chargeOpen = (job: ImageJob) =>\n Promise.all(job.steps.filter((state) => state.sent && !state.charged && !state.released).map((state) => charge(job, state, state.step.priceUsd)));\n\n function request(job: ImageJob, state: StepState, sourceUrl: string | undefined, signal: AbortSignal): ImageGenerationRequest {\n const { step } = state;\n const { aspectRatio } = job.input;\n const { width, height } = step.sizes[aspectRatio];\n return {\n taskUUID: crypto.randomUUID(),\n providerModelId: step.providerModelId,\n prompt: step.prompt ?? job.input.prompt,\n width,\n height,\n aspectRatio: step.profile.aspectRatioValues?.[aspectRatio] ?? aspectRatio,\n ...(sourceUrl !== undefined && { imageUrl: sourceUrl }),\n ...(step.outputFormat && { outputFormat: step.outputFormat }),\n ...(step.providerSettings && { providerSettings: step.providerSettings }),\n abortSignal: signal,\n timeoutMs: options.requestTimeoutMs,\n };\n }\n\n async function runStep(job: ImageJob, state: StepState, sourceUrl: string | undefined, signal: AbortSignal): Promise<ImageGenerationResult[]> {\n const { step } = state;\n const refused = (error: unknown) => {\n if (error instanceof ImageGenerationError && error.refusedBeforeWork && !signal.aborted) {\n state.sent = false;\n state.released = true;\n }\n return error;\n };\n const queue = step.provider.queue;\n if (!queue) {\n state.sent = true;\n let results: ImageGenerationResult[];\n try {\n results = await step.provider.generate(request(job, state, sourceUrl, signal), step.connection);\n } catch (error) {\n throw refused(error);\n }\n await charge(job, state, costOf(results, step.priceUsd));\n return results;\n }\n\n // Задача уже у провайдера (повтор, фоновый опрос после паузы) — сначала проверка, без ожидания.\n const checkNow = state.taskId !== undefined;\n if (state.taskId === undefined) {\n // Прошлая отправка оборвалась без id и уже оплачена по цене модели: новая задача — новая оплата.\n state.charged = false;\n state.sent = true;\n try {\n state.taskId = await queue.submit(request(job, state, sourceUrl, signal), step.connection);\n } catch (error) {\n throw refused(error);\n }\n }\n const taskId = state.taskId;\n const outcome = await waitForImageTask({ check: (abortSignal) => queue.check(taskId, step.connection, abortSignal), deadline: job.hardDeadline, abortSignal: signal, checkNow });\n if (outcome.status === \"ready\") {\n await charge(job, state, costOf(outcome.results, step.priceUsd));\n return outcome.results;\n }\n if (outcome.status === \"failed\") {\n await charge(job, state, outcome.costUsd ?? step.priceUsd);\n throw new ImageGenerationError(outcome.reason, { refusedBeforeWork: false });\n }\n throw EXPIRED;\n }\n\n /** Адрес картинки шага: у модели с основой первым идёт фон без предмета, картинка — следующая. */\n function resultUrl(state: StepState): string {\n const result = state.results?.[state.step.profile.firstResultIsBase ? 1 : 0];\n if (!result) throw new ImageGenerationError(\"Провайдер не вернул картинку\", { refusedBeforeWork: false });\n return result.url;\n }\n\n async function save(job: ImageJob, url: string, signal: AbortSignal): Promise<DeliveredImage> {\n const { input } = job;\n const last = job.steps.at(-1)!.step;\n const output = input.setup.output;\n const file = await downloadGeneratedImage({\n url,\n fetch: last.connection.fetch,\n maxImageBytes: options.maxImageBytes,\n ...(last.trustedOrigin !== undefined && { trustedOrigin: last.trustedOrigin }),\n ...(output && { transform: (bytes: Uint8Array, mediaType: string) => postprocessImage(bytes, mediaType, output) }),\n abortSignal: signal,\n });\n const planned = input.setup.steps[0]!.sizes[input.aspectRatio];\n const image = { bytes: file.bytes, mediaType: file.mediaType, width: file.width ?? planned.width, height: file.height ?? planned.height };\n const received = await options.receive(input, image, signal);\n return { ...received, mediaType: image.mediaType, width: image.width, height: image.height };\n }\n\n async function drive(job: ImageJob, signal: AbortSignal) {\n let saving = false;\n try {\n let sourceUrl: string | undefined;\n for (const [index, state] of job.steps.entries()) {\n state.results ??= await runStep(job, state, sourceUrl, signal);\n if (signal.aborted) return;\n if (index < job.steps.length - 1) sourceUrl = state.results[0]?.url;\n }\n saving = job.saving = true;\n const image = await save(job, resultUrl(job.steps.at(-1)!), signal);\n if (signal.aborted) return;\n finish(job, { status: \"saved\", image });\n } catch (error) {\n // «Стоп» или пауза: оплату и состояние ведёт `pause`.\n if (signal.aborted) return;\n if (error === EXPIRED) return void (await pause(job));\n let reason: string;\n if (error instanceof ImageGenerationError) reason = options.hideSecrets(error.reason);\n else if (saving) {\n options.logError(`Картинки: не удалось сохранить картинку: ${errorText(error)}`);\n reason = \"Картинка не сохранилась\";\n } else {\n options.logError(`Картинки: сбой провайдера ${job.steps.find((state) => !state.results)?.step.provider.id ?? \"\"}: ${errorText(error)}`);\n reason = \"Провайдер не смог создать картинку\";\n }\n await chargeOpen(job);\n finish(job, { status: \"failed\", reason });\n }\n }\n\n function finish(job: ImageJob, result: Exclude<Settled, { status: \"paused\" }>) {\n if (result.status === \"saved\" && job.waiters === 0 && job.key !== undefined) {\n // Вызов, который ждал, уже ушёл: картинка ждёт повтора, а приложение узнаёт о ней сразу.\n job.state = \"saved\";\n job.image = result.image;\n forgetLater(job);\n // Свой приёмник картинку уже получил; о вложении чата приложение узнаёт здесь.\n if (result.image.id !== undefined) void notifyLate(job, result.image.id, result.image);\n } else {\n job.state = \"finished\";\n forget(job);\n }\n job.settle(result);\n }\n\n async function notifyLate(job: ImageJob, attachmentId: string, image: DeliveredImage) {\n const { input } = job;\n try {\n await options.onLateImage?.({\n ownerId: input.ownerId,\n chatId: input.chatId,\n number: input.number,\n attachmentId,\n mediaType: image.mediaType,\n width: image.width,\n height: image.height,\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n modelId: input.setup.steps[0]!.profile.id,\n ...(input.setup.purpose !== undefined && { purpose: input.setup.purpose }),\n });\n } catch (error) {\n options.logError(`Картинки: onLateImage не принял картинку: ${errorText(error)}`);\n }\n }\n\n /**\n * Опрос остановлен («Стоп», срок): ушедшие задачи оплачены по цене модели. Задача с очередью ждёт повтора, синхронная\n * забывается — проверить её нечем.\n */\n async function pause(job: ImageJob) {\n if (job.state !== \"running\") return;\n job.state = job.key !== undefined ? \"paused\" : \"finished\";\n job.controller.abort(new DOMException(\"paused\", \"AbortError\"));\n await chargeOpen(job);\n if (job.state === \"paused\") forgetLater(job);\n else forget(job);\n job.settle({ status: \"paused\" });\n }\n\n function start(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n job.state = \"running\";\n job.saving = false;\n job.controller = new AbortController();\n job.hardDeadline = Date.now() + options.requestTimeoutMs + options.lateResultWindowMs;\n job.settled = new Promise((resolve) => (job.settle = resolve));\n void drive(job, job.controller.signal);\n }\n\n /** Срок вызова: только у задач с очередью и только пока картинка не готова. */\n function deadline(job: ImageJob): { promise: Promise<typeof DEADLINE>; cancel(): void } {\n if (job.key === undefined) return { promise: new Promise(() => {}), cancel() {} };\n let timer: ReturnType<typeof setTimeout>;\n const promise = new Promise<typeof DEADLINE>((resolve) => (timer = setTimeout(() => resolve(DEADLINE), options.requestTimeoutMs)));\n return { promise, cancel: () => clearTimeout(timer) };\n }\n\n /** Вызов ждёт картинку: готова, провал, «Стоп» или срок вызова. Последний ушедший без картинки останавливает опрос сам. */\n async function attend(job: ImageJob, abortSignal: AbortSignal | undefined): Promise<ImageJobOutcome> {\n job.waiters++;\n const stopped = whenStopped(abortSignal);\n const timer = deadline(job);\n let outcome = await Promise.race([job.settled, stopped, timer.promise]);\n if (outcome === DEADLINE && job.saving) outcome = await Promise.race([job.settled, stopped]);\n timer.cancel();\n job.waiters--;\n if (outcome === STOPPED) {\n if (job.waiters === 0) await pause(job);\n return { status: \"stopped\" };\n }\n if (outcome === DEADLINE) {\n // Фонового окна нет или оно кончилось: опрос останавливается вместе с вызовом.\n if (job.waiters === 0 && Date.now() >= job.hardDeadline) await pause(job);\n return { status: \"pending\" };\n }\n if (outcome.status === \"paused\") return { status: \"pending\" };\n return outcome;\n }\n\n return {\n /**\n * Картинка для вызова: уже сохранённая с прошлого раза (сразу, без оплаты), старая задача (ждать или проверить) или\n * новая. Найденная готовая картинка «израсходована»: следующий такой же вызов — новая.\n */\n async obtain(input: ImageJobInput, abortSignal: AbortSignal | undefined): Promise<ImageJobOutcome> {\n const queued = input.setup.steps.some((step) => step.provider.queue !== undefined);\n const key = queued ? keyOf(input) : undefined;\n const existing = key !== undefined ? byKey.get(key) : undefined;\n if (existing?.state === \"saved\") {\n forget(existing);\n existing.state = \"finished\";\n return { status: \"saved\", image: existing.image! };\n }\n let job = existing;\n if (job?.state === \"paused\") start(job);\n if (!job) {\n job = {\n ...(key !== undefined && { key }),\n input,\n steps: input.setup.steps.map((step) => ({ step, sent: false, charged: false, released: false })),\n waiters: 0,\n } as Omit<ImageJob, \"state\" | \"saving\" | \"controller\" | \"hardDeadline\" | \"settled\" | \"settle\"> as ImageJob;\n jobs.add(job);\n if (key !== undefined) byKey.set(key, job);\n start(job);\n }\n return attend(job, abortSignal);\n },\n\n /** Задачи в работе, ещё не оплаченные: их цена по профилям — у человека и в этом чате. */\n pendingUsd(ownerId: string, chatId: string): { user: number; chat: number } {\n let user = 0;\n let chat = 0;\n for (const job of jobs) {\n if (job.state !== \"running\" || job.input.ownerId !== ownerId) continue;\n const open = job.steps.reduce((total, state) => total + (state.charged || state.released ? 0 : state.step.priceUsd), 0);\n user += open;\n if (job.input.chatId === chatId) chat += open;\n }\n return { user, chat };\n },\n };\n}\n\nexport type ImageJobs = ReturnType<typeof createImageJobs>;\n","import { SpendingLimitReachedError } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage, SpendingLimits } from \"@ykurilov/agent-chat-core\";\nimport { downloadGeneratedImage } from \"../download-image\";\nimport type { DownloadedImage } from \"../download-image\";\nimport { aspectRatios } from \"../image-catalog\";\nimport type { AspectRatio, ImageCatalog, ImageGenerationRequest, ImageGenerationResult, ImageQuality, ImageSize, ImageTaskState } from \"../image-catalog\";\nimport { ImageGenerationError } from \"../image-generation-error\";\nimport { resolveImageSetups } from \"../image-setup\";\nimport type { ImagePurpose, ImageSetup, ImageStep } from \"../image-setup\";\nimport { postprocessImage } from \"../postprocess-image\";\nimport type { ImageOutputOptions } from \"../postprocess-image\";\nimport { ImageJobKeyTakenError, isOpenImageJob, memoryImageJobStore } from \"./image-job-store\";\nimport type { ImageJobCharge, ImageJobFilter, ImageJobPatch, ImageJobRecord, ImageJobStatus, ImageJobStep, ImageJobStore, ImageJobTask } from \"./image-job-store\";\nimport { createTurnCounter } from \"./turn-counter\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 3_000;\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 150_000;\nconst DEFAULT_LATE_RESULT_WINDOW_MS = 10 * 60 * 1000;\n/** В позднем окне проверки реже: картинка уже опоздала, частый опрос ничего не ускорит. */\nconst MIN_LATE_POLL_INTERVAL_MS = 10_000;\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\nconst DEFAULT_DOWNLOAD_ATTEMPTS = 3;\nconst DEFAULT_LEASE_MS = 30_000;\nconst DEFAULT_ACCEPT_WAIT_MS = 5_000;\nconst DEFAULT_FORGET_FINISHED_AFTER_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_IMAGE_BYTES = 10 * 1024 * 1024;\n/** Уборка законченных задач — не чаще этого. */\nconst PRUNE_EVERY_MS = 60 * 60 * 1000;\n/** Условная запись не прошла столько раз подряд — задачу кто-то правит без конца, это сбой. */\nconst MAX_WRITE_TRIES = 10;\n/** Место всё время занимают заново (другие копии сервера заказывают на него же) — столько попыток занять его. */\nconst MAX_PLACE_TRIES = 5;\nconst LIMIT_REACHED = \"лимит расходов исчерпан\";\nconst QUEUE_STOPPED = \"очередь остановлена\";\n\n/** Готовая картинка для приёмника. Размер — из файла; не известен — по пропорции модели. */\nexport interface GeneratedImage {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n /** Модель картинки (первого шага). */\n modelId: string;\n}\n\n/** Задача картинки, какой её видит приложение. */\nexport interface ImageJob {\n id: string;\n key: string;\n ownerId: string;\n chatId: string;\n turnId?: string;\n group?: string;\n /** Назначение; нет — очередь без назначений. */\n purpose?: string;\n prompt: string;\n aspectRatio: AspectRatio;\n /** Данные приложения из заказа, как были. */\n data?: unknown;\n status: ImageJobStatus;\n /** Почему не получилась. */\n reason?: string;\n /** Ответ приёмника. */\n result?: unknown;\n attempt: number;\n /** Попытки кончились, но задача у провайдера ещё опрашивается до этого времени: может прийти поздно. */\n lateUntil?: string;\n createdAt: string;\n updatedAt: string;\n finishedAt?: string;\n}\n\n/**\n * Доставка ещё нужна? Задачу могут вытеснить новым заказом на то же место, пока приёмник пишет файл: запись должна быть\n * условной — перед ней спросить `isCurrent()`, а долгую работу обрывать по `signal`.\n */\nexport interface ImageDeliveryContext {\n /** Срабатывает, когда доставка этой копией больше не нужна: задачу вытеснили, повторили или очередь остановлена. */\n signal: AbortSignal;\n /** Задача всё ещё наша и ждёт этой картинки (перечитывает хранилище). `false` — не записывать, вернуть `false`. */\n isCurrent(): Promise<boolean>;\n}\n\n/** Заказ не принят: лимит расходов, предел за ход, неверный заказ. Задачи нет, денег не потрачено. */\nexport interface ImageOrderRefusal {\n status: \"refused\";\n reason: string;\n}\n\nexport interface ImageOrder {\n ownerId: string;\n chatId: string;\n /** Номер хода агента (`model.turnId` в `agent.tools`): по нему считается `maxImagesPerTurn`. */\n turnId?: string;\n /** Место картинки (например, путь файла): новый заказ на то же место у того же владельца вытесняет прежний. Нет — своё у каждого заказа. */\n key?: string;\n /** Группа для списка: например, приложение, в которое идут картинки. */\n group?: string;\n /** Назначение из `purposes`; по умолчанию первое. */\n purpose?: string;\n prompt: string;\n /** По умолчанию `1:1`. */\n aspectRatio?: AspectRatio;\n /** Данные приложения (путь, размер): приёмник получит их в `job.data`. Только JSON. */\n data?: unknown;\n}\n\n/** Назначение очереди: как у `generate_images`, но описание не обязательно (его читает только агент). */\nexport type ImageQueuePurpose = Omit<ImagePurpose, \"description\"> & { description?: string };\n\nexport interface CreateImageQueueOptions {\n /** Окружение сервера: ключ провайдера берётся только отсюда, по `apiKeyEnv`. */\n env: Record<string, string | undefined>;\n catalog?: ImageCatalog;\n /** Модель картинок из каталога; по умолчанию `gpt-image-2`. С `purposes` не задаётся. */\n model?: string;\n provider?: string;\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024; по умолчанию — размер модели. */\n size?: ImageSize;\n transparentBackground?: boolean;\n /** Постобработка перед приёмником: формат файла и предел большей стороны (sharp). */\n output?: ImageOutputOptions;\n purposes?: Record<string, ImageQueuePurpose>;\n /**\n * Приёмник готовой картинки: записать файл, положить во вложения, обновить экран. Ответ (JSON) сохраняется в задаче;\n * `false` — картинка больше не нужна (место занято другим), задача «отброшена». Ошибка — задача «не получилась», и\n * «Повторить» доставит ту же картинку без новой оплаты. Приёмника зовут хотя бы один раз: после сбоя сервера между\n * приёмником и отметкой «готово» — ещё раз, поэтому повтор должен быть безопасен. Пока приёмник работает, задачу могут\n * вытеснить: запись делать условной, по `context.isCurrent()`.\n */\n deliver(image: GeneratedImage, job: ImageJob, context: ImageDeliveryContext): unknown;\n /** Где лежат задачи; по умолчанию в памяти (до перезапуска). `sqliteImageJobStore` или `postgresImageJobStore` — переживают перезапуск. */\n store?: ImageJobStore;\n /** Расход в лимиты: запись на каждую оплаченную задачу провайдера и проверка лимита до заказа. */\n spending?: {\n /** `hasUsageRecord` — по желанию: после сбоя вставки отличает «уже записано» от «не записалось». */\n storage: Pick<ChatStorage, \"insertUsageRecord\" | \"hasUsageRecord\">;\n limits?: Pick<SpendingLimits, \"assertWithinLimit\" | \"afterSpending\">;\n };\n /** Шаг проверки задачи у провайдера с очередью; по умолчанию 3000 мс. */\n pollIntervalMs?: number;\n /** Срок одной попытки; по умолчанию 150 000 мс. */\n attemptTimeoutMs?: number;\n /**\n * Сколько попыток; по умолчанию 1. Следующая попытка после срока проверяет ту же задачу (новой оплаты нет), после\n * провала у провайдера — заказывает заново.\n */\n attempts?: number;\n /** Попытки кончились, а задача у провайдера ещё идёт: сколько её ждать; по умолчанию 10 минут, 0 — не ждать. */\n lateResultWindowMs?: number;\n /** Шаг проверки в позднем окне; по умолчанию 10 000 мс (или шаг опроса, если он больше). */\n latePollIntervalMs?: number;\n /** Срок одного обращения к провайдеру с очередью (отправка, проверка); по умолчанию 30 000 мс. */\n callTimeoutMs?: number;\n /** Сколько раз скачивать готовую картинку с того же адреса, прежде чем сдаться; по умолчанию 3. */\n downloadAttempts?: number;\n /**\n * Провайдер без очереди (Runware) не сообщает «принял»: заказ ждёт его ответа не дольше этого, по умолчанию 5000 мс.\n * Отказ (неверный ключ, нет денег) приходит раньше — и заказ сразу отвечает «не получилась».\n */\n acceptWaitMs?: number;\n /** Аренда задачи копией сервера; по умолчанию 30 000 мс. Упавшую копию заменяет другая после этого срока. */\n leaseMs?: number;\n /** Не больше стольких заказов за ход (`turnId`); по умолчанию без предела. */\n maxImagesPerTurn?: number;\n /** Законченные задачи удаляются из хранилища через столько; по умолчанию сутки. */\n forgetFinishedAfterMs?: number;\n /** Предел одного файла; по умолчанию 10 МБ. */\n maxImageBytes?: number;\n fetch?: typeof globalThis.fetch;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\nexport interface ImageQueue {\n /** Заказ. Ответ — как только задача ушла провайдеру (или сразу не получилась); картинка придёт в `deliver` позже. */\n order(input: ImageOrder): Promise<ImageJob | ImageOrderRefusal>;\n /**\n * «Повторить» последнюю задачу места. Не получилась — новый запуск: идущая у провайдера задача проверяется, а не\n * заказывается заново; оплаченная, но не доставленная картинка скачивается с того же адреса. Задача идёт или уже\n * готова — она же, без нового заказа. Нет задачи — `null`.\n */\n retry(input: { ownerId: string; key: string }): Promise<ImageJob | ImageOrderRefusal | null>;\n get(id: string): Promise<ImageJob | null>;\n /** По порядку создания. */\n list(filter: ImageJobFilter): Promise<ImageJob[]>;\n /** Заказанное и ещё не оплаченное этой копией сервера: у человека и в чате. Для проверки лимитов. */\n pendingUsd(ownerId: string, chatId: string): { user: number; chat: number };\n /** Подобрать незаконченные задачи из хранилища и обходить его дальше. Зовётся само при создании; повтор безвреден. */\n start(): Promise<void>;\n /** Остановить опрос и таймеры, отпустить аренду. Задачи остаются в хранилище и продолжаются следующим `start`. */\n stop(): Promise<void>;\n}\n\n/** Запуск задачи этой копией сервера: опрос, деньги, доставка. */\ninterface Runtime {\n ownerId: string;\n chatId: string;\n setup: ImageSetup;\n /** Бронь до записи задачи: цена того, что предстоит заказать. */\n reservedUsd: number;\n record?: ImageJobRecord;\n controller: AbortController;\n /** Записи задачи идут по одной. */\n chain: Promise<unknown>;\n /** Задача ушла провайдеру (или запуск кончился): заказ можно отдать приложению. */\n sent: { promise: Promise<void>; resolve(): void };\n done?: Promise<void>;\n leaseTimer?: ReturnType<typeof setTimeout>;\n ended: boolean;\n /** Первая проверка — сразу: задача уже у провайдера (перезапуск, новая попытка, «Повторить»). */\n checkNow: boolean;\n /** «Повторить» после сбоя доставки: тот же адрес снова не скачался — заказать последний шаг заново. */\n reorderOnDeliveryFailure: boolean;\n}\n\ntype StepOutcome = { kind: \"ready\" } | { kind: \"failed\"; reason: string } | { kind: \"refused\"; reason: string } | { kind: \"timeout\" };\n\n/** Запуск закончен: вытеснен, «Повторить», чужая аренда или `stop()`. Дальше он ничего не заказывает и не пишет. */\nclass RunEnded extends Error {\n constructor() {\n super(\"Запуск задачи картинки закончен\");\n }\n}\nconst RUN_ENDED = new RunEnded();\n\n/** Обращение к провайдеру не уложилось в свой срок. */\nclass CallTimedOut extends Error {\n constructor(readonly ms: number) {\n super(`Провайдер не ответил за ${Math.round(ms / 1000)} с`);\n }\n}\n\nconst iso = (ms: number) => new Date(ms).toISOString();\nconst seconds = (ms: number) => Math.round(ms / 1000);\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\nconst unref = (timer: ReturnType<typeof setTimeout>) => (timer as { unref?: () => void }).unref?.();\nconst hasUnrecorded = (steps: readonly ImageJobStep[]) => steps.some((step) => step.charges.some((charge) => !charge.recorded));\nconst isOpen = isOpenImageJob;\nconst withStep = (steps: readonly ImageJobStep[], index: number, change: (step: ImageJobStep) => ImageJobStep) => steps.map((step, at) => (at === index ? change(step) : step));\n\nfunction withoutTask(step: ImageJobStep): ImageJobStep {\n const { task: _task, ...rest } = step;\n return rest;\n}\n\nconst chargeOf = (task: ImageJobTask, costUsd: number): ImageJobCharge => ({ usageId: task.usageId, modelId: task.modelId, providerId: task.providerId, costUsd, recorded: false });\n\n/** Цена ответа провайдера; не сообщил — цена модели. */\nfunction costOf(results: readonly ImageGenerationResult[], fallbackUsd: number): number {\n const known = results.filter((result) => result.costUsd !== undefined);\n return known.length > 0 ? known.reduce((total, result) => total + result.costUsd!, 0) : fallbackUsd;\n}\n\n/** Отметка «расход записан»; задача без работы — заодно без аренды. Отметка уже есть — `null`. */\nfunction markRecorded(record: ImageJobRecord, usageId: string): ImageJobPatch | null {\n let found = false;\n const steps = record.steps.map((step) => ({\n ...step,\n charges: step.charges.map((charge) => {\n if (charge.usageId !== usageId || charge.recorded) return charge;\n found = true;\n return { ...charge, recorded: true };\n }),\n }));\n if (!found) return null;\n if (isOpen(record)) return { steps };\n const left = hasUnrecorded(steps);\n return { steps, active: left, ...(!left && { leaseOwner: undefined, leaseUntil: undefined }) };\n}\n\nfunction view(record: ImageJobRecord): ImageJob {\n return {\n id: record.id,\n key: record.key,\n ownerId: record.ownerId,\n chatId: record.chatId,\n ...(record.turnId !== undefined && { turnId: record.turnId }),\n ...(record.group !== undefined && { group: record.group }),\n ...(record.purpose !== \"\" && { purpose: record.purpose }),\n prompt: record.prompt,\n aspectRatio: record.aspectRatio,\n ...(record.data !== undefined && { data: record.data }),\n status: record.status,\n ...(record.reason !== undefined && { reason: record.reason }),\n ...(record.result !== undefined && { result: record.result }),\n attempt: record.attempt,\n ...(record.lateUntil !== undefined && { lateUntil: record.lateUntil }),\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n ...(record.finishedAt !== undefined && { finishedAt: record.finishedAt }),\n };\n}\n\nconst refused = (reason: string): ImageOrderRefusal => ({ status: \"refused\", reason });\n\nfunction checkOptions(options: CreateImageQueueOptions) {\n const positive = [\"pollIntervalMs\", \"attemptTimeoutMs\", \"latePollIntervalMs\", \"callTimeoutMs\", \"acceptWaitMs\", \"leaseMs\", \"forgetFinishedAfterMs\", \"maxImageBytes\"] as const;\n for (const name of positive) {\n const value = options[name];\n if (value !== undefined && !(Number.isFinite(value) && value > 0)) throw new Error(`Картинки: ${name} — нужно число больше нуля`);\n }\n for (const name of [\"attempts\", \"downloadAttempts\", \"maxImagesPerTurn\"] as const) {\n const value = options[name];\n if (value !== undefined && !(Number.isInteger(value) && value >= 1)) throw new Error(`Картинки: ${name} — целое число от 1`);\n }\n const window = options.lateResultWindowMs;\n if (window !== undefined && !(Number.isFinite(window) && window >= 0)) throw new Error(\"Картинки: lateResultWindowMs — число не меньше нуля\");\n if (typeof options.deliver !== \"function\") throw new Error(\"Картинки: deliver — нужна функция-приёмник\");\n}\n\n/**\n * Очередь картинок для своих инструментов приложения: заказ отвечает сразу, как задача ушла провайдеру, картинка\n * приходит в `deliver` позже. Задачи лежат в хранилище и переживают перезапуск: старт подбирает незаконченные,\n * доопрашивает провайдера и доставляет. Каждая задача провайдера оплачивается ровно один раз (постоянный id записи\n * расхода), готовая картинка не заказывается заново — только скачивается.\n */\nexport function createImageQueue(options: CreateImageQueueOptions): ImageQueue {\n checkOptions(options);\n const { purposes: queuePurposes, ...choice } = options;\n const purposes: Record<string, ImagePurpose> | undefined =\n queuePurposes && Object.fromEntries(Object.entries(queuePurposes).map(([name, purpose]) => [name, { ...purpose, description: purpose.description?.trim() || name }]));\n const setups = resolveImageSetups({ ...choice, ...(purposes && { purposes }) });\n const firstPurpose = setups.hasPurposes ? [...setups.byPurpose.keys()][0]! : \"\";\n const store = options.store ?? memoryImageJobStore();\n const now = options.now ?? (() => new Date());\n const nowMs = () => now().getTime();\n const hide = setups.hideSecrets;\n const logError = (message: string) => options.logError?.(hide(message));\n\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const attemptTimeoutMs = options.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const attempts = options.attempts ?? 1;\n const lateResultWindowMs = options.lateResultWindowMs ?? DEFAULT_LATE_RESULT_WINDOW_MS;\n const latePollIntervalMs = options.latePollIntervalMs ?? Math.max(pollIntervalMs, MIN_LATE_POLL_INTERVAL_MS);\n const callTimeoutMs = options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const downloadAttempts = options.downloadAttempts ?? DEFAULT_DOWNLOAD_ATTEMPTS;\n const leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;\n const acceptWaitMs = options.acceptWaitMs ?? DEFAULT_ACCEPT_WAIT_MS;\n const forgetFinishedAfterMs = options.forgetFinishedAfterMs ?? DEFAULT_FORGET_FINISHED_AFTER_MS;\n const maxImageBytes = options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES;\n const timeoutReason = `Картинка не успела за ${seconds(attemptTimeoutMs)} с`;\n\n /** Эта копия сервера: так помечена её аренда. */\n const me = crypto.randomUUID();\n /** Брони и запуски этой копии по порядку появления: проверка лимита видит тех, кто раньше. */\n const runtimes = new Set<Runtime>();\n const byJob = new Map<string, Runtime>();\n const keyLocks = new Map<string, Promise<unknown>>();\n const retries = new Map<string, Promise<ImageJob | ImageOrderRefusal | null>>();\n const turns = createTurnCounter(options.maxImagesPerTurn);\n let started = false;\n let stopped = false;\n let scanning: Promise<void> | undefined;\n let scanTimer: ReturnType<typeof setTimeout> | undefined;\n let lastPruneAt = Number.NEGATIVE_INFINITY;\n\n function sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) return reject(RUN_ENDED);\n const onAbort = () => {\n clearTimeout(timer);\n reject(RUN_ENDED);\n };\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n unref(timer);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n\n const throwIfEnded = (rt: Runtime) => {\n if (rt.controller.signal.aborted) throw RUN_ENDED;\n };\n\n /** Обращение к провайдеру со своим сроком; запуск закончился — обрывается и оно. */\n async function callProvider<T>(rt: Runtime, ms: number, call: (signal: AbortSignal) => Promise<T>): Promise<T> {\n const timeout = new AbortController();\n const timer = setTimeout(() => timeout.abort(new DOMException(\"timeout\", \"TimeoutError\")), ms);\n unref(timer);\n try {\n return await call(AbortSignal.any([rt.controller.signal, timeout.signal]));\n } catch (error) {\n if (timeout.signal.aborted && !rt.controller.signal.aborted) throw new CallTimedOut(ms);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n // --- запись ---\n\n /**\n * Условная запись с перечитыванием: правка считается от свежей записи. Запись сменилась так, что она больше не «наша»\n * (`stillOurs`), — `null`. Правке нечего делать — запись как есть.\n */\n async function updateJob(start: ImageJobRecord, patchOf: (record: ImageJobRecord) => ImageJobPatch | null, stillOurs: (record: ImageJobRecord) => boolean): Promise<ImageJobRecord | null> {\n let current = start;\n for (let tries = 0; tries < MAX_WRITE_TRIES; tries++) {\n const patch = patchOf(current);\n if (!patch) return current;\n const next = await store.update(current.id, { ...patch, updatedAt: iso(nowMs()) }, current.version);\n if (next) return next;\n const fresh = await store.get(current.id);\n if (!fresh || !stillOurs(fresh)) return null;\n current = fresh;\n }\n throw new Error(`Картинки: задачу ${start.id} не удалось записать: её всё время правят`);\n }\n\n /** Запись запуска: по одной, только пока запуск свой (тот же номер и наша аренда). Чужой — запуск кончается. */\n function write(rt: Runtime, patchOf: (record: ImageJobRecord) => ImageJobPatch | null): Promise<ImageJobRecord> {\n const run = async () => {\n throwIfEnded(rt);\n const record = rt.record!;\n const next = await updateJob(record, patchOf, (fresh) => fresh.run === record.run && fresh.leaseOwner === me);\n if (!next) {\n rt.controller.abort(RUN_ENDED);\n throw RUN_ENDED;\n }\n rt.record = next;\n return next;\n };\n const result = rt.chain.then(run, run);\n rt.chain = result.catch(() => {});\n return result;\n }\n\n // --- деньги ---\n\n /** Ошибка базы «такой id уже есть» (PostgreSQL, SQLite), в том числе завёрнутая в `cause`. */\n function isDuplicateKey(error: unknown): boolean {\n for (let current = error, depth = 0; current && typeof current === \"object\" && depth < 5; current = (current as { cause?: unknown }).cause, depth++) {\n const code = (current as { code?: unknown }).code;\n if (code === \"23505\" || code === \"SQLITE_CONSTRAINT_PRIMARYKEY\" || code === \"SQLITE_CONSTRAINT_UNIQUE\") return true;\n }\n return false;\n }\n\n /**\n * Запись расхода с постоянным id: вторая вставка того же id отвергается базой, расход не задваивается. `true` — расход\n * в базе (вставлен сейчас или был раньше); `false` — не записан (временный сбой), его допишет следующий обход.\n */\n async function insertUsage(record: ImageJobRecord, charge: ImageJobCharge): Promise<boolean> {\n const spending = options.spending;\n if (!spending) return true;\n try {\n await spending.storage.insertUsageRecord({\n id: charge.usageId,\n ownerId: record.ownerId,\n chatId: record.chatId,\n messageId: null,\n source: \"images\",\n modelId: charge.modelId,\n providerId: charge.providerId,\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n costUsd: charge.costUsd,\n createdAt: iso(nowMs()),\n });\n } catch (error) {\n const known = await (spending.storage.hasUsageRecord?.(charge.usageId) ?? Promise.resolve(isDuplicateKey(error))).catch(() => false);\n if (known) return true;\n logError(`Картинки: расход ${charge.modelId} (${charge.usageId}) пока не записан, запись повторится: ${errorText(error)}`);\n return false;\n }\n const afterSpending = spending.limits?.afterSpending;\n // Пороги — фоном: задачу они не задерживают.\n if (afterSpending) {\n void afterSpending\n .call(spending.limits, { userId: record.ownerId, chatId: record.chatId })\n .catch((error: unknown) => logError(`Картинки: лимиты после расхода (чат ${record.chatId}): ${errorText(error)}`));\n }\n return true;\n }\n\n const unrecorded = (record: ImageJobRecord) => record.steps.flatMap((step) => step.charges.filter((charge) => !charge.recorded));\n\n async function recordCharges(rt: Runtime) {\n for (const charge of unrecorded(rt.record!)) {\n if (await insertUsage(rt.record!, charge)) await write(rt, (record) => markRecorded(record, charge.usageId));\n }\n }\n\n /** Расходы задачи без запуска (вытеснена, подобрана после сбоя); в конце — задача без работы и аренды. */\n async function settleClosed(record: ImageJobRecord) {\n let current: ImageJobRecord | null = record;\n for (const charge of unrecorded(record)) {\n if (!(await insertUsage(record, charge))) continue;\n current = await updateJob(current!, (fresh) => markRecorded(fresh, charge.usageId), () => true);\n if (!current) return;\n }\n await updateJob(current!, (fresh) => (isOpen(fresh) || (!fresh.active && fresh.leaseOwner === undefined) ? null : { active: hasUnrecorded(fresh.steps), leaseOwner: undefined, leaseUntil: undefined }), () => true);\n }\n\n /** Оплата ушедшей задачи шага: ценой провайдера, а без неё — ценой модели. */\n async function chargeTask(rt: Runtime, index: number, costUsd?: number) {\n await write(rt, (record) => {\n const task = record.steps[index]!.task;\n if (!task) return null;\n return { steps: withStep(record.steps, index, (step) => ({ ...withoutTask(step), charges: [...step.charges, chargeOf(task, costUsd ?? task.priceUsd)] })) };\n });\n await recordCharges(rt);\n }\n\n /** Шаг готов: адреса и оплата — одной записью, дальше шаг только скачивается. */\n async function acceptResults(rt: Runtime, index: number, results: readonly ImageGenerationResult[]) {\n const urls = results.map((result) => result.url);\n await write(rt, (record) => {\n const task = record.steps[index]!.task;\n if (!task) return null;\n return { steps: withStep(record.steps, index, (step) => ({ ...withoutTask(step), urls, charges: [...step.charges, chargeOf(task, costOf(results, task.priceUsd))] })) };\n });\n await recordCharges(rt);\n }\n\n /**\n * Закрыть задачу без её запуска: вытеснена новым заказом или настройка сменилась. Ушедшие задачи провайдера —\n * по цене модели; номер запуска растёт, и прежний запуск больше ничего не делает.\n */\n async function closeJob(record: ImageJobRecord, status: \"superseded\" | \"failed\", reason?: string) {\n const closed = await updateJob(\n record,\n (fresh) => {\n if (!isOpen(fresh)) return null;\n const steps = fresh.steps.map((step) => (step.task ? { ...withoutTask(step), charges: [...step.charges, chargeOf(step.task, step.task.priceUsd)] } : step));\n return { status, reason, run: fresh.run + 1, steps, lateUntil: undefined, active: hasUnrecorded(steps), finishedAt: iso(nowMs()), leaseOwner: undefined, leaseUntil: undefined };\n },\n () => true,\n );\n if (!closed) return;\n byJob.get(record.id)?.controller.abort(RUN_ENDED);\n await settleClosed(closed);\n }\n\n function openUsd(rt: Runtime): number {\n const record = rt.record;\n if (!record) return rt.reservedUsd;\n if (!isOpen(record)) return 0;\n const late = record.lateUntil !== undefined;\n const last = record.steps.length - 1;\n return record.steps.reduce((total, step, index) => {\n // «Повторить» после сбоя доставки: адрес снова не скачается — последний шаг закажется заново.\n if (step.urls) return rt.reorderOnDeliveryFailure && index === last ? total + rt.setup.steps[index]!.priceUsd : total;\n if (step.task) return total + step.task.priceUsd;\n return late ? total : total + (rt.setup.steps[index]?.priceUsd ?? 0);\n }, 0);\n }\n\n /** Заказанное и не оплаченное; `before` — только брони и запуски, появившиеся раньше него. */\n function pendingUsd(ownerId: string, chatId: string, before?: Runtime): { user: number; chat: number } {\n let user = 0;\n let chat = 0;\n for (const rt of runtimes) {\n if (rt === before) break;\n if (rt.ownerId !== ownerId) continue;\n const open = openUsd(rt);\n user += open;\n if (rt.chatId === chatId) chat += open;\n }\n return { user: roundUsd(user), chat: roundUsd(chat) };\n }\n\n async function withinLimit(rt: Runtime): Promise<string | undefined> {\n const limits = options.spending?.limits;\n if (!limits) return undefined;\n const pending = pendingUsd(rt.ownerId, rt.chatId, rt);\n try {\n await limits.assertWithinLimit({ userId: rt.ownerId, chatId: rt.chatId, ...(pending.user > 0 && { pendingUsd: pending }) });\n return undefined;\n } catch (error) {\n if (error instanceof SpendingLimitReachedError) return LIMIT_REACHED;\n logError(`Картинки: не удалось проверить лимит расходов: ${errorText(error)}`);\n return \"не удалось проверить лимит расходов\";\n }\n }\n\n // --- провайдер ---\n\n function requestOf(record: ImageJobRecord, step: ImageStep, sourceUrl: string | undefined, timeoutMs: number, signal: AbortSignal): ImageGenerationRequest {\n const { width, height } = step.sizes[record.aspectRatio];\n return {\n taskUUID: crypto.randomUUID(),\n providerModelId: step.providerModelId,\n prompt: step.prompt ?? record.prompt,\n width,\n height,\n aspectRatio: step.profile.aspectRatioValues?.[record.aspectRatio] ?? record.aspectRatio,\n ...(sourceUrl !== undefined && { imageUrl: sourceUrl }),\n ...(step.outputFormat && { outputFormat: step.outputFormat }),\n ...(step.providerSettings && { providerSettings: step.providerSettings }),\n abortSignal: signal,\n timeoutMs,\n };\n }\n\n /** Отправка не удалась: отказ до работы — без оплаты; иначе ушла ли задача, неизвестно — по цене модели. */\n async function failedSend(rt: Runtime, index: number, error: unknown, wholeAttempt: boolean): Promise<StepOutcome> {\n throwIfEnded(rt);\n if (error instanceof ImageGenerationError && error.refusedBeforeWork) {\n await write(rt, (record) => ({ steps: withStep(record.steps, index, withoutTask) }));\n return { kind: \"refused\", reason: hide(error.reason) };\n }\n let reason: string;\n if (error instanceof ImageGenerationError) reason = hide(error.reason);\n else if (error instanceof CallTimedOut) reason = wholeAttempt ? timeoutReason : error.message;\n else {\n logError(`Картинки: сбой провайдера ${rt.setup.steps[index]!.provider.id}: ${errorText(error)}`);\n reason = \"Провайдер не смог создать картинку\";\n }\n await chargeTask(rt, index);\n return { kind: \"failed\", reason };\n }\n\n async function send(rt: Runtime, index: number): Promise<StepOutcome> {\n const step = rt.setup.steps[index]!;\n const task: ImageJobTask = { usageId: crypto.randomUUID(), modelId: step.profile.id, providerId: step.provider.id, priceUsd: step.priceUsd, sentAt: iso(nowMs()) };\n // Сначала отметка «уходит»: упал сервер посреди отправки — задача всё равно будет оплачена.\n await write(rt, (record) => ({ steps: withStep(record.steps, index, (current) => ({ ...current, task })) }));\n const record = rt.record!;\n const sourceUrl = index > 0 ? record.steps[index - 1]!.urls?.[0] : undefined;\n const queue = step.provider.queue;\n if (queue) {\n let taskId: string;\n try {\n taskId = await callProvider(rt, callTimeoutMs, (signal) => queue.submit(requestOf(record, step, sourceUrl, callTimeoutMs, signal), step.connection));\n } catch (error) {\n return failedSend(rt, index, error, false);\n }\n await write(rt, (fresh) => ({ steps: withStep(fresh.steps, index, (current) => ({ ...current, task: { ...current.task!, taskId } })) }));\n return poll(rt, index);\n }\n // Без очереди «принял» не бывает: заказ отвечает, когда провайдер ответил (отказ виден сразу) или через acceptWaitMs.\n const accepted = setTimeout(() => rt.sent.resolve(), acceptWaitMs);\n unref(accepted);\n const timeoutMs = Math.max(1, Date.parse(record.attemptDeadline) - nowMs());\n let results: ImageGenerationResult[];\n try {\n results = await callProvider(rt, timeoutMs, (signal) => step.provider.generate(requestOf(record, step, sourceUrl, timeoutMs, signal), step.connection));\n } catch (error) {\n return failedSend(rt, index, error, true);\n } finally {\n clearTimeout(accepted);\n }\n throwIfEnded(rt);\n await acceptResults(rt, index, results);\n return { kind: \"ready\" };\n }\n\n /** Опрос задачи в очереди провайдера до готовности, провала или срока (попытки или позднего окна). */\n async function poll(rt: Runtime, index: number): Promise<StepOutcome> {\n rt.sent.resolve();\n const step = rt.setup.steps[index]!;\n const queue = step.provider.queue!;\n const taskId = rt.record!.steps[index]!.task!.taskId!;\n const checkNow = rt.checkNow;\n rt.checkNow = false;\n for (let first = true; ; first = false) {\n if (!(first && checkNow)) {\n const record = rt.record!;\n const deadline = Date.parse(record.lateUntil ?? record.attemptDeadline);\n const wait = Math.min(record.lateUntil !== undefined ? latePollIntervalMs : pollIntervalMs, deadline - nowMs());\n if (wait > 0) await sleep(wait, rt.controller.signal);\n if (nowMs() >= deadline) return { kind: \"timeout\" };\n }\n let state: ImageTaskState;\n try {\n state = await callProvider(rt, callTimeoutMs, (signal) => queue.check(taskId, step.connection, signal));\n } catch (error) {\n throwIfEnded(rt);\n if (error instanceof ImageGenerationError) {\n await chargeTask(rt, index);\n return { kind: \"failed\", reason: hide(error.reason) };\n }\n // Временный сбой или срок обращения: проверка повторится.\n continue;\n }\n throwIfEnded(rt);\n if (state.status === \"ready\") {\n await acceptResults(rt, index, state.results);\n return { kind: \"ready\" };\n }\n if (state.status === \"failed\") {\n await chargeTask(rt, index, state.costUsd);\n return { kind: \"failed\", reason: hide(state.reason) };\n }\n }\n }\n\n // --- запуск ---\n\n async function finishFailed(rt: Runtime, reason: string, extra: ImageJobPatch = {}) {\n await write(rt, (record) => ({\n status: \"failed\",\n reason,\n lateUntil: undefined,\n ...extra,\n active: hasUnrecorded(record.steps),\n finishedAt: iso(nowMs()),\n leaseOwner: undefined,\n leaseUntil: undefined,\n }));\n }\n\n const nextAttempt = (rt: Runtime) => write(rt, (record) => ({ attempt: record.attempt + 1, attemptDeadline: iso(nowMs() + attemptTimeoutMs) }));\n\n /** Шаг до готовых адресов; `false` — задача закончилась без них (запись уже сделана). */\n async function runStep(rt: Runtime, index: number): Promise<boolean> {\n for (;;) {\n throwIfEnded(rt);\n const record = rt.record!;\n const step = record.steps[index]!;\n if (step.urls) return true;\n const late = record.lateUntil !== undefined;\n let outcome: StepOutcome;\n if (step.task?.taskId !== undefined) outcome = await poll(rt, index);\n else if (step.task) {\n // Сервер упал посреди отправки: ушла ли задача, неизвестно — по цене модели, попытка не удалась.\n await chargeTask(rt, index);\n outcome = { kind: \"failed\", reason: \"Провайдер не ответил: сервер перезапускался\" };\n } else if (late) outcome = { kind: \"failed\", reason: record.reason ?? timeoutReason };\n else if (nowMs() >= Date.parse(record.attemptDeadline)) outcome = { kind: \"failed\", reason: timeoutReason };\n else outcome = await send(rt, index);\n\n if (outcome.kind === \"ready\") {\n if (late) await write(rt, () => ({ status: \"generating\", reason: undefined, lateUntil: undefined, attemptDeadline: iso(nowMs() + attemptTimeoutMs) }));\n return true;\n }\n if (outcome.kind === \"refused\") {\n await finishFailed(rt, outcome.reason);\n return false;\n }\n if (outcome.kind === \"failed\") {\n if (!late && rt.record!.attempt < attempts) {\n await nextAttempt(rt);\n continue;\n }\n await finishFailed(rt, outcome.reason);\n return false;\n }\n // Срок вышел, задача у провайдера ещё идёт.\n if (late) {\n await chargeTask(rt, index);\n await finishFailed(rt, rt.record!.reason ?? timeoutReason);\n return false;\n }\n if (rt.record!.attempt < attempts) {\n await nextAttempt(rt);\n rt.checkNow = true;\n continue;\n }\n if (lateResultWindowMs > 0) {\n await write(rt, () => ({ status: \"failed\", reason: timeoutReason, lateUntil: iso(nowMs() + lateResultWindowMs) }));\n continue;\n }\n await chargeTask(rt, index);\n await finishFailed(rt, timeoutReason);\n return false;\n }\n }\n\n /** Скачать с того же адреса (несколько раз) и отдать приёмнику. `reorder` — после «Повторить» адрес так и не скачался. */\n async function deliverResult(rt: Runtime): Promise<\"done\" | \"reorder\"> {\n rt.sent.resolve();\n const record = rt.record!;\n const last = rt.setup.steps.at(-1)!;\n // У модели с основой первым идёт фон без предмета, картинка — следующая.\n const url = record.steps.at(-1)!.urls?.[last.profile.firstResultIsBase ? 1 : 0];\n if (!url) {\n await finishFailed(rt, \"Провайдер не вернул картинку\");\n return \"done\";\n }\n const output = rt.setup.output;\n let file: DownloadedImage;\n for (let attempt = 1; ; attempt++) {\n try {\n file = await downloadGeneratedImage({\n url,\n fetch: last.connection.fetch,\n maxImageBytes,\n ...(last.trustedOrigin !== undefined && { trustedOrigin: last.trustedOrigin }),\n abortSignal: rt.controller.signal,\n ...(output && { transform: (bytes: Uint8Array, mediaType: string) => postprocessImage(bytes, mediaType, output) }),\n });\n break;\n } catch (error) {\n throwIfEnded(rt);\n if (attempt >= downloadAttempts) {\n if (rt.reorderOnDeliveryFailure) return \"reorder\";\n await finishFailed(rt, error instanceof ImageGenerationError ? hide(error.reason) : \"Картинка не скачалась\", { deliveryFailed: true });\n return \"done\";\n }\n await sleep(pollIntervalMs, rt.controller.signal);\n }\n }\n throwIfEnded(rt);\n const first = rt.setup.steps[0]!.profile;\n const planned = rt.setup.steps[0]!.sizes[record.aspectRatio];\n const image: GeneratedImage = { bytes: file.bytes, mediaType: file.mediaType, width: file.width ?? planned.width, height: file.height ?? planned.height, modelId: first.id };\n const run = record.run;\n const context: ImageDeliveryContext = {\n signal: rt.controller.signal,\n async isCurrent() {\n if (rt.controller.signal.aborted) return false;\n const fresh = await store.get(record.id);\n return fresh !== null && fresh.run === run && fresh.leaseOwner === me && isOpen(fresh) && !rt.controller.signal.aborted;\n },\n };\n let result: unknown;\n try {\n result = await options.deliver(image, view(record), context);\n } catch (error) {\n throwIfEnded(rt);\n logError(`Картинки: приёмник не принял картинку задачи ${record.id}: ${errorText(error)}`);\n await finishFailed(rt, \"Приёмник не принял картинку\", { deliveryFailed: true });\n return \"done\";\n }\n throwIfEnded(rt);\n await write(rt, (fresh) => ({\n status: result === false ? \"discarded\" : \"ready\",\n reason: undefined,\n lateUntil: undefined,\n deliveryFailed: undefined,\n ...(result !== false && result !== undefined && { result }),\n active: hasUnrecorded(fresh.steps),\n finishedAt: iso(nowMs()),\n leaseOwner: undefined,\n leaseUntil: undefined,\n }));\n return \"done\";\n }\n\n async function generate(rt: Runtime) {\n for (;;) {\n for (let index = 0; index < rt.setup.steps.length; index++) {\n if (!(await runStep(rt, index))) return;\n }\n if ((await deliverResult(rt)) === \"done\") return;\n // Тот же адрес не скачался и после «Повторить»: последний шаг заказывается заново, прежние — нет.\n rt.reorderOnDeliveryFailure = false;\n const last = rt.setup.steps.length - 1;\n await write(rt, (record) => ({\n deliveryFailed: undefined,\n attemptDeadline: iso(nowMs() + attemptTimeoutMs),\n steps: withStep(record.steps, last, ({ urls: _urls, ...step }) => step),\n }));\n }\n }\n\n async function drive(rt: Runtime) {\n try {\n await recordCharges(rt);\n if (isOpen(rt.record!)) await generate(rt);\n } catch (error) {\n if (error !== RUN_ENDED && !rt.controller.signal.aborted) logError(`Картинки: задача ${rt.record?.id ?? \"\"} остановилась: ${errorText(error)}`);\n } finally {\n rt.ended = true;\n clearTimeout(rt.leaseTimer);\n runtimes.delete(rt);\n if (rt.record && byJob.get(rt.record.id) === rt) byJob.delete(rt.record.id);\n rt.sent.resolve();\n }\n }\n\n function keepLease(rt: Runtime) {\n const renew = () => {\n rt.leaseTimer = setTimeout(async () => {\n if (rt.ended || rt.controller.signal.aborted) return;\n try {\n await write(rt, (record) => (record.leaseOwner === me ? { leaseUntil: iso(nowMs() + leaseMs) } : null));\n } catch (error) {\n if (error !== RUN_ENDED) logError(`Картинки: аренда задачи ${rt.record?.id ?? \"\"} не продлилась: ${errorText(error)}`);\n }\n if (!rt.ended && !rt.controller.signal.aborted) renew();\n }, Math.max(1, Math.floor(leaseMs / 3)));\n unref(rt.leaseTimer);\n };\n renew();\n }\n\n function newRuntime(ownerId: string, chatId: string, setup: ImageSetup, reservedUsd: number): Runtime {\n let resolve!: () => void;\n const promise = new Promise<void>((done) => (resolve = done));\n return { ownerId, chatId, setup, reservedUsd, controller: new AbortController(), chain: Promise.resolve(), sent: { promise, resolve }, ended: false, checkNow: false, reorderOnDeliveryFailure: false };\n }\n\n function launch(rt: Runtime, record: ImageJobRecord) {\n rt.record = record;\n byJob.set(record.id, rt);\n keepLease(rt);\n rt.done = drive(rt);\n }\n\n /** Настройка задачи из её назначения; назначение пропало или шаги другие — `undefined`. */\n function setupOf(record: ImageJobRecord): ImageSetup | undefined {\n const setup = setups.byPurpose.get(record.purpose);\n if (!setup || setup.steps.length !== record.steps.length) return undefined;\n const sameProviders = record.steps.every((step, index) => !step.task || step.task.providerId === setup.steps[index]!.provider.id);\n return sameProviders ? setup : undefined;\n }\n\n /** Работа с одним местом (`ownerId` + `key`) — по одной в этой копии: заказ и «Повторить» не перебивают друг друга. */\n function underKey<T>(ownerId: string, key: string, work: () => Promise<T>): Promise<T> {\n const lockKey = JSON.stringify([ownerId, key]);\n const current = (keyLocks.get(lockKey) ?? Promise.resolve()).then(work, work);\n const tail = current.catch(() => {});\n keyLocks.set(lockKey, tail);\n void tail.then(() => {\n if (keyLocks.get(lockKey) === tail) keyLocks.delete(lockKey);\n });\n return current;\n }\n\n // --- обход хранилища ---\n\n /** Взять задачу из хранилища: аренда свободна или истекла. Закрытой — дописать расход, открытую — вести. */\n async function claim(record: ImageJobRecord) {\n if (stopped || byJob.has(record.id)) return;\n const claimed = await store.update(record.id, { leaseOwner: me, leaseUntil: iso(nowMs() + leaseMs), updatedAt: iso(nowMs()) }, record.version);\n if (!claimed) return;\n if (!isOpen(claimed)) return settleClosed(claimed);\n const setup = setupOf(claimed);\n if (!setup) return closeJob(claimed, \"failed\", `Настройка картинок сменилась: назначения «${claimed.purpose}» в ней больше нет`);\n const rt = newRuntime(claimed.ownerId, claimed.chatId, setup, 0);\n rt.checkNow = true;\n rt.reorderOnDeliveryFailure = false;\n runtimes.add(rt);\n launch(rt, claimed);\n }\n\n function scheduleScan() {\n if (stopped || !started) return;\n clearTimeout(scanTimer);\n scanTimer = setTimeout(() => void scan(), leaseMs);\n unref(scanTimer);\n }\n\n function scan(): Promise<void> {\n if (stopped) return Promise.resolve();\n if (scanning) return scanning;\n scanning = (async () => {\n try {\n if (nowMs() - lastPruneAt >= PRUNE_EVERY_MS) {\n lastPruneAt = nowMs();\n await store.deleteFinishedBefore(iso(nowMs() - forgetFinishedAfterMs));\n }\n for (const record of await store.listResumable(iso(nowMs()))) {\n if (stopped) break;\n await claim(record).catch((error: unknown) => logError(`Картинки: задачу ${record.id} не удалось подобрать: ${errorText(error)}`));\n }\n } catch (error) {\n logError(`Картинки: обход задач не удался: ${errorText(error)}`);\n } finally {\n scanning = undefined;\n scheduleScan();\n }\n })();\n return scanning;\n }\n\n // --- снаружи ---\n\n async function order(input: ImageOrder): Promise<ImageJob | ImageOrderRefusal> {\n if (stopped) throw new Error(\"Картинки: очередь остановлена (stop) — заказы не принимаются\");\n if (!input.ownerId || !input.chatId) throw new Error(\"Картинки: order — нужны ownerId и chatId\");\n const aspectRatio = input.aspectRatio ?? \"1:1\";\n if (!aspectRatios.includes(aspectRatio)) return refused(`пропорции ${aspectRatio} нет: ${aspectRatios.join(\", \")}`);\n if (typeof input.prompt !== \"string\" || !input.prompt.trim()) return refused(\"пустое описание картинки\");\n const purpose = setups.hasPurposes ? (input.purpose ?? firstPurpose) : input.purpose === undefined ? \"\" : undefined;\n const setup = purpose === undefined ? undefined : setups.byPurpose.get(purpose);\n if (purpose === undefined || !setup) return refused(`назначения «${input.purpose}» нет`);\n if (!turns.take(input.turnId)) return refused(`не больше ${options.maxImagesPerTurn} картинок за ход`);\n\n // Бронь — до первого await: параллельный заказ уже видит её в pendingUsd.\n const rt = newRuntime(input.ownerId, input.chatId, setup, setup.priceUsd);\n runtimes.add(rt);\n let accepted = false;\n try {\n const refusal = await withinLimit(rt);\n if (refusal) return refused(refusal);\n if (rt.controller.signal.aborted) return refused(QUEUE_STOPPED);\n const id = crypto.randomUUID();\n const key = input.key ?? id;\n const newRecord = (): ImageJobRecord => {\n const at = nowMs();\n return {\n id,\n key,\n ownerId: input.ownerId,\n chatId: input.chatId,\n ...(input.turnId !== undefined && { turnId: input.turnId }),\n ...(input.group !== undefined && { group: input.group }),\n purpose,\n prompt: input.prompt,\n aspectRatio,\n ...(input.data !== undefined && { data: input.data }),\n status: \"generating\",\n active: true,\n run: 1,\n attempt: 1,\n attemptDeadline: iso(at + attemptTimeoutMs),\n steps: setup.steps.map(() => ({ charges: [] })),\n leaseOwner: me,\n leaseUntil: iso(at + leaseMs),\n version: 1,\n createdAt: iso(at),\n updatedAt: iso(at),\n };\n };\n const record = await underKey(input.ownerId, key, async () => {\n // Открытая задача на место — одна, это держит база: если другая копия сервера успела занять место между\n // закрытием и вставкой, вставка не проходит — закрываем и её, пробуем снова.\n for (let tries = 1; ; tries++) {\n for (const old of await store.list({ ownerId: input.ownerId, key })) if (isOpen(old)) await closeJob(old, \"superseded\");\n if (stopped) return null;\n const created = newRecord();\n try {\n await store.insert(created);\n return created;\n } catch (error) {\n if (!(error instanceof ImageJobKeyTakenError) || tries >= MAX_PLACE_TRIES) throw error;\n }\n }\n });\n if (!record) return refused(QUEUE_STOPPED);\n // stop() пришёл, пока задача записывалась: провайдеру ничего не ушло — задача закрывается без аренды.\n if (stopped) {\n await closeJob(record, \"failed\", QUEUE_STOPPED);\n return refused(QUEUE_STOPPED);\n }\n accepted = true;\n launch(rt, record);\n await rt.sent.promise;\n return view(rt.record!);\n } finally {\n if (!accepted) {\n runtimes.delete(rt);\n turns.give(input.turnId);\n }\n }\n }\n\n async function reviveOrTaken(id: string, patch: ImageJobPatch, version: number): Promise<ImageJobRecord | null | \"taken\"> {\n try {\n return await store.update(id, patch, version);\n } catch (error) {\n if (error instanceof ImageJobKeyTakenError) return \"taken\";\n throw error;\n }\n }\n\n function retryJob(ownerId: string, key: string): Promise<ImageJob | ImageOrderRefusal | null> {\n return underKey(ownerId, key, async () => {\n const latest = (await store.list({ ownerId, key })).at(-1);\n if (!latest) return null;\n if (latest.status !== \"failed\") return view(latest);\n const setup = setupOf(latest);\n if (!setup) return refused(`назначения «${latest.purpose}» в настройке больше нет`);\n const remaining = latest.steps.reduce((total, step, index) => (step.urls ? total : total + (step.task?.priceUsd ?? setup.steps[index]!.priceUsd)), 0);\n // Сбой доставки: тот же адрес может не скачаться снова, тогда последний шаг заказывается заново — бронь на него.\n const reorderUsd = latest.deliveryFailed ? setup.steps.at(-1)!.priceUsd : 0;\n const rt = newRuntime(ownerId, latest.chatId, setup, remaining + reorderUsd);\n runtimes.add(rt);\n let accepted = false;\n try {\n const refusal = await withinLimit(rt);\n if (refusal) return refused(refusal);\n let current: ImageJobRecord | null = latest;\n let revived: ImageJobRecord | null | \"taken\" = null;\n // Новый запуск — условной записью: из двух «Повторить» (и двух копий сервера) проходит один.\n while (current?.status === \"failed\") {\n if (stopped) return refused(QUEUE_STOPPED);\n const at = nowMs();\n revived = await reviveOrTaken(\n current.id,\n {\n status: \"generating\",\n run: current.run + 1,\n attempt: 1,\n attemptDeadline: iso(at + attemptTimeoutMs),\n reason: undefined,\n lateUntil: undefined,\n deliveryFailed: undefined,\n finishedAt: undefined,\n active: true,\n leaseOwner: me,\n leaseUntil: iso(at + leaseMs),\n updatedAt: iso(at),\n },\n current.version,\n );\n // Место заняла новая задача (заказ с другой копии сервера): «Повторить» отдаёт её.\n if (revived === \"taken\") {\n const newest = (await store.list({ ownerId, key })).at(-1);\n return newest ? view(newest) : null;\n }\n if (revived) break;\n current = await store.get(latest.id);\n }\n if (!revived) return current ? view(current) : null;\n // stop() пришёл посреди «Повторить»: аренда отпускается, задачу продолжит следующий старт.\n if (stopped) {\n await updateJob(revived, (record) => (record.leaseOwner === me ? { leaseOwner: undefined, leaseUntil: undefined } : null), (record) => record.leaseOwner === me);\n return refused(QUEUE_STOPPED);\n }\n // Прежний запуск (опрос в позднем окне) кончается: дальше задачу ведёт новый.\n byJob.get(revived.id)?.controller.abort(RUN_ENDED);\n rt.checkNow = true;\n rt.reorderOnDeliveryFailure = current!.deliveryFailed === true;\n accepted = true;\n launch(rt, revived);\n await rt.sent.promise;\n return view(rt.record!);\n } finally {\n if (!accepted) runtimes.delete(rt);\n }\n });\n }\n\n async function start() {\n if (started) return scanning ?? Promise.resolve();\n started = true;\n stopped = false;\n return scan();\n }\n\n async function stop() {\n stopped = true;\n started = false;\n clearTimeout(scanTimer);\n scanTimer = undefined;\n await scanning;\n const all = [...runtimes];\n for (const rt of all) {\n clearTimeout(rt.leaseTimer);\n rt.controller.abort(RUN_ENDED);\n }\n await Promise.all(all.map((rt) => rt.done));\n // Аренда отпускается сразу: следующий старт (или другая копия) берёт задачи без ожидания срока.\n for (const rt of all) {\n if (!rt.record) continue;\n await rt.chain;\n await updateJob(rt.record, (record) => (record.leaseOwner === me ? { leaseOwner: undefined, leaseUntil: undefined } : null), (record) => record.leaseOwner === me).catch((error: unknown) =>\n logError(`Картинки: аренда задачи ${rt.record?.id ?? \"\"} не отпущена: ${errorText(error)}`),\n );\n }\n }\n\n void start();\n\n return {\n order,\n retry({ ownerId, key }) {\n if (stopped) return Promise.reject(new Error(\"Картинки: очередь остановлена (stop) — повтор не принимается\"));\n const flight = JSON.stringify([ownerId, key]);\n const running = retries.get(flight);\n if (running) return running;\n const promise = retryJob(ownerId, key).finally(() => retries.delete(flight));\n retries.set(flight, promise);\n return promise;\n },\n async get(id) {\n const record = await store.get(id);\n return record ? view(record) : null;\n },\n async list(filter) {\n return (await store.list(filter)).map(view);\n },\n pendingUsd: (ownerId, chatId) => pendingUsd(ownerId, chatId),\n start,\n stop,\n };\n}\n","import { hideSecrets } from \"./hide-secrets\";\nimport { imageQualities, imageSizes, standardImageCatalog } from \"./image-catalog\";\nimport type { ImageCatalog, ImageFileFormat, ImageModelProfile, ImageProviderConnection, ImageProviderDescription, ImageQuality, ImageSize, ImageSizesByAspectRatio } from \"./image-catalog\";\nimport { checkImageOutput } from \"./postprocess-image\";\nimport type { ImageOutputOptions } from \"./postprocess-image\";\n\nconst DEFAULT_MODEL = \"gpt-image-2\";\n/** Подсказка разделению на слои: предмет целиком одним слоем, без тени (живой запрос 5 октября 2026: 2 адреса, $0.032). */\nexport const DEFAULT_REMOVE_BACKGROUND_PROMPT = \"Separate the whole main subject, without its shadow, into a single layer.\";\n\n/** Шаг «убрать фон»: модель правки (разделение на слои) по готовой картинке. */\nexport interface ImageBackgroundRemoval {\n /** Модель с `needsSourceImage`: `seedream-5-flash-layers`. */\n model: string;\n provider?: string;\n /** Подсказка модели правки; по умолчанию — предмет целиком одним слоем. */\n prompt?: string;\n}\n\n/** Назначение картинки: агент выбирает его параметром `purpose`, приложение задаёт модель и настройки. */\nexport interface ImagePurpose {\n /** Когда выбирать это назначение — для агента, попадает в описание инструмента. */\n description: string;\n model: string;\n provider?: string;\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024; по умолчанию — размер модели. */\n size?: ImageSize;\n transparentBackground?: boolean;\n output?: ImageOutputOptions;\n /** Сначала картинка, потом её же без фона: сохраняется предмет, фон убран. */\n removeBackground?: ImageBackgroundRemoval;\n}\n\n/** Общие настройки модели: для одной модели или по умолчанию для всех назначений. */\nexport interface ImageModelChoice {\n catalog?: ImageCatalog;\n model?: string;\n provider?: string;\n quality?: ImageQuality;\n size?: ImageSize;\n transparentBackground?: boolean;\n output?: ImageOutputOptions;\n purposes?: Record<string, ImagePurpose>;\n env: Record<string, string | undefined>;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Один шаг задачи: модель, провайдер, подключение и поля запроса. */\nexport interface ImageStep {\n profile: ImageModelProfile;\n provider: ImageProviderDescription;\n providerModelId: string;\n connection: ImageProviderConnection;\n /** Стороны выбранного размера для каждой пропорции. */\n sizes: ImageSizesByAspectRatio;\n /** Цена картинки шага по профилю — с размером и качеством: по ней бронь в лимитах и запись без цены провайдера. */\n priceUsd: number;\n providerSettings?: Record<string, unknown>;\n outputFormat?: ImageFileFormat;\n /** Своя подсказка шага (разделение на слои); нет — описание картинки от агента. */\n prompt?: string;\n /** Сайт подключения из настройки (свой прокси, поддельный провайдер): с него картинку можно скачать и по http. */\n trustedOrigin?: string;\n}\n\n/** Как создаётся картинка одного назначения (или единственной модели). */\nexport interface ImageSetup {\n /** Имя назначения; без назначений — нет. */\n purpose?: string;\n description?: string;\n steps: ImageStep[];\n output?: ImageOutputOptions;\n /** Цена всех шагов: так задача бронируется в лимите. */\n priceUsd: number;\n}\n\nexport interface ImageSetups {\n /** Назначения по имени в порядке настройки; без назначений — одно, под ключом `\"\"`. */\n byPurpose: Map<string, ImageSetup>;\n /** Есть назначения: в схеме инструмента появляется `purpose`. */\n hasPurposes: boolean;\n /** Ключ и заголовки всех подключений спрятаны. */\n hideSecrets(text: string): string;\n}\n\n/** Сумма в долларах без хвоста двоичной дроби. */\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\n\n/** Поля качества и фона по пути профиля (`[\"settings\"]`), поверх полей профиля; профиль не меняется. */\nfunction withSettings(base: Record<string, unknown> | undefined, path: readonly string[], fields: Record<string, unknown>): Record<string, unknown> {\n const root: Record<string, unknown> = { ...base };\n let node = root;\n for (const key of path) {\n const next = node[key];\n node[key] = { ...(typeof next === \"object\" && next !== null ? (next as Record<string, unknown>) : {}) };\n node = node[key] as Record<string, unknown>;\n }\n Object.assign(node, fields);\n return root;\n}\n\n/**\n * Настройка картинок целиком: модели, провайдеры, ключи, качество, фон и назначения. Ошибка — исключение сразу, при\n * создании, с именем поля или модели в тексте.\n */\nexport function resolveImageSetups(choice: ImageModelChoice): ImageSetups {\n const catalog = choice.catalog ?? standardImageCatalog;\n const connections = new Map<string, ImageProviderConnection>();\n\n function connect(provider: ImageProviderDescription): ImageProviderConnection {\n const existing = connections.get(provider.id);\n if (existing) return existing;\n const apiKey = choice.env[provider.apiKeyEnv]?.trim();\n if (!apiKey) throw new Error(`Картинки: нет ключа провайдера ${provider.displayName} в переменной окружения ${provider.apiKeyEnv}`);\n const connection: ImageProviderConnection = {\n apiKey,\n ...(provider.baseURL !== undefined && { baseURL: provider.baseURL }),\n ...(provider.headers !== undefined && { headers: provider.headers }),\n fetch: choice.fetch ?? globalThis.fetch,\n };\n connections.set(provider.id, connection);\n return connection;\n }\n\n function step(modelId: string, providerId: string | undefined, where: string, settings: { quality?: ImageQuality; size?: ImageSize; transparentBackground?: boolean } = {}): ImageStep {\n const profile = catalog.models.find((model) => model.id === modelId);\n if (!profile) throw new Error(`Картинки: ${where}модели ${modelId} нет в каталоге`);\n const chosen = providerId ?? Object.keys(profile.providerModelIds)[0];\n if (!chosen || profile.providerModelIds[chosen] === undefined) throw new Error(`Картинки: ${where}у модели ${modelId} нет провайдера ${chosen ?? \"\"}`.trim());\n const provider = catalog.providers.find((item) => item.id === chosen);\n if (!provider) throw new Error(`Картинки: ${where}провайдера ${chosen} нет в каталоге`);\n\n const fields: Record<string, unknown> = {};\n if (settings.quality !== undefined) {\n if (!imageQualities.includes(settings.quality)) throw new Error(`Картинки: ${where}quality — одно из ${imageQualities.join(\", \")}`);\n if (!profile.settingsPath) throw new Error(`Картинки: ${where}модель ${modelId} не умеет выбирать качество`);\n fields.quality = settings.quality;\n }\n let sizes = profile.aspectRatios;\n let sizePriceUsd = profile.pricePerImageUsd;\n if (settings.size !== undefined && settings.size !== \"default\") {\n if (!imageSizes.includes(settings.size)) throw new Error(`Картинки: ${where}size — одно из ${imageSizes.join(\", \")}`);\n const option = profile.sizes?.[settings.size];\n if (!option) throw new Error(`Картинки: ${where}модель ${modelId} не умеет размер ${settings.size}`);\n sizes = option.aspectRatios;\n sizePriceUsd = option.pricePerImageUsd ?? profile.pricePerImageUsd;\n }\n const qualityFactor = settings.quality !== undefined ? (profile.qualityPriceFactors?.[settings.quality] ?? 1) : 1;\n if (settings.transparentBackground) {\n if (!profile.transparentBackground || !profile.settingsPath) throw new Error(`Картинки: ${where}модель ${modelId} не умеет прозрачный фон`);\n fields.background = \"transparent\";\n }\n const providerSettings = profile.settingsPath && Object.keys(fields).length > 0 ? withSettings(profile.providerSettings, profile.settingsPath, fields) : profile.providerSettings;\n return {\n profile,\n provider,\n providerModelId: profile.providerModelIds[chosen]!,\n connection: connect(provider),\n sizes,\n priceUsd: roundUsd(sizePriceUsd * qualityFactor),\n ...(provider.baseURL !== undefined && { trustedOrigin: new URL(provider.baseURL).origin }),\n ...(providerSettings && { providerSettings }),\n ...(settings.transparentBackground && { outputFormat: \"png\" as const }),\n };\n }\n\n function setup(purpose: Omit<ImagePurpose, \"description\"> & { description?: string }, where: string, name?: string): ImageSetup {\n const quality = purpose.quality ?? choice.quality;\n const size = purpose.size ?? choice.size;\n const transparentBackground = purpose.transparentBackground ?? choice.transparentBackground;\n const output = purpose.output ?? choice.output;\n checkImageOutput(output, where);\n const base = step(purpose.model, purpose.provider, where, { ...(quality !== undefined && { quality }), ...(size !== undefined && { size }), ...(transparentBackground && { transparentBackground }) });\n if (base.profile.needsSourceImage) throw new Error(`Картинки: ${where}модель ${base.profile.id} правит готовую картинку — ей нужна исходная, она годится только для removeBackground`);\n const steps = [base];\n if (purpose.removeBackground) {\n const removal = step(purpose.removeBackground.model, purpose.removeBackground.provider, `${where}removeBackground: `);\n if (!removal.profile.needsSourceImage) throw new Error(`Картинки: ${where}removeBackground: модель ${removal.profile.id} не правит исходную картинку`);\n steps.push({ ...removal, prompt: purpose.removeBackground.prompt ?? DEFAULT_REMOVE_BACKGROUND_PROMPT });\n }\n if ((transparentBackground || purpose.removeBackground) && output?.format === \"jpeg\") throw new Error(`Картинки: ${where}прозрачный фон не сохранится в jpeg — выбери png или webp`);\n return {\n ...(name !== undefined && { purpose: name, description: purpose.description }),\n steps,\n ...(output && { output }),\n priceUsd: roundUsd(steps.reduce((total, item) => total + item.priceUsd, 0)),\n };\n }\n\n const byPurpose = new Map<string, ImageSetup>();\n if (choice.purposes === undefined) {\n byPurpose.set(\"\", setup({ model: choice.model ?? DEFAULT_MODEL, ...(choice.provider !== undefined && { provider: choice.provider }) }, \"\"));\n } else {\n if (choice.model !== undefined || choice.provider !== undefined) throw new Error(\"Картинки: purposes задают модель каждому назначению — model и provider рядом с ними не нужны\");\n const entries = Object.entries(choice.purposes);\n if (entries.length === 0) throw new Error(\"Картинки: purposes пуст — нужно хотя бы одно назначение\");\n for (const [name, purpose] of entries) {\n if (!/^[a-z][a-z0-9_-]{0,39}$/.test(name)) throw new Error(`Картинки: имя назначения «${name}» — латиница, цифры, «-» и «_», до 40 знаков`);\n if (!purpose.description?.trim()) throw new Error(`Картинки: у назначения ${name} пустое описание для агента`);\n byPurpose.set(name, setup(purpose, `назначение ${name}: `, name));\n }\n }\n\n const all = [...connections.values()];\n return {\n byPurpose,\n hasPurposes: choice.purposes !== undefined,\n hideSecrets: (text) => all.reduce((result, connection) => hideSecrets(result, connection), text),\n };\n}\n","/** Ход длиннее этого не бывает: счёт старого хода забывается, память не копится. */\nconst FORGET_TURN_AFTER_MS = 6 * 60 * 60 * 1000;\n\n/**\n * Предел «не больше N картинок за ход» по номеру хода (`turnId`). Номер один на все попытки хода и на запасного\n * провайдера, поэтому новая попытка счёт не обнуляет. Без предела или без номера — не считается.\n */\nexport function createTurnCounter(limit: number | undefined) {\n const turns = new Map<string, { count: number; touchedAt: number }>();\n\n function forgetOld(now: number) {\n for (const [turnId, turn] of turns) if (now - turn.touchedAt > FORGET_TURN_AFTER_MS) turns.delete(turnId);\n }\n\n return {\n /** Занять место; `false` — предел хода исчерпан. */\n take(turnId: string | undefined): boolean {\n if (limit === undefined || turnId === undefined) return true;\n const now = Date.now();\n forgetOld(now);\n const turn = turns.get(turnId) ?? { count: 0, touchedAt: now };\n if (turn.count >= limit) return false;\n turns.set(turnId, { count: turn.count + 1, touchedAt: now });\n return true;\n },\n /** Вернуть место: заказ не принят (лимит расходов, ошибка до записи). */\n give(turnId: string | undefined): void {\n if (limit === undefined || turnId === undefined) return;\n const turn = turns.get(turnId);\n if (turn) turn.count = Math.max(0, turn.count - 1);\n },\n };\n}\n","import type { ChatStorage } from \"@ykurilov/agent-chat-core\";\n\nexport interface ImageUsage {\n storage: Pick<ChatStorage, \"insertUsageRecord\">;\n ownerId: string;\n chatId: string;\n /** Id модели картинок из каталога: `gpt-image-2`. */\n modelId: string;\n providerId: string;\n costUsd: number;\n now: () => Date;\n logError?: (message: string) => void;\n}\n\n/**\n * Расход одной картинки (FR-041): та же таблица, по которой считает лимит расходов. Сбой записи не роняет вызов —\n * картинка уже создана, — а уходит в журнал.\n */\nexport async function recordImageUsage(usage: ImageUsage): Promise<void> {\n try {\n await usage.storage.insertUsageRecord({\n id: crypto.randomUUID(),\n ownerId: usage.ownerId,\n chatId: usage.chatId,\n messageId: null,\n source: \"images\",\n modelId: usage.modelId,\n providerId: usage.providerId,\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n costUsd: usage.costUsd,\n createdAt: usage.now().toISOString(),\n });\n } catch (error) {\n usage.logError?.(`Картинки: не удалось записать расход ${usage.modelId}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n","import type { AttachmentStorage } from \"@ykurilov/agent-chat-attachments\";\nimport { downloadGeneratedImage, imageExtensions } from \"./download-image\";\nimport type { DownloadedImage } from \"./download-image\";\nimport type { ProcessedImage } from \"./postprocess-image\";\n\nexport { DOWNLOAD_TIMEOUT_MS } from \"./download-image\";\n\nexport interface SaveGeneratedImageInput {\n /** Адрес готовой картинки у провайдера. */\n url: string;\n fetch: typeof globalThis.fetch;\n storage: AttachmentStorage;\n chatId: string;\n ownerId: string;\n /** Номер картинки в вызове, с 1: из него имя файла `image-<n>.<ext>`. */\n number: number;\n maxImageBytes: number;\n /** Сайт подключения провайдера из настройки (`https://host:port`): с него можно скачивать и по http, и с localhost. */\n trustedOrigin?: string;\n /** «Стоп» хода: пришедшее после него не сохраняется. */\n abortSignal?: AbortSignal;\n /** Обработка перед сохранением (формат, размер); её итог и сохраняется. */\n transform?: (bytes: Uint8Array, mediaType: string) => Promise<ProcessedImage>;\n now: () => Date;\n}\n\n/** Сохранённая картинка; размер — из файла (PNG или после обработки), иначе не известен. */\nexport interface SavedImageFile {\n id: string;\n mediaType: string;\n size: number;\n width?: number;\n height?: number;\n}\n\n/**\n * Скачивает готовую картинку по адресу провайдера (без заголовков подключения: это чужой склад) и сохраняет её\n * прикреплённым файлом чата и владельца (FR-038). Тип — только по содержимому: PNG, JPEG или WebP. Сбой —\n * `ImageGenerationError`; «Стоп» пробрасывается как есть, и ничего не сохраняется (FR-042).\n */\nexport async function saveGeneratedImage(input: SaveGeneratedImageInput): Promise<SavedImageFile> {\n const file = await downloadGeneratedImage({\n url: input.url,\n fetch: input.fetch,\n maxImageBytes: input.maxImageBytes,\n ...(input.trustedOrigin !== undefined && { trustedOrigin: input.trustedOrigin }),\n ...(input.abortSignal && { abortSignal: input.abortSignal }),\n ...(input.transform && { transform: input.transform }),\n });\n return saveImageToChat({ storage: input.storage, chatId: input.chatId, ownerId: input.ownerId, number: input.number, file, now: input.now });\n}\n\nexport interface SaveImageToChatInput {\n storage: AttachmentStorage;\n chatId: string;\n ownerId: string;\n /** Номер картинки в вызове, с 1: из него имя файла `image-<n>.<ext>`. */\n number: number;\n file: DownloadedImage;\n now: () => Date;\n}\n\n/** Скачанная картинка — прикреплённым файлом чата и владельца. */\nexport async function saveImageToChat(input: SaveImageToChatInput): Promise<SavedImageFile> {\n const { file } = input;\n const extension = imageExtensions[file.mediaType]!;\n const id = crypto.randomUUID();\n await input.storage.save(\n {\n id,\n chatId: input.chatId,\n ownerId: input.ownerId,\n name: `image-${input.number}.${extension}`,\n mediaType: file.mediaType,\n kind: \"image\",\n size: file.bytes.length,\n createdAt: input.now().toISOString(),\n attached: true,\n },\n file.bytes,\n );\n return { id, mediaType: file.mediaType, size: file.bytes.length, ...(file.width !== undefined && file.height !== undefined && { width: file.width, height: file.height }) };\n}\n"],"mappings":"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","names":["SpendingLimitReachedError","view","refused","roundUsd","errorText","costOf","start","record","DEFAULT_MAX_IMAGE_BYTES","DEFAULT_LATE_RESULT_WINDOW_MS","LIMIT_REACHED","roundUsd","errorText","SpendingLimitReachedError","queue"]}
1
+ {"version":3,"sources":["../src/create-image-generation.ts","../src/download-image.ts","../src/download-address.ts","../src/postprocess-image.ts","../src/image-jobs.ts","../src/image-queue/create-image-queue.ts","../src/image-setup.ts","../src/image-queue/turn-counter.ts","../src/images-usage.ts","../src/save-images.ts"],"sourcesContent":["import { tool } from \"ai\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport type { AttachmentStorage } from \"@ykurilov/agent-chat-attachments\";\nimport { SpendingLimitReachedError, declareToolTimeout } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage, SpendingLimits } from \"@ykurilov/agent-chat-core\";\nimport { aspectRatios } from \"./image-catalog\";\nimport type { AspectRatio, ImageCatalog, ImageQuality, ImageSize } from \"./image-catalog\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport { createImageJobs } from \"./image-jobs\";\nimport type { ImageJobInput, ImageJobOutcome, LateImage, ReceivedImageFile } from \"./image-jobs\";\nimport { createImageQueue } from \"./image-queue/create-image-queue\";\nimport type { GeneratedImage, ImageDeliveryContext, ImageJob, ImageQueue } from \"./image-queue/create-image-queue\";\nimport type { ImageJobStore } from \"./image-queue/image-job-store\";\nimport { createTurnCounter } from \"./image-queue/turn-counter\";\nimport { resolveImageSetups } from \"./image-setup\";\nimport type { ImagePurpose, ImageSetup } from \"./image-setup\";\nimport { recordImageUsage } from \"./images-usage\";\nimport type { ImageOutputOptions } from \"./postprocess-image\";\nimport { DOWNLOAD_TIMEOUT_MS, saveImageToChat } from \"./save-images\";\n\nconst DEFAULT_MAX_IMAGE_BYTES = 10 * 1024 * 1024;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 150_000;\nconst DEFAULT_LATE_RESULT_WINDOW_MS = 10 * 60 * 1000;\n\n/**\n * Срок вызова `generate_images` при сроке генерации по умолчанию: генерация до 150 с и скачивание до 60 с. Инструмент\n * объявляет ядру свой срок сам (`requestTimeoutMs` + скачивание), настраивать `agent.toolTimeoutMs` не нужно.\n */\nexport const imageToolTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS + DOWNLOAD_TIMEOUT_MS;\n\nexport interface CreateImageGenerationOptions {\n /** Тот же объект `createAttachments`: картинки ложатся в его хранилище и отдаются его маршрутом. */\n attachments: { storage: AttachmentStorage };\n /** Окружение сервера: ключ провайдера берётся только отсюда, по `apiKeyEnv`. */\n env: Record<string, string | undefined>;\n catalog?: ImageCatalog;\n /** Модель картинок из каталога; по умолчанию `gpt-image-2`. С `purposes` не задаётся. */\n model?: string;\n /** Провайдер модели; по умолчанию первый из её `providerModelIds`. */\n provider?: string;\n /** Качество GPT Image (`low`, `medium`, `high`, `auto`); по умолчанию — из профиля (`low`). Для назначений — общее по умолчанию. */\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024 (1:1 — 1024×1024); по умолчанию — размер модели. Для назначений — общий по умолчанию. */\n size?: ImageSize;\n /** Прозрачный фон (GPT Image 2.5 у Runware): файл PNG с альфой. Для назначений — общий по умолчанию. */\n transparentBackground?: boolean;\n /** Постобработка перед сохранением: формат файла и предел большей стороны (sharp). Для назначений — общая по умолчанию. */\n output?: ImageOutputOptions;\n /**\n * Назначения: имя → модель и описание для агента. Агент выбирает назначение параметром `purpose`, по умолчанию —\n * первое. Описание инструмента собирается из них один раз и дальше не меняется (кэш модели).\n */\n purposes?: Record<string, ImagePurpose>;\n /**\n * Расход картинок в лимиты: запись на каждую отправленную задачу и проверка лимита до генерации. Заказанное, но ещё не\n * записанное (вызовы, которые идут прямо сейчас, и задачи в фоне) проверка видит в `pendingUsd`: параллельные вызовы\n * одного шага не проходят лимит все разом.\n */\n spending?: {\n storage: Pick<ChatStorage, \"insertUsageRecord\">;\n /** `afterSpending` (если есть) зовётся после каждой записи расхода: пороги лимитов видят и картинки. */\n limits?: Pick<SpendingLimits, \"assertWithinLimit\" | \"afterSpending\">;\n };\n /** Предел одного файла; по умолчанию 10 МБ. */\n maxImageBytes?: number;\n /** Срок одной генерации; по умолчанию 150 000 мс. */\n requestTimeoutMs?: number;\n /**\n * Провайдер с очередью (KIE): сколько ещё ждать картинку в фоне после срока вызова; по умолчанию 10 минут, 0 — не\n * ждать. Готовая сохраняется в чат и отдаётся повтору того же вызова бесплатно, а приложение узнаёт о ней в `onLateImage`.\n */\n lateResultWindowMs?: number;\n /**\n * Картинка пришла после срока вызова (в фоновом режиме — всегда) и сохранена в чат. Ошибка обработчика — в `logError`,\n * не наружу. Со своим приёмником `deliver` не зовётся: картинку получает приёмник.\n */\n onLateImage?: (image: LateImage) => void | Promise<void>;\n /**\n * Свой приёмник картинки вместо вложений чата: записать файл, обновить экран. Ответ (JSON) получает модель в\n * `результат`; `false` — картинка больше не нужна. В фоновом режиме приёмника зовут хотя бы один раз (после сбоя\n * сервера — ещё раз), поэтому повтор должен быть безопасен. Запись — условная, по `context.isCurrent()`: пока приёмник\n * работает, задачу могут вытеснить или остановить.\n */\n deliver?: (image: GeneratedImage, delivery: ImageDelivery, context: ImageDeliveryContext) => unknown;\n /**\n * `wait` (по умолчанию) — вызов ждёт картинку. `background` — вызов отвечает, как только задача ушла провайдеру, а\n * картинка приходит позже: во вложения (и `onLateImage`) или в `deliver`. Фоновые задачи лежат в `jobStore`.\n */\n mode?: \"wait\" | \"background\";\n /** Фоновый режим: где лежат задачи; по умолчанию в памяти. `sqliteImageJobStore` или `postgresImageJobStore` — переживают перезапуск. */\n jobStore?: ImageJobStore;\n /** Фоновый режим: шаг проверки задачи у провайдера с очередью; по умолчанию 3000 мс. */\n pollIntervalMs?: number;\n /** Фоновый режим: сколько попыток по `requestTimeoutMs`; следующая проверяет ту же задачу без новой оплаты. По умолчанию 1. */\n attempts?: number;\n /** Не больше стольких картинок за ход (`turnId`; без него — за набор `tools()`). Лишние — в ошибках, без заказа. */\n maxImagesPerTurn?: number;\n /** Фоновый режим: поля для ответа модели о заказанной картинке (например, куда она ляжет). */\n onQueued?: (job: ImageJob) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;\n /**\n * Свой `fetch` приложения: запросы к провайдеру и скачивание картинок. Это граница доверия приложения: закрепить\n * соединение за проверенным адресом (от подмены DNS) должен он сам (`createPinnedFetch` из\n * `@ykurilov/agent-chat-web-tools/network`). Без него картинку со склада провайдера качает закреплённый `fetch`.\n */\n fetch?: typeof globalThis.fetch;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\n/** Чья и какая картинка пришла в `deliver`. */\nexport interface ImageDelivery {\n ownerId: string;\n chatId: string;\n turnId?: string;\n /** Номер картинки в вызове, с 1. */\n number: number;\n prompt: string;\n aspectRatio: AspectRatio;\n purpose?: string;\n /** Модель картинки (первого шага). */\n modelId: string;\n /** Фоновый режим: id задачи из ответа модели. */\n jobId?: string;\n}\n\n/** Выбранная модель хода: ядро передаёт в `agent.tools(model)` того, чей ход, чат и номер хода. */\nexport interface ImageGenerationModel {\n ownerId?: string;\n chatId?: string;\n /** Номер хода: один на все попытки и запасного провайдера. По нему считается `maxImagesPerTurn`. */\n turnId?: string;\n}\n\nexport interface ImageGeneration {\n /** `{ generate_images }` для `agent.tools`; без `ownerId` и `chatId` — пустой набор. */\n tools(model: ImageGenerationModel): ToolSet;\n /** Фоновый режим: подобрать незаконченные задачи. Зовётся само при создании; в режиме `wait` ничего не делает. */\n start(): Promise<void>;\n /** Фоновый режим: остановить опрос и таймеры перед закрытием хранилища; задачи продолжатся следующим `start`. */\n stop(): Promise<void>;\n}\n\n/** Кто заказывает: из `tools(model)`. */\ninterface Caller {\n ownerId: string;\n chatId: string;\n turnId?: string;\n /** Счёт предела за ход: номер хода или свой у набора `tools()`. */\n turnKey: string;\n}\n\ninterface GenerateImagesInput {\n prompt: string;\n purpose?: string;\n count: number;\n aspectRatio: AspectRatio;\n}\n\ninterface SavedImage {\n номер: number;\n /** Вложение чата; со своим приёмником его нет. */\n id?: string;\n ширина: number;\n высота: number;\n /** Ответ своего приёмника. */\n результат?: unknown;\n}\ninterface FailedImage {\n номер: number;\n причина: string;\n /** Картинка ещё готовится у провайдера: повтор того же вызова получит её без новой оплаты. */\n готовится?: true;\n}\n\n/** Основа описания не зависит от человека и времени: от этого зависит кэш модели. */\nconst baseDescription =\n \"Создаёт картинки по описанию и показывает их человеку в чате. \" +\n \"Пиши описание подробно: что изображено, стиль, цвета, композиция. \" +\n \"Человек видит картинки сам: в ответе не пересказывай их и не вставляй ссылки.\";\n/** Фоновый режим: картинка приходит сама, повторять вызов незачем. */\nconst backgroundDescription =\n \"Заказывает картинки по описанию: они готовятся в фоне и появятся у человека сами. \" +\n \"Пиши описание подробно: что изображено, стиль, цвета, композиция. \" +\n \"Ответ приходит сразу, без картинок: не жди их, не вызывай инструмент снова ради них и не вставляй ссылки.\";\nconst queuedMessage = \"Картинки готовятся в фоне и появятся у человека сами. Не жди их и не заказывай заново.\";\n/** Только если есть провайдер с очередью: иначе «готовится» не бывает. */\nconst repeatHint = \" Если у картинки в ошибках «готовится», повтори вызов с теми же параметрами позже: готовая придёт без новой оплаты.\";\n\nconst promptSchema = z.string().min(2).max(2000).describe(\"Описание картинки, от 2 до 2000 знаков.\");\nconst countSchema = z.number().int().min(1).max(8).default(1).describe(\"Сколько вариантов создать, от 1 до 8.\");\nconst aspectRatioSchema = z.enum(aspectRatios as [AspectRatio, ...AspectRatio[]]).default(\"1:1\").describe(\"Пропорции картинки.\");\n\nconst LIMIT_REACHED = \"лимит расходов исчерпан\";\nconst NOT_NEEDED = \"картинка больше не нужна приложению\";\n\n/** Бронь вызова, который идёт: задачи, ещё не отданные реестру задач, по цене назначения. */\ninterface Reservation {\n ownerId: string;\n chatId: string;\n openTasks: number;\n priceUsd: number;\n}\n\n/** Сумма в долларах без хвоста двоичной дроби: 8 × 0,02 — это 0,16. */\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\n\nfunction checkNumbers(options: CreateImageGenerationOptions) {\n for (const [name, value] of [[\"maxImageBytes\", options.maxImageBytes], [\"requestTimeoutMs\", options.requestTimeoutMs]] as const) {\n if (value !== undefined && !(Number.isFinite(value) && value > 0)) throw new Error(`Картинки: ${name} — нужно число больше нуля`);\n }\n const window = options.lateResultWindowMs;\n if (window !== undefined && !(Number.isFinite(window) && window >= 0)) throw new Error(\"Картинки: lateResultWindowMs — число не меньше нуля\");\n const limit = options.maxImagesPerTurn;\n if (limit !== undefined && !(Number.isInteger(limit) && limit >= 1)) throw new Error(\"Картинки: maxImagesPerTurn — целое число от 1\");\n if (options.mode !== \"background\") {\n const backgroundOnly = ([\"jobStore\", \"pollIntervalMs\", \"attempts\", \"onQueued\"] as const).filter((name) => options[name] !== undefined);\n if (backgroundOnly.length > 0) throw new Error(`Картинки: ${backgroundOnly.join(\", \")} — только для mode: \"background\"`);\n }\n}\n\n/**\n * Генерация картинок: инструмент `generate_images` для набора `agent.tools`. Настройка проверяется сразу: нет модели,\n * провайдера или ключа — исключение здесь, а не при первом вызове.\n */\nexport function createImageGeneration(options: CreateImageGenerationOptions): ImageGeneration {\n const setups = resolveImageSetups(options);\n checkNumbers(options);\n const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n const now = options.now ?? (() => new Date());\n const logError = (message: string) => options.logError?.(setups.hideSecrets(message));\n const errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n const all = [...setups.byPurpose.values()];\n const queued = all.some((setup) => setup.steps.some((step) => step.provider.queue !== undefined));\n\n const turns = createTurnCounter(options.maxImagesPerTurn);\n const deliver = options.deliver;\n\n /** Свой приёмник: ответ — модели, `false` и сбой — в ошибки картинки. */\n async function deliverToApp(image: GeneratedImage, delivery: ImageDelivery, context: ImageDeliveryContext): Promise<unknown> {\n let result: unknown;\n try {\n result = await deliver!(image, delivery, context);\n } catch (error) {\n logError(`Картинки: приложение не приняло картинку ${delivery.number} (чат ${delivery.chatId}): ${errorText(error)}`);\n throw new ImageGenerationError(\"Приложение не приняло картинку\", { refusedBeforeWork: false });\n }\n if (result === false) throw new ImageGenerationError(NOT_NEEDED, { refusedBeforeWork: false });\n return result;\n }\n\n const deliveryOf = (input: ImageJobInput, turnId: string | undefined, modelId: string): ImageDelivery => ({\n ownerId: input.ownerId,\n chatId: input.chatId,\n ...(turnId !== undefined && { turnId }),\n number: input.number,\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n ...(input.setup.purpose !== undefined && { purpose: input.setup.purpose }),\n modelId,\n });\n\n /** Номер хода задачи прежнего режима: его ждёт свой приёмник. */\n const turnIds = new WeakMap<ImageJobInput, string>();\n\n async function receive(input: ImageJobInput, file: ReceivedImageFile, signal: AbortSignal): Promise<{ id?: string; result?: unknown }> {\n const modelId = input.setup.steps[0]!.profile.id;\n if (deliver) {\n const context: ImageDeliveryContext = { signal, isCurrent: async () => !signal.aborted };\n const result = await deliverToApp({ ...file, modelId }, deliveryOf(input, turnIds.get(input), modelId), context);\n return result === undefined ? {} : { result };\n }\n const saved = await saveImageToChat({ storage: options.attachments.storage, chatId: input.chatId, ownerId: input.ownerId, number: input.number, file, now });\n return { id: saved.id };\n }\n\n const jobs = createImageJobs({\n receive,\n maxImageBytes: options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES,\n requestTimeoutMs,\n lateResultWindowMs: options.lateResultWindowMs ?? DEFAULT_LATE_RESULT_WINDOW_MS,\n now,\n async record(input, step, costUsd) {\n if (!options.spending) return;\n await recordImageUsage({ storage: options.spending.storage, ownerId: input.ownerId, chatId: input.chatId, modelId: step.profile.id, providerId: step.provider.id, costUsd, now, logError });\n const afterSpending = options.spending.limits?.afterSpending;\n // Пороги — фоном: картинку ход не задерживает.\n if (afterSpending) {\n void afterSpending\n .call(options.spending.limits, { userId: input.ownerId, chatId: input.chatId })\n .catch((error: unknown) => logError(`Картинки: лимиты после расхода (чат ${input.chatId}): ${errorText(error)}`));\n }\n },\n ...(options.onLateImage && { onLateImage: options.onLateImage }),\n logError,\n hideSecrets: setups.hideSecrets,\n });\n\n const reservations = new Set<Reservation>();\n const limitChecks = new Map<string, Promise<unknown>>();\n\n /** Заказанное, но не записанное: всего у человека и в этом чате — брони вызовов и задачи в работе. */\n function pendingUsd(ownerId: string, chatId: string): { user: number; chat: number } {\n const working = jobs.pendingUsd(ownerId, chatId);\n let user = working.user;\n let chat = working.chat;\n for (const reservation of reservations) {\n if (reservation.ownerId !== ownerId) continue;\n user += reservation.openTasks * reservation.priceUsd;\n if (reservation.chatId === chatId) chat += reservation.openTasks * reservation.priceUsd;\n }\n return { user: roundUsd(user), chat: roundUsd(chat) };\n }\n\n async function withinLimit(ownerId: string, chatId: string): Promise<string | undefined> {\n const pending = pendingUsd(ownerId, chatId);\n try {\n await options.spending?.limits?.assertWithinLimit({ userId: ownerId, chatId, ...(pending.user > 0 && { pendingUsd: pending }) });\n return undefined;\n } catch (error) {\n if (error instanceof SpendingLimitReachedError) return LIMIT_REACHED;\n logError(`Картинки: не удалось проверить лимит расходов: ${errorText(error)}`);\n return \"не удалось проверить лимит расходов\";\n }\n }\n\n /**\n * Проверка лимита и бронь `count` задач — по одному на человека: параллельный вызов того же шага проверяется уже\n * с бронью предыдущего. Сама генерация друг друга не ждёт.\n */\n function reserve(ownerId: string, chatId: string, count: number, priceUsd: number): Promise<{ refusal: string } | { reservation: Reservation }> {\n const previous = limitChecks.get(ownerId) ?? Promise.resolve();\n const check = previous.then(async () => {\n const refusal = await withinLimit(ownerId, chatId);\n if (refusal) return { refusal };\n const reservation: Reservation = { ownerId, chatId, openTasks: count, priceUsd };\n reservations.add(reservation);\n return { reservation };\n });\n limitChecks.set(ownerId, check);\n void check.finally(() => {\n if (limitChecks.get(ownerId) === check) limitChecks.delete(ownerId);\n });\n return check;\n }\n\n /** Места хода по номерам картинок: вышедшие за предел — в ошибки, без заказа. */\n function takeTurnPlaces(caller: Caller, count: number): { allowed: number[]; overLimit: FailedImage[] } {\n const allowed: number[] = [];\n const overLimit: FailedImage[] = [];\n for (let number = 1; number <= count; number++) {\n if (turns.take(caller.turnKey)) allowed.push(number);\n else overLimit.push({ номер: number, причина: `не больше ${options.maxImagesPerTurn} картинок за ход` });\n }\n return { allowed, overLimit };\n }\n\n const giveTurnPlaces = (caller: Caller, numbers: readonly number[]) => numbers.forEach(() => turns.give(caller.turnKey));\n\n async function generateImages(input: GenerateImagesInput, caller: Caller, abortSignal: AbortSignal | undefined) {\n const { ownerId, chatId } = caller;\n const setup = setups.byPurpose.get(setups.hasPurposes ? (input.purpose ?? \"\") : \"\")!;\n // «Стоп» до начала: ни проверки, ни генерации, ни расхода.\n const stoppedBeforeWork = { создано: 0, заказано: input.count, картинки: [], остановлено: true };\n if (abortSignal?.aborted) return stoppedBeforeWork;\n const { allowed, overLimit } = takeTurnPlaces(caller, input.count);\n if (allowed.length === 0) return { создано: 0, заказано: input.count, картинки: [], ошибки: overLimit };\n const booked = await reserve(ownerId, chatId, allowed.length, setup.priceUsd);\n if (\"refusal\" in booked) {\n giveTurnPlaces(caller, allowed);\n return { отказ: booked.refusal };\n }\n try {\n if (abortSignal?.aborted) {\n giveTurnPlaces(caller, allowed);\n return stoppedBeforeWork;\n }\n return await runTasks(input, setup, caller, allowed, overLimit, booked.reservation, abortSignal);\n } finally {\n // Вызов закончен: его задачи или оплачены, или в реестре задач (и видны лимиту оттуда), бронь больше не нужна.\n reservations.delete(booked.reservation);\n }\n }\n\n async function runTasks(\n input: GenerateImagesInput,\n setup: ImageSetup,\n caller: Caller,\n numbers: readonly number[],\n overLimit: readonly FailedImage[],\n reservation: Reservation,\n abortSignal: AbortSignal | undefined,\n ) {\n const { ownerId, chatId } = caller;\n const saved: SavedImage[] = [];\n const failed: FailedImage[] = [...overLimit];\n const pendingReason =\n `Картинка не успела за ${Math.round(requestTimeoutMs / 1000)} с и ещё готовится: ` + \"повтори вызов с теми же параметрами позже — готовая придёт без новой оплаты\";\n\n const collect = (number: number, outcome: ImageJobOutcome) => {\n if (outcome.status === \"saved\") {\n const { image } = outcome;\n saved.push({\n номер: number,\n ...(image.id !== undefined && { id: image.id }),\n ширина: image.width,\n высота: image.height,\n ...(image.result !== undefined && { результат: image.result }),\n });\n }\n else if (outcome.status === \"failed\") failed.push({ номер: number, причина: outcome.reason });\n else if (outcome.status === \"pending\") failed.push({ номер: number, причина: pendingReason, готовится: true });\n };\n await Promise.all(\n numbers.map(async (number) => {\n const jobInput: ImageJobInput = { ownerId, chatId, number, prompt: input.prompt, aspectRatio: input.aspectRatio, setup };\n if (caller.turnId !== undefined) turnIds.set(jobInput, caller.turnId);\n // Задача уходит в реестр: дальше лимит видит её там, а не в брони.\n const outcome = jobs.obtain(jobInput, abortSignal);\n reservation.openTasks--;\n collect(number, await outcome);\n }),\n );\n const byNumber = <T extends { номер: number }>(items: T[]) => [...items].sort((a, b) => a.номер - b.номер);\n return {\n создано: saved.length,\n заказано: input.count,\n картинки: byNumber(saved),\n ...(failed.length > 0 && { ошибки: byNumber(failed) }),\n ...(abortSignal?.aborted && { остановлено: true }),\n };\n }\n\n // --- фоновый режим ---\n\n const background = options.mode === \"background\";\n\n /** Готовая фоновая картинка: свой приёмник или вложения чата с `onLateImage`. */\n async function receiveQueued(image: GeneratedImage, job: ImageJob, context: ImageDeliveryContext): Promise<unknown> {\n const number = (job.data as { number?: number } | undefined)?.number ?? 1;\n const delivery: ImageDelivery = {\n ownerId: job.ownerId,\n chatId: job.chatId,\n ...(job.turnId !== undefined && { turnId: job.turnId }),\n number,\n prompt: job.prompt,\n aspectRatio: job.aspectRatio,\n ...(job.purpose !== undefined && { purpose: job.purpose }),\n modelId: image.modelId,\n jobId: job.id,\n };\n if (deliver) return deliver(image, delivery, context);\n const saved = await saveImageToChat({ storage: options.attachments.storage, chatId: job.chatId, ownerId: job.ownerId, number, file: image, now });\n try {\n await options.onLateImage?.({\n ownerId: job.ownerId,\n chatId: job.chatId,\n number,\n attachmentId: saved.id,\n mediaType: image.mediaType,\n width: image.width,\n height: image.height,\n prompt: job.prompt,\n aspectRatio: job.aspectRatio,\n modelId: image.modelId,\n ...(job.purpose !== undefined && { purpose: job.purpose }),\n });\n } catch (error) {\n logError(`Картинки: onLateImage не принял картинку: ${errorText(error)}`);\n }\n return { attachmentId: saved.id };\n }\n\n const queue: ImageQueue | undefined = background\n ? createImageQueue({\n env: options.env,\n ...(options.catalog && { catalog: options.catalog }),\n ...(options.model !== undefined && { model: options.model }),\n ...(options.provider !== undefined && { provider: options.provider }),\n ...(options.quality !== undefined && { quality: options.quality }),\n ...(options.size !== undefined && { size: options.size }),\n ...(options.transparentBackground !== undefined && { transparentBackground: options.transparentBackground }),\n ...(options.output && { output: options.output }),\n ...(options.purposes && { purposes: options.purposes }),\n ...(options.fetch && { fetch: options.fetch }),\n deliver: receiveQueued,\n ...(options.jobStore && { store: options.jobStore }),\n ...(options.spending && { spending: options.spending }),\n ...(options.pollIntervalMs !== undefined && { pollIntervalMs: options.pollIntervalMs }),\n attemptTimeoutMs: requestTimeoutMs,\n ...(options.attempts !== undefined && { attempts: options.attempts }),\n ...(options.lateResultWindowMs !== undefined && { lateResultWindowMs: options.lateResultWindowMs }),\n ...(options.maxImageBytes !== undefined && { maxImageBytes: options.maxImageBytes }),\n ...(options.logError && { logError: options.logError }),\n ...(options.now && { now: options.now }),\n })\n : undefined;\n\n /** Фоновый заказ: ответ модели, как только задачи ушли провайдеру. */\n async function orderImages(queue: ImageQueue, input: GenerateImagesInput, caller: Caller, abortSignal: AbortSignal | undefined) {\n if (abortSignal?.aborted) return { заказано: input.count, принято: 0, задачи: [], остановлено: true };\n const { allowed, overLimit } = takeTurnPlaces(caller, input.count);\n const failed: FailedImage[] = [...overLimit];\n const queuedJobs: Array<Record<string, unknown> & { номер: number }> = [];\n await Promise.all(\n allowed.map(async (number) => {\n let placed: Awaited<ReturnType<ImageQueue[\"order\"]>>;\n try {\n placed = await queue.order({\n ownerId: caller.ownerId,\n chatId: caller.chatId,\n ...(caller.turnId !== undefined && { turnId: caller.turnId }),\n ...(setups.hasPurposes && input.purpose !== undefined && { purpose: input.purpose }),\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n data: { number },\n });\n } catch (error) {\n // Каждый заказ — сам по себе: сбой одного не роняет уже принятые, иначе модель повторит вызов и оплатит их снова.\n turns.give(caller.turnKey);\n logError(`Картинки: заказ ${number} не принят: ${errorText(error)}`);\n failed.push({ номер: number, причина: \"Не удалось заказать картинку\" });\n return;\n }\n if (placed.status === \"refused\") {\n turns.give(caller.turnKey);\n failed.push({ номер: number, причина: placed.reason });\n } else if (placed.status === \"failed\") failed.push({ номер: number, причина: placed.reason ?? \"Провайдер не смог создать картинку\" });\n else queuedJobs.push({ номер: number, id: placed.id, ...(await queuedFields(placed)) });\n }),\n );\n const byNumber = <T extends { номер: number }>(items: T[]) => [...items].sort((a, b) => a.номер - b.номер);\n return {\n заказано: input.count,\n принято: queuedJobs.length,\n задачи: byNumber(queuedJobs),\n ...(failed.length > 0 && { ошибки: byNumber(failed) }),\n ...(queuedJobs.length > 0 && { сообщение: queuedMessage }),\n };\n }\n\n async function queuedFields(job: ImageJob): Promise<Record<string, unknown>> {\n try {\n return (await options.onQueued?.(job)) ?? {};\n } catch (error) {\n logError(`Картинки: onQueued не сработал (задача ${job.id}): ${errorText(error)}`);\n return {};\n }\n }\n\n // Описание и схема собираются один раз: не зависят от человека, чата и времени (кэш модели).\n const purposeList = setups.hasPurposes\n ? \" Назначения (параметр purpose): \" + all.map((setup) => `${setup.purpose} — ${setup.description}`).join(\"; \") + \".\"\n : \"\";\n const description = background ? backgroundDescription + purposeList : baseDescription + purposeList + (queued ? repeatHint : \"\");\n const purposeNames = all.map((setup) => setup.purpose!) as [string, ...string[]];\n const inputSchema = setups.hasPurposes\n ? z.object({\n prompt: promptSchema,\n purpose: z.enum(purposeNames).default(purposeNames[0]).describe(\"Назначение картинки: от него зависит модель.\"),\n count: countSchema,\n aspectRatio: aspectRatioSchema,\n })\n : z.object({ prompt: promptSchema, count: countSchema, aspectRatio: aspectRatioSchema });\n\n // Генерация и скачивание: ядро не обрывает вызов раньше, и модель не заказывает (и не оплачивает) картинки заново.\n const toolTimeoutMs = requestTimeoutMs + DOWNLOAD_TIMEOUT_MS;\n const generateImagesTool = (caller: Caller) =>\n declareToolTimeout(\n tool({\n description,\n inputSchema: inputSchema as z.ZodType<GenerateImagesInput>,\n execute: (input: GenerateImagesInput, { abortSignal }): Promise<Record<string, unknown>> =>\n queue ? orderImages(queue, input, caller, abortSignal) : generateImages(input, caller, abortSignal),\n }),\n toolTimeoutMs,\n );\n\n return {\n tools(model): ToolSet {\n if (!model.ownerId || !model.chatId) return {};\n const caller: Caller = {\n ownerId: model.ownerId,\n chatId: model.chatId,\n ...(model.turnId !== undefined && { turnId: model.turnId }),\n turnKey: model.turnId ?? `tools:${crypto.randomUUID()}`,\n };\n return { generate_images: generateImagesTool(caller) };\n },\n start: () => queue?.start() ?? Promise.resolve(),\n stop: () => queue?.stop() ?? Promise.resolve(),\n };\n}\n","import { detectFileKind } from \"@ykurilov/agent-chat-attachments\";\nimport { dnsHostResolver, sharedPinnedFetch, WebToolError } from \"@ykurilov/agent-chat-web-tools/network\";\nimport type { HostResolver } from \"@ykurilov/agent-chat-web-tools/network\";\nimport { isAllowedImageAddress } from \"./download-address\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport { pngSize } from \"./postprocess-image\";\nimport type { ProcessedImage } from \"./postprocess-image\";\n\n/** Скачивание одной картинки — не дольше этого: задача у провайдера уже готова, файл лежит на его складе. */\nexport const DOWNLOAD_TIMEOUT_MS = 60_000;\n/** Переадресаций склада не больше этого: каждая проверяется, как первый адрес. */\nconst MAX_REDIRECTS = 3;\n\n/** Типы картинок, которые принимаются, и их расширения. */\nexport const imageExtensions: Record<string, string> = { \"image/png\": \"png\", \"image/jpeg\": \"jpg\", \"image/webp\": \"webp\" };\n\nexport interface DownloadGeneratedImageInput {\n /** Адрес готовой картинки у провайдера. */\n url: string;\n /**\n * Свой `fetch` приложения (прокси) — граница доверия приложения: по нему идут все адреса, а закрепить соединение за\n * проверенным адресом (от подмены DNS) должен он сам. Нет — склад провайдера качает общий закреплённый `fetch`:\n * имя проверяется в момент соединения, во внутреннюю сеть не пускает. Сайт `trustedOrigin` — обычным `fetch`.\n */\n fetch?: typeof globalThis.fetch;\n /** Как закреплённый `fetch` разрешает имена. По умолчанию системный DNS; заменяется в тестах. */\n resolveHost?: HostResolver;\n maxImageBytes: number;\n /** Сайт подключения провайдера из настройки (`https://host:port`): с него можно скачивать и по http, и с localhost. */\n trustedOrigin?: string;\n /** «Стоп»: пришедшее после него не отдаётся. */\n abortSignal?: AbortSignal;\n /** Обработка после скачивания (формат, размер); её итог и отдаётся. */\n transform?: (bytes: Uint8Array, mediaType: string) => Promise<ProcessedImage>;\n}\n\n/** Скачанная картинка; размер — из файла (PNG или после обработки), иначе не известен. */\nexport interface DownloadedImage {\n bytes: Uint8Array;\n mediaType: string;\n width?: number;\n height?: number;\n}\n\nconst failure = (reason: string) => new ImageGenerationError(reason, { refusedBeforeWork: false });\nconst megabytes = (bytes: number) => `${Math.round((bytes / (1024 * 1024)) * 10) / 10} МБ`;\n\n/** Тело ответа, но не больше `maxBytes`: на пределе чтение обрывается, файл не копится в памяти дальше. */\nasync function readWithin(response: Response, maxBytes: number): Promise<Uint8Array | null> {\n if (!response.body) return new Uint8Array();\n const chunks: Uint8Array[] = [];\n let total = 0;\n const reader = response.body.getReader();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.length;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n return null;\n }\n chunks.push(value);\n }\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.length;\n }\n return bytes;\n}\n\nfunction checkedAddress(url: string, base: URL | undefined, trustedOrigin: string | undefined): URL {\n let address: URL;\n try {\n address = new URL(url, base);\n } catch {\n throw failure(\"Картинка не скачалась: неверный адрес\");\n }\n if (!isAllowedImageAddress(address, trustedOrigin)) throw failure(\"Картинка не скачалась: неверный адрес\");\n return address;\n}\n\n/**\n * Чем качать адрес. Сайт подключения из настройки — доверенный, пока адрес на нём: свой прокси или поддельный провайдер\n * на localhost. Переадресация на другой сайт этого доверия не наследует и снова идёт закреплённым `fetch`.\n */\nfunction fetchFor(input: DownloadGeneratedImageInput, address: URL): typeof globalThis.fetch {\n if (input.fetch) return input.fetch;\n if (input.trustedOrigin !== undefined && address.origin === input.trustedOrigin) return globalThis.fetch;\n return sharedPinnedFetch(input.resolveHost ?? dnsHostResolver);\n}\n\n/** Ответ склада по адресу; переадресации идут вручную, каждый следующий адрес проверяется (download-address). */\nasync function fetchImage(input: DownloadGeneratedImageInput, first: URL, signal: AbortSignal): Promise<Response> {\n let address = first;\n for (let hop = 0; ; hop++) {\n const response = await fetchFor(input, address)(address.href, { signal, redirect: \"manual\" });\n const location = response.status >= 300 && response.status < 400 ? response.headers.get(\"location\") : null;\n if (location === null) return response;\n await response.body?.cancel().catch(() => {});\n if (hop >= MAX_REDIRECTS) throw failure(\"Картинка не скачалась: слишком много переадресаций\");\n address = checkedAddress(location, address, input.trustedOrigin);\n }\n}\n\n/**\n * Скачивает готовую картинку по адресу провайдера (без заголовков подключения: это чужой склад) и обрабатывает её.\n * Тип — только по содержимому: PNG, JPEG или WebP. Сбой — `ImageGenerationError`; «Стоп» пробрасывается как есть.\n */\nexport async function downloadGeneratedImage(input: DownloadGeneratedImageInput): Promise<DownloadedImage> {\n const address = checkedAddress(input.url, undefined, input.trustedOrigin);\n\n const timeout = new AbortController();\n const timer = setTimeout(() => timeout.abort(new DOMException(\"timeout\", \"TimeoutError\")), DOWNLOAD_TIMEOUT_MS);\n // Срок скачивания не держит процесс: его держит сам запрос.\n (timer as { unref?: () => void }).unref?.();\n const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, timeout.signal]) : timeout.signal;\n let bytes: Uint8Array | null;\n try {\n const response = await fetchImage(input, address, signal);\n if (!response.ok) throw failure(`Картинка не скачалась: ${response.status}`);\n const declared = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > input.maxImageBytes) throw failure(`Картинка больше ${megabytes(input.maxImageBytes)}`);\n bytes = await readWithin(response, input.maxImageBytes);\n } catch (error) {\n if (input.abortSignal?.aborted) throw input.abortSignal.reason ?? error;\n if (error instanceof ImageGenerationError) throw error;\n // Имя склада указало во внутреннюю сеть или не разрешилось — проверка при соединении.\n if (error instanceof WebToolError && error.kind === \"bad_address\") throw failure(\"Картинка не скачалась: неверный адрес\");\n throw failure(timeout.signal.aborted ? \"Картинка не скачалась: не успела\" : \"Картинка не скачалась\");\n } finally {\n clearTimeout(timer);\n }\n if (input.abortSignal?.aborted) throw input.abortSignal.reason;\n if (!bytes) throw failure(`Картинка больше ${megabytes(input.maxImageBytes)}`);\n\n const detected = detectFileKind(bytes, \"\");\n if (!detected || !imageExtensions[detected.mediaType]) throw failure(\"Провайдер вернул не картинку\");\n let file: DownloadedImage = { bytes, mediaType: detected.mediaType, ...pngSize(bytes) };\n if (input.transform) {\n try {\n file = await input.transform(bytes, detected.mediaType);\n } catch {\n throw failure(\"Картинка не обработалась\");\n }\n }\n if (input.abortSignal?.aborted) throw input.abortSignal.reason;\n return file;\n}\n","/*\n * Куда можно идти за готовой картинкой. Адрес приходит в ответе провайдера; без проверки подменённый ответ (прокси,\n * взломанный склад) отправил бы сервер во внутреннюю сеть: к метаданным облака, базе, админке. Поэтому — только\n * `https:` и не внутренние адреса. Исключение — сайт подключения провайдера из настройки приложения: ему доверяют\n * и так (свой прокси, поддельный провайдер демо на localhost). Имена хостов в адреса здесь не разрешаются.\n */\n\n/** Внутренние диапазоны IPv4: [первый октет, второй от, второй до] или [первый октет]. */\nconst PRIVATE_IPV4: Array<[number, number?, number?]> = [\n [0], [10], [127], [100, 64, 127], [169, 254, 254], [172, 16, 31], [192, 168, 168], [198, 18, 19],\n];\n\nfunction isPrivateIpv4(octets: number[]): boolean {\n const [first = 0, second = 0, third = 0] = octets;\n if (first >= 224) return true; // групповые, зарезервированные, широковещательный\n if (first === 192 && second === 0 && third === 0) return true;\n return PRIVATE_IPV4.some(([a, from, to]) => first === a && (from === undefined || (second >= from && second <= (to ?? from))));\n}\n\n/** Восемь групп IPv6 из записи, которую уже привёл к виду разборщик URL (без скобок). */\nfunction ipv6Groups(host: string): number[] | null {\n const [head = \"\", tail, extra] = host.split(\"::\");\n if (extra !== undefined) return null;\n const part = (text: string) => (text === \"\" ? [] : text.split(\":\").map((group) => Number.parseInt(group, 16)));\n const left = part(head);\n const right = tail === undefined ? [] : part(tail);\n const groups = tail === undefined ? left : [...left, ...Array<number>(8 - left.length - right.length).fill(0), ...right];\n return groups.length === 8 && groups.every((group) => Number.isInteger(group) && group >= 0 && group <= 0xffff) ? groups : null;\n}\n\nfunction isPrivateIpv6(host: string): boolean {\n const groups = ipv6Groups(host);\n if (!groups) return true;\n const [first = 0] = groups;\n if (groups.slice(0, 7).every((group) => group === 0) && (groups[7] ?? 0) <= 1) return true; // :: и ::1\n if ((first & 0xfe00) === 0xfc00 || (first & 0xffc0) === 0xfe80 || (first & 0xff00) === 0xff00) return true;\n // IPv4 внутри IPv6: ::ffff:a.b.c.d и ::a.b.c.d.\n if (groups.slice(0, 5).every((group) => group === 0) && (groups[5] === 0xffff || groups[5] === 0)) {\n const [high = 0, low = 0] = groups.slice(6);\n return isPrivateIpv4([high >> 8, high & 0xff, low >> 8, low & 0xff]);\n }\n return false;\n}\n\nfunction isInternalHost(hostname: string): boolean {\n const host = hostname.toLowerCase().replace(/\\.$/, \"\");\n if (host.startsWith(\"[\")) return isPrivateIpv6(host.slice(1, -1));\n const ipv4 = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(host);\n if (ipv4) return isPrivateIpv4(ipv4.slice(1).map(Number));\n return !host.includes(\".\") || /\\.(?:localhost|local|internal|home\\.arpa)$/.test(host);\n}\n\n/** Можно ли скачать картинку по этому адресу. `trustedOrigin` — сайт подключения провайдера из настройки. */\nexport function isAllowedImageAddress(address: URL, trustedOrigin?: string): boolean {\n if (trustedOrigin !== undefined && address.origin === trustedOrigin) return true;\n return address.protocol === \"https:\" && !isInternalHost(address.hostname);\n}\n","import type { ImageFileFormat } from \"./image-catalog\";\n\n/** Постобработка готовой картинки перед сохранением: формат файла и предел большей стороны. */\nexport interface ImageOutputOptions {\n /** Формат файла; не указан — формат провайдера. Прозрачность держат `png` и `webp`. */\n format?: ImageFileFormat;\n /** Большая сторона не длиннее этого, пикселей; меньшая картинка не увеличивается. */\n maxSide?: number;\n}\n\nexport interface ProcessedImage {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n}\n\nexport const imageFileFormats: readonly ImageFileFormat[] = [\"png\", \"jpeg\", \"webp\"];\n\nconst mediaTypes: Record<ImageFileFormat, string> = { png: \"image/png\", jpeg: \"image/jpeg\", webp: \"image/webp\" };\nconst formatOf: Record<string, ImageFileFormat> = { \"image/png\": \"png\", \"image/jpeg\": \"jpeg\", \"image/webp\": \"webp\" };\n\n/** Ошибка настройки `output` — текст с именем поля. */\nexport function checkImageOutput(output: ImageOutputOptions | undefined, where: string): void {\n if (!output) return;\n if (output.format !== undefined && !imageFileFormats.includes(output.format)) {\n throw new Error(`Картинки: ${where}output.format — одно из ${imageFileFormats.join(\", \")}`);\n }\n if (output.maxSide !== undefined && !(Number.isInteger(output.maxSide) && output.maxSide > 0)) {\n throw new Error(`Картинки: ${where}output.maxSide — целое число больше нуля`);\n }\n}\n\n/**\n * Формат и размер через sharp. sharp грузится только здесь и только при первой обработке: приложение без `output` его\n * не загружает. Прозрачность сохраняется: PNG и WebP пишутся с альфой.\n */\nexport async function postprocessImage(bytes: Uint8Array, mediaType: string, output: ImageOutputOptions): Promise<ProcessedImage> {\n const { default: sharp } = await import(\"sharp\");\n const format = output.format ?? formatOf[mediaType] ?? \"png\";\n let image = sharp(bytes);\n if (output.maxSide !== undefined) image = image.resize({ width: output.maxSide, height: output.maxSide, fit: \"inside\", withoutEnlargement: true });\n const { data, info } = await image.toFormat(format).toBuffer({ resolveWithObject: true });\n return { bytes: new Uint8Array(data), mediaType: mediaTypes[format], width: info.width, height: info.height };\n}\n\n/** Размер PNG из заголовка (IHDR), без разбора картинки; не PNG или заголовок битый — `undefined`. */\nexport function pngSize(bytes: Uint8Array): { width: number; height: number } | undefined {\n if (bytes.length < 24) return undefined;\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const isHeader = String.fromCharCode(bytes[12]!, bytes[13]!, bytes[14]!, bytes[15]!) === \"IHDR\";\n if (view.getUint32(0) !== 0x89504e47 || !isHeader) return undefined;\n const width = view.getUint32(16);\n const height = view.getUint32(20);\n return width > 0 && height > 0 ? { width, height } : undefined;\n}\n","import { downloadGeneratedImage } from \"./download-image\";\nimport type { AspectRatio, ImageGenerationRequest, ImageGenerationResult } from \"./image-catalog\";\nimport { ImageGenerationError } from \"./image-generation-error\";\nimport type { ImageSetup, ImageStep } from \"./image-setup\";\nimport { waitForImageTask } from \"./image-task-polling\";\nimport { postprocessImage } from \"./postprocess-image\";\n\n/** Пауза или готовая картинка без вызова, который её ждёт, хранится столько: повтор найдёт её, потом память чистится. */\nconst FORGET_AFTER_MS = 60 * 60 * 1000;\n\n/** Одна картинка одного вызова: кто, где, что и по какой настройке. */\nexport interface ImageJobInput {\n ownerId: string;\n chatId: string;\n /** Номер картинки в вызове, с 1. */\n number: number;\n prompt: string;\n aspectRatio: AspectRatio;\n setup: ImageSetup;\n}\n\n/** Картинка у приложения: во вложениях чата (`id`) или у своего приёмника (`result` — его ответ). */\nexport interface DeliveredImage {\n id?: string;\n result?: unknown;\n mediaType: string;\n width: number;\n height: number;\n}\n\n/** Картинка пришла после срока вызова и уже сохранена в чат: приложение может показать её человеку. */\nexport interface LateImage {\n ownerId: string;\n chatId: string;\n number: number;\n attachmentId: string;\n mediaType: string;\n width: number;\n height: number;\n prompt: string;\n aspectRatio: AspectRatio;\n /** Модель картинки (первого шага). */\n modelId: string;\n /** Назначение, если приложение их задало. */\n purpose?: string;\n}\n\n/** Чем кончилось ожидание одной картинки для вызова. */\nexport type ImageJobOutcome =\n | { status: \"saved\"; image: DeliveredImage }\n | { status: \"failed\"; reason: string }\n /** Срок вызова вышел, задача ещё у провайдера: повтор того же вызова найдёт её. */\n | { status: \"pending\" }\n | { status: \"stopped\" };\n\n/** Скачанная картинка с размером: из файла, а не известен — по пропорции модели. */\nexport interface ReceivedImageFile {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n}\n\nexport interface ImageJobsOptions {\n /** Приёмник скачанной картинки: вложения чата или свой у приложения. */\n /** `signal` — задача остановлена: приёмник не записывает картинку. */\n receive(input: ImageJobInput, image: ReceivedImageFile, signal: AbortSignal): Promise<{ id?: string; result?: unknown }>;\n maxImageBytes: number;\n requestTimeoutMs: number;\n lateResultWindowMs: number;\n now: () => Date;\n /** Запись расхода шага: один раз на задачу провайдера. */\n record(input: ImageJobInput, step: ImageStep, costUsd: number): Promise<void>;\n onLateImage?: (image: LateImage) => void | Promise<void>;\n logError(message: string): void;\n hideSecrets(text: string): string;\n}\n\ninterface StepState {\n step: ImageStep;\n /** Id задачи в очереди провайдера: по нему повтор проверяет старую задачу. */\n taskId?: string;\n /** Задача могла уйти провайдеру: без ответа она всё равно оплачивается по цене модели. */\n sent: boolean;\n charged: boolean;\n /** Провайдер отказал до работы: платить не за что. */\n released: boolean;\n results?: ImageGenerationResult[];\n}\n\ntype Settled = { status: \"saved\"; image: DeliveredImage } | { status: \"failed\"; reason: string } | { status: \"paused\" };\n\ninterface ImageJob {\n /** Ключ повтора; есть только у задач с очередью — синхронную задачу проверить потом нечем. */\n key?: string;\n input: ImageJobInput;\n steps: StepState[];\n state: \"running\" | \"paused\" | \"saved\" | \"finished\";\n /** Задача скачивается и сохраняется: срок вызова её уже не обрывает. */\n saving: boolean;\n controller: AbortController;\n /** Дальше опрос не идёт: срок вызова плюс фоновое окно. */\n hardDeadline: number;\n waiters: number;\n settled: Promise<Settled>;\n settle(result: Settled): void;\n image?: DeliveredImage;\n forgetTimer?: ReturnType<typeof setTimeout>;\n}\n\n/** Срок фонового опроса вышел без результата. */\nconst EXPIRED = Symbol(\"expired\");\nconst STOPPED = Symbol(\"stopped\");\nconst DEADLINE = Symbol(\"deadline\");\n\nfunction whenStopped(signal: AbortSignal | undefined): Promise<typeof STOPPED> {\n return new Promise((resolve) => {\n if (!signal) return;\n if (signal.aborted) resolve(STOPPED);\n else signal.addEventListener(\"abort\", () => resolve(STOPPED), { once: true });\n });\n}\n\n/** Цена ответа провайдера; не сообщил — цена модели. */\nfunction costOf(results: readonly ImageGenerationResult[], fallbackUsd: number): number {\n const known = results.filter((result) => result.costUsd !== undefined);\n return known.length > 0 ? known.reduce((total, result) => total + result.costUsd!, 0) : fallbackUsd;\n}\n\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n\n/**\n * Задачи картинок, которые живут дольше вызова. Задача с очередью (KIE) после срока вызова опрашивается в фоне, а повтор\n * того же вызова (тот же человек, чат, назначение, описание, пропорция и номер) сначала берёт старую задачу: ждёт её,\n * проверяет или отдаёт уже сохранённую картинку. Каждая задача провайдера оплачивается ровно один раз: его ценой, а без\n * ответа («Стоп», срок, сбой) — ценой модели. Реестр — в памяти процесса.\n */\nexport function createImageJobs(options: ImageJobsOptions) {\n const jobs = new Set<ImageJob>();\n const byKey = new Map<string, ImageJob>();\n\n const keyOf = (input: ImageJobInput) =>\n JSON.stringify([input.ownerId, input.chatId, input.setup.purpose ?? input.setup.steps[0]!.profile.id, input.prompt, input.aspectRatio, input.number]);\n\n function forget(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n jobs.delete(job);\n if (job.key !== undefined && byKey.get(job.key) === job) byKey.delete(job.key);\n }\n\n function forgetLater(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n job.forgetTimer = setTimeout(() => forget(job), FORGET_AFTER_MS);\n (job.forgetTimer as { unref?: () => void }).unref?.();\n }\n\n async function charge(job: ImageJob, state: StepState, costUsd: number) {\n if (state.charged) return;\n state.charged = true;\n await options.record(job.input, state.step, costUsd);\n }\n\n /** Ушедшие и не оплаченные задачи — по цене модели: лучше записать лишнее, чем потерять. */\n const chargeOpen = (job: ImageJob) =>\n Promise.all(job.steps.filter((state) => state.sent && !state.charged && !state.released).map((state) => charge(job, state, state.step.priceUsd)));\n\n function request(job: ImageJob, state: StepState, sourceUrl: string | undefined, signal: AbortSignal): ImageGenerationRequest {\n const { step } = state;\n const { aspectRatio } = job.input;\n const { width, height } = step.sizes[aspectRatio];\n return {\n taskUUID: crypto.randomUUID(),\n providerModelId: step.providerModelId,\n prompt: step.prompt ?? job.input.prompt,\n width,\n height,\n aspectRatio: step.profile.aspectRatioValues?.[aspectRatio] ?? aspectRatio,\n ...(sourceUrl !== undefined && { imageUrl: sourceUrl }),\n ...(step.outputFormat && { outputFormat: step.outputFormat }),\n ...(step.providerSettings && { providerSettings: step.providerSettings }),\n abortSignal: signal,\n timeoutMs: options.requestTimeoutMs,\n };\n }\n\n async function runStep(job: ImageJob, state: StepState, sourceUrl: string | undefined, signal: AbortSignal): Promise<ImageGenerationResult[]> {\n const { step } = state;\n const refused = (error: unknown) => {\n if (error instanceof ImageGenerationError && error.refusedBeforeWork && !signal.aborted) {\n state.sent = false;\n state.released = true;\n }\n return error;\n };\n const queue = step.provider.queue;\n if (!queue) {\n state.sent = true;\n let results: ImageGenerationResult[];\n try {\n results = await step.provider.generate(request(job, state, sourceUrl, signal), step.connection);\n } catch (error) {\n throw refused(error);\n }\n await charge(job, state, costOf(results, step.priceUsd));\n return results;\n }\n\n // Задача уже у провайдера (повтор, фоновый опрос после паузы) — сначала проверка, без ожидания.\n const checkNow = state.taskId !== undefined;\n if (state.taskId === undefined) {\n // Прошлая отправка оборвалась без id и уже оплачена по цене модели: новая задача — новая оплата.\n state.charged = false;\n state.sent = true;\n try {\n state.taskId = await queue.submit(request(job, state, sourceUrl, signal), step.connection);\n } catch (error) {\n throw refused(error);\n }\n }\n const taskId = state.taskId;\n const outcome = await waitForImageTask({ check: (abortSignal) => queue.check(taskId, step.connection, abortSignal), deadline: job.hardDeadline, abortSignal: signal, checkNow });\n if (outcome.status === \"ready\") {\n await charge(job, state, costOf(outcome.results, step.priceUsd));\n return outcome.results;\n }\n if (outcome.status === \"failed\") {\n await charge(job, state, outcome.costUsd ?? step.priceUsd);\n throw new ImageGenerationError(outcome.reason, { refusedBeforeWork: false });\n }\n throw EXPIRED;\n }\n\n /** Адрес картинки шага: у модели с основой первым идёт фон без предмета, картинка — следующая. */\n function resultUrl(state: StepState): string {\n const result = state.results?.[state.step.profile.firstResultIsBase ? 1 : 0];\n if (!result) throw new ImageGenerationError(\"Провайдер не вернул картинку\", { refusedBeforeWork: false });\n return result.url;\n }\n\n async function save(job: ImageJob, url: string, signal: AbortSignal): Promise<DeliveredImage> {\n const { input } = job;\n const last = job.steps.at(-1)!.step;\n const output = input.setup.output;\n const file = await downloadGeneratedImage({\n url,\n ...(last.downloadFetch && { fetch: last.downloadFetch }),\n maxImageBytes: options.maxImageBytes,\n ...(last.trustedOrigin !== undefined && { trustedOrigin: last.trustedOrigin }),\n ...(output && { transform: (bytes: Uint8Array, mediaType: string) => postprocessImage(bytes, mediaType, output) }),\n abortSignal: signal,\n });\n const planned = input.setup.steps[0]!.sizes[input.aspectRatio];\n const image = { bytes: file.bytes, mediaType: file.mediaType, width: file.width ?? planned.width, height: file.height ?? planned.height };\n const received = await options.receive(input, image, signal);\n return { ...received, mediaType: image.mediaType, width: image.width, height: image.height };\n }\n\n async function drive(job: ImageJob, signal: AbortSignal) {\n let saving = false;\n try {\n let sourceUrl: string | undefined;\n for (const [index, state] of job.steps.entries()) {\n state.results ??= await runStep(job, state, sourceUrl, signal);\n if (signal.aborted) return;\n if (index < job.steps.length - 1) sourceUrl = state.results[0]?.url;\n }\n saving = job.saving = true;\n const image = await save(job, resultUrl(job.steps.at(-1)!), signal);\n if (signal.aborted) return;\n finish(job, { status: \"saved\", image });\n } catch (error) {\n // «Стоп» или пауза: оплату и состояние ведёт `pause`.\n if (signal.aborted) return;\n if (error === EXPIRED) return void (await pause(job));\n let reason: string;\n if (error instanceof ImageGenerationError) reason = options.hideSecrets(error.reason);\n else if (saving) {\n options.logError(`Картинки: не удалось сохранить картинку: ${errorText(error)}`);\n reason = \"Картинка не сохранилась\";\n } else {\n options.logError(`Картинки: сбой провайдера ${job.steps.find((state) => !state.results)?.step.provider.id ?? \"\"}: ${errorText(error)}`);\n reason = \"Провайдер не смог создать картинку\";\n }\n await chargeOpen(job);\n finish(job, { status: \"failed\", reason });\n }\n }\n\n function finish(job: ImageJob, result: Exclude<Settled, { status: \"paused\" }>) {\n if (result.status === \"saved\" && job.waiters === 0 && job.key !== undefined) {\n // Вызов, который ждал, уже ушёл: картинка ждёт повтора, а приложение узнаёт о ней сразу.\n job.state = \"saved\";\n job.image = result.image;\n forgetLater(job);\n // Свой приёмник картинку уже получил; о вложении чата приложение узнаёт здесь.\n if (result.image.id !== undefined) void notifyLate(job, result.image.id, result.image);\n } else {\n job.state = \"finished\";\n forget(job);\n }\n job.settle(result);\n }\n\n async function notifyLate(job: ImageJob, attachmentId: string, image: DeliveredImage) {\n const { input } = job;\n try {\n await options.onLateImage?.({\n ownerId: input.ownerId,\n chatId: input.chatId,\n number: input.number,\n attachmentId,\n mediaType: image.mediaType,\n width: image.width,\n height: image.height,\n prompt: input.prompt,\n aspectRatio: input.aspectRatio,\n modelId: input.setup.steps[0]!.profile.id,\n ...(input.setup.purpose !== undefined && { purpose: input.setup.purpose }),\n });\n } catch (error) {\n options.logError(`Картинки: onLateImage не принял картинку: ${errorText(error)}`);\n }\n }\n\n /**\n * Опрос остановлен («Стоп», срок): ушедшие задачи оплачены по цене модели. Задача с очередью ждёт повтора, синхронная\n * забывается — проверить её нечем.\n */\n async function pause(job: ImageJob) {\n if (job.state !== \"running\") return;\n job.state = job.key !== undefined ? \"paused\" : \"finished\";\n job.controller.abort(new DOMException(\"paused\", \"AbortError\"));\n await chargeOpen(job);\n if (job.state === \"paused\") forgetLater(job);\n else forget(job);\n job.settle({ status: \"paused\" });\n }\n\n function start(job: ImageJob) {\n clearTimeout(job.forgetTimer);\n job.state = \"running\";\n job.saving = false;\n job.controller = new AbortController();\n job.hardDeadline = Date.now() + options.requestTimeoutMs + options.lateResultWindowMs;\n job.settled = new Promise((resolve) => (job.settle = resolve));\n void drive(job, job.controller.signal);\n }\n\n /** Срок вызова: только у задач с очередью и только пока картинка не готова. */\n function deadline(job: ImageJob): { promise: Promise<typeof DEADLINE>; cancel(): void } {\n if (job.key === undefined) return { promise: new Promise(() => {}), cancel() {} };\n let timer: ReturnType<typeof setTimeout>;\n const promise = new Promise<typeof DEADLINE>((resolve) => (timer = setTimeout(() => resolve(DEADLINE), options.requestTimeoutMs)));\n return { promise, cancel: () => clearTimeout(timer) };\n }\n\n /** Вызов ждёт картинку: готова, провал, «Стоп» или срок вызова. Последний ушедший без картинки останавливает опрос сам. */\n async function attend(job: ImageJob, abortSignal: AbortSignal | undefined): Promise<ImageJobOutcome> {\n job.waiters++;\n const stopped = whenStopped(abortSignal);\n const timer = deadline(job);\n let outcome = await Promise.race([job.settled, stopped, timer.promise]);\n if (outcome === DEADLINE && job.saving) outcome = await Promise.race([job.settled, stopped]);\n timer.cancel();\n job.waiters--;\n if (outcome === STOPPED) {\n if (job.waiters === 0) await pause(job);\n return { status: \"stopped\" };\n }\n if (outcome === DEADLINE) {\n // Фонового окна нет или оно кончилось: опрос останавливается вместе с вызовом.\n if (job.waiters === 0 && Date.now() >= job.hardDeadline) await pause(job);\n return { status: \"pending\" };\n }\n if (outcome.status === \"paused\") return { status: \"pending\" };\n return outcome;\n }\n\n return {\n /**\n * Картинка для вызова: уже сохранённая с прошлого раза (сразу, без оплаты), старая задача (ждать или проверить) или\n * новая. Найденная готовая картинка «израсходована»: следующий такой же вызов — новая.\n */\n async obtain(input: ImageJobInput, abortSignal: AbortSignal | undefined): Promise<ImageJobOutcome> {\n const queued = input.setup.steps.some((step) => step.provider.queue !== undefined);\n const key = queued ? keyOf(input) : undefined;\n const existing = key !== undefined ? byKey.get(key) : undefined;\n if (existing?.state === \"saved\") {\n forget(existing);\n existing.state = \"finished\";\n return { status: \"saved\", image: existing.image! };\n }\n let job = existing;\n if (job?.state === \"paused\") start(job);\n if (!job) {\n job = {\n ...(key !== undefined && { key }),\n input,\n steps: input.setup.steps.map((step) => ({ step, sent: false, charged: false, released: false })),\n waiters: 0,\n } as Omit<ImageJob, \"state\" | \"saving\" | \"controller\" | \"hardDeadline\" | \"settled\" | \"settle\"> as ImageJob;\n jobs.add(job);\n if (key !== undefined) byKey.set(key, job);\n start(job);\n }\n return attend(job, abortSignal);\n },\n\n /** Задачи в работе, ещё не оплаченные: их цена по профилям — у человека и в этом чате. */\n pendingUsd(ownerId: string, chatId: string): { user: number; chat: number } {\n let user = 0;\n let chat = 0;\n for (const job of jobs) {\n if (job.state !== \"running\" || job.input.ownerId !== ownerId) continue;\n const open = job.steps.reduce((total, state) => total + (state.charged || state.released ? 0 : state.step.priceUsd), 0);\n user += open;\n if (job.input.chatId === chatId) chat += open;\n }\n return { user, chat };\n },\n };\n}\n\nexport type ImageJobs = ReturnType<typeof createImageJobs>;\n","import { SpendingLimitReachedError } from \"@ykurilov/agent-chat-core\";\nimport type { ChatStorage, SpendingLimits } from \"@ykurilov/agent-chat-core\";\nimport { downloadGeneratedImage } from \"../download-image\";\nimport type { DownloadedImage } from \"../download-image\";\nimport { aspectRatios } from \"../image-catalog\";\nimport type { AspectRatio, ImageCatalog, ImageGenerationRequest, ImageGenerationResult, ImageQuality, ImageSize, ImageTaskState } from \"../image-catalog\";\nimport { ImageGenerationError } from \"../image-generation-error\";\nimport { resolveImageSetups } from \"../image-setup\";\nimport type { ImagePurpose, ImageSetup, ImageStep } from \"../image-setup\";\nimport { postprocessImage } from \"../postprocess-image\";\nimport type { ImageOutputOptions } from \"../postprocess-image\";\nimport { ImageJobKeyTakenError, isOpenImageJob, memoryImageJobStore } from \"./image-job-store\";\nimport type { ImageJobCharge, ImageJobFilter, ImageJobPatch, ImageJobRecord, ImageJobStatus, ImageJobStep, ImageJobStore, ImageJobTask } from \"./image-job-store\";\nimport { createTurnCounter } from \"./turn-counter\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 3_000;\nconst DEFAULT_ATTEMPT_TIMEOUT_MS = 150_000;\nconst DEFAULT_LATE_RESULT_WINDOW_MS = 10 * 60 * 1000;\n/** В позднем окне проверки реже: картинка уже опоздала, частый опрос ничего не ускорит. */\nconst MIN_LATE_POLL_INTERVAL_MS = 10_000;\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\nconst DEFAULT_DOWNLOAD_ATTEMPTS = 3;\nconst DEFAULT_LEASE_MS = 30_000;\nconst DEFAULT_ACCEPT_WAIT_MS = 5_000;\nconst DEFAULT_FORGET_FINISHED_AFTER_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_MAX_IMAGE_BYTES = 10 * 1024 * 1024;\n/** Уборка законченных задач — не чаще этого. */\nconst PRUNE_EVERY_MS = 60 * 60 * 1000;\n/** Условная запись не прошла столько раз подряд — задачу кто-то правит без конца, это сбой. */\nconst MAX_WRITE_TRIES = 10;\n/** Место всё время занимают заново (другие копии сервера заказывают на него же) — столько попыток занять его. */\nconst MAX_PLACE_TRIES = 5;\nconst LIMIT_REACHED = \"лимит расходов исчерпан\";\nconst QUEUE_STOPPED = \"очередь остановлена\";\n\n/** Готовая картинка для приёмника. Размер — из файла; не известен — по пропорции модели. */\nexport interface GeneratedImage {\n bytes: Uint8Array;\n mediaType: string;\n width: number;\n height: number;\n /** Модель картинки (первого шага). */\n modelId: string;\n}\n\n/** Задача картинки, какой её видит приложение. */\nexport interface ImageJob {\n id: string;\n key: string;\n ownerId: string;\n chatId: string;\n turnId?: string;\n group?: string;\n /** Назначение; нет — очередь без назначений. */\n purpose?: string;\n prompt: string;\n aspectRatio: AspectRatio;\n /** Данные приложения из заказа, как были. */\n data?: unknown;\n status: ImageJobStatus;\n /** Почему не получилась. */\n reason?: string;\n /** Ответ приёмника. */\n result?: unknown;\n attempt: number;\n /** Попытки кончились, но задача у провайдера ещё опрашивается до этого времени: может прийти поздно. */\n lateUntil?: string;\n createdAt: string;\n updatedAt: string;\n finishedAt?: string;\n}\n\n/**\n * Доставка ещё нужна? Задачу могут вытеснить новым заказом на то же место, пока приёмник пишет файл: запись должна быть\n * условной — перед ней спросить `isCurrent()`, а долгую работу обрывать по `signal`.\n */\nexport interface ImageDeliveryContext {\n /** Срабатывает, когда доставка этой копией больше не нужна: задачу вытеснили, повторили или очередь остановлена. */\n signal: AbortSignal;\n /** Задача всё ещё наша и ждёт этой картинки (перечитывает хранилище). `false` — не записывать, вернуть `false`. */\n isCurrent(): Promise<boolean>;\n}\n\n/** Заказ не принят: лимит расходов, предел за ход, неверный заказ. Задачи нет, денег не потрачено. */\nexport interface ImageOrderRefusal {\n status: \"refused\";\n reason: string;\n}\n\nexport interface ImageOrder {\n ownerId: string;\n chatId: string;\n /** Номер хода агента (`model.turnId` в `agent.tools`): по нему считается `maxImagesPerTurn`. */\n turnId?: string;\n /** Место картинки (например, путь файла): новый заказ на то же место у того же владельца вытесняет прежний. Нет — своё у каждого заказа. */\n key?: string;\n /** Группа для списка: например, приложение, в которое идут картинки. */\n group?: string;\n /** Назначение из `purposes`; по умолчанию первое. */\n purpose?: string;\n prompt: string;\n /** По умолчанию `1:1`. */\n aspectRatio?: AspectRatio;\n /** Данные приложения (путь, размер): приёмник получит их в `job.data`. Только JSON. */\n data?: unknown;\n}\n\n/** Назначение очереди: как у `generate_images`, но описание не обязательно (его читает только агент). */\nexport type ImageQueuePurpose = Omit<ImagePurpose, \"description\"> & { description?: string };\n\nexport interface CreateImageQueueOptions {\n /** Окружение сервера: ключ провайдера берётся только отсюда, по `apiKeyEnv`. */\n env: Record<string, string | undefined>;\n catalog?: ImageCatalog;\n /** Модель картинок из каталога; по умолчанию `gpt-image-2`. С `purposes` не задаётся. */\n model?: string;\n provider?: string;\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024; по умолчанию — размер модели. */\n size?: ImageSize;\n transparentBackground?: boolean;\n /** Постобработка перед приёмником: формат файла и предел большей стороны (sharp). */\n output?: ImageOutputOptions;\n purposes?: Record<string, ImageQueuePurpose>;\n /**\n * Приёмник готовой картинки: записать файл, положить во вложения, обновить экран. Ответ (JSON) сохраняется в задаче;\n * `false` — картинка больше не нужна (место занято другим), задача «отброшена». Ошибка — задача «не получилась», и\n * «Повторить» доставит ту же картинку без новой оплаты. Приёмника зовут хотя бы один раз: после сбоя сервера между\n * приёмником и отметкой «готово» — ещё раз, поэтому повтор должен быть безопасен. Пока приёмник работает, задачу могут\n * вытеснить: запись делать условной, по `context.isCurrent()`.\n */\n deliver(image: GeneratedImage, job: ImageJob, context: ImageDeliveryContext): unknown;\n /** Где лежат задачи; по умолчанию в памяти (до перезапуска). `sqliteImageJobStore` или `postgresImageJobStore` — переживают перезапуск. */\n store?: ImageJobStore;\n /** Расход в лимиты: запись на каждую оплаченную задачу провайдера и проверка лимита до заказа. */\n spending?: {\n /** `hasUsageRecord` — по желанию: после сбоя вставки отличает «уже записано» от «не записалось». */\n storage: Pick<ChatStorage, \"insertUsageRecord\" | \"hasUsageRecord\">;\n limits?: Pick<SpendingLimits, \"assertWithinLimit\" | \"afterSpending\">;\n };\n /** Шаг проверки задачи у провайдера с очередью; по умолчанию 3000 мс. */\n pollIntervalMs?: number;\n /** Срок одной попытки; по умолчанию 150 000 мс. */\n attemptTimeoutMs?: number;\n /**\n * Сколько попыток; по умолчанию 1. Следующая попытка после срока проверяет ту же задачу (новой оплаты нет), после\n * провала у провайдера — заказывает заново.\n */\n attempts?: number;\n /** Попытки кончились, а задача у провайдера ещё идёт: сколько её ждать; по умолчанию 10 минут, 0 — не ждать. */\n lateResultWindowMs?: number;\n /** Шаг проверки в позднем окне; по умолчанию 10 000 мс (или шаг опроса, если он больше). */\n latePollIntervalMs?: number;\n /** Срок одного обращения к провайдеру с очередью (отправка, проверка); по умолчанию 30 000 мс. */\n callTimeoutMs?: number;\n /** Сколько раз скачивать готовую картинку с того же адреса, прежде чем сдаться; по умолчанию 3. */\n downloadAttempts?: number;\n /**\n * Провайдер без очереди (Runware) не сообщает «принял»: заказ ждёт его ответа не дольше этого, по умолчанию 5000 мс.\n * Отказ (неверный ключ, нет денег) приходит раньше — и заказ сразу отвечает «не получилась».\n */\n acceptWaitMs?: number;\n /** Аренда задачи копией сервера; по умолчанию 30 000 мс. Упавшую копию заменяет другая после этого срока. */\n leaseMs?: number;\n /** Не больше стольких заказов за ход (`turnId`); по умолчанию без предела. */\n maxImagesPerTurn?: number;\n /** Законченные задачи удаляются из хранилища через столько; по умолчанию сутки. */\n forgetFinishedAfterMs?: number;\n /** Предел одного файла; по умолчанию 10 МБ. */\n maxImageBytes?: number;\n /**\n * Свой `fetch` приложения: запросы к провайдеру и скачивание картинок. Это граница доверия приложения: закрепить\n * соединение за проверенным адресом (от подмены DNS) должен он сам (`createPinnedFetch` из\n * `@ykurilov/agent-chat-web-tools/network`). Без него картинку со склада провайдера качает закреплённый `fetch`.\n */\n fetch?: typeof globalThis.fetch;\n logError?: (message: string) => void;\n now?: () => Date;\n}\n\nexport interface ImageQueue {\n /** Заказ. Ответ — как только задача ушла провайдеру (или сразу не получилась); картинка придёт в `deliver` позже. */\n order(input: ImageOrder): Promise<ImageJob | ImageOrderRefusal>;\n /**\n * «Повторить» последнюю задачу места. Не получилась — новый запуск: идущая у провайдера задача проверяется, а не\n * заказывается заново; оплаченная, но не доставленная картинка скачивается с того же адреса. Задача идёт или уже\n * готова — она же, без нового заказа. Нет задачи — `null`.\n */\n retry(input: { ownerId: string; key: string }): Promise<ImageJob | ImageOrderRefusal | null>;\n get(id: string): Promise<ImageJob | null>;\n /** По порядку создания. */\n list(filter: ImageJobFilter): Promise<ImageJob[]>;\n /** Заказанное и ещё не оплаченное этой копией сервера: у человека и в чате. Для проверки лимитов. */\n pendingUsd(ownerId: string, chatId: string): { user: number; chat: number };\n /** Подобрать незаконченные задачи из хранилища и обходить его дальше. Зовётся само при создании; повтор безвреден. */\n start(): Promise<void>;\n /** Остановить опрос и таймеры, отпустить аренду. Задачи остаются в хранилище и продолжаются следующим `start`. */\n stop(): Promise<void>;\n}\n\n/** Запуск задачи этой копией сервера: опрос, деньги, доставка. */\ninterface Runtime {\n ownerId: string;\n chatId: string;\n setup: ImageSetup;\n /** Бронь до записи задачи: цена того, что предстоит заказать. */\n reservedUsd: number;\n record?: ImageJobRecord;\n controller: AbortController;\n /** Записи задачи идут по одной. */\n chain: Promise<unknown>;\n /** Задача ушла провайдеру (или запуск кончился): заказ можно отдать приложению. */\n sent: { promise: Promise<void>; resolve(): void };\n done?: Promise<void>;\n leaseTimer?: ReturnType<typeof setTimeout>;\n ended: boolean;\n /** Первая проверка — сразу: задача уже у провайдера (перезапуск, новая попытка, «Повторить»). */\n checkNow: boolean;\n /** «Повторить» после сбоя доставки: тот же адрес снова не скачался — заказать последний шаг заново. */\n reorderOnDeliveryFailure: boolean;\n}\n\ntype StepOutcome = { kind: \"ready\" } | { kind: \"failed\"; reason: string } | { kind: \"refused\"; reason: string } | { kind: \"timeout\" };\n\n/** Запуск закончен: вытеснен, «Повторить», чужая аренда или `stop()`. Дальше он ничего не заказывает и не пишет. */\nclass RunEnded extends Error {\n constructor() {\n super(\"Запуск задачи картинки закончен\");\n }\n}\nconst RUN_ENDED = new RunEnded();\n\n/** Обращение к провайдеру не уложилось в свой срок. */\nclass CallTimedOut extends Error {\n constructor(readonly ms: number) {\n super(`Провайдер не ответил за ${Math.round(ms / 1000)} с`);\n }\n}\n\nconst iso = (ms: number) => new Date(ms).toISOString();\nconst seconds = (ms: number) => Math.round(ms / 1000);\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\nconst unref = (timer: ReturnType<typeof setTimeout>) => (timer as { unref?: () => void }).unref?.();\nconst hasUnrecorded = (steps: readonly ImageJobStep[]) => steps.some((step) => step.charges.some((charge) => !charge.recorded));\nconst isOpen = isOpenImageJob;\nconst withStep = (steps: readonly ImageJobStep[], index: number, change: (step: ImageJobStep) => ImageJobStep) => steps.map((step, at) => (at === index ? change(step) : step));\n\nfunction withoutTask(step: ImageJobStep): ImageJobStep {\n const { task: _task, ...rest } = step;\n return rest;\n}\n\nconst chargeOf = (task: ImageJobTask, costUsd: number): ImageJobCharge => ({ usageId: task.usageId, modelId: task.modelId, providerId: task.providerId, costUsd, recorded: false });\n\n/** Цена ответа провайдера; не сообщил — цена модели. */\nfunction costOf(results: readonly ImageGenerationResult[], fallbackUsd: number): number {\n const known = results.filter((result) => result.costUsd !== undefined);\n return known.length > 0 ? known.reduce((total, result) => total + result.costUsd!, 0) : fallbackUsd;\n}\n\n/** Отметка «расход записан»; задача без работы — заодно без аренды. Отметка уже есть — `null`. */\nfunction markRecorded(record: ImageJobRecord, usageId: string): ImageJobPatch | null {\n let found = false;\n const steps = record.steps.map((step) => ({\n ...step,\n charges: step.charges.map((charge) => {\n if (charge.usageId !== usageId || charge.recorded) return charge;\n found = true;\n return { ...charge, recorded: true };\n }),\n }));\n if (!found) return null;\n if (isOpen(record)) return { steps };\n const left = hasUnrecorded(steps);\n return { steps, active: left, ...(!left && { leaseOwner: undefined, leaseUntil: undefined }) };\n}\n\nfunction view(record: ImageJobRecord): ImageJob {\n return {\n id: record.id,\n key: record.key,\n ownerId: record.ownerId,\n chatId: record.chatId,\n ...(record.turnId !== undefined && { turnId: record.turnId }),\n ...(record.group !== undefined && { group: record.group }),\n ...(record.purpose !== \"\" && { purpose: record.purpose }),\n prompt: record.prompt,\n aspectRatio: record.aspectRatio,\n ...(record.data !== undefined && { data: record.data }),\n status: record.status,\n ...(record.reason !== undefined && { reason: record.reason }),\n ...(record.result !== undefined && { result: record.result }),\n attempt: record.attempt,\n ...(record.lateUntil !== undefined && { lateUntil: record.lateUntil }),\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n ...(record.finishedAt !== undefined && { finishedAt: record.finishedAt }),\n };\n}\n\nconst refused = (reason: string): ImageOrderRefusal => ({ status: \"refused\", reason });\n\nfunction checkOptions(options: CreateImageQueueOptions) {\n const positive = [\"pollIntervalMs\", \"attemptTimeoutMs\", \"latePollIntervalMs\", \"callTimeoutMs\", \"acceptWaitMs\", \"leaseMs\", \"forgetFinishedAfterMs\", \"maxImageBytes\"] as const;\n for (const name of positive) {\n const value = options[name];\n if (value !== undefined && !(Number.isFinite(value) && value > 0)) throw new Error(`Картинки: ${name} — нужно число больше нуля`);\n }\n for (const name of [\"attempts\", \"downloadAttempts\", \"maxImagesPerTurn\"] as const) {\n const value = options[name];\n if (value !== undefined && !(Number.isInteger(value) && value >= 1)) throw new Error(`Картинки: ${name} — целое число от 1`);\n }\n const window = options.lateResultWindowMs;\n if (window !== undefined && !(Number.isFinite(window) && window >= 0)) throw new Error(\"Картинки: lateResultWindowMs — число не меньше нуля\");\n if (typeof options.deliver !== \"function\") throw new Error(\"Картинки: deliver — нужна функция-приёмник\");\n}\n\n/**\n * Очередь картинок для своих инструментов приложения: заказ отвечает сразу, как задача ушла провайдеру, картинка\n * приходит в `deliver` позже. Задачи лежат в хранилище и переживают перезапуск: старт подбирает незаконченные,\n * доопрашивает провайдера и доставляет. Каждая задача провайдера оплачивается ровно один раз (постоянный id записи\n * расхода), готовая картинка не заказывается заново — только скачивается.\n */\nexport function createImageQueue(options: CreateImageQueueOptions): ImageQueue {\n checkOptions(options);\n const { purposes: queuePurposes, ...choice } = options;\n const purposes: Record<string, ImagePurpose> | undefined =\n queuePurposes && Object.fromEntries(Object.entries(queuePurposes).map(([name, purpose]) => [name, { ...purpose, description: purpose.description?.trim() || name }]));\n const setups = resolveImageSetups({ ...choice, ...(purposes && { purposes }) });\n const firstPurpose = setups.hasPurposes ? [...setups.byPurpose.keys()][0]! : \"\";\n const store = options.store ?? memoryImageJobStore();\n const now = options.now ?? (() => new Date());\n const nowMs = () => now().getTime();\n const hide = setups.hideSecrets;\n const logError = (message: string) => options.logError?.(hide(message));\n\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const attemptTimeoutMs = options.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;\n const attempts = options.attempts ?? 1;\n const lateResultWindowMs = options.lateResultWindowMs ?? DEFAULT_LATE_RESULT_WINDOW_MS;\n const latePollIntervalMs = options.latePollIntervalMs ?? Math.max(pollIntervalMs, MIN_LATE_POLL_INTERVAL_MS);\n const callTimeoutMs = options.callTimeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const downloadAttempts = options.downloadAttempts ?? DEFAULT_DOWNLOAD_ATTEMPTS;\n const leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;\n const acceptWaitMs = options.acceptWaitMs ?? DEFAULT_ACCEPT_WAIT_MS;\n const forgetFinishedAfterMs = options.forgetFinishedAfterMs ?? DEFAULT_FORGET_FINISHED_AFTER_MS;\n const maxImageBytes = options.maxImageBytes ?? DEFAULT_MAX_IMAGE_BYTES;\n const timeoutReason = `Картинка не успела за ${seconds(attemptTimeoutMs)} с`;\n\n /** Эта копия сервера: так помечена её аренда. */\n const me = crypto.randomUUID();\n /** Брони и запуски этой копии по порядку появления: проверка лимита видит тех, кто раньше. */\n const runtimes = new Set<Runtime>();\n const byJob = new Map<string, Runtime>();\n const keyLocks = new Map<string, Promise<unknown>>();\n const retries = new Map<string, Promise<ImageJob | ImageOrderRefusal | null>>();\n const turns = createTurnCounter(options.maxImagesPerTurn);\n let started = false;\n let stopped = false;\n let scanning: Promise<void> | undefined;\n let scanTimer: ReturnType<typeof setTimeout> | undefined;\n let lastPruneAt = Number.NEGATIVE_INFINITY;\n\n function sleep(ms: number, signal: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal.aborted) return reject(RUN_ENDED);\n const onAbort = () => {\n clearTimeout(timer);\n reject(RUN_ENDED);\n };\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n unref(timer);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n\n const throwIfEnded = (rt: Runtime) => {\n if (rt.controller.signal.aborted) throw RUN_ENDED;\n };\n\n /** Обращение к провайдеру со своим сроком; запуск закончился — обрывается и оно. */\n async function callProvider<T>(rt: Runtime, ms: number, call: (signal: AbortSignal) => Promise<T>): Promise<T> {\n const timeout = new AbortController();\n const timer = setTimeout(() => timeout.abort(new DOMException(\"timeout\", \"TimeoutError\")), ms);\n unref(timer);\n try {\n return await call(AbortSignal.any([rt.controller.signal, timeout.signal]));\n } catch (error) {\n if (timeout.signal.aborted && !rt.controller.signal.aborted) throw new CallTimedOut(ms);\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n // --- запись ---\n\n /**\n * Условная запись с перечитыванием: правка считается от свежей записи. Запись сменилась так, что она больше не «наша»\n * (`stillOurs`), — `null`. Правке нечего делать — запись как есть.\n */\n async function updateJob(start: ImageJobRecord, patchOf: (record: ImageJobRecord) => ImageJobPatch | null, stillOurs: (record: ImageJobRecord) => boolean): Promise<ImageJobRecord | null> {\n let current = start;\n for (let tries = 0; tries < MAX_WRITE_TRIES; tries++) {\n const patch = patchOf(current);\n if (!patch) return current;\n const next = await store.update(current.id, { ...patch, updatedAt: iso(nowMs()) }, current.version);\n if (next) return next;\n const fresh = await store.get(current.id);\n if (!fresh || !stillOurs(fresh)) return null;\n current = fresh;\n }\n throw new Error(`Картинки: задачу ${start.id} не удалось записать: её всё время правят`);\n }\n\n /** Запись запуска: по одной, только пока запуск свой (тот же номер и наша аренда). Чужой — запуск кончается. */\n function write(rt: Runtime, patchOf: (record: ImageJobRecord) => ImageJobPatch | null): Promise<ImageJobRecord> {\n const run = async () => {\n throwIfEnded(rt);\n const record = rt.record!;\n const next = await updateJob(record, patchOf, (fresh) => fresh.run === record.run && fresh.leaseOwner === me);\n if (!next) {\n rt.controller.abort(RUN_ENDED);\n throw RUN_ENDED;\n }\n rt.record = next;\n return next;\n };\n const result = rt.chain.then(run, run);\n rt.chain = result.catch(() => {});\n return result;\n }\n\n // --- деньги ---\n\n /** Ошибка базы «такой id уже есть» (PostgreSQL, SQLite), в том числе завёрнутая в `cause`. */\n function isDuplicateKey(error: unknown): boolean {\n for (let current = error, depth = 0; current && typeof current === \"object\" && depth < 5; current = (current as { cause?: unknown }).cause, depth++) {\n const code = (current as { code?: unknown }).code;\n if (code === \"23505\" || code === \"SQLITE_CONSTRAINT_PRIMARYKEY\" || code === \"SQLITE_CONSTRAINT_UNIQUE\") return true;\n }\n return false;\n }\n\n /**\n * Запись расхода с постоянным id: вторая вставка того же id отвергается базой, расход не задваивается. `true` — расход\n * в базе (вставлен сейчас или был раньше); `false` — не записан (временный сбой), его допишет следующий обход.\n */\n async function insertUsage(record: ImageJobRecord, charge: ImageJobCharge): Promise<boolean> {\n const spending = options.spending;\n if (!spending) return true;\n try {\n await spending.storage.insertUsageRecord({\n id: charge.usageId,\n ownerId: record.ownerId,\n chatId: record.chatId,\n messageId: null,\n source: \"images\",\n modelId: charge.modelId,\n providerId: charge.providerId,\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n costUsd: charge.costUsd,\n createdAt: iso(nowMs()),\n });\n } catch (error) {\n const known = await (spending.storage.hasUsageRecord?.(charge.usageId) ?? Promise.resolve(isDuplicateKey(error))).catch(() => false);\n if (known) return true;\n logError(`Картинки: расход ${charge.modelId} (${charge.usageId}) пока не записан, запись повторится: ${errorText(error)}`);\n return false;\n }\n const afterSpending = spending.limits?.afterSpending;\n // Пороги — фоном: задачу они не задерживают.\n if (afterSpending) {\n void afterSpending\n .call(spending.limits, { userId: record.ownerId, chatId: record.chatId })\n .catch((error: unknown) => logError(`Картинки: лимиты после расхода (чат ${record.chatId}): ${errorText(error)}`));\n }\n return true;\n }\n\n const unrecorded = (record: ImageJobRecord) => record.steps.flatMap((step) => step.charges.filter((charge) => !charge.recorded));\n\n async function recordCharges(rt: Runtime) {\n for (const charge of unrecorded(rt.record!)) {\n if (await insertUsage(rt.record!, charge)) await write(rt, (record) => markRecorded(record, charge.usageId));\n }\n }\n\n /** Расходы задачи без запуска (вытеснена, подобрана после сбоя); в конце — задача без работы и аренды. */\n async function settleClosed(record: ImageJobRecord) {\n let current: ImageJobRecord | null = record;\n for (const charge of unrecorded(record)) {\n if (!(await insertUsage(record, charge))) continue;\n current = await updateJob(current!, (fresh) => markRecorded(fresh, charge.usageId), () => true);\n if (!current) return;\n }\n await updateJob(current!, (fresh) => (isOpen(fresh) || (!fresh.active && fresh.leaseOwner === undefined) ? null : { active: hasUnrecorded(fresh.steps), leaseOwner: undefined, leaseUntil: undefined }), () => true);\n }\n\n /** Оплата ушедшей задачи шага: ценой провайдера, а без неё — ценой модели. */\n async function chargeTask(rt: Runtime, index: number, costUsd?: number) {\n await write(rt, (record) => {\n const task = record.steps[index]!.task;\n if (!task) return null;\n return { steps: withStep(record.steps, index, (step) => ({ ...withoutTask(step), charges: [...step.charges, chargeOf(task, costUsd ?? task.priceUsd)] })) };\n });\n await recordCharges(rt);\n }\n\n /** Шаг готов: адреса и оплата — одной записью, дальше шаг только скачивается. */\n async function acceptResults(rt: Runtime, index: number, results: readonly ImageGenerationResult[]) {\n const urls = results.map((result) => result.url);\n await write(rt, (record) => {\n const task = record.steps[index]!.task;\n if (!task) return null;\n return { steps: withStep(record.steps, index, (step) => ({ ...withoutTask(step), urls, charges: [...step.charges, chargeOf(task, costOf(results, task.priceUsd))] })) };\n });\n await recordCharges(rt);\n }\n\n /**\n * Закрыть задачу без её запуска: вытеснена новым заказом или настройка сменилась. Ушедшие задачи провайдера —\n * по цене модели; номер запуска растёт, и прежний запуск больше ничего не делает.\n */\n async function closeJob(record: ImageJobRecord, status: \"superseded\" | \"failed\", reason?: string) {\n const closed = await updateJob(\n record,\n (fresh) => {\n if (!isOpen(fresh)) return null;\n const steps = fresh.steps.map((step) => (step.task ? { ...withoutTask(step), charges: [...step.charges, chargeOf(step.task, step.task.priceUsd)] } : step));\n return { status, reason, run: fresh.run + 1, steps, lateUntil: undefined, active: hasUnrecorded(steps), finishedAt: iso(nowMs()), leaseOwner: undefined, leaseUntil: undefined };\n },\n () => true,\n );\n if (!closed) return;\n byJob.get(record.id)?.controller.abort(RUN_ENDED);\n await settleClosed(closed);\n }\n\n function openUsd(rt: Runtime): number {\n const record = rt.record;\n if (!record) return rt.reservedUsd;\n if (!isOpen(record)) return 0;\n const late = record.lateUntil !== undefined;\n const last = record.steps.length - 1;\n return record.steps.reduce((total, step, index) => {\n // «Повторить» после сбоя доставки: адрес снова не скачается — последний шаг закажется заново.\n if (step.urls) return rt.reorderOnDeliveryFailure && index === last ? total + rt.setup.steps[index]!.priceUsd : total;\n if (step.task) return total + step.task.priceUsd;\n return late ? total : total + (rt.setup.steps[index]?.priceUsd ?? 0);\n }, 0);\n }\n\n /** Заказанное и не оплаченное; `before` — только брони и запуски, появившиеся раньше него. */\n function pendingUsd(ownerId: string, chatId: string, before?: Runtime): { user: number; chat: number } {\n let user = 0;\n let chat = 0;\n for (const rt of runtimes) {\n if (rt === before) break;\n if (rt.ownerId !== ownerId) continue;\n const open = openUsd(rt);\n user += open;\n if (rt.chatId === chatId) chat += open;\n }\n return { user: roundUsd(user), chat: roundUsd(chat) };\n }\n\n async function withinLimit(rt: Runtime): Promise<string | undefined> {\n const limits = options.spending?.limits;\n if (!limits) return undefined;\n const pending = pendingUsd(rt.ownerId, rt.chatId, rt);\n try {\n await limits.assertWithinLimit({ userId: rt.ownerId, chatId: rt.chatId, ...(pending.user > 0 && { pendingUsd: pending }) });\n return undefined;\n } catch (error) {\n if (error instanceof SpendingLimitReachedError) return LIMIT_REACHED;\n logError(`Картинки: не удалось проверить лимит расходов: ${errorText(error)}`);\n return \"не удалось проверить лимит расходов\";\n }\n }\n\n // --- провайдер ---\n\n function requestOf(record: ImageJobRecord, step: ImageStep, sourceUrl: string | undefined, timeoutMs: number, signal: AbortSignal): ImageGenerationRequest {\n const { width, height } = step.sizes[record.aspectRatio];\n return {\n taskUUID: crypto.randomUUID(),\n providerModelId: step.providerModelId,\n prompt: step.prompt ?? record.prompt,\n width,\n height,\n aspectRatio: step.profile.aspectRatioValues?.[record.aspectRatio] ?? record.aspectRatio,\n ...(sourceUrl !== undefined && { imageUrl: sourceUrl }),\n ...(step.outputFormat && { outputFormat: step.outputFormat }),\n ...(step.providerSettings && { providerSettings: step.providerSettings }),\n abortSignal: signal,\n timeoutMs,\n };\n }\n\n /** Отправка не удалась: отказ до работы — без оплаты; иначе ушла ли задача, неизвестно — по цене модели. */\n async function failedSend(rt: Runtime, index: number, error: unknown, wholeAttempt: boolean): Promise<StepOutcome> {\n throwIfEnded(rt);\n if (error instanceof ImageGenerationError && error.refusedBeforeWork) {\n await write(rt, (record) => ({ steps: withStep(record.steps, index, withoutTask) }));\n return { kind: \"refused\", reason: hide(error.reason) };\n }\n let reason: string;\n if (error instanceof ImageGenerationError) reason = hide(error.reason);\n else if (error instanceof CallTimedOut) reason = wholeAttempt ? timeoutReason : error.message;\n else {\n logError(`Картинки: сбой провайдера ${rt.setup.steps[index]!.provider.id}: ${errorText(error)}`);\n reason = \"Провайдер не смог создать картинку\";\n }\n await chargeTask(rt, index);\n return { kind: \"failed\", reason };\n }\n\n async function send(rt: Runtime, index: number): Promise<StepOutcome> {\n const step = rt.setup.steps[index]!;\n const task: ImageJobTask = { usageId: crypto.randomUUID(), modelId: step.profile.id, providerId: step.provider.id, priceUsd: step.priceUsd, sentAt: iso(nowMs()) };\n // Сначала отметка «уходит»: упал сервер посреди отправки — задача всё равно будет оплачена.\n await write(rt, (record) => ({ steps: withStep(record.steps, index, (current) => ({ ...current, task })) }));\n const record = rt.record!;\n const sourceUrl = index > 0 ? record.steps[index - 1]!.urls?.[0] : undefined;\n const queue = step.provider.queue;\n if (queue) {\n let taskId: string;\n try {\n taskId = await callProvider(rt, callTimeoutMs, (signal) => queue.submit(requestOf(record, step, sourceUrl, callTimeoutMs, signal), step.connection));\n } catch (error) {\n return failedSend(rt, index, error, false);\n }\n await write(rt, (fresh) => ({ steps: withStep(fresh.steps, index, (current) => ({ ...current, task: { ...current.task!, taskId } })) }));\n return poll(rt, index);\n }\n // Без очереди «принял» не бывает: заказ отвечает, когда провайдер ответил (отказ виден сразу) или через acceptWaitMs.\n const accepted = setTimeout(() => rt.sent.resolve(), acceptWaitMs);\n unref(accepted);\n const timeoutMs = Math.max(1, Date.parse(record.attemptDeadline) - nowMs());\n let results: ImageGenerationResult[];\n try {\n results = await callProvider(rt, timeoutMs, (signal) => step.provider.generate(requestOf(record, step, sourceUrl, timeoutMs, signal), step.connection));\n } catch (error) {\n return failedSend(rt, index, error, true);\n } finally {\n clearTimeout(accepted);\n }\n throwIfEnded(rt);\n await acceptResults(rt, index, results);\n return { kind: \"ready\" };\n }\n\n /** Опрос задачи в очереди провайдера до готовности, провала или срока (попытки или позднего окна). */\n async function poll(rt: Runtime, index: number): Promise<StepOutcome> {\n rt.sent.resolve();\n const step = rt.setup.steps[index]!;\n const queue = step.provider.queue!;\n const taskId = rt.record!.steps[index]!.task!.taskId!;\n const checkNow = rt.checkNow;\n rt.checkNow = false;\n for (let first = true; ; first = false) {\n if (!(first && checkNow)) {\n const record = rt.record!;\n const deadline = Date.parse(record.lateUntil ?? record.attemptDeadline);\n const wait = Math.min(record.lateUntil !== undefined ? latePollIntervalMs : pollIntervalMs, deadline - nowMs());\n if (wait > 0) await sleep(wait, rt.controller.signal);\n if (nowMs() >= deadline) return { kind: \"timeout\" };\n }\n let state: ImageTaskState;\n try {\n state = await callProvider(rt, callTimeoutMs, (signal) => queue.check(taskId, step.connection, signal));\n } catch (error) {\n throwIfEnded(rt);\n if (error instanceof ImageGenerationError) {\n await chargeTask(rt, index);\n return { kind: \"failed\", reason: hide(error.reason) };\n }\n // Временный сбой или срок обращения: проверка повторится.\n continue;\n }\n throwIfEnded(rt);\n if (state.status === \"ready\") {\n await acceptResults(rt, index, state.results);\n return { kind: \"ready\" };\n }\n if (state.status === \"failed\") {\n await chargeTask(rt, index, state.costUsd);\n return { kind: \"failed\", reason: hide(state.reason) };\n }\n }\n }\n\n // --- запуск ---\n\n async function finishFailed(rt: Runtime, reason: string, extra: ImageJobPatch = {}) {\n await write(rt, (record) => ({\n status: \"failed\",\n reason,\n lateUntil: undefined,\n ...extra,\n active: hasUnrecorded(record.steps),\n finishedAt: iso(nowMs()),\n leaseOwner: undefined,\n leaseUntil: undefined,\n }));\n }\n\n const nextAttempt = (rt: Runtime) => write(rt, (record) => ({ attempt: record.attempt + 1, attemptDeadline: iso(nowMs() + attemptTimeoutMs) }));\n\n /** Шаг до готовых адресов; `false` — задача закончилась без них (запись уже сделана). */\n async function runStep(rt: Runtime, index: number): Promise<boolean> {\n for (;;) {\n throwIfEnded(rt);\n const record = rt.record!;\n const step = record.steps[index]!;\n if (step.urls) return true;\n const late = record.lateUntil !== undefined;\n let outcome: StepOutcome;\n if (step.task?.taskId !== undefined) outcome = await poll(rt, index);\n else if (step.task) {\n // Сервер упал посреди отправки: ушла ли задача, неизвестно — по цене модели, попытка не удалась.\n await chargeTask(rt, index);\n outcome = { kind: \"failed\", reason: \"Провайдер не ответил: сервер перезапускался\" };\n } else if (late) outcome = { kind: \"failed\", reason: record.reason ?? timeoutReason };\n else if (nowMs() >= Date.parse(record.attemptDeadline)) outcome = { kind: \"failed\", reason: timeoutReason };\n else outcome = await send(rt, index);\n\n if (outcome.kind === \"ready\") {\n if (late) await write(rt, () => ({ status: \"generating\", reason: undefined, lateUntil: undefined, attemptDeadline: iso(nowMs() + attemptTimeoutMs) }));\n return true;\n }\n if (outcome.kind === \"refused\") {\n await finishFailed(rt, outcome.reason);\n return false;\n }\n if (outcome.kind === \"failed\") {\n if (!late && rt.record!.attempt < attempts) {\n await nextAttempt(rt);\n continue;\n }\n await finishFailed(rt, outcome.reason);\n return false;\n }\n // Срок вышел, задача у провайдера ещё идёт.\n if (late) {\n await chargeTask(rt, index);\n await finishFailed(rt, rt.record!.reason ?? timeoutReason);\n return false;\n }\n if (rt.record!.attempt < attempts) {\n await nextAttempt(rt);\n rt.checkNow = true;\n continue;\n }\n if (lateResultWindowMs > 0) {\n await write(rt, () => ({ status: \"failed\", reason: timeoutReason, lateUntil: iso(nowMs() + lateResultWindowMs) }));\n continue;\n }\n await chargeTask(rt, index);\n await finishFailed(rt, timeoutReason);\n return false;\n }\n }\n\n /** Скачать с того же адреса (несколько раз) и отдать приёмнику. `reorder` — после «Повторить» адрес так и не скачался. */\n async function deliverResult(rt: Runtime): Promise<\"done\" | \"reorder\"> {\n rt.sent.resolve();\n const record = rt.record!;\n const last = rt.setup.steps.at(-1)!;\n // У модели с основой первым идёт фон без предмета, картинка — следующая.\n const url = record.steps.at(-1)!.urls?.[last.profile.firstResultIsBase ? 1 : 0];\n if (!url) {\n await finishFailed(rt, \"Провайдер не вернул картинку\");\n return \"done\";\n }\n const output = rt.setup.output;\n let file: DownloadedImage;\n for (let attempt = 1; ; attempt++) {\n try {\n file = await downloadGeneratedImage({\n url,\n ...(last.downloadFetch && { fetch: last.downloadFetch }),\n maxImageBytes,\n ...(last.trustedOrigin !== undefined && { trustedOrigin: last.trustedOrigin }),\n abortSignal: rt.controller.signal,\n ...(output && { transform: (bytes: Uint8Array, mediaType: string) => postprocessImage(bytes, mediaType, output) }),\n });\n break;\n } catch (error) {\n throwIfEnded(rt);\n if (attempt >= downloadAttempts) {\n if (rt.reorderOnDeliveryFailure) return \"reorder\";\n await finishFailed(rt, error instanceof ImageGenerationError ? hide(error.reason) : \"Картинка не скачалась\", { deliveryFailed: true });\n return \"done\";\n }\n await sleep(pollIntervalMs, rt.controller.signal);\n }\n }\n throwIfEnded(rt);\n const first = rt.setup.steps[0]!.profile;\n const planned = rt.setup.steps[0]!.sizes[record.aspectRatio];\n const image: GeneratedImage = { bytes: file.bytes, mediaType: file.mediaType, width: file.width ?? planned.width, height: file.height ?? planned.height, modelId: first.id };\n const run = record.run;\n const context: ImageDeliveryContext = {\n signal: rt.controller.signal,\n async isCurrent() {\n if (rt.controller.signal.aborted) return false;\n const fresh = await store.get(record.id);\n return fresh !== null && fresh.run === run && fresh.leaseOwner === me && isOpen(fresh) && !rt.controller.signal.aborted;\n },\n };\n let result: unknown;\n try {\n result = await options.deliver(image, view(record), context);\n } catch (error) {\n throwIfEnded(rt);\n logError(`Картинки: приёмник не принял картинку задачи ${record.id}: ${errorText(error)}`);\n await finishFailed(rt, \"Приёмник не принял картинку\", { deliveryFailed: true });\n return \"done\";\n }\n throwIfEnded(rt);\n await write(rt, (fresh) => ({\n status: result === false ? \"discarded\" : \"ready\",\n reason: undefined,\n lateUntil: undefined,\n deliveryFailed: undefined,\n ...(result !== false && result !== undefined && { result }),\n active: hasUnrecorded(fresh.steps),\n finishedAt: iso(nowMs()),\n leaseOwner: undefined,\n leaseUntil: undefined,\n }));\n return \"done\";\n }\n\n async function generate(rt: Runtime) {\n for (;;) {\n for (let index = 0; index < rt.setup.steps.length; index++) {\n if (!(await runStep(rt, index))) return;\n }\n if ((await deliverResult(rt)) === \"done\") return;\n // Тот же адрес не скачался и после «Повторить»: последний шаг заказывается заново, прежние — нет.\n rt.reorderOnDeliveryFailure = false;\n const last = rt.setup.steps.length - 1;\n await write(rt, (record) => ({\n deliveryFailed: undefined,\n attemptDeadline: iso(nowMs() + attemptTimeoutMs),\n steps: withStep(record.steps, last, ({ urls: _urls, ...step }) => step),\n }));\n }\n }\n\n async function drive(rt: Runtime) {\n try {\n await recordCharges(rt);\n if (isOpen(rt.record!)) await generate(rt);\n } catch (error) {\n if (error !== RUN_ENDED && !rt.controller.signal.aborted) logError(`Картинки: задача ${rt.record?.id ?? \"\"} остановилась: ${errorText(error)}`);\n } finally {\n rt.ended = true;\n clearTimeout(rt.leaseTimer);\n runtimes.delete(rt);\n if (rt.record && byJob.get(rt.record.id) === rt) byJob.delete(rt.record.id);\n rt.sent.resolve();\n }\n }\n\n function keepLease(rt: Runtime) {\n const renew = () => {\n rt.leaseTimer = setTimeout(async () => {\n if (rt.ended || rt.controller.signal.aborted) return;\n try {\n await write(rt, (record) => (record.leaseOwner === me ? { leaseUntil: iso(nowMs() + leaseMs) } : null));\n } catch (error) {\n if (error !== RUN_ENDED) logError(`Картинки: аренда задачи ${rt.record?.id ?? \"\"} не продлилась: ${errorText(error)}`);\n }\n if (!rt.ended && !rt.controller.signal.aborted) renew();\n }, Math.max(1, Math.floor(leaseMs / 3)));\n unref(rt.leaseTimer);\n };\n renew();\n }\n\n function newRuntime(ownerId: string, chatId: string, setup: ImageSetup, reservedUsd: number): Runtime {\n let resolve!: () => void;\n const promise = new Promise<void>((done) => (resolve = done));\n return { ownerId, chatId, setup, reservedUsd, controller: new AbortController(), chain: Promise.resolve(), sent: { promise, resolve }, ended: false, checkNow: false, reorderOnDeliveryFailure: false };\n }\n\n function launch(rt: Runtime, record: ImageJobRecord) {\n rt.record = record;\n byJob.set(record.id, rt);\n keepLease(rt);\n rt.done = drive(rt);\n }\n\n /** Настройка задачи из её назначения; назначение пропало или шаги другие — `undefined`. */\n function setupOf(record: ImageJobRecord): ImageSetup | undefined {\n const setup = setups.byPurpose.get(record.purpose);\n if (!setup || setup.steps.length !== record.steps.length) return undefined;\n const sameProviders = record.steps.every((step, index) => !step.task || step.task.providerId === setup.steps[index]!.provider.id);\n return sameProviders ? setup : undefined;\n }\n\n /** Работа с одним местом (`ownerId` + `key`) — по одной в этой копии: заказ и «Повторить» не перебивают друг друга. */\n function underKey<T>(ownerId: string, key: string, work: () => Promise<T>): Promise<T> {\n const lockKey = JSON.stringify([ownerId, key]);\n const current = (keyLocks.get(lockKey) ?? Promise.resolve()).then(work, work);\n const tail = current.catch(() => {});\n keyLocks.set(lockKey, tail);\n void tail.then(() => {\n if (keyLocks.get(lockKey) === tail) keyLocks.delete(lockKey);\n });\n return current;\n }\n\n // --- обход хранилища ---\n\n /** Взять задачу из хранилища: аренда свободна или истекла. Закрытой — дописать расход, открытую — вести. */\n async function claim(record: ImageJobRecord) {\n if (stopped || byJob.has(record.id)) return;\n const claimed = await store.update(record.id, { leaseOwner: me, leaseUntil: iso(nowMs() + leaseMs), updatedAt: iso(nowMs()) }, record.version);\n if (!claimed) return;\n if (!isOpen(claimed)) return settleClosed(claimed);\n const setup = setupOf(claimed);\n if (!setup) return closeJob(claimed, \"failed\", `Настройка картинок сменилась: назначения «${claimed.purpose}» в ней больше нет`);\n const rt = newRuntime(claimed.ownerId, claimed.chatId, setup, 0);\n rt.checkNow = true;\n rt.reorderOnDeliveryFailure = false;\n runtimes.add(rt);\n launch(rt, claimed);\n }\n\n function scheduleScan() {\n if (stopped || !started) return;\n clearTimeout(scanTimer);\n scanTimer = setTimeout(() => void scan(), leaseMs);\n unref(scanTimer);\n }\n\n function scan(): Promise<void> {\n if (stopped) return Promise.resolve();\n if (scanning) return scanning;\n scanning = (async () => {\n try {\n if (nowMs() - lastPruneAt >= PRUNE_EVERY_MS) {\n lastPruneAt = nowMs();\n await store.deleteFinishedBefore(iso(nowMs() - forgetFinishedAfterMs));\n }\n for (const record of await store.listResumable(iso(nowMs()))) {\n if (stopped) break;\n await claim(record).catch((error: unknown) => logError(`Картинки: задачу ${record.id} не удалось подобрать: ${errorText(error)}`));\n }\n } catch (error) {\n logError(`Картинки: обход задач не удался: ${errorText(error)}`);\n } finally {\n scanning = undefined;\n scheduleScan();\n }\n })();\n return scanning;\n }\n\n // --- снаружи ---\n\n async function order(input: ImageOrder): Promise<ImageJob | ImageOrderRefusal> {\n if (stopped) throw new Error(\"Картинки: очередь остановлена (stop) — заказы не принимаются\");\n if (!input.ownerId || !input.chatId) throw new Error(\"Картинки: order — нужны ownerId и chatId\");\n const aspectRatio = input.aspectRatio ?? \"1:1\";\n if (!aspectRatios.includes(aspectRatio)) return refused(`пропорции ${aspectRatio} нет: ${aspectRatios.join(\", \")}`);\n if (typeof input.prompt !== \"string\" || !input.prompt.trim()) return refused(\"пустое описание картинки\");\n const purpose = setups.hasPurposes ? (input.purpose ?? firstPurpose) : input.purpose === undefined ? \"\" : undefined;\n const setup = purpose === undefined ? undefined : setups.byPurpose.get(purpose);\n if (purpose === undefined || !setup) return refused(`назначения «${input.purpose}» нет`);\n if (!turns.take(input.turnId)) return refused(`не больше ${options.maxImagesPerTurn} картинок за ход`);\n\n // Бронь — до первого await: параллельный заказ уже видит её в pendingUsd.\n const rt = newRuntime(input.ownerId, input.chatId, setup, setup.priceUsd);\n runtimes.add(rt);\n let accepted = false;\n try {\n const refusal = await withinLimit(rt);\n if (refusal) return refused(refusal);\n if (rt.controller.signal.aborted) return refused(QUEUE_STOPPED);\n const id = crypto.randomUUID();\n const key = input.key ?? id;\n const newRecord = (): ImageJobRecord => {\n const at = nowMs();\n return {\n id,\n key,\n ownerId: input.ownerId,\n chatId: input.chatId,\n ...(input.turnId !== undefined && { turnId: input.turnId }),\n ...(input.group !== undefined && { group: input.group }),\n purpose,\n prompt: input.prompt,\n aspectRatio,\n ...(input.data !== undefined && { data: input.data }),\n status: \"generating\",\n active: true,\n run: 1,\n attempt: 1,\n attemptDeadline: iso(at + attemptTimeoutMs),\n steps: setup.steps.map(() => ({ charges: [] })),\n leaseOwner: me,\n leaseUntil: iso(at + leaseMs),\n version: 1,\n createdAt: iso(at),\n updatedAt: iso(at),\n };\n };\n const record = await underKey(input.ownerId, key, async () => {\n // Открытая задача на место — одна, это держит база: если другая копия сервера успела занять место между\n // закрытием и вставкой, вставка не проходит — закрываем и её, пробуем снова.\n for (let tries = 1; ; tries++) {\n for (const old of await store.list({ ownerId: input.ownerId, key })) if (isOpen(old)) await closeJob(old, \"superseded\");\n if (stopped) return null;\n const created = newRecord();\n try {\n await store.insert(created);\n return created;\n } catch (error) {\n if (!(error instanceof ImageJobKeyTakenError) || tries >= MAX_PLACE_TRIES) throw error;\n }\n }\n });\n if (!record) return refused(QUEUE_STOPPED);\n // stop() пришёл, пока задача записывалась: провайдеру ничего не ушло — задача закрывается без аренды.\n if (stopped) {\n await closeJob(record, \"failed\", QUEUE_STOPPED);\n return refused(QUEUE_STOPPED);\n }\n accepted = true;\n launch(rt, record);\n await rt.sent.promise;\n return view(rt.record!);\n } finally {\n if (!accepted) {\n runtimes.delete(rt);\n turns.give(input.turnId);\n }\n }\n }\n\n async function reviveOrTaken(id: string, patch: ImageJobPatch, version: number): Promise<ImageJobRecord | null | \"taken\"> {\n try {\n return await store.update(id, patch, version);\n } catch (error) {\n if (error instanceof ImageJobKeyTakenError) return \"taken\";\n throw error;\n }\n }\n\n function retryJob(ownerId: string, key: string): Promise<ImageJob | ImageOrderRefusal | null> {\n return underKey(ownerId, key, async () => {\n const latest = (await store.list({ ownerId, key })).at(-1);\n if (!latest) return null;\n if (latest.status !== \"failed\") return view(latest);\n const setup = setupOf(latest);\n if (!setup) return refused(`назначения «${latest.purpose}» в настройке больше нет`);\n const remaining = latest.steps.reduce((total, step, index) => (step.urls ? total : total + (step.task?.priceUsd ?? setup.steps[index]!.priceUsd)), 0);\n // Сбой доставки: тот же адрес может не скачаться снова, тогда последний шаг заказывается заново — бронь на него.\n const reorderUsd = latest.deliveryFailed ? setup.steps.at(-1)!.priceUsd : 0;\n const rt = newRuntime(ownerId, latest.chatId, setup, remaining + reorderUsd);\n runtimes.add(rt);\n let accepted = false;\n try {\n const refusal = await withinLimit(rt);\n if (refusal) return refused(refusal);\n let current: ImageJobRecord | null = latest;\n let revived: ImageJobRecord | null | \"taken\" = null;\n // Новый запуск — условной записью: из двух «Повторить» (и двух копий сервера) проходит один.\n while (current?.status === \"failed\") {\n if (stopped) return refused(QUEUE_STOPPED);\n const at = nowMs();\n revived = await reviveOrTaken(\n current.id,\n {\n status: \"generating\",\n run: current.run + 1,\n attempt: 1,\n attemptDeadline: iso(at + attemptTimeoutMs),\n reason: undefined,\n lateUntil: undefined,\n deliveryFailed: undefined,\n finishedAt: undefined,\n active: true,\n leaseOwner: me,\n leaseUntil: iso(at + leaseMs),\n updatedAt: iso(at),\n },\n current.version,\n );\n // Место заняла новая задача (заказ с другой копии сервера): «Повторить» отдаёт её.\n if (revived === \"taken\") {\n const newest = (await store.list({ ownerId, key })).at(-1);\n return newest ? view(newest) : null;\n }\n if (revived) break;\n current = await store.get(latest.id);\n }\n if (!revived) return current ? view(current) : null;\n // stop() пришёл посреди «Повторить»: аренда отпускается, задачу продолжит следующий старт.\n if (stopped) {\n await updateJob(revived, (record) => (record.leaseOwner === me ? { leaseOwner: undefined, leaseUntil: undefined } : null), (record) => record.leaseOwner === me);\n return refused(QUEUE_STOPPED);\n }\n // Прежний запуск (опрос в позднем окне) кончается: дальше задачу ведёт новый.\n byJob.get(revived.id)?.controller.abort(RUN_ENDED);\n rt.checkNow = true;\n rt.reorderOnDeliveryFailure = current!.deliveryFailed === true;\n accepted = true;\n launch(rt, revived);\n await rt.sent.promise;\n return view(rt.record!);\n } finally {\n if (!accepted) runtimes.delete(rt);\n }\n });\n }\n\n async function start() {\n if (started) return scanning ?? Promise.resolve();\n started = true;\n stopped = false;\n return scan();\n }\n\n async function stop() {\n stopped = true;\n started = false;\n clearTimeout(scanTimer);\n scanTimer = undefined;\n await scanning;\n const all = [...runtimes];\n for (const rt of all) {\n clearTimeout(rt.leaseTimer);\n rt.controller.abort(RUN_ENDED);\n }\n await Promise.all(all.map((rt) => rt.done));\n // Аренда отпускается сразу: следующий старт (или другая копия) берёт задачи без ожидания срока.\n for (const rt of all) {\n if (!rt.record) continue;\n await rt.chain;\n await updateJob(rt.record, (record) => (record.leaseOwner === me ? { leaseOwner: undefined, leaseUntil: undefined } : null), (record) => record.leaseOwner === me).catch((error: unknown) =>\n logError(`Картинки: аренда задачи ${rt.record?.id ?? \"\"} не отпущена: ${errorText(error)}`),\n );\n }\n }\n\n void start();\n\n return {\n order,\n retry({ ownerId, key }) {\n if (stopped) return Promise.reject(new Error(\"Картинки: очередь остановлена (stop) — повтор не принимается\"));\n const flight = JSON.stringify([ownerId, key]);\n const running = retries.get(flight);\n if (running) return running;\n const promise = retryJob(ownerId, key).finally(() => retries.delete(flight));\n retries.set(flight, promise);\n return promise;\n },\n async get(id) {\n const record = await store.get(id);\n return record ? view(record) : null;\n },\n async list(filter) {\n return (await store.list(filter)).map(view);\n },\n pendingUsd: (ownerId, chatId) => pendingUsd(ownerId, chatId),\n start,\n stop,\n };\n}\n","import { hideSecrets } from \"./hide-secrets\";\nimport { imageQualities, imageSizes, standardImageCatalog } from \"./image-catalog\";\nimport type { ImageCatalog, ImageFileFormat, ImageModelProfile, ImageProviderConnection, ImageProviderDescription, ImageQuality, ImageSize, ImageSizesByAspectRatio } from \"./image-catalog\";\nimport { checkImageOutput } from \"./postprocess-image\";\nimport type { ImageOutputOptions } from \"./postprocess-image\";\n\nconst DEFAULT_MODEL = \"gpt-image-2\";\n/** Подсказка разделению на слои: предмет целиком одним слоем, без тени (живой запрос 5 октября 2026: 2 адреса, $0.032). */\nexport const DEFAULT_REMOVE_BACKGROUND_PROMPT = \"Separate the whole main subject, without its shadow, into a single layer.\";\n\n/** Шаг «убрать фон»: модель правки (разделение на слои) по готовой картинке. */\nexport interface ImageBackgroundRemoval {\n /** Модель с `needsSourceImage`: `seedream-5-flash-layers`. */\n model: string;\n provider?: string;\n /** Подсказка модели правки; по умолчанию — предмет целиком одним слоем. */\n prompt?: string;\n}\n\n/** Назначение картинки: агент выбирает его параметром `purpose`, приложение задаёт модель и настройки. */\nexport interface ImagePurpose {\n /** Когда выбирать это назначение — для агента, попадает в описание инструмента. */\n description: string;\n model: string;\n provider?: string;\n quality?: ImageQuality;\n /** Размер картинки: `1k` — длинная сторона около 1024; по умолчанию — размер модели. */\n size?: ImageSize;\n transparentBackground?: boolean;\n output?: ImageOutputOptions;\n /** Сначала картинка, потом её же без фона: сохраняется предмет, фон убран. */\n removeBackground?: ImageBackgroundRemoval;\n}\n\n/** Общие настройки модели: для одной модели или по умолчанию для всех назначений. */\nexport interface ImageModelChoice {\n catalog?: ImageCatalog;\n model?: string;\n provider?: string;\n quality?: ImageQuality;\n size?: ImageSize;\n transparentBackground?: boolean;\n output?: ImageOutputOptions;\n purposes?: Record<string, ImagePurpose>;\n env: Record<string, string | undefined>;\n fetch?: typeof globalThis.fetch;\n}\n\n/** Один шаг задачи: модель, провайдер, подключение и поля запроса. */\nexport interface ImageStep {\n profile: ImageModelProfile;\n provider: ImageProviderDescription;\n providerModelId: string;\n connection: ImageProviderConnection;\n /** Стороны выбранного размера для каждой пропорции. */\n sizes: ImageSizesByAspectRatio;\n /** Цена картинки шага по профилю — с размером и качеством: по ней бронь в лимитах и запись без цены провайдера. */\n priceUsd: number;\n providerSettings?: Record<string, unknown>;\n outputFormat?: ImageFileFormat;\n /** Своя подсказка шага (разделение на слои); нет — описание картинки от агента. */\n prompt?: string;\n /** Сайт подключения из настройки (свой прокси, поддельный провайдер): с него картинку можно скачать и по http. */\n trustedOrigin?: string;\n /** Свой `fetch` приложения для скачивания картинки; нет — закреплённый `fetch` (download-image). */\n downloadFetch?: typeof globalThis.fetch;\n}\n\n/** Как создаётся картинка одного назначения (или единственной модели). */\nexport interface ImageSetup {\n /** Имя назначения; без назначений — нет. */\n purpose?: string;\n description?: string;\n steps: ImageStep[];\n output?: ImageOutputOptions;\n /** Цена всех шагов: так задача бронируется в лимите. */\n priceUsd: number;\n}\n\nexport interface ImageSetups {\n /** Назначения по имени в порядке настройки; без назначений — одно, под ключом `\"\"`. */\n byPurpose: Map<string, ImageSetup>;\n /** Есть назначения: в схеме инструмента появляется `purpose`. */\n hasPurposes: boolean;\n /** Ключ и заголовки всех подключений спрятаны. */\n hideSecrets(text: string): string;\n}\n\n/** Сумма в долларах без хвоста двоичной дроби. */\nconst roundUsd = (value: number) => Math.round(value * 1e9) / 1e9;\n\n/** Поля качества и фона по пути профиля (`[\"settings\"]`), поверх полей профиля; профиль не меняется. */\nfunction withSettings(base: Record<string, unknown> | undefined, path: readonly string[], fields: Record<string, unknown>): Record<string, unknown> {\n const root: Record<string, unknown> = { ...base };\n let node = root;\n for (const key of path) {\n const next = node[key];\n node[key] = { ...(typeof next === \"object\" && next !== null ? (next as Record<string, unknown>) : {}) };\n node = node[key] as Record<string, unknown>;\n }\n Object.assign(node, fields);\n return root;\n}\n\n/**\n * Настройка картинок целиком: модели, провайдеры, ключи, качество, фон и назначения. Ошибка — исключение сразу, при\n * создании, с именем поля или модели в тексте.\n */\nexport function resolveImageSetups(choice: ImageModelChoice): ImageSetups {\n const catalog = choice.catalog ?? standardImageCatalog;\n const connections = new Map<string, ImageProviderConnection>();\n\n function connect(provider: ImageProviderDescription): ImageProviderConnection {\n const existing = connections.get(provider.id);\n if (existing) return existing;\n const apiKey = choice.env[provider.apiKeyEnv]?.trim();\n if (!apiKey) throw new Error(`Картинки: нет ключа провайдера ${provider.displayName} в переменной окружения ${provider.apiKeyEnv}`);\n const connection: ImageProviderConnection = {\n apiKey,\n ...(provider.baseURL !== undefined && { baseURL: provider.baseURL }),\n ...(provider.headers !== undefined && { headers: provider.headers }),\n fetch: choice.fetch ?? globalThis.fetch,\n };\n connections.set(provider.id, connection);\n return connection;\n }\n\n function step(modelId: string, providerId: string | undefined, where: string, settings: { quality?: ImageQuality; size?: ImageSize; transparentBackground?: boolean } = {}): ImageStep {\n const profile = catalog.models.find((model) => model.id === modelId);\n if (!profile) throw new Error(`Картинки: ${where}модели ${modelId} нет в каталоге`);\n const chosen = providerId ?? Object.keys(profile.providerModelIds)[0];\n if (!chosen || profile.providerModelIds[chosen] === undefined) throw new Error(`Картинки: ${where}у модели ${modelId} нет провайдера ${chosen ?? \"\"}`.trim());\n const provider = catalog.providers.find((item) => item.id === chosen);\n if (!provider) throw new Error(`Картинки: ${where}провайдера ${chosen} нет в каталоге`);\n\n const fields: Record<string, unknown> = {};\n if (settings.quality !== undefined) {\n if (!imageQualities.includes(settings.quality)) throw new Error(`Картинки: ${where}quality — одно из ${imageQualities.join(\", \")}`);\n if (!profile.settingsPath) throw new Error(`Картинки: ${where}модель ${modelId} не умеет выбирать качество`);\n fields.quality = settings.quality;\n }\n let sizes = profile.aspectRatios;\n let sizePriceUsd = profile.pricePerImageUsd;\n if (settings.size !== undefined && settings.size !== \"default\") {\n if (!imageSizes.includes(settings.size)) throw new Error(`Картинки: ${where}size — одно из ${imageSizes.join(\", \")}`);\n const option = profile.sizes?.[settings.size];\n if (!option) throw new Error(`Картинки: ${where}модель ${modelId} не умеет размер ${settings.size}`);\n sizes = option.aspectRatios;\n sizePriceUsd = option.pricePerImageUsd ?? profile.pricePerImageUsd;\n }\n const qualityFactor = settings.quality !== undefined ? (profile.qualityPriceFactors?.[settings.quality] ?? 1) : 1;\n if (settings.transparentBackground) {\n if (!profile.transparentBackground || !profile.settingsPath) throw new Error(`Картинки: ${where}модель ${modelId} не умеет прозрачный фон`);\n fields.background = \"transparent\";\n }\n const providerSettings = profile.settingsPath && Object.keys(fields).length > 0 ? withSettings(profile.providerSettings, profile.settingsPath, fields) : profile.providerSettings;\n return {\n profile,\n provider,\n providerModelId: profile.providerModelIds[chosen]!,\n connection: connect(provider),\n sizes,\n priceUsd: roundUsd(sizePriceUsd * qualityFactor),\n ...(provider.baseURL !== undefined && { trustedOrigin: new URL(provider.baseURL).origin }),\n ...(choice.fetch && { downloadFetch: choice.fetch }),\n ...(providerSettings && { providerSettings }),\n ...(settings.transparentBackground && { outputFormat: \"png\" as const }),\n };\n }\n\n function setup(purpose: Omit<ImagePurpose, \"description\"> & { description?: string }, where: string, name?: string): ImageSetup {\n const quality = purpose.quality ?? choice.quality;\n const size = purpose.size ?? choice.size;\n const transparentBackground = purpose.transparentBackground ?? choice.transparentBackground;\n const output = purpose.output ?? choice.output;\n checkImageOutput(output, where);\n const base = step(purpose.model, purpose.provider, where, { ...(quality !== undefined && { quality }), ...(size !== undefined && { size }), ...(transparentBackground && { transparentBackground }) });\n if (base.profile.needsSourceImage) throw new Error(`Картинки: ${where}модель ${base.profile.id} правит готовую картинку — ей нужна исходная, она годится только для removeBackground`);\n const steps = [base];\n if (purpose.removeBackground) {\n const removal = step(purpose.removeBackground.model, purpose.removeBackground.provider, `${where}removeBackground: `);\n if (!removal.profile.needsSourceImage) throw new Error(`Картинки: ${where}removeBackground: модель ${removal.profile.id} не правит исходную картинку`);\n steps.push({ ...removal, prompt: purpose.removeBackground.prompt ?? DEFAULT_REMOVE_BACKGROUND_PROMPT });\n }\n if ((transparentBackground || purpose.removeBackground) && output?.format === \"jpeg\") throw new Error(`Картинки: ${where}прозрачный фон не сохранится в jpeg — выбери png или webp`);\n return {\n ...(name !== undefined && { purpose: name, description: purpose.description }),\n steps,\n ...(output && { output }),\n priceUsd: roundUsd(steps.reduce((total, item) => total + item.priceUsd, 0)),\n };\n }\n\n const byPurpose = new Map<string, ImageSetup>();\n if (choice.purposes === undefined) {\n byPurpose.set(\"\", setup({ model: choice.model ?? DEFAULT_MODEL, ...(choice.provider !== undefined && { provider: choice.provider }) }, \"\"));\n } else {\n if (choice.model !== undefined || choice.provider !== undefined) throw new Error(\"Картинки: purposes задают модель каждому назначению — model и provider рядом с ними не нужны\");\n const entries = Object.entries(choice.purposes);\n if (entries.length === 0) throw new Error(\"Картинки: purposes пуст — нужно хотя бы одно назначение\");\n for (const [name, purpose] of entries) {\n if (!/^[a-z][a-z0-9_-]{0,39}$/.test(name)) throw new Error(`Картинки: имя назначения «${name}» — латиница, цифры, «-» и «_», до 40 знаков`);\n if (!purpose.description?.trim()) throw new Error(`Картинки: у назначения ${name} пустое описание для агента`);\n byPurpose.set(name, setup(purpose, `назначение ${name}: `, name));\n }\n }\n\n const all = [...connections.values()];\n return {\n byPurpose,\n hasPurposes: choice.purposes !== undefined,\n hideSecrets: (text) => all.reduce((result, connection) => hideSecrets(result, connection), text),\n };\n}\n","/** Ход длиннее этого не бывает: счёт старого хода забывается, память не копится. */\nconst FORGET_TURN_AFTER_MS = 6 * 60 * 60 * 1000;\n\n/**\n * Предел «не больше N картинок за ход» по номеру хода (`turnId`). Номер один на все попытки хода и на запасного\n * провайдера, поэтому новая попытка счёт не обнуляет. Без предела или без номера — не считается.\n */\nexport function createTurnCounter(limit: number | undefined) {\n const turns = new Map<string, { count: number; touchedAt: number }>();\n\n function forgetOld(now: number) {\n for (const [turnId, turn] of turns) if (now - turn.touchedAt > FORGET_TURN_AFTER_MS) turns.delete(turnId);\n }\n\n return {\n /** Занять место; `false` — предел хода исчерпан. */\n take(turnId: string | undefined): boolean {\n if (limit === undefined || turnId === undefined) return true;\n const now = Date.now();\n forgetOld(now);\n const turn = turns.get(turnId) ?? { count: 0, touchedAt: now };\n if (turn.count >= limit) return false;\n turns.set(turnId, { count: turn.count + 1, touchedAt: now });\n return true;\n },\n /** Вернуть место: заказ не принят (лимит расходов, ошибка до записи). */\n give(turnId: string | undefined): void {\n if (limit === undefined || turnId === undefined) return;\n const turn = turns.get(turnId);\n if (turn) turn.count = Math.max(0, turn.count - 1);\n },\n };\n}\n","import type { ChatStorage } from \"@ykurilov/agent-chat-core\";\n\nexport interface ImageUsage {\n storage: Pick<ChatStorage, \"insertUsageRecord\">;\n ownerId: string;\n chatId: string;\n /** Id модели картинок из каталога: `gpt-image-2`. */\n modelId: string;\n providerId: string;\n costUsd: number;\n now: () => Date;\n logError?: (message: string) => void;\n}\n\n/**\n * Расход одной картинки (FR-041): та же таблица, по которой считает лимит расходов. Сбой записи не роняет вызов —\n * картинка уже создана, — а уходит в журнал.\n */\nexport async function recordImageUsage(usage: ImageUsage): Promise<void> {\n try {\n await usage.storage.insertUsageRecord({\n id: crypto.randomUUID(),\n ownerId: usage.ownerId,\n chatId: usage.chatId,\n messageId: null,\n source: \"images\",\n modelId: usage.modelId,\n providerId: usage.providerId,\n inputTokens: 0,\n cachedInputTokens: 0,\n cacheWriteTokens: 0,\n outputTokens: 0,\n reasoningTokens: 0,\n costUsd: usage.costUsd,\n createdAt: usage.now().toISOString(),\n });\n } catch (error) {\n usage.logError?.(`Картинки: не удалось записать расход ${usage.modelId}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n","import type { AttachmentStorage } from \"@ykurilov/agent-chat-attachments\";\nimport { downloadGeneratedImage, imageExtensions } from \"./download-image\";\nimport type { DownloadedImage } from \"./download-image\";\nimport type { ProcessedImage } from \"./postprocess-image\";\n\nexport { DOWNLOAD_TIMEOUT_MS } from \"./download-image\";\n\nexport interface SaveGeneratedImageInput {\n /** Адрес готовой картинки у провайдера. */\n url: string;\n fetch: typeof globalThis.fetch;\n storage: AttachmentStorage;\n chatId: string;\n ownerId: string;\n /** Номер картинки в вызове, с 1: из него имя файла `image-<n>.<ext>`. */\n number: number;\n maxImageBytes: number;\n /** Сайт подключения провайдера из настройки (`https://host:port`): с него можно скачивать и по http, и с localhost. */\n trustedOrigin?: string;\n /** «Стоп» хода: пришедшее после него не сохраняется. */\n abortSignal?: AbortSignal;\n /** Обработка перед сохранением (формат, размер); её итог и сохраняется. */\n transform?: (bytes: Uint8Array, mediaType: string) => Promise<ProcessedImage>;\n now: () => Date;\n}\n\n/** Сохранённая картинка; размер — из файла (PNG или после обработки), иначе не известен. */\nexport interface SavedImageFile {\n id: string;\n mediaType: string;\n size: number;\n width?: number;\n height?: number;\n}\n\n/**\n * Скачивает готовую картинку по адресу провайдера (без заголовков подключения: это чужой склад) и сохраняет её\n * прикреплённым файлом чата и владельца (FR-038). Тип — только по содержимому: PNG, JPEG или WebP. Сбой —\n * `ImageGenerationError`; «Стоп» пробрасывается как есть, и ничего не сохраняется (FR-042).\n */\nexport async function saveGeneratedImage(input: SaveGeneratedImageInput): Promise<SavedImageFile> {\n const file = await downloadGeneratedImage({\n url: input.url,\n fetch: input.fetch,\n maxImageBytes: input.maxImageBytes,\n ...(input.trustedOrigin !== undefined && { trustedOrigin: input.trustedOrigin }),\n ...(input.abortSignal && { abortSignal: input.abortSignal }),\n ...(input.transform && { transform: input.transform }),\n });\n return saveImageToChat({ storage: input.storage, chatId: input.chatId, ownerId: input.ownerId, number: input.number, file, now: input.now });\n}\n\nexport interface SaveImageToChatInput {\n storage: AttachmentStorage;\n chatId: string;\n ownerId: string;\n /** Номер картинки в вызове, с 1: из него имя файла `image-<n>.<ext>`. */\n number: number;\n file: DownloadedImage;\n now: () => Date;\n}\n\n/** Скачанная картинка — прикреплённым файлом чата и владельца. */\nexport async function saveImageToChat(input: SaveImageToChatInput): Promise<SavedImageFile> {\n const { file } = input;\n const extension = imageExtensions[file.mediaType]!;\n const id = crypto.randomUUID();\n await input.storage.save(\n {\n id,\n chatId: input.chatId,\n ownerId: input.ownerId,\n name: `image-${input.number}.${extension}`,\n mediaType: file.mediaType,\n kind: \"image\",\n size: file.bytes.length,\n createdAt: input.now().toISOString(),\n attached: true,\n },\n file.bytes,\n );\n return { id, mediaType: file.mediaType, size: file.bytes.length, ...(file.width !== undefined && file.height !== undefined && { width: file.width, height: file.height }) };\n}\n"],"mappings":"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","names":["SpendingLimitReachedError","view","refused","roundUsd","errorText","costOf","start","record","DEFAULT_MAX_IMAGE_BYTES","DEFAULT_LATE_RESULT_WINDOW_MS","LIMIT_REACHED","roundUsd","errorText","SpendingLimitReachedError","queue"]}
@@ -83,6 +83,11 @@ export interface CreateImageGenerationOptions {
83
83
  maxImagesPerTurn?: number;
84
84
  /** Фоновый режим: поля для ответа модели о заказанной картинке (например, куда она ляжет). */
85
85
  onQueued?: (job: ImageJob) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;
86
+ /**
87
+ * Свой `fetch` приложения: запросы к провайдеру и скачивание картинок. Это граница доверия приложения: закрепить
88
+ * соединение за проверенным адресом (от подмены DNS) должен он сам (`createPinnedFetch` из
89
+ * `@ykurilov/agent-chat-web-tools/network`). Без него картинку со склада провайдера качает закреплённый `fetch`.
90
+ */
86
91
  fetch?: typeof globalThis.fetch;
87
92
  logError?: (message: string) => void;
88
93
  now?: () => Date;
@@ -1,3 +1,4 @@
1
+ import type { HostResolver } from "@ykurilov/agent-chat-web-tools/network";
1
2
  import type { ProcessedImage } from "./postprocess-image.js";
2
3
  /** Скачивание одной картинки — не дольше этого: задача у провайдера уже готова, файл лежит на его складе. */
3
4
  export declare const DOWNLOAD_TIMEOUT_MS = 60000;
@@ -6,7 +7,14 @@ export declare const imageExtensions: Record<string, string>;
6
7
  export interface DownloadGeneratedImageInput {
7
8
  /** Адрес готовой картинки у провайдера. */
8
9
  url: string;
9
- fetch: typeof globalThis.fetch;
10
+ /**
11
+ * Свой `fetch` приложения (прокси) — граница доверия приложения: по нему идут все адреса, а закрепить соединение за
12
+ * проверенным адресом (от подмены DNS) должен он сам. Нет — склад провайдера качает общий закреплённый `fetch`:
13
+ * имя проверяется в момент соединения, во внутреннюю сеть не пускает. Сайт `trustedOrigin` — обычным `fetch`.
14
+ */
15
+ fetch?: typeof globalThis.fetch;
16
+ /** Как закреплённый `fetch` разрешает имена. По умолчанию системный DNS; заменяется в тестах. */
17
+ resolveHost?: HostResolver;
10
18
  maxImageBytes: number;
11
19
  /** Сайт подключения провайдера из настройки (`https://host:port`): с него можно скачивать и по http, и с localhost. */
12
20
  trustedOrigin?: string;
@@ -134,6 +134,11 @@ export interface CreateImageQueueOptions {
134
134
  forgetFinishedAfterMs?: number;
135
135
  /** Предел одного файла; по умолчанию 10 МБ. */
136
136
  maxImageBytes?: number;
137
+ /**
138
+ * Свой `fetch` приложения: запросы к провайдеру и скачивание картинок. Это граница доверия приложения: закрепить
139
+ * соединение за проверенным адресом (от подмены DNS) должен он сам (`createPinnedFetch` из
140
+ * `@ykurilov/agent-chat-web-tools/network`). Без него картинку со склада провайдера качает закреплённый `fetch`.
141
+ */
137
142
  fetch?: typeof globalThis.fetch;
138
143
  logError?: (message: string) => void;
139
144
  now?: () => Date;
@@ -53,6 +53,8 @@ export interface ImageStep {
53
53
  prompt?: string;
54
54
  /** Сайт подключения из настройки (свой прокси, поддельный провайдер): с него картинку можно скачать и по http. */
55
55
  trustedOrigin?: string;
56
+ /** Свой `fetch` приложения для скачивания картинки; нет — закреплённый `fetch` (download-image). */
57
+ downloadFetch?: typeof globalThis.fetch;
56
58
  }
57
59
  /** Как создаётся картинка одного назначения (или единственной модели). */
58
60
  export interface ImageSetup {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ykurilov/agent-chat-images",
3
- "version": "0.80.0",
3
+ "version": "0.81.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -49,14 +49,15 @@
49
49
  "drizzle-orm": "^0.45.3",
50
50
  "sharp": "^0.35.5",
51
51
  "zod": "^4.6.5",
52
- "@ykurilov/agent-chat-attachments": "0.80.0",
53
- "@ykurilov/agent-chat-core": "0.80.0"
52
+ "@ykurilov/agent-chat-attachments": "0.81.0",
53
+ "@ykurilov/agent-chat-core": "0.81.0",
54
+ "@ykurilov/agent-chat-web-tools": "0.81.0"
54
55
  },
55
56
  "peerDependencies": {
56
57
  "better-sqlite3": "^13.0.3",
57
58
  "pg": "^8.23.0",
58
59
  "react": "^19.3.0",
59
- "@ykurilov/agent-chat-react": "^0.80.0"
60
+ "@ykurilov/agent-chat-react": "^0.81.0"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@electric-sql/pglite": "0.5.8",
@@ -71,7 +72,7 @@
71
72
  "pg": "^8.23.0",
72
73
  "react": "^19.3.0",
73
74
  "tsx": "4.23.15",
74
- "@ykurilov/agent-chat-react": "0.80.0"
75
+ "@ykurilov/agent-chat-react": "0.81.0"
75
76
  },
76
77
  "peerDependenciesMeta": {
77
78
  "react": {