billion-context 0.1.39 → 0.1.41

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
@@ -43595,9 +43595,11 @@ function prune(messages, state, options = {}) {
43595
43595
  const indexById = /* @__PURE__ */ new Map();
43596
43596
  messages.forEach((message, index) => indexById.set(message.id, index));
43597
43597
  const anchors = inject ? collectSummaryAnchors(state, indexById) : [];
43598
- return stripOrphanedToolResults(
43599
- stripOrphanedToolCalls(
43600
- rebuildMessages(messages, covered, firstUserIndex, anchors)
43598
+ return stripOrphanedReasoning(
43599
+ stripOrphanedToolResults(
43600
+ stripOrphanedToolCalls(
43601
+ rebuildMessages(messages, covered, firstUserIndex, anchors)
43602
+ )
43601
43603
  )
43602
43604
  );
43603
43605
  }
@@ -43674,6 +43676,24 @@ function stripOrphanedToolCalls(messages) {
43674
43676
  (m2) => m2.contentType !== "tool-call" || !m2.toolCallId || m2.toolName === "compress" || knownResultIds.has(m2.toolCallId)
43675
43677
  );
43676
43678
  }
43679
+ function stripOrphanedReasoning(messages) {
43680
+ const drop = /* @__PURE__ */ new Set();
43681
+ for (let i = 0; i < messages.length; i++) {
43682
+ if (drop.has(i)) continue;
43683
+ if (messages[i].contentType !== "reasoning") continue;
43684
+ let j2 = i;
43685
+ while (j2 + 1 < messages.length && messages[j2 + 1].contentType === "reasoning") {
43686
+ j2++;
43687
+ }
43688
+ const companion = messages[j2 + 1];
43689
+ const hasCompanion = companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call");
43690
+ if (!hasCompanion) {
43691
+ for (let k2 = i; k2 <= j2; k2++) drop.add(k2);
43692
+ }
43693
+ }
43694
+ if (drop.size === 0) return messages;
43695
+ return messages.filter((_2, i) => !drop.has(i));
43696
+ }
43677
43697
  function syncBlocks(messages, state) {
43678
43698
  const presentIds = new Set(messages.map((message) => message.id));
43679
43699
  const deactivated = [];
@@ -43730,7 +43750,7 @@ function defaultConfig(modelContextLimit, overrides = {}) {
43730
43750
  const base = {
43731
43751
  tiers: { enabled: true, tier2Trigger: 5, tier3Trigger: 10 },
43732
43752
  nudge: {
43733
- maxContextLimitPct: 0.55,
43753
+ maxContextLimitPct: 0.75,
43734
43754
  minContextLimitPct: 0.45,
43735
43755
  frequency: 5,
43736
43756
  iterationThreshold: 15,
@@ -43740,10 +43760,10 @@ function defaultConfig(modelContextLimit, overrides = {}) {
43740
43760
  growthCap: 5e4,
43741
43761
  minGrowthFloor: 2e4,
43742
43762
  minGrowthRatio: 0.45,
43743
- emergencyThresholdPct: 0.8
43763
+ emergencyThresholdPct: 0.95
43744
43764
  },
43745
43765
  promotionThreshold: 5,
43746
- truncate: { threshold: 1 },
43766
+ truncate: { threshold: 0.95 },
43747
43767
  compress: {
43748
43768
  minCompressRange: 5e3,
43749
43769
  maxSummaryLength: 2e4,
@@ -43773,6 +43793,11 @@ function validateConfig(config) {
43773
43793
  "nudge.minContextLimitPct must not exceed nudge.maxContextLimitPct"
43774
43794
  );
43775
43795
  }
43796
+ if (config.nudge.maxContextLimitPct > config.nudge.emergencyThresholdPct) {
43797
+ errors.push(
43798
+ "nudge.maxContextLimitPct must not exceed nudge.emergencyThresholdPct"
43799
+ );
43800
+ }
43776
43801
  if (config.promotionThreshold < 1) {
43777
43802
  errors.push("promotionThreshold must be >= 1");
43778
43803
  }
@@ -43805,6 +43830,18 @@ function parseBoundary(ref) {
43805
43830
  }
43806
43831
  return null;
43807
43832
  }
43833
+ var BoundaryNotFoundError = class extends Error {
43834
+ code = "BOUNDARY_NOT_FOUND";
43835
+ kind;
43836
+ endpoint;
43837
+ constructor(kind, endpoint, message) {
43838
+ super(message);
43839
+ this.name = "BoundaryNotFoundError";
43840
+ this.code = "BOUNDARY_NOT_FOUND";
43841
+ this.kind = kind;
43842
+ this.endpoint = endpoint;
43843
+ }
43844
+ };
43808
43845
  function resolveBoundaries(input) {
43809
43846
  const start = parseBoundary(input.startRef);
43810
43847
  const end = parseBoundary(input.endRef);
@@ -43817,13 +43854,8 @@ function resolveBoundaries(input) {
43817
43854
  input.messages.forEach(
43818
43855
  (message, index) => indexByRawId.set(message.id, index)
43819
43856
  );
43820
- let startIndex = resolveAnchorIndex(start, input.state, indexByRawId);
43821
- let endIndex = resolveAnchorIndex(end, input.state, indexByRawId);
43822
- if (startIndex === null || endIndex === null) {
43823
- throw new Error(
43824
- `Boundary not found in visible context (likely consumed by an existing block). startId="${input.startRef}", endId="${input.endRef}".`
43825
- );
43826
- }
43857
+ let startIndex = resolveAnchorIndex(start, input.state, indexByRawId, "start");
43858
+ let endIndex = resolveAnchorIndex(end, input.state, indexByRawId, "end");
43827
43859
  if (startIndex > endIndex) {
43828
43860
  [startIndex, endIndex] = [endIndex, startIndex];
43829
43861
  }
@@ -43854,16 +43886,51 @@ function resolveBoundaries(input) {
43854
43886
  protectedGaps
43855
43887
  };
43856
43888
  }
43857
- function resolveAnchorIndex(boundary, state, indexByRawId) {
43889
+ function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
43890
+ const label = endpoint === "start" ? "startId" : "endId";
43858
43891
  if (boundary.kind === "message") {
43859
43892
  const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
43860
- if (!rawId) return null;
43893
+ if (!rawId) {
43894
+ throw new BoundaryNotFoundError(
43895
+ "unknown",
43896
+ endpoint,
43897
+ `${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
43898
+ );
43899
+ }
43861
43900
  const index = indexByRawId.get(rawId);
43862
- return index === void 0 ? null : index;
43901
+ if (index === void 0) {
43902
+ throw new BoundaryNotFoundError(
43903
+ "consumed",
43904
+ endpoint,
43905
+ `${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
43906
+ );
43907
+ }
43908
+ return index;
43863
43909
  }
43864
43910
  const block = blockById(state, `b${boundary.numericId}`);
43865
- if (!block || !block.active) return null;
43866
- return earliestIndexOfIds(block.effectiveMessageIds, indexByRawId);
43911
+ if (!block) {
43912
+ throw new BoundaryNotFoundError(
43913
+ "unknown",
43914
+ endpoint,
43915
+ `${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
43916
+ );
43917
+ }
43918
+ if (!block.active) {
43919
+ throw new BoundaryNotFoundError(
43920
+ "consumed",
43921
+ endpoint,
43922
+ `${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
43923
+ );
43924
+ }
43925
+ const anchor = earliestIndexOfIds(block.effectiveMessageIds, indexByRawId);
43926
+ if (anchor === null) {
43927
+ throw new BoundaryNotFoundError(
43928
+ "consumed",
43929
+ endpoint,
43930
+ `${label}="b${boundary.numericId}" not found in visible context (block messages consumed by a higher-tier block).`
43931
+ );
43932
+ }
43933
+ return anchor;
43867
43934
  }
43868
43935
  function formatPaddedRef(index) {
43869
43936
  return `m${String(index).padStart(5, "0")}`;
@@ -44240,6 +44307,38 @@ function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan =
44240
44307
  }
44241
44308
  return { startIndex: newStartIndex, endIndex: newEndIndex };
44242
44309
  }
44310
+ function adjustBoundariesForReasoningPairs(startIndex, endIndex, messages) {
44311
+ if (startIndex > endIndex) {
44312
+ return { startIndex, endIndex };
44313
+ }
44314
+ let newStartIndex = startIndex;
44315
+ let newEndIndex = endIndex;
44316
+ for (let i = startIndex; i <= endIndex && i < messages.length; i++) {
44317
+ const msg2 = messages[i];
44318
+ if (!msg2) continue;
44319
+ if (msg2.contentType === "reasoning") {
44320
+ let j2 = i;
44321
+ while (j2 + 1 < messages.length && messages[j2 + 1].contentType === "reasoning") {
44322
+ j2++;
44323
+ }
44324
+ const companion = messages[j2 + 1];
44325
+ if (companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call") && j2 + 1 > newEndIndex) {
44326
+ newEndIndex = j2 + 1;
44327
+ }
44328
+ }
44329
+ if (msg2.role === "assistant" && (msg2.contentType === "text" || msg2.contentType === "tool-call")) {
44330
+ let k2 = i - 1;
44331
+ while (k2 >= 0 && messages[k2].contentType === "reasoning") {
44332
+ k2--;
44333
+ }
44334
+ const runStart = k2 + 1;
44335
+ if (runStart < i && runStart >= 0 && messages[runStart].contentType === "reasoning" && runStart < newStartIndex) {
44336
+ newStartIndex = runStart;
44337
+ }
44338
+ }
44339
+ }
44340
+ return { startIndex: newStartIndex, endIndex: newEndIndex };
44341
+ }
44243
44342
  function refNum(ref) {
44244
44343
  const n = parseInt(ref.slice(1), 10);
44245
44344
  return Number.isNaN(n) ? -1 : n;
@@ -44394,6 +44493,9 @@ function runPipeline(nodes, initial, ctx) {
44394
44493
  }
44395
44494
  return io2;
44396
44495
  }
44496
+ function rangeError(spec, message) {
44497
+ return `range ${spec.startRef}..${spec.endRef}: ${message}`;
44498
+ }
44397
44499
  function createCore(ports = {}) {
44398
44500
  const countTokens = ports.countTokens ?? defaultCountTokens;
44399
44501
  function applyCompression(input) {
@@ -44405,20 +44507,44 @@ function createCore(ports = {}) {
44405
44507
  const warnings = [];
44406
44508
  const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config, countTokens);
44407
44509
  const preExistingCoverage = collectCoverage(state);
44408
- const rangeIndexSets = [];
44510
+ const classifications = /* @__PURE__ */ new Map();
44511
+ const classificationErrors = [];
44512
+ const consumedRanges = [];
44409
44513
  for (const spec of input.ranges) {
44410
- let resolved;
44411
44514
  try {
44412
- resolved = resolveBoundaries({
44515
+ const resolved = resolveBoundaries({
44413
44516
  startRef: spec.startRef,
44414
44517
  endRef: spec.endRef,
44415
44518
  messages: input.messages,
44416
44519
  state
44417
44520
  });
44418
- } catch {
44419
- continue;
44521
+ classifications.set(spec, { status: "ok", resolved });
44522
+ } catch (error) {
44523
+ if (error instanceof BoundaryNotFoundError) {
44524
+ classifications.set(
44525
+ spec,
44526
+ error.kind === "unknown" ? { status: "unknown", error } : { status: "consumed", error }
44527
+ );
44528
+ if (error.kind === "consumed") {
44529
+ consumedRanges.push(spec);
44530
+ } else {
44531
+ classificationErrors.push(rangeError(spec, error.message));
44532
+ }
44533
+ } else {
44534
+ classifications.set(spec, {
44535
+ status: "invalid",
44536
+ error: error instanceof Error ? error : new Error(String(error))
44537
+ });
44538
+ classificationErrors.push(
44539
+ rangeError(spec, error instanceof Error ? error.message : String(error))
44540
+ );
44541
+ }
44420
44542
  }
44421
- const indices = resolved.messageIds.map(
44543
+ }
44544
+ const rangeIndexSets = [];
44545
+ for (const [spec, resolution] of classifications) {
44546
+ if (resolution.status !== "ok") continue;
44547
+ const indices = resolution.resolved.messageIds.map(
44422
44548
  (id) => input.messages.findIndex((m2) => m2.id === id)
44423
44549
  ).filter((i) => i >= 0);
44424
44550
  rangeIndexSets.push({ spec, indices });
@@ -44445,37 +44571,27 @@ function createCore(ports = {}) {
44445
44571
  if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
44446
44572
  let totalRangeChars = 0;
44447
44573
  let hasBlockBoundaryRange = false;
44448
- for (const spec of input.ranges) {
44449
- if (skipSpecs.has(spec)) continue;
44450
- let resolved;
44451
- try {
44452
- resolved = resolveBoundaries({
44453
- startRef: spec.startRef,
44454
- endRef: spec.endRef,
44455
- messages: input.messages,
44456
- state
44457
- });
44458
- } catch {
44459
- continue;
44460
- }
44461
- if (resolved.boundaryKind === "block") {
44574
+ let countedRanges = 0;
44575
+ for (const [spec, resolution] of classifications) {
44576
+ if (resolution.status !== "ok" || skipSpecs.has(spec)) continue;
44577
+ if (resolution.resolved.boundaryKind === "block") {
44462
44578
  hasBlockBoundaryRange = true;
44463
44579
  continue;
44464
44580
  }
44465
- for (const id of resolved.messageIds) {
44581
+ countedRanges++;
44582
+ for (const id of resolution.resolved.messageIds) {
44466
44583
  const msg2 = input.messages.find((m2) => m2.id === id);
44467
44584
  totalRangeChars += msg2?.text?.length ?? 0;
44468
44585
  }
44469
44586
  }
44470
44587
  if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
44588
+ const gateMessage = consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}); remaining compressible content ${totalRangeChars} chars < min ${input.config.compress.minCompressRange}. Nothing to do \u2014 run acp_status to see current compressible ranges.` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
44471
44589
  return {
44472
44590
  state: input.state,
44473
44591
  result: {
44474
44592
  blocksCreated: 0,
44475
44593
  tokensCompressed: 0,
44476
- errors: [
44477
- `Total compressible content too small (${totalRangeChars} chars across ${input.ranges.length} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`
44478
- ],
44594
+ errors: [gateMessage, ...classificationErrors],
44479
44595
  warnings: []
44480
44596
  }
44481
44597
  };
@@ -44483,6 +44599,18 @@ function createCore(ports = {}) {
44483
44599
  }
44484
44600
  for (const spec of input.ranges) {
44485
44601
  if (skipSpecs.has(spec)) continue;
44602
+ const resolution = classifications.get(spec);
44603
+ if (resolution === void 0) continue;
44604
+ if (resolution.status === "consumed") {
44605
+ warnings.push(
44606
+ `Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed (messages consumed by existing block(s)); nothing to compress.`
44607
+ );
44608
+ continue;
44609
+ }
44610
+ if (resolution.status === "unknown" || resolution.status === "invalid") {
44611
+ errors.push(rangeError(spec, resolution.error.message));
44612
+ continue;
44613
+ }
44486
44614
  try {
44487
44615
  const outcome = applySingleRange({
44488
44616
  spec,
@@ -44498,7 +44626,7 @@ function createCore(ports = {}) {
44498
44626
  tokensCompressed += outcome.tokens;
44499
44627
  warnings.push(...outcome.warnings);
44500
44628
  } catch (error) {
44501
- errors.push(error instanceof Error ? error.message : String(error));
44629
+ errors.push(rangeError(spec, error instanceof Error ? error.message : String(error)));
44502
44630
  }
44503
44631
  }
44504
44632
  state.stats.compressionCount += blocksCreated;
@@ -44706,7 +44834,7 @@ function applySingleRange(input) {
44706
44834
  messages: input.messages,
44707
44835
  state: input.state
44708
44836
  });
44709
- const rangeMessageIds = applyToolPairAdjustment(
44837
+ const rangeMessageIds = applyPairBoundaryAdjustments(
44710
44838
  resolved,
44711
44839
  input.messages
44712
44840
  );
@@ -44820,20 +44948,33 @@ function applySingleRange(input) {
44820
44948
  }
44821
44949
  return { tokens: compressedTokens, warnings };
44822
44950
  }
44823
- function applyToolPairAdjustment(resolved, messages) {
44951
+ function applyPairBoundaryAdjustments(resolved, messages) {
44824
44952
  if (resolved.boundaryKind === "block") {
44825
44953
  return resolved.messageIds;
44826
44954
  }
44827
- const adjusted = adjustBoundariesForToolPairs(
44828
- resolved.startIndex,
44829
- resolved.endIndex,
44830
- messages
44831
- );
44832
- if (adjusted.startIndex === resolved.startIndex && adjusted.endIndex === resolved.endIndex) {
44955
+ let startIndex = resolved.startIndex;
44956
+ let endIndex = resolved.endIndex;
44957
+ for (let pass = 0; pass < 2; pass++) {
44958
+ const reasoningAdjusted = adjustBoundariesForReasoningPairs(
44959
+ startIndex,
44960
+ endIndex,
44961
+ messages
44962
+ );
44963
+ const toolAdjusted = adjustBoundariesForToolPairs(
44964
+ reasoningAdjusted.startIndex,
44965
+ reasoningAdjusted.endIndex,
44966
+ messages
44967
+ );
44968
+ const changed = toolAdjusted.startIndex !== startIndex || toolAdjusted.endIndex !== endIndex;
44969
+ startIndex = toolAdjusted.startIndex;
44970
+ endIndex = toolAdjusted.endIndex;
44971
+ if (!changed) break;
44972
+ }
44973
+ if (startIndex === resolved.startIndex && endIndex === resolved.endIndex) {
44833
44974
  return resolved.messageIds;
44834
44975
  }
44835
44976
  const ids = [];
44836
- for (let i = adjusted.startIndex; i <= adjusted.endIndex; i++) {
44977
+ for (let i = startIndex; i <= endIndex; i++) {
44837
44978
  const msg2 = messages[i];
44838
44979
  if (msg2) ids.push(msg2.id);
44839
44980
  }
@@ -44933,6 +45074,7 @@ function decideNudge(input) {
44933
45074
  const limit = config.modelContextLimit;
44934
45075
  const usage = limit > 0 ? tokenCount / limit : 0;
44935
45076
  const nudgeGrowthTokens = resolveAdaptiveGrowth(limit, config.nudge);
45077
+ const overLimit = usage >= config.nudge.maxContextLimitPct;
44936
45078
  const emergencyOverride = usage >= config.nudge.emergencyThresholdPct;
44937
45079
  const baseline = state.nudge.lastPerMessageNudgeTokens;
44938
45080
  const hadPendingNudge = state.nudge.lastNudgeShownTokens > 0;
@@ -44949,7 +45091,7 @@ function decideNudge(input) {
44949
45091
  let injectedTier = null;
44950
45092
  let injectedReason = "";
44951
45093
  const growthReady = growthSinceReference >= growthFloor;
44952
- if (!emergencyOverride && growthReady) {
45094
+ if (!overLimit && growthReady) {
44953
45095
  for (const tier of [1, 2, 3]) {
44954
45096
  if (!config.tiers.enabled && tier > 1) break;
44955
45097
  const info = tiers[tier];
@@ -44961,11 +45103,26 @@ function decideNudge(input) {
44961
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)}%`;
44962
45104
  break;
44963
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;
45114
+ }
44964
45115
  }
44965
- const shouldInject = emergencyOverride || injectedTier !== null;
45116
+ const shouldInject = injectedTier !== null || overLimit && (rec?.recommendedRanges?.length ?? 0) > 0;
44966
45117
  let reason;
44967
- if (emergencyOverride) {
44968
- reason = `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}%`;
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)`;
44969
45126
  } else if (injectedTier !== null) {
44970
45127
  reason = injectedReason;
44971
45128
  } else {
@@ -45001,6 +45158,7 @@ function decideNudge(input) {
45001
45158
  nudgeGrowthTokens,
45002
45159
  growthFloor,
45003
45160
  hasPendingNudge: hasPendingNudge ? 1 : 0,
45161
+ overLimit: overLimit ? 1 : 0,
45004
45162
  emergencyOverride: emergencyOverride ? 1 : 0,
45005
45163
  pendingT1: tiers[1].pending,
45006
45164
  pendingT2: tiers[2].pending,
@@ -45172,12 +45330,22 @@ DROP:
45172
45330
  - Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
45173
45331
 
45174
45332
  SIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output \u2248 N \xD7 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;
45175
- var EFFICIENCY_NOTE = `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.
45333
+ var defaultPrompts = Object.freeze({
45334
+ compressPhilosophy: COMPRESS_PHILOSOPHY,
45335
+ howToCompressRules: HOW_TO_COMPRESS_RULES,
45336
+ tier2DistillRules: TIER2_DISTILL_RULES,
45337
+ tier3CondenseRules: TIER3_CONDENSE_RULES
45338
+ });
45339
+ function efficiencyNote(prompts) {
45340
+ 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.
45176
45341
 
45177
- ${COMPRESS_PHILOSOPHY}`;
45178
- var EMERGENCY_HEADER = `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
45342
+ ${prompts.compressPhilosophy}`;
45343
+ }
45344
+ function emergencyHeader(prompts) {
45345
+ return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
45179
45346
 
45180
- ${COMPRESS_PHILOSOPHY}`;
45347
+ ${prompts.compressPhilosophy}`;
45348
+ }
45181
45349
  function formatK(n) {
45182
45350
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
45183
45351
  return `${n}`;
@@ -45285,7 +45453,7 @@ function formatRanges(compressible, protectedRanges) {
45285
45453
  return `Compressible ranges (${merged.length}, oldest first):
45286
45454
  ${lines.join("\n")}`;
45287
45455
  }
45288
- function renderNudgeText(decision) {
45456
+ function renderNudgeText(decision, prompts = defaultPrompts) {
45289
45457
  const breakdownStr = formatBreakdown(decision.contextBreakdown);
45290
45458
  const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
45291
45459
  if (decision.tier !== null && decision.tier >= 2) {
@@ -45297,7 +45465,7 @@ function renderNudgeText(decision) {
45297
45465
  return {
45298
45466
  voice: "gentle",
45299
45467
  text: [
45300
- EFFICIENCY_NOTE,
45468
+ efficiencyNote(prompts),
45301
45469
  "",
45302
45470
  breakdownStr,
45303
45471
  "",
@@ -45306,22 +45474,22 @@ function renderNudgeText(decision) {
45306
45474
  blockList,
45307
45475
  `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
45308
45476
  "",
45309
- HOW_TO_COMPRESS_RULES,
45477
+ prompts.howToCompressRules,
45310
45478
  "",
45311
- isT2 ? TIER2_DISTILL_RULES : TIER3_CONDENSE_RULES
45479
+ isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
45312
45480
  ].join("\n")
45313
45481
  };
45314
45482
  }
45315
- const isEmergency = !!decision.breakdown?.emergencyOverride;
45483
+ const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
45316
45484
  if (isEmergency) {
45317
45485
  return {
45318
45486
  voice: "emergency",
45319
45487
  text: [
45320
- EMERGENCY_HEADER,
45488
+ emergencyHeader(prompts),
45321
45489
  "",
45322
45490
  breakdownStr,
45323
45491
  "",
45324
- HOW_TO_COMPRESS_RULES,
45492
+ prompts.howToCompressRules,
45325
45493
  "",
45326
45494
  `{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
45327
45495
  "Only use IDs from visible messages above. Compress older work first.",
@@ -45333,11 +45501,11 @@ function renderNudgeText(decision) {
45333
45501
  return {
45334
45502
  voice: "gentle",
45335
45503
  text: [
45336
- EFFICIENCY_NOTE,
45504
+ efficiencyNote(prompts),
45337
45505
  "",
45338
45506
  breakdownStr,
45339
45507
  "",
45340
- HOW_TO_COMPRESS_RULES,
45508
+ prompts.howToCompressRules,
45341
45509
  "",
45342
45510
  rangesStr,
45343
45511
  "",
@@ -46290,29 +46458,24 @@ function lookupContextLimit(model) {
46290
46458
  function resolveContextLimit(routes, upstreamUrl, model) {
46291
46459
  return resolveConfiguredContextLimit(routes, upstreamUrl, model) ?? lookupContextLimit(model);
46292
46460
  }
46293
- function resolveConfiguredContextLimit(routes, upstreamUrl, model) {
46294
- if (!model || !upstreamUrl) return void 0;
46461
+ function findRoute(routes, upstreamUrl) {
46462
+ if (!upstreamUrl) return void 0;
46295
46463
  let bestKey = "";
46296
46464
  for (const key of Object.keys(routes)) {
46297
46465
  if (upstreamUrl === key || upstreamUrl.startsWith(key + "/")) {
46298
46466
  if (key.length > bestKey.length) bestKey = key;
46299
46467
  }
46300
46468
  }
46301
- if (bestKey) {
46302
- const m2 = routes[bestKey].models?.[model];
46303
- if (m2?.context && m2.context > 0) return m2.context;
46304
- }
46469
+ return bestKey ? routes[bestKey] : void 0;
46470
+ }
46471
+ function resolveConfiguredContextLimit(routes, upstreamUrl, model) {
46472
+ if (!model || !upstreamUrl) return void 0;
46473
+ const m2 = findRoute(routes, upstreamUrl)?.models?.[model];
46474
+ if (m2?.context && m2.context > 0) return m2.context;
46305
46475
  return void 0;
46306
46476
  }
46307
46477
  function resolveCompressProtocol(routes, upstreamUrl) {
46308
- if (!upstreamUrl) return void 0;
46309
- let bestKey = "";
46310
- for (const key of Object.keys(routes)) {
46311
- if (upstreamUrl === key || upstreamUrl.startsWith(key + "/")) {
46312
- if (key.length > bestKey.length) bestKey = key;
46313
- }
46314
- }
46315
- return bestKey ? routes[bestKey].compressProtocol : void 0;
46478
+ return findRoute(routes, upstreamUrl)?.compressProtocol;
46316
46479
  }
46317
46480
  function loadRoutes(env = process.env) {
46318
46481
  const fileConfig = loadConfigFile();
@@ -46388,6 +46551,7 @@ function loadOptions(env = process.env) {
46388
46551
  modelContextLimit,
46389
46552
  kernelConfig: defaultConfig(modelContextLimit),
46390
46553
  compress: {
46554
+ ...fileConfig.compress ?? {},
46391
46555
  injectTool: (env.ACP_COMPRESS_TOOL ?? (fileConfig.compress?.injectTool === false ? "0" : "1")) !== "0",
46392
46556
  injectNudge: (env.ACP_COMPRESS_NUDGE ?? (fileConfig.compress?.injectNudge === false ? "0" : "1")) !== "0"
46393
46557
  },
@@ -46461,6 +46625,7 @@ function parseRouteEntry(v2) {
46461
46625
  const route = { models: obj.models };
46462
46626
  if (typeof obj.proxy === "string") route.proxy = obj.proxy;
46463
46627
  if (obj.compressProtocol === "marker" || obj.compressProtocol === "tools") route.compressProtocol = obj.compressProtocol;
46628
+ if (obj.compress) route.compress = obj.compress;
46464
46629
  return route;
46465
46630
  }
46466
46631
  if (v2 === null) return {};
@@ -46483,6 +46648,73 @@ function rejectLegacyRoute(key, value) {
46483
46648
  import http from "http";
46484
46649
  import fs5 from "fs";
46485
46650
 
46651
+ // src/compress-settings.ts
46652
+ function resolveContextLimitValue(raw, nativeLimit) {
46653
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) return Math.max(1, Math.floor(raw));
46654
+ if (typeof raw === "string") {
46655
+ const pct2 = /^(\d+(?:\.\d+)?)\s*%$/.exec(raw.trim());
46656
+ if (pct2) return Math.max(1, Math.floor(nativeLimit * Number(pct2[1]) / 100));
46657
+ const n = Number(raw);
46658
+ if (Number.isFinite(n) && n > 0) return Math.floor(n);
46659
+ }
46660
+ return Math.max(1, Math.floor(nativeLimit));
46661
+ }
46662
+ function mergeCompress(global2, provider, model) {
46663
+ const pick2 = (k2) => model?.[k2] ?? provider?.[k2] ?? global2?.[k2];
46664
+ return {
46665
+ modelContextLimit: pick2("modelContextLimit"),
46666
+ maxContextLimit: pick2("maxContextLimit"),
46667
+ emergencyThresholdPercent: pick2("emergencyThresholdPercent"),
46668
+ nudgeGrowthTokens: pick2("nudgeGrowthTokens"),
46669
+ preserveRecentMessages: pick2("preserveRecentMessages"),
46670
+ preserveRecentTokens: pick2("preserveRecentTokens"),
46671
+ minCompressRange: pick2("minCompressRange"),
46672
+ tiers: pick2("tiers")
46673
+ };
46674
+ }
46675
+ function resolveCompress(routes, upstreamUrl, model, global2) {
46676
+ const route = findRoute(routes, upstreamUrl);
46677
+ return mergeCompress(global2, route?.compress, model ? route?.models?.[model]?.compress : void 0);
46678
+ }
46679
+ function hasCompressSettings(s3) {
46680
+ return Object.values(s3).some((v2) => v2 !== void 0);
46681
+ }
46682
+ function applyCompressSettings(base, limit, s3) {
46683
+ const nudge = { ...base.nudge };
46684
+ const truncate = { ...base.truncate };
46685
+ if (s3.maxContextLimit !== void 0) nudge.maxContextLimitPct = parsePercent(s3.maxContextLimit);
46686
+ if (s3.emergencyThresholdPercent !== void 0) {
46687
+ const pct2 = parsePercent(s3.emergencyThresholdPercent);
46688
+ nudge.emergencyThresholdPct = pct2;
46689
+ truncate.threshold = pct2;
46690
+ }
46691
+ if (s3.nudgeGrowthTokens !== void 0 && s3.nudgeGrowthTokens > 0) {
46692
+ nudge.growthFloor = s3.nudgeGrowthTokens;
46693
+ nudge.growthCap = s3.nudgeGrowthTokens;
46694
+ }
46695
+ const tiers = { ...base.tiers };
46696
+ if (s3.tiers !== void 0) tiers.enabled = s3.tiers;
46697
+ return {
46698
+ ...base,
46699
+ modelContextLimit: limit,
46700
+ nudge,
46701
+ truncate,
46702
+ tiers,
46703
+ preserveRecentMessages: s3.preserveRecentMessages ?? base.preserveRecentMessages,
46704
+ preserveRecentTokens: s3.preserveRecentTokens ?? base.preserveRecentTokens,
46705
+ compress: {
46706
+ ...base.compress,
46707
+ minCompressRange: s3.minCompressRange ?? base.compress.minCompressRange
46708
+ }
46709
+ };
46710
+ }
46711
+ function parsePercent(v2) {
46712
+ if (typeof v2 === "number") return v2;
46713
+ const s3 = v2.trim();
46714
+ if (s3.endsWith("%")) return Number(s3.slice(0, -1)) / 100;
46715
+ return Number(s3);
46716
+ }
46717
+
46486
46718
  // src/registry.ts
46487
46719
  import { readFile, writeFile, mkdir } from "fs/promises";
46488
46720
  import { existsSync as existsSync2, statSync as statSync2 } from "fs";
@@ -48609,7 +48841,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48609
48841
  }
48610
48842
  }
48611
48843
  } else if (ev.kind === "tool_call") {
48612
- calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments });
48844
+ calls.push({ name: ev.name, callId: ev.callId, arguments: ev.arguments, passthrough: ev.passthrough });
48613
48845
  } else if (ev.kind === "usage") {
48614
48846
  usage = {
48615
48847
  inputTokens: ev.inputTokens,
@@ -48727,6 +48959,7 @@ async function* runCompressLoop(upstream, ctx, requestBody, requestOptions, adap
48727
48959
  }
48728
48960
  }
48729
48961
  for (const tc of realToolCalls) {
48962
+ if (tc.passthrough) continue;
48730
48963
  yield adapter.emitToolCall(tc);
48731
48964
  }
48732
48965
  const reRequest = proxyResults.length > 0 && realCalls === 0;
@@ -48969,16 +49202,21 @@ function createResponsesAdapter(textProtocol, projection) {
48969
49202
  } else if (type === "response.output_item.added") {
48970
49203
  const item = obj.item;
48971
49204
  if (item?.type === "function_call") {
48972
- const itemId = typeof item.id === "string" ? item.id : "";
48973
- pending.set(itemId, {
48974
- itemId,
48975
- callId: typeof item.call_id === "string" ? item.call_id : "",
48976
- name: typeof item.name === "string" ? item.name : "",
48977
- arguments: ""
48978
- });
49205
+ const fcName = typeof item.name === "string" ? item.name : "";
49206
+ if (PROXY_TOOL_NAMES.has(fcName)) {
49207
+ const itemId = typeof item.id === "string" ? item.id : "";
49208
+ pending.set(itemId, {
49209
+ itemId,
49210
+ callId: typeof item.call_id === "string" ? item.call_id : "",
49211
+ name: fcName,
49212
+ arguments: ""
49213
+ });
49214
+ } else {
49215
+ yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49216
+ }
48979
49217
  } else if (item?.type === "custom_tool_call") {
48980
49218
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
48981
- } else if (!suppressTextLifecycle) {
49219
+ } else if (item?.type !== "message" || !suppressTextLifecycle) {
48982
49220
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
48983
49221
  }
48984
49222
  } else if (type === "response.content_part.added" || type === "response.content_part.done" || type === "response.output_text.done") {
@@ -48995,11 +49233,13 @@ function createResponsesAdapter(textProtocol, projection) {
48995
49233
  const delta = typeof obj.delta === "string" ? obj.delta : "";
48996
49234
  const fc = pending.get(itemId);
48997
49235
  if (fc) fc.arguments += delta;
49236
+ else yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
48998
49237
  } else if (type === "response.function_call_arguments.done") {
48999
49238
  const itemId = typeof obj.item_id === "string" ? obj.item_id : "";
49000
49239
  const args = typeof obj.arguments === "string" ? obj.arguments : "";
49001
49240
  const fc = pending.get(itemId);
49002
49241
  if (fc && args) fc.arguments = args;
49242
+ else yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49003
49243
  } else if (type === "response.output_item.done") {
49004
49244
  const item = obj.item;
49005
49245
  if (item?.type === "function_call") {
@@ -49014,10 +49254,19 @@ function createResponsesAdapter(textProtocol, projection) {
49014
49254
  callId: fc.callId,
49015
49255
  arguments: fc.arguments
49016
49256
  };
49257
+ } else {
49258
+ yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49259
+ yield {
49260
+ kind: "tool_call",
49261
+ name: typeof item.name === "string" ? item.name : "",
49262
+ callId: typeof item.call_id === "string" ? item.call_id : "",
49263
+ arguments: typeof item.arguments === "string" ? item.arguments : "",
49264
+ passthrough: true
49265
+ };
49017
49266
  }
49018
49267
  } else if (item?.type === "custom_tool_call") {
49019
49268
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: false };
49020
- } else if (!suppressTextLifecycle) {
49269
+ } else if (item?.type !== "message" || !suppressTextLifecycle) {
49021
49270
  yield { kind: "meta", chunk: rawBuf, firstRoundOnly: true };
49022
49271
  }
49023
49272
  } else if (type === "response.completed") {
@@ -51415,8 +51664,9 @@ ${bodyBuffer.toString("utf8")}`);
51415
51664
  const model = parsed.model;
51416
51665
  if (model) {
51417
51666
  const embeddedUrl = route?.rewrittenUrl;
51418
- let limit = resolveContextLimit(opts.routes, embeddedUrl, model);
51419
- if (!limit && embeddedUrl) {
51667
+ const compress = resolveCompress(opts.routes, embeddedUrl, model, opts.compress);
51668
+ let native = resolveContextLimit(opts.routes, embeddedUrl, model);
51669
+ if (!native && embeddedUrl) {
51420
51670
  const host = (() => {
51421
51671
  try {
51422
51672
  return new URL(embeddedUrl).host;
@@ -51424,10 +51674,12 @@ ${bodyBuffer.toString("utf8")}`);
51424
51674
  return void 0;
51425
51675
  }
51426
51676
  })();
51427
- limit = await contextFromRegistry(model, host);
51677
+ native = await contextFromRegistry(model, host);
51428
51678
  }
51429
- if (limit && limit !== config.modelContextLimit) {
51430
- reqConfig = { ...config, modelContextLimit: limit };
51679
+ const limit = resolveContextLimitValue(compress.modelContextLimit, native ?? config.modelContextLimit);
51680
+ const tuned = hasCompressSettings(compress);
51681
+ if (tuned || limit !== config.modelContextLimit) {
51682
+ reqConfig = applyCompressSettings(config, limit, compress);
51431
51683
  }
51432
51684
  }
51433
51685
  }
@@ -51523,7 +51775,7 @@ function prepareAnthropic(parsed, req, opts, core, config, log2, session) {
51523
51775
  if (opts.compress.injectTool) {
51524
51776
  toolsOut = injectTool(parsed.tools);
51525
51777
  }
51526
- if (turn.nudge?.shouldInject) {
51778
+ if (opts.compress.injectNudge && turn.nudge?.shouldInject) {
51527
51779
  try {
51528
51780
  const rendered = renderNudgeText(turn.nudge);
51529
51781
  if (rendered.text) {
@@ -51575,7 +51827,7 @@ function prepareOpenai(parsed, req, opts, core, config, log2, session) {
51575
51827
  if (shouldInject) {
51576
51828
  toolsOut = injectOpenaiTool(parsed.tools);
51577
51829
  }
51578
- if (turn.nudge?.shouldInject && shouldInject) {
51830
+ if (opts.compress.injectNudge && turn.nudge?.shouldInject && shouldInject) {
51579
51831
  try {
51580
51832
  const rendered = renderNudgeText(turn.nudge);
51581
51833
  if (rendered.text) {
@@ -51642,7 +51894,7 @@ function prepareResponses(parsed, req, opts, core, config, log2, session, identi
51642
51894
  } else if (projection.systemParts.length > 0) {
51643
51895
  rebuiltInput = injectResponsesDeveloperMessage(rebuiltInput, projection.systemParts.join("\n\n---\n\n"));
51644
51896
  }
51645
- if (turn.nudge?.shouldInject && shouldInject) {
51897
+ if (opts.compress.injectNudge && turn.nudge?.shouldInject && shouldInject) {
51646
51898
  try {
51647
51899
  const rendered = renderNudgeText(turn.nudge);
51648
51900
  if (rendered.text) {
@@ -55725,7 +55977,7 @@ function stopProxy(handle2) {
55725
55977
  function runClient(cmd, args, env, deps) {
55726
55978
  const spawnImpl = deps?.spawnImpl ?? spawn;
55727
55979
  return new Promise((resolve, reject) => {
55728
- const child = spawnImpl(cmd, args, { stdio: "inherit", env });
55980
+ const child = spawnImpl(cmd, args, { stdio: "inherit", env, shell: process.platform === "win32" });
55729
55981
  child.on?.("error", (...rest) => reject(rest[0]));
55730
55982
  child.on?.("exit", (...rest) => {
55731
55983
  const code = rest[0];
@@ -55734,31 +55986,36 @@ function runClient(cmd, args, env, deps) {
55734
55986
  });
55735
55987
  });
55736
55988
  }
55737
- function isOnPath(name, env) {
55989
+ var PATH_EXTS = process.platform === "win32" ? [".cmd", ".bat", ".exe", ""] : [""];
55990
+ function resolveOnPath(name, env) {
55738
55991
  const p2 = env.PATH;
55739
- if (!p2) return false;
55740
- return p2.split(":").some((dir) => {
55741
- if (!dir) return false;
55742
- try {
55743
- const f2 = path9.join(dir, name);
55744
- return fs7.existsSync(f2) && fs7.statSync(f2).isFile();
55745
- } catch {
55746
- return false;
55992
+ if (!p2) return void 0;
55993
+ for (const dir of p2.split(path9.delimiter)) {
55994
+ if (!dir) continue;
55995
+ for (const ext of PATH_EXTS) {
55996
+ const f2 = path9.join(dir, name + ext);
55997
+ try {
55998
+ if (fs7.existsSync(f2) && fs7.statSync(f2).isFile()) return f2;
55999
+ } catch {
56000
+ }
55747
56001
  }
55748
- });
56002
+ }
56003
+ return void 0;
55749
56004
  }
55750
56005
  function resolveClientCommand(client, env) {
55751
56006
  if (client === "pi") {
55752
56007
  const piBin = env.PI_BIN?.trim();
55753
56008
  if (piBin) return { command: piBin, prefixArgs: [] };
55754
- if (isOnPath("pi", env)) return { command: "pi", prefixArgs: [] };
56009
+ const piResolved = resolveOnPath("pi", env);
56010
+ if (piResolved) return { command: piResolved, prefixArgs: [] };
55755
56011
  const cli = path9.join(
55756
56012
  os4.homedir(),
55757
56013
  ".pi/agent/npm/node_modules/@earendil-works/pi-coding-agent/dist/cli.js"
55758
56014
  );
55759
56015
  return { command: process.execPath, prefixArgs: [cli] };
55760
56016
  }
55761
- return { command: client, prefixArgs: [] };
56017
+ const resolved = resolveOnPath(client, env);
56018
+ return { command: resolved ?? client, prefixArgs: [] };
55762
56019
  }
55763
56020
  function parsePort(raw) {
55764
56021
  const port = raw && raw.trim() ? parseInt(raw, 10) : LAUNCHER_DEFAULT_PORT;