@ykurilov/agent-chat-core 0.60.0 → 0.61.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,29 @@
1
1
  # @ykurilov/agent-chat-core
2
2
 
3
+ ## 0.61.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b3f42a3: Снимок страницы: не проверено — не значит «не умеет». `page_screenshot` и снимок в `person_link_view` даются любой
8
+ модели, которая видит картинки, кроме провайдеров из `blindToolResultImageProviders` профиля. Картинка никогда не уходит
9
+ модели base64-строкой. Картинку получают провайдеры с `toolResultImages: true` (встроенные помечены) и провайдеры из
10
+ `toolResultImageProviders` профиля; остальным ядро шлёт текст страницы. Провайдер отказал из-за картинки (код 400, 415 или 422, в тексте ошибки — про картинку) — тот же запрос сразу
11
+ уходит текстом; удался повтор — строка `[картинки инструментов]` в журнал, и до перезапуска службы эта пара модели и
12
+ провайдера получает текст. Повтор упал — пара не запоминается, дальше идёт ошибка повтора. 413 — текстом только этот
13
+ запрос. Переполнение контекста и прочие отказы идут дальше как есть. Ошибка внутри уже открытого потока (код 200) не
14
+ откатывается. Первый переход на текст один раз сбрасывает кэш модели. Firecrawl отдаёт
15
+ текст страницы вместе со снимком (тот же кредит).
16
+
17
+ Что сделать приложению:
18
+
19
+ - своя проверка «провайдер проверен — даём снимок» больше не нужна;
20
+ - запрещали снимок тем, что не вписывали провайдер в `toolResultImageProviders`, — теперь нужен
21
+ `blindToolResultImageProviders`, иначе снимок уйдёт;
22
+ - провайдер точно не видит картинку из инструмента — `blindToolResultImageProviders: ["id"]` в профиле модели;
23
+ - свой провайдер, передающий картинки инструментов частью `file` SDK, — `toolResultImages: true` в его описании;
24
+ - своя служба снимков может вернуть `page` (текст той же страницы); свой инструмент с картинкой — запасной текст
25
+ `toolImageFallbackText(текст)` из `@ykurilov/agent-chat-core`.
26
+
3
27
  ## 0.60.0
4
28
 
5
29
  ### Minor Changes
@@ -749,6 +749,7 @@ function withRouteModels(models, providers) {
749
749
  const pricing = model.pricing && { ...model.pricing };
750
750
  const imageProviders = model.toolResultImageProviders && [...model.toolResultImageProviders];
751
751
  const imagesByProvider = model.acceptsImagesByProvider && { ...model.acceptsImagesByProvider };
752
+ const blindProviders = model.blindToolResultImageProviders && [...model.blindToolResultImageProviders];
752
753
  for (const route of routes) {
753
754
  const source = route.servesModelsOf;
754
755
  if (!model.providerModelIds[source]) continue;
@@ -756,13 +757,15 @@ function withRouteModels(models, providers) {
756
757
  if (pricing?.[source] && !pricing[route.id]) pricing[route.id] = pricing[source];
757
758
  if (imageProviders?.includes(source) && !imageProviders.includes(route.id)) imageProviders.push(route.id);
758
759
  if (imagesByProvider && source in imagesByProvider && !(route.id in imagesByProvider)) imagesByProvider[route.id] = imagesByProvider[source];
760
+ if (blindProviders?.includes(source) && !blindProviders.includes(route.id) && !imageProviders?.includes(route.id)) blindProviders.push(route.id);
759
761
  }
760
762
  return {
761
763
  ...model,
762
764
  providerModelIds,
763
765
  ...pricing && { pricing },
764
766
  ...imageProviders && { toolResultImageProviders: imageProviders },
765
- ...imagesByProvider && { acceptsImagesByProvider: imagesByProvider }
767
+ ...imagesByProvider && { acceptsImagesByProvider: imagesByProvider },
768
+ ...blindProviders && { blindToolResultImageProviders: blindProviders }
766
769
  };
767
770
  });
768
771
  }
@@ -2341,6 +2344,151 @@ function createForgedBlockGuard(formats) {
2341
2344
  };
2342
2345
  }
2343
2346
 
2347
+ // src/agent-run/tool-result-images-policy.ts
2348
+ import { wrapLanguageModel as wrapLanguageModel3 } from "ai";
2349
+
2350
+ // src/provider-errors/error-fields.ts
2351
+ import { APICallError, RetryError } from "ai";
2352
+ function underlyingError(error) {
2353
+ return RetryError.isInstance(error) ? error.lastError : error;
2354
+ }
2355
+ function errorStatusCode(error) {
2356
+ if (APICallError.isInstance(error)) return error.statusCode;
2357
+ if (typeof error !== "object" || error === null) return void 0;
2358
+ const fields = error;
2359
+ const nested = typeof fields.error === "object" && fields.error !== null ? fields.error.code : void 0;
2360
+ for (const value of [fields.statusCode, fields.status, fields.code, nested]) {
2361
+ const code = asStatusCode(value);
2362
+ if (code !== void 0) return code;
2363
+ }
2364
+ return void 0;
2365
+ }
2366
+ function asStatusCode(value) {
2367
+ const code = typeof value === "number" ? value : typeof value === "string" && /^\s*\d{3}\s*$/.test(value) ? Number(value) : void 0;
2368
+ return code !== void 0 && Number.isInteger(code) && code >= 100 && code <= 599 ? code : void 0;
2369
+ }
2370
+ function errorText(error) {
2371
+ if (APICallError.isInstance(error)) return [error.responseBody, error.message].filter(Boolean).join("\n");
2372
+ if (error instanceof Error) return error.message;
2373
+ if (typeof error === "string") return error;
2374
+ try {
2375
+ return JSON.stringify(error) ?? "";
2376
+ } catch {
2377
+ return "";
2378
+ }
2379
+ }
2380
+ function errorHeaders(error) {
2381
+ return APICallError.isInstance(error) ? error.responseHeaders : void 0;
2382
+ }
2383
+ function errorName(error) {
2384
+ const name = typeof error === "object" && error !== null ? error.name : void 0;
2385
+ return typeof name === "string" && name !== "" ? name : void 0;
2386
+ }
2387
+ function requestedRetryMs(error, nowMs) {
2388
+ const headers = errorHeaders(error);
2389
+ if (!headers) return void 0;
2390
+ let requestedMs;
2391
+ const inMs = Number.parseFloat(headers["retry-after-ms"] ?? "");
2392
+ if (!Number.isNaN(inMs)) requestedMs = inMs;
2393
+ const retryAfter = headers["retry-after"];
2394
+ if (requestedMs === void 0 && retryAfter) {
2395
+ const inSeconds = Number.parseFloat(retryAfter);
2396
+ requestedMs = Number.isNaN(inSeconds) ? Date.parse(retryAfter) - nowMs : inSeconds * 1e3;
2397
+ }
2398
+ if (requestedMs === void 0 || Number.isNaN(requestedMs) || requestedMs < 0) return void 0;
2399
+ return requestedMs;
2400
+ }
2401
+
2402
+ // src/agent-run/tool-result-images-policy.ts
2403
+ var FALLBACK_NAMESPACE = "agentChat";
2404
+ var NO_IMAGE_NOTE = "[\u041A\u0430\u0440\u0442\u0438\u043D\u043A\u0430 \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u0430: \u044D\u0442\u0430 \u043C\u043E\u0434\u0435\u043B\u044C \u0443 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u0438\u0437 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432.]";
2405
+ var REFUSAL_CODES = /* @__PURE__ */ new Set([400, 413, 415, 422]);
2406
+ var TOO_LARGE = 413;
2407
+ var IMAGE_REFUSAL = /image|vision|multi-?modal|content[ _-]?type|base64|unsupported content|media[ _-]?type|mime/i;
2408
+ var CONTEXT_OVERFLOW = /context (length|window)|maximum context|too many tokens|token limit|prompt is too long|reduce the length/i;
2409
+ function toolImageFallbackText(text) {
2410
+ return { type: "text", text, providerOptions: { [FALLBACK_NAMESPACE]: { toolImageFallback: true } } };
2411
+ }
2412
+ function modelMaySeeToolResultImages(profile, providerId) {
2413
+ return modelAcceptsImages(profile, providerId) && !profile.blindToolResultImageProviders?.includes(providerId);
2414
+ }
2415
+ var refusalsByCatalog = /* @__PURE__ */ new WeakMap();
2416
+ function refusalsOf(catalog) {
2417
+ let refusals = refusalsByCatalog.get(catalog);
2418
+ if (!refusals) {
2419
+ refusals = /* @__PURE__ */ new Set();
2420
+ refusalsByCatalog.set(catalog, refusals);
2421
+ }
2422
+ return refusals;
2423
+ }
2424
+ function withToolResultImagePolicy(model, context) {
2425
+ if (typeof model === "string") return model;
2426
+ const { profile, provider } = context;
2427
+ const pair = `${profile.id}@${provider.id}`;
2428
+ const refusals = refusalsOf(context.catalog);
2429
+ const carries = provider.toolResultImages === true || profile.toolResultImageProviders?.includes(provider.id) === true;
2430
+ const imagesAllowed = () => carries && modelMaySeeToolResultImages(profile, provider.id) && !refusals.has(pair);
2431
+ async function attempt(params, call) {
2432
+ if (!hasToolResultImages(params.prompt)) return call(params);
2433
+ if (!imagesAllowed()) return call({ ...params, prompt: withToolResultImages(params.prompt, false) });
2434
+ try {
2435
+ return await call({ ...params, prompt: withToolResultImages(params.prompt, true) });
2436
+ } catch (error) {
2437
+ const cause = underlyingError(error);
2438
+ const code = errorStatusCode(cause);
2439
+ if (params.abortSignal?.aborted || code === void 0 || !REFUSAL_CODES.has(code)) throw error;
2440
+ const text = errorText(cause);
2441
+ if (CONTEXT_OVERFLOW.test(text)) throw error;
2442
+ const aboutImage = code !== TOO_LARGE && IMAGE_REFUSAL.test(text);
2443
+ if (code !== TOO_LARGE && !aboutImage) throw error;
2444
+ const result = await call({ ...params, prompt: withToolResultImages(params.prompt, false) });
2445
+ if (aboutImage) refusals.add(pair);
2446
+ context.logError?.(
2447
+ aboutImage ? `[\u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432] ${provider.id} \u043E\u0442\u043A\u0430\u0437\u0430\u043B \u043C\u043E\u0434\u0435\u043B\u0438 ${profile.id} \u0432 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0435 \u0438\u0437 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0430 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430 (\u043A\u043E\u0434 ${code}): \u0437\u0430\u043F\u0440\u043E\u0441 \u043F\u043E\u0432\u0442\u043E\u0440\u0451\u043D \u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B, \u0434\u043E \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0441\u043B\u0443\u0436\u0431\u044B \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u0435\u043A\u0441\u0442` : `[\u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432] ${provider.id}: \u0437\u0430\u043F\u0440\u043E\u0441 \u043C\u043E\u0434\u0435\u043B\u0438 ${profile.id} \u0441 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u043E\u0439 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0432\u0435\u043B\u0438\u043A (\u043A\u043E\u0434 ${code}), \u043F\u043E\u0432\u0442\u043E\u0440\u0451\u043D \u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B`
2448
+ );
2449
+ return result;
2450
+ }
2451
+ }
2452
+ return wrapLanguageModel3({
2453
+ model,
2454
+ middleware: {
2455
+ specificationVersion: "v4",
2456
+ wrapGenerate: ({ params, model: inner }) => attempt(params, (next) => inner.doGenerate(next)),
2457
+ wrapStream: ({ params, model: inner }) => attempt(params, (next) => inner.doStream(next))
2458
+ }
2459
+ });
2460
+ }
2461
+ function isImage(item) {
2462
+ return item.type === "file" && (item.mediaType === "image" || item.mediaType.startsWith("image/"));
2463
+ }
2464
+ function isFallback(item) {
2465
+ return item.type === "text" && item.providerOptions?.[FALLBACK_NAMESPACE]?.toolImageFallback === true;
2466
+ }
2467
+ function hasToolResultImages(prompt) {
2468
+ return prompt.some(
2469
+ (message) => message.role === "tool" && message.content.some((part) => part.type === "tool-result" && part.output.type === "content" && part.output.value.some((item) => isImage(item) || isFallback(item)))
2470
+ );
2471
+ }
2472
+ function withToolResultImages(prompt, images) {
2473
+ return prompt.map((message) => {
2474
+ if (message.role !== "tool") return message;
2475
+ return {
2476
+ ...message,
2477
+ content: message.content.map((part) => {
2478
+ if (part.type !== "tool-result" || part.output.type !== "content") return part;
2479
+ return { ...part, output: { ...part.output, value: contentFor(part.output.value, images) } };
2480
+ })
2481
+ };
2482
+ });
2483
+ }
2484
+ function contentFor(value, images) {
2485
+ if (images) return value.filter((item) => !isFallback(item));
2486
+ const fallbacks = value.filter(isFallback).map((item) => ({ type: "text", text: item.text }));
2487
+ const kept = value.filter((item) => !isImage(item) && !isFallback(item));
2488
+ if (fallbacks.length > 0) return [...kept, ...fallbacks];
2489
+ return value.some(isImage) ? [...kept, { type: "text", text: NO_IMAGE_NOTE }] : kept;
2490
+ }
2491
+
2344
2492
  // src/storage/chat-storage.ts
2345
2493
  var RELEASED_ANSWER_ALIVE_AT = (/* @__PURE__ */ new Date(0)).toISOString();
2346
2494
  function aliveAtByLocalClock(mark) {
@@ -2849,58 +2997,6 @@ function isEmptyTokenUsage(usage) {
2849
2997
  return usage.inputTokens === 0 && usage.outputTokens === 0 && usage.reasoningTokens === 0 && (usage.cacheWriteTokens ?? 0) === 0;
2850
2998
  }
2851
2999
 
2852
- // src/provider-errors/error-fields.ts
2853
- import { APICallError, RetryError } from "ai";
2854
- function underlyingError(error) {
2855
- return RetryError.isInstance(error) ? error.lastError : error;
2856
- }
2857
- function errorStatusCode(error) {
2858
- if (APICallError.isInstance(error)) return error.statusCode;
2859
- if (typeof error !== "object" || error === null) return void 0;
2860
- const fields = error;
2861
- const nested = typeof fields.error === "object" && fields.error !== null ? fields.error.code : void 0;
2862
- for (const value of [fields.statusCode, fields.status, fields.code, nested]) {
2863
- const code = asStatusCode(value);
2864
- if (code !== void 0) return code;
2865
- }
2866
- return void 0;
2867
- }
2868
- function asStatusCode(value) {
2869
- const code = typeof value === "number" ? value : typeof value === "string" && /^\s*\d{3}\s*$/.test(value) ? Number(value) : void 0;
2870
- return code !== void 0 && Number.isInteger(code) && code >= 100 && code <= 599 ? code : void 0;
2871
- }
2872
- function errorText(error) {
2873
- if (APICallError.isInstance(error)) return [error.responseBody, error.message].filter(Boolean).join("\n");
2874
- if (error instanceof Error) return error.message;
2875
- if (typeof error === "string") return error;
2876
- try {
2877
- return JSON.stringify(error) ?? "";
2878
- } catch {
2879
- return "";
2880
- }
2881
- }
2882
- function errorHeaders(error) {
2883
- return APICallError.isInstance(error) ? error.responseHeaders : void 0;
2884
- }
2885
- function errorName(error) {
2886
- const name = typeof error === "object" && error !== null ? error.name : void 0;
2887
- return typeof name === "string" && name !== "" ? name : void 0;
2888
- }
2889
- function requestedRetryMs(error, nowMs) {
2890
- const headers = errorHeaders(error);
2891
- if (!headers) return void 0;
2892
- let requestedMs;
2893
- const inMs = Number.parseFloat(headers["retry-after-ms"] ?? "");
2894
- if (!Number.isNaN(inMs)) requestedMs = inMs;
2895
- const retryAfter = headers["retry-after"];
2896
- if (requestedMs === void 0 && retryAfter) {
2897
- const inSeconds = Number.parseFloat(retryAfter);
2898
- requestedMs = Number.isNaN(inSeconds) ? Date.parse(retryAfter) - nowMs : inSeconds * 1e3;
2899
- }
2900
- if (requestedMs === void 0 || Number.isNaN(requestedMs) || requestedMs < 0) return void 0;
2901
- return requestedMs;
2902
- }
2903
-
2904
3000
  // src/provider-errors/error-signs.ts
2905
3001
  var signs = (patterns) => new RegExp(patterns.join("|"), "i");
2906
3002
  var OVERFLOW_SIGNS = [
@@ -3406,7 +3502,7 @@ function usageMinusJournaled(usage, journaled) {
3406
3502
  }
3407
3503
 
3408
3504
  // src/agent-run/build-model-request.ts
3409
- import { convertToModelMessages, wrapLanguageModel as wrapLanguageModel5 } from "ai";
3505
+ import { convertToModelMessages, wrapLanguageModel as wrapLanguageModel6 } from "ai";
3410
3506
 
3411
3507
  // src/conversation/hanging-tool-calls.ts
3412
3508
  var PRELIMINARY_OUTPUT_MAX_LENGTH = 2e3;
@@ -3626,7 +3722,7 @@ ${chunk.delta}` } : chunk];
3626
3722
  }
3627
3723
 
3628
3724
  // src/agent-run/observe-model-calls.ts
3629
- import { wrapLanguageModel as wrapLanguageModel3 } from "ai";
3725
+ import { wrapLanguageModel as wrapLanguageModel4 } from "ai";
3630
3726
  function observeModelCalls(model, context) {
3631
3727
  if (typeof model === "string") return model;
3632
3728
  const now = context.now ?? Date.now;
@@ -3636,7 +3732,7 @@ function observeModelCalls(model, context) {
3636
3732
  attempts.set(params.prompt, attempt);
3637
3733
  return attempt;
3638
3734
  };
3639
- return wrapLanguageModel3({
3735
+ return wrapLanguageModel4({
3640
3736
  model,
3641
3737
  middleware: {
3642
3738
  specificationVersion: "v4",
@@ -4224,7 +4320,7 @@ function withoutUndelivered(history) {
4224
4320
  }
4225
4321
 
4226
4322
  // src/agent-run/garbled-tail.ts
4227
- import { wrapLanguageModel as wrapLanguageModel4 } from "ai";
4323
+ import { wrapLanguageModel as wrapLanguageModel5 } from "ai";
4228
4324
  var SIGN_ORDER = ["think", "role", "restart", "tool"];
4229
4325
  var TOKEN_SOURCE = String.raw`<\/?think>|<\|[^|\n<>]{1,40}\|>|<|[^|\n<>]{1,40}|>`;
4230
4326
  var TEMPLATE_TOKEN = new RegExp(TOKEN_SOURCE, "g");
@@ -4396,7 +4492,7 @@ function createGarbledTailGuard() {
4396
4492
  /** Модель попытки под присмотром: каждый запрос — новый шаг. */
4397
4493
  wrap(model) {
4398
4494
  if (typeof model === "string") return model;
4399
- return wrapLanguageModel4({
4495
+ return wrapLanguageModel5({
4400
4496
  model,
4401
4497
  middleware: {
4402
4498
  specificationVersion: "v4",
@@ -4519,7 +4615,12 @@ function withoutCodeAndQuotes(text) {
4519
4615
  async function buildModelRequest(input) {
4520
4616
  const found = findModel(input.catalog, input.modelId, input.providerId);
4521
4617
  const { profile, provider } = found;
4522
- const base = resolveLanguageModel(found, input.env, input.fetch);
4618
+ const base = withToolResultImagePolicy(resolveLanguageModel(found, input.env, input.fetch), {
4619
+ catalog: input.catalog,
4620
+ profile,
4621
+ provider,
4622
+ ...input.logError && { logError: input.logError }
4623
+ });
4523
4624
  const observed = input.observe ? observeModelCalls(base, {
4524
4625
  ...input.observe,
4525
4626
  chatId: input.chatId,
@@ -4624,7 +4725,7 @@ function withoutLoneSurrogatesDeep(value) {
4624
4725
  }
4625
4726
  function withRequestWatch(model, provider, watch) {
4626
4727
  if (!watch || typeof model === "string") return model;
4627
- return wrapLanguageModel5({
4728
+ return wrapLanguageModel6({
4628
4729
  model,
4629
4730
  middleware: {
4630
4731
  specificationVersion: "v4",
@@ -4644,7 +4745,7 @@ function withRequestWatch(model, provider, watch) {
4644
4745
  });
4645
4746
  }
4646
4747
  function withTransientRetries(model, context) {
4647
- return typeof model === "string" ? model : wrapLanguageModel5({ model, middleware: createTransientRetryMiddleware(context) });
4748
+ return typeof model === "string" ? model : wrapLanguageModel6({ model, middleware: createTransientRetryMiddleware(context) });
4648
4749
  }
4649
4750
  function withLastMessageMark(messages, options2) {
4650
4751
  if (!options2 || messages.length === 0) return messages;
@@ -8218,6 +8319,7 @@ function createDeepSeekProvider(connectionOptions = {}) {
8218
8319
  // незнакомые поля не шлёт: `samplingRequest` нет, `sampling.minP` модели сюда не уходит.
8219
8320
  // Кончившиеся деньги DeepSeek описывает телом ответа; код бывает не только 402.
8220
8321
  errorHints: [{ match: /insufficient balance/i, kind: "insufficient_funds" }],
8322
+ toolResultImages: true,
8221
8323
  ...connectionField(connection),
8222
8324
  ...silenceField("DeepSeek", connectionOptions),
8223
8325
  ...hiddenField("DeepSeek", connectionOptions)
@@ -8243,7 +8345,7 @@ function sdkReasoningRequest(level) {
8243
8345
  }
8244
8346
 
8245
8347
  // src/models/providers/tool-result-images.ts
8246
- function withToolResultImages(given) {
8348
+ function withToolResultImages2(given) {
8247
8349
  return (async (input, init) => {
8248
8350
  const fetchImplementation = given ?? globalThis.fetch;
8249
8351
  if (typeof init?.body !== "string" || !init.body.includes('"role":"tool"')) return fetchImplementation(input, init);
@@ -8320,7 +8422,7 @@ function createFireworksProvider(connectionOptions = {}) {
8320
8422
  id: "fireworks",
8321
8423
  displayName: "Fireworks",
8322
8424
  apiKeyEnv: "FIREWORKS_API_KEY",
8323
- createLanguageModel: (providerModelId, apiKey, options2) => createFireworks({ apiKey, fetch: withToolResultImages(options2?.fetch), ...connection })(providerModelId),
8425
+ createLanguageModel: (providerModelId, apiKey, options2) => createFireworks({ apiKey, fetch: withToolResultImages2(options2?.fetch), ...connection })(providerModelId),
8324
8426
  reasoningRequest: (level) => sdkReasoningRequest(level),
8325
8427
  requestOptions: ({ chatId }) => ({
8326
8428
  providerOptions: {
@@ -8328,6 +8430,7 @@ function createFireworksProvider(connectionOptions = {}) {
8328
8430
  },
8329
8431
  headers: { "x-session-affinity": sessionHeaderValue(chatId) }
8330
8432
  }),
8433
+ toolResultImages: true,
8331
8434
  ...connectionField(connection),
8332
8435
  ...silenceField("Fireworks", connectionOptions),
8333
8436
  ...hiddenField("Fireworks", connectionOptions)
@@ -8337,7 +8440,7 @@ var fireworksProvider = createFireworksProvider();
8337
8440
 
8338
8441
  // src/models/providers/openrouter.ts
8339
8442
  import { createOpenRouter } from "@openrouter/ai-sdk-provider";
8340
- import { wrapLanguageModel as wrapLanguageModel6 } from "ai";
8443
+ import { wrapLanguageModel as wrapLanguageModel7 } from "ai";
8341
8444
 
8342
8445
  // src/models/providers/openrouter-cache-mark-limit.ts
8343
8446
  function openrouterCacheMarkLimit(providerModelId) {
@@ -8575,6 +8678,7 @@ function createOpenRouterProvider(options2 = {}) {
8575
8678
  id,
8576
8679
  displayName,
8577
8680
  ...id !== OPENROUTER_ID && { servesModelsOf: OPENROUTER_ID },
8681
+ toolResultImages: true,
8578
8682
  ...connectionField(connection),
8579
8683
  ...silenceField("OpenRouter", options2),
8580
8684
  ...hiddenField("OpenRouter", options2),
@@ -8587,7 +8691,7 @@ function createOpenRouterProvider(options2 = {}) {
8587
8691
  createLanguageModel: (providerModelId, apiKey, requestOptions) => {
8588
8692
  const markLimit = openrouterCacheMarkLimit(providerModelId);
8589
8693
  const fetch = markLimit === void 0 ? requestOptions?.fetch : fetchWithCacheMarkLimit(requestOptions?.fetch, markLimit);
8590
- return wrapLanguageModel6({
8694
+ return wrapLanguageModel7({
8591
8695
  model: createOpenRouter({ apiKey, fetch, ...connection }).chat(providerModelId, routingOptions),
8592
8696
  middleware: [
8593
8697
  openrouterRoutingMiddleware(routing),
@@ -8692,10 +8796,11 @@ function createTogetherProvider(connectionOptions = {}) {
8692
8796
  id: "together",
8693
8797
  displayName: "Together AI",
8694
8798
  apiKeyEnv: "TOGETHER_API_KEY",
8695
- createLanguageModel: (providerModelId, apiKey, options2) => createTogetherAI({ apiKey, fetch: withToolResultImages(options2?.fetch), ...connection })(providerModelId),
8799
+ createLanguageModel: (providerModelId, apiKey, options2) => createTogetherAI({ apiKey, fetch: withToolResultImages2(options2?.fetch), ...connection })(providerModelId),
8696
8800
  reasoningRequest: (level) => sdkReasoningRequest(level),
8697
8801
  samplingRequest: ({ minP }) => minP === void 0 ? {} : { togetherai: { min_p: minP } },
8698
8802
  requestOptions: () => ({}),
8803
+ toolResultImages: true,
8699
8804
  ...connectionField(connection),
8700
8805
  ...silenceField("Together AI", connectionOptions),
8701
8806
  ...hiddenField("Together AI", connectionOptions)
@@ -10489,7 +10594,7 @@ function isPlainObject3(value) {
10489
10594
  }
10490
10595
 
10491
10596
  // src/memory/memory-after-answer.ts
10492
- import { wrapLanguageModel as wrapLanguageModel7 } from "ai";
10597
+ import { wrapLanguageModel as wrapLanguageModel8 } from "ai";
10493
10598
  var DRAIN_WAIT_MS = 5e3;
10494
10599
  function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turnObserver }) {
10495
10600
  const running = /* @__PURE__ */ new Set();
@@ -10534,7 +10639,7 @@ function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turn
10534
10639
  };
10535
10640
  }
10536
10641
  function withRetries(model, context) {
10537
- return typeof model === "string" ? model : wrapLanguageModel7({ model, middleware: createTransientRetryMiddleware(context) });
10642
+ return typeof model === "string" ? model : wrapLanguageModel8({ model, middleware: createTransientRetryMiddleware(context) });
10538
10643
  }
10539
10644
 
10540
10645
  // src/conversation/to-agent-chat-message.ts
@@ -14070,6 +14175,8 @@ export {
14070
14175
  serviceBlockName,
14071
14176
  checkServiceBlockFormats,
14072
14177
  cutForgedBlocks,
14178
+ toolImageFallbackText,
14179
+ modelMaySeeToolResultImages,
14073
14180
  RELEASED_ANSWER_ALIVE_AT,
14074
14181
  aliveAtByLocalClock,
14075
14182
  silenceBoundaryByLocalClock,
@@ -14169,4 +14276,4 @@ export {
14169
14276
  InvalidWidgetAnswerError,
14170
14277
  createChatService
14171
14278
  };
14172
- //# sourceMappingURL=chunk-WQZQZCAB.js.map
14279
+ //# sourceMappingURL=chunk-XQ5FJ2VT.js.map