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 +93 -432
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/decompress.d.ts.map +1 -1
- package/dist/lib/compress/status.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +1 -16
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/gc/merge.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts +1 -0
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/messages/query.d.ts +0 -16
- package/dist/lib/messages/query.d.ts.map +1 -1
- package/dist/lib/messages/truncate-tools.d.ts +0 -1
- package/dist/lib/messages/truncate-tools.d.ts.map +1 -1
- package/dist/lib/prompts/system.d.ts +1 -1
- package/dist/lib/prompts/system.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts +0 -2
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +0 -6
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts +0 -16
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/lib/messages/enforce-budget.d.ts +0 -65
- package/dist/lib/messages/enforce-budget.d.ts.map +0 -1
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
|
-
|
|
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
|
-
|
|
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("%") ||
|
|
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 *
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
7043
|
+
let tokens2 = 0;
|
|
7187
7044
|
const tools = /* @__PURE__ */ new Set();
|
|
7188
7045
|
for (const part of msg.parts || []) {
|
|
7189
|
-
if (part.type
|
|
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
|
-
|
|
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
|
-
|
|
8041
|
-
|
|
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
|
|
8958
|
-
|
|
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
|
|
9024
|
-
|
|
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
|
|
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
|
|
9265
|
+
filtered.sort((a, b) => a.tool.localeCompare(b.tool) || sizeOf(b) - sizeOf(a));
|
|
9403
9266
|
} else {
|
|
9404
|
-
filtered.sort((a, b) => b
|
|
9267
|
+
filtered.sort((a, b) => sizeOf(b) - sizeOf(a));
|
|
9405
9268
|
}
|
|
9406
|
-
const totalTokens = filtered.reduce((s, m) => s + m
|
|
9407
|
-
const allTokens = visibleMessages.reduce((s, m) => s + m
|
|
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
|
|
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.
|
|
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:
|
|
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
|
-
|
|
10517
|
-
if (!effective) return;
|
|
10378
|
+
if (!state.modelContextLimit) return;
|
|
10518
10379
|
const currentTokens = getCurrentTokenUsage(state, messages);
|
|
10519
10380
|
if (currentTokens === 0) return;
|
|
10520
|
-
const
|
|
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
|
-
|
|
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 <
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
11970
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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,
|