opencode-acp 1.14.25-pr.350.76 → 1.14.25-pr.351.83
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/README.md +6 -5
- package/dist/index.js +165 -184
- package/dist/index.js.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +19 -6
- package/dist/lib/config.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 +27 -1
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- 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/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/README.md
CHANGED
|
@@ -324,11 +324,12 @@ Each level overrides the previous, so project settings take priority over global
|
|
|
324
324
|
// Soft upper threshold: above this, ACP keeps injecting strong
|
|
325
325
|
// compression nudges (based on nudgeFrequency), so compression is
|
|
326
326
|
// much more likely. Accepts: number or "X%" of model context window.
|
|
327
|
-
"maxContextLimit": "
|
|
328
|
-
// Soft lower threshold for reminder nudges: below this,
|
|
329
|
-
// reminders are off (compression less likely). At/above this,
|
|
330
|
-
// are on.
|
|
331
|
-
|
|
327
|
+
"maxContextLimit": "80%",
|
|
328
|
+
// Soft lower threshold for turn/iteration reminder nudges: below this,
|
|
329
|
+
// those reminders are off (compression less likely). At/above this, they
|
|
330
|
+
// are on. Growth nudges have their own floor: minNudgeContextPercent.
|
|
331
|
+
// Accepts: number or "X%" of model context window.
|
|
332
|
+
"minContextLimit": "80%",
|
|
332
333
|
// Optional per-model override for maxContextLimit by providerID/modelID.
|
|
333
334
|
// If present, this wins over the global maxContextLimit.
|
|
334
335
|
// Accepts: number or "X%".
|
package/dist/index.js
CHANGED
|
@@ -888,6 +888,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
888
888
|
"compress.minContextLimit",
|
|
889
889
|
"compress.modelMaxLimits",
|
|
890
890
|
"compress.modelMinLimits",
|
|
891
|
+
"compress.providers",
|
|
891
892
|
"compress.nudgeFrequency",
|
|
892
893
|
"compress.minNudgeContextPercent",
|
|
893
894
|
"compress.nudgeGrowthTokens",
|
|
@@ -908,7 +909,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
908
909
|
"compress.preserveRecentMessages",
|
|
909
910
|
"compress.preserveRecentTokens",
|
|
910
911
|
"compress.preserveLastUserMessage",
|
|
911
|
-
"compress.completionReserveTokens",
|
|
912
912
|
"gc",
|
|
913
913
|
"gc.algorithm",
|
|
914
914
|
"gc.promotionThreshold",
|
|
@@ -932,7 +932,7 @@ function getConfigKeyPaths(obj, prefix = "") {
|
|
|
932
932
|
for (const key of Object.keys(obj)) {
|
|
933
933
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
934
934
|
keys.push(fullKey);
|
|
935
|
-
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "messageFilters.filters") {
|
|
935
|
+
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "compress.providers" || fullKey === "messageFilters.filters") {
|
|
936
936
|
continue;
|
|
937
937
|
}
|
|
938
938
|
if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
|
@@ -1262,20 +1262,6 @@ function validateConfigTypes(config) {
|
|
|
1262
1262
|
actual: typeof compress.preserveLastUserMessage
|
|
1263
1263
|
});
|
|
1264
1264
|
}
|
|
1265
|
-
if (compress.completionReserveTokens !== void 0 && typeof compress.completionReserveTokens !== "number") {
|
|
1266
|
-
errors.push({
|
|
1267
|
-
key: "compress.completionReserveTokens",
|
|
1268
|
-
expected: "number",
|
|
1269
|
-
actual: typeof compress.completionReserveTokens
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
if (typeof compress.completionReserveTokens === "number" && compress.completionReserveTokens < 0) {
|
|
1273
|
-
errors.push({
|
|
1274
|
-
key: "compress.completionReserveTokens",
|
|
1275
|
-
expected: "non-negative number (>= 0)",
|
|
1276
|
-
actual: `${compress.completionReserveTokens}`
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
1265
|
if (typeof compress.iterationNudgeThreshold === "number" && compress.iterationNudgeThreshold < 1) {
|
|
1280
1266
|
errors.push({
|
|
1281
1267
|
key: "compress.iterationNudgeThreshold",
|
|
@@ -1326,6 +1312,85 @@ function validateConfigTypes(config) {
|
|
|
1326
1312
|
}
|
|
1327
1313
|
validateModelLimits("compress.modelMaxLimits", compress.modelMaxLimits);
|
|
1328
1314
|
validateModelLimits("compress.modelMinLimits", compress.modelMinLimits);
|
|
1315
|
+
const validatePercentField = (key, value) => {
|
|
1316
|
+
if (value === void 0) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 100) {
|
|
1320
|
+
errors.push({
|
|
1321
|
+
key,
|
|
1322
|
+
expected: "number (0-100)",
|
|
1323
|
+
actual: JSON.stringify(value)
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
const validateModelOverrides = (prefix, overrides) => {
|
|
1328
|
+
if (overrides === void 0) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
|
|
1332
|
+
errors.push({ key: prefix, expected: "CompressModelOverrides", actual: typeof overrides });
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const model = overrides;
|
|
1336
|
+
for (const field of Object.keys(model)) {
|
|
1337
|
+
if (field !== "minNudgeContextPercent") {
|
|
1338
|
+
errors.push({
|
|
1339
|
+
key: `${prefix}.${field}`,
|
|
1340
|
+
expected: "known override field (minNudgeContextPercent)",
|
|
1341
|
+
actual: "unknown field"
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
validatePercentField(`${prefix}.minNudgeContextPercent`, model.minNudgeContextPercent);
|
|
1346
|
+
};
|
|
1347
|
+
const validateProviderOverrides = (providers) => {
|
|
1348
|
+
if (providers === void 0) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (typeof providers !== "object" || providers === null || Array.isArray(providers)) {
|
|
1352
|
+
errors.push({
|
|
1353
|
+
key: "compress.providers",
|
|
1354
|
+
expected: "Record<string, ProviderOverrides>",
|
|
1355
|
+
actual: typeof providers
|
|
1356
|
+
});
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
for (const [providerId, providerValue] of Object.entries(providers)) {
|
|
1360
|
+
const prefix = `compress.providers.${providerId}`;
|
|
1361
|
+
if (typeof providerValue !== "object" || providerValue === null || Array.isArray(providerValue)) {
|
|
1362
|
+
errors.push({ key: prefix, expected: "ProviderOverrides", actual: typeof providerValue });
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
const provider = providerValue;
|
|
1366
|
+
for (const field of Object.keys(provider)) {
|
|
1367
|
+
if (field !== "minNudgeContextPercent" && field !== "models") {
|
|
1368
|
+
errors.push({
|
|
1369
|
+
key: `${prefix}.${field}`,
|
|
1370
|
+
expected: "known override field (minNudgeContextPercent, models)",
|
|
1371
|
+
actual: "unknown field"
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
validatePercentField(`${prefix}.minNudgeContextPercent`, provider.minNudgeContextPercent);
|
|
1376
|
+
const models = provider.models;
|
|
1377
|
+
if (models === void 0) {
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
if (typeof models !== "object" || models === null || Array.isArray(models)) {
|
|
1381
|
+
errors.push({
|
|
1382
|
+
key: `${prefix}.models`,
|
|
1383
|
+
expected: "Record<string, ModelOverrides>",
|
|
1384
|
+
actual: typeof models
|
|
1385
|
+
});
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
for (const [modelId, modelValue] of Object.entries(models)) {
|
|
1389
|
+
validateModelOverrides(`${prefix}.models.${modelId}`, modelValue);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
validateProviderOverrides(compress.providers);
|
|
1329
1394
|
const validValues = ["ask", "allow", "deny"];
|
|
1330
1395
|
if (compress.permission !== void 0 && !validValues.includes(compress.permission)) {
|
|
1331
1396
|
errors.push({
|
|
@@ -1504,7 +1569,7 @@ var defaultConfig = {
|
|
|
1504
1569
|
maxContextLimit: "80%",
|
|
1505
1570
|
minContextLimit: "80%",
|
|
1506
1571
|
nudgeFrequency: 5,
|
|
1507
|
-
minNudgeContextPercent:
|
|
1572
|
+
minNudgeContextPercent: 5,
|
|
1508
1573
|
iterationNudgeThreshold: 15,
|
|
1509
1574
|
nudgeForce: "soft",
|
|
1510
1575
|
protectedTools: [...COMPRESS_DEFAULT_PROTECTED_TOOLS],
|
|
@@ -1626,6 +1691,39 @@ function loadConfigFile(configPath) {
|
|
|
1626
1691
|
return { data: null, parseError: error.message || "Failed to parse config" };
|
|
1627
1692
|
}
|
|
1628
1693
|
}
|
|
1694
|
+
function stripUndefined(value) {
|
|
1695
|
+
const result = {};
|
|
1696
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1697
|
+
if (entry !== void 0) {
|
|
1698
|
+
result[key] = entry;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
return result;
|
|
1702
|
+
}
|
|
1703
|
+
function mergeModelOverrides(base, override) {
|
|
1704
|
+
if (base === void 0) return override;
|
|
1705
|
+
if (override === void 0) return base;
|
|
1706
|
+
const merged = { ...base };
|
|
1707
|
+
for (const [modelId, model] of Object.entries(override)) {
|
|
1708
|
+
merged[modelId] = { ...base[modelId], ...stripUndefined(model) };
|
|
1709
|
+
}
|
|
1710
|
+
return merged;
|
|
1711
|
+
}
|
|
1712
|
+
function mergeProviderOverrides(base, override) {
|
|
1713
|
+
if (base === void 0) return override;
|
|
1714
|
+
if (override === void 0) return base;
|
|
1715
|
+
const merged = { ...base };
|
|
1716
|
+
for (const [providerId, provider] of Object.entries(override)) {
|
|
1717
|
+
const baseProvider = base[providerId];
|
|
1718
|
+
const mergedModels = mergeModelOverrides(baseProvider?.models, provider.models);
|
|
1719
|
+
merged[providerId] = {
|
|
1720
|
+
...baseProvider,
|
|
1721
|
+
...stripUndefined(provider),
|
|
1722
|
+
...mergedModels !== void 0 ? { models: mergedModels } : {}
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
return merged;
|
|
1726
|
+
}
|
|
1629
1727
|
function mergeCompress(base, override) {
|
|
1630
1728
|
if (!override) {
|
|
1631
1729
|
return base;
|
|
@@ -1638,6 +1736,7 @@ function mergeCompress(base, override) {
|
|
|
1638
1736
|
minContextLimit: override.minContextLimit ?? base.minContextLimit,
|
|
1639
1737
|
modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
|
|
1640
1738
|
modelMinLimits: override.modelMinLimits ?? base.modelMinLimits,
|
|
1739
|
+
providers: mergeProviderOverrides(base.providers, override.providers),
|
|
1641
1740
|
nudgeFrequency: override.nudgeFrequency ?? base.nudgeFrequency,
|
|
1642
1741
|
minNudgeContextPercent: override.minNudgeContextPercent ?? base.minNudgeContextPercent,
|
|
1643
1742
|
nudgeGrowthTokens: override.nudgeGrowthTokens,
|
|
@@ -1657,8 +1756,7 @@ function mergeCompress(base, override) {
|
|
|
1657
1756
|
lastSegmentSoftBlock: override.lastSegmentSoftBlock ?? base.lastSegmentSoftBlock,
|
|
1658
1757
|
preserveRecentMessages: override.preserveRecentMessages ?? base.preserveRecentMessages,
|
|
1659
1758
|
preserveRecentTokens: override.preserveRecentTokens ?? base.preserveRecentTokens,
|
|
1660
|
-
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1661
|
-
completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
|
|
1759
|
+
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1662
1760
|
};
|
|
1663
1761
|
}
|
|
1664
1762
|
function mergeCommands(base, override) {
|
|
@@ -1689,6 +1787,22 @@ function deepCloneConfig(config) {
|
|
|
1689
1787
|
...config.compress,
|
|
1690
1788
|
modelMaxLimits: { ...config.compress.modelMaxLimits },
|
|
1691
1789
|
modelMinLimits: { ...config.compress.modelMinLimits },
|
|
1790
|
+
providers: config.compress.providers ? Object.fromEntries(
|
|
1791
|
+
Object.entries(config.compress.providers).map(([providerId, provider]) => [
|
|
1792
|
+
providerId,
|
|
1793
|
+
{
|
|
1794
|
+
...provider,
|
|
1795
|
+
...provider.models ? {
|
|
1796
|
+
models: Object.fromEntries(
|
|
1797
|
+
Object.entries(provider.models).map(([modelId, model]) => [
|
|
1798
|
+
modelId,
|
|
1799
|
+
{ ...model }
|
|
1800
|
+
])
|
|
1801
|
+
)
|
|
1802
|
+
} : {}
|
|
1803
|
+
}
|
|
1804
|
+
])
|
|
1805
|
+
) : void 0,
|
|
1692
1806
|
protectedTools: [...config.compress.protectedTools]
|
|
1693
1807
|
},
|
|
1694
1808
|
gc: {
|
|
@@ -4317,8 +4431,7 @@ function createSessionState() {
|
|
|
4317
4431
|
modelProviderID: void 0,
|
|
4318
4432
|
modelID: void 0,
|
|
4319
4433
|
systemPromptTokens: void 0,
|
|
4320
|
-
qualityGateRetryPending: false
|
|
4321
|
-
noContextLimitWarned: false
|
|
4434
|
+
qualityGateRetryPending: false
|
|
4322
4435
|
};
|
|
4323
4436
|
}
|
|
4324
4437
|
function resetSessionState(state) {
|
|
@@ -4360,7 +4473,6 @@ function resetSessionState(state) {
|
|
|
4360
4473
|
state.modelID = void 0;
|
|
4361
4474
|
state.systemPromptTokens = void 0;
|
|
4362
4475
|
state.qualityGateRetryPending = false;
|
|
4363
|
-
state.noContextLimitWarned = false;
|
|
4364
4476
|
}
|
|
4365
4477
|
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config) {
|
|
4366
4478
|
if (state.sessionId === sessionId) {
|
|
@@ -6975,6 +7087,26 @@ function computeShouldNudge2(params) {
|
|
|
6975
7087
|
return policy.computeShouldNudge(params);
|
|
6976
7088
|
}
|
|
6977
7089
|
var DEFAULT_NUDGE_GROWTH_TOKENS = 5e4;
|
|
7090
|
+
var DEFAULT_MIN_NUDGE_CONTEXT_PERCENT = 5;
|
|
7091
|
+
function resolveMinNudgeContextPercent(config, providerId, modelId) {
|
|
7092
|
+
const modelOverrides = providerId !== void 0 && modelId !== void 0 ? config.compress?.providers?.[providerId]?.models?.[modelId] : void 0;
|
|
7093
|
+
if (modelOverrides?.minNudgeContextPercent !== void 0) {
|
|
7094
|
+
return modelOverrides.minNudgeContextPercent;
|
|
7095
|
+
}
|
|
7096
|
+
const providerOverrides = providerId !== void 0 ? config.compress?.providers?.[providerId] : void 0;
|
|
7097
|
+
if (providerOverrides?.minNudgeContextPercent !== void 0) {
|
|
7098
|
+
return providerOverrides.minNudgeContextPercent;
|
|
7099
|
+
}
|
|
7100
|
+
return config.compress?.minNudgeContextPercent;
|
|
7101
|
+
}
|
|
7102
|
+
function resolveMinNudgeFloorTokens(config, modelContextLimit, providerId, modelId) {
|
|
7103
|
+
if (modelContextLimit === void 0) {
|
|
7104
|
+
return void 0;
|
|
7105
|
+
}
|
|
7106
|
+
const percent = resolveMinNudgeContextPercent(config, providerId, modelId) ?? DEFAULT_MIN_NUDGE_CONTEXT_PERCENT;
|
|
7107
|
+
const clamped = Math.min(100, Math.max(0, percent));
|
|
7108
|
+
return Math.round(clamped / 100 * modelContextLimit);
|
|
7109
|
+
}
|
|
6978
7110
|
function addAnchor(anchorMessageIds, anchorMessageId, anchorMessageIndex, messages, interval) {
|
|
6979
7111
|
if (anchorMessageIndex < 0) {
|
|
6980
7112
|
return false;
|
|
@@ -7657,11 +7789,18 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7657
7789
|
overMinLimit,
|
|
7658
7790
|
overMaxLimit,
|
|
7659
7791
|
lastNudgeTokens: growthReference,
|
|
7660
|
-
minNudgeContextPercent: config
|
|
7792
|
+
minNudgeContextPercent: resolveMinNudgeContextPercent(config, providerId, modelId) ?? DEFAULT_MIN_NUDGE_CONTEXT_PERCENT,
|
|
7661
7793
|
nudgeGrowthTokens: effectiveThreshold
|
|
7662
7794
|
});
|
|
7663
7795
|
const growthSinceBaseline = currentTokens !== void 0 && growthReference !== void 0 ? currentTokens - growthReference : void 0;
|
|
7664
|
-
const
|
|
7796
|
+
const minNudgeFloorTokens = resolveMinNudgeFloorTokens(
|
|
7797
|
+
config,
|
|
7798
|
+
modelContextLimit,
|
|
7799
|
+
providerId,
|
|
7800
|
+
modelId
|
|
7801
|
+
);
|
|
7802
|
+
const overMinNudgeFloor = minNudgeFloorTokens === void 0 || currentTokens === void 0 || currentTokens >= minNudgeFloorTokens;
|
|
7803
|
+
const nudgeAllowed = emergencyOverride || decision.shouldNudge && (overMaxLimit || overMinNudgeFloor) && growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
|
|
7665
7804
|
const effectiveTipsVariant = emergencyOverride ? "maxLimit" : decision.tipsVariant;
|
|
7666
7805
|
if (state.nudges.lastPerMessageNudgeTokens === void 0 && currentTokens !== void 0) {
|
|
7667
7806
|
state.nudges.lastPerMessageNudgeTokens = currentTokens;
|
|
@@ -9128,7 +9267,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9128
9267
|
import { join as join3 } from "path";
|
|
9129
9268
|
import { existsSync as existsSync3 } from "fs";
|
|
9130
9269
|
import { homedir as homedir3 } from "os";
|
|
9131
|
-
var LOG_VERSION = true ? "1.14.25-pr.
|
|
9270
|
+
var LOG_VERSION = true ? "1.14.25-pr.351.83" : "dev";
|
|
9132
9271
|
var LEVEL_RANK = {
|
|
9133
9272
|
debug: 10,
|
|
9134
9273
|
info: 20,
|
|
@@ -10013,153 +10152,6 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
10013
10152
|
}
|
|
10014
10153
|
}
|
|
10015
10154
|
|
|
10016
|
-
// lib/messages/enforce-budget.ts
|
|
10017
|
-
var DEFAULT_COMPLETION_RESERVE_TOKENS = 32768;
|
|
10018
|
-
var TRUNCATION_MARKER2 = "[truncated for context space";
|
|
10019
|
-
var KEEP_PREFIX_CHARS2 = 2e3;
|
|
10020
|
-
var KEEP_SUFFIX_CHARS2 = 2e3;
|
|
10021
|
-
var PROTECT_RECENT_MESSAGES2 = 3;
|
|
10022
|
-
var MIN_CLEAR_TOKENS = 200;
|
|
10023
|
-
function resolveContextWindow(state) {
|
|
10024
|
-
if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
|
|
10025
|
-
return state.modelContextLimit;
|
|
10026
|
-
}
|
|
10027
|
-
return void 0;
|
|
10028
|
-
}
|
|
10029
|
-
function estimateWireTokens(state, messages) {
|
|
10030
|
-
const base = getCurrentTokenUsage(state, messages);
|
|
10031
|
-
if (base > 0) {
|
|
10032
|
-
let baseAssistant = -1;
|
|
10033
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
10034
|
-
if (messages[i].info.role !== "assistant") continue;
|
|
10035
|
-
const tokens = messages[i].info.tokens;
|
|
10036
|
-
if ((tokens?.input || 0) <= 0 && (tokens?.output || 0) <= 0) continue;
|
|
10037
|
-
baseAssistant = i;
|
|
10038
|
-
break;
|
|
10039
|
-
}
|
|
10040
|
-
if (baseAssistant >= 0) {
|
|
10041
|
-
let additions = 0;
|
|
10042
|
-
for (let i = baseAssistant + 1; i < messages.length; i++) {
|
|
10043
|
-
additions += countAllMessageTokens(messages[i]);
|
|
10044
|
-
}
|
|
10045
|
-
return base + additions;
|
|
10046
|
-
}
|
|
10047
|
-
}
|
|
10048
|
-
let total = 0;
|
|
10049
|
-
for (const m of messages) total += countAllMessageTokens(m);
|
|
10050
|
-
return total + (state.systemPromptTokens ?? 0);
|
|
10051
|
-
}
|
|
10052
|
-
function enforceContextBudget(state, config, logger, messages) {
|
|
10053
|
-
const window = resolveContextWindow(state);
|
|
10054
|
-
if (window === void 0) return void 0;
|
|
10055
|
-
const configuredReserve = config.compress?.completionReserveTokens;
|
|
10056
|
-
const reserve = typeof configuredReserve === "number" && Number.isFinite(configuredReserve) && configuredReserve >= 0 ? configuredReserve : DEFAULT_COMPLETION_RESERVE_TOKENS;
|
|
10057
|
-
const budget = window - reserve;
|
|
10058
|
-
if (budget <= 0) return void 0;
|
|
10059
|
-
const estimatedTokens = estimateWireTokens(state, messages);
|
|
10060
|
-
if (estimatedTokens <= budget) {
|
|
10061
|
-
return {
|
|
10062
|
-
applied: false,
|
|
10063
|
-
window,
|
|
10064
|
-
reserve,
|
|
10065
|
-
budget,
|
|
10066
|
-
estimatedTokens,
|
|
10067
|
-
finalEstimate: estimatedTokens,
|
|
10068
|
-
truncatedCount: 0,
|
|
10069
|
-
clearedCount: 0
|
|
10070
|
-
};
|
|
10071
|
-
}
|
|
10072
|
-
const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES2;
|
|
10073
|
-
const protectedTools = new Set(config.compress?.protectedTools ?? []);
|
|
10074
|
-
const candidates = [];
|
|
10075
|
-
for (let mi = 0; mi < protectedIndex; mi++) {
|
|
10076
|
-
if (mi === 0 && messages[mi].info.role === "user") continue;
|
|
10077
|
-
const msg = messages[mi];
|
|
10078
|
-
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
10079
|
-
for (const part of parts) {
|
|
10080
|
-
if (part?.type !== "tool") continue;
|
|
10081
|
-
if (part.state?.status !== "completed") continue;
|
|
10082
|
-
if (part.tool === "compress") continue;
|
|
10083
|
-
if (protectedTools.has(part.tool)) continue;
|
|
10084
|
-
const content = extractCompletedToolOutput(part);
|
|
10085
|
-
if (content === void 0) continue;
|
|
10086
|
-
if (content === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
|
|
10087
|
-
const tokens = countTokens2(content);
|
|
10088
|
-
if (tokens <= 0) continue;
|
|
10089
|
-
candidates.push({ part, content, tokens, index: mi });
|
|
10090
|
-
}
|
|
10091
|
-
}
|
|
10092
|
-
let saved = 0;
|
|
10093
|
-
let truncatedCount = 0;
|
|
10094
|
-
let clearedCount = 0;
|
|
10095
|
-
const truncatable = candidates.filter(
|
|
10096
|
-
(c) => !c.content.includes(TRUNCATION_MARKER2) && c.content.length > KEEP_PREFIX_CHARS2 + KEEP_SUFFIX_CHARS2
|
|
10097
|
-
).sort((a, b) => b.tokens - a.tokens);
|
|
10098
|
-
for (const c of truncatable) {
|
|
10099
|
-
if (estimatedTokens - saved <= budget) break;
|
|
10100
|
-
const prefix = c.content.slice(0, KEEP_PREFIX_CHARS2);
|
|
10101
|
-
const suffix = c.content.slice(-KEEP_SUFFIX_CHARS2);
|
|
10102
|
-
const truncated = prefix + `
|
|
10103
|
-
|
|
10104
|
-
...${TRUNCATION_MARKER2} \u2014 original ~${c.tokens} tokens]...
|
|
10105
|
-
|
|
10106
|
-
` + suffix;
|
|
10107
|
-
if (truncated.length >= c.content.length) continue;
|
|
10108
|
-
c.part.state.output = truncated;
|
|
10109
|
-
saved += c.tokens - countTokens2(truncated);
|
|
10110
|
-
truncatedCount++;
|
|
10111
|
-
}
|
|
10112
|
-
if (estimatedTokens - saved > budget) {
|
|
10113
|
-
const clearable = candidates.filter((c) => {
|
|
10114
|
-
const out = extractCompletedToolOutput(c.part);
|
|
10115
|
-
return out !== void 0 && out !== COMPACTED_TOOL_OUTPUT_PLACEHOLDER && countTokens2(out) > MIN_CLEAR_TOKENS;
|
|
10116
|
-
}).sort((a, b) => a.index - b.index);
|
|
10117
|
-
for (const c of clearable) {
|
|
10118
|
-
if (estimatedTokens - saved <= budget) break;
|
|
10119
|
-
const current = extractCompletedToolOutput(c.part);
|
|
10120
|
-
if (current === void 0 || current === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
|
|
10121
|
-
c.part.state.output = COMPACTED_TOOL_OUTPUT_PLACEHOLDER;
|
|
10122
|
-
saved += countTokens2(current) - countTokens2(COMPACTED_TOOL_OUTPUT_PLACEHOLDER);
|
|
10123
|
-
clearedCount++;
|
|
10124
|
-
}
|
|
10125
|
-
}
|
|
10126
|
-
const finalEstimate = Math.max(0, estimatedTokens - saved);
|
|
10127
|
-
if (truncatedCount > 0 || clearedCount > 0) {
|
|
10128
|
-
logger.warn("Context budget guard: pruned tool outputs to fit the request", {
|
|
10129
|
-
session: state.sessionId,
|
|
10130
|
-
estimatedTokens: Math.round(estimatedTokens),
|
|
10131
|
-
budget,
|
|
10132
|
-
window,
|
|
10133
|
-
reserve,
|
|
10134
|
-
truncatedCount,
|
|
10135
|
-
clearedCount,
|
|
10136
|
-
estimatedSavedTokens: Math.round(saved),
|
|
10137
|
-
finalEstimate: Math.round(finalEstimate)
|
|
10138
|
-
});
|
|
10139
|
-
}
|
|
10140
|
-
if (finalEstimate > budget) {
|
|
10141
|
-
logger.warn(
|
|
10142
|
-
"Context budget guard: still over budget after pruning all compressible tool outputs; the request may be rejected by the model",
|
|
10143
|
-
{
|
|
10144
|
-
session: state.sessionId,
|
|
10145
|
-
finalEstimate: Math.round(finalEstimate),
|
|
10146
|
-
budget,
|
|
10147
|
-
window
|
|
10148
|
-
}
|
|
10149
|
-
);
|
|
10150
|
-
}
|
|
10151
|
-
return {
|
|
10152
|
-
applied: truncatedCount > 0 || clearedCount > 0,
|
|
10153
|
-
window,
|
|
10154
|
-
reserve,
|
|
10155
|
-
budget,
|
|
10156
|
-
estimatedTokens,
|
|
10157
|
-
finalEstimate,
|
|
10158
|
-
truncatedCount,
|
|
10159
|
-
clearedCount
|
|
10160
|
-
};
|
|
10161
|
-
}
|
|
10162
|
-
|
|
10163
10155
|
// lib/commands/context.ts
|
|
10164
10156
|
function analyzeTokens(state, messages) {
|
|
10165
10157
|
const breakdown = {
|
|
@@ -11261,16 +11253,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11261
11253
|
});
|
|
11262
11254
|
}
|
|
11263
11255
|
await updatePerTurnState(state, logger, messages);
|
|
11264
|
-
if (state.modelContextLimit === void 0 && !state.noContextLimitWarned && requestModel?.providerID && requestModel?.modelID && registry4.resolveModelLimit(requestModel.providerID, requestModel.modelID) === void 0) {
|
|
11265
|
-
state.noContextLimitWarned = true;
|
|
11266
|
-
logger.warn(
|
|
11267
|
-
'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.',
|
|
11268
|
-
{
|
|
11269
|
-
session: state.sessionId,
|
|
11270
|
-
model: `${requestModel.providerID}/${requestModel.modelID}`
|
|
11271
|
-
}
|
|
11272
|
-
);
|
|
11273
|
-
}
|
|
11274
11256
|
}
|
|
11275
11257
|
syncCompressPermissionState(state, config, hostPermissions, output.messages);
|
|
11276
11258
|
if (state.isSubAgent && !config.allowSubAgents) {
|
|
@@ -11301,7 +11283,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11301
11283
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
11302
11284
|
prune(state, logger, config, output.messages);
|
|
11303
11285
|
truncateLargeToolOutputs(state, config, logger, output.messages);
|
|
11304
|
-
enforceContextBudget(state, config, logger, output.messages);
|
|
11305
11286
|
hideConsumedCompressCalls(state, output.messages);
|
|
11306
11287
|
assignMessageRefs(state, output.messages);
|
|
11307
11288
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
@@ -11727,7 +11708,7 @@ var server = (async (ctx) => {
|
|
|
11727
11708
|
}
|
|
11728
11709
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
11729
11710
|
logger.info("ACP plugin initialized", {
|
|
11730
|
-
version: true ? "1.14.25-pr.
|
|
11711
|
+
version: true ? "1.14.25-pr.351.83" : "dev",
|
|
11731
11712
|
workspace: ctx.directory,
|
|
11732
11713
|
logLevel: logger.level,
|
|
11733
11714
|
debug: config.debug,
|