opencode-acp 1.14.25-pr.348.70 → 1.14.25-pr.349.71
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 +333 -384
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/decompress.d.ts.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config.d.ts +7 -15
- 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/index.d.ts +0 -2
- package/dist/lib/messages/index.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/messages/truncate-tools.d.ts +1 -0
- package/dist/lib/messages/truncate-tools.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts +2 -0
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/types.d.ts +0 -13
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/state/utils.d.ts +16 -0
- package/dist/lib/state/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/lib/messages/prune-to-fit.d.ts +0 -25
- package/dist/lib/messages/prune-to-fit.d.ts.map +0 -1
- package/dist/lib/messages/uncalibrated-window.d.ts +0 -31
- package/dist/lib/messages/uncalibrated-window.d.ts.map +0 -1
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.contextLimitFallback",
|
|
891
892
|
"compress.nudgeFrequency",
|
|
892
893
|
"compress.minNudgeContextPercent",
|
|
893
894
|
"compress.nudgeGrowthTokens",
|
|
@@ -908,8 +909,6 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
908
909
|
"compress.preserveRecentMessages",
|
|
909
910
|
"compress.preserveRecentTokens",
|
|
910
911
|
"compress.preserveLastUserMessage",
|
|
911
|
-
"compress.overflowGuard",
|
|
912
|
-
"compress.overflowGuardReserve",
|
|
913
912
|
"gc",
|
|
914
913
|
"gc.algorithm",
|
|
915
914
|
"gc.promotionThreshold",
|
|
@@ -1263,27 +1262,6 @@ function validateConfigTypes(config) {
|
|
|
1263
1262
|
actual: typeof compress.preserveLastUserMessage
|
|
1264
1263
|
});
|
|
1265
1264
|
}
|
|
1266
|
-
if (compress.overflowGuard !== void 0 && typeof compress.overflowGuard !== "boolean") {
|
|
1267
|
-
errors.push({
|
|
1268
|
-
key: "compress.overflowGuard",
|
|
1269
|
-
expected: "boolean",
|
|
1270
|
-
actual: typeof compress.overflowGuard
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
if (compress.overflowGuardReserve !== void 0 && typeof compress.overflowGuardReserve !== "number") {
|
|
1274
|
-
errors.push({
|
|
1275
|
-
key: "compress.overflowGuardReserve",
|
|
1276
|
-
expected: "number",
|
|
1277
|
-
actual: typeof compress.overflowGuardReserve
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
if (typeof compress.overflowGuardReserve === "number" && compress.overflowGuardReserve < 0) {
|
|
1281
|
-
errors.push({
|
|
1282
|
-
key: "compress.overflowGuardReserve",
|
|
1283
|
-
expected: "non-negative number (>= 0)",
|
|
1284
|
-
actual: `${compress.overflowGuardReserve}`
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
1265
|
if (typeof compress.iterationNudgeThreshold === "number" && compress.iterationNudgeThreshold < 1) {
|
|
1288
1266
|
errors.push({
|
|
1289
1267
|
key: "compress.iterationNudgeThreshold",
|
|
@@ -1334,6 +1312,20 @@ function validateConfigTypes(config) {
|
|
|
1334
1312
|
}
|
|
1335
1313
|
validateModelLimits("compress.modelMaxLimits", compress.modelMaxLimits);
|
|
1336
1314
|
validateModelLimits("compress.modelMinLimits", compress.modelMinLimits);
|
|
1315
|
+
if (compress.contextLimitFallback !== void 0 && typeof compress.contextLimitFallback !== "number") {
|
|
1316
|
+
errors.push({
|
|
1317
|
+
key: "compress.contextLimitFallback",
|
|
1318
|
+
expected: "number",
|
|
1319
|
+
actual: typeof compress.contextLimitFallback
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
if (typeof compress.contextLimitFallback === "number" && compress.contextLimitFallback < 0) {
|
|
1323
|
+
errors.push({
|
|
1324
|
+
key: "compress.contextLimitFallback",
|
|
1325
|
+
expected: "non-negative number (0 disables the fallback)",
|
|
1326
|
+
actual: `${compress.contextLimitFallback}`
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1337
1329
|
const validValues = ["ask", "allow", "deny"];
|
|
1338
1330
|
if (compress.permission !== void 0 && !validValues.includes(compress.permission)) {
|
|
1339
1331
|
errors.push({
|
|
@@ -1511,6 +1503,7 @@ var defaultConfig = {
|
|
|
1511
1503
|
summaryBuffer: true,
|
|
1512
1504
|
maxContextLimit: "80%",
|
|
1513
1505
|
minContextLimit: "80%",
|
|
1506
|
+
contextLimitFallback: 128e3,
|
|
1514
1507
|
nudgeFrequency: 5,
|
|
1515
1508
|
minNudgeContextPercent: 15,
|
|
1516
1509
|
iterationNudgeThreshold: 15,
|
|
@@ -1529,9 +1522,7 @@ var defaultConfig = {
|
|
|
1529
1522
|
lastSegmentSoftBlock: true,
|
|
1530
1523
|
preserveRecentMessages: 5,
|
|
1531
1524
|
preserveRecentTokens: 5e3,
|
|
1532
|
-
preserveLastUserMessage: true
|
|
1533
|
-
overflowGuard: true,
|
|
1534
|
-
overflowGuardReserve: 32768
|
|
1525
|
+
preserveLastUserMessage: true
|
|
1535
1526
|
},
|
|
1536
1527
|
gc: {
|
|
1537
1528
|
algorithm: "truncate",
|
|
@@ -1648,6 +1639,7 @@ function mergeCompress(base, override) {
|
|
|
1648
1639
|
minContextLimit: override.minContextLimit ?? base.minContextLimit,
|
|
1649
1640
|
modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
|
|
1650
1641
|
modelMinLimits: override.modelMinLimits ?? base.modelMinLimits,
|
|
1642
|
+
contextLimitFallback: override.contextLimitFallback ?? base.contextLimitFallback,
|
|
1651
1643
|
nudgeFrequency: override.nudgeFrequency ?? base.nudgeFrequency,
|
|
1652
1644
|
minNudgeContextPercent: override.minNudgeContextPercent ?? base.minNudgeContextPercent,
|
|
1653
1645
|
nudgeGrowthTokens: override.nudgeGrowthTokens,
|
|
@@ -1667,9 +1659,7 @@ function mergeCompress(base, override) {
|
|
|
1667
1659
|
lastSegmentSoftBlock: override.lastSegmentSoftBlock ?? base.lastSegmentSoftBlock,
|
|
1668
1660
|
preserveRecentMessages: override.preserveRecentMessages ?? base.preserveRecentMessages,
|
|
1669
1661
|
preserveRecentTokens: override.preserveRecentTokens ?? base.preserveRecentTokens,
|
|
1670
|
-
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1671
|
-
overflowGuard: override.overflowGuard ?? base.overflowGuard,
|
|
1672
|
-
overflowGuardReserve: override.overflowGuardReserve ?? base.overflowGuardReserve
|
|
1662
|
+
preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
|
|
1673
1663
|
};
|
|
1674
1664
|
}
|
|
1675
1665
|
function mergeCommands(base, override) {
|
|
@@ -2392,6 +2382,16 @@ function resetOnCompaction(state) {
|
|
|
2392
2382
|
nextRef: 1
|
|
2393
2383
|
};
|
|
2394
2384
|
}
|
|
2385
|
+
function resolveEffectiveContextLimit(state, config) {
|
|
2386
|
+
if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
|
|
2387
|
+
return { limit: state.modelContextLimit, source: "model" };
|
|
2388
|
+
}
|
|
2389
|
+
const fallback = config.compress.contextLimitFallback;
|
|
2390
|
+
if (typeof fallback === "number" && fallback > 0) {
|
|
2391
|
+
return { limit: fallback, source: "fallback" };
|
|
2392
|
+
}
|
|
2393
|
+
return void 0;
|
|
2394
|
+
}
|
|
2395
2395
|
|
|
2396
2396
|
// lib/state/persistence.ts
|
|
2397
2397
|
function getStorageDir() {
|
|
@@ -4237,6 +4237,24 @@ var SessionStateRegistry = class {
|
|
|
4237
4237
|
hydrateModelLimitsFromClient(client) {
|
|
4238
4238
|
return this.catalog.hydrateFromClient(client);
|
|
4239
4239
|
}
|
|
4240
|
+
// [FIX #346] The init-time seed (above) is fire-and-forget and races
|
|
4241
|
+
// server readiness: in headless spawn+resume mode the provider-config
|
|
4242
|
+
// call can fail before the server is up, leaving the catalog empty for
|
|
4243
|
+
// the process's lifetime. During a request the server is guaranteed up
|
|
4244
|
+
// (we are inside its pipeline), so on a catalog miss we retry hydration
|
|
4245
|
+
// once per process before giving up (the fallback limit then applies).
|
|
4246
|
+
lazyHydrated = false;
|
|
4247
|
+
async hydrateAndResolve(client, providerId, modelId) {
|
|
4248
|
+
const existing = this.catalog.resolve(providerId, modelId);
|
|
4249
|
+
if (existing !== void 0) {
|
|
4250
|
+
return existing;
|
|
4251
|
+
}
|
|
4252
|
+
if (!this.lazyHydrated) {
|
|
4253
|
+
this.lazyHydrated = true;
|
|
4254
|
+
await this.catalog.hydrateFromClient(client);
|
|
4255
|
+
}
|
|
4256
|
+
return this.catalog.resolve(providerId, modelId);
|
|
4257
|
+
}
|
|
4240
4258
|
get(sessionId) {
|
|
4241
4259
|
return this.states.get(sessionId);
|
|
4242
4260
|
}
|
|
@@ -4328,9 +4346,7 @@ function createSessionState() {
|
|
|
4328
4346
|
modelProviderID: void 0,
|
|
4329
4347
|
modelID: void 0,
|
|
4330
4348
|
systemPromptTokens: void 0,
|
|
4331
|
-
qualityGateRetryPending: false
|
|
4332
|
-
uncalibratedWindowTransforms: 0,
|
|
4333
|
-
uncalibratedWindowWarned: false
|
|
4349
|
+
qualityGateRetryPending: false
|
|
4334
4350
|
};
|
|
4335
4351
|
}
|
|
4336
4352
|
function resetSessionState(state) {
|
|
@@ -4372,8 +4388,6 @@ function resetSessionState(state) {
|
|
|
4372
4388
|
state.modelID = void 0;
|
|
4373
4389
|
state.systemPromptTokens = void 0;
|
|
4374
4390
|
state.qualityGateRetryPending = false;
|
|
4375
|
-
state.uncalibratedWindowTransforms = 0;
|
|
4376
|
-
state.uncalibratedWindowWarned = false;
|
|
4377
4391
|
}
|
|
4378
4392
|
async function ensureSessionInitialized(client, state, sessionId, logger, messages, config) {
|
|
4379
4393
|
if (state.sessionId === sessionId) {
|
|
@@ -6420,66 +6434,159 @@ var filterCompressedRanges = (state, messages) => {
|
|
|
6420
6434
|
messages.push(...result);
|
|
6421
6435
|
};
|
|
6422
6436
|
|
|
6423
|
-
// lib/
|
|
6424
|
-
function
|
|
6425
|
-
const
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
const
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
for (let i = 0; i < 3 && i * chunkSize < blocksWithRef.length; i++) {
|
|
6450
|
-
const chunk = blocksWithRef.slice(i * chunkSize, (i + 1) * chunkSize);
|
|
6451
|
-
if (chunk.length < 2) continue;
|
|
6452
|
-
const startRef = chunk[0].ref;
|
|
6453
|
-
const endRef = chunk[chunk.length - 1].ref;
|
|
6454
|
-
const chunkTokens = chunk.reduce((s, x) => s + (x.block.summaryTokens ?? 0), 0);
|
|
6455
|
-
const chunkK = Math.max(1, Math.round(chunkTokens / 1e3));
|
|
6456
|
-
targets.push(` \u2022 compress ${startRef}\u2192${endRef}: ${chunk.length} blocks (~${chunkK}K tokens)`);
|
|
6437
|
+
// lib/messages/sync.ts
|
|
6438
|
+
function sortBlocksByCreation(a, b) {
|
|
6439
|
+
const createdAtDiff = a.createdAt - b.createdAt;
|
|
6440
|
+
if (createdAtDiff !== 0) {
|
|
6441
|
+
return createdAtDiff;
|
|
6442
|
+
}
|
|
6443
|
+
return a.blockId - b.blockId;
|
|
6444
|
+
}
|
|
6445
|
+
var syncCompressionBlocks = (state, logger, messages) => {
|
|
6446
|
+
const messagesState = state.prune.messages;
|
|
6447
|
+
if (!messagesState?.blocksById?.size) {
|
|
6448
|
+
return;
|
|
6449
|
+
}
|
|
6450
|
+
const messageIds = new Set(messages.map((msg) => msg.info.id));
|
|
6451
|
+
const previousActiveBlockIds = new Set(
|
|
6452
|
+
Array.from(messagesState.blocksById.values()).filter((block) => block.active).map((block) => block.blockId)
|
|
6453
|
+
);
|
|
6454
|
+
messagesState.activeBlockIds.clear();
|
|
6455
|
+
messagesState.activeByAnchorMessageId.clear();
|
|
6456
|
+
const now = Date.now();
|
|
6457
|
+
const orderedBlocks = Array.from(messagesState.blocksById.values()).sort(sortBlocksByCreation);
|
|
6458
|
+
for (const block of orderedBlocks) {
|
|
6459
|
+
if (block.deactivatedByUser || block.deactivatedByUserDeep) {
|
|
6460
|
+
block.active = false;
|
|
6461
|
+
if (block.deactivatedAt === void 0) {
|
|
6462
|
+
block.deactivatedAt = now;
|
|
6457
6463
|
}
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6464
|
+
block.deactivatedByBlockId = void 0;
|
|
6465
|
+
continue;
|
|
6466
|
+
}
|
|
6467
|
+
for (const consumedBlockId of block.consumedBlockIds) {
|
|
6468
|
+
if (!messagesState.activeBlockIds.has(consumedBlockId)) {
|
|
6469
|
+
continue;
|
|
6470
|
+
}
|
|
6471
|
+
const consumedBlock = messagesState.blocksById.get(consumedBlockId);
|
|
6472
|
+
if (consumedBlock) {
|
|
6473
|
+
consumedBlock.active = false;
|
|
6474
|
+
consumedBlock.deactivatedAt = now;
|
|
6475
|
+
consumedBlock.deactivatedByBlockId = block.blockId;
|
|
6476
|
+
const mappedBlockId = messagesState.activeByAnchorMessageId.get(
|
|
6477
|
+
consumedBlock.anchorMessageId
|
|
6478
|
+
);
|
|
6479
|
+
if (mappedBlockId === consumedBlock.blockId) {
|
|
6480
|
+
messagesState.activeByAnchorMessageId.delete(consumedBlock.anchorMessageId);
|
|
6481
|
+
}
|
|
6462
6482
|
}
|
|
6483
|
+
messagesState.activeBlockIds.delete(consumedBlockId);
|
|
6484
|
+
}
|
|
6485
|
+
block.active = true;
|
|
6486
|
+
block.deactivatedAt = void 0;
|
|
6487
|
+
block.deactivatedByBlockId = void 0;
|
|
6488
|
+
messagesState.activeBlockIds.add(block.blockId);
|
|
6489
|
+
if (messageIds.has(block.anchorMessageId)) {
|
|
6490
|
+
messagesState.activeByAnchorMessageId.set(block.anchorMessageId, block.blockId);
|
|
6463
6491
|
}
|
|
6464
6492
|
}
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
return nudgeText;
|
|
6493
|
+
for (const entry of messagesState.byMessageId.values()) {
|
|
6494
|
+
const allBlockIds = Array.isArray(entry.allBlockIds) ? [...new Set(entry.allBlockIds.filter((id) => Number.isInteger(id) && id > 0))] : [];
|
|
6495
|
+
entry.allBlockIds = allBlockIds;
|
|
6496
|
+
entry.activeBlockIds = allBlockIds.filter((id) => messagesState.activeBlockIds.has(id));
|
|
6470
6497
|
}
|
|
6471
|
-
const
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6498
|
+
const nextActiveBlockIds = messagesState.activeBlockIds;
|
|
6499
|
+
let deactivatedCount = 0;
|
|
6500
|
+
let reactivatedCount = 0;
|
|
6501
|
+
for (const blockId of previousActiveBlockIds) {
|
|
6502
|
+
if (!nextActiveBlockIds.has(blockId)) {
|
|
6503
|
+
deactivatedCount++;
|
|
6504
|
+
}
|
|
6475
6505
|
}
|
|
6476
|
-
const
|
|
6477
|
-
|
|
6478
|
-
|
|
6506
|
+
for (const blockId of nextActiveBlockIds) {
|
|
6507
|
+
if (!previousActiveBlockIds.has(blockId)) {
|
|
6508
|
+
reactivatedCount++;
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
if (deactivatedCount > 0 || reactivatedCount > 0) {
|
|
6512
|
+
logger.info("Synced compress block state", {
|
|
6513
|
+
deactivatedCount,
|
|
6514
|
+
reactivatedCount
|
|
6515
|
+
});
|
|
6516
|
+
}
|
|
6517
|
+
};
|
|
6479
6518
|
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6519
|
+
// lib/host-permissions.ts
|
|
6520
|
+
var findLastMatchingRule = (rules, predicate) => {
|
|
6521
|
+
for (let index = rules.length - 1; index >= 0; index -= 1) {
|
|
6522
|
+
const rule = rules[index];
|
|
6523
|
+
if (rule && predicate(rule)) {
|
|
6524
|
+
return rule;
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
return void 0;
|
|
6528
|
+
};
|
|
6529
|
+
var wildcardMatch = (value, pattern) => {
|
|
6530
|
+
const normalizedValue = value.replaceAll("\\", "/");
|
|
6531
|
+
let escaped = pattern.replaceAll("\\", "/").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
6532
|
+
if (escaped.endsWith(" .*")) {
|
|
6533
|
+
escaped = escaped.slice(0, -3) + "( .*)?";
|
|
6534
|
+
}
|
|
6535
|
+
const flags = process.platform === "win32" ? "si" : "s";
|
|
6536
|
+
return new RegExp(`^${escaped}$`, flags).test(normalizedValue);
|
|
6537
|
+
};
|
|
6538
|
+
var getPermissionRules = (permissionConfigs) => {
|
|
6539
|
+
const rules = [];
|
|
6540
|
+
for (const permissionConfig of permissionConfigs) {
|
|
6541
|
+
if (!permissionConfig) {
|
|
6542
|
+
continue;
|
|
6543
|
+
}
|
|
6544
|
+
for (const [permission, value] of Object.entries(permissionConfig)) {
|
|
6545
|
+
if (value === "ask" || value === "allow" || value === "deny") {
|
|
6546
|
+
rules.push({ permission, pattern: "*", action: value });
|
|
6547
|
+
continue;
|
|
6548
|
+
}
|
|
6549
|
+
for (const [pattern, action] of Object.entries(value)) {
|
|
6550
|
+
if (action === "ask" || action === "allow" || action === "deny") {
|
|
6551
|
+
rules.push({ permission, pattern, action });
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6556
|
+
return rules;
|
|
6557
|
+
};
|
|
6558
|
+
var compressDisabledByOpencode = (...permissionConfigs) => {
|
|
6559
|
+
const match = findLastMatchingRule(
|
|
6560
|
+
getPermissionRules(permissionConfigs),
|
|
6561
|
+
(rule) => wildcardMatch("compress", rule.permission)
|
|
6562
|
+
);
|
|
6563
|
+
return match?.pattern === "*" && match.action === "deny";
|
|
6564
|
+
};
|
|
6565
|
+
var resolveEffectiveCompressPermission = (basePermission, hostPermissions, agentName) => {
|
|
6566
|
+
if (basePermission === "deny") {
|
|
6567
|
+
return "deny";
|
|
6568
|
+
}
|
|
6569
|
+
return compressDisabledByOpencode(
|
|
6570
|
+
hostPermissions.global,
|
|
6571
|
+
agentName ? hostPermissions.agents[agentName] : void 0
|
|
6572
|
+
) ? "deny" : basePermission;
|
|
6573
|
+
};
|
|
6574
|
+
var hasExplicitToolPermission = (permissionConfig, tool6) => {
|
|
6575
|
+
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig, tool6) : false;
|
|
6576
|
+
};
|
|
6577
|
+
|
|
6578
|
+
// lib/compress-permission.ts
|
|
6579
|
+
var compressPermission = (state, config) => {
|
|
6580
|
+
return state.compressPermission ?? config.compress.permission;
|
|
6581
|
+
};
|
|
6582
|
+
var syncCompressPermissionState = (state, config, hostPermissions, messages) => {
|
|
6583
|
+
const activeAgent = getLastUserMessage(messages)?.info.agent;
|
|
6584
|
+
state.compressPermission = resolveEffectiveCompressPermission(
|
|
6585
|
+
config.compress.permission,
|
|
6586
|
+
hostPermissions,
|
|
6587
|
+
activeAgent
|
|
6588
|
+
);
|
|
6589
|
+
};
|
|
6483
6590
|
|
|
6484
6591
|
// lib/messages/utils.ts
|
|
6485
6592
|
import { createHash } from "crypto";
|
|
@@ -6655,6 +6762,67 @@ var dropEmptyMessages = (messages) => {
|
|
|
6655
6762
|
return removed;
|
|
6656
6763
|
};
|
|
6657
6764
|
|
|
6765
|
+
// lib/prompts/extensions/nudge.ts
|
|
6766
|
+
function buildCompressedBlockGuidance(state, context) {
|
|
6767
|
+
const activeBlockIds = Array.from(state.prune.messages.activeBlockIds).filter((id) => Number.isInteger(id) && id > 0).sort((a, b) => a - b);
|
|
6768
|
+
const blockCount = activeBlockIds.length;
|
|
6769
|
+
const blocksForStats = activeBlockIds.map((id) => state.prune.messages.blocksById.get(id)).filter((b) => b !== void 0 && b.active);
|
|
6770
|
+
const totalSummaryTokens = blocksForStats.reduce((s, b) => s + (b.summaryTokens ?? 0), 0);
|
|
6771
|
+
const totalSummaryDisplay = totalSummaryTokens >= 1e3 ? `${(totalSummaryTokens / 1e3).toFixed(1)}K` : String(totalSummaryTokens);
|
|
6772
|
+
const lastBlock = blocksForStats.length > 0 ? blocksForStats.reduce((latest, b) => b.createdAt > latest.createdAt ? b : latest) : null;
|
|
6773
|
+
const ageStr = lastBlock ? formatAge(lastBlock.createdAt) : "never";
|
|
6774
|
+
const lines = [
|
|
6775
|
+
`- Compressed blocks: ${blockCount} (${totalSummaryDisplay} summary, last ${ageStr}). Use acp_status for details.`
|
|
6776
|
+
];
|
|
6777
|
+
if (blockCount > 50) {
|
|
6778
|
+
const oldBlockIds = activeBlockIds.slice(0, Math.max(0, blockCount - 20));
|
|
6779
|
+
const allOldBlocks = oldBlockIds.map((id) => state.prune.messages.blocksById.get(id)).filter((b) => b !== void 0);
|
|
6780
|
+
const visibleMessageIds = context?.visibleMessageIds;
|
|
6781
|
+
const visibleOldBlocks = visibleMessageIds === void 0 ? allOldBlocks : allOldBlocks.filter((b) => b.anchorMessageId && visibleMessageIds.has(b.anchorMessageId));
|
|
6782
|
+
if (visibleOldBlocks.length > 5) {
|
|
6783
|
+
const blocksWithRef = visibleOldBlocks.map((block) => {
|
|
6784
|
+
const ref = state.messageIds.byRawId.get(block.anchorMessageId);
|
|
6785
|
+
return ref ? { block, ref } : null;
|
|
6786
|
+
}).filter((x) => x !== null).sort((a, b) => a.ref.localeCompare(b.ref));
|
|
6787
|
+
const totalTokens = blocksWithRef.reduce((s, x) => s + (x.block.summaryTokens ?? 0), 0);
|
|
6788
|
+
const totalK = Math.max(1, Math.round(totalTokens / 1e3));
|
|
6789
|
+
const targets = [];
|
|
6790
|
+
const chunkSize = Math.ceil(blocksWithRef.length / 3);
|
|
6791
|
+
for (let i = 0; i < 3 && i * chunkSize < blocksWithRef.length; i++) {
|
|
6792
|
+
const chunk = blocksWithRef.slice(i * chunkSize, (i + 1) * chunkSize);
|
|
6793
|
+
if (chunk.length < 2) continue;
|
|
6794
|
+
const startRef = chunk[0].ref;
|
|
6795
|
+
const endRef = chunk[chunk.length - 1].ref;
|
|
6796
|
+
const chunkTokens = chunk.reduce((s, x) => s + (x.block.summaryTokens ?? 0), 0);
|
|
6797
|
+
const chunkK = Math.max(1, Math.round(chunkTokens / 1e3));
|
|
6798
|
+
targets.push(` \u2022 compress ${startRef}\u2192${endRef}: ${chunk.length} blocks (~${chunkK}K tokens)`);
|
|
6799
|
+
}
|
|
6800
|
+
if (targets.length > 0) {
|
|
6801
|
+
lines.push(`- \u{1F500} ${blocksWithRef.length} old blocks using ~${totalK}K tokens. Consolidate into ${targets.length}:`);
|
|
6802
|
+
lines.push(...targets);
|
|
6803
|
+
lines.push(` System auto-detects blocks in range \u2014 no need to manually list (bN) placeholders. Just write your summary normally.`);
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
return lines.join("\n");
|
|
6808
|
+
}
|
|
6809
|
+
function appendGuidanceToDcpTag(nudgeText, guidance) {
|
|
6810
|
+
if (!guidance.trim()) {
|
|
6811
|
+
return nudgeText;
|
|
6812
|
+
}
|
|
6813
|
+
const closeTag = "</dcp-system-reminder>";
|
|
6814
|
+
const closeTagIndex = nudgeText.lastIndexOf(closeTag);
|
|
6815
|
+
if (closeTagIndex === -1) {
|
|
6816
|
+
return nudgeText;
|
|
6817
|
+
}
|
|
6818
|
+
const beforeClose = nudgeText.slice(0, closeTagIndex).trimEnd();
|
|
6819
|
+
const afterClose = nudgeText.slice(closeTagIndex);
|
|
6820
|
+
return `${beforeClose}
|
|
6821
|
+
|
|
6822
|
+
${guidance}
|
|
6823
|
+
${afterClose}`;
|
|
6824
|
+
}
|
|
6825
|
+
|
|
6658
6826
|
// node_modules/context-compress-algorithms/dist/chunk-BZYW3CH5.js
|
|
6659
6827
|
var NUDGE_GROWTH_FLOOR = 6e3;
|
|
6660
6828
|
var NUDGE_GROWTH_CAP = 5e4;
|
|
@@ -6757,6 +6925,7 @@ function getModelInfo(messages) {
|
|
|
6757
6925
|
};
|
|
6758
6926
|
}
|
|
6759
6927
|
function resolveContextTokenLimit(config, state, providerId, modelId, threshold) {
|
|
6928
|
+
const effectiveLimit = resolveEffectiveContextLimit(state, config);
|
|
6760
6929
|
const parseLimitValue = (limit) => {
|
|
6761
6930
|
if (limit === void 0) {
|
|
6762
6931
|
return void 0;
|
|
@@ -6764,7 +6933,7 @@ function resolveContextTokenLimit(config, state, providerId, modelId, threshold)
|
|
|
6764
6933
|
if (typeof limit === "number") {
|
|
6765
6934
|
return limit;
|
|
6766
6935
|
}
|
|
6767
|
-
if (!limit.endsWith("%") ||
|
|
6936
|
+
if (!limit.endsWith("%") || effectiveLimit === void 0) {
|
|
6768
6937
|
return void 0;
|
|
6769
6938
|
}
|
|
6770
6939
|
const parsedPercent = parseFloat(limit.slice(0, -1));
|
|
@@ -6773,7 +6942,7 @@ function resolveContextTokenLimit(config, state, providerId, modelId, threshold)
|
|
|
6773
6942
|
}
|
|
6774
6943
|
const roundedPercent = Math.round(parsedPercent);
|
|
6775
6944
|
const clampedPercent = Math.max(0, Math.min(100, roundedPercent));
|
|
6776
|
-
return Math.round(clampedPercent / 100 *
|
|
6945
|
+
return Math.round(clampedPercent / 100 * effectiveLimit.limit);
|
|
6777
6946
|
};
|
|
6778
6947
|
const modelLimits = threshold === "max" ? config.compress.modelMaxLimits : config.compress.modelMinLimits;
|
|
6779
6948
|
if (modelLimits && providerId !== void 0 && modelId !== void 0) {
|
|
@@ -6818,11 +6987,12 @@ function isContextOverLimits(config, state, providerId, modelId, messages) {
|
|
|
6818
6987
|
if (!overMaxLimit) break;
|
|
6819
6988
|
}
|
|
6820
6989
|
}
|
|
6990
|
+
const effectiveLimit = resolveEffectiveContextLimit(state, config);
|
|
6821
6991
|
return {
|
|
6822
6992
|
overMaxLimit,
|
|
6823
6993
|
overMinLimit,
|
|
6824
6994
|
currentTokens,
|
|
6825
|
-
modelContextLimit:
|
|
6995
|
+
modelContextLimit: effectiveLimit?.limit
|
|
6826
6996
|
};
|
|
6827
6997
|
}
|
|
6828
6998
|
ensureBuiltinTriggerPolicyRegistered();
|
|
@@ -7358,277 +7528,6 @@ function excludeProtectedRanges(ranges, protectedRefs) {
|
|
|
7358
7528
|
);
|
|
7359
7529
|
}
|
|
7360
7530
|
|
|
7361
|
-
// lib/messages/prune-to-fit.ts
|
|
7362
|
-
var WIRE_SAFETY_MARGIN = 8192;
|
|
7363
|
-
var CLEAR_PLACEHOLDER = "[cleared by ACP overflow guard \u2014 re-run tool if needed]";
|
|
7364
|
-
var MIN_CLEAR_TOKENS = 500;
|
|
7365
|
-
function resolveKnownWindow(config, state, providerId, modelId) {
|
|
7366
|
-
if (state.modelContextLimit !== void 0) {
|
|
7367
|
-
return state.modelContextLimit;
|
|
7368
|
-
}
|
|
7369
|
-
const maxLimits = config.compress.modelMaxLimits;
|
|
7370
|
-
if (maxLimits && providerId !== void 0 && modelId !== void 0) {
|
|
7371
|
-
const perModel = maxLimits[`${providerId}/${modelId}`];
|
|
7372
|
-
if (typeof perModel === "number") return perModel;
|
|
7373
|
-
}
|
|
7374
|
-
const global = config.compress.maxContextLimit;
|
|
7375
|
-
if (typeof global === "number") return global;
|
|
7376
|
-
return void 0;
|
|
7377
|
-
}
|
|
7378
|
-
function estimateWireTokens(state, messages) {
|
|
7379
|
-
const base = getCurrentTokenUsage(state, messages);
|
|
7380
|
-
if (base > 0) {
|
|
7381
|
-
return base + WIRE_SAFETY_MARGIN;
|
|
7382
|
-
}
|
|
7383
|
-
let total = 0;
|
|
7384
|
-
for (const msg of messages) {
|
|
7385
|
-
total += countAllMessageTokens(msg);
|
|
7386
|
-
}
|
|
7387
|
-
return total + (state.systemPromptTokens ?? 0) + WIRE_SAFETY_MARGIN;
|
|
7388
|
-
}
|
|
7389
|
-
function pruneToFit(state, config, logger, messages) {
|
|
7390
|
-
if (!config.compress.overflowGuard) return;
|
|
7391
|
-
const knownWindow = resolveKnownWindow(config, state, state.modelProviderID, state.modelID);
|
|
7392
|
-
if (knownWindow === void 0) return;
|
|
7393
|
-
const reserve = config.compress.overflowGuardReserve ?? 32768;
|
|
7394
|
-
const safeBudget = knownWindow - reserve;
|
|
7395
|
-
if (safeBudget <= 0) return;
|
|
7396
|
-
const estimate = estimateWireTokens(state, messages);
|
|
7397
|
-
if (estimate <= safeBudget) return;
|
|
7398
|
-
const protectedRefs = computeProtectedRefs(messages, state, config.compress);
|
|
7399
|
-
const lastNonIgnored = findLastNonIgnoredMessage(messages);
|
|
7400
|
-
const lastMsgId = lastNonIgnored?.message.info.id;
|
|
7401
|
-
const protectedTools = config.compress.protectedTools;
|
|
7402
|
-
const protectedFilePatterns = config.protectedFilePatterns;
|
|
7403
|
-
let freed = 0;
|
|
7404
|
-
let clearedCount = 0;
|
|
7405
|
-
for (const msg of messages) {
|
|
7406
|
-
if (estimate - freed <= safeBudget) break;
|
|
7407
|
-
if (msg.info.id === lastMsgId) continue;
|
|
7408
|
-
if (msg.info.role === "user") continue;
|
|
7409
|
-
const ref = state.messageIds.byRawId.get(msg.info.id);
|
|
7410
|
-
if (ref && protectedRefs.has(ref)) continue;
|
|
7411
|
-
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
7412
|
-
for (const part of parts) {
|
|
7413
|
-
if (estimate - freed <= safeBudget) break;
|
|
7414
|
-
if (part?.type !== "tool") continue;
|
|
7415
|
-
const toolState = part.state;
|
|
7416
|
-
if (toolState.status !== "completed") continue;
|
|
7417
|
-
const content = extractCompletedToolOutput(part);
|
|
7418
|
-
if (content === void 0) continue;
|
|
7419
|
-
if (content === CLEAR_PLACEHOLDER) continue;
|
|
7420
|
-
if (content === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
|
|
7421
|
-
if (isToolNameProtected(part.tool, protectedTools)) continue;
|
|
7422
|
-
if (protectedFilePatterns.length > 0) {
|
|
7423
|
-
const filePaths = getFilePathsFromParameters(part.tool, toolState.input);
|
|
7424
|
-
if (isFilePathProtected(filePaths, protectedFilePatterns)) continue;
|
|
7425
|
-
}
|
|
7426
|
-
const outputTokens = countTokens2(content);
|
|
7427
|
-
if (outputTokens < MIN_CLEAR_TOKENS) continue;
|
|
7428
|
-
toolState.output = CLEAR_PLACEHOLDER;
|
|
7429
|
-
freed += outputTokens - countTokens2(CLEAR_PLACEHOLDER);
|
|
7430
|
-
clearedCount++;
|
|
7431
|
-
}
|
|
7432
|
-
}
|
|
7433
|
-
if (clearedCount === 0) return;
|
|
7434
|
-
const after = estimate - freed;
|
|
7435
|
-
const detail = {
|
|
7436
|
-
session: state.sessionId,
|
|
7437
|
-
estimate,
|
|
7438
|
-
safeBudget,
|
|
7439
|
-
knownWindow,
|
|
7440
|
-
reserve,
|
|
7441
|
-
clearedCount,
|
|
7442
|
-
freedTokens: Math.round(freed),
|
|
7443
|
-
afterTokens: Math.round(after)
|
|
7444
|
-
};
|
|
7445
|
-
if (after <= safeBudget) {
|
|
7446
|
-
logger.warn("ACP overflow guard: cleared tool outputs to fit context window", detail);
|
|
7447
|
-
} else {
|
|
7448
|
-
logger.error(
|
|
7449
|
-
"ACP overflow guard: cleared tool outputs but context STILL exceeds window",
|
|
7450
|
-
detail
|
|
7451
|
-
);
|
|
7452
|
-
}
|
|
7453
|
-
}
|
|
7454
|
-
|
|
7455
|
-
// lib/messages/uncalibrated-window.ts
|
|
7456
|
-
var UNCALIBRATED_WINDOW_WARN_THRESHOLD = 3;
|
|
7457
|
-
function trackUncalibratedWindow(state, logger) {
|
|
7458
|
-
if (state.modelContextLimit === void 0) {
|
|
7459
|
-
state.uncalibratedWindowTransforms++;
|
|
7460
|
-
if (state.uncalibratedWindowTransforms >= UNCALIBRATED_WINDOW_WARN_THRESHOLD && !state.uncalibratedWindowWarned) {
|
|
7461
|
-
state.uncalibratedWindowWarned = true;
|
|
7462
|
-
logger.warn(
|
|
7463
|
-
"Model reports no context window \u2014 ACP percentage thresholds are disabled",
|
|
7464
|
-
{
|
|
7465
|
-
session: state.sessionId,
|
|
7466
|
-
provider: state.modelProviderID,
|
|
7467
|
-
model: state.modelID,
|
|
7468
|
-
transforms: state.uncalibratedWindowTransforms,
|
|
7469
|
-
hint: 'set the model\'s `limit` in opencode.json (e.g. {"context": 262144, "output": 16384}) or use absolute compress.maxContextLimit / compress.minContextLimit in acp.jsonc; the request-side overflow guard also needs a known window to fire'
|
|
7470
|
-
}
|
|
7471
|
-
);
|
|
7472
|
-
}
|
|
7473
|
-
} else {
|
|
7474
|
-
state.uncalibratedWindowTransforms = 0;
|
|
7475
|
-
}
|
|
7476
|
-
}
|
|
7477
|
-
|
|
7478
|
-
// lib/messages/sync.ts
|
|
7479
|
-
function sortBlocksByCreation(a, b) {
|
|
7480
|
-
const createdAtDiff = a.createdAt - b.createdAt;
|
|
7481
|
-
if (createdAtDiff !== 0) {
|
|
7482
|
-
return createdAtDiff;
|
|
7483
|
-
}
|
|
7484
|
-
return a.blockId - b.blockId;
|
|
7485
|
-
}
|
|
7486
|
-
var syncCompressionBlocks = (state, logger, messages) => {
|
|
7487
|
-
const messagesState = state.prune.messages;
|
|
7488
|
-
if (!messagesState?.blocksById?.size) {
|
|
7489
|
-
return;
|
|
7490
|
-
}
|
|
7491
|
-
const messageIds = new Set(messages.map((msg) => msg.info.id));
|
|
7492
|
-
const previousActiveBlockIds = new Set(
|
|
7493
|
-
Array.from(messagesState.blocksById.values()).filter((block) => block.active).map((block) => block.blockId)
|
|
7494
|
-
);
|
|
7495
|
-
messagesState.activeBlockIds.clear();
|
|
7496
|
-
messagesState.activeByAnchorMessageId.clear();
|
|
7497
|
-
const now = Date.now();
|
|
7498
|
-
const orderedBlocks = Array.from(messagesState.blocksById.values()).sort(sortBlocksByCreation);
|
|
7499
|
-
for (const block of orderedBlocks) {
|
|
7500
|
-
if (block.deactivatedByUser || block.deactivatedByUserDeep) {
|
|
7501
|
-
block.active = false;
|
|
7502
|
-
if (block.deactivatedAt === void 0) {
|
|
7503
|
-
block.deactivatedAt = now;
|
|
7504
|
-
}
|
|
7505
|
-
block.deactivatedByBlockId = void 0;
|
|
7506
|
-
continue;
|
|
7507
|
-
}
|
|
7508
|
-
for (const consumedBlockId of block.consumedBlockIds) {
|
|
7509
|
-
if (!messagesState.activeBlockIds.has(consumedBlockId)) {
|
|
7510
|
-
continue;
|
|
7511
|
-
}
|
|
7512
|
-
const consumedBlock = messagesState.blocksById.get(consumedBlockId);
|
|
7513
|
-
if (consumedBlock) {
|
|
7514
|
-
consumedBlock.active = false;
|
|
7515
|
-
consumedBlock.deactivatedAt = now;
|
|
7516
|
-
consumedBlock.deactivatedByBlockId = block.blockId;
|
|
7517
|
-
const mappedBlockId = messagesState.activeByAnchorMessageId.get(
|
|
7518
|
-
consumedBlock.anchorMessageId
|
|
7519
|
-
);
|
|
7520
|
-
if (mappedBlockId === consumedBlock.blockId) {
|
|
7521
|
-
messagesState.activeByAnchorMessageId.delete(consumedBlock.anchorMessageId);
|
|
7522
|
-
}
|
|
7523
|
-
}
|
|
7524
|
-
messagesState.activeBlockIds.delete(consumedBlockId);
|
|
7525
|
-
}
|
|
7526
|
-
block.active = true;
|
|
7527
|
-
block.deactivatedAt = void 0;
|
|
7528
|
-
block.deactivatedByBlockId = void 0;
|
|
7529
|
-
messagesState.activeBlockIds.add(block.blockId);
|
|
7530
|
-
if (messageIds.has(block.anchorMessageId)) {
|
|
7531
|
-
messagesState.activeByAnchorMessageId.set(block.anchorMessageId, block.blockId);
|
|
7532
|
-
}
|
|
7533
|
-
}
|
|
7534
|
-
for (const entry of messagesState.byMessageId.values()) {
|
|
7535
|
-
const allBlockIds = Array.isArray(entry.allBlockIds) ? [...new Set(entry.allBlockIds.filter((id) => Number.isInteger(id) && id > 0))] : [];
|
|
7536
|
-
entry.allBlockIds = allBlockIds;
|
|
7537
|
-
entry.activeBlockIds = allBlockIds.filter((id) => messagesState.activeBlockIds.has(id));
|
|
7538
|
-
}
|
|
7539
|
-
const nextActiveBlockIds = messagesState.activeBlockIds;
|
|
7540
|
-
let deactivatedCount = 0;
|
|
7541
|
-
let reactivatedCount = 0;
|
|
7542
|
-
for (const blockId of previousActiveBlockIds) {
|
|
7543
|
-
if (!nextActiveBlockIds.has(blockId)) {
|
|
7544
|
-
deactivatedCount++;
|
|
7545
|
-
}
|
|
7546
|
-
}
|
|
7547
|
-
for (const blockId of nextActiveBlockIds) {
|
|
7548
|
-
if (!previousActiveBlockIds.has(blockId)) {
|
|
7549
|
-
reactivatedCount++;
|
|
7550
|
-
}
|
|
7551
|
-
}
|
|
7552
|
-
if (deactivatedCount > 0 || reactivatedCount > 0) {
|
|
7553
|
-
logger.info("Synced compress block state", {
|
|
7554
|
-
deactivatedCount,
|
|
7555
|
-
reactivatedCount
|
|
7556
|
-
});
|
|
7557
|
-
}
|
|
7558
|
-
};
|
|
7559
|
-
|
|
7560
|
-
// lib/host-permissions.ts
|
|
7561
|
-
var findLastMatchingRule = (rules, predicate) => {
|
|
7562
|
-
for (let index = rules.length - 1; index >= 0; index -= 1) {
|
|
7563
|
-
const rule = rules[index];
|
|
7564
|
-
if (rule && predicate(rule)) {
|
|
7565
|
-
return rule;
|
|
7566
|
-
}
|
|
7567
|
-
}
|
|
7568
|
-
return void 0;
|
|
7569
|
-
};
|
|
7570
|
-
var wildcardMatch = (value, pattern) => {
|
|
7571
|
-
const normalizedValue = value.replaceAll("\\", "/");
|
|
7572
|
-
let escaped = pattern.replaceAll("\\", "/").replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
7573
|
-
if (escaped.endsWith(" .*")) {
|
|
7574
|
-
escaped = escaped.slice(0, -3) + "( .*)?";
|
|
7575
|
-
}
|
|
7576
|
-
const flags = process.platform === "win32" ? "si" : "s";
|
|
7577
|
-
return new RegExp(`^${escaped}$`, flags).test(normalizedValue);
|
|
7578
|
-
};
|
|
7579
|
-
var getPermissionRules = (permissionConfigs) => {
|
|
7580
|
-
const rules = [];
|
|
7581
|
-
for (const permissionConfig of permissionConfigs) {
|
|
7582
|
-
if (!permissionConfig) {
|
|
7583
|
-
continue;
|
|
7584
|
-
}
|
|
7585
|
-
for (const [permission, value] of Object.entries(permissionConfig)) {
|
|
7586
|
-
if (value === "ask" || value === "allow" || value === "deny") {
|
|
7587
|
-
rules.push({ permission, pattern: "*", action: value });
|
|
7588
|
-
continue;
|
|
7589
|
-
}
|
|
7590
|
-
for (const [pattern, action] of Object.entries(value)) {
|
|
7591
|
-
if (action === "ask" || action === "allow" || action === "deny") {
|
|
7592
|
-
rules.push({ permission, pattern, action });
|
|
7593
|
-
}
|
|
7594
|
-
}
|
|
7595
|
-
}
|
|
7596
|
-
}
|
|
7597
|
-
return rules;
|
|
7598
|
-
};
|
|
7599
|
-
var compressDisabledByOpencode = (...permissionConfigs) => {
|
|
7600
|
-
const match = findLastMatchingRule(
|
|
7601
|
-
getPermissionRules(permissionConfigs),
|
|
7602
|
-
(rule) => wildcardMatch("compress", rule.permission)
|
|
7603
|
-
);
|
|
7604
|
-
return match?.pattern === "*" && match.action === "deny";
|
|
7605
|
-
};
|
|
7606
|
-
var resolveEffectiveCompressPermission = (basePermission, hostPermissions, agentName) => {
|
|
7607
|
-
if (basePermission === "deny") {
|
|
7608
|
-
return "deny";
|
|
7609
|
-
}
|
|
7610
|
-
return compressDisabledByOpencode(
|
|
7611
|
-
hostPermissions.global,
|
|
7612
|
-
agentName ? hostPermissions.agents[agentName] : void 0
|
|
7613
|
-
) ? "deny" : basePermission;
|
|
7614
|
-
};
|
|
7615
|
-
var hasExplicitToolPermission = (permissionConfig, tool6) => {
|
|
7616
|
-
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig, tool6) : false;
|
|
7617
|
-
};
|
|
7618
|
-
|
|
7619
|
-
// lib/compress-permission.ts
|
|
7620
|
-
var compressPermission = (state, config) => {
|
|
7621
|
-
return state.compressPermission ?? config.compress.permission;
|
|
7622
|
-
};
|
|
7623
|
-
var syncCompressPermissionState = (state, config, hostPermissions, messages) => {
|
|
7624
|
-
const activeAgent = getLastUserMessage(messages)?.info.agent;
|
|
7625
|
-
state.compressPermission = resolveEffectiveCompressPermission(
|
|
7626
|
-
config.compress.permission,
|
|
7627
|
-
hostPermissions,
|
|
7628
|
-
activeAgent
|
|
7629
|
-
);
|
|
7630
|
-
};
|
|
7631
|
-
|
|
7632
7531
|
// lib/messages/inject/inject.ts
|
|
7633
7532
|
var ACP_SUFFIX_SEED = "acp-dynamic-guidance";
|
|
7634
7533
|
function createSuffixMessage(messages) {
|
|
@@ -8535,8 +8434,9 @@ function createDecompressTool(factoryCtx) {
|
|
|
8535
8434
|
async execute(args, toolCtx) {
|
|
8536
8435
|
const ctx = resolveToolContext(factoryCtx, toolCtx.sessionID);
|
|
8537
8436
|
const { rawMessages } = await prepareDecompressSession(ctx, toolCtx);
|
|
8538
|
-
const
|
|
8539
|
-
|
|
8437
|
+
const effectiveLimitBefore = resolveEffectiveContextLimit(ctx.state, ctx.config);
|
|
8438
|
+
const contextUsageBefore = effectiveLimitBefore ? Math.round(
|
|
8439
|
+
getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitBefore.limit * 100
|
|
8540
8440
|
) : void 0;
|
|
8541
8441
|
const resolved = resolveTargets(args, ctx.state, rawMessages, ctx.logger);
|
|
8542
8442
|
if (!resolved.ok) {
|
|
@@ -8600,8 +8500,9 @@ function createDecompressTool(factoryCtx) {
|
|
|
8600
8500
|
0,
|
|
8601
8501
|
ctx.state.stats.totalPruneTokens - restoredTokens
|
|
8602
8502
|
);
|
|
8603
|
-
const
|
|
8604
|
-
|
|
8503
|
+
const effectiveLimitAfter = resolveEffectiveContextLimit(ctx.state, ctx.config);
|
|
8504
|
+
const contextUsageAfter = effectiveLimitAfter ? Math.round(
|
|
8505
|
+
getCurrentTokenUsage(ctx.state, rawMessages) / effectiveLimitAfter.limit * 100
|
|
8605
8506
|
) : void 0;
|
|
8606
8507
|
await finalizeDecompressSession(ctx);
|
|
8607
8508
|
const restoredContentPreview = buildRestoredContentPreview(
|
|
@@ -9258,7 +9159,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
9258
9159
|
import { join as join3 } from "path";
|
|
9259
9160
|
import { existsSync as existsSync3 } from "fs";
|
|
9260
9161
|
import { homedir as homedir3 } from "os";
|
|
9261
|
-
var LOG_VERSION = true ? "1.14.25-pr.
|
|
9162
|
+
var LOG_VERSION = true ? "1.14.25-pr.349.71" : "dev";
|
|
9262
9163
|
var LEVEL_RANK = {
|
|
9263
9164
|
debug: 10,
|
|
9264
9165
|
info: 20,
|
|
@@ -10082,6 +9983,7 @@ var MIN_OUTPUT_TOKENS = 1e3;
|
|
|
10082
9983
|
var KEEP_PREFIX_CHARS = 2e3;
|
|
10083
9984
|
var KEEP_SUFFIX_CHARS = 2e3;
|
|
10084
9985
|
var PROTECT_RECENT_MESSAGES = 3;
|
|
9986
|
+
var OUTPUT_RESERVE_TOKENS = 16384;
|
|
10085
9987
|
function parseGcThreshold(threshold, modelContextLimit) {
|
|
10086
9988
|
if (typeof threshold === "number") return threshold;
|
|
10087
9989
|
const str = threshold ?? "100%";
|
|
@@ -10090,10 +9992,22 @@ function parseGcThreshold(threshold, modelContextLimit) {
|
|
|
10090
9992
|
return modelContextLimit;
|
|
10091
9993
|
}
|
|
10092
9994
|
function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
10093
|
-
|
|
9995
|
+
const effective = resolveEffectiveContextLimit(state, config);
|
|
9996
|
+
if (!effective) return;
|
|
10094
9997
|
const currentTokens = getCurrentTokenUsage(state, messages);
|
|
10095
9998
|
if (currentTokens === 0) return;
|
|
10096
|
-
const
|
|
9999
|
+
const configuredThreshold = parseGcThreshold(config.gc?.majorGcThresholdPercent, effective.limit);
|
|
10000
|
+
const overhead = (state.systemPromptTokens ?? 0) + OUTPUT_RESERVE_TOKENS;
|
|
10001
|
+
const threshold = Math.min(configuredThreshold, effective.limit - overhead);
|
|
10002
|
+
if (threshold <= 0) {
|
|
10003
|
+
logger.error("ACP: model context window too small to fit overhead", {
|
|
10004
|
+
session: state.sessionId,
|
|
10005
|
+
limit: effective.limit,
|
|
10006
|
+
contextLimitSource: effective.source,
|
|
10007
|
+
overhead
|
|
10008
|
+
});
|
|
10009
|
+
return;
|
|
10010
|
+
}
|
|
10097
10011
|
if (currentTokens < threshold) return;
|
|
10098
10012
|
const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES;
|
|
10099
10013
|
const candidates = [];
|
|
@@ -10138,7 +10052,9 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
|
|
|
10138
10052
|
truncatedCount,
|
|
10139
10053
|
estimatedSavedTokens: Math.round(savedTokens),
|
|
10140
10054
|
currentTokens,
|
|
10141
|
-
threshold
|
|
10055
|
+
threshold,
|
|
10056
|
+
contextLimit: effective.limit,
|
|
10057
|
+
contextLimitSource: effective.source
|
|
10142
10058
|
});
|
|
10143
10059
|
}
|
|
10144
10060
|
}
|
|
@@ -11099,11 +11015,12 @@ function runBatchCleanup(state, config, logger, messages) {
|
|
|
11099
11015
|
mergedCount: 0,
|
|
11100
11016
|
savedTokens: 0
|
|
11101
11017
|
};
|
|
11102
|
-
|
|
11018
|
+
const effective = resolveEffectiveContextLimit(state, config);
|
|
11019
|
+
if (!effective) {
|
|
11103
11020
|
return noop;
|
|
11104
11021
|
}
|
|
11105
11022
|
const currentTokens = getCurrentTokenUsage(state, messages);
|
|
11106
|
-
if (currentTokens <
|
|
11023
|
+
if (currentTokens < effective.limit) {
|
|
11107
11024
|
return noop;
|
|
11108
11025
|
}
|
|
11109
11026
|
const maxMergedLength = config.gc.maxOldGenSummaryLength;
|
|
@@ -11120,7 +11037,8 @@ function runBatchCleanup(state, config, logger, messages) {
|
|
|
11120
11037
|
mergedCount: result.mergedCount,
|
|
11121
11038
|
savedTokens: result.savedTokens,
|
|
11122
11039
|
currentTokens,
|
|
11123
|
-
contextLimit:
|
|
11040
|
+
contextLimit: effective.limit,
|
|
11041
|
+
contextLimitSource: effective.source
|
|
11124
11042
|
});
|
|
11125
11043
|
return {
|
|
11126
11044
|
tier: 3,
|
|
@@ -11154,11 +11072,6 @@ function createSystemPromptHandler(registry4, logger, config, prompts) {
|
|
|
11154
11072
|
input.model?.limit?.context
|
|
11155
11073
|
);
|
|
11156
11074
|
const state = input.sessionID ? registry4.get(input.sessionID) : void 0;
|
|
11157
|
-
if (state && input.model?.limit?.context) {
|
|
11158
|
-
state.modelContextLimit = input.model.limit.context;
|
|
11159
|
-
state.modelProviderID = input.model?.providerID;
|
|
11160
|
-
state.modelID = input.model?.id;
|
|
11161
|
-
}
|
|
11162
11075
|
if (!state || state.isSubAgent && !config.allowSubAgents) {
|
|
11163
11076
|
return;
|
|
11164
11077
|
}
|
|
@@ -11167,6 +11080,19 @@ function createSystemPromptHandler(registry4, logger, config, prompts) {
|
|
|
11167
11080
|
logger.info("Skipping DCP system prompt injection for internal agent");
|
|
11168
11081
|
return;
|
|
11169
11082
|
}
|
|
11083
|
+
if (input.model?.limit?.context) {
|
|
11084
|
+
const limit = input.model.limit.context;
|
|
11085
|
+
const providerID = input.model?.providerID;
|
|
11086
|
+
const modelID = input.model?.id;
|
|
11087
|
+
const changed = state.modelContextLimit !== limit || state.modelProviderID !== providerID || state.modelID !== modelID;
|
|
11088
|
+
state.modelContextLimit = limit;
|
|
11089
|
+
state.modelProviderID = providerID;
|
|
11090
|
+
state.modelID = modelID;
|
|
11091
|
+
if (changed) {
|
|
11092
|
+
saveSessionState(state, logger).catch(() => {
|
|
11093
|
+
});
|
|
11094
|
+
}
|
|
11095
|
+
}
|
|
11170
11096
|
const effectivePermission = compressPermission(state, config);
|
|
11171
11097
|
if (effectivePermission === "deny") {
|
|
11172
11098
|
return;
|
|
@@ -11211,10 +11137,17 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11211
11137
|
config
|
|
11212
11138
|
);
|
|
11213
11139
|
const requestModel = lastUserMessage.info.model;
|
|
11214
|
-
|
|
11140
|
+
let requestModelLimit = registry4.resolveModelLimit(
|
|
11215
11141
|
requestModel?.providerID,
|
|
11216
11142
|
requestModel?.modelID
|
|
11217
11143
|
);
|
|
11144
|
+
if (requestModelLimit === void 0 && requestModel?.providerID && requestModel?.modelID) {
|
|
11145
|
+
requestModelLimit = await registry4.hydrateAndResolve(
|
|
11146
|
+
client,
|
|
11147
|
+
requestModel.providerID,
|
|
11148
|
+
requestModel.modelID
|
|
11149
|
+
);
|
|
11150
|
+
}
|
|
11218
11151
|
const prevModelID = state.modelID;
|
|
11219
11152
|
if (requestModelLimit !== void 0) {
|
|
11220
11153
|
state.modelContextLimit = requestModelLimit;
|
|
@@ -11244,7 +11177,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11244
11177
|
});
|
|
11245
11178
|
}
|
|
11246
11179
|
await updatePerTurnState(state, logger, messages);
|
|
11247
|
-
trackUncalibratedWindow(state, logger);
|
|
11248
11180
|
}
|
|
11249
11181
|
syncCompressPermissionState(state, config, hostPermissions, output.messages);
|
|
11250
11182
|
if (state.isSubAgent && !config.allowSubAgents) {
|
|
@@ -11252,10 +11184,11 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11252
11184
|
}
|
|
11253
11185
|
stripHallucinations(output.messages);
|
|
11254
11186
|
ensureBuiltinFiltersRegistered();
|
|
11187
|
+
const effectiveLimit = resolveEffectiveContextLimit(state, config);
|
|
11255
11188
|
applyMessageFilters(output.messages, config.messageFilters, logger, {
|
|
11256
11189
|
sessionId: state.sessionId ?? "",
|
|
11257
11190
|
isSubAgent: state.isSubAgent,
|
|
11258
|
-
modelContextLimit:
|
|
11191
|
+
modelContextLimit: effectiveLimit?.limit
|
|
11259
11192
|
});
|
|
11260
11193
|
cacheSystemPromptTokens(state, output.messages);
|
|
11261
11194
|
assignMessageRefs(state, output.messages);
|
|
@@ -11275,7 +11208,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
|
|
|
11275
11208
|
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
11276
11209
|
prune(state, logger, config, output.messages);
|
|
11277
11210
|
truncateLargeToolOutputs(state, config, logger, output.messages);
|
|
11278
|
-
pruneToFit(state, config, logger, output.messages);
|
|
11279
11211
|
hideConsumedCompressCalls(state, output.messages);
|
|
11280
11212
|
assignMessageRefs(state, output.messages);
|
|
11281
11213
|
const compressionPriorities = buildPriorityMap(config, state, output.messages);
|
|
@@ -11307,14 +11239,31 @@ ${text}`);
|
|
|
11307
11239
|
stripStaleMetadata(output.messages);
|
|
11308
11240
|
dropEmptyMessages(output.messages);
|
|
11309
11241
|
const postTokens = getCurrentTokenUsage(state, output.messages);
|
|
11242
|
+
if (postTokens !== void 0 && effectiveLimit) {
|
|
11243
|
+
const budget = effectiveLimit.limit - (state.systemPromptTokens ?? 0) - OUTPUT_RESERVE_TOKENS;
|
|
11244
|
+
if (postTokens > budget) {
|
|
11245
|
+
logger.error(
|
|
11246
|
+
"ACP hard guard: context exceeds model budget after in-flight reduction",
|
|
11247
|
+
{
|
|
11248
|
+
session: state.sessionId,
|
|
11249
|
+
postTokens,
|
|
11250
|
+
budget,
|
|
11251
|
+
contextLimit: effectiveLimit.limit,
|
|
11252
|
+
contextLimitSource: effectiveLimit.source,
|
|
11253
|
+
hint: "request will likely be rejected; run /compact or start a new session"
|
|
11254
|
+
}
|
|
11255
|
+
);
|
|
11256
|
+
}
|
|
11257
|
+
}
|
|
11310
11258
|
logger.info("Chat transform complete", {
|
|
11311
11259
|
session: state.sessionId,
|
|
11312
11260
|
model: state.modelID,
|
|
11313
11261
|
messages: output.messages.length,
|
|
11314
11262
|
prePruneTokens,
|
|
11315
11263
|
postTokens,
|
|
11316
|
-
contextLimit:
|
|
11317
|
-
|
|
11264
|
+
contextLimit: effectiveLimit?.limit,
|
|
11265
|
+
contextLimitSource: effectiveLimit?.source,
|
|
11266
|
+
usagePct: postTokens !== void 0 && effectiveLimit ? `${(postTokens / effectiveLimit.limit * 100).toFixed(1)}%` : void 0,
|
|
11318
11267
|
nudged: state.nudges.shouldInjectThisTurn
|
|
11319
11268
|
});
|
|
11320
11269
|
if (state.sessionId) {
|
|
@@ -11701,7 +11650,7 @@ var server = (async (ctx) => {
|
|
|
11701
11650
|
}
|
|
11702
11651
|
const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
|
|
11703
11652
|
logger.info("ACP plugin initialized", {
|
|
11704
|
-
version: true ? "1.14.25-pr.
|
|
11653
|
+
version: true ? "1.14.25-pr.349.71" : "dev",
|
|
11705
11654
|
workspace: ctx.directory,
|
|
11706
11655
|
logLevel: logger.level,
|
|
11707
11656
|
debug: config.debug,
|