billion-context 0.1.99 → 0.1.100-pr.647.508

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
@@ -47777,7 +47777,14 @@ async function fetchWithTimeout(url, opts, timeoutMs, externalSignal) {
47777
47777
  const finalOpts = {
47778
47778
  ...opts,
47779
47779
  signal: controller.signal,
47780
- dispatcher: opts.dispatcher ?? directDispatcher(effective)
47780
+ dispatcher: opts.dispatcher ?? directDispatcher(effective),
47781
+ // Forward-proxy correctness: never silently follow a redirect.
47782
+ // undici's default (follow) downgrades POST→GET and drops the body
47783
+ // on 301/302/303, so a redirecting upstream (CDN/WAF) turns a valid
47784
+ // POST into a 405 at the redirect target. Pass the 3xx through to
47785
+ // the client, which follows it with its own policy. Internal bili
47786
+ // fetches that want to follow (registry, upstream test) opt in.
47787
+ redirect: opts.redirect ?? "manual"
47781
47788
  };
47782
47789
  const raw = await fetch(url, finalOpts);
47783
47790
  if (raw.body) {
@@ -48514,6 +48521,7 @@ function loadOptions(env = process.env) {
48514
48521
  passthrough: passthrough.enabled,
48515
48522
  passthroughSource: passthrough.source,
48516
48523
  autoUpdate: (env.ACP_AUTO_UPDATE ?? (fileConfig.autoUpdate === false ? "0" : "1")) !== "0",
48524
+ hostUsageCredit: parseHostUsageCredit(env.BILI_HOST_USAGE_CREDIT ?? fileConfig.hostUsageCredit),
48517
48525
  updateTag: (env.ACP_UPDATE_TAG ?? fileConfig.updateTag ?? "latest").trim() || "latest",
48518
48526
  logFile: env.ACP_LOG_FILE !== void 0 ? env.ACP_LOG_FILE || void 0 : fileConfig.logFile,
48519
48527
  mitm: {
@@ -48579,6 +48587,7 @@ function parseRouteEntry(v2) {
48579
48587
  if (obj.compress) route.compress = obj.compress;
48580
48588
  const compatRoles = parseCompatRoles(obj.compat?.roles);
48581
48589
  if (compatRoles) route.compat = { roles: compatRoles };
48590
+ if (typeof obj.passthrough === "boolean") route.passthrough = obj.passthrough;
48582
48591
  return route;
48583
48592
  }
48584
48593
  if (v2 === null) return {};
@@ -48590,6 +48599,9 @@ function parsePromptCacheRouting(value) {
48590
48599
  function parseUpstreamProxyMode(value) {
48591
48600
  return value === "manual" || value === "auto" ? value : "direct";
48592
48601
  }
48602
+ function parseHostUsageCredit(value) {
48603
+ return value === "off" ? "off" : "auto";
48604
+ }
48593
48605
  function parseCompressSettings(v2) {
48594
48606
  if (!v2 || typeof v2 !== "object" || Array.isArray(v2)) return void 0;
48595
48607
  const obj = v2;
@@ -49005,7 +49017,8 @@ async function fetchFresh() {
49005
49017
  try {
49006
49018
  const { response, clearTimer } = await fetchWithTimeout(REGISTRY_URL, {
49007
49019
  ...opts,
49008
- headers: { Accept: "application/json" }
49020
+ headers: { Accept: "application/json" },
49021
+ redirect: "follow"
49009
49022
  }, 15e3);
49010
49023
  const text = await response.text();
49011
49024
  clearTimer();
@@ -51498,20 +51511,44 @@ ${body}`;
51498
51511
  }
51499
51512
 
51500
51513
  // src/loop/tag-echo-filter.ts
51501
- var PAIRED = /\x3cacp\s[^<>]{0,256}>([^<>]{0,64})<\/acp>/;
51502
- var LONE_OPEN = /\x3cacp(?:\s[^<>]{0,256})?>/;
51503
- var LONE_CLOSE = /<\/acp(?=[\s>])[^<>]{0,32}>/;
51504
- var PARTIAL_TAIL = /(\x3cacp\s[^<>]*|\x3c\/acp(?:\s[^<>]{0,32})?|<\/?a?c?p?)$/;
51505
- var TRUNC_OPEN = /\x3cacp\s[^<>]*$/;
51506
- var TRUNC_CLOSE = /\x3c\/acp(?:\s[^<>]{0,32})?$/;
51507
- var CLOSE_TAG = "</acp";
51514
+ function buildAcplikeName() {
51515
+ const cores = ["acp", "apc", "cap", "cpa", "pac", "pca"];
51516
+ const names = new Set(cores);
51517
+ for (const c of cores) {
51518
+ for (const ch of ["a", "c", "p", "i"]) {
51519
+ for (let pos = 0; pos <= c.length; pos++) names.add(c.slice(0, pos) + ch + c.slice(pos));
51520
+ }
51521
+ }
51522
+ return [...names].sort((a, b2) => b2.length - a.length).join("|");
51523
+ }
51524
+ var ACP_NAME_ALT = `(?:${buildAcplikeName()})`;
51525
+ var NAME = ACP_NAME_ALT;
51526
+ var PAIRED = new RegExp("<" + NAME + "\\s[^<>]{0,256}>([^<>]{0,64})<\\/" + NAME + ">");
51527
+ var LONE_OPEN = new RegExp("<" + NAME + "(?:\\s[^<>]{0,256})?>");
51528
+ var LONE_CLOSE = new RegExp("<\\/" + NAME + "(?=[\\s>])[^<>]{0,32}>");
51529
+ var PARTIAL_TAIL = new RegExp("(<" + NAME + "\\s[^<>]*|<\\/" + NAME + "(?:\\s[^<>]{0,32})?|<\\/?[acip]*)$");
51530
+ var TRUNC_OPEN = new RegExp("<" + NAME + "\\s[^<>]*$");
51531
+ var TRUNC_CLOSE = new RegExp("<\\/" + NAME + "(?:\\s[^<>]{0,32})?$");
51532
+ var DEFINITE_TAIL = new RegExp("^<" + NAME + "\\s|^<\\/" + NAME);
51533
+ var OPEN_WITH_ATTRS = new RegExp("^<" + NAME + "\\s");
51534
+ var CLOSE_HEAD = "</";
51535
+ var CLOSE_NAME_ANCHORED = new RegExp("^" + NAME);
51508
51536
  var HOLD_LIMIT = 128;
51509
51537
  var TAG_OPEN_CAP = 4096;
51510
51538
  var SWALLOW_CAP = 80;
51539
+ function looseCloseEnd(s3) {
51540
+ let idx = s3.indexOf(CLOSE_HEAD);
51541
+ while (idx >= 0) {
51542
+ const m2 = CLOSE_NAME_ANCHORED.exec(s3.slice(idx + 2));
51543
+ if (m2 && s3[idx + 2 + m2[0].length] === ">") return idx + 2 + m2[0].length + 1;
51544
+ idx = s3.indexOf(CLOSE_HEAD, idx + 1);
51545
+ }
51546
+ return -1;
51547
+ }
51511
51548
  function stripAcpTags(text) {
51512
51549
  return text.replace(new RegExp(PAIRED.source, "g"), "").replace(new RegExp(LONE_OPEN.source, "g"), "").replace(new RegExp(LONE_CLOSE.source, "g"), "").replace(new RegExp(TRUNC_OPEN.source), "").replace(new RegExp(TRUNC_CLOSE.source), "");
51513
51550
  }
51514
- var RENDER_TAG_DETECT = /\x3c\/?acp(?=[\s>])|\\u003c\/?acp(?=[\s>\\])/;
51551
+ var RENDER_TAG_DETECT = new RegExp("<\\/?" + NAME + "(?=[\\s>])|\\\\u003c\\/?" + NAME + "(?=[\\s>\\\\])");
51515
51552
  function containsRenderTagText(s3) {
51516
51553
  return RENDER_TAG_DETECT.test(s3);
51517
51554
  }
@@ -51528,6 +51565,8 @@ function createTagEchoFilter(onDrop) {
51528
51565
  let swallowed = "";
51529
51566
  let droppedAny = false;
51530
51567
  let notified = false;
51568
+ let inputChars = 0;
51569
+ let outputChars = 0;
51531
51570
  const drop = (snippet) => {
51532
51571
  droppedAny = true;
51533
51572
  if (onDrop && !notified) {
@@ -51541,10 +51580,9 @@ function createTagEchoFilter(onDrop) {
51541
51580
  for (; ; ) {
51542
51581
  if (swallowUntilClose) {
51543
51582
  const combined = swallowed + buf;
51544
- const closeIdx = combined.indexOf(CLOSE_TAG);
51545
- if (closeIdx >= 0 && combined[closeIdx + CLOSE_TAG.length] === ">") {
51546
- const end = closeIdx + CLOSE_TAG.length + 1;
51547
- drop(swallowed + combined.slice(0, end));
51583
+ const end = looseCloseEnd(combined);
51584
+ if (end >= 0) {
51585
+ drop(combined.slice(0, end));
51548
51586
  swallowed = "";
51549
51587
  swallowUntilClose = false;
51550
51588
  buf = combined.slice(end);
@@ -51569,7 +51607,7 @@ function createTagEchoFilter(onDrop) {
51569
51607
  if (!m2) {
51570
51608
  const t = PARTIAL_TAIL.exec(buf);
51571
51609
  if (t) {
51572
- const definite = /^\x3cacp\s/.test(t[0]) || /^\x3c\/acp/.test(t[0]);
51610
+ const definite = DEFINITE_TAIL.test(t[0]);
51573
51611
  const cap = definite ? TAG_OPEN_CAP : HOLD_LIMIT;
51574
51612
  if (t[0].length <= cap) {
51575
51613
  held = t[0];
@@ -51588,7 +51626,7 @@ function createTagEchoFilter(onDrop) {
51588
51626
  drop(m2[0]);
51589
51627
  out += buf.slice(0, m2.index);
51590
51628
  buf = buf.slice(m2.index + m2[0].length);
51591
- if (m2 === o && /^\x3cacp\s/.test(m2[0])) {
51629
+ if (m2 === o && OPEN_WITH_ATTRS.test(m2[0])) {
51592
51630
  swallowUntilClose = true;
51593
51631
  swallowed = "";
51594
51632
  }
@@ -51597,9 +51635,12 @@ function createTagEchoFilter(onDrop) {
51597
51635
  };
51598
51636
  return {
51599
51637
  push(delta) {
51638
+ inputChars += delta.length;
51600
51639
  const chunk = held + delta;
51601
51640
  held = "";
51602
- return process2(chunk);
51641
+ const r = process2(chunk);
51642
+ outputChars += r.length;
51643
+ return r;
51603
51644
  },
51604
51645
  flush() {
51605
51646
  const rest = swallowed + held;
@@ -51607,27 +51648,36 @@ function createTagEchoFilter(onDrop) {
51607
51648
  swallowed = "";
51608
51649
  held = "";
51609
51650
  swallowUntilClose = false;
51651
+ let result;
51610
51652
  if (wasSwallowing) {
51611
51653
  if (rest.length > 0) drop(rest);
51612
- return "";
51613
- }
51614
- const t = new RegExp(TRUNC_OPEN.source).exec(rest);
51615
- if (t) {
51616
- drop(t[0]);
51617
- return rest.slice(0, t.index);
51618
- }
51619
- const tc = new RegExp(TRUNC_CLOSE.source).exec(rest);
51620
- if (tc) {
51621
- drop(tc[0]);
51622
- return rest.slice(0, tc.index);
51654
+ result = "";
51655
+ } else {
51656
+ const t = new RegExp(TRUNC_OPEN.source).exec(rest);
51657
+ if (t) {
51658
+ drop(t[0]);
51659
+ result = rest.slice(0, t.index);
51660
+ } else {
51661
+ const tc = new RegExp(TRUNC_CLOSE.source).exec(rest);
51662
+ if (tc) {
51663
+ drop(tc[0]);
51664
+ result = rest.slice(0, tc.index);
51665
+ } else {
51666
+ result = rest;
51667
+ }
51668
+ }
51623
51669
  }
51624
- return rest;
51670
+ outputChars += result.length;
51671
+ return result;
51625
51672
  },
51626
51673
  dropped() {
51627
51674
  return droppedAny;
51628
51675
  },
51629
51676
  pending() {
51630
51677
  return held.length > 0 || swallowUntilClose;
51678
+ },
51679
+ stats() {
51680
+ return { inputChars, outputChars, dropped: droppedAny };
51631
51681
  }
51632
51682
  };
51633
51683
  }
@@ -51932,16 +51982,41 @@ function splitChunks(messages, startIdx, endIdx, budget, minUnits, countText = d
51932
51982
  }
51933
51983
  return chunks;
51934
51984
  }
51935
- function summaryPayload(protocol, model, system, content, stream2) {
51985
+ function summaryPayload(protocol, model, system, content, stream2, includeMaxOutputTokens) {
51936
51986
  if (protocol === "anthropic") {
51937
51987
  return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, system, messages: [{ role: "user", content }], stream: stream2 };
51938
51988
  }
51939
51989
  if (protocol === "openai") {
51940
51990
  return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, messages: [{ role: "system", content: system }, { role: "user", content }], stream: stream2 };
51941
51991
  }
51942
- return { model, max_output_tokens: MAX_SUMMARY_OUTPUT_TOKENS, instructions: system, input: [{ role: "user", content }], stream: stream2, store: false };
51992
+ const payload = { model, instructions: system, input: [{ role: "user", content }], stream: stream2, store: false };
51993
+ if (includeMaxOutputTokens) payload.max_output_tokens = MAX_SUMMARY_OUTPUT_TOKENS;
51994
+ return payload;
51943
51995
  }
51944
51996
  var STREAM_REQUIRED_RE = /\bstream\b[^\n]{0,60}\btrue\b/i;
51997
+ var MAX_OUTPUT_TOKENS_REJECTED_RE = /\bmax_output_tokens\b/i;
51998
+ function noMaxOutputTokensKey(deps) {
51999
+ return `${deps.url}\0${deps.model}`;
52000
+ }
52001
+ function hasLearnedNoMaxOutputTokens(deps) {
52002
+ const learned = deps.session.metadata.preflightNoMaxOutputTokens;
52003
+ return typeof learned === "object" && learned !== null && learned[noMaxOutputTokensKey(deps)] === true;
52004
+ }
52005
+ function rememberNoMaxOutputTokens(deps) {
52006
+ const learned = deps.session.metadata.preflightNoMaxOutputTokens;
52007
+ const map = typeof learned === "object" && learned !== null ? learned : {};
52008
+ map[noMaxOutputTokensKey(deps)] = true;
52009
+ deps.session.metadata.preflightNoMaxOutputTokens = map;
52010
+ }
52011
+ var SUMMARY_STRIP_HEADERS = /* @__PURE__ */ new Set(["x-openai-internal-codex-responses-lite"]);
52012
+ function summaryHeaders(deps) {
52013
+ const headers = {};
52014
+ for (const [k2, v2] of Object.entries(deps.headers)) {
52015
+ if (SUMMARY_STRIP_HEADERS.has(k2.toLowerCase())) continue;
52016
+ headers[k2] = v2;
52017
+ }
52018
+ return headers;
52019
+ }
51945
52020
  function extractSummaryFromSse(protocol, text) {
51946
52021
  let out = "";
51947
52022
  for (const line of text.split("\n")) {
@@ -52005,25 +52080,39 @@ async function summarizeRange(deps, content, startRef, endRef) {
52005
52080
  const system = buildCompressSystemPrompt(deps.prompts) + `
52006
52081
 
52007
52082
  TASK: The conversation segment below (messages ${startRef}\u2013${endRef}) must be compressed because the session context exceeds the current model's window. Write a tier-1 compression summary of the segment following every rule above. Output ONLY the summary text \u2014 no preamble, no closing remarks, no tool calls.`;
52008
- const learned = deps.session.metadata.preflightStreamSummary === true;
52009
- try {
52010
- return await requestSummary(deps, system, content, learned);
52011
- } catch (err2) {
52012
- if (err2 instanceof UpstreamHttpError && err2.status === 400 && !learned && STREAM_REQUIRED_RE.test(err2.body)) {
52013
- deps.session.metadata.preflightStreamSummary = true;
52014
- deps.log("info", "[preflight] upstream requires stream for summaries; retrying with SSE (learned for this session)");
52015
- return await requestSummary(deps, system, content, true);
52083
+ let stream2 = deps.session.metadata.preflightStreamSummary === true;
52084
+ let includeMaxOutputTokens = !(deps.protocol === "responses" && hasLearnedNoMaxOutputTokens(deps));
52085
+ for (; ; ) {
52086
+ try {
52087
+ return await requestSummary(deps, system, content, stream2, includeMaxOutputTokens);
52088
+ } catch (err2) {
52089
+ if (err2 instanceof UpstreamHttpError && err2.status === 400) {
52090
+ let adapted = false;
52091
+ if (!stream2 && STREAM_REQUIRED_RE.test(err2.body)) {
52092
+ deps.session.metadata.preflightStreamSummary = true;
52093
+ stream2 = true;
52094
+ adapted = true;
52095
+ deps.log("info", "[preflight] upstream requires stream for summaries; retrying with SSE (learned for this session)");
52096
+ }
52097
+ if (deps.protocol === "responses" && includeMaxOutputTokens && MAX_OUTPUT_TOKENS_REJECTED_RE.test(err2.body)) {
52098
+ rememberNoMaxOutputTokens(deps);
52099
+ includeMaxOutputTokens = false;
52100
+ adapted = true;
52101
+ deps.log("info", `[preflight] upstream rejects max_output_tokens for summaries (model=${deps.model}); retrying without it (learned for this session+upstream+model)`);
52102
+ }
52103
+ if (adapted) continue;
52104
+ }
52105
+ throw err2;
52016
52106
  }
52017
- throw err2;
52018
52107
  }
52019
52108
  }
52020
- async function requestSummary(deps, system, content, stream2) {
52109
+ async function requestSummary(deps, system, content, stream2, includeMaxOutputTokens) {
52021
52110
  const { response, clearTimer } = await fetchWithRetry(
52022
52111
  deps.url,
52023
52112
  {
52024
52113
  method: "POST",
52025
- headers: { "content-type": "application/json", ...deps.headers },
52026
- body: JSON.stringify(summaryPayload(deps.protocol, deps.model, system, content, stream2)),
52114
+ headers: { "content-type": "application/json", ...summaryHeaders(deps) },
52115
+ body: JSON.stringify(summaryPayload(deps.protocol, deps.model, system, content, stream2, includeMaxOutputTokens)),
52027
52116
  dispatcher: proxyDispatcher(deps.proxyUrl)
52028
52117
  },
52029
52118
  void 0,
@@ -53660,6 +53749,18 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
53660
53749
  }
53661
53750
  }
53662
53751
 
53752
+ // src/degenerate-turn.ts
53753
+ function degenerateTurnWarning(o) {
53754
+ if ((o.reason ?? o.terminalReason) !== o.terminalReason) return null;
53755
+ if (o.toolCalls > 0) return null;
53756
+ if (o.text.outputChars > 0) return null;
53757
+ const bits = [];
53758
+ if (o.sawThinking) bits.push("thinking present");
53759
+ if (o.text.dropped && o.text.inputChars > 0) bits.push(`${o.text.inputChars} chars of emitted text stripped as render-tag echo`);
53760
+ else bits.push("no visible text emitted");
53761
+ return `[degenerate-turn] ${o.wire}: turn ended ${o.terminalReason} with zero visible text and zero tool calls (${bits.join("; ")}) \u2014 the agent receives an empty turn and may stall until nudged (#673)`;
53762
+ }
53763
+
53663
53764
  // src/loop/adapter-responses.ts
53664
53765
  var RESPONSES_ITEM_ID_MAX = 64;
53665
53766
  function normalizeResponsesMessageItems(input) {
@@ -53723,7 +53824,7 @@ function dropWhitespaceResponsesMessages(input) {
53723
53824
  }
53724
53825
  return dropped;
53725
53826
  }
53726
- var RENDER_TAG_RE = /\x3cacp\s[^>]*\x3e[^<]*\x3c\/acp\x3e|\x3cacp\s[^>]*\/\x3e/g;
53827
+ var RENDER_TAG_RE = new RegExp("<" + ACP_NAME_ALT + "\\s[^>]*>[^<]*<\\/" + ACP_NAME_ALT + ">|<" + ACP_NAME_ALT + "\\s[^>]*\\/>", "g");
53727
53828
  function stripRenderTags(text) {
53728
53829
  return text.replace(RENDER_TAG_RE, "");
53729
53830
  }
@@ -53938,6 +54039,17 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
53938
54039
  yield { kind: "text", delta: tail, ...raw ? { raw } : {} };
53939
54040
  }
53940
54041
  };
54042
+ let sawReasoning = false;
54043
+ let toolCallsEmitted = 0;
54044
+ let degenerateWarned = false;
54045
+ const maybeWarnDegenerate = (reason) => {
54046
+ if (degenerateWarned) return;
54047
+ const msg = degenerateTurnWarning({ reason, terminalReason: "completed", toolCalls: toolCallsEmitted, text: tagFilter.stats(), sawThinking: sawReasoning, wire: "responses" });
54048
+ if (msg) {
54049
+ degenerateWarned = true;
54050
+ log("warn", msg);
54051
+ }
54052
+ };
53941
54053
  for await (const eventStr of iterSseEvents(upstream)) {
53942
54054
  const explicitType = extractEventType(eventStr);
53943
54055
  const dataLine = extractDataLine(eventStr);
@@ -53950,6 +54062,7 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
53950
54062
  }
53951
54063
  const type = explicitType ?? (typeof obj.type === "string" ? obj.type : null);
53952
54064
  if (!type) continue;
54065
+ if (type.startsWith("response.reasoning")) sawReasoning = true;
53953
54066
  const rawBuf = Buffer.from(eventStr + "\n\n", "utf8");
53954
54067
  if (round === 1 && typeof obj.output_index === "number") {
53955
54068
  outputIndex = Math.max(outputIndex, obj.output_index + 1);
@@ -54033,6 +54146,7 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
54033
54146
  if (fc) {
54034
54147
  if (typeof item.arguments === "string" && item.arguments) fc.arguments = item.arguments;
54035
54148
  pending.delete(itemId);
54149
+ toolCallsEmitted++;
54036
54150
  yield {
54037
54151
  kind: "tool_call",
54038
54152
  name: fc.name,
@@ -54041,6 +54155,7 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
54041
54155
  };
54042
54156
  } else {
54043
54157
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
54158
+ toolCallsEmitted++;
54044
54159
  yield {
54045
54160
  kind: "tool_call",
54046
54161
  name: typeof item.name === "string" ? item.name : "",
@@ -54077,6 +54192,7 @@ data: ${JSON.stringify({ type: "response.created", response: { id: createdRespId
54077
54192
  outputTokens: typeof respUsage?.output_tokens === "number" ? respUsage.output_tokens : void 0,
54078
54193
  cachedTokens: typeof pd?.cached_tokens === "number" ? pd.cached_tokens : void 0
54079
54194
  };
54195
+ maybeWarnDegenerate("completed");
54080
54196
  yield { kind: "done", finishReason: "completed" };
54081
54197
  } else if (type === "response.incomplete") {
54082
54198
  yield* flushFilter();
@@ -54394,11 +54510,23 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54394
54510
  yield { kind: "text", delta: tail, raw: buildContent(tail) };
54395
54511
  }
54396
54512
  };
54513
+ let sawReasoning = false;
54514
+ let toolCallsEmitted = 0;
54515
+ let degenerateWarned = false;
54516
+ const maybeWarnDegenerate = (reason) => {
54517
+ if (degenerateWarned) return;
54518
+ const msg = degenerateTurnWarning({ reason, terminalReason: "stop", toolCalls: toolCallsEmitted, text: tagFilter.stats(), sawThinking: sawReasoning, wire: "openai" });
54519
+ if (msg) {
54520
+ degenerateWarned = true;
54521
+ log("warn", msg);
54522
+ }
54523
+ };
54397
54524
  const rawToolChunks = [];
54398
54525
  let sawRealToolCall = false;
54399
54526
  const flushPendingAsStructured = function* () {
54400
54527
  for (const [, tc] of pending) {
54401
54528
  if (tc.name.length > 0 || tc.id.length > 0) {
54529
+ toolCallsEmitted++;
54402
54530
  yield {
54403
54531
  kind: "tool_call",
54404
54532
  name: tc.name,
@@ -54421,6 +54549,7 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54421
54549
  }
54422
54550
  for (const [idx, tc] of pending) {
54423
54551
  if (realIndexes.has(idx) && (tc.name.length > 0 || tc.id.length > 0)) {
54552
+ toolCallsEmitted++;
54424
54553
  yield {
54425
54554
  kind: "tool_call",
54426
54555
  name: tc.name,
@@ -54440,6 +54569,7 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54440
54569
  }
54441
54570
  for (const [idx, tc] of pending) {
54442
54571
  if (!realIndexes.has(idx) && tc.name.length > 0) {
54572
+ toolCallsEmitted++;
54443
54573
  yield {
54444
54574
  kind: "tool_call",
54445
54575
  name: tc.name,
@@ -54464,6 +54594,7 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54464
54594
  if (sawRealToolCall) {
54465
54595
  yield { kind: "meta", chunk: Buffer.from(eventStr + "\n\n", "utf8") };
54466
54596
  }
54597
+ maybeWarnDegenerate("stop");
54467
54598
  yield { kind: "done", finishReason: "stop", ...sawRealToolCall ? { suppressCompletion: true } : {} };
54468
54599
  continue;
54469
54600
  }
@@ -54509,9 +54640,11 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54509
54640
  if (sawRealToolCall) {
54510
54641
  const chunk = patchUsageChunk(eventStr, parsed, u2 ?? {}, hostCredit);
54511
54642
  yield { kind: "meta", chunk };
54643
+ maybeWarnDegenerate(finishReason);
54512
54644
  yield { kind: "done", finishReason, suppressCompletion: true };
54513
54645
  continue;
54514
54646
  } else {
54647
+ maybeWarnDegenerate(finishReason);
54515
54648
  yield {
54516
54649
  kind: "done",
54517
54650
  finishReason: hadToolCalls && finishReason === "stop" ? "tool_calls" : finishReason
@@ -54520,6 +54653,7 @@ function createOpenaiAdapter(requestBody, clientSystem, hostCredit = 0, absorbNa
54520
54653
  }
54521
54654
  if (!delta) continue;
54522
54655
  if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
54656
+ sawReasoning = true;
54523
54657
  yield { kind: "reasoning", delta: delta.reasoning_content, raw: finishReason ? stripFinishReasonChunk(rawBuf) : rawBuf };
54524
54658
  }
54525
54659
  if (delta.tool_calls) {
@@ -54797,6 +54931,17 @@ ${systemPrompt}` : systemPrompt;
54797
54931
  log("warn", `[tag-echo] stripped model-emitted render tag: ${snippet.slice(0, 80).replace(/\n/g, " ")}`);
54798
54932
  });
54799
54933
  let lastTextIndex = null;
54934
+ let sawThinking = false;
54935
+ let toolCallsEmitted = 0;
54936
+ let degenerateWarned = false;
54937
+ const maybeWarnDegenerate = (reason) => {
54938
+ if (degenerateWarned) return;
54939
+ const msg = degenerateTurnWarning({ reason, terminalReason: "end_turn", toolCalls: toolCallsEmitted, text: tagFilter.stats(), sawThinking, wire: "anthropic" });
54940
+ if (msg) {
54941
+ degenerateWarned = true;
54942
+ log("warn", msg);
54943
+ }
54944
+ };
54800
54945
  for await (const eventStr of iterSseEvents2(upstream)) {
54801
54946
  const parsed = parseAnthropicSse(eventStr);
54802
54947
  if (!parsed) continue;
@@ -54833,7 +54978,10 @@ ${systemPrompt}` : systemPrompt;
54833
54978
  const id = typeof block.id === "string" ? block.id : `toolu_${upstreamIndex}`;
54834
54979
  pending.set(upstreamIndex, { id, name, json: "" });
54835
54980
  } else {
54836
- if (block.type === "thinking" || block.type === "redacted_thinking") thinkingIndexes.add(upstreamIndex);
54981
+ if (block.type === "thinking" || block.type === "redacted_thinking") {
54982
+ thinkingIndexes.add(upstreamIndex);
54983
+ sawThinking = true;
54984
+ }
54837
54985
  const ci2 = clientIndex++;
54838
54986
  indexMap.set(upstreamIndex, ci2);
54839
54987
  openBlocks.push(ci2);
@@ -54878,6 +55026,7 @@ ${systemPrompt}` : systemPrompt;
54878
55026
  const tb = pending.get(upstreamIndex);
54879
55027
  if (tb) {
54880
55028
  pending.delete(upstreamIndex);
55029
+ toolCallsEmitted++;
54881
55030
  yield {
54882
55031
  kind: "tool_call",
54883
55032
  name: tb.name,
@@ -54924,6 +55073,7 @@ ${systemPrompt}` : systemPrompt;
54924
55073
  cachedTokens: roundCached
54925
55074
  };
54926
55075
  }
55076
+ maybeWarnDegenerate(stopReason);
54927
55077
  yield { kind: "done", finishReason: stopReason };
54928
55078
  } else if (type === "message_stop") {
54929
55079
  if (lastTextIndex !== null) {
@@ -54942,6 +55092,7 @@ ${systemPrompt}` : systemPrompt;
54942
55092
  cachedTokens: roundCached
54943
55093
  };
54944
55094
  }
55095
+ maybeWarnDegenerate(stopReason);
54945
55096
  yield { kind: "done", finishReason: stopReason ?? "end_turn" };
54946
55097
  } else if (round === 1) {
54947
55098
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
@@ -56552,11 +56703,18 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56552
56703
 
56553
56704
  `;
56554
56705
  };
56706
+ let sawToolUse = false;
56707
+ let sawThinking = false;
56708
+ let visibleTextChars = 0;
56709
+ let finalFinishReason;
56555
56710
  const flushTails = () => {
56556
56711
  let out = "";
56557
56712
  for (const s3 of streams.values()) {
56558
56713
  const tail = s3.filter.flush();
56559
- if (tail.length > 0) out += syntheticTail(s3.field, s3.index, tail);
56714
+ if (tail.length > 0) {
56715
+ out += syntheticTail(s3.field, s3.index, tail);
56716
+ if (s3.field === "content" || s3.field === "text") visibleTextChars += tail.length;
56717
+ }
56560
56718
  }
56561
56719
  return out;
56562
56720
  };
@@ -56579,6 +56737,28 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56579
56737
  reason: "plugin chat passthrough stream ended without a completion event"
56580
56738
  });
56581
56739
  };
56740
+ const maybeWarnDegenerate = () => {
56741
+ if (!sawTerminal || res.destroyed || res.writableEnded) return;
56742
+ let inputChars = 0;
56743
+ let dropped = false;
56744
+ for (const s3 of streams.values()) {
56745
+ const st2 = s3.filter.stats();
56746
+ inputChars += st2.inputChars;
56747
+ dropped = dropped || st2.dropped;
56748
+ }
56749
+ const msg = degenerateTurnWarning({
56750
+ reason: finalFinishReason,
56751
+ terminalReason: protocol === "anthropic" ? "end_turn" : "stop",
56752
+ toolCalls: sawToolUse ? 1 : 0,
56753
+ text: { inputChars, outputChars: visibleTextChars, dropped },
56754
+ sawThinking,
56755
+ wire: `plugin-passthrough-${protocol}`
56756
+ });
56757
+ if (msg) {
56758
+ log("warn", msg);
56759
+ log2?.(msg);
56760
+ }
56761
+ };
56582
56762
  const pushField = (field, index, text) => {
56583
56763
  const s3 = filterFor(field, index);
56584
56764
  const clean = s3.filter.push(text);
@@ -56598,21 +56778,28 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56598
56778
  let hadText = false;
56599
56779
  for (let ci2 = 0; ci2 < choices.length; ci2++) {
56600
56780
  const ch = choices[ci2];
56781
+ if (ch && typeof ch["finish_reason"] === "string") finalFinishReason = ch["finish_reason"];
56601
56782
  const d = ch?.["delta"];
56602
56783
  if (!d || typeof d !== "object") continue;
56603
56784
  const dd = d;
56785
+ if (dd["tool_calls"] !== void 0) sawToolUse = true;
56604
56786
  for (const field of ["content", "reasoning_content", "reasoning"]) {
56605
56787
  const v2 = dd[field];
56606
56788
  if (typeof v2 !== "string") continue;
56607
56789
  hadText = true;
56790
+ if (field !== "content" && v2.length > 0) sawThinking = true;
56608
56791
  if (!mayStartRenderTag(v2) && !anyPending()) {
56609
56792
  if (v2.length > 0) keptText = true;
56793
+ if (field === "content") visibleTextChars += v2.length;
56610
56794
  continue;
56611
56795
  }
56612
56796
  const index = typeof ch?.["index"] === "number" ? ch["index"] : ci2;
56613
56797
  const [clean, changed] = pushField(field, index, v2);
56614
56798
  if (clean.length === 0) droppedText = true;
56615
- else keptText = true;
56799
+ else {
56800
+ keptText = true;
56801
+ if (field === "content") visibleTextChars += clean.length;
56802
+ }
56616
56803
  if (changed) {
56617
56804
  if (!rebuilt) {
56618
56805
  rebuilt = { ...ev, choices: choices.map((c) => ({ ...c, delta: { ...c["delta"] } })) };
@@ -56631,6 +56818,13 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56631
56818
  return rawEvent + "\n\n";
56632
56819
  };
56633
56820
  const processAnthropic = (ev, rawEvent) => {
56821
+ if (ev["type"] === "content_block_start") {
56822
+ const cb = ev["content_block"];
56823
+ const bt2 = cb && typeof cb === "object" ? cb["type"] : void 0;
56824
+ if (bt2 === "tool_use") sawToolUse = true;
56825
+ else if (bt2 === "thinking" || bt2 === "redacted_thinking") sawThinking = true;
56826
+ return anyPending() ? flushTails() + rawEvent + "\n\n" : rawEvent + "\n\n";
56827
+ }
56634
56828
  if (ev["type"] !== "content_block_delta") {
56635
56829
  return anyPending() ? flushTails() + rawEvent + "\n\n" : rawEvent + "\n\n";
56636
56830
  }
@@ -56641,11 +56835,17 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56641
56835
  return rawEvent + "\n\n";
56642
56836
  }
56643
56837
  const raw = d[field];
56838
+ if (field === "thinking" && raw.length > 0) sawThinking = true;
56644
56839
  if (!mayStartRenderTag(raw) && !anyPending()) {
56840
+ if (field === "text" && raw.length > 0) visibleTextChars += raw.length;
56645
56841
  return rawEvent + "\n\n";
56646
56842
  }
56647
56843
  const [clean, changed] = pushField(field, index, raw);
56648
- if (!changed) return rawEvent + "\n\n";
56844
+ if (!changed) {
56845
+ if (field === "text" && raw.length > 0) visibleTextChars += raw.length;
56846
+ return rawEvent + "\n\n";
56847
+ }
56848
+ if (field === "text" && clean.length > 0) visibleTextChars += clean.length;
56649
56849
  if (clean.length === 0 && Object.keys(d ?? {}).length <= 2) return "";
56650
56850
  return rebuildEvent(rawEvent, { ...ev, delta: { ...d, [field]: clean } });
56651
56851
  };
@@ -56676,6 +56876,10 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56676
56876
  continue;
56677
56877
  }
56678
56878
  if (ev["type"] === "message_stop") sawTerminal = true;
56879
+ if (ev["type"] === "message_delta") {
56880
+ const d = ev["delta"];
56881
+ if (d && typeof d["stop_reason"] === "string") finalFinishReason = d["stop_reason"];
56882
+ }
56679
56883
  const sample = usageFromSseEvent(ev);
56680
56884
  if (sample) mergeUsageSample(acc, sample);
56681
56885
  let backfilled = false;
@@ -56699,6 +56903,7 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
56699
56903
  if (rest.length > 0 && !res.destroyed && !res.writableEnded) await write(rest);
56700
56904
  settleUsage();
56701
56905
  maybeNoteTruncated();
56906
+ maybeWarnDegenerate();
56702
56907
  } catch (e) {
56703
56908
  settleUsage();
56704
56909
  maybeNoteTruncated();
@@ -56735,6 +56940,9 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
56735
56940
  log("warn", `[tag-echo] stripped model-emitted render tag (plugin passthrough): ${snippet.slice(0, 80).replace(/\n/g, " ")}`);
56736
56941
  log2?.(`[tag-echo] stripped model-emitted render tag from plugin passthrough text`);
56737
56942
  });
56943
+ let sawFunctionCall = false;
56944
+ let sawReasoning = false;
56945
+ let responseStatus;
56738
56946
  const write = (s3) => {
56739
56947
  if (!res.write(Buffer.from(s3, "utf8"))) {
56740
56948
  return new Promise((r) => res.once("drain", () => r()));
@@ -56755,6 +56963,22 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
56755
56963
  reason: "plugin responses passthrough stream ended without a completion event"
56756
56964
  });
56757
56965
  };
56966
+ const maybeWarnDegenerate = () => {
56967
+ if (!sawTerminal || res.destroyed || res.writableEnded) return;
56968
+ const st2 = tagFilter.stats();
56969
+ const msg = degenerateTurnWarning({
56970
+ reason: responseStatus,
56971
+ terminalReason: "completed",
56972
+ toolCalls: sawFunctionCall ? 1 : 0,
56973
+ text: st2,
56974
+ sawThinking: sawReasoning,
56975
+ wire: "plugin-passthrough-responses"
56976
+ });
56977
+ if (msg) {
56978
+ log("warn", msg);
56979
+ log2?.(msg);
56980
+ }
56981
+ };
56758
56982
  let lastDeltaMeta = null;
56759
56983
  const flushTail = (after) => {
56760
56984
  const tail = tagFilter.flush();
@@ -56794,6 +57018,16 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
56794
57018
  const sample = usageFromSseEvent(ev);
56795
57019
  if (sample) mergeUsageSample(acc, sample);
56796
57020
  const type = ev["type"];
57021
+ if (typeof type === "string") {
57022
+ if (type.startsWith("response.reasoning")) sawReasoning = true;
57023
+ if (type === "response.output_item.added" || type === "response.output_item.done") {
57024
+ const item = ev["item"];
57025
+ const it2 = item?.["type"];
57026
+ if (it2 === "function_call" || it2 === "custom_tool_call") sawFunctionCall = true;
57027
+ }
57028
+ const resp = ev["response"];
57029
+ if (resp && typeof resp["status"] === "string") responseStatus = resp["status"];
57030
+ }
56797
57031
  if (type === "response.output_text.done" || type === "response.content_part.done" || type === "response.output_item.done" || type === "response.completed" || type === "response.failed" || type === "response.incomplete") {
56798
57032
  if (type === "response.completed" || type === "response.failed" || type === "response.incomplete") sawTerminal = true;
56799
57033
  let evOut = ev;
@@ -56839,6 +57073,7 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
56839
57073
  const rest = flushTail("");
56840
57074
  if (rest.length > 0) await write(rest);
56841
57075
  }
57076
+ maybeWarnDegenerate();
56842
57077
  settleUsage();
56843
57078
  maybeNoteTruncated();
56844
57079
  } catch (e) {
@@ -58914,6 +59149,7 @@ async function handle(req, res, opts, core, config, log2, instanceId, instanceSt
58914
59149
  try {
58915
59150
  const result = await fetchWithTimeout(targetUrl2, {
58916
59151
  method: "HEAD",
59152
+ redirect: "follow",
58917
59153
  ...proxyUrl ? { dispatcher: proxyDispatcher(proxyUrl, 15e3) } : {}
58918
59154
  }, 15e3);
58919
59155
  result.clearTimer();
@@ -59110,7 +59346,11 @@ ${bodyBuffer.toString("utf8")}`);
59110
59346
  }
59111
59347
  }
59112
59348
  let prepared = null;
59113
- if (!opts.passthrough && hopMarker === void 0 && protocol && parsed && typeof parsed === "object") {
59349
+ const routePassthrough = !opts.passthrough && findRoute(opts.routes, route?.rewrittenUrl)?.passthrough === true;
59350
+ if (routePassthrough && hopMarker === void 0 && protocol && parsed) {
59351
+ log2("info", `[route-passthrough] ${maskUrlsInText(route?.rewrittenUrl ?? "")} matches a passthrough route \u2014 forwarding verbatim, kernel bypassed`);
59352
+ }
59353
+ if (!opts.passthrough && !routePassthrough && hopMarker === void 0 && protocol && parsed && typeof parsed === "object") {
59114
59354
  const sessionHeader = headerValue2(req, opts.sessionHeader);
59115
59355
  let pluginAgent = pluginAgentHeader(req.headers);
59116
59356
  let pluginConversation = pluginConversationHeader(req.headers);
@@ -59371,7 +59611,7 @@ ${bodyBuffer.toString("utf8")}`);
59371
59611
  }
59372
59612
  }
59373
59613
  if (!prepared) {
59374
- if (protocol === null && !opts.passthrough && !isModelDiscoveryPath(urlPath)) {
59614
+ if (protocol === null && !opts.passthrough && !routePassthrough && !isModelDiscoveryPath(urlPath)) {
59375
59615
  logUnrecognizedPath(log2, req.url ?? "");
59376
59616
  }
59377
59617
  await forward(req, res, opts, bodyBuffer, null, core, reqConfig, log2, route, instanceId, void 0);
@@ -59473,9 +59713,11 @@ function diagNudge(turn, sessionId, tokenCount, limit, model, willInject) {
59473
59713
  const modelTag = model ? ` model=${model}` : "";
59474
59714
  return `[${sessionId}] nudge ${inject}: usage=${pct2} (${tokenCount}/${limit}), growth=${growth}/${floor} (ref=${ref}, interval=${interval}), pendingT1=${pendingT1}/${interval}${modelTag}, reason="${n.reason.slice(0, 120)}"`;
59475
59715
  }
59476
- function armHostUsageCredit(session, originalMessages, processedMessages, log2) {
59716
+ function armHostUsageCredit(session, originalMessages, processedMessages, headers, hostUsageCredit, log2) {
59477
59717
  session.hostCreditTokens = 0;
59718
+ if (hostUsageCredit === "off") return;
59478
59719
  if (session.metadata.pluginAgent === "pi" || session.metadata.pluginAgent === "omp") return;
59720
+ if (isCodexClient(headers)) return;
59479
59721
  session.hostCreditTokens = processedMessages.length > 0 ? Math.max(0, estimateCoreMessages(originalMessages) - estimateCoreMessages(processedMessages)) : 0;
59480
59722
  if (session.hostCreditTokens > 0) {
59481
59723
  log2("info", `[${session.id}] host usage backfill armed: +${session.hostCreditTokens} tok (forwarded view is folded); host usage will report the uncompressed baseline`);
@@ -59556,7 +59798,7 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
59556
59798
  markDirty(session);
59557
59799
  const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
59558
59800
  delete rebuilt.prompt_cache_key;
59559
- armHostUsageCredit(session, originalMessages, processedMessages, log2);
59801
+ armHostUsageCredit(session, originalMessages, processedMessages, req.headers, opts.hostUsageCredit, log2);
59560
59802
  return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: injectTools, pluginMode, nudge, prompts, renderTags: "text-only" };
59561
59803
  }
59562
59804
  var OUTPUT_CLAMP_MARGIN_PCT = 0.05;
@@ -59688,7 +59930,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
59688
59930
  if (stream2 && rebuilt.stream_options === void 0) {
59689
59931
  rebuilt.stream_options = { include_usage: true };
59690
59932
  }
59691
- armHostUsageCredit(session, originalMessages, processedMessages, log2);
59933
+ armHostUsageCredit(session, originalMessages, processedMessages, req.headers, opts.hostUsageCredit, log2);
59692
59934
  if (!isTitleGen && openaiOutboundSystem !== void 0) {
59693
59935
  session.metadata.systemPromptTokens = countSystemAndToolsTokens(openaiOutboundSystem, toolsOut);
59694
59936
  }
@@ -59840,7 +60082,7 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
59840
60082
  });
59841
60083
  log2("info", `[${sessionId}] responses forward tools=[${fwdTools.join(",")}] injectTool=${injectTools}${pluginMode ? " (plugin mode: wire injection suppressed)" : ""} NO_INJECT_TOOL=${!!process.env.ACP_NO_INJECT_TOOL} NO_COMPRESS_PROMPT=${!!process.env.ACP_NO_COMPRESS_PROMPT}`);
59842
60084
  }
59843
- armHostUsageCredit(session, originalMessages, processedMessages, log2);
60085
+ armHostUsageCredit(session, originalMessages, processedMessages, req.headers, opts.hostUsageCredit, log2);
59844
60086
  if (transformOk) {
59845
60087
  session.metadata.systemPromptTokens = countSystemAndToolsTokens(responsesDevContent ?? "", toolsOut);
59846
60088
  }