billion-context-pi 0.1.46 → 0.1.47-pr.217.13
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 +772 -103
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +16 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -324,7 +324,7 @@ function viableRanges(ranges) {
|
|
|
324
324
|
return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
// node_modules/acp-kernel/dist/chunk-
|
|
327
|
+
// node_modules/acp-kernel/dist/chunk-UX4LINT7.js
|
|
328
328
|
function createInitialState() {
|
|
329
329
|
return {
|
|
330
330
|
blocks: [],
|
|
@@ -337,7 +337,8 @@ function createInitialState() {
|
|
|
337
337
|
anchors: {},
|
|
338
338
|
lastShownByTier: {}
|
|
339
339
|
},
|
|
340
|
-
stats: { tokensCompressed: 0, compressionCount: 0 },
|
|
340
|
+
stats: { tokensCompressed: 0, compressionCount: 0, absorbedTokens: 0 },
|
|
341
|
+
absorbed: [],
|
|
341
342
|
nextBlockId: 1,
|
|
342
343
|
nextRunId: 1
|
|
343
344
|
};
|
|
@@ -601,6 +602,7 @@ function syncBlocks(messages, state) {
|
|
|
601
602
|
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
602
603
|
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
603
604
|
stats: { ...state.stats },
|
|
605
|
+
absorbed: (state.absorbed ?? []).map((record) => ({ ...record })),
|
|
604
606
|
nextBlockId: state.nextBlockId,
|
|
605
607
|
nextRunId: state.nextRunId
|
|
606
608
|
};
|
|
@@ -661,7 +663,14 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
661
663
|
protectedTools: [],
|
|
662
664
|
preserveRecentMessages: 5,
|
|
663
665
|
preserveRecentTokens: 5e3,
|
|
664
|
-
modelContextLimit
|
|
666
|
+
modelContextLimit,
|
|
667
|
+
absorb: {
|
|
668
|
+
enabled: false,
|
|
669
|
+
toolName: "absorb",
|
|
670
|
+
minToolTokens: 1e3,
|
|
671
|
+
contextThresholdPct: 0,
|
|
672
|
+
excludeTools: []
|
|
673
|
+
}
|
|
665
674
|
};
|
|
666
675
|
return {
|
|
667
676
|
...base,
|
|
@@ -669,7 +678,8 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
669
678
|
tiers: { ...base.tiers, ...overrides.tiers },
|
|
670
679
|
nudge: { ...base.nudge, ...overrides.nudge },
|
|
671
680
|
truncate: { ...base.truncate, ...overrides.truncate },
|
|
672
|
-
compress: { ...base.compress, ...overrides.compress }
|
|
681
|
+
compress: { ...base.compress, ...overrides.compress },
|
|
682
|
+
absorb: overrides.absorb ? { ...base.absorb, ...overrides.absorb } : base.absorb
|
|
673
683
|
};
|
|
674
684
|
}
|
|
675
685
|
function validateConfig(config) {
|
|
@@ -699,6 +709,17 @@ function validateConfig(config) {
|
|
|
699
709
|
if (config.tiers.tier3Trigger <= config.tiers.tier2Trigger) {
|
|
700
710
|
errors.push("tiers.tier3Trigger must be greater than tiers.tier2Trigger");
|
|
701
711
|
}
|
|
712
|
+
if (config.absorb) {
|
|
713
|
+
if (config.absorb.enabled && !config.absorb.toolName) {
|
|
714
|
+
errors.push("absorb.toolName must be a non-empty string when enabled");
|
|
715
|
+
}
|
|
716
|
+
if (!Number.isFinite(config.absorb.minToolTokens) || config.absorb.minToolTokens < 0) {
|
|
717
|
+
errors.push("absorb.minToolTokens must be >= 0");
|
|
718
|
+
}
|
|
719
|
+
if (config.absorb.contextThresholdPct < 0 || config.absorb.contextThresholdPct > 1) {
|
|
720
|
+
errors.push("absorb.contextThresholdPct must be in [0, 1]");
|
|
721
|
+
}
|
|
722
|
+
}
|
|
702
723
|
return errors;
|
|
703
724
|
}
|
|
704
725
|
var MESSAGE_REF_PATTERN = /^m0*(\d{1,5})$/;
|
|
@@ -1055,6 +1076,306 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
1055
1076
|
}
|
|
1056
1077
|
return { messages: result, hidden };
|
|
1057
1078
|
}
|
|
1079
|
+
var COMPRESS_TOOL_NAME = "compress";
|
|
1080
|
+
var DECOMPRESS_TOOL_NAME = "decompress";
|
|
1081
|
+
var SEARCH_CONTEXT_TOOL_NAME = "search_context";
|
|
1082
|
+
var ACP_STATUS_TOOL_NAME = "acp_status";
|
|
1083
|
+
var ABSORB_TOOL_NAME = "absorb";
|
|
1084
|
+
var COMPRESS_TOOL = {
|
|
1085
|
+
name: COMPRESS_TOOL_NAME,
|
|
1086
|
+
description: "Replace a contiguous range of older conversation with a detailed summary you write. Use when content is genuinely consumed. Batch form: content=[{startId,endId,summary,topic?}]. REQUIRED \u2014 compress without content is invalid.",
|
|
1087
|
+
input_schema: {
|
|
1088
|
+
type: "object",
|
|
1089
|
+
properties: {
|
|
1090
|
+
topic: {
|
|
1091
|
+
type: "string",
|
|
1092
|
+
description: "Optional short title for the compressed range"
|
|
1093
|
+
},
|
|
1094
|
+
content: {
|
|
1095
|
+
type: "array",
|
|
1096
|
+
description: "One or more ranges to compress into separate summary blocks",
|
|
1097
|
+
items: {
|
|
1098
|
+
type: "object",
|
|
1099
|
+
properties: {
|
|
1100
|
+
topic: { type: "string" },
|
|
1101
|
+
startId: {
|
|
1102
|
+
type: "string",
|
|
1103
|
+
description: "mNNNNN ref at the start of the range"
|
|
1104
|
+
},
|
|
1105
|
+
endId: {
|
|
1106
|
+
type: "string",
|
|
1107
|
+
description: "mNNNNN ref at the end of the range"
|
|
1108
|
+
},
|
|
1109
|
+
summary: {
|
|
1110
|
+
type: "string",
|
|
1111
|
+
description: "Self-contained summary replacing the range"
|
|
1112
|
+
}
|
|
1113
|
+
},
|
|
1114
|
+
required: ["startId", "endId", "summary"]
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
required: ["content"]
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
var COMPRESS_TOOL_OPENAI = {
|
|
1122
|
+
type: "function",
|
|
1123
|
+
function: {
|
|
1124
|
+
name: COMPRESS_TOOL_NAME,
|
|
1125
|
+
description: COMPRESS_TOOL.description,
|
|
1126
|
+
parameters: {
|
|
1127
|
+
type: "object",
|
|
1128
|
+
properties: {
|
|
1129
|
+
topic: {
|
|
1130
|
+
type: "string",
|
|
1131
|
+
description: "Optional short title for the compressed range"
|
|
1132
|
+
},
|
|
1133
|
+
content: {
|
|
1134
|
+
type: "array",
|
|
1135
|
+
description: "One or more ranges to compress into separate summary blocks. REQUIRED \u2014 compress without content is invalid.",
|
|
1136
|
+
items: {
|
|
1137
|
+
type: "object",
|
|
1138
|
+
properties: {
|
|
1139
|
+
topic: { type: "string" },
|
|
1140
|
+
startId: {
|
|
1141
|
+
type: "string",
|
|
1142
|
+
description: "mNNNNN ref at the start of the range"
|
|
1143
|
+
},
|
|
1144
|
+
endId: {
|
|
1145
|
+
type: "string",
|
|
1146
|
+
description: "mNNNNN ref at the end of the range"
|
|
1147
|
+
},
|
|
1148
|
+
summary: {
|
|
1149
|
+
type: "string",
|
|
1150
|
+
description: "Self-contained summary replacing the range"
|
|
1151
|
+
}
|
|
1152
|
+
},
|
|
1153
|
+
required: ["startId", "endId", "summary"]
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
required: ["content"]
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1161
|
+
var DECOMPRESS_TOOL_OPENAI = {
|
|
1162
|
+
type: "function",
|
|
1163
|
+
function: {
|
|
1164
|
+
name: DECOMPRESS_TOOL_NAME,
|
|
1165
|
+
description: "Restores previously compressed content. Use when you need exact details lost in compression. By default restores one tier up. Use full:true for all the way to original messages. Use toFile to write to file instead of inflating context.",
|
|
1166
|
+
parameters: {
|
|
1167
|
+
type: "object",
|
|
1168
|
+
properties: {
|
|
1169
|
+
blockId: {
|
|
1170
|
+
type: "string",
|
|
1171
|
+
description: "Block ID to decompress (e.g. b5)"
|
|
1172
|
+
},
|
|
1173
|
+
toFile: {
|
|
1174
|
+
type: "string",
|
|
1175
|
+
description: "Optional: write content to file instead of context"
|
|
1176
|
+
},
|
|
1177
|
+
full: {
|
|
1178
|
+
type: "boolean",
|
|
1179
|
+
description: "Restore all the way to original messages"
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
required: ["blockId"]
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
var SEARCH_CONTEXT_TOOL_OPENAI = {
|
|
1187
|
+
type: "function",
|
|
1188
|
+
function: {
|
|
1189
|
+
name: SEARCH_CONTEXT_TOOL_NAME,
|
|
1190
|
+
description: "Search through compressed block summaries by keyword. Use BEFORE decompressing to find the right block.",
|
|
1191
|
+
parameters: {
|
|
1192
|
+
type: "object",
|
|
1193
|
+
properties: {
|
|
1194
|
+
query: { type: "string", description: "Search query" },
|
|
1195
|
+
limit: { type: "number", description: "Max results (default 5)" }
|
|
1196
|
+
},
|
|
1197
|
+
required: ["query"]
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
var ACP_STATUS_TOOL_OPENAI = {
|
|
1202
|
+
type: "function",
|
|
1203
|
+
function: {
|
|
1204
|
+
name: ACP_STATUS_TOOL_NAME,
|
|
1205
|
+
description: "Show context usage and compressible ranges. No args = overview. Use to find what to compress next.",
|
|
1206
|
+
parameters: {
|
|
1207
|
+
type: "object",
|
|
1208
|
+
properties: {}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
var DECOMPRESS_TOOL = {
|
|
1213
|
+
name: DECOMPRESS_TOOL_NAME,
|
|
1214
|
+
description: DECOMPRESS_TOOL_OPENAI.function.description,
|
|
1215
|
+
input_schema: DECOMPRESS_TOOL_OPENAI.function.parameters
|
|
1216
|
+
};
|
|
1217
|
+
var SEARCH_CONTEXT_TOOL = {
|
|
1218
|
+
name: SEARCH_CONTEXT_TOOL_NAME,
|
|
1219
|
+
description: SEARCH_CONTEXT_TOOL_OPENAI.function.description,
|
|
1220
|
+
input_schema: SEARCH_CONTEXT_TOOL_OPENAI.function.parameters
|
|
1221
|
+
};
|
|
1222
|
+
var ACP_STATUS_TOOL = {
|
|
1223
|
+
name: ACP_STATUS_TOOL_NAME,
|
|
1224
|
+
description: ACP_STATUS_TOOL_OPENAI.function.description,
|
|
1225
|
+
input_schema: ACP_STATUS_TOOL_OPENAI.function.parameters
|
|
1226
|
+
};
|
|
1227
|
+
var COMPRESS_TOOL_RESPONSES = {
|
|
1228
|
+
type: "function",
|
|
1229
|
+
name: COMPRESS_TOOL_NAME,
|
|
1230
|
+
description: COMPRESS_TOOL.description,
|
|
1231
|
+
parameters: COMPRESS_TOOL_OPENAI.function.parameters
|
|
1232
|
+
};
|
|
1233
|
+
var DECOMPRESS_TOOL_RESPONSES = {
|
|
1234
|
+
type: "function",
|
|
1235
|
+
name: DECOMPRESS_TOOL_OPENAI.function.name,
|
|
1236
|
+
description: DECOMPRESS_TOOL_OPENAI.function.description,
|
|
1237
|
+
parameters: DECOMPRESS_TOOL_OPENAI.function.parameters
|
|
1238
|
+
};
|
|
1239
|
+
var SEARCH_CONTEXT_TOOL_RESPONSES = {
|
|
1240
|
+
type: "function",
|
|
1241
|
+
name: SEARCH_CONTEXT_TOOL_OPENAI.function.name,
|
|
1242
|
+
description: SEARCH_CONTEXT_TOOL_OPENAI.function.description,
|
|
1243
|
+
parameters: SEARCH_CONTEXT_TOOL_OPENAI.function.parameters
|
|
1244
|
+
};
|
|
1245
|
+
var ACP_STATUS_TOOL_RESPONSES = {
|
|
1246
|
+
type: "function",
|
|
1247
|
+
name: ACP_STATUS_TOOL_OPENAI.function.name,
|
|
1248
|
+
description: ACP_STATUS_TOOL_OPENAI.function.description,
|
|
1249
|
+
parameters: ACP_STATUS_TOOL_OPENAI.function.parameters
|
|
1250
|
+
};
|
|
1251
|
+
var ACP_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1252
|
+
COMPRESS_TOOL_NAME,
|
|
1253
|
+
DECOMPRESS_TOOL_NAME,
|
|
1254
|
+
SEARCH_CONTEXT_TOOL_NAME,
|
|
1255
|
+
ACP_STATUS_TOOL_NAME
|
|
1256
|
+
]);
|
|
1257
|
+
var ALWAYS_PROTECTED_TOOLS = ["compress"];
|
|
1258
|
+
var NEVER_PRESERVE_RECENT_TOOLS = [
|
|
1259
|
+
"decompress",
|
|
1260
|
+
"search_context",
|
|
1261
|
+
"read",
|
|
1262
|
+
"bash"
|
|
1263
|
+
];
|
|
1264
|
+
function isNeverPreserveRecent(msg) {
|
|
1265
|
+
if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result") {
|
|
1266
|
+
return false;
|
|
1267
|
+
}
|
|
1268
|
+
if (!msg.toolName) return false;
|
|
1269
|
+
return NEVER_PRESERVE_RECENT_TOOLS.includes(msg.toolName);
|
|
1270
|
+
}
|
|
1271
|
+
function matchToolPattern(toolName, pattern) {
|
|
1272
|
+
if (pattern.endsWith("*")) {
|
|
1273
|
+
return toolName.startsWith(pattern.slice(0, -1));
|
|
1274
|
+
}
|
|
1275
|
+
return toolName === pattern;
|
|
1276
|
+
}
|
|
1277
|
+
function isMessageProtected(msg, config) {
|
|
1278
|
+
if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result" || !msg.toolName) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
if (ALWAYS_PROTECTED_TOOLS.includes(msg.toolName)) {
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
for (const pattern of config.protectedTools) {
|
|
1285
|
+
if (matchToolPattern(msg.toolName, pattern)) return true;
|
|
1286
|
+
}
|
|
1287
|
+
if (config.isToolProtected?.(msg.toolName, msg.text)) return true;
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
function collectProtectedToolCallIds(messages, config) {
|
|
1291
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1292
|
+
for (const m of messages) {
|
|
1293
|
+
if (m.contentType === "tool-call" && m.toolCallId && isMessageProtected(m, config)) {
|
|
1294
|
+
ids.add(m.toolCallId);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
return ids;
|
|
1298
|
+
}
|
|
1299
|
+
function isMessageProtectedWithPairing(msg, config, protectedCallIds) {
|
|
1300
|
+
if (isMessageProtected(msg, config)) return true;
|
|
1301
|
+
if (msg.contentType === "tool-result" && msg.toolCallId && protectedCallIds.has(msg.toolCallId)) {
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
var ABSORB_PROMPT_MARKER = "[ACP absorb]";
|
|
1307
|
+
var DEFAULT_ABSORB_CONFIG = {
|
|
1308
|
+
enabled: false,
|
|
1309
|
+
toolName: ABSORB_TOOL_NAME,
|
|
1310
|
+
minToolTokens: 1e3,
|
|
1311
|
+
contextThresholdPct: 0,
|
|
1312
|
+
excludeTools: []
|
|
1313
|
+
};
|
|
1314
|
+
function resolveAbsorbConfig(config) {
|
|
1315
|
+
return { ...DEFAULT_ABSORB_CONFIG, ...config.absorb ?? {} };
|
|
1316
|
+
}
|
|
1317
|
+
function formatTokenCount(tokens) {
|
|
1318
|
+
if (tokens < 1e3) return String(tokens);
|
|
1319
|
+
if (tokens < 1e4) return (tokens / 1e3).toFixed(1) + "K";
|
|
1320
|
+
return Math.round(tokens / 1e3) + "K";
|
|
1321
|
+
}
|
|
1322
|
+
function buildAbsorbPrompt(ref, tokens, toolName = ABSORB_TOOL_NAME) {
|
|
1323
|
+
return `${ABSORB_PROMPT_MARKER} This tool result (~${formatTokenCount(tokens)} tokens) will be REMOVED from context. Your IMMEDIATE next action: call ${toolName}({ ref: "${ref}", summary: "..." }) \u2014 summary = distilled essentials only (outcome, key values, exact paths:lines, error text verbatim, decisions). Afterwards work from your summary; do NOT re-run this tool. If the result contains nothing you need, call ${toolName} with summary "(nothing needed)".`;
|
|
1324
|
+
}
|
|
1325
|
+
function isAcpOrConfiguredTool(toolName, cfg) {
|
|
1326
|
+
if (!toolName) return false;
|
|
1327
|
+
if (toolName === cfg.toolName) return true;
|
|
1328
|
+
return ACP_TOOL_NAMES.has(toolName);
|
|
1329
|
+
}
|
|
1330
|
+
function isAbsorbCandidate(msg, config) {
|
|
1331
|
+
if (msg.contentType !== "tool-result" || !msg.toolCallId) return false;
|
|
1332
|
+
const cfg = resolveAbsorbConfig(config);
|
|
1333
|
+
if (isAcpOrConfiguredTool(msg.toolName, cfg)) return false;
|
|
1334
|
+
if (isMessageProtected(msg, config)) return false;
|
|
1335
|
+
for (const pattern of cfg.excludeTools) {
|
|
1336
|
+
if (msg.toolName && matchToolPattern(msg.toolName, pattern)) return false;
|
|
1337
|
+
}
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1340
|
+
function hideAbsorbedMessages(messages, state) {
|
|
1341
|
+
const records = state.absorbed ?? [];
|
|
1342
|
+
if (records.length === 0) return messages;
|
|
1343
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
1344
|
+
for (const record of records) {
|
|
1345
|
+
if (record.callMessageId) hidden.add(record.callMessageId);
|
|
1346
|
+
if (record.resultMessageId) hidden.add(record.resultMessageId);
|
|
1347
|
+
}
|
|
1348
|
+
return messages.filter((msg) => !hidden.has(msg.id));
|
|
1349
|
+
}
|
|
1350
|
+
function appendAbsorbPrompts(messages, state, config, tokenCount, countTokens) {
|
|
1351
|
+
const cfg = resolveAbsorbConfig(config);
|
|
1352
|
+
if (!cfg.enabled) return { messages, promptedCount: 0 };
|
|
1353
|
+
const limit = config.modelContextLimit;
|
|
1354
|
+
if (cfg.contextThresholdPct > 0 && limit > 0 && tokenCount < cfg.contextThresholdPct * limit) {
|
|
1355
|
+
return { messages, promptedCount: 0 };
|
|
1356
|
+
}
|
|
1357
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
1358
|
+
for (const record of state.absorbed ?? []) {
|
|
1359
|
+
if (record.resultMessageId) absorbedIds.add(record.resultMessageId);
|
|
1360
|
+
}
|
|
1361
|
+
let promptedCount = 0;
|
|
1362
|
+
const out = messages.map((msg) => {
|
|
1363
|
+
if (!isAbsorbCandidate(msg, config)) return msg;
|
|
1364
|
+
if (absorbedIds.has(msg.id)) return msg;
|
|
1365
|
+
const text = msg.text ?? "";
|
|
1366
|
+
if (text.includes(ABSORB_PROMPT_MARKER)) return msg;
|
|
1367
|
+
const tokens = countTokens(text);
|
|
1368
|
+
if (tokens < cfg.minToolTokens) return msg;
|
|
1369
|
+
const ref = refForRaw(state.messageRefs, msg.id);
|
|
1370
|
+
if (!ref || ref === BLOCKED_REF) return msg;
|
|
1371
|
+
promptedCount++;
|
|
1372
|
+
return {
|
|
1373
|
+
...msg,
|
|
1374
|
+
text: text + "\n\n" + buildAbsorbPrompt(ref, tokens, cfg.toolName)
|
|
1375
|
+
};
|
|
1376
|
+
});
|
|
1377
|
+
return { messages: out, promptedCount };
|
|
1378
|
+
}
|
|
1058
1379
|
var registry = /* @__PURE__ */ new Map();
|
|
1059
1380
|
function listMessageFilters() {
|
|
1060
1381
|
return [...registry.values()];
|
|
@@ -1205,55 +1526,6 @@ function createRenderRefsNode(strategy) {
|
|
|
1205
1526
|
};
|
|
1206
1527
|
}
|
|
1207
1528
|
var renderRefsNode = createRenderRefsNode("all");
|
|
1208
|
-
var ALWAYS_PROTECTED_TOOLS = ["compress"];
|
|
1209
|
-
var NEVER_PRESERVE_RECENT_TOOLS = [
|
|
1210
|
-
"decompress",
|
|
1211
|
-
"search_context",
|
|
1212
|
-
"read",
|
|
1213
|
-
"bash"
|
|
1214
|
-
];
|
|
1215
|
-
function isNeverPreserveRecent(msg) {
|
|
1216
|
-
if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result") {
|
|
1217
|
-
return false;
|
|
1218
|
-
}
|
|
1219
|
-
if (!msg.toolName) return false;
|
|
1220
|
-
return NEVER_PRESERVE_RECENT_TOOLS.includes(msg.toolName);
|
|
1221
|
-
}
|
|
1222
|
-
function matchToolPattern(toolName, pattern) {
|
|
1223
|
-
if (pattern.endsWith("*")) {
|
|
1224
|
-
return toolName.startsWith(pattern.slice(0, -1));
|
|
1225
|
-
}
|
|
1226
|
-
return toolName === pattern;
|
|
1227
|
-
}
|
|
1228
|
-
function isMessageProtected(msg, config) {
|
|
1229
|
-
if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result" || !msg.toolName) {
|
|
1230
|
-
return false;
|
|
1231
|
-
}
|
|
1232
|
-
if (ALWAYS_PROTECTED_TOOLS.includes(msg.toolName)) {
|
|
1233
|
-
return true;
|
|
1234
|
-
}
|
|
1235
|
-
for (const pattern of config.protectedTools) {
|
|
1236
|
-
if (matchToolPattern(msg.toolName, pattern)) return true;
|
|
1237
|
-
}
|
|
1238
|
-
if (config.isToolProtected?.(msg.toolName, msg.text)) return true;
|
|
1239
|
-
return false;
|
|
1240
|
-
}
|
|
1241
|
-
function collectProtectedToolCallIds(messages, config) {
|
|
1242
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1243
|
-
for (const m of messages) {
|
|
1244
|
-
if (m.contentType === "tool-call" && m.toolCallId && isMessageProtected(m, config)) {
|
|
1245
|
-
ids.add(m.toolCallId);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
return ids;
|
|
1249
|
-
}
|
|
1250
|
-
function isMessageProtectedWithPairing(msg, config, protectedCallIds) {
|
|
1251
|
-
if (isMessageProtected(msg, config)) return true;
|
|
1252
|
-
if (msg.contentType === "tool-result" && msg.toolCallId && protectedCallIds.has(msg.toolCallId)) {
|
|
1253
|
-
return true;
|
|
1254
|
-
}
|
|
1255
|
-
return false;
|
|
1256
|
-
}
|
|
1257
1529
|
function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan = 20) {
|
|
1258
1530
|
const callIdsInRange = /* @__PURE__ */ new Set();
|
|
1259
1531
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
@@ -1628,7 +1900,7 @@ function createCore(ports = {}) {
|
|
|
1628
1900
|
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
1629
1901
|
const live = activeBlocks(state).map((b) => b.blockId).sort((x, y) => numericBlockId(x) - numericBlockId(y));
|
|
1630
1902
|
const liveHint = live.length > 0 ? ` Current active blocks span ${live[0]}..${live[live.length - 1]} \u2014 retry with startId/endId set to active block IDs in that span.` : "";
|
|
1631
|
-
const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref failed with "does not exist in this session". Refs recorded before an earlier compress are stale: each successful compress renumbers the remaining refs. Run acp_status, then
|
|
1903
|
+
const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref failed with "does not exist in this session". Refs recorded before an earlier compress are stale: each successful compress renumbers the remaining refs. Run acp_status, then call the compress tool again using only the refs it reports.` : consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}) \u2014 your refs are stale: a prior compress renumbered the remaining refs, so this range now falls inside an active block. Run acp_status, then call the compress tool again using only the CURRENT compressible ranges it reports.${liveHint}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
|
|
1632
1904
|
return {
|
|
1633
1905
|
state: input.state,
|
|
1634
1906
|
result: {
|
|
@@ -1746,6 +2018,8 @@ function createCore(ports = {}) {
|
|
|
1746
2018
|
assignRefsNode,
|
|
1747
2019
|
syncBlocksNode,
|
|
1748
2020
|
pruneNode,
|
|
2021
|
+
absorbHideNode,
|
|
2022
|
+
absorbPromptNode,
|
|
1749
2023
|
filterNode,
|
|
1750
2024
|
hideCompressCallsNode,
|
|
1751
2025
|
recommendNode,
|
|
@@ -1791,6 +2065,31 @@ var pruneNode = {
|
|
|
1791
2065
|
return { ...io, messages: prune(io.messages, io.state) };
|
|
1792
2066
|
}
|
|
1793
2067
|
};
|
|
2068
|
+
var absorbHideNode = {
|
|
2069
|
+
name: "absorb-hide",
|
|
2070
|
+
enabled: (io) => (io.state.absorbed?.length ?? 0) > 0,
|
|
2071
|
+
run(io) {
|
|
2072
|
+
return { ...io, messages: hideAbsorbedMessages(io.messages, io.state) };
|
|
2073
|
+
}
|
|
2074
|
+
};
|
|
2075
|
+
var absorbPromptNode = {
|
|
2076
|
+
name: "absorb-prompt",
|
|
2077
|
+
enabled: (_io, ctx) => ctx.config.absorb?.enabled === true,
|
|
2078
|
+
run(io, ctx) {
|
|
2079
|
+
const applied = appendAbsorbPrompts(
|
|
2080
|
+
io.messages,
|
|
2081
|
+
io.state,
|
|
2082
|
+
ctx.config,
|
|
2083
|
+
ctx.tokenCount,
|
|
2084
|
+
ctx.countTokens
|
|
2085
|
+
);
|
|
2086
|
+
return {
|
|
2087
|
+
...io,
|
|
2088
|
+
messages: applied.messages,
|
|
2089
|
+
effects: { ...io.effects, absorbPromptedCount: applied.promptedCount }
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
1794
2093
|
var filterNode = {
|
|
1795
2094
|
name: "filter",
|
|
1796
2095
|
enabled: (_io, ctx) => !!ctx.config.messageFilters?.enabled && listMessageFilters().length > 0,
|
|
@@ -2328,6 +2627,7 @@ function cloneState(state) {
|
|
|
2328
2627
|
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
2329
2628
|
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
2330
2629
|
stats: { ...state.stats },
|
|
2630
|
+
absorbed: (state.absorbed ?? []).map((record) => ({ ...record })),
|
|
2331
2631
|
nextBlockId: state.nextBlockId,
|
|
2332
2632
|
nextRunId: state.nextRunId
|
|
2333
2633
|
};
|
|
@@ -2544,14 +2844,14 @@ function renderUncompressedRanges(visible) {
|
|
|
2544
2844
|
};
|
|
2545
2845
|
const merged = [];
|
|
2546
2846
|
for (const m of visible) {
|
|
2547
|
-
const
|
|
2847
|
+
const num2 = refNum2(m.ref);
|
|
2548
2848
|
const last = merged[merged.length - 1];
|
|
2549
|
-
if (last &&
|
|
2849
|
+
if (last && num2 === last.startNum + last.count) {
|
|
2550
2850
|
last.endRef = m.ref;
|
|
2551
2851
|
last.count += 1;
|
|
2552
2852
|
last.tokens += m.tokens;
|
|
2553
2853
|
} else {
|
|
2554
|
-
merged.push({ startRef: m.ref, endRef: m.ref, startNum:
|
|
2854
|
+
merged.push({ startRef: m.ref, endRef: m.ref, startNum: num2, count: 1, tokens: m.tokens, tool: m.tool });
|
|
2555
2855
|
}
|
|
2556
2856
|
}
|
|
2557
2857
|
for (const r of merged.slice(0, 30)) {
|
|
@@ -2618,6 +2918,296 @@ function renderCompressedDrilldown(blocks, state, sort, limit, countTokens) {
|
|
|
2618
2918
|
}
|
|
2619
2919
|
return lines.join("\n");
|
|
2620
2920
|
}
|
|
2921
|
+
function parseCompressArgs(input, opts) {
|
|
2922
|
+
const callId = opts?.callId;
|
|
2923
|
+
const diag = { ok: false, kind: "ok", invalidItems: 0 };
|
|
2924
|
+
if (input === null || input === void 0) {
|
|
2925
|
+
diag.kind = "empty-input";
|
|
2926
|
+
return finish([], diag);
|
|
2927
|
+
}
|
|
2928
|
+
if (typeof input === "string") {
|
|
2929
|
+
return parseStringInput(input, callId, diag);
|
|
2930
|
+
}
|
|
2931
|
+
if (typeof input !== "object" || Array.isArray(input)) {
|
|
2932
|
+
diag.kind = "not-object";
|
|
2933
|
+
return finish([], diag);
|
|
2934
|
+
}
|
|
2935
|
+
return parseObjectValue(input, callId, diag);
|
|
2936
|
+
}
|
|
2937
|
+
function parseStringInput(raw, callId, diag) {
|
|
2938
|
+
diag.rawPrefix = raw.slice(0, 800);
|
|
2939
|
+
diag.length = raw.length;
|
|
2940
|
+
const cleaned = stripFence(raw.trim());
|
|
2941
|
+
if (cleaned === "") {
|
|
2942
|
+
diag.kind = "empty-input";
|
|
2943
|
+
return finish([], diag);
|
|
2944
|
+
}
|
|
2945
|
+
let value = tryParseLenient(cleaned);
|
|
2946
|
+
if (typeof value === "string") {
|
|
2947
|
+
const inner = tryParseLenient(stripFence(value));
|
|
2948
|
+
if (inner !== void 0) value = inner;
|
|
2949
|
+
}
|
|
2950
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
2951
|
+
return parseObjectValue(value, callId, diag);
|
|
2952
|
+
}
|
|
2953
|
+
if (value !== void 0) {
|
|
2954
|
+
diag.kind = "not-object";
|
|
2955
|
+
return finish([], diag);
|
|
2956
|
+
}
|
|
2957
|
+
const entries = salvageContentEntries(cleaned);
|
|
2958
|
+
return finishSalvage(entries, callId, diag, looksTruncated(cleaned));
|
|
2959
|
+
}
|
|
2960
|
+
function parseObjectValue(value, callId, diag) {
|
|
2961
|
+
diag.keys = Object.keys(value);
|
|
2962
|
+
const content = value["content"];
|
|
2963
|
+
if (content === void 0) {
|
|
2964
|
+
const single = validateEntry(value, callId);
|
|
2965
|
+
if (single) {
|
|
2966
|
+
diag.kind = "ok";
|
|
2967
|
+
return finish([single], diag);
|
|
2968
|
+
}
|
|
2969
|
+
diag.kind = "missing-content";
|
|
2970
|
+
return finish([], diag);
|
|
2971
|
+
}
|
|
2972
|
+
let entries;
|
|
2973
|
+
let salvaged = false;
|
|
2974
|
+
if (Array.isArray(content)) {
|
|
2975
|
+
entries = content;
|
|
2976
|
+
} else if (typeof content === "string") {
|
|
2977
|
+
const parsed = parseContentArray(content);
|
|
2978
|
+
if (parsed === null) {
|
|
2979
|
+
diag.kind = "content-not-array";
|
|
2980
|
+
return finish([], diag);
|
|
2981
|
+
}
|
|
2982
|
+
entries = parsed.entries;
|
|
2983
|
+
salvaged = parsed.salvaged;
|
|
2984
|
+
} else {
|
|
2985
|
+
diag.kind = "content-not-array";
|
|
2986
|
+
return finish([], diag);
|
|
2987
|
+
}
|
|
2988
|
+
const { ranges, invalid } = validateEntries(entries, callId);
|
|
2989
|
+
const topTopic = stringOr(value["topic"]);
|
|
2990
|
+
const topMaxChars = value["summaryMaxChars"];
|
|
2991
|
+
const hasTopMaxChars = typeof topMaxChars === "number" && Number.isFinite(topMaxChars);
|
|
2992
|
+
if (topTopic !== void 0 || hasTopMaxChars) {
|
|
2993
|
+
for (const r of ranges) {
|
|
2994
|
+
if (r.topic === void 0 && topTopic !== void 0) r.topic = topTopic;
|
|
2995
|
+
if (r.summaryMaxChars === void 0 && hasTopMaxChars) r.summaryMaxChars = topMaxChars;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
diag.invalidItems = invalid;
|
|
2999
|
+
diag.kind = salvaged ? "truncated" : ranges.length > 0 ? "ok" : "no-valid-ranges";
|
|
3000
|
+
return finish(ranges, diag);
|
|
3001
|
+
}
|
|
3002
|
+
function parseContentArray(s) {
|
|
3003
|
+
const cleaned = stripFence(s.trim());
|
|
3004
|
+
let value = cleaned === "" ? void 0 : tryParseLenient(cleaned);
|
|
3005
|
+
if (typeof value === "string") {
|
|
3006
|
+
value = tryParseLenient(stripFence(value));
|
|
3007
|
+
}
|
|
3008
|
+
if (Array.isArray(value)) {
|
|
3009
|
+
return { entries: value, salvaged: false };
|
|
3010
|
+
}
|
|
3011
|
+
if (value === void 0) {
|
|
3012
|
+
const entries = salvageContentEntries('{"content": ' + cleaned);
|
|
3013
|
+
return { entries, salvaged: entries.length > 0 };
|
|
3014
|
+
}
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
function finish(ranges, diag) {
|
|
3018
|
+
diag.ok = ranges.length > 0;
|
|
3019
|
+
return { ranges, diagnostics: diag };
|
|
3020
|
+
}
|
|
3021
|
+
function finishSalvage(entries, callId, diag, truncatedShape) {
|
|
3022
|
+
const { ranges, invalid } = validateEntries(entries, callId);
|
|
3023
|
+
diag.invalidItems = invalid;
|
|
3024
|
+
diag.kind = entries.length > 0 || truncatedShape ? "truncated" : "malformed-json";
|
|
3025
|
+
return finish(ranges, diag);
|
|
3026
|
+
}
|
|
3027
|
+
function validateEntries(entries, callId) {
|
|
3028
|
+
const ranges = [];
|
|
3029
|
+
let invalid = 0;
|
|
3030
|
+
for (const entry of entries) {
|
|
3031
|
+
const range = validateEntry(entry, callId);
|
|
3032
|
+
if (range !== null) ranges.push(range);
|
|
3033
|
+
else invalid++;
|
|
3034
|
+
}
|
|
3035
|
+
return { ranges, invalid };
|
|
3036
|
+
}
|
|
3037
|
+
function validateEntry(entry, callId) {
|
|
3038
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return null;
|
|
3039
|
+
const e = entry;
|
|
3040
|
+
const start = stringOr(e["startRef"]) ?? stringOr(e["startId"]) ?? stringOr(e["messageId"]);
|
|
3041
|
+
const end = stringOr(e["endRef"]) ?? stringOr(e["endId"]) ?? stringOr(e["messageId"]);
|
|
3042
|
+
if (start === void 0 || end === void 0) return null;
|
|
3043
|
+
const summary = stringOr(e["summary"]);
|
|
3044
|
+
if (summary === void 0) return null;
|
|
3045
|
+
const range = { startRef: start, endRef: end, summary };
|
|
3046
|
+
const topic = stringOr(e["topic"]);
|
|
3047
|
+
if (topic !== void 0) range.topic = topic;
|
|
3048
|
+
const maxChars = e["summaryMaxChars"];
|
|
3049
|
+
if (typeof maxChars === "number" && Number.isFinite(maxChars)) range.summaryMaxChars = maxChars;
|
|
3050
|
+
if (callId !== void 0) range.compressCallId = callId;
|
|
3051
|
+
return range;
|
|
3052
|
+
}
|
|
3053
|
+
function stringOr(value) {
|
|
3054
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3055
|
+
}
|
|
3056
|
+
function tryParseLenient(s) {
|
|
3057
|
+
if (s === "") return void 0;
|
|
3058
|
+
try {
|
|
3059
|
+
return JSON.parse(s);
|
|
3060
|
+
} catch {
|
|
3061
|
+
}
|
|
3062
|
+
const noTrailingCommas = stripTrailingCommas(s);
|
|
3063
|
+
if (noTrailingCommas !== s) {
|
|
3064
|
+
try {
|
|
3065
|
+
return JSON.parse(noTrailingCommas);
|
|
3066
|
+
} catch {
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
const fixed = escapeRawNewlinesInStrings(noTrailingCommas);
|
|
3070
|
+
if (fixed !== noTrailingCommas) {
|
|
3071
|
+
try {
|
|
3072
|
+
return JSON.parse(fixed);
|
|
3073
|
+
} catch {
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return void 0;
|
|
3077
|
+
}
|
|
3078
|
+
function stripFence(s) {
|
|
3079
|
+
if (!s.startsWith("```")) return s;
|
|
3080
|
+
const firstNewline = s.indexOf("\n");
|
|
3081
|
+
if (firstNewline === -1) return s;
|
|
3082
|
+
const bodyStart = firstNewline + 1;
|
|
3083
|
+
const end = s.lastIndexOf("```");
|
|
3084
|
+
return end > bodyStart ? s.slice(bodyStart, end).trim() : s.slice(bodyStart).trim();
|
|
3085
|
+
}
|
|
3086
|
+
function stripTrailingCommas(s) {
|
|
3087
|
+
let out = "";
|
|
3088
|
+
let inString = false;
|
|
3089
|
+
let escaped = false;
|
|
3090
|
+
for (let i = 0; i < s.length; i++) {
|
|
3091
|
+
const ch = s.charAt(i);
|
|
3092
|
+
if (inString) {
|
|
3093
|
+
out += ch;
|
|
3094
|
+
if (escaped) escaped = false;
|
|
3095
|
+
else if (ch === "\\") escaped = true;
|
|
3096
|
+
else if (ch === '"') inString = false;
|
|
3097
|
+
continue;
|
|
3098
|
+
}
|
|
3099
|
+
if (ch === '"') {
|
|
3100
|
+
inString = true;
|
|
3101
|
+
out += ch;
|
|
3102
|
+
continue;
|
|
3103
|
+
}
|
|
3104
|
+
if (ch === ",") {
|
|
3105
|
+
let j = i + 1;
|
|
3106
|
+
while (j < s.length && (s.charAt(j) === " " || s.charAt(j) === " " || s.charAt(j) === "\n" || s.charAt(j) === "\r")) j++;
|
|
3107
|
+
if (j < s.length && (s.charAt(j) === "}" || s.charAt(j) === "]")) continue;
|
|
3108
|
+
}
|
|
3109
|
+
out += ch;
|
|
3110
|
+
}
|
|
3111
|
+
return out;
|
|
3112
|
+
}
|
|
3113
|
+
function escapeRawNewlinesInStrings(s) {
|
|
3114
|
+
let out = "";
|
|
3115
|
+
let inString = false;
|
|
3116
|
+
let escaped = false;
|
|
3117
|
+
for (let i = 0; i < s.length; i++) {
|
|
3118
|
+
const ch = s.charAt(i);
|
|
3119
|
+
if (!inString) {
|
|
3120
|
+
if (ch === '"') inString = true;
|
|
3121
|
+
out += ch;
|
|
3122
|
+
continue;
|
|
3123
|
+
}
|
|
3124
|
+
if (escaped) {
|
|
3125
|
+
out += ch;
|
|
3126
|
+
escaped = false;
|
|
3127
|
+
continue;
|
|
3128
|
+
}
|
|
3129
|
+
if (ch === "\\") {
|
|
3130
|
+
out += ch;
|
|
3131
|
+
escaped = true;
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
3134
|
+
if (ch === "\n") {
|
|
3135
|
+
out += "\\n";
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
if (ch === "\r") {
|
|
3139
|
+
out += "\\r";
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
3142
|
+
if (ch === " ") {
|
|
3143
|
+
out += "\\t";
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
if (ch === '"') inString = false;
|
|
3147
|
+
out += ch;
|
|
3148
|
+
}
|
|
3149
|
+
return out;
|
|
3150
|
+
}
|
|
3151
|
+
function looksTruncated(s) {
|
|
3152
|
+
let depth = 0;
|
|
3153
|
+
let inString = false;
|
|
3154
|
+
let escaped = false;
|
|
3155
|
+
for (let i = 0; i < s.length; i++) {
|
|
3156
|
+
const ch = s.charAt(i);
|
|
3157
|
+
if (inString) {
|
|
3158
|
+
if (escaped) escaped = false;
|
|
3159
|
+
else if (ch === "\\") escaped = true;
|
|
3160
|
+
else if (ch === '"') inString = false;
|
|
3161
|
+
continue;
|
|
3162
|
+
}
|
|
3163
|
+
if (ch === '"') {
|
|
3164
|
+
inString = true;
|
|
3165
|
+
continue;
|
|
3166
|
+
}
|
|
3167
|
+
if (ch === "{" || ch === "[") depth++;
|
|
3168
|
+
else if (ch === "}" || ch === "]") depth--;
|
|
3169
|
+
}
|
|
3170
|
+
return depth > 0 || inString;
|
|
3171
|
+
}
|
|
3172
|
+
function salvageContentEntries(raw) {
|
|
3173
|
+
const match = /"content"\s*:\s*\[/.exec(raw);
|
|
3174
|
+
if (match === null) return [];
|
|
3175
|
+
const arrayStart = match.index + match[0].length - 1;
|
|
3176
|
+
const entries = [];
|
|
3177
|
+
let depth = 0;
|
|
3178
|
+
let inString = false;
|
|
3179
|
+
let escaped = false;
|
|
3180
|
+
let entryStart = -1;
|
|
3181
|
+
for (let i = arrayStart + 1; i < raw.length; i++) {
|
|
3182
|
+
const ch = raw.charAt(i);
|
|
3183
|
+
if (inString) {
|
|
3184
|
+
if (escaped) escaped = false;
|
|
3185
|
+
else if (ch === "\\") escaped = true;
|
|
3186
|
+
else if (ch === '"') inString = false;
|
|
3187
|
+
continue;
|
|
3188
|
+
}
|
|
3189
|
+
if (ch === '"') {
|
|
3190
|
+
inString = true;
|
|
3191
|
+
continue;
|
|
3192
|
+
}
|
|
3193
|
+
if (ch === "{" || ch === "[") {
|
|
3194
|
+
if (depth === 0 && ch === "{" && entryStart === -1) entryStart = i;
|
|
3195
|
+
depth++;
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
if (ch === "}" || ch === "]") {
|
|
3199
|
+
depth--;
|
|
3200
|
+
if (depth < 0) break;
|
|
3201
|
+
if (depth === 0 && entryStart !== -1) {
|
|
3202
|
+
const entrySlice = raw.slice(entryStart, i + 1);
|
|
3203
|
+
entryStart = -1;
|
|
3204
|
+
const parsed = tryParseLenient(entrySlice);
|
|
3205
|
+
if (parsed !== void 0) entries.push(parsed);
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
return entries;
|
|
3210
|
+
}
|
|
2621
3211
|
function stem(word) {
|
|
2622
3212
|
let w = word;
|
|
2623
3213
|
if (w.length <= 3) return w;
|
|
@@ -4017,6 +4607,7 @@ function pruneOrphanRefs(state, messages) {
|
|
|
4017
4607
|
}
|
|
4018
4608
|
}
|
|
4019
4609
|
var MAX_COMPRESS_ATTEMPTS = 3;
|
|
4610
|
+
var MAX_RETRY_PROMPT_FIRES = 5;
|
|
4020
4611
|
function createRuntime(adapter) {
|
|
4021
4612
|
const density = new DensityEstimator();
|
|
4022
4613
|
let countModelId = "default";
|
|
@@ -4061,33 +4652,52 @@ function createRuntime(adapter) {
|
|
|
4061
4652
|
const compressOutcomeSeen = /* @__PURE__ */ new Set();
|
|
4062
4653
|
let compressFailTurnKey = null;
|
|
4063
4654
|
let compressFailCount = 0;
|
|
4655
|
+
let retryPromptFires = 0;
|
|
4656
|
+
let retryExhaustedNotified = false;
|
|
4064
4657
|
function noteCompressOutcomes(turnKey, outcomes) {
|
|
4065
4658
|
if (compressFailTurnKey !== turnKey) {
|
|
4066
4659
|
compressFailTurnKey = turnKey;
|
|
4067
4660
|
compressFailCount = 0;
|
|
4661
|
+
retryPromptFires = 0;
|
|
4662
|
+
retryExhaustedNotified = false;
|
|
4068
4663
|
}
|
|
4069
4664
|
const prevCount = compressFailCount;
|
|
4665
|
+
let sawNewOutcome = false;
|
|
4070
4666
|
for (const o of outcomes) {
|
|
4071
4667
|
if (compressOutcomeSeen.has(o.toolCallId)) continue;
|
|
4072
4668
|
compressOutcomeSeen.add(o.toolCallId);
|
|
4669
|
+
sawNewOutcome = true;
|
|
4073
4670
|
if (o.isError || o.noop === true) {
|
|
4074
4671
|
compressFailCount += 1;
|
|
4075
4672
|
} else if (o.success) {
|
|
4076
4673
|
compressFailCount = 0;
|
|
4077
4674
|
}
|
|
4078
4675
|
}
|
|
4676
|
+
if (sawNewOutcome) {
|
|
4677
|
+
retryPromptFires = 0;
|
|
4678
|
+
retryExhaustedNotified = false;
|
|
4679
|
+
}
|
|
4079
4680
|
const latest = outcomes.length > 0 ? outcomes[outcomes.length - 1] : void 0;
|
|
4080
|
-
const
|
|
4681
|
+
const latestFailed = latest !== void 0 && (latest.isError || latest.noop === true);
|
|
4682
|
+
const retryFor = latestFailed && compressFailCount >= 1 && compressFailCount < MAX_COMPRESS_ATTEMPTS && retryPromptFires < MAX_RETRY_PROMPT_FIRES ? latest.toolCallId : null;
|
|
4683
|
+
if (retryFor !== null) retryPromptFires += 1;
|
|
4081
4684
|
const cappedNow = compressFailCount >= MAX_COMPRESS_ATTEMPTS && prevCount < MAX_COMPRESS_ATTEMPTS;
|
|
4082
|
-
|
|
4685
|
+
const exhaustedNow = retryFor === null && latestFailed && compressFailCount >= 1 && compressFailCount < MAX_COMPRESS_ATTEMPTS && retryPromptFires >= MAX_RETRY_PROMPT_FIRES && !retryExhaustedNotified;
|
|
4686
|
+
if (exhaustedNow) retryExhaustedNotified = true;
|
|
4687
|
+
return { count: compressFailCount, retryFor, cappedNow, exhaustedNow, fires: retryPromptFires };
|
|
4083
4688
|
}
|
|
4084
4689
|
function compressRetryCappedFor(turnKey) {
|
|
4085
4690
|
return compressFailTurnKey === turnKey && compressFailCount >= MAX_COMPRESS_ATTEMPTS;
|
|
4086
4691
|
}
|
|
4692
|
+
function compressRetryExhaustedFor(turnKey) {
|
|
4693
|
+
return compressFailTurnKey === turnKey && compressFailCount >= 1 && compressFailCount < MAX_COMPRESS_ATTEMPTS && retryPromptFires >= MAX_RETRY_PROMPT_FIRES;
|
|
4694
|
+
}
|
|
4087
4695
|
function clearCompressRetryTracking() {
|
|
4088
4696
|
compressOutcomeSeen.clear();
|
|
4089
4697
|
compressFailTurnKey = null;
|
|
4090
4698
|
compressFailCount = 0;
|
|
4699
|
+
retryPromptFires = 0;
|
|
4700
|
+
retryExhaustedNotified = false;
|
|
4091
4701
|
}
|
|
4092
4702
|
async function acquireLock(sid) {
|
|
4093
4703
|
const prev = locks.get(sid) ?? Promise.resolve();
|
|
@@ -4176,7 +4786,7 @@ function createRuntime(adapter) {
|
|
|
4176
4786
|
nudgeShownTurns.add(k);
|
|
4177
4787
|
}, nudgeShownFor: (k) => nudgeShownTurns.has(k), clearNudgeTracking: () => {
|
|
4178
4788
|
nudgeShownTurns.clear();
|
|
4179
|
-
}, noteCompressOutcomes, compressRetryCappedFor, clearCompressRetryTracking, liveContextLimit, configFor, reloadConfig, stateFor, save, acquireLock, overflowFor, overflowDrop, throttleFor, throttleDrop };
|
|
4789
|
+
}, noteCompressOutcomes, compressRetryCappedFor, compressRetryExhaustedFor, clearCompressRetryTracking, liveContextLimit, configFor, reloadConfig, stateFor, save, acquireLock, overflowFor, overflowDrop, throttleFor, throttleDrop };
|
|
4180
4790
|
}
|
|
4181
4791
|
|
|
4182
4792
|
// node_modules/typebox/build/system/memory/memory.mjs
|
|
@@ -8668,25 +9278,24 @@ function makeCompressTool(runtime) {
|
|
|
8668
9278
|
}
|
|
8669
9279
|
};
|
|
8670
9280
|
}
|
|
8671
|
-
function normalizeRanges(
|
|
8672
|
-
|
|
8673
|
-
if (
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
} catch (e) {
|
|
8677
|
-
return `Invalid content: not valid JSON (${e instanceof Error ? e.message : String(e)}). content must be an ARRAY of {startId, endId, summary} objects \u2014 pass the array directly, not a string.`;
|
|
8678
|
-
}
|
|
9281
|
+
function normalizeRanges(args) {
|
|
9282
|
+
const { ranges, diagnostics } = parseCompressArgs(args);
|
|
9283
|
+
if (ranges.length === 0) {
|
|
9284
|
+
if (Array.isArray(args.content) && args.content.length === 0) return [];
|
|
9285
|
+
return describeDiagnostics(diagnostics, args.content);
|
|
8679
9286
|
}
|
|
8680
|
-
|
|
8681
|
-
|
|
9287
|
+
return ranges.map((r) => ({ startId: r.startRef, endId: r.endRef, summary: r.summary, topic: r.topic }));
|
|
9288
|
+
}
|
|
9289
|
+
function describeDiagnostics(diagnostics, content) {
|
|
9290
|
+
const shape = typeof content === "string" ? "a JSON-encoded string (non-strict-tool providers stringify array arguments)" : content === null ? "null" : `a ${typeof content}`;
|
|
9291
|
+
const base = `Invalid compress content (${diagnostics.kind}): got ${shape}`;
|
|
9292
|
+
if (diagnostics.kind === "truncated") {
|
|
9293
|
+
return `${base}; the input was truncated and no complete ranges could be recovered. Shorten the summary or split into smaller ranges.`;
|
|
8682
9294
|
}
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
if (!o || typeof o !== "object" || typeof o.startId !== "string" || typeof o.endId !== "string" || typeof o.summary !== "string") {
|
|
8686
|
-
return `Invalid content[${i}]: each range must be an object with string fields startId, endId, summary.`;
|
|
8687
|
-
}
|
|
9295
|
+
if (diagnostics.invalidItems > 0) {
|
|
9296
|
+
return `${base}; ${diagnostics.invalidItems} entr${diagnostics.invalidItems === 1 ? "y was" : "ies were"} dropped as invalid. Each range must be an object with string fields startId, endId, summary.`;
|
|
8688
9297
|
}
|
|
8689
|
-
return
|
|
9298
|
+
return `${base}. content must be an ARRAY of {startId, endId, summary} objects.`;
|
|
8690
9299
|
}
|
|
8691
9300
|
function compressPanelBlocks(text) {
|
|
8692
9301
|
if (!text.trimStart().startsWith("\u25A3 ACP |")) return -1;
|
|
@@ -8713,7 +9322,7 @@ function tier3OnlyRewrite(newBlocks, allBlocks) {
|
|
|
8713
9322
|
return spans;
|
|
8714
9323
|
}
|
|
8715
9324
|
async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
8716
|
-
const maybeRanges = normalizeRanges(args
|
|
9325
|
+
const maybeRanges = normalizeRanges(args);
|
|
8717
9326
|
if (typeof maybeRanges === "string") throw new Error(maybeRanges);
|
|
8718
9327
|
const ranges = maybeRanges;
|
|
8719
9328
|
if (ranges.length === 0) return "No ranges provided.";
|
|
@@ -9923,7 +10532,7 @@ function makeDelegateWaitTool(_pi) {
|
|
|
9923
10532
|
return new Promise((resolve3) => {
|
|
9924
10533
|
let settled = false;
|
|
9925
10534
|
let timer2;
|
|
9926
|
-
const
|
|
10535
|
+
const finish2 = (result) => {
|
|
9927
10536
|
if (settled) return;
|
|
9928
10537
|
settled = true;
|
|
9929
10538
|
run.waiter = void 0;
|
|
@@ -9932,19 +10541,19 @@ function makeDelegateWaitTool(_pi) {
|
|
|
9932
10541
|
resolve3(result);
|
|
9933
10542
|
};
|
|
9934
10543
|
const onAbort = () => {
|
|
9935
|
-
|
|
10544
|
+
finish2({ details: void 0, content: [{ type: "text", text: `Aborted; delegate \`${args.runId}\` is still running in the background. A notification will be injected when it finishes.` }] });
|
|
9936
10545
|
};
|
|
9937
10546
|
run.waiter = () => {
|
|
9938
10547
|
run.consumed = true;
|
|
9939
10548
|
if (run.status === "cancelled") {
|
|
9940
|
-
|
|
10549
|
+
finish2(buildWaitResult(run, `Delegate \`${run.runId}\` was cancelled (no result).${remainingLineForWait(run.runId)}`, displayMode));
|
|
9941
10550
|
return;
|
|
9942
10551
|
}
|
|
9943
|
-
|
|
10552
|
+
finish2(buildWaitResult(run, formatRunResult(run), displayMode));
|
|
9944
10553
|
};
|
|
9945
10554
|
signal?.addEventListener("abort", onAbort);
|
|
9946
10555
|
timer2 = setTimeout(
|
|
9947
|
-
() =>
|
|
10556
|
+
() => finish2({ details: void 0, content: [{ type: "text", text: `Failed: delegate \`${args.runId}\` result not ready after ${Math.round(timeoutMs / 1e3)}s. Do NOT keep waiting or retry \u2014 go do other work now. The run continues in the background and a completion notification (with the result file path) will be injected into the chat when it finishes.` }] }),
|
|
9948
10557
|
timeoutMs
|
|
9949
10558
|
);
|
|
9950
10559
|
});
|
|
@@ -10222,20 +10831,20 @@ function waitForChild(child, signal) {
|
|
|
10222
10831
|
});
|
|
10223
10832
|
const timer2 = setTimeout(() => {
|
|
10224
10833
|
child.kill("SIGTERM");
|
|
10225
|
-
|
|
10834
|
+
finish2({ code: null, stdout: "", stderr: stderrText, timedOut: true });
|
|
10226
10835
|
}, SYNC_TIMEOUT_MS);
|
|
10227
10836
|
const onAbort = () => {
|
|
10228
10837
|
clearTimeout(timer2);
|
|
10229
10838
|
child.kill("SIGTERM");
|
|
10230
10839
|
};
|
|
10231
10840
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
10232
|
-
function
|
|
10841
|
+
function finish2(r) {
|
|
10233
10842
|
clearTimeout(timer2);
|
|
10234
10843
|
signal?.removeEventListener("abort", onAbort);
|
|
10235
10844
|
resolve3(r);
|
|
10236
10845
|
}
|
|
10237
10846
|
child.on("close", (code) => {
|
|
10238
|
-
|
|
10847
|
+
finish2({
|
|
10239
10848
|
code,
|
|
10240
10849
|
stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(),
|
|
10241
10850
|
stderr: stderrText,
|
|
@@ -10243,7 +10852,7 @@ function waitForChild(child, signal) {
|
|
|
10243
10852
|
});
|
|
10244
10853
|
});
|
|
10245
10854
|
child.on("error", (err) => {
|
|
10246
|
-
|
|
10855
|
+
finish2({ code: null, stdout: "", stderr: err.message, timedOut: false });
|
|
10247
10856
|
});
|
|
10248
10857
|
});
|
|
10249
10858
|
}
|
|
@@ -10448,6 +11057,37 @@ function formatCompactTokens2(count) {
|
|
|
10448
11057
|
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
10449
11058
|
return `${Math.round(count / 1e6)}M`;
|
|
10450
11059
|
}
|
|
11060
|
+
function num(v) {
|
|
11061
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
11062
|
+
}
|
|
11063
|
+
function cacheHitStats(usages) {
|
|
11064
|
+
let cacheRead = 0;
|
|
11065
|
+
let billedPrompt = 0;
|
|
11066
|
+
let requests = 0;
|
|
11067
|
+
let last;
|
|
11068
|
+
for (const u of usages) {
|
|
11069
|
+
const read = num(u?.cacheRead);
|
|
11070
|
+
const write = num(u?.cacheWrite);
|
|
11071
|
+
const input = num(u?.input);
|
|
11072
|
+
if (read + write <= 0) continue;
|
|
11073
|
+
const total = read + write + input;
|
|
11074
|
+
if (total <= 0) continue;
|
|
11075
|
+
cacheRead += read;
|
|
11076
|
+
billedPrompt += total;
|
|
11077
|
+
requests += 1;
|
|
11078
|
+
last = read / total;
|
|
11079
|
+
}
|
|
11080
|
+
return {
|
|
11081
|
+
session: requests > 0 ? cacheRead / billedPrompt : void 0,
|
|
11082
|
+
last,
|
|
11083
|
+
requests,
|
|
11084
|
+
cacheRead,
|
|
11085
|
+
billedPrompt
|
|
11086
|
+
};
|
|
11087
|
+
}
|
|
11088
|
+
function formatHitRate(rate) {
|
|
11089
|
+
return `${(Math.max(0, Math.min(1, rate)) * 100).toFixed(1)}%`;
|
|
11090
|
+
}
|
|
10451
11091
|
function bar(value, total, width = 20) {
|
|
10452
11092
|
if (total === 0) return "";
|
|
10453
11093
|
const filled = Math.max(0, Math.min(width, Math.round(value / total * width)));
|
|
@@ -10500,6 +11140,15 @@ function buildStatusPanel(input) {
|
|
|
10500
11140
|
lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct2).padStart(3)}% ${fmt2(cat.value)}`);
|
|
10501
11141
|
}
|
|
10502
11142
|
}
|
|
11143
|
+
if (input.cacheUsages) {
|
|
11144
|
+
const cache2 = cacheHitStats(input.cacheUsages);
|
|
11145
|
+
if (cache2.requests > 0 && cache2.session !== void 0 && cache2.last !== void 0) {
|
|
11146
|
+
lines.push("");
|
|
11147
|
+
lines.push(
|
|
11148
|
+
`Prompt cache (provider-reported): ${formatHitRate(cache2.last)} last \xB7 ${formatHitRate(cache2.session)} session avg \u2014 ${fmt2(cache2.cacheRead)} of ${fmt2(cache2.billedPrompt)} billed prompt tokens served from cache (${cache2.requests} req)`
|
|
11149
|
+
);
|
|
11150
|
+
}
|
|
11151
|
+
}
|
|
10503
11152
|
lines.push("");
|
|
10504
11153
|
if (nudge) {
|
|
10505
11154
|
if (nudge.shouldInject) {
|
|
@@ -10730,6 +11379,16 @@ function ensureSubagentAcpTools(settingsPath, options) {
|
|
|
10730
11379
|
}
|
|
10731
11380
|
|
|
10732
11381
|
// src/commands.ts
|
|
11382
|
+
function cacheUsageSamples(entries) {
|
|
11383
|
+
const out = [];
|
|
11384
|
+
for (const e of entries) {
|
|
11385
|
+
if (e.type !== "message") continue;
|
|
11386
|
+
const m = e.message;
|
|
11387
|
+
if (m?.role !== "assistant" || !m.usage) continue;
|
|
11388
|
+
out.push({ input: m.usage.input ?? 0, cacheRead: m.usage.cacheRead ?? 0, cacheWrite: m.usage.cacheWrite ?? 0 });
|
|
11389
|
+
}
|
|
11390
|
+
return out;
|
|
11391
|
+
}
|
|
10733
11392
|
function makeCommands(runtime) {
|
|
10734
11393
|
return [
|
|
10735
11394
|
{
|
|
@@ -10816,7 +11475,7 @@ ${text}`);
|
|
|
10816
11475
|
];
|
|
10817
11476
|
}
|
|
10818
11477
|
async function statusReport(runtime, ctx) {
|
|
10819
|
-
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
11478
|
+
const { state, coreMessages, entries } = await runtime.stateFor(ctx);
|
|
10820
11479
|
const config = runtime.configFor(ctx);
|
|
10821
11480
|
const realUsage = ctx.getContextUsage?.();
|
|
10822
11481
|
const systemPromptText = getSystemPromptText(ctx);
|
|
@@ -10826,7 +11485,7 @@ async function statusReport(runtime, ctx) {
|
|
|
10826
11485
|
const modelId = ctx.model?.id ?? "default";
|
|
10827
11486
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
10828
11487
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: calibrateTokens(sentTokens, runtime.density.densityFor(modelId)) });
|
|
10829
|
-
const versionStr = "0.1.
|
|
11488
|
+
const versionStr = "0.1.47" ? `billion-context-pi@${"0.1.47"}` : void 0;
|
|
10830
11489
|
let text = buildStatusPanel({
|
|
10831
11490
|
version: versionStr,
|
|
10832
11491
|
tokenCount: sessionTokens,
|
|
@@ -10834,7 +11493,8 @@ async function statusReport(runtime, ctx) {
|
|
|
10834
11493
|
state: turn.state,
|
|
10835
11494
|
nudge: turn.nudge,
|
|
10836
11495
|
modelContextLimit: config.modelContextLimit,
|
|
10837
|
-
unprunedTokens: coreMessages.reduce((sum, m) => sum + defaultCountTokens(m.text ?? ""), 0)
|
|
11496
|
+
unprunedTokens: coreMessages.reduce((sum, m) => sum + defaultCountTokens(m.text ?? ""), 0),
|
|
11497
|
+
cacheUsages: cacheUsageSamples(entries ?? [])
|
|
10838
11498
|
});
|
|
10839
11499
|
const delegateUsage = getDelegateUsage();
|
|
10840
11500
|
if (delegateUsage && delegateUsage.totalTokens > 0) {
|
|
@@ -11025,14 +11685,12 @@ function wireToolGuardrails(pi, runtime) {
|
|
|
11025
11685
|
const fullPath = isBash ? event.details?.fullOutputPath : void 0;
|
|
11026
11686
|
const timeoutSecs = isBash && event.isError ? detectBashTimeout(event.content) : void 0;
|
|
11027
11687
|
let modified;
|
|
11028
|
-
const max = runtime.adapter.toolOutputMaxBytes;
|
|
11029
|
-
|
|
11030
|
-
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
logWarn("guardrail", { event: "output-cap", max, hadPath: !!fullPath });
|
|
11035
|
-
}
|
|
11688
|
+
const max = runtime.adapter.toolOutputMaxBytes ?? DEFAULT_TOOL_OUTPUT_MAX_BYTES;
|
|
11689
|
+
const next = capToolOutput(event.content, max, fullPath);
|
|
11690
|
+
if (next) {
|
|
11691
|
+
modified = next;
|
|
11692
|
+
debug.event("guardrail-output-cap", { max, hadPath: !!fullPath });
|
|
11693
|
+
logWarn("guardrail", { event: "output-cap", max, hadPath: !!fullPath });
|
|
11036
11694
|
}
|
|
11037
11695
|
if (timeoutSecs !== void 0) {
|
|
11038
11696
|
modified = appendTimeoutNotice(modified ?? event.content, timeoutSecs);
|
|
@@ -11188,7 +11846,7 @@ async function autoInstallLatest(latest, extDirOverride) {
|
|
|
11188
11846
|
"--no-fund",
|
|
11189
11847
|
"--no-save"
|
|
11190
11848
|
];
|
|
11191
|
-
const prevVersion = (await readPackageJson(join8(extDir, "package.json")))?.version ?? "0.1.
|
|
11849
|
+
const prevVersion = (await readPackageJson(join8(extDir, "package.json")))?.version ?? "0.1.47";
|
|
11192
11850
|
const { code, stderr } = await runNpmImpl(installArgs(latest), { cwd: npmDir, timeout: 6e4 });
|
|
11193
11851
|
if (code !== 0) {
|
|
11194
11852
|
logWarn("update", {
|
|
@@ -11201,7 +11859,7 @@ async function autoInstallLatest(latest, extDirOverride) {
|
|
|
11201
11859
|
}
|
|
11202
11860
|
const verify = await verifyInstall(npmDir, latest);
|
|
11203
11861
|
if (!verify.ok) {
|
|
11204
|
-
const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.
|
|
11862
|
+
const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.47";
|
|
11205
11863
|
logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
|
|
11206
11864
|
const rb = await runNpmImpl(installArgs(rollbackTo), { cwd: npmDir, timeout: 6e4 });
|
|
11207
11865
|
logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
|
|
@@ -11262,7 +11920,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
11262
11920
|
const runtimeVersion = await getRuntimeVersion();
|
|
11263
11921
|
const latest = await fetchLatestVersion();
|
|
11264
11922
|
if (!latest) return;
|
|
11265
|
-
const current = runtimeVersion ?? "0.1.
|
|
11923
|
+
const current = runtimeVersion ?? "0.1.47";
|
|
11266
11924
|
const hasUpdate = isNewer(latest, current);
|
|
11267
11925
|
debug.event("update-check", {
|
|
11268
11926
|
current,
|
|
@@ -11303,6 +11961,10 @@ async function getRuntimeVersion() {
|
|
|
11303
11961
|
// src/index.ts
|
|
11304
11962
|
function createAcpExtension(adapter = {}) {
|
|
11305
11963
|
return (pi) => {
|
|
11964
|
+
if (process.env.BILLION_CONTEXT_PROXY) {
|
|
11965
|
+
console.log("[bcp] disabled: BILLION_CONTEXT_PROXY detected \u2014 proxy handles compression");
|
|
11966
|
+
return;
|
|
11967
|
+
}
|
|
11306
11968
|
const runtime = createRuntime(adapter);
|
|
11307
11969
|
wireCompactionDisable(pi);
|
|
11308
11970
|
wireSessionLifecycle(pi, runtime);
|
|
@@ -11337,7 +11999,7 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
11337
11999
|
const sid = ctx.sessionManager.getSessionId();
|
|
11338
12000
|
runtime.clearSessionTracking(sid);
|
|
11339
12001
|
const modelInfo = ctx.model;
|
|
11340
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
12002
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.47" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
|
|
11341
12003
|
try {
|
|
11342
12004
|
await runtime.reloadConfig(ctx.cwd);
|
|
11343
12005
|
setDelegateDisplayUsage(resolveDelegate(runtime.adapter).displayUsage);
|
|
@@ -11466,7 +12128,8 @@ function wireContextTransform(pi, runtime) {
|
|
|
11466
12128
|
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
11467
12129
|
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
11468
12130
|
const retryCapped = runtime.compressRetryCappedFor(turnKey);
|
|
11469
|
-
const
|
|
12131
|
+
const retryQuiet = retryCapped || runtime.compressRetryExhaustedFor(turnKey);
|
|
12132
|
+
const alreadyShown = retryQuiet || !emergency && runtime.nudgeShownFor(turnKey);
|
|
11470
12133
|
if (!alreadyShown) {
|
|
11471
12134
|
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts));
|
|
11472
12135
|
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
@@ -11491,7 +12154,7 @@ ${rendered.text}${example}`);
|
|
|
11491
12154
|
const failed = outcome.retryFor !== null ? compressOutcomes.find((o) => o.toolCallId === outcome.retryFor) : void 0;
|
|
11492
12155
|
if (failed) {
|
|
11493
12156
|
rebuilt.push(compressRetryMessage(failed.text, outcome.count, MAX_COMPRESS_ATTEMPTS));
|
|
11494
|
-
logWarn("nudge", { sid, event: "compress-retry-inject", attempt: outcome.count, max: MAX_COMPRESS_ATTEMPTS, toolCallId: failed.toolCallId });
|
|
12157
|
+
logWarn("nudge", { sid, event: "compress-retry-inject", attempt: outcome.count, max: MAX_COMPRESS_ATTEMPTS, fires: outcome.fires, fireMax: MAX_RETRY_PROMPT_FIRES, toolCallId: failed.toolCallId });
|
|
11495
12158
|
debug.event("compress-retry-injected", { sid, turnKey, attempt: outcome.count, toolCallId: failed.toolCallId, text: failed.text.slice(0, 200) });
|
|
11496
12159
|
} else if (outcome.cappedNow) {
|
|
11497
12160
|
logWarn("nudge", { sid, event: "compress-retry-capped", failures: outcome.count });
|
|
@@ -11499,6 +12162,12 @@ ${rendered.text}${example}`);
|
|
|
11499
12162
|
if (ctx.hasUI) {
|
|
11500
12163
|
ctx.ui.notify(`[ACP] compress failed ${outcome.count}\xD7 this turn \u2014 retry prompts disabled until the next user message.`);
|
|
11501
12164
|
}
|
|
12165
|
+
} else if (outcome.exhaustedNow) {
|
|
12166
|
+
logWarn("nudge", { sid, event: "compress-retry-exhausted", failures: outcome.count, fires: outcome.fires, fireMax: MAX_RETRY_PROMPT_FIRES });
|
|
12167
|
+
debug.event("compress-retry-exhausted", { sid, turnKey, failures: outcome.count });
|
|
12168
|
+
if (ctx.hasUI) {
|
|
12169
|
+
ctx.ui.notify(`[ACP] compress retry prompt ignored ${MAX_RETRY_PROMPT_FIRES}\xD7 this turn \u2014 retry prompts paused until a new compress attempt or the next user message.`);
|
|
12170
|
+
}
|
|
11502
12171
|
}
|
|
11503
12172
|
}
|
|
11504
12173
|
debug.event("context-out", { outMsgs: rebuilt.length, injected: turn.nudge?.shouldInject ?? false, emergency: turn.nudge?.breakdown?.emergencyOverride === 1 });
|