billion-context 0.1.72 → 0.1.73-pr.440.290

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -51466,8 +51466,26 @@ function recordUsage(ctx, usage, round) {
51466
51466
  async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adapter, systemPrompt, signal) {
51467
51467
  let activeClearTimer = null;
51468
51468
  let currentUpstream = upstream;
51469
+ let roundBody = requestBody;
51469
51470
  const coreMessages = [...ctx.messages];
51470
51471
  let degradedRetried = false;
51472
+ let truncationRetried = false;
51473
+ const fetchUpstream = (body) => fetchWithRetry(
51474
+ requestOptions.url,
51475
+ {
51476
+ method: "POST",
51477
+ headers: requestOptions.headers,
51478
+ body: JSON.stringify(body),
51479
+ ...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
51480
+ },
51481
+ void 0,
51482
+ signal,
51483
+ (info) => {
51484
+ const lc = lastCompressSuffix(ctx.session.lastCompress);
51485
+ ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0, 120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);
51486
+ log("warn", `[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`);
51487
+ }
51488
+ );
51471
51489
  try {
51472
51490
  for (let round = 1; round <= MAX_LOOP_ROUNDS; round++) {
51473
51491
  if (signal?.aborted) break;
@@ -51480,50 +51498,93 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51480
51498
  let finishReason;
51481
51499
  let sawDone = false;
51482
51500
  let suppressCompletion = false;
51483
- for await (const ev of adapter.parseStream(currentUpstream, round)) {
51484
- if (signal?.aborted) break;
51485
- if (ev.kind === "text") {
51486
- assistantText += ev.delta;
51487
- if (!ctx.textProtocol && ev.raw) {
51488
- yield ev.raw;
51489
- } else if (!ctx.textProtocol && round > 1 && ev.delta.length > 0) {
51490
- yield adapter.emitText(ev.delta);
51491
- }
51492
- } else if (ev.kind === "reasoning") {
51493
- assistantReasoning += ev.delta;
51494
- let seg = reasoningSegments[reasoningSegments.length - 1];
51495
- if (reasoningSealed || !seg) {
51496
- seg = { text: "", signature: "" };
51497
- reasoningSegments.push(seg);
51498
- reasoningSealed = false;
51499
- }
51500
- seg.text += ev.delta;
51501
- if (ev.signature) seg.signature += ev.signature;
51502
- if (ev.blockEnd) reasoningSealed = true;
51503
- if (!ctx.textProtocol) {
51504
- if (ev.raw) {
51505
- yield ev.raw;
51506
- } else if (round > 1 && ev.delta.length > 0 && adapter.emitReasoning) {
51507
- yield adapter.emitReasoning(ev.delta);
51508
- }
51509
- }
51510
- } else if (ev.kind === "tool_call") {
51511
- calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
51512
- } else if (ev.kind === "usage") {
51513
- usage = {
51514
- inputTokens: ev.inputTokens,
51515
- outputTokens: ev.outputTokens,
51516
- cachedTokens: ev.cachedTokens
51517
- };
51518
- } else if (ev.kind === "done") {
51519
- sawDone = true;
51520
- finishReason = ev.finishReason;
51521
- suppressCompletion = ev.suppressCompletion === true;
51522
- } else if (ev.kind === "meta") {
51523
- if (round === 1 || !ev.firstRoundOnly) {
51524
- yield ev.chunk;
51501
+ let truncatedDone = false;
51502
+ let forwardedAny = false;
51503
+ const fwd = (chunk) => {
51504
+ forwardedAny = true;
51505
+ return chunk;
51506
+ };
51507
+ for (; ; ) {
51508
+ assistantText = "";
51509
+ assistantReasoning = "";
51510
+ reasoningSegments.length = 0;
51511
+ reasoningSealed = true;
51512
+ calls.length = 0;
51513
+ usage = {};
51514
+ finishReason = void 0;
51515
+ sawDone = false;
51516
+ suppressCompletion = false;
51517
+ truncatedDone = false;
51518
+ forwardedAny = false;
51519
+ for await (const ev of adapter.parseStream(currentUpstream, round)) {
51520
+ if (signal?.aborted) break;
51521
+ if (ev.kind === "text") {
51522
+ assistantText += ev.delta;
51523
+ if (!ctx.textProtocol && ev.raw) {
51524
+ yield fwd(ev.raw);
51525
+ } else if (!ctx.textProtocol && round > 1 && ev.delta.length > 0) {
51526
+ yield fwd(adapter.emitText(ev.delta));
51527
+ }
51528
+ } else if (ev.kind === "reasoning") {
51529
+ assistantReasoning += ev.delta;
51530
+ let seg = reasoningSegments[reasoningSegments.length - 1];
51531
+ if (reasoningSealed || !seg) {
51532
+ seg = { text: "", signature: "" };
51533
+ reasoningSegments.push(seg);
51534
+ reasoningSealed = false;
51535
+ }
51536
+ seg.text += ev.delta;
51537
+ if (ev.signature) seg.signature += ev.signature;
51538
+ if (ev.blockEnd) reasoningSealed = true;
51539
+ if (!ctx.textProtocol) {
51540
+ if (ev.raw) {
51541
+ yield fwd(ev.raw);
51542
+ } else if (round > 1 && ev.delta.length > 0 && adapter.emitReasoning) {
51543
+ yield fwd(adapter.emitReasoning(ev.delta));
51544
+ }
51545
+ }
51546
+ } else if (ev.kind === "tool_call") {
51547
+ calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
51548
+ } else if (ev.kind === "usage") {
51549
+ usage = {
51550
+ inputTokens: ev.inputTokens,
51551
+ outputTokens: ev.outputTokens,
51552
+ cachedTokens: ev.cachedTokens
51553
+ };
51554
+ } else if (ev.kind === "done") {
51555
+ sawDone = true;
51556
+ finishReason = ev.finishReason;
51557
+ suppressCompletion = ev.suppressCompletion === true;
51558
+ truncatedDone = ev.truncated === true;
51559
+ } else if (ev.kind === "meta") {
51560
+ if (round === 1 || !ev.firstRoundOnly) {
51561
+ yield fwd(ev.chunk);
51562
+ }
51563
+ }
51564
+ }
51565
+ if ((!sawDone || truncatedDone) && !forwardedAny && calls.length === 0 && !(ctx.textProtocol && assistantText.length > 0) && !signal?.aborted && !truncationRetried) {
51566
+ truncationRetried = true;
51567
+ ctx.log(`[acp-loop] round ${round}: upstream truncated before any content reached the client; retrying fetch once`);
51568
+ try {
51569
+ const respResult2 = await fetchUpstream(roundBody);
51570
+ if (!respResult2.response.body) {
51571
+ respResult2.clearTimer();
51572
+ throw new UpstreamHttpError(respResult2.response.status, "(empty response body)", 1);
51573
+ }
51574
+ currentUpstream = respResult2.response.body;
51575
+ if (activeClearTimer) activeClearTimer();
51576
+ activeClearTimer = respResult2.clearTimer;
51577
+ continue;
51578
+ } catch (e) {
51579
+ if (e instanceof UpstreamHttpError) {
51580
+ const suffix = e.attempts > 1 ? ` after ${e.attempts} attempt(s)` : "";
51581
+ ctx.log(`[acp-loop] round ${round}: truncation retry failed (upstream error ${e.status}${suffix}: ${e.body.slice(0, 200)})`);
51582
+ } else {
51583
+ ctx.log(`[acp-loop] round ${round}: truncation retry failed (${e instanceof Error ? e.message : String(e)})`);
51584
+ }
51525
51585
  }
51526
51586
  }
51587
+ break;
51527
51588
  }
51528
51589
  if (usage.inputTokens !== void 0 || usage.outputTokens !== void 0 || usage.cachedTokens !== void 0) {
51529
51590
  recordUsage(ctx, usage, round);
@@ -51657,7 +51718,6 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51657
51718
  const partialReasoning = assistantReasoning.length;
51658
51719
  const msg = `upstream stream truncated (no completion event; round ${round}, ${partialText} text chars + ${partialReasoning} reasoning chars received)`;
51659
51720
  ctx.log(`[acp-loop] round ${round}: ${msg}`);
51660
- log("error", `[acp-loop] ${msg}`);
51661
51721
  yield adapter.emitError(msg);
51662
51722
  return;
51663
51723
  }
@@ -51674,22 +51734,6 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51674
51734
  }
51675
51735
  ctx.log(`[acp-loop] round ${round}: proxy tool executed; re-requesting so the model sees the result`);
51676
51736
  if (signal?.aborted) break;
51677
- const fetchUpstream = (body) => fetchWithRetry(
51678
- requestOptions.url,
51679
- {
51680
- method: "POST",
51681
- headers: requestOptions.headers,
51682
- body: JSON.stringify(body),
51683
- ...ctx.proxyUrl ? { dispatcher: proxyDispatcher(ctx.proxyUrl) } : {}
51684
- },
51685
- void 0,
51686
- signal,
51687
- (info) => {
51688
- const lc = lastCompressSuffix(ctx.session.lastCompress);
51689
- ctx.log(`[acp-proxy: upstream rejected replay (HTTP ${info.status}: ${info.detail.slice(0, 120)}); likely provider risk-control \u2014 retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}]`);
51690
- log("warn", `[acp-loop] upstream rejected replay (HTTP ${info.status}); retrying in ${info.delayMs}ms (attempt ${info.attempt}/${info.maxAttempts})${lc}`);
51691
- }
51692
- );
51693
51737
  let newBody = adapter.buildRequest(coreMessages, systemPrompt, requestBody);
51694
51738
  if (process.env.ACP_DUMP_BODY === "1") {
51695
51739
  try {
@@ -51732,6 +51776,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
51732
51776
  return;
51733
51777
  }
51734
51778
  currentUpstream = respResult.response.body;
51779
+ roundBody = newBody;
51735
51780
  if (activeClearTimer) activeClearTimer();
51736
51781
  activeClearTimer = respResult.clearTimer;
51737
51782
  }
@@ -52056,6 +52101,20 @@ function createResponsesAdapter(textProtocol, projection) {
52056
52101
  let responseObj = null;
52057
52102
  let terminalRaw = null;
52058
52103
  let terminalKind = null;
52104
+ let createdForwarded = false;
52105
+ let createdRespId = null;
52106
+ const ensureCreated = () => {
52107
+ if (createdForwarded) return null;
52108
+ createdForwarded = true;
52109
+ if (!createdRespId) createdRespId = `resp-proxy-${Date.now()}`;
52110
+ return Buffer.from(
52111
+ `event: response.created
52112
+ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId, status: "in_progress", output: [] } })}
52113
+
52114
+ `,
52115
+ "utf8"
52116
+ );
52117
+ };
52059
52118
  return {
52060
52119
  buildRequest(coreMessages, systemPrompt, requestBody) {
52061
52120
  const customToolCallIds = /* @__PURE__ */ new Set();
@@ -52110,6 +52169,11 @@ function createResponsesAdapter(textProtocol, projection) {
52110
52169
  outputIndex = Math.max(outputIndex, obj.output_index + 1);
52111
52170
  }
52112
52171
  if (type === "response.created" || type === "response.in_progress") {
52172
+ if (type === "response.created") {
52173
+ const resp = obj.response;
52174
+ if (resp && typeof resp.id === "string") createdRespId = resp.id;
52175
+ if (round === 1) createdForwarded = true;
52176
+ }
52113
52177
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
52114
52178
  } else if (type === "response.output_item.added") {
52115
52179
  const item = obj.item;
@@ -52243,7 +52307,7 @@ function createResponsesAdapter(textProtocol, projection) {
52243
52307
  }
52244
52308
  }
52245
52309
  if (!terminalKind) {
52246
- yield { kind: "done", finishReason: "failed" };
52310
+ yield { kind: "done", finishReason: "failed", truncated: true };
52247
52311
  }
52248
52312
  },
52249
52313
  emitText(delta) {
@@ -52266,18 +52330,22 @@ function createResponsesAdapter(textProtocol, projection) {
52266
52330
  return terminalRaw;
52267
52331
  }
52268
52332
  if (!responseObj && opts?.finishReason === "failed") {
52333
+ const parts = [];
52334
+ const created = ensureCreated();
52335
+ if (created) parts.push(created);
52269
52336
  const failed = {
52270
- id: `resp-error-${Date.now()}`,
52337
+ id: createdRespId ?? `resp-error-${Date.now()}`,
52271
52338
  status: "failed",
52272
52339
  error: { code: "server_error", message: "upstream returned no response" }
52273
52340
  };
52274
- return Buffer.from(
52341
+ parts.push(Buffer.from(
52275
52342
  `event: response.failed
52276
52343
  data: ${JSON.stringify({ type: "response.failed", response: failed })}
52277
52344
 
52278
52345
  `,
52279
52346
  "utf8"
52280
- );
52347
+ ));
52348
+ return Buffer.concat(parts);
52281
52349
  }
52282
52350
  let resp = responseObj ? { ...responseObj } : { id: `resp-proxy-${Date.now()}`, status: "completed", output: [] };
52283
52351
  if (opts?.usage) {
@@ -52297,18 +52365,22 @@ data: ${JSON.stringify({ type: "response.failed", response: failed })}
52297
52365
  return buildCompleted(resp);
52298
52366
  },
52299
52367
  emitError(message) {
52368
+ const parts = [];
52369
+ const created = ensureCreated();
52370
+ if (created) parts.push(created);
52300
52371
  const resp = {
52301
- id: `resp-error-${Date.now()}`,
52372
+ id: createdRespId ?? `resp-error-${Date.now()}`,
52302
52373
  status: "failed",
52303
52374
  error: { code: "server_error", message }
52304
52375
  };
52305
- return Buffer.from(
52376
+ parts.push(Buffer.from(
52306
52377
  `event: response.failed
52307
52378
  data: ${JSON.stringify({ type: "response.failed", response: resp })}
52308
52379
 
52309
52380
  `,
52310
52381
  "utf8"
52311
- );
52382
+ ));
52383
+ return Buffer.concat(parts);
52312
52384
  },
52313
52385
  extractTextTriggers(text) {
52314
52386
  const calls = [];
@@ -52818,6 +52890,33 @@ function createAnthropicAdapter(requestBody, originalSystem) {
52818
52890
  const model = requestBody.model ?? void 0;
52819
52891
  let messageId;
52820
52892
  let clientIndex = 0;
52893
+ let messageStartForwarded = false;
52894
+ const openBlocks = [];
52895
+ const removeOpenBlock = (index) => {
52896
+ const i = openBlocks.indexOf(index);
52897
+ if (i >= 0) openBlocks.splice(i, 1);
52898
+ };
52899
+ const buildSyntheticMessageStart = () => {
52900
+ const extra = {};
52901
+ if (model) extra.model = model;
52902
+ const msg = {
52903
+ id: messageId ?? `msg_acp_error_${Date.now()}`,
52904
+ type: "message",
52905
+ role: "assistant",
52906
+ content: [],
52907
+ stop_reason: null,
52908
+ stop_sequence: null,
52909
+ usage: { input_tokens: 0, output_tokens: 0 },
52910
+ ...extra
52911
+ };
52912
+ return Buffer.from(
52913
+ `event: message_start
52914
+ data: ${JSON.stringify({ type: "message_start", message: msg })}
52915
+
52916
+ `,
52917
+ "utf8"
52918
+ );
52919
+ };
52821
52920
  const buildTextBlock = (index, text) => Buffer.from(
52822
52921
  `event: content_block_start
52823
52922
  data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "text", text: "" } })}
@@ -52901,6 +53000,7 @@ ${systemPrompt}` : systemPrompt;
52901
53000
  if (typeof u2.input_tokens === "number") roundInput = u2.input_tokens;
52902
53001
  if (typeof u2.cache_read_input_tokens === "number") roundCached = u2.cache_read_input_tokens;
52903
53002
  if (round === 1) {
53003
+ messageStartForwarded = true;
52904
53004
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
52905
53005
  }
52906
53006
  } else if (type === "ping") {
@@ -52916,6 +53016,7 @@ ${systemPrompt}` : systemPrompt;
52916
53016
  if (block.type === "thinking" || block.type === "redacted_thinking") thinkingIndexes.add(upstreamIndex);
52917
53017
  const ci2 = clientIndex++;
52918
53018
  indexMap.set(upstreamIndex, ci2);
53019
+ openBlocks.push(ci2);
52919
53020
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: round === 1 };
52920
53021
  }
52921
53022
  } else if (type === "content_block_delta") {
@@ -52965,10 +53066,12 @@ ${systemPrompt}` : systemPrompt;
52965
53066
  };
52966
53067
  } else if (thinkingIndexes.delete(upstreamIndex)) {
52967
53068
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
53069
+ removeOpenBlock(ci2);
52968
53070
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: false };
52969
53071
  yield { kind: "reasoning", delta: "", blockEnd: true };
52970
53072
  } else {
52971
53073
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
53074
+ removeOpenBlock(ci2);
52972
53075
  if (lastTextIndex !== null) {
52973
53076
  const tail = tagFilter.flush();
52974
53077
  if (tail.length > 0) {
@@ -53060,10 +53163,22 @@ data: ${JSON.stringify({ type: "content_block_stop", index })}
53060
53163
  );
53061
53164
  },
53062
53165
  emitError(message) {
53063
- const errBlock = buildTextBlock(clientIndex++, `
53166
+ const parts = [];
53167
+ if (!messageStartForwarded) {
53168
+ parts.push(buildSyntheticMessageStart());
53169
+ messageStartForwarded = true;
53170
+ }
53171
+ for (const index of openBlocks.splice(0)) {
53172
+ parts.push(Buffer.from(`event: content_block_stop
53173
+ data: ${JSON.stringify({ type: "content_block_stop", index })}
53174
+
53175
+ `, "utf8"));
53176
+ }
53177
+ parts.push(buildTextBlock(clientIndex++, `
53064
53178
  [acp-proxy: ${message}]
53065
- `);
53066
- return Buffer.concat([errBlock, buildTerminal("end_turn", 0, 0, 0)]);
53179
+ `));
53180
+ parts.push(buildTerminal("end_turn", 0, 0, 0));
53181
+ return Buffer.concat(parts);
53067
53182
  }
53068
53183
  };
53069
53184
  }
@@ -53254,6 +53369,86 @@ async function compressLoopResponsesJson(initialResponse, ctx, requestBody, requ
53254
53369
  return current;
53255
53370
  }
53256
53371
 
53372
+ // src/fake-completion.ts
53373
+ var FAKE_COMPLETION_HINT = "[billion-context] Your last reply described a tool call as plain text instead of invoking it, so no tool actually ran. To act, invoke the tool through the proper tool-calling mechanism (emit a tool_use / tool_calls / function_call block). Do not write tool-call markup as text in your reply.";
53374
+ function maxFakeCompletionRetries() {
53375
+ const n = Number.parseInt(process.env.BILI_FAKE_COMPLETION_RETRIES ?? "0", 10);
53376
+ return Number.isFinite(n) && n >= 0 ? n : 0;
53377
+ }
53378
+ function fakeBufCap() {
53379
+ const n = Number.parseInt(process.env.BILI_FAKE_BUF_CAP ?? String(16 * 1024 * 1024), 10);
53380
+ return Number.isFinite(n) && n > 0 ? n : 16 * 1024 * 1024;
53381
+ }
53382
+ var ANTHROPIC_TOOL_BLOCK = /"type"\s*:\s*"tool_use"/;
53383
+ var OPENAI_TOOL_BLOCK = /"tool_calls"\s*:\s*\[\s*\{/;
53384
+ var RESPONSES_TOOL_BLOCK = /"type"\s*:\s*"function_call"/;
53385
+ function hasToolBlock(protocol, rawText) {
53386
+ switch (protocol) {
53387
+ case "anthropic":
53388
+ return ANTHROPIC_TOOL_BLOCK.test(rawText);
53389
+ case "openai":
53390
+ return OPENAI_TOOL_BLOCK.test(rawText);
53391
+ case "responses":
53392
+ return RESPONSES_TOOL_BLOCK.test(rawText);
53393
+ }
53394
+ }
53395
+ var TOOL_OPEN = /\x3c(?!\s*\/)(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b|\\u003c(?!\s*\/)(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b/i;
53396
+ var TOOL_CLOSE_GLOBAL = /\x3c\/(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b|\\u003c\/(?:antml:)?(invoke|tool_calls|tool_call|parameter|parameters)\b/gi;
53397
+ function hasToolCallStructure(text) {
53398
+ if (!containsToolCallXmlFragment(text)) return false;
53399
+ if (TOOL_OPEN.test(text)) return true;
53400
+ const closes = /* @__PURE__ */ new Set();
53401
+ for (const m2 of text.matchAll(TOOL_CLOSE_GLOBAL)) {
53402
+ const name = m2[1] ?? m2[2];
53403
+ if (name) closes.add(name.toLowerCase());
53404
+ }
53405
+ return closes.size >= 2;
53406
+ }
53407
+ function isFakeCompletion(protocol, rawText) {
53408
+ return hasToolCallStructure(rawText) && !hasToolBlock(protocol, rawText);
53409
+ }
53410
+ function injectFakeCompletionHint(protocol, body) {
53411
+ const raw = typeof body === "string" ? body : body.toString("utf8");
53412
+ let obj;
53413
+ try {
53414
+ obj = JSON.parse(raw);
53415
+ } catch {
53416
+ return null;
53417
+ }
53418
+ if (protocol === "responses") {
53419
+ const input = obj.input;
53420
+ if (!Array.isArray(input)) return null;
53421
+ const arr2 = input;
53422
+ const last2 = arr2[arr2.length - 1];
53423
+ if (last2 && typeof last2 === "object" && last2.role === "user") {
53424
+ const c = last2.content;
53425
+ if (typeof c === "string") last2.content = `${c}
53426
+
53427
+ ${FAKE_COMPLETION_HINT}`;
53428
+ else if (Array.isArray(c)) last2.content = [...c, { type: "input_text", text: FAKE_COMPLETION_HINT }];
53429
+ else last2.content = FAKE_COMPLETION_HINT;
53430
+ } else {
53431
+ arr2.push({ role: "user", content: [{ type: "input_text", text: FAKE_COMPLETION_HINT }] });
53432
+ }
53433
+ return JSON.stringify(obj);
53434
+ }
53435
+ const messages = obj.messages;
53436
+ if (!Array.isArray(messages)) return null;
53437
+ const arr = messages;
53438
+ const last = arr[arr.length - 1];
53439
+ if (last && typeof last === "object" && last.role === "user") {
53440
+ const c = last.content;
53441
+ if (typeof c === "string") last.content = `${c}
53442
+
53443
+ ${FAKE_COMPLETION_HINT}`;
53444
+ else if (Array.isArray(c)) last.content = [...c, { type: "text", text: FAKE_COMPLETION_HINT }];
53445
+ else last.content = FAKE_COMPLETION_HINT;
53446
+ } else {
53447
+ arr.push({ role: "user", content: FAKE_COMPLETION_HINT });
53448
+ }
53449
+ return JSON.stringify(obj);
53450
+ }
53451
+
53257
53452
  // src/stream-openai.ts
53258
53453
  function rewriteOpenaiJsonResponse(body, ctx) {
53259
53454
  if (!body || typeof body !== "object") return body;
@@ -54221,7 +54416,7 @@ function mergeUsageSample(acc, sample) {
54221
54416
  if (sample.cachedTokens !== void 0) acc.cachedTokens = sample.cachedTokens;
54222
54417
  if (sample.outputTokens !== void 0) acc.outputTokens = sample.outputTokens;
54223
54418
  }
54224
- async function pipePluginChatWithStrip(stream2, res, session, protocol, log2) {
54419
+ async function pipePluginChatWithStrip(stream2, res, protocol, session, log2) {
54225
54420
  const reader = stream2.getReader();
54226
54421
  const decoder = new TextDecoder("utf-8");
54227
54422
  let buf = "";
@@ -54375,7 +54570,7 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
54375
54570
  }
54376
54571
  const rest = flushTails();
54377
54572
  if (rest.length > 0 && !res.destroyed && !res.writableEnded) await write(rest);
54378
- if (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0) {
54573
+ if (session && (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0)) {
54379
54574
  applyUsageSample(session, acc, protocol);
54380
54575
  markDirty(session);
54381
54576
  }
@@ -54484,7 +54679,7 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
54484
54679
  const rest = flushTail("");
54485
54680
  if (rest.length > 0) await write(rest);
54486
54681
  }
54487
- if (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0) {
54682
+ if (session && (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0)) {
54488
54683
  applyUsageSample(session, acc, "responses");
54489
54684
  markDirty(session);
54490
54685
  }
@@ -57511,7 +57706,7 @@ ${hdrText}
57511
57706
  if (prepared.protocol === "responses") {
57512
57707
  await pipePluginResponsesWithStrip(upstream.body, res, prepared.session, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
57513
57708
  } else {
57514
- await pipePluginChatWithStrip(upstream.body, res, prepared.session, prepared.protocol, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
57709
+ await pipePluginChatWithStrip(upstream.body, res, prepared.protocol, prepared.session, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
57515
57710
  }
57516
57711
  } else {
57517
57712
  await pipePluginJson(upstream.body, res, prepared.session, prepared.protocol);
@@ -57519,10 +57714,24 @@ ${hdrText}
57519
57714
  clearUpstreamTimer();
57520
57715
  return;
57521
57716
  }
57717
+ let responseBody = upstream.body;
57718
+ if (prepared !== null && prepared.stream && maxFakeCompletionRetries() > 0) {
57719
+ const resolvedBuf = await resolveFakeCompletion(upstream.body, {
57720
+ protocol: prepared.protocol,
57721
+ body,
57722
+ upstreamUrl,
57723
+ reqHeaders: buildForwardHeaders(headers),
57724
+ proxyUrl,
57725
+ signal: clientAbort.signal,
57726
+ session: prepared.session,
57727
+ log: log2
57728
+ });
57729
+ responseBody = bufferToStream(resolvedBuf);
57730
+ }
57522
57731
  const useRewriter = prepared !== null && prepared.compressInjected && prepared.processedMessages.length > 0;
57523
57732
  if (!useRewriter || prepared === null) {
57524
57733
  if (prepared && prepared.resetAfterSuccess) {
57525
- const [toClient, toObserve] = upstream.body.tee();
57734
+ const [toClient, toObserve] = responseBody.tee();
57526
57735
  const observed = observeResponsesTerminalState(toObserve, prepared.stream);
57527
57736
  await pipeThrough(toClient, res);
57528
57737
  clearUpstreamTimer();
@@ -57533,8 +57742,17 @@ ${hdrText}
57533
57742
  } else {
57534
57743
  log2("warn", `[${prepared.session.id}] native compact response terminal=${terminal}; rebase NOT scheduled`);
57535
57744
  }
57745
+ } else if (prepared && prepared.stream && (upstream.headers.get("content-type") ?? "").includes("text/event-stream")) {
57746
+ const p2 = prepared;
57747
+ const tagLog = (msg) => log2("info", `[${p2.session.id}] ${msg}`);
57748
+ if (p2.protocol === "responses") {
57749
+ await pipePluginResponsesWithStrip(responseBody, res, void 0, tagLog);
57750
+ } else {
57751
+ await pipePluginChatWithStrip(responseBody, res, p2.protocol, void 0, tagLog);
57752
+ }
57753
+ clearUpstreamTimer();
57536
57754
  } else {
57537
- await pipeThrough(upstream.body, res);
57755
+ await pipeThrough(responseBody, res);
57538
57756
  clearUpstreamTimer();
57539
57757
  }
57540
57758
  return;
@@ -57548,10 +57766,10 @@ ${hdrText}
57548
57766
  debug: opts.debug
57549
57767
  };
57550
57768
  if (prepared.stream) {
57551
- let streamToRead = upstream.body;
57769
+ let streamToRead = responseBody;
57552
57770
  let dumpRaw;
57553
57771
  if (opts.dumpSse) {
57554
- const [a, b2] = upstream.body.tee();
57772
+ const [a, b2] = responseBody.tee();
57555
57773
  streamToRead = a;
57556
57774
  dumpRaw = dumpStreamToFile(b2, opts.dumpSse, `${Date.now()}-${safeSessionId(prepared.session.id)}-raw.sse`);
57557
57775
  }
@@ -57677,6 +57895,58 @@ async function readStreamToBuffer(stream2, maxBytes = 1 << 20) {
57677
57895
  }
57678
57896
  return Buffer.concat(chunks);
57679
57897
  }
57898
+ function bufferToStream(buf) {
57899
+ return new ReadableStream({
57900
+ start(controller) {
57901
+ controller.enqueue(new Uint8Array(buf));
57902
+ controller.close();
57903
+ }
57904
+ });
57905
+ }
57906
+ async function resolveFakeCompletion(stream2, opts) {
57907
+ let buffer = await readStreamToBuffer(stream2, fakeBufCap());
57908
+ const max = maxFakeCompletionRetries();
57909
+ const sid = opts.session.id;
57910
+ const priorStreak = opts.session.metadata.fakeCompletionStreak ?? 0;
57911
+ if (max > 0 && priorStreak < max && isFakeCompletion(opts.protocol, buffer.toString("utf8"))) {
57912
+ for (let attempt = 1; attempt <= max && !opts.signal.aborted; attempt++) {
57913
+ const hinted = injectFakeCompletionHint(opts.protocol, opts.body);
57914
+ if (hinted === null) break;
57915
+ opts.log("warn", `[${sid}] fake completion (tool-call XML, no tool block); retry ${attempt}/${max} with corrective hint`);
57916
+ let r;
57917
+ try {
57918
+ r = await fetchWithTimeout(
57919
+ opts.upstreamUrl,
57920
+ {
57921
+ method: "POST",
57922
+ headers: opts.reqHeaders,
57923
+ body: hinted,
57924
+ ...opts.proxyUrl ? { dispatcher: proxyDispatcher(opts.proxyUrl) } : {}
57925
+ },
57926
+ void 0,
57927
+ opts.signal
57928
+ );
57929
+ } catch (e) {
57930
+ opts.log("warn", `[${sid}] fake-completion retry failed: ${String(e)}; presenting original`);
57931
+ break;
57932
+ }
57933
+ try {
57934
+ if (!r.response.ok || !r.response.body) {
57935
+ opts.log("warn", `[${sid}] fake-completion retry rejected (HTTP ${r.response.status}); presenting original`);
57936
+ break;
57937
+ }
57938
+ buffer = await readStreamToBuffer(r.response.body, fakeBufCap());
57939
+ } finally {
57940
+ r.clearTimer();
57941
+ }
57942
+ if (!isFakeCompletion(opts.protocol, buffer.toString("utf8"))) break;
57943
+ }
57944
+ }
57945
+ const stillFake = isFakeCompletion(opts.protocol, buffer.toString("utf8"));
57946
+ opts.session.metadata.fakeCompletionStreak = stillFake ? priorStreak + 1 : 0;
57947
+ markDirty(opts.session);
57948
+ return buffer;
57949
+ }
57680
57950
  async function pipeThrough(stream2, res) {
57681
57951
  const reader = stream2.getReader();
57682
57952
  try {