billion-context 0.1.84 → 0.1.85

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
@@ -44164,6 +44164,9 @@ function validateConfig(config) {
44164
44164
  "nudge.maxContextLimitPct must not exceed nudge.emergencyThresholdPct"
44165
44165
  );
44166
44166
  }
44167
+ if (config.nudge.minPressureBenefitTokens !== void 0 && (!Number.isFinite(config.nudge.minPressureBenefitTokens) || config.nudge.minPressureBenefitTokens < 0)) {
44168
+ errors.push("nudge.minPressureBenefitTokens must be finite and >= 0");
44169
+ }
44167
44170
  if (config.promotionThreshold < 1) {
44168
44171
  errors.push("promotionThreshold must be >= 1");
44169
44172
  }
@@ -46021,6 +46024,9 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
46021
46024
  )
46022
46025
  );
46023
46026
  }
46027
+ function resolveMinPressureBenefit(modelContextLimit, nudge) {
46028
+ return nudge.minPressureBenefitTokens ?? Math.max(5e3, Math.round(modelContextLimit * 0.01));
46029
+ }
46024
46030
  function pendingByTier(state, recommendation, countTokens, minCompressRange) {
46025
46031
  const out = {};
46026
46032
  const merged = recommendation?.recommendedRanges ?? [];
@@ -46047,6 +46053,7 @@ function decideNudge(input) {
46047
46053
  const limit = config.modelContextLimit;
46048
46054
  const usage = limit > 0 ? tokenCount / limit : 0;
46049
46055
  const nudgeGrowthTokens = resolveAdaptiveGrowth(limit, config.nudge);
46056
+ const minPressureBenefit = resolveMinPressureBenefit(limit, config.nudge);
46050
46057
  const overLimit = usage >= config.nudge.maxContextLimitPct;
46051
46058
  const emergencyOverride = usage >= config.nudge.emergencyThresholdPct;
46052
46059
  const pressure = overLimit || emergencyOverride;
@@ -46072,6 +46079,7 @@ function decideNudge(input) {
46072
46079
  );
46073
46080
  let injectedTier = null;
46074
46081
  let injectedReason = "";
46082
+ let bestPending = 0;
46075
46083
  const t1Eff = tiers[1]?.pending ?? 0;
46076
46084
  const t2Pen = tiers[2]?.pending ?? 0;
46077
46085
  const t3Pen = tiers[3]?.pending ?? 0;
@@ -46085,7 +46093,6 @@ function decideNudge(input) {
46085
46093
  candidates.push(2, 3);
46086
46094
  }
46087
46095
  let best = null;
46088
- let bestPending = 0;
46089
46096
  for (const t of candidates) {
46090
46097
  const p2 = tiers[t]?.pending ?? 0;
46091
46098
  if (p2 > bestPending) {
@@ -46093,7 +46100,7 @@ function decideNudge(input) {
46093
46100
  best = t;
46094
46101
  }
46095
46102
  }
46096
- if (best !== null && bestPending > 0) {
46103
+ if (best !== null && bestPending >= minPressureBenefit) {
46097
46104
  injectedTier = best;
46098
46105
  const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
46099
46106
  injectedReason = best === 1 ? `${label} T1: max effective pending ${bestPending}, usage ${Math.round(usage * 100)}%` : `${label} T${best} distill: max pending ${bestPending} (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}), usage ${Math.round(usage * 100)}%`;
@@ -46127,7 +46134,7 @@ function decideNudge(input) {
46127
46134
  reason = injectedReason;
46128
46135
  } else if (pressure) {
46129
46136
  const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
46130
- reason = `${label}: usage ${Math.round(usage * 100)}% but no tier has effective compressible content (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 nudge suppressed to avoid offering ranges below minCompressRange`;
46137
+ reason = bestPending === 0 ? `${label}: usage ${Math.round(usage * 100)}% but no tier has effective compressible content (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 nudge suppressed to avoid offering ranges below minCompressRange` : `${label}: usage ${Math.round(usage * 100)}% but max pending ${bestPending} < min benefit ${minPressureBenefit} tokens (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 suppressed: rewriting below the benefit floor reclaims almost nothing while usage stays high; truncate.threshold remains the safety valve`;
46131
46138
  } else {
46132
46139
  const tiersList = [1, 2, 3];
46133
46140
  const eligible = tiersList.filter((t) => config.tiers.enabled || t === 1);
@@ -46183,6 +46190,7 @@ function decideNudge(input) {
46183
46190
  hasPendingNudge: hasPendingNudge ? 1 : 0,
46184
46191
  overLimit: overLimit ? 1 : 0,
46185
46192
  emergencyOverride: emergencyOverride ? 1 : 0,
46193
+ minPressureBenefit,
46186
46194
  pendingT1: tiers[1].pending,
46187
46195
  pendingT2: tiers[2].pending,
46188
46196
  pendingT3: tiers[3].pending
@@ -51157,7 +51165,7 @@ function generateRootCA() {
51157
51165
  cert.setSubject(attrs);
51158
51166
  cert.setIssuer(attrs);
51159
51167
  cert.setExtensions([
51160
- { name: "basicConstraints", cA: true },
51168
+ { name: "basicConstraints", cA: true, critical: true },
51161
51169
  { name: "keyUsage", keyCertSign: true, cRLSign: true, digitalSignature: true },
51162
51170
  { name: "subjectKeyIdentifier" }
51163
51171
  ]);
@@ -51193,16 +51201,10 @@ function ensureRootCA() {
51193
51201
  rootKey = import_node_forge.default.pki.privateKeyFromPem(key);
51194
51202
  writeCombinedBundle();
51195
51203
  }
51196
- function getSecureContext(host) {
51204
+ function mintHostCert(host) {
51197
51205
  if (!rootCertPem || !rootKeyPem || !rootCert || !rootKey) {
51198
51206
  throw new Error("CA not initialized \u2014 call ensureRootCA() first");
51199
51207
  }
51200
- const cached = secureContextCache.get(host);
51201
- if (cached) {
51202
- secureContextCache.delete(host);
51203
- secureContextCache.set(host, cached);
51204
- return cached;
51205
- }
51206
51208
  const keys = import_node_forge.default.pki.rsa.generateKeyPair({ bits: 2048 });
51207
51209
  const cert = import_node_forge.default.pki.createCertificate();
51208
51210
  cert.publicKey = keys.publicKey;
@@ -51212,16 +51214,35 @@ function getSecureContext(host) {
51212
51214
  cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 2);
51213
51215
  cert.setSubject([{ name: "commonName", value: host }]);
51214
51216
  cert.setIssuer(rootCert.subject.attributes);
51217
+ const rootSki = rootCert.generateSubjectKeyIdentifier().getBytes();
51215
51218
  cert.setExtensions([
51216
51219
  { name: "basicConstraints", cA: false },
51217
51220
  { name: "keyUsage", digitalSignature: true, keyEncipherment: true },
51218
51221
  { name: "extKeyUsage", serverAuth: true },
51219
- { name: "subjectAltName", altNames: [{ type: 2, value: host }] }
51222
+ { name: "subjectAltName", altNames: [{ type: 2, value: host }] },
51223
+ { name: "subjectKeyIdentifier" },
51224
+ { name: "authorityKeyIdentifier", keyIdentifier: rootSki }
51220
51225
  ]);
51221
51226
  cert.sign(rootKey, import_node_forge.default.md.sha256.create());
51227
+ return {
51228
+ certPem: import_node_forge.default.pki.certificateToPem(cert),
51229
+ keyPem: import_node_forge.default.pki.privateKeyToPem(keys.privateKey)
51230
+ };
51231
+ }
51232
+ function getSecureContext(host) {
51233
+ if (!rootCertPem || !rootKeyPem || !rootCert || !rootKey) {
51234
+ throw new Error("CA not initialized \u2014 call ensureRootCA() first");
51235
+ }
51236
+ const cached = secureContextCache.get(host);
51237
+ if (cached) {
51238
+ secureContextCache.delete(host);
51239
+ secureContextCache.set(host, cached);
51240
+ return cached;
51241
+ }
51242
+ const { certPem, keyPem } = mintHostCert(host);
51222
51243
  const ctx = tls2.createSecureContext({
51223
- cert: import_node_forge.default.pki.certificateToPem(cert),
51224
- key: import_node_forge.default.pki.privateKeyToPem(keys.privateKey),
51244
+ cert: certPem,
51245
+ key: keyPem,
51225
51246
  ca: rootCertPem
51226
51247
  });
51227
51248
  secureContextCache.set(host, ctx);
@@ -52001,10 +52022,11 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
52001
52022
  if (realCalls > 0) ctx.log(`[acp-loop] round ${round}: ${realCalls} real tool call(s) forwarded to client`);
52002
52023
  }
52003
52024
  if (proxyResults.length > 0) {
52025
+ const requiresThinkingSignature = ctx.protocol === "anthropic";
52004
52026
  if (reasoningSegments.length > 0) {
52005
52027
  for (let i = 0; i < reasoningSegments.length; i++) {
52006
52028
  const seg = reasoningSegments[i];
52007
- if (seg.text.length === 0 || seg.signature.length === 0) continue;
52029
+ if (seg.text.length === 0 || requiresThinkingSignature && seg.signature.length === 0) continue;
52008
52030
  const reasoningMsg = {
52009
52031
  id: i === 0 ? `acp_loop_r${round}_reasoning` : `acp_loop_r${round}_reasoning_${i + 1}`,
52010
52032
  role: "assistant",
@@ -52127,7 +52149,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
52127
52149
  try {
52128
52150
  respResult = await fetchUpstream(newBody);
52129
52151
  } catch (e) {
52130
- if (!(e instanceof UpstreamHttpError) || e.status < 400 || e.status >= 500 || degradedRetried || !coreMessages.some(isLoopThinking)) {
52152
+ if (!(e instanceof UpstreamHttpError) || e.status < 400 || e.status >= 500 || degradedRetried || ctx.protocol !== "anthropic" || !coreMessages.some(isLoopThinking)) {
52131
52153
  throw e;
52132
52154
  }
52133
52155
  degradedRetried = true;
@@ -52349,6 +52371,7 @@ data: ${JSON.stringify(obj)}
52349
52371
  }
52350
52372
  async function* iterSseEvents(stream2) {
52351
52373
  const reader = stream2.getReader();
52374
+ const decoder = new TextDecoder();
52352
52375
  let buf = "";
52353
52376
  try {
52354
52377
  while (true) {
@@ -52360,7 +52383,7 @@ async function* iterSseEvents(stream2) {
52360
52383
  break;
52361
52384
  }
52362
52385
  if (done) break;
52363
- buf += new TextDecoder().decode(value, { stream: true });
52386
+ buf += decoder.decode(value, { stream: true });
52364
52387
  buf = buf.replace(/\r\n|\r/g, "\n");
52365
52388
  let idx;
52366
52389
  while ((idx = buf.indexOf("\n\n")) >= 0) {
@@ -52838,6 +52861,7 @@ function filterRealToolFragments(parsed, realIndexes) {
52838
52861
  }
52839
52862
  async function* iterSseChunks(stream2) {
52840
52863
  const reader = stream2.getReader();
52864
+ const decoder = new TextDecoder();
52841
52865
  let buf = "";
52842
52866
  try {
52843
52867
  while (true) {
@@ -52849,7 +52873,7 @@ async function* iterSseChunks(stream2) {
52849
52873
  break;
52850
52874
  }
52851
52875
  if (done) break;
52852
- buf += new TextDecoder().decode(value, { stream: true });
52876
+ buf += decoder.decode(value, { stream: true });
52853
52877
  buf = buf.replace(/\r\n|\r/g, "\n");
52854
52878
  let idx;
52855
52879
  while ((idx = buf.indexOf("\n\n")) >= 0) {
@@ -53175,6 +53199,7 @@ function createOpenaiAdapter(requestBody, clientSystem) {
53175
53199
  // src/loop/adapter-anthropic.ts
53176
53200
  async function* iterSseEvents2(stream2) {
53177
53201
  const reader = stream2.getReader();
53202
+ const decoder = new TextDecoder();
53178
53203
  let buf = "";
53179
53204
  try {
53180
53205
  while (true) {
@@ -53186,7 +53211,7 @@ async function* iterSseEvents2(stream2) {
53186
53211
  break;
53187
53212
  }
53188
53213
  if (done) break;
53189
- buf += new TextDecoder().decode(value, { stream: true });
53214
+ buf += decoder.decode(value, { stream: true });
53190
53215
  buf = buf.replace(/\r\n|\r/g, "\n");
53191
53216
  let idx;
53192
53217
  while ((idx = buf.indexOf("\n\n")) >= 0) {
@@ -53833,6 +53858,76 @@ ${FAKE_COMPLETION_HINT}`;
53833
53858
  return JSON.stringify(obj);
53834
53859
  }
53835
53860
 
53861
+ // src/acp-panel.ts
53862
+ var PANEL_BOX_TOP = "\u256D";
53863
+ var PANEL_BOX_TITLE = "ACP Context Analysis";
53864
+ var PANEL_BOX_FOOTER = "Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.";
53865
+ var PANEL_FALLBACK_HEADER = "\u{1F4CA} ACP status";
53866
+ function isAcpPanelText(text) {
53867
+ const t = text.trim();
53868
+ if (t.length === 0) return false;
53869
+ return isBoxPanel(t) || isFallbackPanel(t);
53870
+ }
53871
+ function isBoxPanel(t) {
53872
+ return t.startsWith(PANEL_BOX_TOP) && t.includes(PANEL_BOX_TITLE) && t.endsWith(PANEL_BOX_FOOTER);
53873
+ }
53874
+ function isFallbackPanel(t) {
53875
+ const lines = t.split("\n");
53876
+ if (lines[0] !== PANEL_FALLBACK_HEADER) return false;
53877
+ for (let i = 1; i < lines.length; i++) {
53878
+ const line = lines[i];
53879
+ if (line.length > 0 && !line.startsWith(" ")) return false;
53880
+ }
53881
+ return true;
53882
+ }
53883
+ function messageText(content) {
53884
+ if (typeof content === "string") return content;
53885
+ if (Array.isArray(content)) {
53886
+ let joined = "";
53887
+ for (const part of content) {
53888
+ const p2 = part;
53889
+ if (p2 === null || typeof p2 !== "object") return void 0;
53890
+ const pt2 = p2.type;
53891
+ if (pt2 !== void 0 && pt2 !== "text" && pt2 !== "input_text") return void 0;
53892
+ if (typeof p2.text === "string") joined += p2.text;
53893
+ }
53894
+ return joined;
53895
+ }
53896
+ return void 0;
53897
+ }
53898
+ function stripAcpPanelMessages(messages) {
53899
+ if (!Array.isArray(messages)) return 0;
53900
+ let stripped = 0;
53901
+ for (let i = messages.length - 1; i >= 0; i--) {
53902
+ const rec = messages[i];
53903
+ if (rec === null || typeof rec !== "object") continue;
53904
+ if (rec.role !== "user") continue;
53905
+ const text = messageText(rec.content);
53906
+ if (text !== void 0 && isAcpPanelText(text)) {
53907
+ messages.splice(i, 1);
53908
+ stripped++;
53909
+ }
53910
+ }
53911
+ return stripped;
53912
+ }
53913
+ function stripAcpPanelResponsesInput(input) {
53914
+ if (!Array.isArray(input)) return 0;
53915
+ let stripped = 0;
53916
+ for (let i = input.length - 1; i >= 0; i--) {
53917
+ const rec = input[i];
53918
+ if (rec === null || typeof rec !== "object") continue;
53919
+ const type = rec.type;
53920
+ if (type !== "message" && type !== void 0) continue;
53921
+ if (rec.role !== "user") continue;
53922
+ const text = messageText(rec.content);
53923
+ if (text !== void 0 && isAcpPanelText(text)) {
53924
+ input.splice(i, 1);
53925
+ stripped++;
53926
+ }
53927
+ }
53928
+ return stripped;
53929
+ }
53930
+
53836
53931
  // src/stream-openai.ts
53837
53932
  function rewriteOpenaiJsonResponse(body, ctx) {
53838
53933
  if (!body || typeof body !== "object") return body;
@@ -55559,6 +55654,7 @@ function readOmpConfig(ompHome) {
55559
55654
  }
55560
55655
  function parseHermesYaml(text) {
55561
55656
  const result = { providers: {} };
55657
+ const candidates = /* @__PURE__ */ new Map();
55562
55658
  let mode = "none";
55563
55659
  let sectionIndent = -1;
55564
55660
  let entryIndent = -1;
@@ -55603,8 +55699,8 @@ function parseHermesYaml(text) {
55603
55699
  current = m2 ? m2[1] : null;
55604
55700
  if (current && !result.providers[current]) result.providers[current] = {};
55605
55701
  } else if (indent > entryIndent && current) {
55606
- const apiMatch = /^api:\s*(\S+)/.exec(trimmed);
55607
- if (apiMatch) result.providers[current].api = apiMatch[1];
55702
+ const apiMatch = /^(base_url|url|api):\s*(\S+)/.exec(trimmed);
55703
+ if (apiMatch) candidates.set(`${current}\0${apiMatch[1]}`, apiMatch[2]);
55608
55704
  }
55609
55705
  } else {
55610
55706
  const dashMatch = /^-\s+(.*)$/.exec(trimmed);
@@ -55621,6 +55717,10 @@ function parseHermesYaml(text) {
55621
55717
  }
55622
55718
  }
55623
55719
  }
55720
+ for (const name of Object.keys(result.providers)) {
55721
+ const pick = candidates.get(`${name}\0base_url`) ?? candidates.get(`${name}\0url`) ?? candidates.get(`${name}\0api`);
55722
+ if (pick !== void 0) result.providers[name].api = pick;
55723
+ }
55624
55724
  return result;
55625
55725
  }
55626
55726
  function readHermesConfig(hermesHome) {
@@ -56921,6 +57021,16 @@ function resolveUpstream(_opts, reqUrl, req) {
56921
57021
  }
56922
57022
  }
56923
57023
  }
57024
+ if (reqUrl.startsWith("http://") || reqUrl.startsWith("https://")) {
57025
+ try {
57026
+ const u2 = new URL(reqUrl);
57027
+ const ownHost = (req?.headers.host ?? "").toLowerCase();
57028
+ if (u2.host.toLowerCase() !== ownHost) {
57029
+ return { upstream: `${u2.protocol}//${u2.host}`, rewrittenUrl: reqUrl, tunnel: true };
57030
+ }
57031
+ } catch {
57032
+ }
57033
+ }
56924
57034
  return void 0;
56925
57035
  }
56926
57036
  async function startServer(opts) {
@@ -57658,6 +57768,10 @@ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, sessio
57658
57768
  let rebuiltMessages = parsed.messages;
57659
57769
  let systemOut = parsed.system;
57660
57770
  let toolsOut = parsed.tools;
57771
+ const strippedPanels = stripAcpPanelMessages(parsed.messages);
57772
+ if (strippedPanels > 0) {
57773
+ log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
57774
+ }
57661
57775
  try {
57662
57776
  const { msgs, cacheControls } = anthropicToCore(parsed);
57663
57777
  originalMessages = msgs;
@@ -57747,6 +57861,10 @@ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session,
57747
57861
  const isTitleGen = maxTokens <= 200;
57748
57862
  const shouldInject = opts.compress.injectTool && !isTitleGen;
57749
57863
  const injectTools = shouldInject && !pluginMode;
57864
+ const strippedPanels = stripAcpPanelMessages(parsed.messages);
57865
+ if (strippedPanels > 0) {
57866
+ log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
57867
+ }
57750
57868
  try {
57751
57869
  const { msgs, systemText } = openaiToCore(parsed);
57752
57870
  openaiSystemText = systemText;
@@ -57832,6 +57950,10 @@ function prepareResponses(parsed, req, opts, core, config, prompts, log2, sessio
57832
57950
  if (droppedEmpty > 0) {
57833
57951
  log2("info", `[${sessionId}] dropped ${droppedEmpty} whitespace-only message item(s) before projection (flattened-turn artifact)`);
57834
57952
  }
57953
+ const strippedPanels = stripAcpPanelResponsesInput(parsed.input);
57954
+ if (strippedPanels > 0) {
57955
+ log2("info", `[${sessionId}] stripped ${strippedPanels} ACP panel message(s) before projection (UI-only, issue #359)`);
57956
+ }
57835
57957
  const shouldInject = opts.compress.injectTool;
57836
57958
  const injectTools = shouldInject && !pluginMode;
57837
57959
  const isCompactionTrigger = hasCompactionTrigger(parsed.input);
@@ -62862,8 +62984,16 @@ function discoverRoutes(client, config) {
62862
62984
  if (url.protocol !== "http:" && url.protocol !== "https:") continue;
62863
62985
  if (hermesSeen.has(name)) continue;
62864
62986
  hermesSeen.add(name);
62865
- rewriteKeys.add(name);
62866
- httpRewrites.push({ key: name, realUpstream: real });
62987
+ if (url.protocol === "https:") {
62988
+ const host = url.hostname.toLowerCase();
62989
+ if (host && !httpsSeen.has(host)) {
62990
+ httpsSeen.add(host);
62991
+ httpsDomains.push(host);
62992
+ }
62993
+ } else if (!rewriteKeys.has(name)) {
62994
+ rewriteKeys.add(name);
62995
+ httpRewrites.push({ key: name, realUpstream: real });
62996
+ }
62867
62997
  } catch {
62868
62998
  }
62869
62999
  }
@@ -62894,8 +63024,23 @@ function discoverRoutes(client, config) {
62894
63024
  function discoverDomains(client, config) {
62895
63025
  return discoverRoutes(client, config).httpsDomains;
62896
63026
  }
62897
- function buildPiEnv(origin, caPath, baseEnv) {
62898
- return { ...baseEnv, HTTPS_PROXY: origin, NODE_EXTRA_CA_CERTS: caPath, BILLION_CONTEXT_PROXY: origin };
63027
+ function buildPiEnv(origin, caPath, baseEnv, httpRewrites = [], httpsRewrites = []) {
63028
+ const manifest = {};
63029
+ for (const r of httpRewrites) {
63030
+ if (r.key.length === 0 || r.realUpstream.length === 0) continue;
63031
+ manifest[r.key] = wrapUpstream(origin, r.realUpstream);
63032
+ }
63033
+ for (const r of httpsRewrites) {
63034
+ if (r.key.length === 0 || r.realUpstream.length === 0) continue;
63035
+ manifest[r.key] = r.realUpstream;
63036
+ }
63037
+ return {
63038
+ ...baseEnv,
63039
+ HTTPS_PROXY: origin,
63040
+ NODE_EXTRA_CA_CERTS: caPath,
63041
+ BILLION_CONTEXT_PROXY: origin,
63042
+ ...Object.keys(manifest).length > 0 ? { BILI_PROVIDER_REWRITES: JSON.stringify(manifest) } : {}
63043
+ };
62899
63044
  }
62900
63045
  function buildCodexEnv(origin, caPath, baseEnv) {
62901
63046
  return { ...baseEnv, HTTPS_PROXY: origin, SSL_CERT_FILE: caPath, BILLION_CONTEXT_PROXY: origin };
@@ -63426,363 +63571,6 @@ function writeOverlayFileAtomic(overlay, fileName, contents) {
63426
63571
  }
63427
63572
  }
63428
63573
  }
63429
- function writePiCompactionDisabledSettings(piHome, overlay) {
63430
- let settings = {};
63431
- try {
63432
- const parsed = JSON.parse(fs12.readFileSync(path15.join(piHome, "settings.json"), "utf8"));
63433
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
63434
- settings = parsed;
63435
- }
63436
- } catch {
63437
- }
63438
- const compactionRaw = settings.compaction;
63439
- const compaction = compactionRaw && typeof compactionRaw === "object" && !Array.isArray(compactionRaw) ? { ...compactionRaw } : {};
63440
- compaction.enabled = false;
63441
- settings.compaction = compaction;
63442
- writeOverlayFileAtomic(overlay, "settings.json", JSON.stringify(settings, null, 2));
63443
- }
63444
- function preparePiHttpRewrite(piHome, origin, httpRewrites, httpsRewrites) {
63445
- if (!fs12.existsSync(piHome)) return void 0;
63446
- const overlay = `${piHome}-bili`;
63447
- const generated = ["settings.json"];
63448
- let modelsRoot;
63449
- try {
63450
- const parsed = JSON.parse(fs12.readFileSync(path15.join(piHome, "models.json"), "utf8"));
63451
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
63452
- modelsRoot = parsed;
63453
- generated.push("models.json");
63454
- }
63455
- } catch {
63456
- }
63457
- if (!refreshOverlayHome(piHome, overlay, generated)) return void 0;
63458
- if (modelsRoot) {
63459
- const providersVal = modelsRoot.providers;
63460
- if (providersVal && typeof providersVal === "object" && !Array.isArray(providersVal)) {
63461
- const providers = providersVal;
63462
- for (const r of httpRewrites) {
63463
- const prov = providers[r.key];
63464
- if (prov && typeof prov === "object" && !Array.isArray(prov)) {
63465
- const p2 = prov;
63466
- const existing = typeof p2.baseUrl === "string" ? p2.baseUrl : r.realUpstream;
63467
- p2.baseUrl = wrapUpstream(origin, unwrapUpstream2(existing));
63468
- }
63469
- }
63470
- for (const r of httpsRewrites) {
63471
- const prov = providers[r.key];
63472
- if (prov && typeof prov === "object" && !Array.isArray(prov)) {
63473
- const p2 = prov;
63474
- p2.baseUrl = r.realUpstream;
63475
- }
63476
- }
63477
- }
63478
- writeOverlayFileAtomic(overlay, "models.json", JSON.stringify(modelsRoot));
63479
- }
63480
- writePiCompactionDisabledSettings(piHome, overlay);
63481
- return overlay;
63482
- }
63483
- function atomicWriteTextFile(filePath, contents) {
63484
- const draft = `${filePath}.${process.pid}.bili-tmp`;
63485
- try {
63486
- fs12.writeFileSync(draft, contents);
63487
- fs12.renameSync(draft, filePath);
63488
- } catch {
63489
- try {
63490
- fs12.rmSync(draft, { force: true });
63491
- } catch {
63492
- }
63493
- throw new Error(`bili: could not write ${filePath}`);
63494
- }
63495
- }
63496
- function liveProxyPorts() {
63497
- const ports = /* @__PURE__ */ new Set();
63498
- const inst = readProxyInstanceFile();
63499
- if (isProxyInstanceFile(inst)) {
63500
- if (isPidAlive(inst.pid)) ports.add(inst.port);
63501
- } else if (inst) {
63502
- try {
63503
- ports.add(new URL(inst.origin).port === "" ? 80 : Number(new URL(inst.origin).port));
63504
- } catch {
63505
- }
63506
- }
63507
- return ports;
63508
- }
63509
- function unpackDeadProxyUrlsInFile(filePath, livePorts) {
63510
- let txt;
63511
- try {
63512
- txt = fs12.readFileSync(filePath, "utf8");
63513
- } catch {
63514
- return 0;
63515
- }
63516
- const re2 = /https?:\/\/(?:127\.0\.0\.1|localhost|\[::1\]):(\d+)\/bili\/(https?:\/\/\S+)/g;
63517
- let changed = 0;
63518
- const out = txt.replace(re2, (full, portStr, raw) => {
63519
- if (livePorts.has(Number(portStr))) return full;
63520
- changed += 1;
63521
- return raw;
63522
- });
63523
- if (changed === 0) return 0;
63524
- try {
63525
- atomicWriteTextFile(filePath, out);
63526
- } catch {
63527
- return 0;
63528
- }
63529
- return changed;
63530
- }
63531
- function mergeOmpCompactionDisabledYaml(text) {
63532
- const lines = text.split(/\r?\n/);
63533
- let compactionLine = -1;
63534
- for (let i = 0; i < lines.length; i++) {
63535
- const rawLine = lines[i];
63536
- const trimmed = rawLine.trim();
63537
- if (!trimmed || trimmed.startsWith("#")) continue;
63538
- const indent = rawLine.length - rawLine.trimStart().length;
63539
- if (indent === 0 && /^compaction:\s*(#.*)?$/.test(trimmed)) {
63540
- compactionLine = i;
63541
- break;
63542
- }
63543
- }
63544
- if (compactionLine === -1) {
63545
- const base = text === "" || text.endsWith("\n") ? text : `${text}
63546
- `;
63547
- return `${base}compaction:
63548
- enabled: false
63549
- `;
63550
- }
63551
- for (let i = compactionLine + 1; i < lines.length; i++) {
63552
- const rawLine = lines[i];
63553
- const trimmed = rawLine.trim();
63554
- if (!trimmed || trimmed.startsWith("#")) continue;
63555
- const indent = rawLine.length - rawLine.trimStart().length;
63556
- if (indent === 0) break;
63557
- if (/^enabled:/.test(trimmed)) {
63558
- const leading = rawLine.slice(0, rawLine.length - rawLine.trimStart().length);
63559
- const commentMatch = /\s+#.*$/.exec(rawLine);
63560
- const comment = commentMatch ? commentMatch[0] : "";
63561
- lines[i] = `${leading}enabled: false${comment}`;
63562
- return lines.join("\n");
63563
- }
63564
- }
63565
- let childIndent = 2;
63566
- for (let i = compactionLine + 1; i < lines.length; i++) {
63567
- const rawLine = lines[i];
63568
- const trimmed = rawLine.trim();
63569
- if (!trimmed || trimmed.startsWith("#")) continue;
63570
- const indent = rawLine.length - rawLine.trimStart().length;
63571
- if (indent === 0) break;
63572
- childIndent = indent;
63573
- break;
63574
- }
63575
- lines.splice(compactionLine + 1, 0, `${" ".repeat(childIndent)}enabled: false`);
63576
- return lines.join("\n");
63577
- }
63578
- function writeOmpCompactionDisabledConfig(ompHome, overlay) {
63579
- let base;
63580
- let baseIsJson = false;
63581
- for (const name of ["config.yml", "config.yaml"]) {
63582
- try {
63583
- base = fs12.readFileSync(path15.join(ompHome, name), "utf8");
63584
- break;
63585
- } catch {
63586
- }
63587
- }
63588
- if (base === void 0) {
63589
- try {
63590
- base = fs12.readFileSync(path15.join(ompHome, "settings.json"), "utf8");
63591
- baseIsJson = true;
63592
- } catch {
63593
- }
63594
- }
63595
- let merged;
63596
- if (base === void 0) {
63597
- merged = "compaction:\n enabled: false\n";
63598
- } else if (baseIsJson) {
63599
- let settings = {};
63600
- try {
63601
- const parsed = JSON.parse(base);
63602
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
63603
- settings = parsed;
63604
- }
63605
- } catch {
63606
- }
63607
- const compactionRaw = settings.compaction;
63608
- const compaction = compactionRaw && typeof compactionRaw === "object" && !Array.isArray(compactionRaw) ? { ...compactionRaw } : {};
63609
- compaction.enabled = false;
63610
- settings.compaction = compaction;
63611
- merged = JSON.stringify(settings, null, 2);
63612
- } else {
63613
- merged = mergeOmpCompactionDisabledYaml(base);
63614
- }
63615
- writeOverlayFileAtomic(overlay, "config.yml", merged);
63616
- }
63617
- function prepareOmpHttpRewrite(ompHome, origin, httpRewrites, httpsRewrites) {
63618
- if (!fs12.existsSync(ompHome)) return void 0;
63619
- const overlay = `${ompHome}-bili`;
63620
- const generated = ["config.yml"];
63621
- const modelsPath = path15.join(ompHome, "models.yml");
63622
- const unpacked = unpackDeadProxyUrlsInFile(modelsPath, liveProxyPorts());
63623
- if (unpacked > 0) {
63624
- console.error(`bili: unpacked ${unpacked} dead proxy URL(s) from the real ${modelsPath}`);
63625
- }
63626
- let modelsText;
63627
- try {
63628
- modelsText = fs12.readFileSync(modelsPath, "utf8");
63629
- generated.push("models.yml");
63630
- } catch {
63631
- }
63632
- if (!refreshOverlayHome(ompHome, overlay, generated)) return void 0;
63633
- if (modelsText !== void 0) {
63634
- const httpMap = new Map(httpRewrites.map((r) => [r.key, wrapUpstream(origin, r.realUpstream)]));
63635
- const httpsMap = new Map(httpsRewrites.map((r) => [r.key, r.realUpstream]));
63636
- const lines = modelsText.split(/\r?\n/);
63637
- let providersIndent = -1;
63638
- let providerIndent = -1;
63639
- let currentProvider = null;
63640
- for (let i = 0; i < lines.length; i++) {
63641
- const rawLine = lines[i];
63642
- const trimmed = rawLine.trim();
63643
- if (!trimmed || trimmed.startsWith("#")) continue;
63644
- const indent = rawLine.length - rawLine.trimStart().length;
63645
- if (providersIndent === -1) {
63646
- if (/^providers:\s*(#.*)?$/.test(trimmed)) providersIndent = indent;
63647
- continue;
63648
- }
63649
- if (indent <= providersIndent) break;
63650
- if (providerIndent === -1) providerIndent = indent;
63651
- if (indent === providerIndent) {
63652
- const m2 = /^([A-Za-z0-9_.-]+):/.exec(trimmed);
63653
- currentProvider = m2 ? m2[1] : null;
63654
- } else if (indent > providerIndent && currentProvider) {
63655
- const baseMatch = /^(baseUrl:\s*)(\S+)/.exec(trimmed);
63656
- if (baseMatch) {
63657
- const target = httpMap.get(currentProvider) ?? httpsMap.get(currentProvider);
63658
- if (target) {
63659
- const leading = rawLine.slice(0, rawLine.length - rawLine.trimStart().length);
63660
- const commentMatch = /\s+#.*$/.exec(rawLine);
63661
- const comment = commentMatch ? commentMatch[0] : "";
63662
- lines[i] = `${leading}baseUrl: ${target}${comment}`;
63663
- }
63664
- }
63665
- }
63666
- }
63667
- writeOverlayFileAtomic(overlay, "models.yml", lines.join("\n"));
63668
- }
63669
- writeOmpCompactionDisabledConfig(ompHome, overlay);
63670
- return overlay;
63671
- }
63672
- function prepareHermesHome(hermesHome, origin, rewrites) {
63673
- if (rewrites.length === 0) return void 0;
63674
- const cfgPath = path15.join(hermesHome, "config.yaml");
63675
- let txt;
63676
- try {
63677
- txt = fs12.readFileSync(cfgPath, "utf8");
63678
- } catch {
63679
- return void 0;
63680
- }
63681
- const wrapSet = new Set(rewrites.map((r) => r.realUpstream));
63682
- const eol = txt.includes("\r\n") ? "\r\n" : "\n";
63683
- const lines = txt.split(/\r?\n/);
63684
- let changed = false;
63685
- for (let i = 0; i < lines.length; i++) {
63686
- const rawLine = lines[i];
63687
- const m2 = /^(\s*(?:api|base_url|url):\s*)(\S+)(\s+#.*)?$/.exec(rawLine);
63688
- if (!m2) continue;
63689
- const rawUrl = m2[2].replace(/^["']|["']$/g, "");
63690
- if (!/^https?:\/\//i.test(rawUrl)) continue;
63691
- const real = unwrapUpstream2(rawUrl);
63692
- if (!wrapSet.has(real)) continue;
63693
- lines[i] = `${m2[1]}${wrapUpstream(origin, real)}${m2[3] ?? ""}`;
63694
- changed = true;
63695
- }
63696
- if (!changed) return void 0;
63697
- const overlay = `${hermesHome}-bili`;
63698
- if (!refreshOverlayHome(hermesHome, overlay, "config.yaml")) return void 0;
63699
- writeOverlayFileAtomic(overlay, "config.yaml", lines.join(eol));
63700
- return overlay;
63701
- }
63702
- function writeHermesIdentityPlugin(hermesHome, overlay, rewrites) {
63703
- try {
63704
- if (fs12.existsSync(path15.join(hermesHome, "plugins"))) return false;
63705
- } catch {
63706
- return false;
63707
- }
63708
- const dir = path15.join("plugins", "model-providers", "bili-session-identity");
63709
- try {
63710
- fs12.mkdirSync(path15.join(overlay, dir), { recursive: true });
63711
- } catch {
63712
- return false;
63713
- }
63714
- const keys = rewrites.map((r) => r.key);
63715
- const pluginDir = path15.join(overlay, dir);
63716
- try {
63717
- fs12.writeFileSync(path15.join(pluginDir, "__init__.py"), hermesIdentityPluginSource(keys));
63718
- fs12.writeFileSync(path15.join(pluginDir, "plugin.yaml"), [
63719
- "name: bili-session-identity",
63720
- "kind: model-provider",
63721
- "version: 1.0.0",
63722
- "description: billion-context session identity (installed by `bili hermes` \u2014 safe to delete)",
63723
- "author: billion-context",
63724
- ""
63725
- ].join("\n"));
63726
- } catch {
63727
- return false;
63728
- }
63729
- try {
63730
- return fs12.existsSync(path15.join(overlay, dir, "__init__.py"));
63731
- } catch {
63732
- return false;
63733
- }
63734
- }
63735
- function hermesIdentityPluginSource(providerKeys) {
63736
- return [
63737
- '"""Installed by `bili hermes` (billion-context) \u2014 safe to delete.',
63738
- "",
63739
- "Re-registers hermes's `custom` provider profile (plus a `custom:<name>`",
63740
- "profile for every provider the proxy fronts) with one addition: the",
63741
- "REAL hermes session id is sent as `prompt_cache_key` on every request,",
63742
- "which the billion-context proxy uses as the stable conversation",
63743
- "identity for its compression sessions. Removing this file restores",
63744
- 'stock behavior (no prompt_cache_key)."""',
63745
- "from providers import get_provider_profile, register_provider",
63746
- "from providers.base import ProviderProfile",
63747
- "",
63748
- `_PROVIDER_KEYS = ${JSON.stringify(providerKeys)}`,
63749
- "",
63750
- '_custom = get_provider_profile("custom")',
63751
- "_BASE = type(_custom) if _custom is not None else ProviderProfile",
63752
- "",
63753
- "",
63754
- "class _BiliSessionIdentity(_BASE):",
63755
- " def build_api_kwargs_extras(self, *, session_id=None, **ctx):",
63756
- " extra_body, top_level = _BASE.build_api_kwargs_extras(",
63757
- " self, session_id=session_id, **ctx",
63758
- " )",
63759
- ' sid = str(session_id or "").strip()',
63760
- " if sid:",
63761
- ' top_level.setdefault("prompt_cache_key", sid[:64])',
63762
- " return extra_body, top_level",
63763
- "",
63764
- "",
63765
- "def _register(name, template, with_aliases):",
63766
- " fields = {}",
63767
- " if template is not None:",
63768
- " # Aliases are copied ONLY for the canonical custom",
63769
- " # re-registration: copying them onto custom:<name> entries",
63770
- " # would steal the aliases (register_provider re-points each",
63771
- " # alias at the registering name).",
63772
- ' for attr in (("aliases",) if with_aliases else ()) + ("env_vars", "base_url", "default_max_tokens"):',
63773
- " value = getattr(template, attr, None)",
63774
- " if value:",
63775
- " fields[attr] = value",
63776
- " register_provider(_BiliSessionIdentity(name=name, **fields))",
63777
- "",
63778
- "",
63779
- "if _custom is not None:",
63780
- ' _register("custom", _custom, True)',
63781
- "for _key in _PROVIDER_KEYS:",
63782
- ' _register("custom:" + _key, _custom, False)',
63783
- ""
63784
- ].join("\n");
63785
- }
63786
63574
  function prepareDshHome(dshHome, origin, rewrites) {
63787
63575
  const cfgPath = path15.join(dshHome, "settings.yaml");
63788
63576
  let txt;
@@ -63944,7 +63732,16 @@ function pickEphemeralPort(host = LAUNCHER_DEFAULT_HOST) {
63944
63732
  });
63945
63733
  });
63946
63734
  }
63947
- var INHERITED_PROXY_VARS = ["http_proxy", "https_proxy", "all_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"];
63735
+ var INHERITED_PROXY_VARS = [
63736
+ "http_proxy",
63737
+ "https_proxy",
63738
+ "all_proxy",
63739
+ "HTTP_PROXY",
63740
+ "HTTPS_PROXY",
63741
+ "ALL_PROXY",
63742
+ "no_proxy",
63743
+ "NO_PROXY"
63744
+ ];
63948
63745
  function stripInheritedProxy(env) {
63949
63746
  const cleaned = { ...env };
63950
63747
  for (const key of INHERITED_PROXY_VARS) delete cleaned[key];
@@ -64098,9 +63895,30 @@ async function runLaunch(params, deps = {}) {
64098
63895
  const port = parsePort(params.overrides.ACP_PORT ?? process.env.ACP_PORT);
64099
63896
  const passthrough = params.overrides.ACP_PASSTHROUGH === "1";
64100
63897
  const debug = params.overrides.ACP_DEBUG === "1";
64101
- const config = loadClientConfig(process.env, process.cwd());
64102
63898
  const base = baseClientName(params.client);
63899
+ const discoveryEnv = base === "pi" || base === "omp" ? { ...process.env, PI_CODING_AGENT_DIR: void 0 } : process.env;
63900
+ const config = loadClientConfig(discoveryEnv, process.cwd());
64103
63901
  const routes = discoverRoutes(base, config);
63902
+ const piRealHome = resolvePiHome({ ...process.env, PI_CODING_AGENT_DIR: void 0 });
63903
+ const ompRealHome = resolveOmpHome({ ...process.env, PI_CODING_AGENT_DIR: void 0 });
63904
+ if (base === "pi" && (routes.httpRewrites.length > 0 || routes.httpsRewrites.length > 0)) {
63905
+ const piExt = selfDistFile("agent/pi.js");
63906
+ const extAvailable = piExt !== void 0 && fs12.existsSync(piExt) || piPluginInstalled(piRealHome);
63907
+ if (!extAvailable) {
63908
+ throw new Error(
63909
+ "bili: pi needs provider URL rewrites but the bili extension cannot load (dist/agent/pi.js missing and the plugin is not installed in ~/.pi/agent/settings.json) \u2014 reinstall billion-context or run `bili plugin install pi`"
63910
+ );
63911
+ }
63912
+ }
63913
+ if (base === "omp" && routes.httpRewrites.length > 0) {
63914
+ const ompExt = selfDistFile("agent/omp.js");
63915
+ const extAvailable = ompExt !== void 0 && fs12.existsSync(ompExt) || ompPluginLoadedFrom(ompRealHome);
63916
+ if (!extAvailable) {
63917
+ throw new Error(
63918
+ "bili: omp needs provider URL rewrites but the bili extension cannot load (dist/agent/omp.js missing and the plugin is not installed in ~/.omp/agent/config.yml) \u2014 reinstall billion-context or run `bili plugin install omp`"
63919
+ );
63920
+ }
63921
+ }
64104
63922
  const biliRoutes = loadRoutes(process.env);
64105
63923
  const domains = dedupeInOrder([...routes.httpsDomains, ...params.mitmDomains ?? []]);
64106
63924
  const handle2 = await ensureProxyRunning({ host, port, passthrough, debug, mitmDomains: domains, modelWindows: collectModelWindows(config, base) }, deps);
@@ -64113,10 +63931,7 @@ async function runLaunch(params, deps = {}) {
64113
63931
  const ca = resolveCaCertPath(process.env);
64114
63932
  let env;
64115
63933
  let clientArgs = params.clientArgs;
64116
- let piOverlayHome;
64117
- let ompOverlayHome;
64118
63934
  let opencodeTmpFile;
64119
- let hermesOverlayHome;
64120
63935
  let dshOverlayHome;
64121
63936
  const tmpFiles = [];
64122
63937
  const directUrl = launcherDirectUrl(process.env);
@@ -64144,19 +63959,17 @@ async function runLaunch(params, deps = {}) {
64144
63959
  }
64145
63960
  const origin = handle2.origin;
64146
63961
  if (base === "pi") {
64147
- env = buildPiEnv(origin, ca, process.env);
64148
- piOverlayHome = preparePiHttpRewrite(resolvePiHome(process.env), origin, routes.httpRewrites, routes.httpsRewrites);
64149
- if (piOverlayHome) env.PI_CODING_AGENT_DIR = piOverlayHome;
63962
+ env = buildPiEnv(origin, ca, process.env, routes.httpRewrites, routes.httpsRewrites);
63963
+ delete env.PI_CODING_AGENT_DIR;
64150
63964
  const piExt = selfDistFile("agent/pi.js");
64151
- if (piExt && fs12.existsSync(piExt) && !piPluginInstalled(resolvePiHome(process.env))) {
63965
+ if (piExt && fs12.existsSync(piExt) && !piPluginInstalled(piRealHome)) {
64152
63966
  clientArgs = ["-e", piExt, ...clientArgs];
64153
63967
  }
64154
63968
  } else if (base === "omp") {
64155
- env = buildPiEnv(origin, ca, process.env);
64156
- ompOverlayHome = prepareOmpHttpRewrite(resolveOmpHome(process.env), origin, routes.httpRewrites, routes.httpsRewrites);
64157
- if (ompOverlayHome) env.PI_CODING_AGENT_DIR = ompOverlayHome;
63969
+ env = buildPiEnv(origin, ca, process.env, routes.httpRewrites);
63970
+ delete env.PI_CODING_AGENT_DIR;
64158
63971
  const ompExt = selfDistFile("agent/omp.js");
64159
- if (ompExt && fs12.existsSync(ompExt) && !ompPluginLoadedFrom(resolveOmpHome(process.env))) {
63972
+ if (ompExt && fs12.existsSync(ompExt) && !ompPluginLoadedFrom(ompRealHome)) {
64160
63973
  clientArgs = ["-e", ompExt, ...clientArgs];
64161
63974
  }
64162
63975
  } else if (base === "opencode") {
@@ -64166,18 +63979,12 @@ async function runLaunch(params, deps = {}) {
64166
63979
  opencodeTmpFile = prepareOpencodeHttpRewrite(resolveOpencodeConfigFile(process.env), origin, routes.httpRewrites, routes.httpsRewrites, opencodePluginPath);
64167
63980
  if (opencodeTmpFile) env.OPENCODE_CONFIG = opencodeTmpFile;
64168
63981
  } else if (base === "hermes") {
64169
- env = { ...process.env };
64170
- hermesOverlayHome = prepareHermesHome(resolveHermesHome(process.env), origin, routes.httpRewrites);
64171
- if (hermesOverlayHome) {
64172
- env.HERMES_HOME = hermesOverlayHome;
64173
- if (!writeHermesIdentityPlugin(resolveHermesHome(process.env), hermesOverlayHome, routes.httpRewrites)) {
64174
- console.error(
64175
- "bili: ~/.hermes has its own plugins/ \u2014 skipping the session-identity plugin; hermes requests will be rejected by the proxy without an identity (see #286)."
64176
- );
64177
- }
64178
- } else {
63982
+ env = stripInheritedProxy(process.env);
63983
+ env.HTTPS_PROXY = origin;
63984
+ env.HERMES_CA_BUNDLE = ca;
63985
+ if (routes.httpRewrites.length === 0 && routes.httpsDomains.length === 0) {
64179
63986
  console.error(
64180
- routes.httpRewrites.length === 0 ? "bili: no hermes providers found in ~/.hermes/config.yaml \u2014 traffic will NOT go through the proxy (configure a provider first)." : "bili: hermes config.yaml could not be rewritten (unreadable or no matching provider endpoints) \u2014 traffic will NOT go through the proxy."
63987
+ "bili: no hermes providers found in ~/.hermes/config.yaml \u2014 traffic will NOT go through the proxy (configure a provider first)."
64181
63988
  );
64182
63989
  }
64183
63990
  } else if (base === "dsh") {