billion-context 0.1.40 → 0.1.42

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
@@ -13679,8 +13679,8 @@ var require_snapshot_utils = __commonJS({
13679
13679
  match: new Set(matchHeaders.map((header) => caseSensitive ? header : header.toLowerCase()))
13680
13680
  };
13681
13681
  }
13682
- var crypto = runtimeFeatures.has("crypto") ? __require("crypto") : null;
13683
- var hashId2 = crypto?.hash ? (value) => crypto.hash("sha256", value, "base64url") : (value) => Buffer.from(value).toString("base64url");
13682
+ var crypto2 = runtimeFeatures.has("crypto") ? __require("crypto") : null;
13683
+ var hashId2 = crypto2?.hash ? (value) => crypto2.hash("sha256", value, "base64url") : (value) => Buffer.from(value).toString("base64url");
13684
13684
  function isUndiciHeaders(headers) {
13685
13685
  return Array.isArray(headers) && (headers.length & 1) === 0;
13686
13686
  }
@@ -20209,10 +20209,10 @@ var require_subresource_integrity = __commonJS({
20209
20209
  var assert = __require("assert");
20210
20210
  var { runtimeFeatures } = require_runtime_features();
20211
20211
  var validSRIHashAlgorithmTokenSet = /* @__PURE__ */ new Map([["sha256", 0], ["sha384", 1], ["sha512", 2]]);
20212
- var crypto;
20212
+ var crypto2;
20213
20213
  if (runtimeFeatures.has("crypto")) {
20214
- crypto = __require("crypto");
20215
- const cryptoHashes = crypto.getHashes();
20214
+ crypto2 = __require("crypto");
20215
+ const cryptoHashes = crypto2.getHashes();
20216
20216
  if (cryptoHashes.length === 0) {
20217
20217
  validSRIHashAlgorithmTokenSet.clear();
20218
20218
  }
@@ -20302,7 +20302,7 @@ var require_subresource_integrity = __commonJS({
20302
20302
  return result;
20303
20303
  }
20304
20304
  var applyAlgorithmToBytes = (algorithm, bytes) => {
20305
- return crypto.hash(algorithm, bytes, "base64");
20305
+ return crypto2.hash(algorithm, bytes, "base64");
20306
20306
  };
20307
20307
  function caseSensitiveMatch(actualValue, expectedValue) {
20308
20308
  let actualValueLength = actualValue.length;
@@ -23285,7 +23285,7 @@ var require_connection = __commonJS({
23285
23285
  var { WebsocketFrameSend } = require_frame();
23286
23286
  var assert = __require("assert");
23287
23287
  var { runtimeFeatures } = require_runtime_features();
23288
- var crypto = runtimeFeatures.has("crypto") ? __require("crypto") : null;
23288
+ var crypto2 = runtimeFeatures.has("crypto") ? __require("crypto") : null;
23289
23289
  var warningEmitted = false;
23290
23290
  function establishWebSocketConnection(url, protocols, client, handler, options) {
23291
23291
  const requestURL = url;
@@ -23305,7 +23305,7 @@ var require_connection = __commonJS({
23305
23305
  const headersList = getHeadersList(new Headers(options.headers));
23306
23306
  request.headersList = headersList;
23307
23307
  }
23308
- const keyValue = crypto.randomBytes(16).toString("base64");
23308
+ const keyValue = crypto2.randomBytes(16).toString("base64");
23309
23309
  request.headersList.append("sec-websocket-key", keyValue, true);
23310
23310
  request.headersList.append("sec-websocket-version", "13", true);
23311
23311
  for (const protocol of protocols) {
@@ -23345,7 +23345,7 @@ var require_connection = __commonJS({
23345
23345
  return;
23346
23346
  }
23347
23347
  const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
23348
- const digest = crypto.hash("sha1", keyValue + uid, "base64");
23348
+ const digest = crypto2.hash("sha1", keyValue + uid, "base64");
23349
23349
  if (secWSAccept !== digest) {
23350
23350
  failWebsocketConnection(handler, 1002, "Incorrect hash received in Sec-WebSocket-Accept header.");
23351
23351
  return;
@@ -29907,36 +29907,36 @@ var require_pbkdf2 = __commonJS({
29907
29907
  require_md();
29908
29908
  require_util7();
29909
29909
  var pkcs5 = forge2.pkcs5 = forge2.pkcs5 || {};
29910
- var crypto;
29910
+ var crypto2;
29911
29911
  if (forge2.util.isNodejs && !forge2.options.usePureJavaScript) {
29912
- crypto = __require("crypto");
29912
+ crypto2 = __require("crypto");
29913
29913
  }
29914
29914
  module.exports = forge2.pbkdf2 = pkcs5.pbkdf2 = function(p2, s3, c, dkLen, md, callback) {
29915
29915
  if (typeof md === "function") {
29916
29916
  callback = md;
29917
29917
  md = null;
29918
29918
  }
29919
- if (forge2.util.isNodejs && !forge2.options.usePureJavaScript && crypto.pbkdf2 && (md === null || typeof md !== "object") && (crypto.pbkdf2Sync.length > 4 || (!md || md === "sha1"))) {
29919
+ if (forge2.util.isNodejs && !forge2.options.usePureJavaScript && crypto2.pbkdf2 && (md === null || typeof md !== "object") && (crypto2.pbkdf2Sync.length > 4 || (!md || md === "sha1"))) {
29920
29920
  if (typeof md !== "string") {
29921
29921
  md = "sha1";
29922
29922
  }
29923
29923
  p2 = Buffer.from(p2, "binary");
29924
29924
  s3 = Buffer.from(s3, "binary");
29925
29925
  if (!callback) {
29926
- if (crypto.pbkdf2Sync.length === 4) {
29927
- return crypto.pbkdf2Sync(p2, s3, c, dkLen).toString("binary");
29926
+ if (crypto2.pbkdf2Sync.length === 4) {
29927
+ return crypto2.pbkdf2Sync(p2, s3, c, dkLen).toString("binary");
29928
29928
  }
29929
- return crypto.pbkdf2Sync(p2, s3, c, dkLen, md).toString("binary");
29929
+ return crypto2.pbkdf2Sync(p2, s3, c, dkLen, md).toString("binary");
29930
29930
  }
29931
- if (crypto.pbkdf2Sync.length === 4) {
29932
- return crypto.pbkdf2(p2, s3, c, dkLen, function(err3, key) {
29931
+ if (crypto2.pbkdf2Sync.length === 4) {
29932
+ return crypto2.pbkdf2(p2, s3, c, dkLen, function(err3, key) {
29933
29933
  if (err3) {
29934
29934
  return callback(err3);
29935
29935
  }
29936
29936
  callback(null, key.toString("binary"));
29937
29937
  });
29938
29938
  }
29939
- return crypto.pbkdf2(p2, s3, c, dkLen, md, function(err3, key) {
29939
+ return crypto2.pbkdf2(p2, s3, c, dkLen, md, function(err3, key) {
29940
29940
  if (err3) {
29941
29941
  return callback(err3);
29942
29942
  }
@@ -43760,7 +43760,8 @@ function defaultConfig(modelContextLimit, overrides = {}) {
43760
43760
  growthCap: 5e4,
43761
43761
  minGrowthFloor: 2e4,
43762
43762
  minGrowthRatio: 0.45,
43763
- emergencyThresholdPct: 0.95
43763
+ emergencyThresholdPct: 0.95,
43764
+ tier2GrowthMultiplier: 1.5
43764
43765
  },
43765
43766
  promotionThreshold: 5,
43766
43767
  truncate: { threshold: 0.95 },
@@ -44485,6 +44486,44 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
44485
44486
  protected: protectedRanges
44486
44487
  };
44487
44488
  }
44489
+ function mergeBatch(batch) {
44490
+ const first = batch[0];
44491
+ const last = batch[batch.length - 1];
44492
+ const count = batch.reduce((s3, r) => s3 + r.count, 0);
44493
+ const tokens = batch.reduce((s3, r) => s3 + r.tokens, 0);
44494
+ const toolPct = Math.round(
44495
+ batch.reduce((s3, r) => s3 + r.toolPct * r.count, 0) / count
44496
+ );
44497
+ const merged = {
44498
+ startRef: first.startRef,
44499
+ endRef: last.endRef,
44500
+ count,
44501
+ tokens,
44502
+ toolPct,
44503
+ textPct: 100 - toolPct
44504
+ };
44505
+ if (batch.some((r) => r.dangerous === true)) {
44506
+ merged.dangerous = true;
44507
+ }
44508
+ return merged;
44509
+ }
44510
+ function mergeRangesToThreshold(ranges, minChars) {
44511
+ if (minChars <= 0 || ranges.length === 0) return ranges;
44512
+ const result = [];
44513
+ let batch = [];
44514
+ for (const r of ranges) {
44515
+ batch.push(r);
44516
+ const batchTokens = batch.reduce((s3, x) => s3 + x.tokens, 0);
44517
+ if (batchTokens * 4 >= minChars) {
44518
+ result.push(mergeBatch(batch));
44519
+ batch = [];
44520
+ }
44521
+ }
44522
+ if (batch.length > 0) {
44523
+ result.push(mergeBatch(batch));
44524
+ }
44525
+ return result;
44526
+ }
44488
44527
  function runPipeline(nodes, initial, ctx) {
44489
44528
  let io2 = initial;
44490
44529
  for (const node of nodes) {
@@ -44764,7 +44803,10 @@ var recommendNode = {
44764
44803
  const nothingToCompress = contextRanges.compressible.length === 0;
44765
44804
  const recommendation = {
44766
44805
  contextRanges,
44767
- recommendedRanges: contextRanges.compressible,
44806
+ recommendedRanges: mergeRangesToThreshold(
44807
+ contextRanges.compressible,
44808
+ ctx.config.compress.minCompressRange
44809
+ ),
44768
44810
  nothingToCompress
44769
44811
  };
44770
44812
  return { ...io2, effects: { ...io2.effects, recommendation } };
@@ -44790,6 +44832,7 @@ var nudgeNode = {
44790
44832
  if (baseline > 0 && ctx.tokenCount < baseline - nudgeGrowthTokens) {
44791
44833
  stamped.lastPerMessageNudgeTokens = ctx.tokenCount;
44792
44834
  stamped.lastNudgeShownTokens = 0;
44835
+ stamped.lastShownByTier = {};
44793
44836
  }
44794
44837
  if (stamped.lastPerMessageNudgeTokens === 0) {
44795
44838
  stamped.lastPerMessageNudgeTokens = ctx.tokenCount;
@@ -45058,10 +45101,11 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
45058
45101
  )
45059
45102
  );
45060
45103
  }
45061
- function pendingByTier(state, recommendation, countTokens) {
45104
+ function pendingByTier(state, recommendation, countTokens, minCompressRange) {
45062
45105
  const out = {};
45063
- const compressible = recommendation?.contextRanges.compressible ?? [];
45064
- out[1] = { pending: compressible.reduce((s3, r) => s3 + r.tokens, 0), targetBlocks: [] };
45106
+ const merged = recommendation?.recommendedRanges ?? [];
45107
+ const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
45108
+ out[1] = { pending: effective.reduce((s3, r) => s3 + r.tokens, 0), targetBlocks: [] };
45065
45109
  const active = activeBlocks(state);
45066
45110
  const t1 = active.filter((b2) => b2.tier === 1);
45067
45111
  const t2 = active.filter((b2) => b2.tier === 2);
@@ -45076,6 +45120,7 @@ function decideNudge(input) {
45076
45120
  const nudgeGrowthTokens = resolveAdaptiveGrowth(limit, config.nudge);
45077
45121
  const overLimit = usage >= config.nudge.maxContextLimitPct;
45078
45122
  const emergencyOverride = usage >= config.nudge.emergencyThresholdPct;
45123
+ const pressure = overLimit || emergencyOverride;
45079
45124
  const baseline = state.nudge.lastPerMessageNudgeTokens;
45080
45125
  const hadPendingNudge = state.nudge.lastNudgeShownTokens > 0;
45081
45126
  const hasPendingNudge = hadPendingNudge;
@@ -45087,44 +45132,67 @@ function decideNudge(input) {
45087
45132
  );
45088
45133
  const growthSinceReference = tokenCount - growthReference;
45089
45134
  const rec = recommendation;
45090
- const tiers = pendingByTier(state, rec, countTokens);
45135
+ const tiers = pendingByTier(
45136
+ state,
45137
+ rec,
45138
+ countTokens,
45139
+ config.compress.minCompressRange
45140
+ );
45141
+ const tier2Threshold = Math.round(
45142
+ nudgeGrowthTokens * (config.nudge.tier2GrowthMultiplier ?? 1.5)
45143
+ );
45091
45144
  let injectedTier = null;
45092
45145
  let injectedReason = "";
45093
45146
  const growthReady = growthSinceReference >= growthFloor;
45094
- if (!overLimit && growthReady) {
45095
- for (const tier of [1, 2, 3]) {
45096
- if (!config.tiers.enabled && tier > 1) break;
45097
- const info = tiers[tier];
45098
- if (!info || info.pending < nudgeGrowthTokens) continue;
45099
- const lastShown = state.nudge.lastShownByTier[tier] ?? 0;
45147
+ const t1Eff = tiers[1]?.pending ?? 0;
45148
+ const t2Pen = tiers[2]?.pending ?? 0;
45149
+ const t3Pen = tiers[3]?.pending ?? 0;
45150
+ if (pressure) {
45151
+ const candidates = [1];
45152
+ if (config.tiers.enabled) {
45153
+ candidates.push(2, 3);
45154
+ }
45155
+ let best = null;
45156
+ let bestPending = 0;
45157
+ for (const t of candidates) {
45158
+ const p2 = tiers[t]?.pending ?? 0;
45159
+ if (p2 > bestPending) {
45160
+ bestPending = p2;
45161
+ best = t;
45162
+ }
45163
+ }
45164
+ if (best !== null && bestPending > 0) {
45165
+ injectedTier = best;
45166
+ const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
45167
+ 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)}%`;
45168
+ }
45169
+ } else if (growthReady) {
45170
+ if (t1Eff >= nudgeGrowthTokens) {
45171
+ injectedTier = 1;
45172
+ injectedReason = `T1 effective ${t1Eff} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%`;
45173
+ } else if (config.tiers.enabled && t2Pen >= tier2Threshold && t2Pen > t1Eff) {
45174
+ const lastShown = state.nudge.lastShownByTier[2] ?? 0;
45100
45175
  const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
45101
- if (!cadenceMet) continue;
45102
- injectedTier = tier;
45103
- injectedReason = tier === 1 ? `T1 compressible ${info.pending} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%` : `T${tier} distill ready: ${info.targetBlocks.length} tier-${tier - 1} blocks (${info.pending} tokens) >= ${nudgeGrowthTokens}, usage ${Math.round(usage * 100)}%`;
45104
- break;
45105
- }
45106
- } else if (overLimit) {
45107
- for (const tier of [1, 2, 3]) {
45108
- if (!config.tiers.enabled && tier > 1) break;
45109
- const info = tiers[tier];
45110
- if (!info || info.pending < config.compress.minCompressRange) continue;
45111
- injectedTier = tier;
45112
- injectedReason = emergencyOverride ? `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}%, T${tier} pending ${info.pending}` : `OVER-LIMIT: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.maxContextLimitPct * 100)}%, T${tier} pending ${info.pending}`;
45113
- break;
45176
+ if (cadenceMet) {
45177
+ injectedTier = 2;
45178
+ injectedReason = `T2 distill ready: ${tiers[2].targetBlocks.length} tier-1 blocks (${t2Pen} tokens) >= ${tier2Threshold} (1.5x) and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
45179
+ }
45180
+ } else if (config.tiers.enabled && t3Pen >= tier2Threshold && t3Pen > t2Pen && t3Pen > t1Eff) {
45181
+ const lastShown = state.nudge.lastShownByTier[3] ?? 0;
45182
+ const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
45183
+ if (cadenceMet) {
45184
+ injectedTier = 3;
45185
+ injectedReason = `T3 condense ready: ${tiers[3].targetBlocks.length} tier-2 blocks (${t3Pen} tokens) >= ${tier2Threshold} (1.5x) and > T2 ${t2Pen} and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
45186
+ }
45114
45187
  }
45115
45188
  }
45116
- const shouldInject = injectedTier !== null || overLimit && (rec?.recommendedRanges?.length ?? 0) > 0;
45189
+ const shouldInject = injectedTier !== null;
45117
45190
  let reason;
45118
- if (emergencyOverride && injectedTier !== null) {
45119
- reason = injectedReason;
45120
- } else if (emergencyOverride) {
45121
- reason = `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}% (no compressible content)`;
45122
- } else if (overLimit && injectedTier !== null) {
45123
- reason = injectedReason;
45124
- } else if (overLimit) {
45125
- reason = `OVER-LIMIT: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.maxContextLimitPct * 100)}% (no compressible content)`;
45126
- } else if (injectedTier !== null) {
45191
+ if (injectedTier !== null) {
45127
45192
  reason = injectedReason;
45193
+ } else if (pressure) {
45194
+ const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
45195
+ 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`;
45128
45196
  } else {
45129
45197
  const tiersList = [1, 2, 3];
45130
45198
  const eligible = tiersList.filter((t) => config.tiers.enabled || t === 1);
@@ -45336,6 +45404,23 @@ var defaultPrompts = Object.freeze({
45336
45404
  tier2DistillRules: TIER2_DISTILL_RULES,
45337
45405
  tier3CondenseRules: TIER3_CONDENSE_RULES
45338
45406
  });
45407
+ function resolvePrompts(overrides, options = {}) {
45408
+ const clean = {};
45409
+ if (overrides) {
45410
+ for (const [key, value] of Object.entries(overrides)) {
45411
+ if (typeof value === "string") {
45412
+ clean[key] = value;
45413
+ }
45414
+ }
45415
+ }
45416
+ const keys = Object.keys(clean);
45417
+ if (keys.length > 0 && !options.acknowledgeRisk) {
45418
+ throw new Error(
45419
+ `resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. Overridden keys: ${keys.join(", ")}. These rules are quality-critical (tuned over months of production use); changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`
45420
+ );
45421
+ }
45422
+ return { ...defaultPrompts, ...clean };
45423
+ }
45339
45424
  function efficiencyNote(prompts) {
45340
45425
  return `This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.
45341
45426
 
@@ -45456,20 +45541,23 @@ ${lines.join("\n")}`;
45456
45541
  function renderNudgeText(decision, prompts = defaultPrompts) {
45457
45542
  const breakdownStr = formatBreakdown(decision.contextBreakdown);
45458
45543
  const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
45544
+ const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
45459
45545
  if (decision.tier !== null && decision.tier >= 2) {
45460
45546
  const isT2 = decision.tier === 2;
45461
45547
  const targets = decision.tierTargetBlocks ?? [];
45462
45548
  const blockList = formatTierTargetBlocks(targets);
45463
45549
  const startId = targets[0]?.blockId ?? "b1";
45464
45550
  const endId = targets[targets.length - 1]?.blockId ?? "b5";
45551
+ const voice = isEmergency ? "emergency" : "gentle";
45552
+ const triggerLine = isEmergency ? `[EMERGENCY \u2014 TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"}] Context limit reached \u2014 distill NOW into a denser summary to reclaim tokens.` : `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`;
45465
45553
  return {
45466
- voice: "gentle",
45554
+ voice,
45467
45555
  text: [
45468
45556
  efficiencyNote(prompts),
45469
45557
  "",
45470
45558
  breakdownStr,
45471
45559
  "",
45472
- `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`,
45560
+ triggerLine,
45473
45561
  isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
45474
45562
  blockList,
45475
45563
  `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
@@ -45480,7 +45568,6 @@ function renderNudgeText(decision, prompts = defaultPrompts) {
45480
45568
  ].join("\n")
45481
45569
  };
45482
45570
  }
45483
- const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
45484
45571
  if (isEmergency) {
45485
45572
  return {
45486
45573
  voice: "emergency",
@@ -46661,6 +46748,7 @@ function resolveContextLimitValue(raw, nativeLimit) {
46661
46748
  }
46662
46749
  function mergeCompress(global2, provider, model) {
46663
46750
  const pick2 = (k2) => model?.[k2] ?? provider?.[k2] ?? global2?.[k2];
46751
+ const promptLevels = [global2?.prompts, provider?.prompts, model?.prompts].filter(Boolean);
46664
46752
  return {
46665
46753
  modelContextLimit: pick2("modelContextLimit"),
46666
46754
  maxContextLimit: pick2("maxContextLimit"),
@@ -46669,13 +46757,31 @@ function mergeCompress(global2, provider, model) {
46669
46757
  preserveRecentMessages: pick2("preserveRecentMessages"),
46670
46758
  preserveRecentTokens: pick2("preserveRecentTokens"),
46671
46759
  minCompressRange: pick2("minCompressRange"),
46672
- tiers: pick2("tiers")
46760
+ tiers: pick2("tiers"),
46761
+ prompts: promptLevels.length > 0 ? Object.assign({}, ...promptLevels) : void 0,
46762
+ acknowledgePromptsRisk: pick2("acknowledgePromptsRisk")
46673
46763
  };
46674
46764
  }
46675
46765
  function resolveCompress(routes, upstreamUrl, model, global2) {
46676
46766
  const route = findRoute(routes, upstreamUrl);
46677
46767
  return mergeCompress(global2, route?.compress, model ? route?.models?.[model]?.compress : void 0);
46678
46768
  }
46769
+ var warnedPromptsRisk = false;
46770
+ function resolveCompressPrompts(s3) {
46771
+ if (!s3.prompts) return defaultPrompts;
46772
+ if (s3.acknowledgePromptsRisk !== true) {
46773
+ if (!warnedPromptsRisk) {
46774
+ warnedPromptsRisk = true;
46775
+ log("warn", "[compress] prompts override IGNORED: acknowledgePromptsRisk !== true. Set it to true to acknowledge the summary-quality risk.");
46776
+ }
46777
+ return defaultPrompts;
46778
+ }
46779
+ try {
46780
+ return resolvePrompts(s3.prompts, { acknowledgeRisk: true });
46781
+ } catch {
46782
+ return defaultPrompts;
46783
+ }
46784
+ }
46679
46785
  function hasCompressSettings(s3) {
46680
46786
  return Object.values(s3).some((v2) => v2 !== void 0);
46681
46787
  }
@@ -46714,6 +46820,12 @@ function parsePercent(v2) {
46714
46820
  if (s3.endsWith("%")) return Number(s3.slice(0, -1)) / 100;
46715
46821
  return Number(s3);
46716
46822
  }
46823
+ function resolveRequestConfig(base, routes, embeddedUrl, model, native, globalCompress) {
46824
+ const compress = resolveCompress(routes, embeddedUrl, model, globalCompress);
46825
+ const limit = resolveContextLimitValue(compress.modelContextLimit, native ?? base.modelContextLimit);
46826
+ if (!hasCompressSettings(compress) && limit === base.modelContextLimit) return base;
46827
+ return applyCompressSettings(base, limit, compress);
46828
+ }
46717
46829
 
46718
46830
  // src/registry.ts
46719
46831
  import { readFile, writeFile, mkdir } from "fs/promises";
@@ -46878,6 +46990,9 @@ function safeJsonParse(s3) {
46878
46990
  return {};
46879
46991
  }
46880
46992
  }
46993
+ function isLoopbackAddress(addr) {
46994
+ return !!addr && (addr.startsWith("127.") || addr === "::1" || addr.startsWith("::ffff:127."));
46995
+ }
46881
46996
 
46882
46997
  // src/message-id.ts
46883
46998
  function deriveMessageId(role, contentType, text, options = {}) {
@@ -47041,7 +47156,7 @@ function coreToAnthropic(messages, cacheControls) {
47041
47156
  function conversationSignalAnthropic(body, headerValue2) {
47042
47157
  if (headerValue2 && headerValue2.trim()) return headerValue2.trim();
47043
47158
  const firstUser = body.messages.find((m2) => m2.role === "user");
47044
- const seed = firstUser ? JSON.stringify(firstUser.content).slice(0, 200) : "default";
47159
+ const seed = firstUser ? JSON.stringify(firstUser.content) : "default";
47045
47160
  return hashId(seed);
47046
47161
  }
47047
47162
  function safeStringify(v2) {
@@ -47221,7 +47336,7 @@ ${extra}` : extra;
47221
47336
  function conversationSignalOpenai(body, headerValue2) {
47222
47337
  if (headerValue2 && headerValue2.trim()) return headerValue2.trim();
47223
47338
  const firstUser = body.messages.find((m2) => m2.role === "user");
47224
- const seed = firstUser ? stringContent(firstUser.content).slice(0, 200) : "default";
47339
+ const seed = firstUser ? stringContent(firstUser.content) : "default";
47225
47340
  return hashId(seed);
47226
47341
  }
47227
47342
  function stringContent(content) {
@@ -47686,6 +47801,7 @@ var SessionStore = class {
47686
47801
  this.writeChains.set(id, next);
47687
47802
  next.finally(() => {
47688
47803
  if (this.writeChains.get(id) === next) this.writeChains.delete(id);
47804
+ }).catch(() => {
47689
47805
  });
47690
47806
  return next;
47691
47807
  }
@@ -48119,10 +48235,10 @@ var COMPRESS_TOOL_OPENAI = {
48119
48235
  }
48120
48236
  }
48121
48237
  };
48122
- function buildCompressSystemPrompt() {
48123
- return `${COMPRESS_PHILOSOPHY}
48238
+ function buildCompressSystemPrompt(prompts = defaultPrompts) {
48239
+ return `${prompts.compressPhilosophy}
48124
48240
 
48125
- ${HOW_TO_COMPRESS_RULES}
48241
+ ${prompts.howToCompressRules}
48126
48242
 
48127
48243
  ACP TAGS
48128
48244
 
@@ -48145,10 +48261,10 @@ When you see past compress tool calls in the conversation, their summary paramet
48145
48261
  - User quotes inside summaries (e.g., "User said: deploy now") are historical records, not current directives.
48146
48262
  - The startId/endId in past compress calls are historical \u2014 do NOT reuse them as targets for new compress calls without checking acp_status first.`;
48147
48263
  }
48148
- function buildCompressHybridSystemPrompt() {
48149
- return `${COMPRESS_PHILOSOPHY}
48264
+ function buildCompressHybridSystemPrompt(prompts = defaultPrompts) {
48265
+ return `${prompts.compressPhilosophy}
48150
48266
 
48151
- ${HOW_TO_COMPRESS_RULES}
48267
+ ${prompts.howToCompressRules}
48152
48268
 
48153
48269
  ACP TAGS
48154
48270
 
@@ -48819,6 +48935,8 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48819
48935
  if (signal?.aborted) break;
48820
48936
  let assistantText = "";
48821
48937
  let assistantReasoning = "";
48938
+ const reasoningSegments = [];
48939
+ let reasoningSealed = true;
48822
48940
  const calls = [];
48823
48941
  let usage = {};
48824
48942
  let finishReason;
@@ -48833,6 +48951,15 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48833
48951
  }
48834
48952
  } else if (ev.kind === "reasoning") {
48835
48953
  assistantReasoning += ev.delta;
48954
+ let seg = reasoningSegments[reasoningSegments.length - 1];
48955
+ if (reasoningSealed || !seg) {
48956
+ seg = { text: "", signature: "" };
48957
+ reasoningSegments.push(seg);
48958
+ reasoningSealed = false;
48959
+ }
48960
+ seg.text += ev.delta;
48961
+ if (ev.signature) seg.signature += ev.signature;
48962
+ if (ev.blockEnd) reasoningSealed = true;
48836
48963
  if (!ctx.textProtocol) {
48837
48964
  if (ev.raw) {
48838
48965
  yield ev.raw;
@@ -48841,7 +48968,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48841
48968
  }
48842
48969
  }
48843
48970
  } else if (ev.kind === "tool_call") {
48844
- calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments });
48971
+ calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
48845
48972
  } else if (ev.kind === "usage") {
48846
48973
  usage = {
48847
48974
  inputTokens: ev.inputTokens,
@@ -48902,15 +49029,20 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48902
49029
  if (realCalls > 0) ctx.log(`[acp-loop] round ${round}: ${realCalls} real tool call(s) forwarded to client`);
48903
49030
  }
48904
49031
  if (proxyResults.length > 0) {
48905
- if (assistantReasoning.length > 0) {
48906
- const reasoningMsg = {
48907
- id: `acp_loop_r${round}_reasoning`,
48908
- role: "assistant",
48909
- contentType: "reasoning",
48910
- text: assistantReasoning,
48911
- reasoningContent: assistantReasoning
48912
- };
48913
- coreMessages.push(reasoningMsg);
49032
+ if (reasoningSegments.length > 0) {
49033
+ for (let i = 0; i < reasoningSegments.length; i++) {
49034
+ const seg = reasoningSegments[i];
49035
+ if (seg.text.length === 0 && seg.signature.length === 0) continue;
49036
+ const reasoningMsg = {
49037
+ id: i === 0 ? `acp_loop_r${round}_reasoning` : `acp_loop_r${round}_reasoning_${i + 1}`,
49038
+ role: "assistant",
49039
+ contentType: "reasoning",
49040
+ text: seg.text,
49041
+ reasoningContent: seg.text,
49042
+ ...seg.signature.length > 0 ? { thinkingSignature: seg.signature } : {}
49043
+ };
49044
+ coreMessages.push(reasoningMsg);
49045
+ }
48914
49046
  }
48915
49047
  if (assistantText.length > 0) {
48916
49048
  coreMessages.push({
@@ -48959,6 +49091,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48959
49091
  }
48960
49092
  }
48961
49093
  for (const tc of realToolCalls) {
49094
+ if (tc.passthrough) continue;
48962
49095
  yield adapter.emitToolCall(tc);
48963
49096
  }
48964
49097
  const reRequest = proxyResults.length > 0 && realCalls === 0;
@@ -49201,13 +49334,18 @@ function createResponsesAdapter(textProtocol, projection) {
49201
49334
  } else if (type === "response.output_item.added") {
49202
49335
  const item = obj.item;
49203
49336
  if (item?.type === "function_call") {
49204
- const itemId = typeof item.id === "string" ? item.id : "";
49205
- pending.set(itemId, {
49206
- itemId,
49207
- callId: typeof item.call_id === "string" ? item.call_id : "",
49208
- name: typeof item.name === "string" ? item.name : "",
49209
- arguments: ""
49210
- });
49337
+ const fcName = typeof item.name === "string" ? item.name : "";
49338
+ if (PROXY_TOOL_NAMES.has(fcName)) {
49339
+ const itemId = typeof item.id === "string" ? item.id : "";
49340
+ pending.set(itemId, {
49341
+ itemId,
49342
+ callId: typeof item.call_id === "string" ? item.call_id : "",
49343
+ name: fcName,
49344
+ arguments: ""
49345
+ });
49346
+ } else {
49347
+ yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49348
+ }
49211
49349
  } else if (item?.type === "custom_tool_call") {
49212
49350
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49213
49351
  } else if (item?.type !== "message" || !suppressTextLifecycle) {
@@ -49227,11 +49365,13 @@ function createResponsesAdapter(textProtocol, projection) {
49227
49365
  const delta = typeof obj.delta === "string" ? obj.delta : "";
49228
49366
  const fc = pending.get(itemId);
49229
49367
  if (fc) fc.arguments += delta;
49368
+ else yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49230
49369
  } else if (type === "response.function_call_arguments.done") {
49231
49370
  const itemId = typeof obj.item_id === "string" ? obj.item_id : "";
49232
49371
  const args = typeof obj.arguments === "string" ? obj.arguments : "";
49233
49372
  const fc = pending.get(itemId);
49234
49373
  if (fc && args) fc.arguments = args;
49374
+ else yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49235
49375
  } else if (type === "response.output_item.done") {
49236
49376
  const item = obj.item;
49237
49377
  if (item?.type === "function_call") {
@@ -49246,6 +49386,15 @@ function createResponsesAdapter(textProtocol, projection) {
49246
49386
  callId: fc.callId,
49247
49387
  arguments: fc.arguments
49248
49388
  };
49389
+ } else {
49390
+ yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49391
+ yield {
49392
+ kind: "tool_call",
49393
+ name: typeof item.name === "string" ? item.name : "",
49394
+ callId: typeof item.call_id === "string" ? item.call_id : "",
49395
+ arguments: typeof item.arguments === "string" ? item.arguments : "",
49396
+ passthrough: true
49397
+ };
49249
49398
  }
49250
49399
  } else if (item?.type === "custom_tool_call") {
49251
49400
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
@@ -49757,6 +49906,7 @@ ${systemPrompt}` : systemPrompt;
49757
49906
  let stopReason;
49758
49907
  let usageYielded = false;
49759
49908
  const indexMap = /* @__PURE__ */ new Map();
49909
+ const thinkingIndexes = /* @__PURE__ */ new Set();
49760
49910
  for await (const eventStr of iterSseEvents2(upstream)) {
49761
49911
  const parsed = parseAnthropicSse(eventStr);
49762
49912
  if (!parsed) continue;
@@ -49781,6 +49931,7 @@ ${systemPrompt}` : systemPrompt;
49781
49931
  const id = typeof block.id === "string" ? block.id : `toolu_${upstreamIndex}`;
49782
49932
  pending.set(upstreamIndex, { id, name, json: "" });
49783
49933
  } else {
49934
+ if (block.type === "thinking" || block.type === "redacted_thinking") thinkingIndexes.add(upstreamIndex);
49784
49935
  const ci2 = clientIndex++;
49785
49936
  indexMap.set(upstreamIndex, ci2);
49786
49937
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: round === 1 };
@@ -49795,6 +49946,21 @@ ${systemPrompt}` : systemPrompt;
49795
49946
  } else if (delta.type === "text_delta" && typeof delta.text === "string") {
49796
49947
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49797
49948
  yield { kind: "text", delta: delta.text, raw: remapIndexInEvent(eventStr, ci2) };
49949
+ } else if (delta.type === "thinking_delta" && typeof delta.thinking === "string" && delta.thinking.length > 0) {
49950
+ const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49951
+ yield {
49952
+ kind: "reasoning",
49953
+ delta: delta.thinking,
49954
+ ...round === 1 ? { raw: remapIndexInEvent(eventStr, ci2) } : {}
49955
+ };
49956
+ } else if (delta.type === "signature_delta" && typeof delta.signature === "string") {
49957
+ const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49958
+ yield {
49959
+ kind: "reasoning",
49960
+ delta: "",
49961
+ signature: delta.signature,
49962
+ ...round === 1 ? { raw: remapIndexInEvent(eventStr, ci2) } : {}
49963
+ };
49798
49964
  } else if (round === 1) {
49799
49965
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49800
49966
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
@@ -49810,6 +49976,12 @@ ${systemPrompt}` : systemPrompt;
49810
49976
  callId: tb.id,
49811
49977
  arguments: tb.json
49812
49978
  };
49979
+ } else if (thinkingIndexes.delete(upstreamIndex)) {
49980
+ if (round === 1) {
49981
+ const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49982
+ yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: true };
49983
+ }
49984
+ yield { kind: "reasoning", delta: "", blockEnd: true };
49813
49985
  } else {
49814
49986
  const ci2 = indexMap.get(upstreamIndex) ?? upstreamIndex;
49815
49987
  yield { kind: "meta", chunk: remapIndexInEvent(eventStr, ci2), firstRoundOnly: round === 1 };
@@ -49850,6 +50022,22 @@ ${systemPrompt}` : systemPrompt;
49850
50022
  emitText(delta) {
49851
50023
  return buildTextBlock(clientIndex++, delta);
49852
50024
  },
50025
+ emitReasoning(delta) {
50026
+ const index = clientIndex++;
50027
+ return Buffer.from(
50028
+ `event: content_block_start
50029
+ data: ${JSON.stringify({ type: "content_block_start", index, content_block: { type: "thinking", thinking: "" } })}
50030
+
50031
+ event: content_block_delta
50032
+ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: "thinking_delta", thinking: delta } })}
50033
+
50034
+ event: content_block_stop
50035
+ data: ${JSON.stringify({ type: "content_block_stop", index })}
50036
+
50037
+ `,
50038
+ "utf8"
50039
+ );
50040
+ },
49853
50041
  emitToolCall(call) {
49854
50042
  return buildToolUseBlock(clientIndex++, call);
49855
50043
  },
@@ -50095,7 +50283,6 @@ ${note}` : note;
50095
50283
  }
50096
50284
 
50097
50285
  // src/stream-responses.ts
50098
- var TEXT_PROTOCOL = process.env.ACP_COMPRESS_PROTOCOL === "text";
50099
50286
  function rewriteResponsesJsonResponse(body, ctx) {
50100
50287
  if (!body || typeof body !== "object") return body;
50101
50288
  const b2 = body;
@@ -50148,12 +50335,35 @@ function emitStreamError(res, protocol, message, log2) {
50148
50335
  `);
50149
50336
  safeWrite(res, "data: [DONE]\n\n");
50150
50337
  } else if (protocol === "responses") {
50338
+ const itemId = "msg_acp_error";
50339
+ const oi2 = 0;
50340
+ const errorItem = { type: "message", id: itemId, role: "assistant", content: [{ type: "output_text", text: visible }] };
50341
+ safeWrite(res, `event: response.output_item.added
50342
+ data: ${JSON.stringify({ type: "response.output_item.added", output_index: oi2, item: { type: "message", id: itemId, role: "assistant", content: [] } })}
50343
+
50344
+ `);
50345
+ safeWrite(res, `event: response.content_part.added
50346
+ data: ${JSON.stringify({ type: "response.content_part.added", item_id: itemId, output_index: oi2, part: { type: "output_text", text: "" } })}
50347
+
50348
+ `);
50151
50349
  safeWrite(res, `event: response.output_text.delta
50152
- data: ${JSON.stringify({ type: "response.output_text.delta", delta: visible })}
50350
+ data: ${JSON.stringify({ type: "response.output_text.delta", item_id: itemId, output_index: oi2, delta: visible })}
50351
+
50352
+ `);
50353
+ safeWrite(res, `event: response.output_text.done
50354
+ data: ${JSON.stringify({ type: "response.output_text.done", item_id: itemId, output_index: oi2, text: visible })}
50355
+
50356
+ `);
50357
+ safeWrite(res, `event: response.content_part.done
50358
+ data: ${JSON.stringify({ type: "response.content_part.done", item_id: itemId, output_index: oi2, part: { type: "output_text", text: visible } })}
50359
+
50360
+ `);
50361
+ safeWrite(res, `event: response.output_item.done
50362
+ data: ${JSON.stringify({ type: "response.output_item.done", output_index: oi2, item: errorItem })}
50153
50363
 
50154
50364
  `);
50155
50365
  safeWrite(res, `event: response.completed
50156
- data: ${JSON.stringify({ type: "response.completed", response: { status: "completed", output: [] } })}
50366
+ data: ${JSON.stringify({ type: "response.completed", response: { status: "completed", output: [errorItem] } })}
50157
50367
 
50158
50368
  `);
50159
50369
  } else {
@@ -50220,6 +50430,7 @@ var rootKeyPem;
50220
50430
  var rootCert;
50221
50431
  var rootKey;
50222
50432
  var secureContextCache = /* @__PURE__ */ new Map();
50433
+ var SECURE_CONTEXT_CACHE_MAX = 64;
50223
50434
  function generateRootCA() {
50224
50435
  const keys = import_node_forge.default.pki.rsa.generateKeyPair({ bits: 2048 });
50225
50436
  const cert = import_node_forge.default.pki.createCertificate();
@@ -50271,7 +50482,11 @@ function getSecureContext(host) {
50271
50482
  throw new Error("CA not initialized \u2014 call ensureRootCA() first");
50272
50483
  }
50273
50484
  const cached = secureContextCache.get(host);
50274
- if (cached) return cached;
50485
+ if (cached) {
50486
+ secureContextCache.delete(host);
50487
+ secureContextCache.set(host, cached);
50488
+ return cached;
50489
+ }
50275
50490
  const keys = import_node_forge.default.pki.rsa.generateKeyPair({ bits: 2048 });
50276
50491
  const cert = import_node_forge.default.pki.createCertificate();
50277
50492
  cert.publicKey = keys.publicKey;
@@ -50294,6 +50509,10 @@ function getSecureContext(host) {
50294
50509
  ca: rootCertPem
50295
50510
  });
50296
50511
  secureContextCache.set(host, ctx);
50512
+ if (secureContextCache.size > SECURE_CONTEXT_CACHE_MAX) {
50513
+ const oldest = secureContextCache.keys().next().value;
50514
+ if (oldest !== void 0) secureContextCache.delete(oldest);
50515
+ }
50297
50516
  return ctx;
50298
50517
  }
50299
50518
 
@@ -50527,6 +50746,11 @@ var DEFAULT_MITM_DOMAINS = [
50527
50746
  "chatgpt.com"
50528
50747
  ];
50529
50748
  var MITM_UPSTREAM_KEY = "__biliMitmUpstream";
50749
+ var MITM_HANDSHAKE_TIMEOUT_MS_DEFAULT = 1e4;
50750
+ function mitmHandshakeTimeoutMs() {
50751
+ const v2 = Number.parseInt(process.env.BILI_MITM_HANDSHAKE_TIMEOUT_MS ?? "", 10);
50752
+ return Number.isFinite(v2) && v2 > 0 ? v2 : MITM_HANDSHAKE_TIMEOUT_MS_DEFAULT;
50753
+ }
50530
50754
  function isMitmHost(host, extraDomains = []) {
50531
50755
  const h = host.toLowerCase();
50532
50756
  const all = [...DEFAULT_MITM_DOMAINS, ...extraDomains, ...discoverMitmDomains()].map((d) => d.toLowerCase());
@@ -50536,12 +50760,12 @@ function setupMitm(server, extraDomains = [], log2 = () => {
50536
50760
  }, resolveProxyUrl) {
50537
50761
  ensureRootCA();
50538
50762
  server.on("connect", (req, clientSocket, head) => {
50539
- const { hostname, port } = parseHostPort(req.url ?? "");
50540
- if (!hostname) {
50541
- clientSocket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
50542
- clientSocket.end();
50763
+ if (!isLoopbackAddress(clientSocket.remoteAddress)) {
50764
+ log2(`CONNECT ${req.url} rejected: non-loopback client ${clientSocket.remoteAddress}`);
50765
+ clientSocket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
50543
50766
  return;
50544
50767
  }
50768
+ const { hostname, port } = parseHostPort(req.url ?? "");
50545
50769
  const targetPort = port || 443;
50546
50770
  if (!isMitmHost(hostname, extraDomains)) {
50547
50771
  const proxyUrl = resolveProxyUrl?.(hostname);
@@ -50561,14 +50785,20 @@ function parseHostPort(s3) {
50561
50785
  }
50562
50786
  function tunnelThrough(clientSocket, host, port, head, log2, proxyUrl) {
50563
50787
  let established = false;
50788
+ let aborted = false;
50564
50789
  const connectTimer = setTimeout(() => {
50565
50790
  if (!established) {
50791
+ aborted = true;
50566
50792
  log2(`tunnel ${host}:${port} connect timeout`);
50567
50793
  clientSocket.write("HTTP/1.1 504 Gateway Timeout\r\n\r\n");
50568
50794
  clientSocket.destroy();
50569
50795
  }
50570
50796
  }, 15e3);
50571
50797
  connectThroughProxy(host, port, proxyUrl).then((upstream) => {
50798
+ if (aborted) {
50799
+ upstream.destroy();
50800
+ return;
50801
+ }
50572
50802
  established = true;
50573
50803
  clearTimeout(connectTimer);
50574
50804
  clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
@@ -50583,6 +50813,7 @@ function tunnelThrough(clientSocket, host, port, head, log2, proxyUrl) {
50583
50813
  upstream.once("error", (e) => cleanup("upstream", e));
50584
50814
  clientSocket.once("error", (e) => cleanup("client", e));
50585
50815
  }).catch((err2) => {
50816
+ if (aborted) return;
50586
50817
  clearTimeout(connectTimer);
50587
50818
  log2(`tunnel ${host}:${port} connect failed: ${err2.message}`);
50588
50819
  clientSocket.write("HTTP/1.1 502 Bad Gateway\r\n\r\n");
@@ -50612,6 +50843,14 @@ function doMitm(server, clientSocket, host, port, head, log2) {
50612
50843
  tlsSocket.destroy();
50613
50844
  clientSocket.destroy();
50614
50845
  });
50846
+ const handshakeTimer = setTimeout(() => {
50847
+ log2(`mitm ${host}:${port} TLS handshake timeout`);
50848
+ tlsSocket.destroy();
50849
+ clientSocket.destroy();
50850
+ }, mitmHandshakeTimeoutMs());
50851
+ tlsSocket.once("secure", () => clearTimeout(handshakeTimer));
50852
+ tlsSocket.once("close", () => clearTimeout(handshakeTimer));
50853
+ tlsSocket.once("error", () => clearTimeout(handshakeTimer));
50615
50854
  server.emit("connection", tlsSocket);
50616
50855
  log2(`mitm ${host}:${port} tunnel established (TLS terminated locally)`);
50617
50856
  }
@@ -51347,8 +51586,28 @@ var UPSTREAM_HOP_HEADERS = /* @__PURE__ */ new Set([
51347
51586
  // Node's fetch transparently decodes compressed responses. Do not
51348
51587
  // forward the upstream encoding marker when the body is rewritten or
51349
51588
  // streamed from fetch, otherwise clients try to decompress plain bytes.
51350
- "content-encoding"
51589
+ "content-encoding",
51590
+ // RFC 7230 §6.1 hop-by-hop headers. proxy-authorization in particular
51591
+ // carries client→proxy credentials that must never reach the model
51592
+ // endpoint. (#80)
51593
+ "proxy-authenticate",
51594
+ "proxy-authorization",
51595
+ "proxy-connection",
51596
+ "te",
51597
+ "trailer",
51598
+ "upgrade"
51351
51599
  ]);
51600
+ function connectionNamedHeaders(conn) {
51601
+ const out = /* @__PURE__ */ new Set();
51602
+ if (!conn) return out;
51603
+ for (const part of Array.isArray(conn) ? conn : [conn]) {
51604
+ for (const name of part.split(",")) {
51605
+ const t = name.trim().toLowerCase();
51606
+ if (t) out.add(t);
51607
+ }
51608
+ }
51609
+ return out;
51610
+ }
51352
51611
  function buildForwardHeaders(headers) {
51353
51612
  const out = {};
51354
51613
  for (const [k2, v2] of Object.entries(headers)) {
@@ -51468,28 +51727,38 @@ async function startServer(opts) {
51468
51727
  }
51469
51728
  return server;
51470
51729
  }
51471
- function isLoopback(addr) {
51472
- if (!addr) return false;
51473
- return addr === "::1" || addr === "127.0.0.1" || addr.startsWith("127.") || addr.startsWith("::ffff:127.");
51474
- }
51475
- function isTrustedAdminOrigin(origin, host) {
51730
+ function isTrustedAdminOrigin(origin, host, trustedHosts) {
51731
+ if (!host || !trustedHosts.has(host.toLowerCase())) return false;
51476
51732
  if (!origin) return true;
51477
- if (!host) return false;
51478
51733
  try {
51479
51734
  const parsed = new URL(origin);
51480
- return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.host === host;
51735
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
51736
+ return trustedHosts.has(parsed.host.toLowerCase());
51481
51737
  } catch {
51482
51738
  return false;
51483
51739
  }
51484
51740
  }
51741
+ function adminTrustedHosts(bindHost, port) {
51742
+ const p2 = String(port);
51743
+ const names = ["localhost", "127.0.0.1", "[::1]"];
51744
+ if (bindHost && bindHost !== "0.0.0.0" && bindHost !== "::" && !names.includes(bindHost)) {
51745
+ names.push(bindHost);
51746
+ }
51747
+ const set = /* @__PURE__ */ new Set();
51748
+ for (const n of names) {
51749
+ set.add(`${n}:${p2}`.toLowerCase());
51750
+ if (p2 === "80") set.add(n.toLowerCase());
51751
+ }
51752
+ return set;
51753
+ }
51485
51754
  async function handle(req, res, opts, core, config, log2) {
51486
51755
  const isAdminPath = req.url === "/__bili/" || req.url?.startsWith("/__bili/") || req.url === "/__acp/" || req.url?.startsWith("/__acp/");
51487
- if (isAdminPath && !isLoopback(req.socket.remoteAddress)) {
51756
+ if (isAdminPath && !isLoopbackAddress(req.socket.remoteAddress)) {
51488
51757
  res.writeHead(403, { "content-type": "application/json" });
51489
51758
  res.end(JSON.stringify({ error: "management endpoints are loopback-only; access denied for " + (req.socket.remoteAddress ?? "unknown") }));
51490
51759
  return;
51491
51760
  }
51492
- if (isAdminPath && !isTrustedAdminOrigin(req.headers.origin, req.headers.host)) {
51761
+ if (isAdminPath && !isTrustedAdminOrigin(req.headers.origin, req.headers.host, adminTrustedHosts(opts.host, req.socket.localPort ?? opts.port))) {
51493
51762
  res.writeHead(403, { "content-type": "application/json" });
51494
51763
  res.end(JSON.stringify({ error: "management request origin does not match the local bili UI" }));
51495
51764
  return;
@@ -51643,11 +51912,11 @@ ${bodyBuffer.toString("utf8")}`);
51643
51912
  }
51644
51913
  }
51645
51914
  let reqConfig = config;
51915
+ let reqPrompts = defaultPrompts;
51646
51916
  if (parsed && typeof parsed === "object") {
51647
51917
  const model = parsed.model;
51648
51918
  if (model) {
51649
51919
  const embeddedUrl = route?.rewrittenUrl;
51650
- const compress = resolveCompress(opts.routes, embeddedUrl, model, opts.compress);
51651
51920
  let native = resolveContextLimit(opts.routes, embeddedUrl, model);
51652
51921
  if (!native && embeddedUrl) {
51653
51922
  const host = (() => {
@@ -51659,11 +51928,8 @@ ${bodyBuffer.toString("utf8")}`);
51659
51928
  })();
51660
51929
  native = await contextFromRegistry(model, host);
51661
51930
  }
51662
- const limit = resolveContextLimitValue(compress.modelContextLimit, native ?? config.modelContextLimit);
51663
- const tuned = hasCompressSettings(compress);
51664
- if (tuned || limit !== config.modelContextLimit) {
51665
- reqConfig = applyCompressSettings(config, limit, compress);
51666
- }
51931
+ reqConfig = resolveRequestConfig(config, opts.routes, embeddedUrl, model, native, opts.compress);
51932
+ reqPrompts = resolveCompressPrompts(resolveCompress(opts.routes, embeddedUrl, model, opts.compress));
51667
51933
  }
51668
51934
  }
51669
51935
  let prepared = null;
@@ -51684,7 +51950,7 @@ ${bodyBuffer.toString("utf8")}`);
51684
51950
  acquireInFlight(session);
51685
51951
  try {
51686
51952
  await withSessionLock(session, async () => {
51687
- prepared = countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, log2, session) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, log2, session) : responsesCompact ? prepareResponsesCompact(bodyBuffer, parsed, session) : prepareResponses(parsed, req, opts, core, reqConfig, log2, session, responsesIdentity);
51953
+ prepared = countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, reqPrompts, log2, session) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, reqPrompts, log2, session) : responsesCompact ? prepareResponsesCompact(bodyBuffer, parsed, session) : prepareResponses(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity);
51688
51954
  await forward(req, res, opts, prepared.body, prepared, core, reqConfig, log2, route, affinity);
51689
51955
  });
51690
51956
  } finally {
@@ -51726,7 +51992,7 @@ function diagNudge(turn, sessionId, tokenCount, limit) {
51726
51992
  const inject = n.shouldInject ? `INJECT T${n.tier ?? "?"}` : "idle";
51727
51993
  return `[${sessionId}] nudge ${inject}: usage=${pct2} (${tokenCount}/${limit}), growth=${growth}/${floor} (ref=${ref}, interval=${interval}), pendingT1=${pendingT1}/${interval}, reason="${n.reason.slice(0, 120)}"`;
51728
51994
  }
51729
- function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
51995
+ function prepareAnthropic(parsed, req, opts, core, config, prompts, log2, session) {
51730
51996
  const sessionId = session.id;
51731
51997
  const stream2 = parsed.stream === true;
51732
51998
  ++session.stats.requests;
@@ -51754,13 +52020,13 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
51754
52020
  processedMessages = stripKernelSummaries(turn.messages);
51755
52021
  reapOrphanBlocks(session, msgs, deactivateBlock);
51756
52022
  rebuiltMessages = coreToAnthropic(processedMessages, cacheControls);
51757
- systemOut = injectSystem(parsed, opts);
52023
+ systemOut = injectSystem(parsed, opts, prompts);
51758
52024
  if (opts.compress.injectTool) {
51759
52025
  toolsOut = injectTool(parsed.tools);
51760
52026
  }
51761
52027
  if (opts.compress.injectNudge && turn.nudge?.shouldInject) {
51762
52028
  try {
51763
- const rendered = renderNudgeText(turn.nudge);
52029
+ const rendered = renderNudgeText(turn.nudge, prompts);
51764
52030
  if (rendered.text) {
51765
52031
  rebuiltMessages = [...rebuiltMessages, { role: "user", content: rendered.text }];
51766
52032
  }
@@ -51771,11 +52037,11 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
51771
52037
  log2("warn", `[${sessionId}] kernel transform failed, forwarding unchanged: ${String(err2)}`);
51772
52038
  processedMessages = [];
51773
52039
  }
51774
- const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
51775
52040
  markDirty(session);
51776
- return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool, nudge };
52041
+ const rebuilt = { ...parsed, messages: rebuiltMessages, system: systemOut, tools: toolsOut };
52042
+ return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, anthropicSystem: parsed.system, protocol: "anthropic", stream: stream2, compressInjected: opts.compress.injectTool, nudge, prompts };
51777
52043
  }
51778
- function prepareOpenai(parsed, req, opts, core, config, log2, session) {
52044
+ function prepareOpenai(parsed, req, opts, core, config, prompts, log2, session) {
51779
52045
  const sessionId = session.id;
51780
52046
  const stream2 = parsed.stream === true;
51781
52047
  ++session.stats.requests;
@@ -51805,14 +52071,14 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
51805
52071
  reapOrphanBlocks(session, msgs, deactivateBlock);
51806
52072
  rebuiltMessages = coreToOpenai(processedMessages);
51807
52073
  const sysParts = [];
51808
- if (shouldInject) sysParts.push(buildCompressSystemPrompt());
52074
+ if (shouldInject) sysParts.push(buildCompressSystemPrompt(prompts));
51809
52075
  rebuiltMessages = injectOpenaiSystem(rebuiltMessages, sysParts);
51810
52076
  if (shouldInject) {
51811
52077
  toolsOut = injectOpenaiTool(parsed.tools);
51812
52078
  }
51813
52079
  if (opts.compress.injectNudge && turn.nudge?.shouldInject && shouldInject) {
51814
52080
  try {
51815
- const rendered = renderNudgeText(turn.nudge);
52081
+ const rendered = renderNudgeText(turn.nudge, prompts);
51816
52082
  if (rendered.text) {
51817
52083
  rebuiltMessages = [...rebuiltMessages, { role: "user", content: rendered.text }];
51818
52084
  }
@@ -51828,9 +52094,9 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
51828
52094
  rebuilt.stream_options = { include_usage: true };
51829
52095
  }
51830
52096
  markDirty(session);
51831
- return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: shouldInject, nudge };
52097
+ return { body: JSON.stringify(rebuilt), session, processedMessages, originalMessages, protocol: "openai", stream: stream2, compressInjected: shouldInject, nudge, prompts };
51832
52098
  }
51833
- function prepareResponses(parsed, req, opts, core, config, log2, session, identity) {
52099
+ function prepareResponses(parsed, req, opts, core, config, prompts, log2, session, identity) {
51834
52100
  const sessionId = session.id;
51835
52101
  const stream2 = parsed.stream === true;
51836
52102
  ++session.stats.requests;
@@ -51868,7 +52134,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
51868
52134
  reapOrphanBlocks(session, msgs, deactivateBlock);
51869
52135
  rebuiltInput = patchResponsesInput(projection, processedMessages);
51870
52136
  if (shouldInject && !process.env.ACP_NO_COMPRESS_PROMPT) {
51871
- const prompt = responsesTextProtocol ? buildCompressHybridSystemPrompt() : buildCompressSystemPrompt();
52137
+ const prompt = responsesTextProtocol ? buildCompressHybridSystemPrompt(prompts) : buildCompressSystemPrompt(prompts);
51872
52138
  const devContent = [...projection.systemParts, prompt].join("\n\n---\n\n");
51873
52139
  rebuiltInput = injectResponsesDeveloperMessage(rebuiltInput, devContent);
51874
52140
  if (!process.env.ACP_NO_INJECT_TOOL) {
@@ -51879,7 +52145,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
51879
52145
  }
51880
52146
  if (opts.compress.injectNudge && turn.nudge?.shouldInject && shouldInject) {
51881
52147
  try {
51882
- const rendered = renderNudgeText(turn.nudge);
52148
+ const rendered = renderNudgeText(turn.nudge, prompts);
51883
52149
  if (rendered.text) {
51884
52150
  const inputItems = typeof rebuiltInput === "string" ? [{ type: "message", role: "user", content: rebuiltInput }] : rebuiltInput;
51885
52151
  inputItems.push({ type: "message", role: "user", content: rendered.text });
@@ -51921,7 +52187,8 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
51921
52187
  stream: stream2,
51922
52188
  compressInjected: shouldInject,
51923
52189
  responsesTextProtocol,
51924
- nudge
52190
+ nudge,
52191
+ prompts
51925
52192
  };
51926
52193
  }
51927
52194
  function isCountTokensRequest(method, urlPath, hasBody) {
@@ -51984,10 +52251,10 @@ function resolvePromptCacheKey(explicit, identity, routing, upstream) {
51984
52251
  if (!identity.clientProvided || !shouldInjectPromptCacheKey(routing, upstream)) return void 0;
51985
52252
  return identity.value;
51986
52253
  }
51987
- function injectSystem(parsed, opts) {
52254
+ function injectSystem(parsed, opts, prompts = defaultPrompts) {
51988
52255
  const baseText = extractSystem(parsed.system);
51989
52256
  const parts = [];
51990
- if (opts.compress.injectTool) parts.push(buildCompressSystemPrompt());
52257
+ if (opts.compress.injectTool) parts.push(buildCompressSystemPrompt(prompts));
51991
52258
  if (parts.length === 0) return parsed.system;
51992
52259
  const full = baseText ? `${baseText}
51993
52260
 
@@ -52066,8 +52333,10 @@ async function forward(req, res, opts, body, prepared, core, config, log2, route
52066
52333
  }
52067
52334
  }
52068
52335
  const headers = {};
52336
+ const reqConnNamed = connectionNamedHeaders(req.headers["connection"]);
52069
52337
  for (const [k2, v2] of Object.entries(req.headers)) {
52070
- if (UPSTREAM_HOP_HEADERS.has(k2.toLowerCase()) || v2 === void 0) continue;
52338
+ const lower = k2.toLowerCase();
52339
+ if (UPSTREAM_HOP_HEADERS.has(lower) || reqConnNamed.has(lower) || v2 === void 0) continue;
52071
52340
  headers[k2] = Array.isArray(v2) ? v2.join(", ") : v2;
52072
52341
  }
52073
52342
  headers["host"] = new URL(upstreamUrl).host;
@@ -52128,14 +52397,17 @@ ${bodyText}`);
52128
52397
  }
52129
52398
  const { response: upstream, clearTimer: clearUpstreamTimer } = upstreamResult;
52130
52399
  const respHeaders = {};
52400
+ const respConnNamed = connectionNamedHeaders(upstream.headers.get("connection") ?? void 0);
52131
52401
  upstream.headers.forEach((v2, k2) => {
52132
- if (UPSTREAM_HOP_HEADERS.has(k2.toLowerCase())) return;
52402
+ const lower = k2.toLowerCase();
52403
+ if (UPSTREAM_HOP_HEADERS.has(lower) || respConnNamed.has(lower)) return;
52133
52404
  respHeaders[k2] = v2;
52134
52405
  });
52135
52406
  if (opts.debug) {
52136
52407
  const respLog = {};
52137
52408
  upstream.headers.forEach((v2, k2) => {
52138
- if (UPSTREAM_HOP_HEADERS.has(k2.toLowerCase())) return;
52409
+ const lower = k2.toLowerCase();
52410
+ if (UPSTREAM_HOP_HEADERS.has(lower) || respConnNamed.has(lower)) return;
52139
52411
  respLog[k2] = v2.length > 300 ? v2.slice(0, 300) + "..." : v2;
52140
52412
  });
52141
52413
  log2("info", `[${prepared?.session.id ?? "unknown"}] \u2190 upstream response headers: ${JSON.stringify(respLog)}`);
@@ -52198,7 +52470,7 @@ ${hdrText}
52198
52470
  const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
52199
52471
  const reqHeaders = buildForwardHeaders(headers);
52200
52472
  const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
52201
- const systemPrompt = textProtocol ? buildCompressHybridSystemPrompt() : buildCompressSystemPrompt();
52473
+ const systemPrompt = textProtocol ? buildCompressHybridSystemPrompt(prepared.prompts ?? defaultPrompts) : buildCompressSystemPrompt(prepared.prompts ?? defaultPrompts);
52202
52474
  const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem);
52203
52475
  const abortCtrl = new AbortController();
52204
52476
  req.on("close", () => {
@@ -52405,6 +52677,7 @@ function logMsg(opts, level, msg2) {
52405
52677
 
52406
52678
  // src/update.ts
52407
52679
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access, constants, rm, cp, unlink } from "fs/promises";
52680
+ import crypto from "crypto";
52408
52681
 
52409
52682
  // node_modules/tar/dist/esm/index.min.js
52410
52683
  import Qr from "events";
@@ -55382,7 +55655,10 @@ var REGISTRY_BASE = "https://registry.npmjs.org";
55382
55655
  var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
55383
55656
  var THROTTLE_FILE = path8.join(cacheDir(), ".update-check");
55384
55657
  var LOCK_FILE = path8.join(cacheDir(), ".update-lock");
55385
- var LOCK_STALE_MS = 2 * 60 * 1e3;
55658
+ var LOCK_MAX_AGE_MS = 30 * 60 * 1e3;
55659
+ function shouldStealLock(holderAlive, ageMs) {
55660
+ return !holderAlive || ageMs >= LOCK_MAX_AGE_MS;
55661
+ }
55386
55662
  var timer;
55387
55663
  var inFlight = false;
55388
55664
  var firstCheckDone = false;
@@ -55460,12 +55736,12 @@ async function tryAcquireLock() {
55460
55736
  }
55461
55737
  const existing = await readLock();
55462
55738
  if (existing) {
55463
- const age = now - existing.ts;
55464
55739
  const holderAlive = isAlive(existing.pid);
55465
- if (holderAlive && age < LOCK_STALE_MS) {
55740
+ if (!shouldStealLock(holderAlive, now - existing.ts)) {
55741
+ log("info", `[update] lock held by live pid=${existing.pid} (age=${Math.round((now - existing.ts) / 1e3)}s), skipping update`);
55466
55742
  return null;
55467
55743
  }
55468
- log("info", `[update] stealing stale lock (pid=${existing.pid}, age=${Math.round(age / 1e3)}s, alive=${holderAlive})`);
55744
+ log("info", `[update] stealing lock from pid=${existing.pid} (alive=${holderAlive}, age=${Math.round((now - existing.ts) / 1e3)}s)`);
55469
55745
  try {
55470
55746
  await unlink(LOCK_FILE);
55471
55747
  } catch (e) {
@@ -55539,6 +55815,8 @@ async function checkForUpdate(opts, force = false) {
55539
55815
  return;
55540
55816
  }
55541
55817
  const tarballUrl = data.dist?.tarball;
55818
+ const integrity = data.dist?.integrity;
55819
+ const shasum = data.dist?.shasum;
55542
55820
  if (!tarballUrl) {
55543
55821
  log("warn", `[update] registry response for ${latest} had no tarball URL`);
55544
55822
  return;
@@ -55550,7 +55828,7 @@ async function checkForUpdate(opts, force = false) {
55550
55828
  return;
55551
55829
  }
55552
55830
  try {
55553
- const result = await installViaTarball(latest, tarballUrl, installDir);
55831
+ const result = await installViaTarball(latest, tarballUrl, installDir, integrity, shasum);
55554
55832
  if (result.ok) {
55555
55833
  log("info", `[update] installed ${currentVersion} \u2192 ${latest}. Restart to finish.`);
55556
55834
  } else {
@@ -55565,7 +55843,29 @@ async function checkForUpdate(opts, force = false) {
55565
55843
  inFlight = false;
55566
55844
  }
55567
55845
  }
55568
- async function installViaTarball(version2, tarballUrl, installDir) {
55846
+ function verifyTarballIntegrity(buf, integrity, shasum) {
55847
+ if (integrity) {
55848
+ const dash = integrity.indexOf("-");
55849
+ if (dash <= 0) return { ok: false, error: "malformed integrity field" };
55850
+ const alg = integrity.slice(0, dash);
55851
+ const expected = integrity.slice(dash + 1);
55852
+ let actual;
55853
+ try {
55854
+ actual = crypto.createHash(alg).update(buf).digest("base64");
55855
+ } catch {
55856
+ return { ok: false, error: `unsupported integrity algorithm: ${alg}` };
55857
+ }
55858
+ if (actual !== expected) return { ok: false, error: `${alg} mismatch` };
55859
+ return { ok: true };
55860
+ }
55861
+ if (shasum) {
55862
+ const actual = crypto.createHash("sha1").update(buf).digest("hex");
55863
+ if (actual !== shasum) return { ok: false, error: "sha1 shasum mismatch" };
55864
+ return { ok: true };
55865
+ }
55866
+ return { ok: false, error: "no integrity or shasum from registry" };
55867
+ }
55868
+ async function installViaTarball(version2, tarballUrl, installDir, integrity, shasum) {
55569
55869
  if (!installDir) {
55570
55870
  return { ok: false, error: "cannot determine install directory (package.json not found walking up from running binary)" };
55571
55871
  }
@@ -55604,6 +55904,10 @@ async function installViaTarball(version2, tarballUrl, installDir) {
55604
55904
  } catch (e) {
55605
55905
  return { ok: false, error: `tarball download failed: ${String(e)}` };
55606
55906
  }
55907
+ const v2 = verifyTarballIntegrity(tgzBuffer, integrity, shasum);
55908
+ if (!v2.ok) {
55909
+ return { ok: false, error: `tarball integrity verification failed: ${v2.error}` };
55910
+ }
55607
55911
  const tmpFile = path8.join(cacheDir(), `.update-${version2}.tgz`);
55608
55912
  try {
55609
55913
  await mkdir2(cacheDir(), { recursive: true });