opencode-acp 1.14.25-pr.350.73 → 1.14.25-pr.351.82
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 -164
- 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 -61
- 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",
|
|
@@ -931,7 +932,7 @@ function getConfigKeyPaths(obj, prefix = "") {
|
|
|
931
932
|
for (const key of Object.keys(obj)) {
|
|
932
933
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
933
934
|
keys.push(fullKey);
|
|
934
|
-
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "messageFilters.filters") {
|
|
935
|
+
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "compress.providers" || fullKey === "messageFilters.filters") {
|
|
935
936
|
continue;
|
|
936
937
|
}
|
|
937
938
|
if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
|
@@ -1311,6 +1312,85 @@ function validateConfigTypes(config) {
|
|
|
1311
1312
|
}
|
|
1312
1313
|
validateModelLimits("compress.modelMaxLimits", compress.modelMaxLimits);
|
|
1313
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);
|
|
1314
1394
|
const validValues = ["ask", "allow", "deny"];
|
|
1315
1395
|
if (compress.permission !== void 0 && !validValues.includes(compress.permission)) {
|
|
1316
1396
|
errors.push({
|
|
@@ -1489,7 +1569,7 @@ var defaultConfig = {
|
|
|
1489
1569
|
maxContextLimit: "80%",
|
|
1490
1570
|
minContextLimit: "80%",
|
|
1491
1571
|
nudgeFrequency: 5,
|
|
1492
|
-
minNudgeContextPercent:
|
|
1572
|
+
minNudgeContextPercent: 5,
|
|
1493
1573
|
iterationNudgeThreshold: 15,
|
|
1494
1574
|
nudgeForce: "soft",
|
|
1495
1575
|
protectedTools: [...COMPRESS_DEFAULT_PROTECTED_TOOLS],
|
|
@@ -1611,6 +1691,39 @@ function loadConfigFile(configPath) {
|
|
|
1611
1691
|
return { data: null, parseError: error.message || "Failed to parse config" };
|
|
1612
1692
|
}
|
|
1613
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
|
+
}
|
|
1614
1727
|
function mergeCompress(base, override) {
|
|
1615
1728
|
if (!override) {
|
|
1616
1729
|
return base;
|
|
@@ -1623,6 +1736,7 @@ function mergeCompress(base, override) {
|
|
|
1623
1736
|
minContextLimit: override.minContextLimit ?? base.minContextLimit,
|
|
1624
1737
|
modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
|
|
1625
1738
|
modelMinLimits: override.modelMinLimits ?? base.modelMinLimits,
|
|
1739
|
+
providers: mergeProviderOverrides(base.providers, override.providers),
|
|
1626
1740
|
nudgeFrequency: override.nudgeFrequency ?? base.nudgeFrequency,
|
|
1627
1741
|
minNudgeContextPercent: override.minNudgeContextPercent ?? base.minNudgeContextPercent,
|
|
1628
1742
|
nudgeGrowthTokens: override.nudgeGrowthTokens,
|
|
@@ -1642,8 +1756,7 @@ function mergeCompress(base, override) {
|
|
|
1642
1756
|
lastSegmentSoftBlock: override.lastSegmentSoftBlock ?? base.lastSegmentSoftBlock,
|
|
1643
1757
|
preserveRecentMessages: override.preserveRecentMessages ?? base.preserveRecentMessages,
|
|
1644
1758
|
preserveRecentTokens: override.preserveRecentTokens ?? base.preserveRecentTokens,
|
|
1645
|
-
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1646
|
-
completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
|
|
1759
|
+
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1647
1760
|
};
|
|
1648
1761
|
}
|
|
1649
1762
|
function mergeCommands(base, override) {
|
|
@@ -1674,6 +1787,22 @@ function deepCloneConfig(config) {
|
|
|
1674
1787
|
...config.compress,
|
|
1675
1788
|
modelMaxLimits: { ...config.compress.modelMaxLimits },
|
|
1676
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,
|
|
1677
1806
|
protectedTools: [...config.compress.protectedTools]
|
|
1678
1807
|
},
|
|
1679
1808
|
gc: {
|
|
@@ -4302,8 +4431,7 @@ function createSessionState() {
|
|
|
4302
4431
|
modelProviderID: void 0,
|
|
4303
4432
|
modelID: void 0,
|
|
4304
4433
|
systemPromptTokens: void 0,
|
|
4305
|
-
qualityGateRetryPending: false
|
|
4306
|
-
noContextLimitWarned: false
|
|
4434
|
+
qualityGateRetryPending: false
|
|
4307
4435
|
};
|
|
4308
4436
|
}
|
|
4309
4437
|
function resetSessionState(state) {
|
|
@@ -4345,7 +4473,6 @@ function resetSessionState(state) {
|
|
|
4345
4473
|
state.modelID = void 0;
|
|
4346
4474
|
state.systemPromptTokens = void 0;
|
|
4347
4475
|
state.qualityGateRetryPending = false;
|
|
4348
|
-
state.noContextLimitWarned = false;
|
|
4349
4476
|
}
|
|
4350
4477
|
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config) {
|
|
4351
4478
|
if (state.sessionId === sessionId) {
|
|
@@ -6960,6 +7087,26 @@ function computeShouldNudge2(params) {
|
|
|
6960
7087
|
return policy.computeShouldNudge(params);
|
|
6961
7088
|
}
|
|
6962
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
|
+
}
|
|
6963
7110
|
function addAnchor(anchorMessageIds, anchorMessageId, anchorMessageIndex, messages, interval) {
|
|
6964
7111
|
if (anchorMessageIndex < 0) {
|
|
6965
7112
|
return false;
|
|
@@ -7642,11 +7789,18 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7642
7789
|
overMinLimit,
|
|
7643
7790
|
overMaxLimit,
|
|
7644
7791
|
lastNudgeTokens: growthReference,
|
|
7645
|
-
minNudgeContextPercent: config
|
|
7792
|
+
minNudgeContextPercent: resolveMinNudgeContextPercent(config, providerId, modelId) ?? DEFAULT_MIN_NUDGE_CONTEXT_PERCENT,
|
|
7646
7793
|
nudgeGrowthTokens: effectiveThreshold
|
|
7647
7794
|
});
|
|
7648
7795
|
const growthSinceBaseline = currentTokens !== void 0 && growthReference !== void 0 ? currentTokens - growthReference : void 0;
|
|
7649
|
-
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;
|
|
7650
7804
|
const effectiveTipsVariant = emergencyOverride ? "maxLimit" : decision.tipsVariant;
|
|
7651
7805
|
if (state.nudges.lastPerMessageNudgeTokens === void 0 && currentTokens !== void 0) {
|
|
7652
7806
|
state.nudges.lastPerMessageNudgeTokens = currentTokens;
|
|
@@ -9113,7 +9267,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9113
9267
|
import { join as join3 } from "path";
|
|
9114
9268
|
import { existsSync as existsSync3 } from "fs";
|
|
9115
9269
|
import { homedir as homedir3 } from "os";
|
|
9116
|
-
var LOG_VERSION = true ? "1.14.25-pr.
|
|
9270
|
+
var LOG_VERSION = true ? "1.14.25-pr.351.82" : "dev";
|
|
9117
9271
|
var LEVEL_RANK = {
|
|
9118
9272
|
debug: 10,
|
|
9119
9273
|
info: 20,
|
|
@@ -9998,148 +10152,6 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
9998
10152
|
}
|
|
9999
10153
|
}
|
|
10000
10154
|
|
|
10001
|
-
// lib/messages/enforce-budget.ts
|
|
10002
|
-
var DEFAULT_COMPLETION_RESERVE_TOKENS = 32768;
|
|
10003
|
-
var TRUNCATION_MARKER2 = "[truncated for context space";
|
|
10004
|
-
var KEEP_PREFIX_CHARS2 = 2e3;
|
|
10005
|
-
var KEEP_SUFFIX_CHARS2 = 2e3;
|
|
10006
|
-
var PROTECT_RECENT_MESSAGES2 = 3;
|
|
10007
|
-
var MIN_CLEAR_TOKENS = 200;
|
|
10008
|
-
function resolveContextWindow(state) {
|
|
10009
|
-
if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
|
|
10010
|
-
return state.modelContextLimit;
|
|
10011
|
-
}
|
|
10012
|
-
return void 0;
|
|
10013
|
-
}
|
|
10014
|
-
function estimateWireTokens(state, messages) {
|
|
10015
|
-
const base = getCurrentTokenUsage(state, messages);
|
|
10016
|
-
if (base > 0) {
|
|
10017
|
-
let lastAssistant = -1;
|
|
10018
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
10019
|
-
if (messages[i].info.role === "assistant") {
|
|
10020
|
-
lastAssistant = i;
|
|
10021
|
-
break;
|
|
10022
|
-
}
|
|
10023
|
-
}
|
|
10024
|
-
let additions = 0;
|
|
10025
|
-
for (let i = lastAssistant + 1; i < messages.length; i++) {
|
|
10026
|
-
additions += countAllMessageTokens(messages[i]);
|
|
10027
|
-
}
|
|
10028
|
-
return base + additions;
|
|
10029
|
-
}
|
|
10030
|
-
let total = 0;
|
|
10031
|
-
for (const m of messages) total += countAllMessageTokens(m);
|
|
10032
|
-
return total + (state.systemPromptTokens ?? 0);
|
|
10033
|
-
}
|
|
10034
|
-
function enforceContextBudget(state, config, logger, messages) {
|
|
10035
|
-
const window = resolveContextWindow(state);
|
|
10036
|
-
if (window === void 0) return void 0;
|
|
10037
|
-
const reserve = config.compress?.completionReserveTokens ?? DEFAULT_COMPLETION_RESERVE_TOKENS;
|
|
10038
|
-
const budget = window - reserve;
|
|
10039
|
-
if (budget <= 0) return void 0;
|
|
10040
|
-
const estimatedTokens = estimateWireTokens(state, messages);
|
|
10041
|
-
if (estimatedTokens <= budget) {
|
|
10042
|
-
return {
|
|
10043
|
-
applied: false,
|
|
10044
|
-
window,
|
|
10045
|
-
reserve,
|
|
10046
|
-
budget,
|
|
10047
|
-
estimatedTokens,
|
|
10048
|
-
finalEstimate: estimatedTokens,
|
|
10049
|
-
truncatedCount: 0,
|
|
10050
|
-
clearedCount: 0
|
|
10051
|
-
};
|
|
10052
|
-
}
|
|
10053
|
-
const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES2;
|
|
10054
|
-
const protectedTools = new Set(config.compress?.protectedTools ?? []);
|
|
10055
|
-
const candidates = [];
|
|
10056
|
-
for (let mi = 0; mi < protectedIndex; mi++) {
|
|
10057
|
-
if (mi === 0 && messages[mi].info.role === "user") continue;
|
|
10058
|
-
const msg = messages[mi];
|
|
10059
|
-
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
10060
|
-
for (const part of parts) {
|
|
10061
|
-
if (part?.type !== "tool") continue;
|
|
10062
|
-
if (part.state?.status !== "completed") continue;
|
|
10063
|
-
if (part.tool === "compress") continue;
|
|
10064
|
-
if (protectedTools.has(part.tool)) continue;
|
|
10065
|
-
const content = extractCompletedToolOutput(part);
|
|
10066
|
-
if (content === void 0) continue;
|
|
10067
|
-
if (content === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
|
|
10068
|
-
const tokens = countTokens2(content);
|
|
10069
|
-
if (tokens <= 0) continue;
|
|
10070
|
-
candidates.push({ part, content, tokens, index: mi });
|
|
10071
|
-
}
|
|
10072
|
-
}
|
|
10073
|
-
let saved = 0;
|
|
10074
|
-
let truncatedCount = 0;
|
|
10075
|
-
let clearedCount = 0;
|
|
10076
|
-
const truncatable = candidates.filter(
|
|
10077
|
-
(c) => !c.content.includes(TRUNCATION_MARKER2) && c.content.length > KEEP_PREFIX_CHARS2 + KEEP_SUFFIX_CHARS2
|
|
10078
|
-
).sort((a, b) => b.tokens - a.tokens);
|
|
10079
|
-
for (const c of truncatable) {
|
|
10080
|
-
if (estimatedTokens - saved <= budget) break;
|
|
10081
|
-
const prefix = c.content.slice(0, KEEP_PREFIX_CHARS2);
|
|
10082
|
-
const suffix = c.content.slice(-KEEP_SUFFIX_CHARS2);
|
|
10083
|
-
const truncated = prefix + `
|
|
10084
|
-
|
|
10085
|
-
...${TRUNCATION_MARKER2} \u2014 original ~${c.tokens} tokens]...
|
|
10086
|
-
|
|
10087
|
-
` + suffix;
|
|
10088
|
-
c.part.state.output = truncated;
|
|
10089
|
-
saved += c.tokens - countTokens2(truncated);
|
|
10090
|
-
truncatedCount++;
|
|
10091
|
-
}
|
|
10092
|
-
if (estimatedTokens - saved > budget) {
|
|
10093
|
-
const clearable = candidates.filter((c) => {
|
|
10094
|
-
const out = extractCompletedToolOutput(c.part);
|
|
10095
|
-
return out !== void 0 && out !== COMPACTED_TOOL_OUTPUT_PLACEHOLDER && countTokens2(out) > MIN_CLEAR_TOKENS;
|
|
10096
|
-
}).sort((a, b) => a.index - b.index);
|
|
10097
|
-
for (const c of clearable) {
|
|
10098
|
-
if (estimatedTokens - saved <= budget) break;
|
|
10099
|
-
const current = extractCompletedToolOutput(c.part);
|
|
10100
|
-
if (current === void 0 || current === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
|
|
10101
|
-
c.part.state.output = COMPACTED_TOOL_OUTPUT_PLACEHOLDER;
|
|
10102
|
-
saved += countTokens2(current) - countTokens2(COMPACTED_TOOL_OUTPUT_PLACEHOLDER);
|
|
10103
|
-
clearedCount++;
|
|
10104
|
-
}
|
|
10105
|
-
}
|
|
10106
|
-
const finalEstimate = Math.max(0, estimatedTokens - saved);
|
|
10107
|
-
if (truncatedCount > 0 || clearedCount > 0) {
|
|
10108
|
-
logger.warn("Context budget guard: pruned tool outputs to fit the request", {
|
|
10109
|
-
session: state.sessionId,
|
|
10110
|
-
estimatedTokens: Math.round(estimatedTokens),
|
|
10111
|
-
budget,
|
|
10112
|
-
window,
|
|
10113
|
-
reserve,
|
|
10114
|
-
truncatedCount,
|
|
10115
|
-
clearedCount,
|
|
10116
|
-
estimatedSavedTokens: Math.round(saved),
|
|
10117
|
-
finalEstimate: Math.round(finalEstimate)
|
|
10118
|
-
});
|
|
10119
|
-
}
|
|
10120
|
-
if (finalEstimate > budget) {
|
|
10121
|
-
logger.warn(
|
|
10122
|
-
"Context budget guard: still over budget after pruning all compressible tool outputs; the request may be rejected by the model",
|
|
10123
|
-
{
|
|
10124
|
-
session: state.sessionId,
|
|
10125
|
-
finalEstimate: Math.round(finalEstimate),
|
|
10126
|
-
budget,
|
|
10127
|
-
window
|
|
10128
|
-
}
|
|
10129
|
-
);
|
|
10130
|
-
}
|
|
10131
|
-
return {
|
|
10132
|
-
applied: truncatedCount > 0 || clearedCount > 0,
|
|
10133
|
-
window,
|
|
10134
|
-
reserve,
|
|
10135
|
-
budget,
|
|
10136
|
-
estimatedTokens,
|
|
10137
|
-
finalEstimate,
|
|
10138
|
-
truncatedCount,
|
|
10139
|
-
clearedCount
|
|
10140
|
-
};
|
|
10141
|
-
}
|
|
10142
|
-
|
|
10143
10155
|
// lib/commands/context.ts
|
|
10144
10156
|
function analyzeTokens(state, messages) {
|
|
10145
10157
|
const breakdown = {
|
|
@@ -11241,16 +11253,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11241
11253
|
});
|
|
11242
11254
|
}
|
|
11243
11255
|
await updatePerTurnState(state, logger, messages);
|
|
11244
|
-
if (state.modelContextLimit === void 0 && !state.noContextLimitWarned && requestModel?.providerID && requestModel?.modelID && registry4.resolveModelLimit(requestModel.providerID, requestModel.modelID) === void 0) {
|
|
11245
|
-
state.noContextLimitWarned = true;
|
|
11246
|
-
logger.warn(
|
|
11247
|
-
'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}) or set an absolute compress.maxContextLimit in acp.jsonc.',
|
|
11248
|
-
{
|
|
11249
|
-
session: state.sessionId,
|
|
11250
|
-
model: `${requestModel.providerID}/${requestModel.modelID}`
|
|
11251
|
-
}
|
|
11252
|
-
);
|
|
11253
|
-
}
|
|
11254
11256
|
}
|
|
11255
11257
|
syncCompressPermissionState(state, config, hostPermissions, output.messages);
|
|
11256
11258
|
if (state.isSubAgent && !config.allowSubAgents) {
|
|
@@ -11281,7 +11283,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11281
11283
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
11282
11284
|
prune(state, logger, config, output.messages);
|
|
11283
11285
|
truncateLargeToolOutputs(state, config, logger, output.messages);
|
|
11284
|
-
enforceContextBudget(state, config, logger, output.messages);
|
|
11285
11286
|
hideConsumedCompressCalls(state, output.messages);
|
|
11286
11287
|
assignMessageRefs(state, output.messages);
|
|
11287
11288
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
@@ -11707,7 +11708,7 @@ var server = (async (ctx) => {
|
|
|
11707
11708
|
}
|
|
11708
11709
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
11709
11710
|
logger.info("ACP plugin initialized", {
|
|
11710
|
-
version: true ? "1.14.25-pr.
|
|
11711
|
+
version: true ? "1.14.25-pr.351.82" : "dev",
|
|
11711
11712
|
workspace: ctx.directory,
|
|
11712
11713
|
logLevel: logger.level,
|
|
11713
11714
|
debug: config.debug,
|