opencode-acp 1.16.0-pr.360.128 → 1.16.0-pr.374.126

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
@@ -890,7 +890,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
890
890
  "compress.modelMaxLimits",
891
891
  "compress.modelMinLimits",
892
892
  "compress.providers",
893
- "compress.contextLimitFallback",
894
893
  "compress.nudgeFrequency",
895
894
  "compress.minNudgeContextPercent",
896
895
  "compress.nudgeGrowthTokens",
@@ -914,7 +913,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
914
913
  "compress.reasoning",
915
914
  "compress.reasoning.drop",
916
915
  "compress.reasoning.threshold",
917
- "compress.completionReserveTokens",
918
916
  "gc",
919
917
  "gc.algorithm",
920
918
  "gc.promotionThreshold",
@@ -966,11 +964,7 @@ function validateConfigTypes(config) {
966
964
  errors.push({ key: "storagePath", expected: "string", actual: typeof config.storagePath });
967
965
  }
968
966
  if (config.allowSubAgents !== void 0 && typeof config.allowSubAgents !== "boolean") {
969
- errors.push({
970
- key: "allowSubAgents",
971
- expected: "boolean",
972
- actual: typeof config.allowSubAgents
973
- });
967
+ errors.push({ key: "allowSubAgents", expected: "boolean", actual: typeof config.allowSubAgents });
974
968
  }
975
969
  if (config.pruneNotification !== void 0) {
976
970
  const validValues = ["off", "minimal", "detailed"];
@@ -1299,20 +1293,6 @@ function validateConfigTypes(config) {
1299
1293
  }
1300
1294
  }
1301
1295
  }
1302
- if (compress.completionReserveTokens !== void 0 && typeof compress.completionReserveTokens !== "number") {
1303
- errors.push({
1304
- key: "compress.completionReserveTokens",
1305
- expected: "number",
1306
- actual: typeof compress.completionReserveTokens
1307
- });
1308
- }
1309
- if (typeof compress.completionReserveTokens === "number" && compress.completionReserveTokens < 0) {
1310
- errors.push({
1311
- key: "compress.completionReserveTokens",
1312
- expected: "non-negative number (>= 0)",
1313
- actual: `${compress.completionReserveTokens}`
1314
- });
1315
- }
1316
1296
  if (typeof compress.iterationNudgeThreshold === "number" && compress.iterationNudgeThreshold < 1) {
1317
1297
  errors.push({
1318
1298
  key: "compress.iterationNudgeThreshold",
@@ -1431,20 +1411,12 @@ function validateConfigTypes(config) {
1431
1411
  break;
1432
1412
  case "nudgeForce":
1433
1413
  if (value !== "strong" && value !== "soft") {
1434
- errors.push({
1435
- key,
1436
- expected: "'strong' | 'soft'",
1437
- actual: JSON.stringify(value)
1438
- });
1414
+ errors.push({ key, expected: "'strong' | 'soft'", actual: JSON.stringify(value) });
1439
1415
  }
1440
1416
  break;
1441
1417
  case "stringArray":
1442
1418
  if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
1443
- errors.push({
1444
- key,
1445
- expected: "string[]",
1446
- actual: JSON.stringify(value)
1447
- });
1419
+ errors.push({ key, expected: "string[]", actual: JSON.stringify(value) });
1448
1420
  }
1449
1421
  break;
1450
1422
  case "reasoningConfig":
@@ -1479,11 +1451,7 @@ function validateConfigTypes(config) {
1479
1451
  return;
1480
1452
  }
1481
1453
  if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
1482
- errors.push({
1483
- key: prefix,
1484
- expected: "CompressModelOverrides",
1485
- actual: typeof overrides
1486
- });
1454
+ errors.push({ key: prefix, expected: "CompressModelOverrides", actual: typeof overrides });
1487
1455
  return;
1488
1456
  }
1489
1457
  const model = overrides;
@@ -1516,11 +1484,7 @@ function validateConfigTypes(config) {
1516
1484
  for (const [providerId, providerValue] of Object.entries(providers)) {
1517
1485
  const prefix = `compress.providers.${providerId}`;
1518
1486
  if (typeof providerValue !== "object" || providerValue === null || Array.isArray(providerValue)) {
1519
- errors.push({
1520
- key: prefix,
1521
- expected: "ProviderOverrides",
1522
- actual: typeof providerValue
1523
- });
1487
+ errors.push({ key: prefix, expected: "ProviderOverrides", actual: typeof providerValue });
1524
1488
  continue;
1525
1489
  }
1526
1490
  const provider = providerValue;
@@ -1557,20 +1521,6 @@ function validateConfigTypes(config) {
1557
1521
  }
1558
1522
  };
1559
1523
  validateProviderOverrides(compress.providers);
1560
- if (compress.contextLimitFallback !== void 0 && typeof compress.contextLimitFallback !== "number") {
1561
- errors.push({
1562
- key: "compress.contextLimitFallback",
1563
- expected: "number",
1564
- actual: typeof compress.contextLimitFallback
1565
- });
1566
- }
1567
- if (typeof compress.contextLimitFallback === "number" && compress.contextLimitFallback < 0) {
1568
- errors.push({
1569
- key: "compress.contextLimitFallback",
1570
- expected: "non-negative number (0 disables the fallback)",
1571
- actual: `${compress.contextLimitFallback}`
1572
- });
1573
- }
1574
1524
  const validValues = ["ask", "allow", "deny"];
1575
1525
  if (compress.permission !== void 0 && !validValues.includes(compress.permission)) {
1576
1526
  errors.push({
@@ -1656,22 +1606,13 @@ function validateConfigTypes(config) {
1656
1606
  });
1657
1607
  } else {
1658
1608
  if (gc.batchCleanup.lowThreshold !== void 0) {
1659
- validateBatchThreshold(
1660
- "gc.batchCleanup.lowThreshold",
1661
- gc.batchCleanup.lowThreshold
1662
- );
1609
+ validateBatchThreshold("gc.batchCleanup.lowThreshold", gc.batchCleanup.lowThreshold);
1663
1610
  }
1664
1611
  if (gc.batchCleanup.highThreshold !== void 0) {
1665
- validateBatchThreshold(
1666
- "gc.batchCleanup.highThreshold",
1667
- gc.batchCleanup.highThreshold
1668
- );
1612
+ validateBatchThreshold("gc.batchCleanup.highThreshold", gc.batchCleanup.highThreshold);
1669
1613
  }
1670
1614
  if (gc.batchCleanup.forceThreshold !== void 0) {
1671
- validateBatchThreshold(
1672
- "gc.batchCleanup.forceThreshold",
1673
- gc.batchCleanup.forceThreshold
1674
- );
1615
+ validateBatchThreshold("gc.batchCleanup.forceThreshold", gc.batchCleanup.forceThreshold);
1675
1616
  }
1676
1617
  }
1677
1618
  }
@@ -1761,7 +1702,6 @@ var defaultConfig = {
1761
1702
  summaryBuffer: true,
1762
1703
  maxContextLimit: "80%",
1763
1704
  minContextLimit: "80%",
1764
- contextLimitFallback: 128e3,
1765
1705
  nudgeFrequency: 5,
1766
1706
  minNudgeContextPercent: 5,
1767
1707
  iterationNudgeThreshold: 15,
@@ -1932,7 +1872,6 @@ function mergeCompress(base, override) {
1932
1872
  modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
1933
1873
  modelMinLimits: override.modelMinLimits ?? base.modelMinLimits,
1934
1874
  providers: mergeProviderOverrides(base.providers, override.providers),
1935
- contextLimitFallback: override.contextLimitFallback ?? base.contextLimitFallback,
1936
1875
  nudgeFrequency: override.nudgeFrequency ?? base.nudgeFrequency,
1937
1876
  minNudgeContextPercent: override.minNudgeContextPercent ?? base.minNudgeContextPercent,
1938
1877
  nudgeGrowthTokens: override.nudgeGrowthTokens,
@@ -1956,8 +1895,7 @@ function mergeCompress(base, override) {
1956
1895
  reasoning: {
1957
1896
  drop: override.reasoning?.drop ?? base.reasoning?.drop ?? DEFAULT_COMPRESS_REASONING.drop,
1958
1897
  threshold: override.reasoning?.threshold ?? base.reasoning?.threshold ?? DEFAULT_COMPRESS_REASONING.threshold
1959
- },
1960
- completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
1898
+ }
1961
1899
  };
1962
1900
  }
1963
1901
  function mergeCommands(base, override) {
@@ -1995,9 +1933,10 @@ function deepCloneConfig(config) {
1995
1933
  ...provider,
1996
1934
  ...provider.models ? {
1997
1935
  models: Object.fromEntries(
1998
- Object.entries(provider.models).map(
1999
- ([modelId, model]) => [modelId, { ...model }]
2000
- )
1936
+ Object.entries(provider.models).map(([modelId, model]) => [
1937
+ modelId,
1938
+ { ...model }
1939
+ ])
2001
1940
  )
2002
1941
  } : {}
2003
1942
  }
@@ -2071,14 +2010,8 @@ function mergeLayer(config, data) {
2071
2010
  ],
2072
2011
  compress: mergeCompress(config.compress, data.compress),
2073
2012
  gc: mergeGC(config.gc, data.gc),
2074
- qualityGate: mergeQualityGate(
2075
- config.qualityGate,
2076
- data.qualityGate
2077
- ),
2078
- messageFilters: mergeMessageFilters(
2079
- config.messageFilters,
2080
- data.messageFilters
2081
- )
2013
+ qualityGate: mergeQualityGate(config.qualityGate, data.qualityGate),
2014
+ messageFilters: mergeMessageFilters(config.messageFilters, data.messageFilters)
2082
2015
  };
2083
2016
  }
2084
2017
  function scheduleParseWarning(ctx, title, message) {
@@ -2226,56 +2159,6 @@ var messageHasCompressAttempt = (message) => {
2226
2159
  const parts = Array.isArray(message.parts) ? message.parts : [];
2227
2160
  return parts.some((part) => part.type === "tool" && part.tool === "compress");
2228
2161
  };
2229
- var isCaptureOnlyCompress = (message) => {
2230
- if (!isMessageWithInfo(message)) {
2231
- return false;
2232
- }
2233
- if (message.info.role !== "assistant") {
2234
- return false;
2235
- }
2236
- const parts = Array.isArray(message.parts) ? message.parts : [];
2237
- let sawBoundary = false;
2238
- for (const part of parts) {
2239
- if (!(part.type === "tool" && part.tool === "compress")) {
2240
- continue;
2241
- }
2242
- for (const startId of extractCompressBoundaryIds(part.state?.input)) {
2243
- sawBoundary = true;
2244
- if (/^b\d+$/i.test(startId)) {
2245
- return false;
2246
- }
2247
- }
2248
- }
2249
- return sawBoundary;
2250
- };
2251
- function extractCompressBoundaryIds(rawInput) {
2252
- let content = [];
2253
- if (typeof rawInput === "string") {
2254
- try {
2255
- const parsed = JSON.parse(rawInput);
2256
- const c = parsed?.content;
2257
- content = Array.isArray(c) ? c : [];
2258
- } catch {
2259
- return [];
2260
- }
2261
- } else if (rawInput && typeof rawInput === "object") {
2262
- const c = rawInput.content;
2263
- content = Array.isArray(c) ? c : [];
2264
- }
2265
- const ids = [];
2266
- for (const entry of content) {
2267
- if (!entry || typeof entry !== "object") {
2268
- continue;
2269
- }
2270
- const { startId, endId } = entry;
2271
- for (const sid of [startId, endId]) {
2272
- if (typeof sid === "string" && sid.trim() !== "") {
2273
- ids.push(sid.trim());
2274
- }
2275
- }
2276
- }
2277
- return ids;
2278
- }
2279
2162
  var isIgnoredUserMessage = (message) => {
2280
2163
  if (!isMessageWithInfo(message)) {
2281
2164
  return false;
@@ -2752,16 +2635,6 @@ function resetOnCompaction(state) {
2752
2635
  nextRef: 1
2753
2636
  };
2754
2637
  }
2755
- function resolveEffectiveContextLimit(state, config) {
2756
- if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
2757
- return { limit: state.modelContextLimit, source: "model" };
2758
- }
2759
- const fallback = config.compress.contextLimitFallback;
2760
- if (typeof fallback === "number" && fallback > 0) {
2761
- return { limit: fallback, source: "fallback" };
2762
- }
2763
- return void 0;
2764
- }
2765
2638
 
2766
2639
  // lib/state/persistence.ts
2767
2640
  function getDefaultStorageDir() {
@@ -4633,24 +4506,6 @@ var SessionStateRegistry = class {
4633
4506
  hydrateModelLimitsFromClient(client) {
4634
4507
  return this.catalog.hydrateFromClient(client);
4635
4508
  }
4636
- // [FIX #346] The init-time seed (above) is fire-and-forget and races
4637
- // server readiness: in headless spawn+resume mode the provider-config
4638
- // call can fail before the server is up, leaving the catalog empty for
4639
- // the process's lifetime. During a request the server is guaranteed up
4640
- // (we are inside its pipeline), so on a catalog miss we retry hydration
4641
- // once per process before giving up (the fallback limit then applies).
4642
- // The in-flight promise (not a boolean) lets concurrent callers await the
4643
- // same hydration instead of skipping it.
4644
- lazyHydration;
4645
- async hydrateAndResolve(client, providerId, modelId) {
4646
- const existing = this.catalog.resolve(providerId, modelId);
4647
- if (existing !== void 0) {
4648
- return existing;
4649
- }
4650
- this.lazyHydration ??= this.catalog.hydrateFromClient(client);
4651
- await this.lazyHydration;
4652
- return this.catalog.resolve(providerId, modelId);
4653
- }
4654
4509
  get(sessionId) {
4655
4510
  return this.states.get(sessionId);
4656
4511
  }
@@ -4744,8 +4599,7 @@ function createSessionState() {
4744
4599
  modelID: void 0,
4745
4600
  systemPromptTokens: void 0,
4746
4601
  storageDir: void 0,
4747
- qualityGateRetryPending: false,
4748
- noContextLimitWarned: false
4602
+ qualityGateRetryPending: false
4749
4603
  };
4750
4604
  }
4751
4605
  function resetSessionState(state) {
@@ -4788,7 +4642,6 @@ function resetSessionState(state) {
4788
4642
  state.systemPromptTokens = void 0;
4789
4643
  state.storageDir = void 0;
4790
4644
  state.qualityGateRetryPending = false;
4791
- state.noContextLimitWarned = false;
4792
4645
  }
4793
4646
  async function ensureSessionInitialized(client, state, sessionId, logger, messages, config, projectDir) {
4794
4647
  if (state.sessionId === sessionId) {
@@ -6804,7 +6657,6 @@ function getModelInfo(messages) {
6804
6657
  };
6805
6658
  }
6806
6659
  function resolveContextTokenLimit(config, state, providerId, modelId, threshold) {
6807
- const effectiveLimit = resolveEffectiveContextLimit(state, config);
6808
6660
  const parseLimitValue = (limit) => {
6809
6661
  if (limit === void 0) {
6810
6662
  return void 0;
@@ -6812,7 +6664,7 @@ function resolveContextTokenLimit(config, state, providerId, modelId, threshold)
6812
6664
  if (typeof limit === "number") {
6813
6665
  return limit;
6814
6666
  }
6815
- if (!limit.endsWith("%") || effectiveLimit === void 0) {
6667
+ if (!limit.endsWith("%") || state.modelContextLimit === void 0) {
6816
6668
  return void 0;
6817
6669
  }
6818
6670
  const parsedPercent = parseFloat(limit.slice(0, -1));
@@ -6821,7 +6673,7 @@ function resolveContextTokenLimit(config, state, providerId, modelId, threshold)
6821
6673
  }
6822
6674
  const roundedPercent = Math.round(parsedPercent);
6823
6675
  const clampedPercent = Math.max(0, Math.min(100, roundedPercent));
6824
- return Math.round(clampedPercent / 100 * effectiveLimit.limit);
6676
+ return Math.round(clampedPercent / 100 * state.modelContextLimit);
6825
6677
  };
6826
6678
  if (threshold === "max") {
6827
6679
  const nestedLimit = resolveCompressOverrides(config, providerId, modelId).maxContextLimit;
@@ -6872,12 +6724,11 @@ function isContextOverLimits(config, state, providerId, modelId, messages) {
6872
6724
  if (!overMaxLimit) break;
6873
6725
  }
6874
6726
  }
6875
- const effectiveLimit = resolveEffectiveContextLimit(state, config);
6876
6727
  return {
6877
6728
  overMaxLimit,
6878
6729
  overMinLimit,
6879
6730
  currentTokens,
6880
- modelContextLimit: effectiveLimit?.limit
6731
+ modelContextLimit: state.modelContextLimit
6881
6732
  };
6882
6733
  }
6883
6734
  ensureBuiltinTriggerPolicyRegistered();
@@ -7080,6 +6931,7 @@ function estimateContextComposition(messages, state, protectedTools = [], protec
7080
6931
  let summaryTokens = 0;
7081
6932
  let messageTokens = 0;
7082
6933
  let protectedTokens = 0;
6934
+ let reasoningTokens = 0;
7083
6935
  const perMessage = [];
7084
6936
  const perTool = [];
7085
6937
  const perCode = [];
@@ -7133,6 +6985,10 @@ function estimateContextComposition(messages, state, protectedTools = [], protec
7133
6985
  summaryTokens += summaryPartTokens;
7134
6986
  toolTypeMap.set(toolName, (toolTypeMap.get(toolName) || 0) + toolPartTokens);
7135
6987
  if (!msgToolName) msgToolName = toolName;
6988
+ } else if (part.type === "reasoning" && typeof part.text === "string") {
6989
+ const tokens = Math.round(part.text.length / 4);
6990
+ msgTotal += tokens;
6991
+ reasoningTokens += tokens;
7136
6992
  }
7137
6993
  }
7138
6994
  if (isProtected && !isSummary) {
@@ -7160,7 +7016,8 @@ function estimateContextComposition(messages, state, protectedTools = [], protec
7160
7016
  textTokens: Math.max(0, messageTokens - codeTokens),
7161
7017
  systemTokens,
7162
7018
  protectedTokens,
7163
- total: systemTokens + toolTokens + summaryTokens + messageTokens,
7019
+ reasoningTokens,
7020
+ total: systemTokens + toolTokens + summaryTokens + messageTokens + reasoningTokens,
7164
7021
  largestRanges: perMessage.slice(0, 15),
7165
7022
  largestToolRanges: perTool.slice(0, 15),
7166
7023
  largestCodeRanges: perCode.slice(0, 5),
@@ -7183,10 +7040,13 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
7183
7040
  if (!ref) continue;
7184
7041
  const rn = parseInt(ref.slice(1), 10);
7185
7042
  if ((protectedTools.length > 0 || protectedFilePatterns.length > 0) && messageContainsProtectedTool(msg, protectedTools, protectedFilePatterns)) {
7186
- const tokens2 = Math.round(countMessageCharacters(msg) / 4);
7043
+ let tokens2 = 0;
7187
7044
  const tools = /* @__PURE__ */ new Set();
7188
7045
  for (const part of msg.parts || []) {
7189
- if (part.type !== "text" && part.type !== "reasoning") {
7046
+ if (part.type === "text" && typeof part.text === "string") {
7047
+ tokens2 += Math.round(part.text.length / 4);
7048
+ } else if (part.type !== "text" && part.type !== "reasoning") {
7049
+ tokens2 += Math.round(JSON.stringify(part).length / 4);
7190
7050
  const toolName = part?.tool;
7191
7051
  const callID = part?.callID;
7192
7052
  if (toolName && callID) {
@@ -7207,13 +7067,15 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
7207
7067
  protectedMsgInfo.push({ ref, refNum: rn, tokens: tokens2, tools: [...tools] });
7208
7068
  continue;
7209
7069
  }
7210
- const tokens = Math.round(countMessageCharacters(msg) / 4);
7070
+ let tokens = 0;
7211
7071
  let isTool = false;
7212
7072
  let hasMeaningfulPart = false;
7213
7073
  for (const part of msg.parts || []) {
7214
7074
  if (part.type === "text" && typeof part.text === "string") {
7075
+ tokens += Math.round(part.text.length / 4);
7215
7076
  if (part.text.trim().length > 0) hasMeaningfulPart = true;
7216
7077
  } else if (part.type !== "text" && part.type !== "reasoning") {
7078
+ tokens += Math.round(JSON.stringify(part).length / 4);
7217
7079
  isTool = true;
7218
7080
  hasMeaningfulPart = true;
7219
7081
  }
@@ -8037,11 +7899,8 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
8037
7899
  state.nudges.iterationNudgeAnchors.clear();
8038
7900
  state.nudges.lastNudgeShownTokens = void 0;
8039
7901
  state.nudges.lastToolOutputNudgeTokens = void 0;
8040
- const captureOnly = isCaptureOnlyCompress(lastCompressMsg);
8041
- if (!captureOnly) {
8042
- state.nudges.lastTier2NudgeTokens = currentTokens;
8043
- state.nudges.lastTier3NudgeTokens = currentTokens;
8044
- }
7902
+ state.nudges.lastTier2NudgeTokens = currentTokens;
7903
+ state.nudges.lastTier3NudgeTokens = currentTokens;
8045
7904
  const currentTurnHasSuccessfulCompress = messages.slice(currentTurnStart).some((m) => m.info.role === "assistant" && messageHasCompress(m));
8046
7905
  if (currentTurnHasSuccessfulCompress && wasNudgeTriggered && !state.nudges.compressBaselineSet) {
8047
7906
  const baseline = state.nudges.lastPerMessageNudgeTokens;
@@ -8353,7 +8212,7 @@ This is an efficiency nudge to compress early and keep context lean \u2014 not a
8353
8212
  ${COMPRESS_PHILOSOPHY}` : "";
8354
8213
  const sysPart = composition.systemTokens > 0 ? `${fmt(composition.systemTokens)} system (${pct2(composition.systemTokens)}%) | ` : "";
8355
8214
  let breakdown = `${efficiencyNote}
8356
- Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.toolTokens)}%) | ${fmt(composition.summaryTokens)} summaries (${pct2(composition.summaryTokens)}%) | ${fmt(composition.codeTokens)} code (${pct2(composition.codeTokens)}%) | ${fmt(plainTextTokens)} text (${pct2(plainTextTokens)}%)${growthStr}`;
8215
+ Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.toolTokens)}%) | ${fmt(composition.summaryTokens)} summaries (${pct2(composition.summaryTokens)}%) | ${fmt(composition.codeTokens)} code (${pct2(composition.codeTokens)}%) | ${fmt(plainTextTokens)} text (${pct2(plainTextTokens)}%) | ${fmt(composition.reasoningTokens)} reasoning (${pct2(composition.reasoningTokens)}%)${growthStr}`;
8357
8216
  const compressibleTokens = composition.total - composition.systemTokens - composition.protectedTokens - composition.summaryTokens;
8358
8217
  if (composition.protectedTokens > 0) {
8359
8218
  breakdown += `
@@ -8954,9 +8813,8 @@ function createDecompressTool(factoryCtx) {
8954
8813
  async execute(args, toolCtx) {
8955
8814
  const ctx = resolveToolContext(factoryCtx, toolCtx.sessionID);
8956
8815
  const { rawMessages } = await prepareDecompressSession(ctx, toolCtx);
8957
- const effectiveLimitBefore = resolveEffectiveContextLimit(ctx.state, ctx.config);
8958
- const contextUsageBefore = effectiveLimitBefore ? Math.round(
8959
- getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitBefore.limit * 100
8816
+ const contextUsageBefore = ctx.state.modelContextLimit ? Math.round(
8817
+ getCurrentTokenUsage(ctx.state, rawMessages) / ctx.state.modelContextLimit * 100
8960
8818
  ) : void 0;
8961
8819
  const resolved = resolveTargets(args, ctx.state, rawMessages, ctx.logger);
8962
8820
  if (!resolved.ok) {
@@ -9020,9 +8878,8 @@ function createDecompressTool(factoryCtx) {
9020
8878
  0,
9021
8879
  ctx.state.stats.totalPruneTokens - restoredTokens
9022
8880
  );
9023
- const effectiveLimitAfter = resolveEffectiveContextLimit(ctx.state, ctx.config);
9024
- const contextUsageAfter = effectiveLimitAfter ? Math.round(
9025
- getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitAfter.limit * 100
8881
+ const contextUsageAfter = ctx.state.modelContextLimit ? Math.round(
8882
+ getCurrentTokenUsage(ctx.state, rawMessages) / ctx.state.modelContextLimit * 100
9026
8883
  ) : void 0;
9027
8884
  await finalizeDecompressSession(ctx);
9028
8885
  const restoredContentPreview = buildRestoredContentPreview(
@@ -9243,6 +9100,7 @@ function collectVisibleMessages(rawMessages, ctx) {
9243
9100
  const ref = byRawId.get(msgId);
9244
9101
  if (!ref) return;
9245
9102
  let tokens = 0;
9103
+ let reasoning = 0;
9246
9104
  let toolName = "";
9247
9105
  for (const part of msg.parts || []) {
9248
9106
  if (part.type === "text" && typeof part.text === "string") {
@@ -9253,10 +9111,12 @@ function collectVisibleMessages(rawMessages, ctx) {
9253
9111
  if (!toolName) {
9254
9112
  toolName = part?.tool || "unknown";
9255
9113
  }
9114
+ } else if (part.type === "reasoning" && typeof part.text === "string") {
9115
+ reasoning += Math.round(part.text.length / 4);
9256
9116
  }
9257
9117
  }
9258
- if (tokens > 0) {
9259
- result.push({ ref, tokens, tool: toolName || "text", index: idx });
9118
+ if (tokens > 0 || reasoning > 0) {
9119
+ result.push({ ref, tokens, tool: toolName || "text", index: idx, reasoning });
9260
9120
  }
9261
9121
  });
9262
9122
  return {
@@ -9278,14 +9138,16 @@ function renderOverview(visibleMessages, summaryTokens, systemTokens, blocks, fe
9278
9138
  } else {
9279
9139
  const totalTool = visibleMessages.filter((m) => m.tool !== "text" && m.tool !== "step-finish").reduce((s, m) => s + m.tokens, 0);
9280
9140
  const totalText = visibleMessages.filter((m) => m.tool === "text").reduce((s, m) => s + m.tokens, 0);
9281
- const total = systemTokens + totalTool + totalText + summaryTokens;
9141
+ const totalReasoning = visibleMessages.reduce((s, m) => s + m.reasoning, 0);
9142
+ const total = systemTokens + totalTool + totalText + summaryTokens + totalReasoning;
9282
9143
  const sysPct = pct(systemTokens, total);
9283
9144
  const toolPct = pct(totalTool, total);
9284
9145
  const textPct = pct(totalText, total);
9285
9146
  const summaryPct = pct(summaryTokens, total);
9147
+ const reasoningPct = pct(totalReasoning, total);
9286
9148
  lines.push("CONTEXT BREAKDOWN");
9287
9149
  lines.push(
9288
- ` ${formatTokens(systemTokens)} system (${sysPct}%) | ${formatTokens(totalTool)} tool (${toolPct}%) | ${formatTokens(totalText)} text (${textPct}%) | ${formatTokens(summaryTokens)} summaries (${summaryPct}%)`
9150
+ ` ${formatTokens(systemTokens)} system (${sysPct}%) | ${formatTokens(totalTool)} tool (${toolPct}%) | ${formatTokens(totalText)} text (${textPct}%) | ${formatTokens(summaryTokens)} summaries (${summaryPct}%) | ${formatTokens(totalReasoning)} reasoning (${reasoningPct}%)`
9289
9151
  );
9290
9152
  const topTypes = Array.from(toolTypeMap.entries()).map(([tool6, tokens]) => ({ tool: tool6, tokens })).sort((a, b) => b.tokens - a.tokens).slice(0, 3);
9291
9153
  if (topTypes.length > 0) {
@@ -9396,22 +9258,23 @@ function renderUncompressedDrilldown(visibleMessages, toolFilter, sort, limit) {
9396
9258
  if (toolFilter) {
9397
9259
  filtered = filtered.filter((m) => m.tool === toolFilter);
9398
9260
  }
9261
+ const sizeOf = (m) => m.tokens + m.reasoning;
9399
9262
  if (sort === "time") {
9400
9263
  filtered.sort((a, b) => a.index - b.index);
9401
9264
  } else if (sort === "tool") {
9402
- filtered.sort((a, b) => a.tool.localeCompare(b.tool) || b.tokens - a.tokens);
9265
+ filtered.sort((a, b) => a.tool.localeCompare(b.tool) || sizeOf(b) - sizeOf(a));
9403
9266
  } else {
9404
- filtered.sort((a, b) => b.tokens - a.tokens);
9267
+ filtered.sort((a, b) => sizeOf(b) - sizeOf(a));
9405
9268
  }
9406
- const totalTokens = filtered.reduce((s, m) => s + m.tokens, 0);
9407
- const allTokens = visibleMessages.reduce((s, m) => s + m.tokens, 0);
9269
+ const totalTokens = filtered.reduce((s, m) => s + sizeOf(m), 0);
9270
+ const allTokens = visibleMessages.reduce((s, m) => s + sizeOf(m), 0);
9408
9271
  const header = toolFilter ? `UNCOMPRESSED \u2014 ${toolFilter}: ${formatTokens(totalTokens)} | ${filtered.length} msgs | ${pct(totalTokens, allTokens)}% of visible` : `UNCOMPRESSED \u2014 ${formatTokens(totalTokens)} | ${filtered.length} msgs`;
9409
9272
  lines.push(header);
9410
9273
  lines.push(`Sorted by ${sort}`);
9411
9274
  lines.push("");
9412
9275
  const shown = filtered.slice(0, limit);
9413
9276
  for (const m of shown) {
9414
- lines.push(` ${m.ref} (${formatTokens(m.tokens)}) ${m.tool}`);
9277
+ lines.push(` ${m.ref} (${formatTokens(sizeOf(m))}) ${m.tool}`);
9415
9278
  }
9416
9279
  if (filtered.length > shown.length) {
9417
9280
  lines.push("");
@@ -9679,7 +9542,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
9679
9542
  import { join as join4 } from "path";
9680
9543
  import { existsSync as existsSync4 } from "fs";
9681
9544
  import { homedir as homedir3 } from "os";
9682
- var LOG_VERSION = true ? "1.16.0-pr.360.128" : "dev";
9545
+ var LOG_VERSION = true ? "1.16.0-pr.374.126" : "dev";
9683
9546
  var LEVEL_RANK = {
9684
9547
  debug: 10,
9685
9548
  info: 20,
@@ -9975,13 +9838,14 @@ CONTEXT BREAKDOWN
9975
9838
 
9976
9839
  When context usage passes a threshold, the system appends a breakdown showing where your context tokens are spent:
9977
9840
 
9978
- Breakdown: 5.2K system (21%) | 12.3K tool (40%) | 3.1K summaries (10%) | 8.5K code (28%) | 6.5K text (22%)
9841
+ Breakdown: 4.2K system (21%) | 8.0K tool (40%) | 2.0K summaries (10%) | 2.6K code (13%) | 2.2K text (11%) | 1.0K reasoning (5%)
9979
9842
 
9980
9843
  - "system" = system prompt tokens (AGENTS.md, tool definitions \u2014 not compressible)
9981
9844
  - "tool" = tool call outputs (largest category \u2014 compress first when consumed)
9982
9845
  - "summaries" = existing compression block summaries (already compressed; do not re-compress standalone)
9983
9846
  - "code" = messages containing code blocks
9984
9847
  - "text" = plain text messages
9848
+ - "reasoning" = model thinking blocks (counted to match real API usage; freed when their message is compressed)
9985
9849
 
9986
9850
  Below the breakdown, the system lists compressible ranges grouped by conversation turn. All listed ranges should be compressed to summary format \u2014 the only exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct. Compress the largest ranges first when the current step no longer needs them.
9987
9851
 
@@ -10503,8 +10367,6 @@ var MIN_OUTPUT_TOKENS = 1e3;
10503
10367
  var KEEP_PREFIX_CHARS = 2e3;
10504
10368
  var KEEP_SUFFIX_CHARS = 2e3;
10505
10369
  var PROTECT_RECENT_MESSAGES = 3;
10506
- var OUTPUT_RESERVE_TOKENS = 16384;
10507
- var overheadErrorLogged = /* @__PURE__ */ new Set();
10508
10370
  function parseGcThreshold(threshold, modelContextLimit) {
10509
10371
  if (typeof threshold === "number") return threshold;
10510
10372
  const str = threshold ?? "100%";
@@ -10513,26 +10375,10 @@ function parseGcThreshold(threshold, modelContextLimit) {
10513
10375
  return modelContextLimit;
10514
10376
  }
10515
10377
  function truncateLargeToolOutputs(state, config, logger, messages) {
10516
- const effective = resolveEffectiveContextLimit(state, config);
10517
- if (!effective) return;
10378
+ if (!state.modelContextLimit) return;
10518
10379
  const currentTokens = getCurrentTokenUsage(state, messages);
10519
10380
  if (currentTokens === 0) return;
10520
- const configuredThreshold = parseGcThreshold(config.gc?.majorGcThresholdPercent, effective.limit);
10521
- const overhead = (state.systemPromptTokens ?? 0) + OUTPUT_RESERVE_TOKENS;
10522
- const threshold = Math.min(configuredThreshold, effective.limit - overhead);
10523
- if (threshold <= 0) {
10524
- const sessionKey = state.sessionId ?? "unknown";
10525
- if (!overheadErrorLogged.has(sessionKey)) {
10526
- overheadErrorLogged.add(sessionKey);
10527
- logger.error("ACP: model context window too small to fit overhead", {
10528
- session: state.sessionId,
10529
- limit: effective.limit,
10530
- contextLimitSource: effective.source,
10531
- overhead
10532
- });
10533
- }
10534
- return;
10535
- }
10381
+ const threshold = parseGcThreshold(config.gc?.majorGcThresholdPercent, state.modelContextLimit);
10536
10382
  if (currentTokens < threshold) return;
10537
10383
  const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES;
10538
10384
  const candidates = [];
@@ -10577,158 +10423,9 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
10577
10423
  truncatedCount,
10578
10424
  estimatedSavedTokens: Math.round(savedTokens),
10579
10425
  currentTokens,
10580
- threshold,
10581
- contextLimit: effective.limit,
10582
- contextLimitSource: effective.source
10583
- });
10584
- }
10585
- }
10586
-
10587
- // lib/messages/enforce-budget.ts
10588
- var DEFAULT_COMPLETION_RESERVE_TOKENS = 32768;
10589
- var TRUNCATION_MARKER2 = "[truncated for context space";
10590
- var KEEP_PREFIX_CHARS2 = 2e3;
10591
- var KEEP_SUFFIX_CHARS2 = 2e3;
10592
- var PROTECT_RECENT_MESSAGES2 = 3;
10593
- var MIN_CLEAR_TOKENS = 200;
10594
- function resolveContextWindow(state) {
10595
- if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
10596
- return state.modelContextLimit;
10597
- }
10598
- return void 0;
10599
- }
10600
- function estimateWireTokens(state, messages) {
10601
- const base = getCurrentTokenUsage(state, messages);
10602
- if (base > 0) {
10603
- let baseAssistant = -1;
10604
- for (let i = messages.length - 1; i >= 0; i--) {
10605
- if (messages[i].info.role !== "assistant") continue;
10606
- const tokens = messages[i].info.tokens;
10607
- if ((tokens?.input || 0) <= 0 && (tokens?.output || 0) <= 0) continue;
10608
- baseAssistant = i;
10609
- break;
10610
- }
10611
- if (baseAssistant >= 0) {
10612
- let additions = 0;
10613
- for (let i = baseAssistant + 1; i < messages.length; i++) {
10614
- additions += countAllMessageTokens(messages[i]);
10615
- }
10616
- return base + additions;
10617
- }
10618
- }
10619
- let total = 0;
10620
- for (const m of messages) total += countAllMessageTokens(m);
10621
- return total + (state.systemPromptTokens ?? 0);
10622
- }
10623
- function enforceContextBudget(state, config, logger, messages) {
10624
- const window = resolveContextWindow(state);
10625
- if (window === void 0) return void 0;
10626
- const configuredReserve = config.compress?.completionReserveTokens;
10627
- const reserve = typeof configuredReserve === "number" && Number.isFinite(configuredReserve) && configuredReserve >= 0 ? configuredReserve : DEFAULT_COMPLETION_RESERVE_TOKENS;
10628
- const budget = window - reserve;
10629
- if (budget <= 0) return void 0;
10630
- const estimatedTokens = estimateWireTokens(state, messages);
10631
- if (estimatedTokens <= budget) {
10632
- return {
10633
- applied: false,
10634
- window,
10635
- reserve,
10636
- budget,
10637
- estimatedTokens,
10638
- finalEstimate: estimatedTokens,
10639
- truncatedCount: 0,
10640
- clearedCount: 0
10641
- };
10642
- }
10643
- const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES2;
10644
- const protectedTools = new Set(config.compress?.protectedTools ?? []);
10645
- const candidates = [];
10646
- for (let mi = 0; mi < protectedIndex; mi++) {
10647
- if (mi === 0 && messages[mi].info.role === "user") continue;
10648
- const msg = messages[mi];
10649
- const parts = Array.isArray(msg.parts) ? msg.parts : [];
10650
- for (const part of parts) {
10651
- if (part?.type !== "tool") continue;
10652
- if (part.state?.status !== "completed") continue;
10653
- if (part.tool === "compress") continue;
10654
- if (protectedTools.has(part.tool)) continue;
10655
- const content = extractCompletedToolOutput(part);
10656
- if (content === void 0) continue;
10657
- if (content === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
10658
- const tokens = countTokens2(content);
10659
- if (tokens <= 0) continue;
10660
- candidates.push({ part, content, tokens, index: mi });
10661
- }
10662
- }
10663
- let saved = 0;
10664
- let truncatedCount = 0;
10665
- let clearedCount = 0;
10666
- const truncatable = candidates.filter(
10667
- (c) => !c.content.includes(TRUNCATION_MARKER2) && c.content.length > KEEP_PREFIX_CHARS2 + KEEP_SUFFIX_CHARS2
10668
- ).sort((a, b) => b.tokens - a.tokens);
10669
- for (const c of truncatable) {
10670
- if (estimatedTokens - saved <= budget) break;
10671
- const prefix = c.content.slice(0, KEEP_PREFIX_CHARS2);
10672
- const suffix = c.content.slice(-KEEP_SUFFIX_CHARS2);
10673
- const truncated = prefix + `
10674
-
10675
- ...${TRUNCATION_MARKER2} \u2014 original ~${c.tokens} tokens]...
10676
-
10677
- ` + suffix;
10678
- if (truncated.length >= c.content.length) continue;
10679
- c.part.state.output = truncated;
10680
- saved += c.tokens - countTokens2(truncated);
10681
- truncatedCount++;
10682
- }
10683
- if (estimatedTokens - saved > budget) {
10684
- const clearable = candidates.filter((c) => {
10685
- const out = extractCompletedToolOutput(c.part);
10686
- return out !== void 0 && out !== COMPACTED_TOOL_OUTPUT_PLACEHOLDER && countTokens2(out) > MIN_CLEAR_TOKENS;
10687
- }).sort((a, b) => a.index - b.index);
10688
- for (const c of clearable) {
10689
- if (estimatedTokens - saved <= budget) break;
10690
- const current = extractCompletedToolOutput(c.part);
10691
- if (current === void 0 || current === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
10692
- c.part.state.output = COMPACTED_TOOL_OUTPUT_PLACEHOLDER;
10693
- saved += countTokens2(current) - countTokens2(COMPACTED_TOOL_OUTPUT_PLACEHOLDER);
10694
- clearedCount++;
10695
- }
10696
- }
10697
- const finalEstimate = Math.max(0, estimatedTokens - saved);
10698
- if (truncatedCount > 0 || clearedCount > 0) {
10699
- logger.warn("Context budget guard: pruned tool outputs to fit the request", {
10700
- session: state.sessionId,
10701
- estimatedTokens: Math.round(estimatedTokens),
10702
- budget,
10703
- window,
10704
- reserve,
10705
- truncatedCount,
10706
- clearedCount,
10707
- estimatedSavedTokens: Math.round(saved),
10708
- finalEstimate: Math.round(finalEstimate)
10426
+ threshold
10709
10427
  });
10710
10428
  }
10711
- if (finalEstimate > budget) {
10712
- logger.warn(
10713
- "Context budget guard: still over budget after pruning all compressible tool outputs; the request may be rejected by the model",
10714
- {
10715
- session: state.sessionId,
10716
- finalEstimate: Math.round(finalEstimate),
10717
- budget,
10718
- window
10719
- }
10720
- );
10721
- }
10722
- return {
10723
- applied: truncatedCount > 0 || clearedCount > 0,
10724
- window,
10725
- reserve,
10726
- budget,
10727
- estimatedTokens,
10728
- finalEstimate,
10729
- truncatedCount,
10730
- clearedCount
10731
- };
10732
10429
  }
10733
10430
 
10734
10431
  // lib/commands/context.ts
@@ -11687,12 +11384,11 @@ function runBatchCleanup(state, config, logger, messages) {
11687
11384
  mergedCount: 0,
11688
11385
  savedTokens: 0
11689
11386
  };
11690
- const effective = resolveEffectiveContextLimit(state, config);
11691
- if (!effective) {
11387
+ if (!state.modelContextLimit || state.modelContextLimit <= 0) {
11692
11388
  return noop;
11693
11389
  }
11694
11390
  const currentTokens = getCurrentTokenUsage(state, messages);
11695
- if (currentTokens < effective.limit) {
11391
+ if (currentTokens < state.modelContextLimit) {
11696
11392
  return noop;
11697
11393
  }
11698
11394
  const maxMergedLength = config.gc.maxOldGenSummaryLength;
@@ -11709,8 +11405,7 @@ function runBatchCleanup(state, config, logger, messages) {
11709
11405
  mergedCount: result.mergedCount,
11710
11406
  savedTokens: result.savedTokens,
11711
11407
  currentTokens,
11712
- contextLimit: effective.limit,
11713
- contextLimitSource: effective.source
11408
+ contextLimit: state.modelContextLimit
11714
11409
  });
11715
11410
  return {
11716
11411
  tier: 3,
@@ -11744,6 +11439,11 @@ function createSystemPromptHandler(registry4, logger, config, prompts) {
11744
11439
  input.model?.limit?.context
11745
11440
  );
11746
11441
  const state = input.sessionID ? registry4.get(input.sessionID) : void 0;
11442
+ if (state && input.model?.limit?.context) {
11443
+ state.modelContextLimit = input.model.limit.context;
11444
+ state.modelProviderID = input.model?.providerID;
11445
+ state.modelID = input.model?.id;
11446
+ }
11747
11447
  if (!state || state.isSubAgent && !config.allowSubAgents) {
11748
11448
  return;
11749
11449
  }
@@ -11752,23 +11452,6 @@ function createSystemPromptHandler(registry4, logger, config, prompts) {
11752
11452
  logger.info("Skipping DCP system prompt injection for internal agent");
11753
11453
  return;
11754
11454
  }
11755
- if (input.model?.limit?.context) {
11756
- const limit = input.model.limit.context;
11757
- const providerID = input.model?.providerID;
11758
- const modelID = input.model?.id;
11759
- const changed = state.modelContextLimit !== limit || providerID !== void 0 && state.modelProviderID !== providerID || modelID !== void 0 && state.modelID !== modelID;
11760
- state.modelContextLimit = limit;
11761
- if (providerID !== void 0) {
11762
- state.modelProviderID = providerID;
11763
- }
11764
- if (modelID !== void 0) {
11765
- state.modelID = modelID;
11766
- }
11767
- if (changed) {
11768
- saveSessionState(state, logger).catch(() => {
11769
- });
11770
- }
11771
- }
11772
11455
  const effectivePermission = compressPermission(state, config);
11773
11456
  if (effectivePermission === "deny") {
11774
11457
  return;
@@ -11813,17 +11496,10 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11813
11496
  config
11814
11497
  );
11815
11498
  const requestModel = lastUserMessage.info.model;
11816
- let requestModelLimit = registry4.resolveModelLimit(
11499
+ const requestModelLimit = registry4.resolveModelLimit(
11817
11500
  requestModel?.providerID,
11818
11501
  requestModel?.modelID
11819
11502
  );
11820
- if (requestModelLimit === void 0 && requestModel?.providerID && requestModel?.modelID) {
11821
- requestModelLimit = await registry4.hydrateAndResolve(
11822
- client,
11823
- requestModel.providerID,
11824
- requestModel.modelID
11825
- );
11826
- }
11827
11503
  const prevModelID = state.modelID;
11828
11504
  if (requestModelLimit !== void 0) {
11829
11505
  state.modelContextLimit = requestModelLimit;
@@ -11853,16 +11529,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11853
11529
  });
11854
11530
  }
11855
11531
  await updatePerTurnState(state, logger, messages);
11856
- if (state.modelContextLimit === void 0 && !state.noContextLimitWarned && requestModel?.providerID && requestModel?.modelID && registry4.resolveModelLimit(requestModel.providerID, requestModel.modelID) === void 0) {
11857
- state.noContextLimitWarned = true;
11858
- logger.warn(
11859
- 'Model reports no context window and the catalog has no entry for it; all percentage thresholds (min/max/emergency, GC) and the context-budget guard are disabled. Set the model limit in opencode.json (e.g. "limit": {"context": 262144, "output": 16384}) to enable them (also fixes the 32000 max_tokens fallback); an absolute compress.maxContextLimit in acp.jsonc only enables proactive nudges, not the guard.',
11860
- {
11861
- session: state.sessionId,
11862
- model: `${requestModel.providerID}/${requestModel.modelID}`
11863
- }
11864
- );
11865
- }
11866
11532
  }
11867
11533
  syncCompressPermissionState(state, config, hostPermissions, output.messages);
11868
11534
  if (state.isSubAgent && !config.allowSubAgents) {
@@ -11888,11 +11554,10 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11888
11554
  }
11889
11555
  }
11890
11556
  ensureBuiltinFiltersRegistered();
11891
- const effectiveLimit = resolveEffectiveContextLimit(state, config);
11892
11557
  applyMessageFilters(output.messages, config.messageFilters, logger, {
11893
11558
  sessionId: state.sessionId ?? "",
11894
11559
  isSubAgent: state.isSubAgent,
11895
- modelContextLimit: effectiveLimit?.limit
11560
+ modelContextLimit: state.modelContextLimit
11896
11561
  });
11897
11562
  cacheSystemPromptTokens(state, output.messages);
11898
11563
  assignMessageRefs(state, output.messages);
@@ -11912,7 +11577,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11912
11577
  const prePruneTokens = getCurrentTokenUsage(state, output.messages);
11913
11578
  prune(state, logger, config, output.messages);
11914
11579
  truncateLargeToolOutputs(state, config, logger, output.messages);
11915
- enforceContextBudget(state, config, logger, output.messages);
11916
11580
  hideConsumedCompressCalls(state, output.messages);
11917
11581
  assignMessageRefs(state, output.messages);
11918
11582
  const compressionPriorities = buildPriorityMap(config, state, output.messages);
@@ -11944,31 +11608,14 @@ ${text}`);
11944
11608
  stripStaleMetadata(output.messages);
11945
11609
  dropEmptyMessages(output.messages);
11946
11610
  const postTokens = getCurrentTokenUsage(state, output.messages);
11947
- if (postTokens !== void 0 && effectiveLimit) {
11948
- const budget = effectiveLimit.limit - (state.systemPromptTokens ?? 0) - OUTPUT_RESERVE_TOKENS;
11949
- if (postTokens > budget) {
11950
- logger.error(
11951
- "ACP hard guard: context exceeds model budget after in-flight reduction",
11952
- {
11953
- session: state.sessionId,
11954
- postTokens,
11955
- budget,
11956
- contextLimit: effectiveLimit.limit,
11957
- contextLimitSource: effectiveLimit.source,
11958
- hint: "request will likely be rejected; run /compact or start a new session"
11959
- }
11960
- );
11961
- }
11962
- }
11963
11611
  logger.info("Chat transform complete", {
11964
11612
  session: state.sessionId,
11965
11613
  model: state.modelID,
11966
11614
  messages: output.messages.length,
11967
11615
  prePruneTokens,
11968
11616
  postTokens,
11969
- contextLimit: effectiveLimit?.limit,
11970
- contextLimitSource: effectiveLimit?.source,
11971
- usagePct: postTokens !== void 0 && effectiveLimit ? `${(postTokens / effectiveLimit.limit * 100).toFixed(1)}%` : void 0,
11617
+ contextLimit: state.modelContextLimit,
11618
+ usagePct: postTokens !== void 0 && state.modelContextLimit ? `${(postTokens / state.modelContextLimit * 100).toFixed(1)}%` : void 0,
11972
11619
  nudged: state.nudges.shouldInjectThisTurn
11973
11620
  });
11974
11621
  if (state.sessionId) {
@@ -12000,7 +11647,12 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
12000
11647
  path: { id: input.sessionID }
12001
11648
  });
12002
11649
  const messages = filterMessages(messagesResponse.data || messagesResponse);
12003
- const state = await registry4.getOrCreate(client, input.sessionID, messages, config);
11650
+ const state = await registry4.getOrCreate(
11651
+ client,
11652
+ input.sessionID,
11653
+ messages,
11654
+ config
11655
+ );
12004
11656
  syncCompressPermissionState(state, config, hostPermissions, messages);
12005
11657
  const commandCtx = {
12006
11658
  client,
@@ -12014,7 +11666,7 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
12014
11666
  const sub = input.arguments?.trim().toLowerCase();
12015
11667
  if (sub === "stats" || sub === "status" || sub === "") {
12016
11668
  await handleStatsCommand(commandCtx);
12017
- return;
11669
+ throw new Error("__DCP_CONTEXT_HANDLED__");
12018
11670
  }
12019
11671
  if (sub === "export" || sub.startsWith("export ")) {
12020
11672
  const exportArgs = input.arguments?.trim().slice("export".length).trim() || "";
@@ -12022,10 +11674,17 @@ function createCommandExecuteHandler(client, registry4, logger, config, workingD
12022
11674
  throw new Error("__DCP_CONTEXT_HANDLED__");
12023
11675
  }
12024
11676
  if (sub === "help") {
12025
- await sendIgnoredMessage(client, input.sessionID, buildHelpText(), {}, logger);
11677
+ await sendIgnoredMessage(
11678
+ client,
11679
+ input.sessionID,
11680
+ buildHelpText(),
11681
+ {},
11682
+ logger
11683
+ );
12026
11684
  throw new Error("__DCP_CONTEXT_HANDLED__");
12027
11685
  }
12028
11686
  await handleContextCommand(commandCtx);
11687
+ throw new Error("__DCP_CONTEXT_HANDLED__");
12029
11688
  }
12030
11689
  };
12031
11690
  }
@@ -12096,7 +11755,9 @@ function createEventHandler(registry4, logger) {
12096
11755
  return;
12097
11756
  }
12098
11757
  if (typeof part.callID === "string" && typeof part.messageID === "string") {
12099
- timing.startsByCallId.delete(buildCompressionTimingKey(part.messageID, part.callID));
11758
+ timing.startsByCallId.delete(
11759
+ buildCompressionTimingKey(part.messageID, part.callID)
11760
+ );
12100
11761
  }
12101
11762
  };
12102
11763
  }
@@ -12373,7 +12034,7 @@ var server = (async (ctx) => {
12373
12034
  }
12374
12035
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
12375
12036
  logger.info("ACP plugin initialized", {
12376
- version: true ? "1.16.0-pr.360.128" : "dev",
12037
+ version: true ? "1.16.0-pr.374.126" : "dev",
12377
12038
  workspace: ctx.directory,
12378
12039
  logLevel: logger.level,
12379
12040
  debug: config.debug,