billion-context 0.1.101 → 0.1.102-pr.641.521

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/README.md CHANGED
@@ -158,7 +158,7 @@ rewrite until dsh gains a settings-path env or an upstream loopback opt-out.
158
158
  Overlay dirs created by older versions are left in place and never merged back
159
159
  into the real home.
160
160
 
161
- ### Option 1 — Launcher (`bili pi` / `bili codex` / `bili claude` / `bili omp` / `bili opencode` / `bili hermes` / `bili dsh`)
161
+ ### Option 1 — Launcher (`bili pi` / `bili codex` / `bili claude` / `bili omp` / `bili opencode` / `bili hermes` / `bili dsh` / `bili codebuddy`)
162
162
 
163
163
  The launcher wraps a client in one command: it starts a proxy on an
164
164
  independent port (a fresh instance is always spawned — a port is never
@@ -177,6 +177,7 @@ bili omp # pi-style, file-free (#535): env + extens
177
177
  bili opencode # MITM for HTTPS + temp opencode.json (/bili/ for HTTP) + thin /acp plugin
178
178
  bili hermes # file-free (#535): hermes proxy env (HTTPS_PROXY + HERMES_CA_BUNDLE) — https via CONNECT MITM, http via absolute-form forward proxy; real ~/.hermes untouched
179
179
  bili dsh # deepseek-harness: non-loopback upstreams ride proxy envs (https MITM, http absolute-form), loopback keeps the overlay DSH_HOME (~/.dsh-bili) rewrite (#535), built-in deepseek route via DEEPSEEK_BASE_URL, native /acp command injected via --patch
180
+ bili codebuddy # Tencent CodeBuddy Code CLI: CODEBUDDY_BASE_URL /bili/ rewrite (OpenAI chat completions wire), budget aligned via CODEBUDDY_AUTO_COMPACT_WINDOW; real ~/.codebuddy untouched
180
181
  bili pi --mitm-domain api.foo.com # add a domain to the MITM whitelist
181
182
  ```
182
183
 
package/dist/index.js CHANGED
@@ -43467,7 +43467,7 @@ var require_lib = __commonJS({
43467
43467
  }
43468
43468
  });
43469
43469
 
43470
- // node_modules/acp-kernel/dist/chunk-JGNVAFFE.js
43470
+ // node_modules/acp-kernel/dist/chunk-6TAK7DSI.js
43471
43471
  import { createRequire } from "module";
43472
43472
  var require2 = createRequire(import.meta.url);
43473
43473
  function defaultCountTokens(text) {
@@ -43476,6 +43476,12 @@ function defaultCountTokens(text) {
43476
43476
  const cjkCount = cjk?.length ?? 0;
43477
43477
  return cjkCount + Math.ceil((text.length - cjkCount) / 4);
43478
43478
  }
43479
+ function thinkingTokenValue(thinking) {
43480
+ return typeof thinking === "number" && Number.isFinite(thinking) && thinking > 0 ? thinking : 0;
43481
+ }
43482
+ function countMessageTokens(message, countTokens = defaultCountTokens) {
43483
+ return countTokens(message.text ?? "") + thinkingTokenValue(message.thinkingTokens);
43484
+ }
43479
43485
  function estimateTokensFast(text) {
43480
43486
  if (!text) return 0;
43481
43487
  return Math.ceil(text.length / 4);
@@ -45283,7 +45289,8 @@ function renderMessage(message, map, countTokens, strategy, snapshot = null) {
45283
45289
  "^" + escapeRegex(TAG_OPEN) + "[^>]*" + GT + escapeRegex(ref) + escapeRegex(TAG_CLOSE) + "\\n?"
45284
45290
  );
45285
45291
  const cleanText = (message.text || "").replace(ownTagRe, "");
45286
- const tokens = snapshot ? snapshot[ref] ?? (snapshot[ref] = countTokens(cleanText)) : countTokens(cleanText);
45292
+ const textTokens = snapshot ? snapshot[ref] ?? (snapshot[ref] = countTokens(cleanText)) : countTokens(cleanText);
45293
+ const tokens = textTokens + thinkingTokenValue(message.thinkingTokens);
45287
45294
  const type = classifyType(message);
45288
45295
  const prefix = acpTag(ref, tokens, type) + "\n";
45289
45296
  if (!cleanText) return { ...message, text: prefix };
@@ -45362,8 +45369,12 @@ function adjustBoundariesForReasoningPairs(startIndex, endIndex, messages) {
45362
45369
  j2++;
45363
45370
  }
45364
45371
  const companion = messages[j2 + 1];
45365
- if (companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call") && j2 + 1 > newEndIndex) {
45366
- newEndIndex = j2 + 1;
45372
+ if (companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call")) {
45373
+ let e = j2 + 1;
45374
+ while (e + 1 < messages.length && messages[e + 1].role === "assistant" && (messages[e + 1].contentType === "text" || messages[e + 1].contentType === "tool-call")) {
45375
+ e++;
45376
+ }
45377
+ if (e > newEndIndex) newEndIndex = e;
45367
45378
  }
45368
45379
  }
45369
45380
  if (msg.role === "assistant" && (msg.contentType === "text" || msg.contentType === "tool-call")) {
@@ -45379,6 +45390,64 @@ function adjustBoundariesForReasoningPairs(startIndex, endIndex, messages) {
45379
45390
  }
45380
45391
  return { startIndex: newStartIndex, endIndex: newEndIndex };
45381
45392
  }
45393
+ function isAssistantAct(msg) {
45394
+ return msg.role === "assistant" && (msg.contentType === "text" || msg.contentType === "tool-call");
45395
+ }
45396
+ function computeTurnGroups(messages) {
45397
+ const resultIdByCallId = /* @__PURE__ */ new Map();
45398
+ for (const msg of messages) {
45399
+ if (msg.contentType === "tool-result" && typeof msg.toolCallId === "string" && msg.id) {
45400
+ if (!resultIdByCallId.has(msg.toolCallId))
45401
+ resultIdByCallId.set(msg.toolCallId, msg.id);
45402
+ }
45403
+ }
45404
+ const grouped = /* @__PURE__ */ new Set();
45405
+ const groups = [];
45406
+ for (let i = 0; i < messages.length; i++) {
45407
+ const msg = messages[i];
45408
+ if (!msg.id || grouped.has(msg.id)) continue;
45409
+ if (!(msg.contentType === "reasoning" || isAssistantAct(msg))) continue;
45410
+ let reasoningStart = i;
45411
+ if (msg.contentType === "reasoning") {
45412
+ while (reasoningStart > 0 && messages[reasoningStart - 1].contentType === "reasoning") {
45413
+ reasoningStart--;
45414
+ }
45415
+ } else {
45416
+ let s3 = i;
45417
+ while (s3 > 0 && isAssistantAct(messages[s3 - 1])) s3--;
45418
+ reasoningStart = s3;
45419
+ while (reasoningStart > 0 && messages[reasoningStart - 1].contentType === "reasoning") {
45420
+ reasoningStart--;
45421
+ }
45422
+ }
45423
+ const burstStart = (() => {
45424
+ let s3 = reasoningStart;
45425
+ while (s3 < messages.length && messages[s3].contentType === "reasoning")
45426
+ s3++;
45427
+ return s3;
45428
+ })();
45429
+ if (burstStart >= messages.length || !isAssistantAct(messages[burstStart])) {
45430
+ continue;
45431
+ }
45432
+ let burstEnd = burstStart;
45433
+ while (burstEnd + 1 < messages.length && isAssistantAct(messages[burstEnd + 1])) {
45434
+ burstEnd++;
45435
+ }
45436
+ const members = /* @__PURE__ */ new Set();
45437
+ for (let k2 = reasoningStart; k2 <= burstEnd; k2++) {
45438
+ const m2 = messages[k2];
45439
+ if (!m2.id) continue;
45440
+ members.add(m2.id);
45441
+ if (m2.role === "assistant" && m2.contentType === "tool-call" && typeof m2.toolCallId === "string") {
45442
+ const rid = resultIdByCallId.get(m2.toolCallId);
45443
+ if (rid) members.add(rid);
45444
+ }
45445
+ }
45446
+ for (const id of members) grouped.add(id);
45447
+ groups.push([...members]);
45448
+ }
45449
+ return groups;
45450
+ }
45382
45451
  function estimateTextTokens(text) {
45383
45452
  return Math.ceil(text.length / 4);
45384
45453
  }
@@ -45402,7 +45471,7 @@ function computeProtectedRefs(messages, state, config, countTokens = estimateTex
45402
45471
  if (isNeverPreserveRecent(msg)) continue;
45403
45472
  const ref = state.messageRefs.byRaw[msg.id];
45404
45473
  if (!ref || ref === "BLOCKED") continue;
45405
- visible.push({ ref, tokens: countTokens(msg.text ?? "") });
45474
+ visible.push({ ref, tokens: countMessageTokens(msg, countTokens) });
45406
45475
  }
45407
45476
  if (preserveN > 0) {
45408
45477
  for (const m2 of visible.slice(-preserveN)) {
@@ -45445,7 +45514,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
45445
45514
  protectedMsgs.push({
45446
45515
  ref,
45447
45516
  gapBefore: skipSinceProtected,
45448
- tokens: countTokens(msg.text ?? ""),
45517
+ tokens: countMessageTokens(msg, countTokens),
45449
45518
  tools: msg.toolName ? [msg.toolName] : []
45450
45519
  });
45451
45520
  skipSinceProtected = false;
@@ -45460,7 +45529,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
45460
45529
  compressibleMsgs.push({
45461
45530
  ref,
45462
45531
  gapBefore: skipSinceCompressible,
45463
- tokens: countTokens(msg.text ?? ""),
45532
+ tokens: countMessageTokens(msg, countTokens),
45464
45533
  chars: (msg.text ?? "").length,
45465
45534
  isTool: isToolMessage(msg),
45466
45535
  isUser: msg.role === "user"
@@ -46093,6 +46162,42 @@ function applySingleRange(input) {
46093
46162
  )} from compression range (recent/last-user zone).`
46094
46163
  );
46095
46164
  }
46165
+ {
46166
+ const reasoningIds = /* @__PURE__ */ new Set();
46167
+ const callIds = /* @__PURE__ */ new Set();
46168
+ for (const m2 of input.messages) {
46169
+ if (!m2.id) continue;
46170
+ if (m2.contentType === "reasoning") reasoningIds.add(m2.id);
46171
+ if (m2.role === "assistant" && m2.contentType === "tool-call") callIds.add(m2.id);
46172
+ }
46173
+ const withdrawIds = /* @__PURE__ */ new Set();
46174
+ let splitTurnCount = 0;
46175
+ for (const group of computeTurnGroups(input.messages)) {
46176
+ const foldHasReasoning = group.some(
46177
+ (id) => effectiveMessageIds.has(id) && reasoningIds.has(id)
46178
+ );
46179
+ if (!foldHasReasoning) continue;
46180
+ const keptHasCall = group.some(
46181
+ (id) => !effectiveMessageIds.has(id) && callIds.has(id)
46182
+ );
46183
+ if (!keptHasCall) continue;
46184
+ splitTurnCount++;
46185
+ for (const id of group) withdrawIds.add(id);
46186
+ }
46187
+ if (withdrawIds.size > 0) {
46188
+ for (const id of withdrawIds) effectiveMessageIds.delete(id);
46189
+ const beforeWithdraw = filteredIds.length;
46190
+ filteredIds = filteredIds.filter((id) => !withdrawIds.has(id));
46191
+ if (filteredIds.length === 0 && consumedBlockIds.length === 0) {
46192
+ throw new Error(
46193
+ `Range would split ${splitTurnCount} turn(s) at the protected-zone boundary: a visible tool-call must keep its reasoning run (strict-echo providers reject a rebuilt request that lost it). Shrink the range to end before the turn starts, or wait until the whole turn ages out of the protected zone.`
46194
+ );
46195
+ }
46196
+ warnings.push(
46197
+ `Withdrawn ${beforeWithdraw - filteredIds.length} message(s) from compression range to keep ${splitTurnCount} turn(s) intact (visible tool-call would lose its reasoning run).`
46198
+ );
46199
+ }
46200
+ }
46096
46201
  if (!isBlockBoundary && filteredIds.length === 0 && consumedBlockIds.length > 0) {
46097
46202
  const first = consumedBlockIds[0];
46098
46203
  const last = consumedBlockIds[consumedBlockIds.length - 1];
@@ -46106,7 +46211,7 @@ function applySingleRange(input) {
46106
46211
  let compressedTokens = 0;
46107
46212
  for (const id of filteredIds) {
46108
46213
  const message = input.messages.find((entry) => entry.id === id);
46109
- compressedTokens += input.countTokens(message?.text ?? "");
46214
+ compressedTokens += message ? countMessageTokens(message, input.countTokens) : 0;
46110
46215
  }
46111
46216
  for (const consumedId of consumedBlockIds) {
46112
46217
  const consumed = blockById(input.state, consumedId);
@@ -46436,7 +46541,7 @@ function computeContextBreakdown(messages, total, growth, countTokens) {
46436
46541
  const count = countTokens ?? ((t) => Math.ceil(t.length / 4));
46437
46542
  let system = 0, tool = 0, summaries = 0, code = 0, text = 0;
46438
46543
  for (const msg of messages) {
46439
- const tokens = count(msg.text ?? "");
46544
+ const tokens = countMessageTokens(msg, count);
46440
46545
  if (msg.text?.startsWith("[Compressed conversation section]")) {
46441
46546
  summaries += tokens;
46442
46547
  } else if (msg.contentType === "tool-call" || msg.contentType === "tool-result") {
@@ -46622,7 +46727,7 @@ function collectVisible(messages, state, countTokens) {
46622
46727
  if (coveredIds.has(message.id)) return;
46623
46728
  const ref = refForRaw(state.messageRefs, message.id);
46624
46729
  if (!ref) return;
46625
- const tokens = countTokens(message.text ?? "");
46730
+ const tokens = countMessageTokens(message, countTokens);
46626
46731
  const tool = isToolMessage(message) ? message.toolName ?? (message.toolCallId ? toolCallNames.get(message.toolCallId) : void 0) ?? "tool" : "text";
46627
46732
  if (tokens > 0) visible.push({ ref, tokens, tool, index });
46628
46733
  });
@@ -53721,6 +53826,11 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
53721
53826
  }
53722
53827
  } catch (e) {
53723
53828
  if (!(e instanceof UpstreamHttpError)) throw e;
53829
+ if (e.status === 400 && /reasoning_content/i.test(e.body) && ctx.session.metadata.strictReasoningEcho !== true) {
53830
+ ctx.session.metadata.strictReasoningEcho = true;
53831
+ ctx.log(`[acp-loop] 400 mentions reasoning_content \u2014 learned strict reasoning echo for this session; #651 reasoning-drop disabled (#684)`);
53832
+ log("warn", `[acp-loop] learned strictReasoningEcho (session ${ctx.session.id}); reasoning-drop disabled (#684)`);
53833
+ }
53724
53834
  const suffix = e.attempts > 1 ? ` after ${e.attempts} attempt(s)` : "";
53725
53835
  ctx.log(`[acp-proxy: compress loop upstream error ${e.status}${suffix}: ${e.body.slice(0, 200)}]`);
53726
53836
  log("error", `[acp-loop] upstream error ${e.status}${suffix}: ${e.body.slice(0, 200)}`);
@@ -57204,6 +57314,98 @@ function resolveCodexHome(env) {
57204
57314
  const h = os.homedir();
57205
57315
  return nonEmpty2(env.CODEX_HOME) ? env.CODEX_HOME : path10.join(h, ".codex");
57206
57316
  }
57317
+ function resolveCodebuddyHome(env) {
57318
+ const h = os.homedir();
57319
+ return nonEmpty2(env.CODEBUDDY_CONFIG_DIR) ? env.CODEBUDDY_CONFIG_DIR : path10.join(h, ".codebuddy");
57320
+ }
57321
+ function parseCodebuddyModelsJson(obj) {
57322
+ const out = { models: [], urls: [] };
57323
+ const seenUrl = /* @__PURE__ */ new Set();
57324
+ const collect = (id, entry) => {
57325
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return;
57326
+ const e = entry;
57327
+ const url = e.url;
57328
+ if (typeof url === "string" && url.length > 0 && !seenUrl.has(url)) {
57329
+ seenUrl.add(url);
57330
+ out.urls.push(url);
57331
+ }
57332
+ const win = toModelWindow(id, e.maxInputTokens);
57333
+ if (win) out.models.push(win);
57334
+ };
57335
+ if (!obj) return out;
57336
+ if (Array.isArray(obj)) {
57337
+ for (const item of obj) {
57338
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
57339
+ const it2 = item;
57340
+ collect(it2.id ?? it2.name, it2);
57341
+ }
57342
+ return out;
57343
+ }
57344
+ if (typeof obj !== "object") return out;
57345
+ const root = obj;
57346
+ const modelsField = root.models;
57347
+ if (Array.isArray(modelsField)) {
57348
+ for (const item of modelsField) {
57349
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
57350
+ const it2 = item;
57351
+ collect(it2.id ?? it2.name, it2);
57352
+ }
57353
+ return out;
57354
+ }
57355
+ if (modelsField && typeof modelsField === "object") {
57356
+ for (const [id, val] of Object.entries(modelsField)) collect(id, val);
57357
+ return out;
57358
+ }
57359
+ for (const [id, val] of Object.entries(root)) collect(id, val);
57360
+ return out;
57361
+ }
57362
+ function readJsonFile(filePath) {
57363
+ try {
57364
+ return JSON.parse(fs6.readFileSync(filePath, "utf8"));
57365
+ } catch {
57366
+ return null;
57367
+ }
57368
+ }
57369
+ function readCodebuddyConfig(codebuddyHome, cwd, env = process.env) {
57370
+ let codebuddyBaseUrl;
57371
+ let model;
57372
+ let autoCompactWindow;
57373
+ const settings = readJsonObject(path10.join(codebuddyHome, "settings.json"));
57374
+ const settingsEnv = settings?.env;
57375
+ if (settingsEnv && typeof settingsEnv === "object" && !Array.isArray(settingsEnv)) {
57376
+ const e = settingsEnv;
57377
+ const v2 = e.CODEBUDDY_BASE_URL;
57378
+ if (nonEmpty2(v2)) codebuddyBaseUrl = v2;
57379
+ }
57380
+ const tm = settings?.model;
57381
+ if (nonEmpty2(tm)) model = String(tm);
57382
+ const tacw = Number(settings?.autoCompactWindow);
57383
+ if (Number.isFinite(tacw) && tacw > 0) autoCompactWindow = tacw;
57384
+ if (!codebuddyBaseUrl && nonEmpty2(env.CODEBUDDY_BASE_URL)) codebuddyBaseUrl = env.CODEBUDDY_BASE_URL;
57385
+ const windowByModel = /* @__PURE__ */ new Map();
57386
+ const urls = [];
57387
+ const seenUrl = /* @__PURE__ */ new Set();
57388
+ for (const f2 of [
57389
+ path10.join(codebuddyHome, "models.json"),
57390
+ path10.join(cwd, ".codebuddy", "models.json")
57391
+ ]) {
57392
+ const parsed = parseCodebuddyModelsJson(readJsonFile(f2));
57393
+ for (const w2 of parsed.models) windowByModel.set(w2.id, w2.contextWindow);
57394
+ for (const u2 of parsed.urls) {
57395
+ if (!seenUrl.has(u2)) {
57396
+ seenUrl.add(u2);
57397
+ urls.push(u2);
57398
+ }
57399
+ }
57400
+ }
57401
+ return {
57402
+ ...codebuddyBaseUrl ? { codebuddyBaseUrl } : {},
57403
+ ...model ? { model } : {},
57404
+ ...autoCompactWindow ? { autoCompactWindow } : {},
57405
+ ...windowByModel.size > 0 ? { models: [...windowByModel.entries()].map(([id, contextWindow]) => ({ id, contextWindow })) } : {},
57406
+ ...urls.length > 0 ? { modelUrls: urls } : {}
57407
+ };
57408
+ }
57207
57409
  function parseDshSettingsYaml(text) {
57208
57410
  const out = [];
57209
57411
  const seen = /* @__PURE__ */ new Set();
@@ -57577,6 +57779,7 @@ function loadClientConfig(env, cwd) {
57577
57779
  config.opencode = readOpencodeConfig(resolveOpencodeConfigFile(env));
57578
57780
  config.hermes = readHermesConfig(resolveHermesHome(env));
57579
57781
  config.dsh = readDshConfig(resolveDshHome(env));
57782
+ config.codebuddy = readCodebuddyConfig(resolveCodebuddyHome(env), cwd, env);
57580
57783
  return config;
57581
57784
  }
57582
57785
  function collectModelWindows(config, scope) {
@@ -57591,12 +57794,14 @@ function collectModelWindows(config, scope) {
57591
57794
  else if (scope === "pi") for (const p2 of Object.values(config.pi?.providers ?? {})) add(p2.models);
57592
57795
  else if (scope === "omp") for (const p2 of Object.values(config.omp?.providers ?? {})) add(p2.models);
57593
57796
  else if (scope === "opencode") for (const p2 of Object.values(config.opencode?.providers ?? {})) add(p2.models);
57797
+ else if (scope === "codebuddy") add(config.codebuddy?.models);
57594
57798
  return out;
57595
57799
  }
57596
57800
  for (const p2 of Object.values(config.pi?.providers ?? {})) add(p2.models);
57597
57801
  for (const p2 of Object.values(config.omp?.providers ?? {})) add(p2.models);
57598
57802
  for (const p2 of Object.values(config.opencode?.providers ?? {})) add(p2.models);
57599
57803
  add(config.codex?.modelWindows);
57804
+ add(config.codebuddy?.models);
57600
57805
  return out;
57601
57806
  }
57602
57807
 
@@ -57634,18 +57839,26 @@ function extractHttpsHosts(config) {
57634
57839
  if (config.zcode) {
57635
57840
  for (const prov of Object.values(config.zcode.providers)) push(prov.baseURL);
57636
57841
  }
57842
+ if (config.codebuddy) {
57843
+ push(config.codebuddy.codebuddyBaseUrl);
57844
+ for (const u2 of config.codebuddy.modelUrls ?? []) push(u2);
57845
+ }
57637
57846
  return out;
57638
57847
  }
57639
57848
  function configFilePaths(env) {
57640
57849
  const home = os2.homedir();
57641
57850
  const codexHome = nonEmpty2(env.CODEX_HOME) ? env.CODEX_HOME : path11.join(home, ".codex");
57642
57851
  const zcodeHome = nonEmpty2(env.ZCODE_DATA_BASE_DIR) ? env.ZCODE_DATA_BASE_DIR : path11.join(home, ".zcode");
57852
+ const codebuddyHome = resolveCodebuddyHome(env);
57643
57853
  return [
57644
57854
  path11.join(home, ".claude", "settings.json"),
57645
57855
  path11.join(process.cwd(), ".claude", "settings.json"),
57646
57856
  path11.join(codexHome, "config.toml"),
57647
57857
  path11.join(resolvePiHome(env), "models.json"),
57648
- path11.join(zcodeHome, "v2", "config.json")
57858
+ path11.join(zcodeHome, "v2", "config.json"),
57859
+ path11.join(codebuddyHome, "settings.json"),
57860
+ path11.join(codebuddyHome, "models.json"),
57861
+ path11.join(process.cwd(), ".codebuddy", "models.json")
57649
57862
  ];
57650
57863
  }
57651
57864
  function readMtimes(paths) {
@@ -59536,7 +59749,7 @@ ${bodyBuffer.toString("utf8")}`);
59536
59749
  log2("info", `[debug] strip-images: dropped ${stripped.removed} historical image part(s), kept last ${keepRecent} (session=${session.id})`);
59537
59750
  }
59538
59751
  const work = stripped.body;
59539
- return countTokens ? prepareCountTokens(work, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, reasoningCfg) : protocol === "openai" ? prepareOpenai(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, nativeWindow, reasoningCfg) : responsesCompact ? prepareResponsesCompact(stripped.removed > 0 ? Buffer.from(JSON.stringify(work)) : bodyBuffer, work, session, req, core, reqConfig, log2) : prepareResponses(work, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity, pluginMode, upstreamOrigin, nativeWindow, reasoningCfg);
59752
+ return countTokens ? prepareCountTokens(work, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, upstreamOrigin, reasoningCfg) : protocol === "openai" ? prepareOpenai(work, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, upstreamOrigin, nativeWindow, reasoningCfg) : responsesCompact ? prepareResponsesCompact(stripped.removed > 0 ? Buffer.from(JSON.stringify(work)) : bodyBuffer, work, session, req, core, reqConfig, log2) : prepareResponses(work, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity, pluginMode, upstreamOrigin, nativeWindow, reasoningCfg);
59540
59753
  };
59541
59754
  const isCodexCompactTrigger = protocol === "responses" && !responsesCompact && isCodexClient(req.headers) && hasCompactionTrigger(parsed.input);
59542
59755
  if (isCodexCompactTrigger) {
@@ -59624,13 +59837,64 @@ ${bodyBuffer.toString("utf8")}`);
59624
59837
  }
59625
59838
  }
59626
59839
  var ACP_TAG_MARK = "<acp ";
59627
- function withReasoningDrop(msgs, reasoning, log2, sessionId) {
59840
+ function withReasoningDrop(msgs, reasoning, log2, sessionId, strictEcho) {
59841
+ if (strictEcho) return msgs;
59628
59842
  const out = dropCompressReasoning(msgs, reasoning);
59629
59843
  if (out.length !== msgs.length) {
59630
59844
  log2("info", `[${sessionId}] compress-reasoning: dropped ${msgs.length - out.length} reasoning message(s) from closed compress turns (#651)`);
59631
59845
  }
59632
59846
  return out;
59633
59847
  }
59848
+ function isStrictReasoningEcho(session, upstreamOrigin) {
59849
+ if (session.metadata.strictReasoningEcho === true) return true;
59850
+ return upstreamOrigin !== void 0 && /deepseek/i.test(upstreamOrigin);
59851
+ }
59852
+ function warnReasoningPairs(wireMessages, log2, sessionId) {
59853
+ let withRc = 0;
59854
+ let split = 0;
59855
+ for (const m2 of wireMessages) {
59856
+ const msg = m2;
59857
+ if (msg?.role !== "assistant") continue;
59858
+ const hasRc = typeof msg.reasoning_content === "string" && msg.reasoning_content.length > 0;
59859
+ if (hasRc) withRc++;
59860
+ else if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) split++;
59861
+ }
59862
+ if (withRc > 0 && split > 0) {
59863
+ log2("warn", `[${sessionId}] reasoning-pair-violated: ${split} assistant tool-call message(s) lack reasoning_content while ${withRc} carry it \u2014 strict-echo upstreams (DeepSeek thinking mode) will reject the request (#684)`);
59864
+ }
59865
+ }
59866
+ function warnAnthropicThinkingPairs(wireMessages, log2, sessionId) {
59867
+ let withThinking = 0;
59868
+ let split = 0;
59869
+ for (const m2 of wireMessages) {
59870
+ const msg = m2;
59871
+ if (msg?.role !== "assistant" || !Array.isArray(msg.content)) continue;
59872
+ const hasThinking = msg.content.some((b2) => b2?.type === "thinking");
59873
+ if (hasThinking) withThinking++;
59874
+ else if (msg.content.some((b2) => b2?.type === "tool_use")) split++;
59875
+ }
59876
+ if (withThinking > 0 && split > 0) {
59877
+ log2("warn", `[${sessionId}] thinking-pair-violated: ${split} assistant tool_use message(s) lack a thinking block while ${withThinking} carry one \u2014 extended-thinking tool use requires preserved thinking (#684)`);
59878
+ }
59879
+ }
59880
+ function warnResponsesReasoningPairs(input, log2, sessionId) {
59881
+ let withReasoning = 0;
59882
+ let split = 0;
59883
+ let prevWasReasoning = false;
59884
+ for (const item of input) {
59885
+ const it2 = item;
59886
+ if (it2?.type === "reasoning") {
59887
+ withReasoning++;
59888
+ prevWasReasoning = true;
59889
+ continue;
59890
+ }
59891
+ if (it2?.type === "function_call" && !prevWasReasoning) split++;
59892
+ prevWasReasoning = false;
59893
+ }
59894
+ if (withReasoning > 0 && split > 0) {
59895
+ log2("warn", `[${sessionId}] reasoning-pair-violated: ${split} function_call item(s) lack a preceding reasoning item while ${withReasoning} exist \u2014 strict-echo upstreams will reject the request (#684)`);
59896
+ }
59897
+ }
59634
59898
  function stripKernelSummaries(messages, state) {
59635
59899
  const carried = /* @__PURE__ */ new Set();
59636
59900
  for (const b2 of state.blocks) {
@@ -59734,13 +59998,13 @@ function effectiveTokenCount(session, msgs) {
59734
59998
  if (!session.metadata.anonymousPrefixAffinity) return 0;
59735
59999
  return estimateCoreMessagesUpper(msgs);
59736
60000
  }
59737
- function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, session, pluginMode, reasoning) {
60001
+ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, session, pluginMode, upstreamOrigin, reasoning) {
59738
60002
  const sessionId = session.id;
59739
60003
  const stream2 = parsed.stream === true;
59740
60004
  ++session.stats.requests;
59741
60005
  session.hostCreditTokens = 0;
59742
60006
  const injectTools = opts.compress.injectTool && !pluginMode;
59743
- const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId);
60007
+ const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId, isStrictReasoningEcho(session, upstreamOrigin));
59744
60008
  if (isAutoModeClassifier(parsed)) {
59745
60009
  log2("info", `[${sessionId}] auto-mode classifier passthrough (skipping compress injection)`);
59746
60010
  return { body: JSON.stringify(parsed), session, processedMessages: [], originalMessages: [], anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: false, pluginMode, nudge: void 0, prompts };
@@ -59803,6 +60067,7 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
59803
60067
  snapshotMessages(session, originalMessages);
59804
60068
  markDirty(session);
59805
60069
  const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
60070
+ warnAnthropicThinkingPairs(rebuiltMessages, log2, sessionId);
59806
60071
  delete rebuilt.prompt_cache_key;
59807
60072
  armHostUsageCredit(session, originalMessages, processedMessages, req.headers, opts.hostUsageCredit, log2);
59808
60073
  return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: injectTools, pluginMode, nudge, prompts, renderTags: "text-only" };
@@ -59860,13 +60125,13 @@ function clampOutgoingOutput(rebuilt, field, ctx, sessionId, log2) {
59860
60125
  log2("info", `[${sessionId}] output budget clamped ${raw} -> ${capped} (input~${inputEstimate}, window=${ctx.nativeWindow}); prevents input+output overflow (#453)`);
59861
60126
  }
59862
60127
  }
59863
- function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session, pluginMode, nativeWindow, reasoning) {
60128
+ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session, pluginMode, upstreamOrigin, nativeWindow, reasoning) {
59864
60129
  const sessionId = session.id;
59865
60130
  const stream2 = parsed.stream === true;
59866
60131
  ++session.stats.requests;
59867
60132
  session.hostCreditTokens = 0;
59868
60133
  let openaiSystemText = "";
59869
- const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId);
60134
+ const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId, isStrictReasoningEcho(session, upstreamOrigin));
59870
60135
  let openaiOutboundSystem;
59871
60136
  let processedMessages = [];
59872
60137
  let originalMessages = [];
@@ -59931,6 +60196,7 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
59931
60196
  processedMessages = [];
59932
60197
  }
59933
60198
  const rebuilt = { ...parsed, messages: rebuiltMessages, tools: toolsOut };
60199
+ warnReasoningPairs(rebuiltMessages, log2, sessionId);
59934
60200
  clampOutgoingOutput(rebuilt, typeof parsed.max_completion_tokens === "number" ? "max_completion_tokens" : "max_tokens", { systemText: openaiSystemText, tools: toolsOut, processedMessages, lastInputTokens: session.stats.lastInputTokens, nativeWindow, imageTokens: imageTokensInParsedBody("openai", rebuilt) }, sessionId, log2);
59935
60201
  delete rebuilt.prompt_cache_retention;
59936
60202
  if (stream2 && rebuilt.stream_options === void 0) {
@@ -59949,7 +60215,7 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
59949
60215
  const stream2 = parsed.stream === true;
59950
60216
  ++session.stats.requests;
59951
60217
  session.hostCreditTokens = 0;
59952
- const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId);
60218
+ const stripReasoning = (msgs) => withReasoningDrop(msgs, reasoning, log2, sessionId, isStrictReasoningEcho(session, upstreamOrigin));
59953
60219
  if (reconcileNativeCompactionBoundary(session)) {
59954
60220
  log2("info", `[${sessionId}] reconciled ACP state after native Responses compact boundary`);
59955
60221
  }
@@ -60067,6 +60333,7 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
60067
60333
  log2("info", `[${sessionId}] codex compact intercepted (trigger); forged SSE with ${summaries.length} block summary(s), upstream not contacted`);
60068
60334
  }
60069
60335
  const rebuilt = { ...parsed, input: rebuiltInput, tools: toolsOut };
60336
+ warnResponsesReasoningPairs(Array.isArray(rebuiltInput) ? rebuiltInput : [], log2, sessionId);
60070
60337
  if (!isCompactionTrigger) {
60071
60338
  clampOutgoingOutput(rebuilt, "max_output_tokens", { systemText: (responsesProjection?.systemParts ?? []).join("\n"), tools: toolsOut, processedMessages, lastInputTokens: session.stats.lastInputTokens, nativeWindow, imageTokens: imageTokensInParsedBody("responses", rebuilt) }, sessionId, log2);
60072
60339
  }
@@ -65219,7 +65486,7 @@ function selfDistFile(name) {
65219
65486
  return path15.join(selfPackageRoot(), "dist", name);
65220
65487
  }
65221
65488
  var LAUNCHER_DEFAULT_HOST = "127.0.0.1";
65222
- var LAUNCH_CLIENTS = ["pi", "codex", "claude", "omp", "opencode", "hermes", "dsh", "pi-test"];
65489
+ var LAUNCH_CLIENTS = ["pi", "codex", "claude", "omp", "opencode", "hermes", "dsh", "codebuddy", "pi-test"];
65223
65490
  var HEALTH_PATH = "/__bili/health";
65224
65491
  var HEALTH_POLL_INTERVAL_MS = 200;
65225
65492
  var SPAWN_WAIT_MS = 2e4;
@@ -65307,6 +65574,29 @@ function discoverRoutes(client, config) {
65307
65574
  }
65308
65575
  } catch {
65309
65576
  }
65577
+ } else if (client === "codebuddy") {
65578
+ const raw = nonEmpty2(config.codebuddy?.codebuddyBaseUrl) ? config.codebuddy.codebuddyBaseUrl : "https://tencent.sso.codebuddy.cn/v2";
65579
+ const real = unwrapUpstream2(raw);
65580
+ try {
65581
+ const url = new URL(real);
65582
+ if ((url.protocol === "https:" || url.protocol === "http:") && !rewriteKeys.has("CODEBUDDY_BASE_URL")) {
65583
+ rewriteKeys.add("CODEBUDDY_BASE_URL");
65584
+ httpRewrites.push({ key: "CODEBUDDY_BASE_URL", realUpstream: real });
65585
+ }
65586
+ } catch {
65587
+ }
65588
+ for (const rawModelUrl of config.codebuddy?.modelUrls ?? []) {
65589
+ try {
65590
+ const url = new URL(unwrapUpstream2(rawModelUrl));
65591
+ if (url.protocol !== "https:") continue;
65592
+ const host = url.hostname;
65593
+ if (host && !httpsSeen.has(host.toLowerCase())) {
65594
+ httpsSeen.add(host.toLowerCase());
65595
+ httpsDomains.push(host);
65596
+ }
65597
+ } catch {
65598
+ }
65599
+ }
65310
65600
  } else if (client === "pi") {
65311
65601
  for (const [name, prov] of Object.entries(config.pi?.providers ?? {})) {
65312
65602
  classify(prov.baseUrl, name);
@@ -65450,6 +65740,21 @@ function buildClaudeEnv(origin, caPath, httpRewrites, httpsRewrites, baseEnv) {
65450
65740
  if (hr2) env.ANTHROPIC_BASE_URL = hr2.realUpstream;
65451
65741
  return env;
65452
65742
  }
65743
+ async function resolveCodebuddyBudgetEnv(opts) {
65744
+ const { model, userAutoCompactWindow, shellAutoCompactWindow, routes, upstreamUrl } = opts;
65745
+ if (!model || userAutoCompactWindow !== void 0 || nonEmpty2(shellAutoCompactWindow)) return {};
65746
+ const window2 = await resolveLauncherWindow(model, routes, upstreamUrl);
65747
+ if (!window2) return {};
65748
+ return { CODEBUDDY_AUTO_COMPACT_WINDOW: String(window2) };
65749
+ }
65750
+ function buildCodebuddyEnv(origin, caPath, httpRewrites, httpsRewrites, baseEnv) {
65751
+ const env = { ...baseEnv, HTTPS_PROXY: origin, NODE_EXTRA_CA_CERTS: caPath, BILLION_CONTEXT_PROXY: origin };
65752
+ const r = httpRewrites.find((rw) => rw.key === "CODEBUDDY_BASE_URL");
65753
+ if (r) env.CODEBUDDY_BASE_URL = wrapUpstream(origin, r.realUpstream);
65754
+ const hr2 = httpsRewrites.find((rw) => rw.key === "CODEBUDDY_BASE_URL");
65755
+ if (hr2) env.CODEBUDDY_BASE_URL = hr2.realUpstream;
65756
+ return env;
65757
+ }
65453
65758
  function launcherDirectUrl(env) {
65454
65759
  return env.BILI_LAUNCHER_DIRECT === "1";
65455
65760
  }
@@ -65490,7 +65795,7 @@ function isPrivateIPv4(host) {
65490
65795
  return false;
65491
65796
  }
65492
65797
  function launcherInjectMcp(env, base, codexUpstream) {
65493
- if (base === "pi" || base === "omp" || base === "opencode" || base === "hermes" || base === "dsh") return false;
65798
+ if (base === "pi" || base === "omp" || base === "opencode" || base === "hermes" || base === "dsh" || base === "codebuddy") return false;
65494
65799
  if (env.BILI_LAUNCHER_PLUGIN === "0") return false;
65495
65800
  if (base === "codex" && env.BILI_LAUNCHER_PLUGIN === void 0 && codexUpstream !== void 0 && isPrivateUpstreamHost(codexUpstream)) {
65496
65801
  return false;
@@ -66313,6 +66618,10 @@ function resolveClientCommand(client, env) {
66313
66618
  );
66314
66619
  return { command: process.execPath, prefixArgs: [cli] };
66315
66620
  }
66621
+ if (client === "codebuddy") {
66622
+ const resolved2 = resolveOnPath("codebuddy", env) ?? resolveOnPath("cbc", env);
66623
+ return { command: resolved2 ?? "codebuddy", prefixArgs: [] };
66624
+ }
66316
66625
  const resolved = resolveOnPath(client, env);
66317
66626
  return { command: resolved ?? client, prefixArgs: [] };
66318
66627
  }
@@ -66478,6 +66787,19 @@ async function runLaunch(params, deps = {}) {
66478
66787
  Object.assign(env, inj.envPatch);
66479
66788
  if (inj.warning) console.error(`bili: ${inj.warning}`);
66480
66789
  }
66790
+ } else if (base === "codebuddy") {
66791
+ env = buildCodebuddyEnv(origin, ca, routes.httpRewrites, routes.httpsRewrites, process.env);
66792
+ const codebuddyBudget = await resolveCodebuddyBudgetEnv({
66793
+ model: config.codebuddy?.model,
66794
+ userAutoCompactWindow: config.codebuddy?.autoCompactWindow,
66795
+ shellAutoCompactWindow: process.env.CODEBUDDY_AUTO_COMPACT_WINDOW,
66796
+ routes: biliRoutes,
66797
+ upstreamUrl: config.codebuddy?.codebuddyBaseUrl ?? "https://tencent.sso.codebuddy.cn/v2"
66798
+ });
66799
+ Object.assign(env, codebuddyBudget);
66800
+ if (codebuddyBudget.CODEBUDDY_AUTO_COMPACT_WINDOW !== void 0) {
66801
+ console.error(`bili: codebuddy budget aligned \u2014 CODEBUDDY_AUTO_COMPACT_WINDOW=${codebuddyBudget.CODEBUDDY_AUTO_COMPACT_WINDOW}`);
66802
+ }
66481
66803
  } else {
66482
66804
  env = directUrl ? buildClaudePluginEnv(origin, true, process.env) : buildClaudeEnv(origin, ca, routes.httpRewrites, routes.httpsRewrites, process.env);
66483
66805
  if (directUrl) env.BILLION_CONTEXT_PROXY = origin;
@@ -66732,6 +67054,7 @@ Usage:
66732
67054
  bili opencode [opts --] [args] start a proxy + launch opencode against it (cert-MITM)
66733
67055
  bili hermes [opts --] [args] start a proxy + launch hermes-agent against it (/bili/ rewrite)
66734
67056
  bili dsh [opts --] [args] start a proxy + launch deepseek-harness against it (/bili/ rewrite)
67057
+ bili codebuddy [opts --] [args] start a proxy + launch codebuddy against it (/bili/ rewrite)
66735
67058
  bili test pi non-polluting pi smoke test through the proxy
66736
67059
  bili export [session] [--full] list sessions / export one as a Markdown handoff
66737
67060
  (--full includes original messages; --output FILE)
@@ -66746,7 +67069,7 @@ Usage:
66746
67069
  bili --version print version
66747
67070
  bili --help show this help
66748
67071
 
66749
- Launcher (bili pi / bili codex / bili claude / bili omp / bili opencode / bili hermes / bili dsh):
67072
+ Launcher (bili pi / bili codex / bili claude / bili omp / bili opencode / bili hermes / bili dsh / bili codebuddy):
66750
67073
  Brings up a proxy on an independent port (a fresh instance every launch), then runs the client pointed at it via HTTPS_PROXY + the proxy's
66751
67074
  MITM CA \u2014 no config-file edits. Discovered HTTPS upstream domains are
66752
67075
  auto-whitelisted for MITM so the proxy TLS-terminates exactly the hosts the
@@ -66762,6 +67085,7 @@ Launcher (bili pi / bili codex / bili claude / bili omp / bili opencode / bili h
66762
67085
  bili omp # launch omp through the proxy (pi-based; /bili/ rewrite)
66763
67086
  bili hermes # launch hermes-agent through the proxy (/bili/ rewrite of ~/.hermes/config.yaml)
66764
67087
  bili dsh --profile web "task" # launch deepseek-harness through the proxy (/bili/ rewrite of ~/.dsh/settings.yaml)
67088
+ bili codebuddy # launch codebuddy through the proxy (CODEBUDDY_BASE_URL /bili/ rewrite)
66765
67089
  bili test pi # quick end-to-end check of the pi path
66766
67090
  bili --mitm-domain api.foo.com pi # add a domain to the MITM whitelist (flags precede the client)
66767
67091
  bili -F http://127.0.0.1:7897 codex # route bili's upstream through a proxy (gost-style -F)