@codehz/ai 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -227,7 +227,8 @@ declare const WarningCode: {
227
227
  readonly CONTENT_FILTER: "CONTENT_FILTER"; /** 多 choice 仅支持 index 0,其余忽略 */
228
228
  readonly MULTIPLE_CHOICES_IGNORED: "MULTIPLE_CHOICES_IGNORED"; /** MCP 审批流不被支持 */
229
229
  readonly MCP_APPROVAL_REQUIRED: "MCP_APPROVAL_REQUIRED"; /** provider 侧 response.failed 等失败 */
230
- readonly PROVIDER_FAILURE: "PROVIDER_FAILURE";
230
+ readonly PROVIDER_FAILURE: "PROVIDER_FAILURE"; /** 出站 opaque 超限被省略(避免下一轮 accept 自产毒) */
231
+ readonly OPAQUE_REPLAY_OMITTED: "OPAQUE_REPLAY_OMITTED";
231
232
  };
232
233
  type WarningCodeName = (typeof WarningCode)[keyof typeof WarningCode];
233
234
  /** 与 WarningCodeName 同义;保留以兼容既有 KnownWarningCode 命名 */
@@ -836,6 +837,11 @@ type HttpAdapterOptions = {
836
837
  fetch?: FetchFn; /** 额外请求头;后写覆盖内置鉴权 / Content-Type 等 */
837
838
  headers?: Record<string, string>; /** 额外 body 顶层字段;浅层合并,同名键可覆盖 */
838
839
  extraBody?: Record<string, unknown>;
840
+ /**
841
+ * 单条 opaque replay payload 体积上限(JSON.stringify 码元长度)。
842
+ * 默认 1 MiB;夹到 [1, 8 MiB] 硬顶。emit 与 accept 共用。
843
+ */
844
+ maxOpaquePayloadBytes?: number;
839
845
  };
840
846
  type HttpAdapterDefaults = {
841
847
  baseUrl: string;
@@ -849,6 +855,8 @@ declare abstract class HttpAdapterBase extends AdapterBase {
849
855
  protected fetchFn: FetchFn;
850
856
  protected headers: Record<string, string> | undefined;
851
857
  protected extraBody: Record<string, unknown> | undefined;
858
+ /** 已 clamp 的 opaque 体积上限(emit / accept 共用)。 */
859
+ protected maxOpaquePayloadBytes: number;
852
860
  constructor(options: HttpAdapterOptions, defaults: HttpAdapterDefaults);
853
861
  /** 合并内置 headers 与构造期自定义 headers。 */
854
862
  protected mergeHeaders(base: Record<string, string>): Record<string, string>;
package/dist/index.mjs CHANGED
@@ -74,7 +74,9 @@ const WarningCode = {
74
74
  /** MCP 审批流不被支持 */
75
75
  MCP_APPROVAL_REQUIRED: "MCP_APPROVAL_REQUIRED",
76
76
  /** provider 侧 response.failed 等失败 */
77
- PROVIDER_FAILURE: "PROVIDER_FAILURE"
77
+ PROVIDER_FAILURE: "PROVIDER_FAILURE",
78
+ /** 出站 opaque 超限被省略(避免下一轮 accept 自产毒) */
79
+ OPAQUE_REPLAY_OMITTED: "OPAQUE_REPLAY_OMITTED"
78
80
  };
79
81
  /**
80
82
  * 去重键:以 message 为主(与旧 string[] 行为一致)。
@@ -1558,10 +1560,23 @@ function applyExtraBody(body, extraBody) {
1558
1560
  /**
1559
1561
  * Adapter 边界安全辅助
1560
1562
  *
1561
- * - opaque replay 入站 envelope(大小 / 深度)
1563
+ * - opaque replay envelope(大小 / 深度;emit 与 accept 共用)
1562
1564
  * - provider HTTP 错误 body 出站脱敏
1563
1565
  */
1564
- const MAX_OPAQUE_PAYLOAD_BYTES = 65536;
1566
+ /** 默认单条 opaque payload 上限(JSON.stringify 的 UTF-16 码元长度)。 */
1567
+ const DEFAULT_MAX_OPAQUE_PAYLOAD_BYTES = 1 * 1024 * 1024;
1568
+ /** 硬顶:配置不可超过;挡住离谱 blob / DoS。 */
1569
+ const HARD_MAX_OPAQUE_PAYLOAD_BYTES = 8 * 1024 * 1024;
1570
+ /**
1571
+ * 将调用方配置的 opaque 上限夹到合法区间。
1572
+ * 非有限 / <1 → 默认;> HARD → HARD。
1573
+ */
1574
+ function clampOpaquePayloadLimit(maxBytes) {
1575
+ if (maxBytes === void 0 || !Number.isFinite(maxBytes)) return DEFAULT_MAX_OPAQUE_PAYLOAD_BYTES;
1576
+ const n = Math.floor(maxBytes);
1577
+ if (n < 1) return DEFAULT_MAX_OPAQUE_PAYLOAD_BYTES;
1578
+ return Math.min(n, HARD_MAX_OPAQUE_PAYLOAD_BYTES);
1579
+ }
1565
1580
  /** 测量 JSON 值嵌套深度(对象/数组);循环引用按已访问节点深度计。 */
1566
1581
  function measureJsonDepth(value, seen = /* @__PURE__ */ new WeakSet()) {
1567
1582
  if (value === null || typeof value !== "object") return 0;
@@ -1573,10 +1588,10 @@ function measureJsonDepth(value, seen = /* @__PURE__ */ new WeakSet()) {
1573
1588
  return 1 + maxChild;
1574
1589
  }
1575
1590
  /**
1576
- * Opaque replay 通用 envelope:必须是 object、体积 ≤ 64KB、深度 ≤ 8。
1577
- * 不校验 adapter 专用字段形状。
1591
+ * Opaque replay 通用 envelope:必须是 object、体积 ≤ limit、深度 ≤ 8。
1592
+ * 不校验 adapter 专用字段形状。emit / accept 共用。
1578
1593
  */
1579
- function validateOpaqueReplayEnvelope(payload) {
1594
+ function validateOpaqueReplayEnvelope(payload, options) {
1580
1595
  if (typeof payload !== "object" || payload === null) return {
1581
1596
  ok: false,
1582
1597
  reason: "payload must be an object"
@@ -1594,9 +1609,10 @@ function validateOpaqueReplayEnvelope(payload) {
1594
1609
  ok: false,
1595
1610
  reason: "payload is not JSON-serializable"
1596
1611
  };
1597
- if (raw.length > 65536) return {
1612
+ const maxBytes = clampOpaquePayloadLimit(options?.maxBytes);
1613
+ if (raw.length > maxBytes) return {
1598
1614
  ok: false,
1599
- reason: `opaque payload exceeds max size (${raw.length} > ${MAX_OPAQUE_PAYLOAD_BYTES})`
1615
+ reason: `opaque payload exceeds max size (${raw.length} > ${maxBytes})`
1600
1616
  };
1601
1617
  const depth = measureJsonDepth(payload);
1602
1618
  if (depth > 8) return {
@@ -1605,9 +1621,9 @@ function validateOpaqueReplayEnvelope(payload) {
1605
1621
  };
1606
1622
  return { ok: true };
1607
1623
  }
1608
- /** envelope 失败时抛 AIRequestError */
1609
- function assertOpaqueReplayEnvelope(payload) {
1610
- const result = validateOpaqueReplayEnvelope(payload);
1624
+ /** envelope 失败时抛 AIRequestError(入站 accept 路径)。 */
1625
+ function assertOpaqueReplayEnvelope(payload, options) {
1626
+ const result = validateOpaqueReplayEnvelope(payload, options);
1611
1627
  if (!result.ok) throw new AIRequestError(`Invalid opaque replay payload: ${result.reason}`, "INVALID_OPAQUE_REPLAY");
1612
1628
  }
1613
1629
  /**
@@ -1838,6 +1854,8 @@ var HttpAdapterBase = class extends AdapterBase {
1838
1854
  fetchFn;
1839
1855
  headers;
1840
1856
  extraBody;
1857
+ /** 已 clamp 的 opaque 体积上限(emit / accept 共用)。 */
1858
+ maxOpaquePayloadBytes;
1841
1859
  constructor(options, defaults) {
1842
1860
  super();
1843
1861
  this.apiKey = options.apiKey;
@@ -1845,6 +1863,7 @@ var HttpAdapterBase = class extends AdapterBase {
1845
1863
  this.fetchFn = options.fetch ?? globalThis.fetch;
1846
1864
  this.headers = options.headers;
1847
1865
  this.extraBody = options.extraBody;
1866
+ this.maxOpaquePayloadBytes = clampOpaquePayloadLimit(options.maxOpaquePayloadBytes);
1848
1867
  }
1849
1868
  /** 合并内置 headers 与构造期自定义 headers。 */
1850
1869
  mergeHeaders(base) {
@@ -2130,14 +2149,26 @@ function createNdjsonLineParser(isValid) {
2130
2149
  //#region src/provider/finalize-stream-turn.ts
2131
2150
  /**
2132
2151
  * 收敛 incomplete / finish 后的 replay + complete 路径。
2133
- * adapter 负责构造 opaque payload;本 helper 统一拼接 replay 并 complete。
2152
+ * adapter 负责构造 opaque payload;本 helper 统一校验体积、拼接 replay 并 complete。
2153
+ *
2154
+ * emit 与 accept 共用 envelope 上限:超限则省略 opaque(不截断)并打 warning,
2155
+ * 避免写出下一轮 accept 必炸的自产毒。
2134
2156
  */
2135
2157
  /**
2136
2158
  * 从 item session 生成 canonical replay,可选追加 opaque 尾项,再 yield session.complete。
2159
+ * 超限 opaque 会被丢弃并 yield `OPAQUE_REPLAY_OMITTED` warning。
2137
2160
  */
2138
2161
  async function* finalizeStreamTurn(session, items, options = {}) {
2139
2162
  const replay = [...replayFromOutput(items.completedItems())];
2140
- if (options.opaque) replay.push(options.opaque);
2163
+ let opaque = options.opaque ?? null;
2164
+ if (opaque) {
2165
+ const check = validateOpaqueReplayEnvelope(opaque.payload, { maxBytes: options.maxOpaquePayloadBytes });
2166
+ if (!check.ok) {
2167
+ if (options.factory) yield options.factory.responseWarning(`Omitted opaque replay payload: ${check.reason}`, WarningCode.OPAQUE_REPLAY_OMITTED);
2168
+ opaque = null;
2169
+ }
2170
+ }
2171
+ if (opaque) replay.push(opaque);
2141
2172
  yield* session.complete({
2142
2173
  replay,
2143
2174
  stopReason: options.stopReason,
@@ -2163,7 +2194,7 @@ const OPAQUE_SOURCE = {
2163
2194
  * Opaque replay 统一协议(入站薄层)
2164
2195
  *
2165
2196
  * 过滤:仅 `source === expectedSource` 且 `purpose === "replay"` 才处理,否则忽略。
2166
- * envelope:object / ≤64KB / depth≤8;失败抛 AIRequestError / INVALID_OPAQUE_REPLAY。
2197
+ * envelope:object / ≤limit(默认 1MiB,硬顶 8MiB)/ depth≤8;失败抛 AIRequestError / INVALID_OPAQUE_REPLAY。
2167
2198
  * 写入 wire 尾部 assistant/model turn 前:先 rollbackTrailing*,再 append(responses 续写 id 除外)。
2168
2199
  * 已知 shape 非法 → 抛 INVALID_OPAQUE_REPLAY;未知 shape → 静默跳过。
2169
2200
  */
@@ -2171,9 +2202,9 @@ const OPAQUE_SOURCE = {
2171
2202
  * 接受本 adapter 的 opaque replay payload。
2172
2203
  * source/purpose 不匹配返回 null;匹配则 assert envelope 后返回 payload object。
2173
2204
  */
2174
- function acceptOpaqueReplay(item, expectedSource) {
2205
+ function acceptOpaqueReplay(item, expectedSource, options) {
2175
2206
  if (item.source !== expectedSource || item.purpose !== "replay") return null;
2176
- assertOpaqueReplayEnvelope(item.payload);
2207
+ assertOpaqueReplayEnvelope(item.payload, options);
2177
2208
  return item.payload;
2178
2209
  }
2179
2210
  //#endregion
@@ -2465,7 +2496,7 @@ function appendCompactedWindow(input, payload) {
2465
2496
  * stream / compact 共享的 input + instructions + opaque 续写映射。
2466
2497
  * compact 不附带 tools / stream 等生成字段。
2467
2498
  */
2468
- function mapResponsesCore(request) {
2499
+ function mapResponsesCore(request, options) {
2469
2500
  const input = [];
2470
2501
  let previousResponseId;
2471
2502
  let usedCompactedWindow = false;
@@ -2499,7 +2530,7 @@ function mapResponsesCore(request) {
2499
2530
  break;
2500
2531
  }
2501
2532
  case "opaque": {
2502
- const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.RESPONSES);
2533
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.RESPONSES, { maxBytes: options?.maxOpaquePayloadBytes });
2503
2534
  if (!payload) break;
2504
2535
  if (payload.kind === "compacted_window") {
2505
2536
  assertOptionalIdFields(payload);
@@ -2530,8 +2561,8 @@ function mapResponsesCore(request) {
2530
2561
  return mapped;
2531
2562
  }
2532
2563
  /** 构建 Responses 流式请求体;调用方再 `withExtraBody` 合并构造期扩展字段。 */
2533
- function buildResponsesRequest(request) {
2534
- const core = mapResponsesCore(request);
2564
+ function buildResponsesRequest(request, options) {
2565
+ const core = mapResponsesCore(request, options);
2535
2566
  const body = {
2536
2567
  model: request.model,
2537
2568
  input: core.input,
@@ -2566,10 +2597,10 @@ function buildResponsesRequest(request) {
2566
2597
  * 构建 Responses compact 请求体(POST /responses/compact)。
2567
2598
  * 仅映射 model / input / instructions / previous_response_id;无 stream / tools。
2568
2599
  */
2569
- function buildResponsesCompactRequest(request) {
2600
+ function buildResponsesCompactRequest(request, options) {
2570
2601
  if (!request.model || typeof request.model !== "string" || request.model.length === 0) throw new AIRequestError("compress requires a non-empty model", "INPUT_EMPTY");
2571
2602
  if (!Array.isArray(request.input) || request.input.length === 0) throw new AIRequestError("compress requires a non-empty input", "INPUT_EMPTY");
2572
- const core = mapResponsesCore(request);
2603
+ const core = mapResponsesCore(request, options);
2573
2604
  const body = {
2574
2605
  model: request.model,
2575
2606
  input: core.input
@@ -3321,7 +3352,7 @@ var ResponsesAdapter = class extends HttpAdapterBase {
3321
3352
  super(options, { baseUrl: "https://api.openai.com/v1" });
3322
3353
  }
3323
3354
  buildRequest(request) {
3324
- return this.withExtraBody(buildResponsesRequest(request));
3355
+ return this.withExtraBody(buildResponsesRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
3325
3356
  }
3326
3357
  /**
3327
3358
  * 原生上下文压缩:POST /responses/compact。
@@ -3329,7 +3360,7 @@ var ResponsesAdapter = class extends HttpAdapterBase {
3329
3360
  */
3330
3361
  async compress(request) {
3331
3362
  request.signal?.throwIfAborted();
3332
- const body = this.withExtraBody(buildResponsesCompactRequest(request));
3363
+ const body = this.withExtraBody(buildResponsesCompactRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
3333
3364
  const { data } = await postProviderJson({
3334
3365
  fetchFn: this.fetchFn,
3335
3366
  url: `${this.baseUrl}/responses/compact`,
@@ -3346,6 +3377,7 @@ var ResponsesAdapter = class extends HttpAdapterBase {
3346
3377
  output: data.output
3347
3378
  };
3348
3379
  if (typeof data.id === "string" && data.id.length > 0 && data.id.length <= 256) payload.id = data.id;
3380
+ assertOpaqueReplayEnvelope(payload, { maxBytes: this.maxOpaquePayloadBytes });
3349
3381
  const result = { replay: [opaqueItem(OPAQUE_SOURCE.RESPONSES, "replay", payload)] };
3350
3382
  if (data.usage) {
3351
3383
  const usage = usageFromOpenAIResponses(data.usage);
@@ -3394,6 +3426,8 @@ var ResponsesAdapter = class extends HttpAdapterBase {
3394
3426
  }) : null,
3395
3427
  stopReason,
3396
3428
  rawResponseId,
3429
+ factory,
3430
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes,
3397
3431
  onDuplicate: "silent"
3398
3432
  });
3399
3433
  }
@@ -3436,7 +3470,7 @@ function canonicalToMessagesBlock(b) {
3436
3470
  };
3437
3471
  throw new AIRequestError(`messages does not support content block type "${b.type}" in canonical mapping`, "UNSUPPORTED_CONTENT_BLOCK");
3438
3472
  }
3439
- function buildMessagesRequest(request) {
3473
+ function buildMessagesRequest(request, options) {
3440
3474
  mapper$7.assertNoServerTools(request.serverTools);
3441
3475
  const messages = [];
3442
3476
  let systemPrompt;
@@ -3505,7 +3539,7 @@ function buildMessagesRequest(request) {
3505
3539
  break;
3506
3540
  }
3507
3541
  case "opaque": {
3508
- const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.MESSAGES);
3542
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.MESSAGES, { maxBytes: options?.maxOpaquePayloadBytes });
3509
3543
  if (!payload) break;
3510
3544
  if (payload.role === "assistant" && "content" in payload) {
3511
3545
  assertMessagesReplayContent(payload.content);
@@ -3743,6 +3777,8 @@ async function* mapMessagesStream(host, providerRequest, factory, request) {
3743
3777
  }) : null,
3744
3778
  stopReason: stopReason ? mapStopReason(stopReason) : void 0,
3745
3779
  rawResponseId,
3780
+ factory,
3781
+ maxOpaquePayloadBytes: host.maxOpaquePayloadBytes,
3746
3782
  onDuplicate: "silent"
3747
3783
  });
3748
3784
  }
@@ -3762,7 +3798,7 @@ var MessagesAdapter = class extends HttpAdapterBase {
3762
3798
  this.apiVersion = options.apiVersion ?? "2023-06-01";
3763
3799
  }
3764
3800
  buildRequest(request) {
3765
- return this.withExtraBody(buildMessagesRequest(request));
3801
+ return this.withExtraBody(buildMessagesRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
3766
3802
  }
3767
3803
  async *runStream(providerRequest, factory, request) {
3768
3804
  yield* mapMessagesStream({
@@ -3770,7 +3806,8 @@ var MessagesAdapter = class extends HttpAdapterBase {
3770
3806
  baseUrl: this.baseUrl,
3771
3807
  apiKey: this.apiKey,
3772
3808
  mergeHeaders: this.mergeHeaders.bind(this),
3773
- apiVersion: this.apiVersion
3809
+ apiVersion: this.apiVersion,
3810
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes
3774
3811
  }, providerRequest, factory, request);
3775
3812
  }
3776
3813
  };
@@ -3821,7 +3858,7 @@ function assertChatReplayMessages(messages, field) {
3821
3858
  if (!Array.isArray(messages)) throw new AIRequestError(`Invalid opaque replay payload: ${field} must be an array`, "INVALID_OPAQUE_REPLAY");
3822
3859
  for (let i = 0; i < messages.length; i++) if (!isChatReplayMessage(messages[i])) throw new AIRequestError(`Invalid opaque replay payload: ${field}[${i}] is not a valid chat message`, "INVALID_OPAQUE_REPLAY");
3823
3860
  }
3824
- function buildChatCompletionsRequest(request) {
3861
+ function buildChatCompletionsRequest(request, options) {
3825
3862
  mapper$5.assertNoServerTools(request.serverTools);
3826
3863
  const messages = [];
3827
3864
  if (request.instructions) messages.push({
@@ -3871,7 +3908,7 @@ function buildChatCompletionsRequest(request) {
3871
3908
  });
3872
3909
  break;
3873
3910
  case "opaque": {
3874
- const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.CHAT_COMPLETIONS);
3911
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.CHAT_COMPLETIONS, { maxBytes: options?.maxOpaquePayloadBytes });
3875
3912
  if (!payload) break;
3876
3913
  if ("messages" in payload) {
3877
3914
  assertChatReplayMessages(payload.messages, "messages");
@@ -4019,6 +4056,8 @@ async function* mapChatCompletionsStream(host, providerRequest, factory, request
4019
4056
  yield* finalizeStreamTurn(session, items, {
4020
4057
  stopReason,
4021
4058
  rawResponseId,
4059
+ factory,
4060
+ maxOpaquePayloadBytes: host.maxOpaquePayloadBytes,
4022
4061
  opaque: assistantReplayMessage ? opaqueItem(OPAQUE_SOURCE.CHAT_COMPLETIONS, "replay", {
4023
4062
  replaceCanonical: true,
4024
4063
  messages: [assistantReplayMessage]
@@ -4171,14 +4210,15 @@ var ChatCompletionsAdapter = class extends HttpAdapterBase {
4171
4210
  super(options, { baseUrl: "https://api.openai.com/v1" });
4172
4211
  }
4173
4212
  buildRequest(request) {
4174
- return this.withExtraBody(buildChatCompletionsRequest(request));
4213
+ return this.withExtraBody(buildChatCompletionsRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
4175
4214
  }
4176
4215
  async *runStream(providerRequest, factory, request) {
4177
4216
  yield* mapChatCompletionsStream({
4178
4217
  beginJsonStream: this.beginJsonStream.bind(this),
4179
4218
  baseUrl: this.baseUrl,
4180
4219
  apiKey: this.apiKey,
4181
- mergeHeaders: this.mergeHeaders.bind(this)
4220
+ mergeHeaders: this.mergeHeaders.bind(this),
4221
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes
4182
4222
  }, providerRequest, factory, request);
4183
4223
  }
4184
4224
  };
@@ -4203,7 +4243,7 @@ function toWireOllamaToolCalls(toolCalls) {
4203
4243
  arguments: tc.function.arguments
4204
4244
  } }));
4205
4245
  }
4206
- function buildOllamaRequest(request) {
4246
+ function buildOllamaRequest(request, options) {
4207
4247
  mapper$3.assertNoServerTools(request.serverTools);
4208
4248
  const messages = [];
4209
4249
  /** Local-only name → call id queue for best-effort tool_result association (not sent to Ollama). */
@@ -4254,7 +4294,7 @@ function buildOllamaRequest(request) {
4254
4294
  });
4255
4295
  break;
4256
4296
  case "opaque": {
4257
- const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.OLLAMA);
4297
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.OLLAMA, { maxBytes: options?.maxOpaquePayloadBytes });
4258
4298
  if (!payload) break;
4259
4299
  if (payload.role === "assistant" && typeof payload.content === "string") {
4260
4300
  mapper$3.rollbackTrailingAssistantMessages(messages);
@@ -4339,6 +4379,8 @@ async function* mapOllamaStream(host, providerRequest, factory, request) {
4339
4379
  yield* finalizeStreamTurn(session, items, {
4340
4380
  stopReason,
4341
4381
  rawResponseId,
4382
+ factory,
4383
+ maxOpaquePayloadBytes: host.maxOpaquePayloadBytes,
4342
4384
  opaque: accumulatedContent || pendingToolCalls.length > 0 ? opaqueItem(OPAQUE_SOURCE.OLLAMA, "replay", {
4343
4385
  role: "assistant",
4344
4386
  content: accumulatedContent,
@@ -4417,14 +4459,15 @@ var OllamaAdapter = class extends HttpAdapterBase {
4417
4459
  super(options, { baseUrl: "http://localhost:11434" });
4418
4460
  }
4419
4461
  buildRequest(request) {
4420
- return this.withExtraBody(buildOllamaRequest(request));
4462
+ return this.withExtraBody(buildOllamaRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
4421
4463
  }
4422
4464
  async *runStream(providerRequest, factory, request) {
4423
4465
  yield* mapOllamaStream({
4424
4466
  beginJsonStream: this.beginJsonStream.bind(this),
4425
4467
  baseUrl: this.baseUrl,
4426
4468
  apiKey: this.apiKey,
4427
- mergeHeaders: this.mergeHeaders.bind(this)
4469
+ mergeHeaders: this.mergeHeaders.bind(this),
4470
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes
4428
4471
  }, providerRequest, factory, request);
4429
4472
  }
4430
4473
  };
@@ -4474,7 +4517,7 @@ function cloneContent(content) {
4474
4517
  parts: content.parts.map(clonePart$1)
4475
4518
  };
4476
4519
  }
4477
- function buildGeminiRequest(request) {
4520
+ function buildGeminiRequest(request, options) {
4478
4521
  mapper$1.assertNoServerTools(request.serverTools);
4479
4522
  const contents = [];
4480
4523
  let systemInstruction;
@@ -4515,7 +4558,7 @@ function buildGeminiRequest(request) {
4515
4558
  });
4516
4559
  break;
4517
4560
  case "opaque": {
4518
- const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.GEMINI);
4561
+ const payload = acceptOpaqueReplay(item, OPAQUE_SOURCE.GEMINI, { maxBytes: options?.maxOpaquePayloadBytes });
4519
4562
  if (!payload) break;
4520
4563
  if (payload.replaceCanonical === true && "content" in payload) {
4521
4564
  assertGeminiReplayContent(payload.content, "content");
@@ -4628,6 +4671,8 @@ async function* mapGeminiStream(host, providerRequest, factory, request) {
4628
4671
  yield* finalizeStreamTurn(session, items, {
4629
4672
  stopReason: reason,
4630
4673
  rawResponseId,
4674
+ factory,
4675
+ maxOpaquePayloadBytes: host.maxOpaquePayloadBytes,
4631
4676
  opaque: replayParts.length > 0 ? opaqueItem(OPAQUE_SOURCE.GEMINI, "replay", {
4632
4677
  replaceCanonical: true,
4633
4678
  content: {
@@ -4713,14 +4758,15 @@ var GeminiAdapter = class extends HttpAdapterBase {
4713
4758
  super(options, { baseUrl: "https://generativelanguage.googleapis.com/v1beta" });
4714
4759
  }
4715
4760
  buildRequest(request) {
4716
- return this.withExtraBody(buildGeminiRequest(request));
4761
+ return this.withExtraBody(buildGeminiRequest(request, { maxOpaquePayloadBytes: this.maxOpaquePayloadBytes }));
4717
4762
  }
4718
4763
  async *runStream(providerRequest, factory, request) {
4719
4764
  yield* mapGeminiStream({
4720
4765
  beginJsonStream: this.beginJsonStream.bind(this),
4721
4766
  baseUrl: this.baseUrl,
4722
4767
  apiKey: this.apiKey,
4723
- mergeHeaders: this.mergeHeaders.bind(this)
4768
+ mergeHeaders: this.mergeHeaders.bind(this),
4769
+ maxOpaquePayloadBytes: this.maxOpaquePayloadBytes
4724
4770
  }, providerRequest, factory, request);
4725
4771
  }
4726
4772
  };