billion-context-pi 0.1.35 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/README.zh-CN.md +18 -2
- package/dist/config.d.ts +55 -11
- package/dist/delegate-tool.d.ts +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +525 -142
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +3 -1
- package/dist/sequence-match.d.ts +6 -0
- package/dist/system-prompt.d.ts +2 -1
- package/dist/user-config.d.ts +7 -3
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -132,9 +132,11 @@ function prune(messages, state, options = {}) {
|
|
|
132
132
|
const indexById = /* @__PURE__ */ new Map();
|
|
133
133
|
messages.forEach((message, index) => indexById.set(message.id, index));
|
|
134
134
|
const anchors = inject ? collectSummaryAnchors(state, indexById) : [];
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
return stripOrphanedReasoning(
|
|
136
|
+
stripOrphanedToolResults(
|
|
137
|
+
stripOrphanedToolCalls(
|
|
138
|
+
rebuildMessages(messages, covered, firstUserIndex, anchors)
|
|
139
|
+
)
|
|
138
140
|
)
|
|
139
141
|
);
|
|
140
142
|
}
|
|
@@ -211,6 +213,24 @@ function stripOrphanedToolCalls(messages) {
|
|
|
211
213
|
(m) => m.contentType !== "tool-call" || !m.toolCallId || m.toolName === "compress" || knownResultIds.has(m.toolCallId)
|
|
212
214
|
);
|
|
213
215
|
}
|
|
216
|
+
function stripOrphanedReasoning(messages) {
|
|
217
|
+
const drop = /* @__PURE__ */ new Set();
|
|
218
|
+
for (let i = 0; i < messages.length; i++) {
|
|
219
|
+
if (drop.has(i)) continue;
|
|
220
|
+
if (messages[i].contentType !== "reasoning") continue;
|
|
221
|
+
let j = i;
|
|
222
|
+
while (j + 1 < messages.length && messages[j + 1].contentType === "reasoning") {
|
|
223
|
+
j++;
|
|
224
|
+
}
|
|
225
|
+
const companion = messages[j + 1];
|
|
226
|
+
const hasCompanion = companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call");
|
|
227
|
+
if (!hasCompanion) {
|
|
228
|
+
for (let k = i; k <= j; k++) drop.add(k);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (drop.size === 0) return messages;
|
|
232
|
+
return messages.filter((_, i) => !drop.has(i));
|
|
233
|
+
}
|
|
214
234
|
function syncBlocks(messages, state) {
|
|
215
235
|
const presentIds = new Set(messages.map((message) => message.id));
|
|
216
236
|
const deactivated = [];
|
|
@@ -263,7 +283,7 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
263
283
|
const base = {
|
|
264
284
|
tiers: { enabled: true, tier2Trigger: 5, tier3Trigger: 10 },
|
|
265
285
|
nudge: {
|
|
266
|
-
maxContextLimitPct: 0.
|
|
286
|
+
maxContextLimitPct: 0.75,
|
|
267
287
|
minContextLimitPct: 0.45,
|
|
268
288
|
frequency: 5,
|
|
269
289
|
iterationThreshold: 15,
|
|
@@ -273,10 +293,10 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
273
293
|
growthCap: 5e4,
|
|
274
294
|
minGrowthFloor: 2e4,
|
|
275
295
|
minGrowthRatio: 0.45,
|
|
276
|
-
emergencyThresholdPct: 0.
|
|
296
|
+
emergencyThresholdPct: 0.95
|
|
277
297
|
},
|
|
278
298
|
promotionThreshold: 5,
|
|
279
|
-
truncate: { threshold:
|
|
299
|
+
truncate: { threshold: 0.95 },
|
|
280
300
|
compress: {
|
|
281
301
|
minCompressRange: 5e3,
|
|
282
302
|
maxSummaryLength: 2e4,
|
|
@@ -306,6 +326,11 @@ function validateConfig(config) {
|
|
|
306
326
|
"nudge.minContextLimitPct must not exceed nudge.maxContextLimitPct"
|
|
307
327
|
);
|
|
308
328
|
}
|
|
329
|
+
if (config.nudge.maxContextLimitPct > config.nudge.emergencyThresholdPct) {
|
|
330
|
+
errors.push(
|
|
331
|
+
"nudge.maxContextLimitPct must not exceed nudge.emergencyThresholdPct"
|
|
332
|
+
);
|
|
333
|
+
}
|
|
309
334
|
if (config.promotionThreshold < 1) {
|
|
310
335
|
errors.push("promotionThreshold must be >= 1");
|
|
311
336
|
}
|
|
@@ -338,6 +363,18 @@ function parseBoundary(ref) {
|
|
|
338
363
|
}
|
|
339
364
|
return null;
|
|
340
365
|
}
|
|
366
|
+
var BoundaryNotFoundError = class extends Error {
|
|
367
|
+
code = "BOUNDARY_NOT_FOUND";
|
|
368
|
+
kind;
|
|
369
|
+
endpoint;
|
|
370
|
+
constructor(kind, endpoint, message) {
|
|
371
|
+
super(message);
|
|
372
|
+
this.name = "BoundaryNotFoundError";
|
|
373
|
+
this.code = "BOUNDARY_NOT_FOUND";
|
|
374
|
+
this.kind = kind;
|
|
375
|
+
this.endpoint = endpoint;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
341
378
|
function resolveBoundaries(input) {
|
|
342
379
|
const start = parseBoundary(input.startRef);
|
|
343
380
|
const end = parseBoundary(input.endRef);
|
|
@@ -350,13 +387,8 @@ function resolveBoundaries(input) {
|
|
|
350
387
|
input.messages.forEach(
|
|
351
388
|
(message, index) => indexByRawId.set(message.id, index)
|
|
352
389
|
);
|
|
353
|
-
let startIndex = resolveAnchorIndex(start, input.state, indexByRawId);
|
|
354
|
-
let endIndex = resolveAnchorIndex(end, input.state, indexByRawId);
|
|
355
|
-
if (startIndex === null || endIndex === null) {
|
|
356
|
-
throw new Error(
|
|
357
|
-
`Boundary not found in visible context (likely consumed by an existing block). startId="${input.startRef}", endId="${input.endRef}".`
|
|
358
|
-
);
|
|
359
|
-
}
|
|
390
|
+
let startIndex = resolveAnchorIndex(start, input.state, indexByRawId, "start");
|
|
391
|
+
let endIndex = resolveAnchorIndex(end, input.state, indexByRawId, "end");
|
|
360
392
|
if (startIndex > endIndex) {
|
|
361
393
|
[startIndex, endIndex] = [endIndex, startIndex];
|
|
362
394
|
}
|
|
@@ -387,16 +419,51 @@ function resolveBoundaries(input) {
|
|
|
387
419
|
protectedGaps
|
|
388
420
|
};
|
|
389
421
|
}
|
|
390
|
-
function resolveAnchorIndex(boundary, state, indexByRawId) {
|
|
422
|
+
function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
423
|
+
const label = endpoint === "start" ? "startId" : "endId";
|
|
391
424
|
if (boundary.kind === "message") {
|
|
392
425
|
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
393
|
-
if (!rawId)
|
|
426
|
+
if (!rawId) {
|
|
427
|
+
throw new BoundaryNotFoundError(
|
|
428
|
+
"unknown",
|
|
429
|
+
endpoint,
|
|
430
|
+
`${label}="${boundary.raw}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
394
433
|
const index = indexByRawId.get(rawId);
|
|
395
|
-
|
|
434
|
+
if (index === void 0) {
|
|
435
|
+
throw new BoundaryNotFoundError(
|
|
436
|
+
"consumed",
|
|
437
|
+
endpoint,
|
|
438
|
+
`${label}="${boundary.raw}" not found in visible context (likely consumed by an existing block).`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return index;
|
|
396
442
|
}
|
|
397
443
|
const block = blockById(state, `b${boundary.numericId}`);
|
|
398
|
-
if (!block
|
|
399
|
-
|
|
444
|
+
if (!block) {
|
|
445
|
+
throw new BoundaryNotFoundError(
|
|
446
|
+
"unknown",
|
|
447
|
+
endpoint,
|
|
448
|
+
`${label}="b${boundary.numericId}" does not exist in this session (typo or wrong session) \u2014 run acp_status for current refs.`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (!block.active) {
|
|
452
|
+
throw new BoundaryNotFoundError(
|
|
453
|
+
"consumed",
|
|
454
|
+
endpoint,
|
|
455
|
+
`${label}="b${boundary.numericId}" not found in visible context (block distilled/consumed by a higher-tier block).`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
const anchor = earliestIndexOfIds(block.effectiveMessageIds, indexByRawId);
|
|
459
|
+
if (anchor === null) {
|
|
460
|
+
throw new BoundaryNotFoundError(
|
|
461
|
+
"consumed",
|
|
462
|
+
endpoint,
|
|
463
|
+
`${label}="b${boundary.numericId}" not found in visible context (block messages consumed by a higher-tier block).`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return anchor;
|
|
400
467
|
}
|
|
401
468
|
function formatPaddedRef(index) {
|
|
402
469
|
return `m${String(index).padStart(5, "0")}`;
|
|
@@ -773,6 +840,38 @@ function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan =
|
|
|
773
840
|
}
|
|
774
841
|
return { startIndex: newStartIndex, endIndex: newEndIndex };
|
|
775
842
|
}
|
|
843
|
+
function adjustBoundariesForReasoningPairs(startIndex, endIndex, messages) {
|
|
844
|
+
if (startIndex > endIndex) {
|
|
845
|
+
return { startIndex, endIndex };
|
|
846
|
+
}
|
|
847
|
+
let newStartIndex = startIndex;
|
|
848
|
+
let newEndIndex = endIndex;
|
|
849
|
+
for (let i = startIndex; i <= endIndex && i < messages.length; i++) {
|
|
850
|
+
const msg = messages[i];
|
|
851
|
+
if (!msg) continue;
|
|
852
|
+
if (msg.contentType === "reasoning") {
|
|
853
|
+
let j = i;
|
|
854
|
+
while (j + 1 < messages.length && messages[j + 1].contentType === "reasoning") {
|
|
855
|
+
j++;
|
|
856
|
+
}
|
|
857
|
+
const companion = messages[j + 1];
|
|
858
|
+
if (companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call") && j + 1 > newEndIndex) {
|
|
859
|
+
newEndIndex = j + 1;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (msg.role === "assistant" && (msg.contentType === "text" || msg.contentType === "tool-call")) {
|
|
863
|
+
let k = i - 1;
|
|
864
|
+
while (k >= 0 && messages[k].contentType === "reasoning") {
|
|
865
|
+
k--;
|
|
866
|
+
}
|
|
867
|
+
const runStart = k + 1;
|
|
868
|
+
if (runStart < i && runStart >= 0 && messages[runStart].contentType === "reasoning" && runStart < newStartIndex) {
|
|
869
|
+
newStartIndex = runStart;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return { startIndex: newStartIndex, endIndex: newEndIndex };
|
|
874
|
+
}
|
|
776
875
|
function refNum(ref) {
|
|
777
876
|
const n = parseInt(ref.slice(1), 10);
|
|
778
877
|
return Number.isNaN(n) ? -1 : n;
|
|
@@ -927,6 +1026,9 @@ function runPipeline(nodes, initial, ctx) {
|
|
|
927
1026
|
}
|
|
928
1027
|
return io;
|
|
929
1028
|
}
|
|
1029
|
+
function rangeError(spec, message) {
|
|
1030
|
+
return `range ${spec.startRef}..${spec.endRef}: ${message}`;
|
|
1031
|
+
}
|
|
930
1032
|
function createCore(ports = {}) {
|
|
931
1033
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
932
1034
|
function applyCompression(input) {
|
|
@@ -938,20 +1040,44 @@ function createCore(ports = {}) {
|
|
|
938
1040
|
const warnings = [];
|
|
939
1041
|
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config, countTokens);
|
|
940
1042
|
const preExistingCoverage = collectCoverage(state);
|
|
941
|
-
const
|
|
1043
|
+
const classifications = /* @__PURE__ */ new Map();
|
|
1044
|
+
const classificationErrors = [];
|
|
1045
|
+
const consumedRanges = [];
|
|
942
1046
|
for (const spec of input.ranges) {
|
|
943
|
-
let resolved;
|
|
944
1047
|
try {
|
|
945
|
-
resolved = resolveBoundaries({
|
|
1048
|
+
const resolved = resolveBoundaries({
|
|
946
1049
|
startRef: spec.startRef,
|
|
947
1050
|
endRef: spec.endRef,
|
|
948
1051
|
messages: input.messages,
|
|
949
1052
|
state
|
|
950
1053
|
});
|
|
951
|
-
|
|
952
|
-
|
|
1054
|
+
classifications.set(spec, { status: "ok", resolved });
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
if (error instanceof BoundaryNotFoundError) {
|
|
1057
|
+
classifications.set(
|
|
1058
|
+
spec,
|
|
1059
|
+
error.kind === "unknown" ? { status: "unknown", error } : { status: "consumed", error }
|
|
1060
|
+
);
|
|
1061
|
+
if (error.kind === "consumed") {
|
|
1062
|
+
consumedRanges.push(spec);
|
|
1063
|
+
} else {
|
|
1064
|
+
classificationErrors.push(rangeError(spec, error.message));
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
classifications.set(spec, {
|
|
1068
|
+
status: "invalid",
|
|
1069
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
1070
|
+
});
|
|
1071
|
+
classificationErrors.push(
|
|
1072
|
+
rangeError(spec, error instanceof Error ? error.message : String(error))
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
953
1075
|
}
|
|
954
|
-
|
|
1076
|
+
}
|
|
1077
|
+
const rangeIndexSets = [];
|
|
1078
|
+
for (const [spec, resolution] of classifications) {
|
|
1079
|
+
if (resolution.status !== "ok") continue;
|
|
1080
|
+
const indices = resolution.resolved.messageIds.map(
|
|
955
1081
|
(id) => input.messages.findIndex((m) => m.id === id)
|
|
956
1082
|
).filter((i) => i >= 0);
|
|
957
1083
|
rangeIndexSets.push({ spec, indices });
|
|
@@ -978,37 +1104,27 @@ function createCore(ports = {}) {
|
|
|
978
1104
|
if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
|
|
979
1105
|
let totalRangeChars = 0;
|
|
980
1106
|
let hasBlockBoundaryRange = false;
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
resolved = resolveBoundaries({
|
|
986
|
-
startRef: spec.startRef,
|
|
987
|
-
endRef: spec.endRef,
|
|
988
|
-
messages: input.messages,
|
|
989
|
-
state
|
|
990
|
-
});
|
|
991
|
-
} catch {
|
|
992
|
-
continue;
|
|
993
|
-
}
|
|
994
|
-
if (resolved.boundaryKind === "block") {
|
|
1107
|
+
let countedRanges = 0;
|
|
1108
|
+
for (const [spec, resolution] of classifications) {
|
|
1109
|
+
if (resolution.status !== "ok" || skipSpecs.has(spec)) continue;
|
|
1110
|
+
if (resolution.resolved.boundaryKind === "block") {
|
|
995
1111
|
hasBlockBoundaryRange = true;
|
|
996
1112
|
continue;
|
|
997
1113
|
}
|
|
998
|
-
|
|
1114
|
+
countedRanges++;
|
|
1115
|
+
for (const id of resolution.resolved.messageIds) {
|
|
999
1116
|
const msg = input.messages.find((m) => m.id === id);
|
|
1000
1117
|
totalRangeChars += msg?.text?.length ?? 0;
|
|
1001
1118
|
}
|
|
1002
1119
|
}
|
|
1003
1120
|
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
1121
|
+
const gateMessage = consumedRanges.length > 0 ? `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}); remaining compressible content ${totalRangeChars} chars < min ${input.config.compress.minCompressRange}. Nothing to do \u2014 run acp_status to see current compressible ranges.` : `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.`;
|
|
1004
1122
|
return {
|
|
1005
1123
|
state: input.state,
|
|
1006
1124
|
result: {
|
|
1007
1125
|
blocksCreated: 0,
|
|
1008
1126
|
tokensCompressed: 0,
|
|
1009
|
-
errors: [
|
|
1010
|
-
`Total compressible content too small (${totalRangeChars} chars across ${input.ranges.length} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`
|
|
1011
|
-
],
|
|
1127
|
+
errors: [gateMessage, ...classificationErrors],
|
|
1012
1128
|
warnings: []
|
|
1013
1129
|
}
|
|
1014
1130
|
};
|
|
@@ -1016,6 +1132,18 @@ function createCore(ports = {}) {
|
|
|
1016
1132
|
}
|
|
1017
1133
|
for (const spec of input.ranges) {
|
|
1018
1134
|
if (skipSpecs.has(spec)) continue;
|
|
1135
|
+
const resolution = classifications.get(spec);
|
|
1136
|
+
if (resolution === void 0) continue;
|
|
1137
|
+
if (resolution.status === "consumed") {
|
|
1138
|
+
warnings.push(
|
|
1139
|
+
`Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed (messages consumed by existing block(s)); nothing to compress.`
|
|
1140
|
+
);
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (resolution.status === "unknown" || resolution.status === "invalid") {
|
|
1144
|
+
errors.push(rangeError(spec, resolution.error.message));
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1019
1147
|
try {
|
|
1020
1148
|
const outcome = applySingleRange({
|
|
1021
1149
|
spec,
|
|
@@ -1031,7 +1159,7 @@ function createCore(ports = {}) {
|
|
|
1031
1159
|
tokensCompressed += outcome.tokens;
|
|
1032
1160
|
warnings.push(...outcome.warnings);
|
|
1033
1161
|
} catch (error) {
|
|
1034
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
1162
|
+
errors.push(rangeError(spec, error instanceof Error ? error.message : String(error)));
|
|
1035
1163
|
}
|
|
1036
1164
|
}
|
|
1037
1165
|
state.stats.compressionCount += blocksCreated;
|
|
@@ -1239,7 +1367,7 @@ function applySingleRange(input) {
|
|
|
1239
1367
|
messages: input.messages,
|
|
1240
1368
|
state: input.state
|
|
1241
1369
|
});
|
|
1242
|
-
const rangeMessageIds =
|
|
1370
|
+
const rangeMessageIds = applyPairBoundaryAdjustments(
|
|
1243
1371
|
resolved,
|
|
1244
1372
|
input.messages
|
|
1245
1373
|
);
|
|
@@ -1353,20 +1481,33 @@ function applySingleRange(input) {
|
|
|
1353
1481
|
}
|
|
1354
1482
|
return { tokens: compressedTokens, warnings };
|
|
1355
1483
|
}
|
|
1356
|
-
function
|
|
1484
|
+
function applyPairBoundaryAdjustments(resolved, messages) {
|
|
1357
1485
|
if (resolved.boundaryKind === "block") {
|
|
1358
1486
|
return resolved.messageIds;
|
|
1359
1487
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1488
|
+
let startIndex = resolved.startIndex;
|
|
1489
|
+
let endIndex = resolved.endIndex;
|
|
1490
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
1491
|
+
const reasoningAdjusted = adjustBoundariesForReasoningPairs(
|
|
1492
|
+
startIndex,
|
|
1493
|
+
endIndex,
|
|
1494
|
+
messages
|
|
1495
|
+
);
|
|
1496
|
+
const toolAdjusted = adjustBoundariesForToolPairs(
|
|
1497
|
+
reasoningAdjusted.startIndex,
|
|
1498
|
+
reasoningAdjusted.endIndex,
|
|
1499
|
+
messages
|
|
1500
|
+
);
|
|
1501
|
+
const changed = toolAdjusted.startIndex !== startIndex || toolAdjusted.endIndex !== endIndex;
|
|
1502
|
+
startIndex = toolAdjusted.startIndex;
|
|
1503
|
+
endIndex = toolAdjusted.endIndex;
|
|
1504
|
+
if (!changed) break;
|
|
1505
|
+
}
|
|
1506
|
+
if (startIndex === resolved.startIndex && endIndex === resolved.endIndex) {
|
|
1366
1507
|
return resolved.messageIds;
|
|
1367
1508
|
}
|
|
1368
1509
|
const ids = [];
|
|
1369
|
-
for (let i =
|
|
1510
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
1370
1511
|
const msg = messages[i];
|
|
1371
1512
|
if (msg) ids.push(msg.id);
|
|
1372
1513
|
}
|
|
@@ -1466,6 +1607,7 @@ function decideNudge(input) {
|
|
|
1466
1607
|
const limit = config.modelContextLimit;
|
|
1467
1608
|
const usage = limit > 0 ? tokenCount / limit : 0;
|
|
1468
1609
|
const nudgeGrowthTokens = resolveAdaptiveGrowth(limit, config.nudge);
|
|
1610
|
+
const overLimit = usage >= config.nudge.maxContextLimitPct;
|
|
1469
1611
|
const emergencyOverride = usage >= config.nudge.emergencyThresholdPct;
|
|
1470
1612
|
const baseline = state.nudge.lastPerMessageNudgeTokens;
|
|
1471
1613
|
const hadPendingNudge = state.nudge.lastNudgeShownTokens > 0;
|
|
@@ -1482,7 +1624,7 @@ function decideNudge(input) {
|
|
|
1482
1624
|
let injectedTier = null;
|
|
1483
1625
|
let injectedReason = "";
|
|
1484
1626
|
const growthReady = growthSinceReference >= growthFloor;
|
|
1485
|
-
if (!
|
|
1627
|
+
if (!overLimit && growthReady) {
|
|
1486
1628
|
for (const tier of [1, 2, 3]) {
|
|
1487
1629
|
if (!config.tiers.enabled && tier > 1) break;
|
|
1488
1630
|
const info = tiers[tier];
|
|
@@ -1494,11 +1636,26 @@ function decideNudge(input) {
|
|
|
1494
1636
|
injectedReason = tier === 1 ? `T1 compressible ${info.pending} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%` : `T${tier} distill ready: ${info.targetBlocks.length} tier-${tier - 1} blocks (${info.pending} tokens) >= ${nudgeGrowthTokens}, usage ${Math.round(usage * 100)}%`;
|
|
1495
1637
|
break;
|
|
1496
1638
|
}
|
|
1639
|
+
} else if (overLimit) {
|
|
1640
|
+
for (const tier of [1, 2, 3]) {
|
|
1641
|
+
if (!config.tiers.enabled && tier > 1) break;
|
|
1642
|
+
const info = tiers[tier];
|
|
1643
|
+
if (!info || info.pending < config.compress.minCompressRange) continue;
|
|
1644
|
+
injectedTier = tier;
|
|
1645
|
+
injectedReason = emergencyOverride ? `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}%, T${tier} pending ${info.pending}` : `OVER-LIMIT: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.maxContextLimitPct * 100)}%, T${tier} pending ${info.pending}`;
|
|
1646
|
+
break;
|
|
1647
|
+
}
|
|
1497
1648
|
}
|
|
1498
|
-
const shouldInject =
|
|
1649
|
+
const shouldInject = injectedTier !== null || overLimit && (rec?.recommendedRanges?.length ?? 0) > 0;
|
|
1499
1650
|
let reason;
|
|
1500
|
-
if (emergencyOverride) {
|
|
1501
|
-
reason =
|
|
1651
|
+
if (emergencyOverride && injectedTier !== null) {
|
|
1652
|
+
reason = injectedReason;
|
|
1653
|
+
} else if (emergencyOverride) {
|
|
1654
|
+
reason = `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}% (no compressible content)`;
|
|
1655
|
+
} else if (overLimit && injectedTier !== null) {
|
|
1656
|
+
reason = injectedReason;
|
|
1657
|
+
} else if (overLimit) {
|
|
1658
|
+
reason = `OVER-LIMIT: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.maxContextLimitPct * 100)}% (no compressible content)`;
|
|
1502
1659
|
} else if (injectedTier !== null) {
|
|
1503
1660
|
reason = injectedReason;
|
|
1504
1661
|
} else {
|
|
@@ -1534,6 +1691,7 @@ function decideNudge(input) {
|
|
|
1534
1691
|
nudgeGrowthTokens,
|
|
1535
1692
|
growthFloor,
|
|
1536
1693
|
hasPendingNudge: hasPendingNudge ? 1 : 0,
|
|
1694
|
+
overLimit: overLimit ? 1 : 0,
|
|
1537
1695
|
emergencyOverride: emergencyOverride ? 1 : 0,
|
|
1538
1696
|
pendingT1: tiers[1].pending,
|
|
1539
1697
|
pendingT2: tiers[2].pending,
|
|
@@ -1705,12 +1863,39 @@ DROP:
|
|
|
1705
1863
|
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
|
|
1706
1864
|
|
|
1707
1865
|
SIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output \u2248 N \xD7 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;
|
|
1708
|
-
var
|
|
1866
|
+
var defaultPrompts = Object.freeze({
|
|
1867
|
+
compressPhilosophy: COMPRESS_PHILOSOPHY,
|
|
1868
|
+
howToCompressRules: HOW_TO_COMPRESS_RULES,
|
|
1869
|
+
tier2DistillRules: TIER2_DISTILL_RULES,
|
|
1870
|
+
tier3CondenseRules: TIER3_CONDENSE_RULES
|
|
1871
|
+
});
|
|
1872
|
+
function resolvePrompts(overrides, options = {}) {
|
|
1873
|
+
const clean = {};
|
|
1874
|
+
if (overrides) {
|
|
1875
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1876
|
+
if (typeof value === "string") {
|
|
1877
|
+
clean[key] = value;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
const keys = Object.keys(clean);
|
|
1882
|
+
if (keys.length > 0 && !options.acknowledgeRisk) {
|
|
1883
|
+
throw new Error(
|
|
1884
|
+
`resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. Overridden keys: ${keys.join(", ")}. These rules are quality-critical (tuned over months of production use); changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
return { ...defaultPrompts, ...clean };
|
|
1888
|
+
}
|
|
1889
|
+
function efficiencyNote(prompts) {
|
|
1890
|
+
return `This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.
|
|
1709
1891
|
|
|
1710
|
-
${
|
|
1711
|
-
|
|
1892
|
+
${prompts.compressPhilosophy}`;
|
|
1893
|
+
}
|
|
1894
|
+
function emergencyHeader(prompts) {
|
|
1895
|
+
return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
|
|
1712
1896
|
|
|
1713
|
-
${
|
|
1897
|
+
${prompts.compressPhilosophy}`;
|
|
1898
|
+
}
|
|
1714
1899
|
function formatK(n) {
|
|
1715
1900
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1716
1901
|
return `${n}`;
|
|
@@ -1818,7 +2003,7 @@ function formatRanges(compressible, protectedRanges) {
|
|
|
1818
2003
|
return `Compressible ranges (${merged.length}, oldest first):
|
|
1819
2004
|
${lines.join("\n")}`;
|
|
1820
2005
|
}
|
|
1821
|
-
function renderNudgeText(decision) {
|
|
2006
|
+
function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
1822
2007
|
const breakdownStr = formatBreakdown(decision.contextBreakdown);
|
|
1823
2008
|
const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
|
|
1824
2009
|
if (decision.tier !== null && decision.tier >= 2) {
|
|
@@ -1830,7 +2015,7 @@ function renderNudgeText(decision) {
|
|
|
1830
2015
|
return {
|
|
1831
2016
|
voice: "gentle",
|
|
1832
2017
|
text: [
|
|
1833
|
-
|
|
2018
|
+
efficiencyNote(prompts),
|
|
1834
2019
|
"",
|
|
1835
2020
|
breakdownStr,
|
|
1836
2021
|
"",
|
|
@@ -1839,22 +2024,22 @@ function renderNudgeText(decision) {
|
|
|
1839
2024
|
blockList,
|
|
1840
2025
|
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
1841
2026
|
"",
|
|
1842
|
-
|
|
2027
|
+
prompts.howToCompressRules,
|
|
1843
2028
|
"",
|
|
1844
|
-
isT2 ?
|
|
2029
|
+
isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
|
|
1845
2030
|
].join("\n")
|
|
1846
2031
|
};
|
|
1847
2032
|
}
|
|
1848
|
-
const isEmergency = !!decision.breakdown?.emergencyOverride;
|
|
2033
|
+
const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
|
|
1849
2034
|
if (isEmergency) {
|
|
1850
2035
|
return {
|
|
1851
2036
|
voice: "emergency",
|
|
1852
2037
|
text: [
|
|
1853
|
-
|
|
2038
|
+
emergencyHeader(prompts),
|
|
1854
2039
|
"",
|
|
1855
2040
|
breakdownStr,
|
|
1856
2041
|
"",
|
|
1857
|
-
|
|
2042
|
+
prompts.howToCompressRules,
|
|
1858
2043
|
"",
|
|
1859
2044
|
`{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
|
|
1860
2045
|
"Only use IDs from visible messages above. Compress older work first.",
|
|
@@ -1866,11 +2051,11 @@ function renderNudgeText(decision) {
|
|
|
1866
2051
|
return {
|
|
1867
2052
|
voice: "gentle",
|
|
1868
2053
|
text: [
|
|
1869
|
-
|
|
2054
|
+
efficiencyNote(prompts),
|
|
1870
2055
|
"",
|
|
1871
2056
|
breakdownStr,
|
|
1872
2057
|
"",
|
|
1873
|
-
|
|
2058
|
+
prompts.howToCompressRules,
|
|
1874
2059
|
"",
|
|
1875
2060
|
rangesStr,
|
|
1876
2061
|
"",
|
|
@@ -2403,16 +2588,47 @@ function makePreview(text, query, len) {
|
|
|
2403
2588
|
// src/config.ts
|
|
2404
2589
|
var DEFAULT_TOOL_BASH_TIMEOUT = 60;
|
|
2405
2590
|
var DEFAULT_TOOL_OUTPUT_MAX_BYTES = 2e5;
|
|
2591
|
+
function resolveDelegate(adapter) {
|
|
2592
|
+
const d = adapter.delegate;
|
|
2593
|
+
if (typeof d === "object" && d !== null) {
|
|
2594
|
+
return {
|
|
2595
|
+
enabled: d.enabled !== false,
|
|
2596
|
+
displayUsage: d.displayUsage ?? adapter.displayUsage ?? "separate"
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
return {
|
|
2600
|
+
enabled: d !== false,
|
|
2601
|
+
displayUsage: adapter.displayUsage ?? "separate"
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2406
2604
|
function resolveConfig(adapter, liveContextLimit) {
|
|
2407
2605
|
const envLimit = process.env.ACP_MODEL_CONTEXT_LIMIT;
|
|
2408
2606
|
const envLimitNum = envLimit ? Number(envLimit) : NaN;
|
|
2409
2607
|
const FALLBACK_LIMIT = 15e4;
|
|
2410
2608
|
const limit = !Number.isNaN(envLimitNum) && envLimitNum > 0 ? envLimitNum : adapter.modelContextLimit && adapter.modelContextLimit > 0 ? adapter.modelContextLimit : liveContextLimit > 0 ? liveContextLimit : FALLBACK_LIMIT;
|
|
2411
|
-
|
|
2609
|
+
const config = defaultConfig(limit, {
|
|
2412
2610
|
protectedTools: adapter.protectedTools ?? [],
|
|
2413
2611
|
preserveRecentMessages: adapter.preserveRecentMessages ?? 5,
|
|
2414
2612
|
...adapter.coreOverrides
|
|
2415
2613
|
});
|
|
2614
|
+
const c = adapter.compress;
|
|
2615
|
+
if (c?.maxContextLimit !== void 0) config.nudge.maxContextLimitPct = parsePercent(c.maxContextLimit);
|
|
2616
|
+
if (c?.emergencyThresholdPercent !== void 0) {
|
|
2617
|
+
const pct2 = parsePercent(c.emergencyThresholdPercent);
|
|
2618
|
+
config.nudge.emergencyThresholdPct = pct2;
|
|
2619
|
+
config.truncate.threshold = pct2;
|
|
2620
|
+
}
|
|
2621
|
+
if (c?.nudgeGrowthTokens !== void 0) {
|
|
2622
|
+
config.nudge.growthFloor = c.nudgeGrowthTokens;
|
|
2623
|
+
config.nudge.growthCap = c.nudgeGrowthTokens;
|
|
2624
|
+
}
|
|
2625
|
+
return config;
|
|
2626
|
+
}
|
|
2627
|
+
function parsePercent(v) {
|
|
2628
|
+
if (typeof v === "number") return v;
|
|
2629
|
+
const s = v.trim();
|
|
2630
|
+
if (s.endsWith("%")) return Number(s.slice(0, -1)) / 100;
|
|
2631
|
+
return Number(s);
|
|
2416
2632
|
}
|
|
2417
2633
|
|
|
2418
2634
|
// src/messages.ts
|
|
@@ -2927,6 +3143,99 @@ function mergeInitialState(parsed) {
|
|
|
2927
3143
|
};
|
|
2928
3144
|
}
|
|
2929
3145
|
|
|
3146
|
+
// src/sequence-match.ts
|
|
3147
|
+
function findUniqueLongestRun(candidates, live) {
|
|
3148
|
+
if (candidates.length === 0 || live.length === 0) return void 0;
|
|
3149
|
+
const ids = /* @__PURE__ */ new Map();
|
|
3150
|
+
const intern = (key) => {
|
|
3151
|
+
const existing = ids.get(key);
|
|
3152
|
+
if (existing !== void 0) return existing;
|
|
3153
|
+
const id = ids.size + 1;
|
|
3154
|
+
ids.set(key, id);
|
|
3155
|
+
return id;
|
|
3156
|
+
};
|
|
3157
|
+
const candidateIds = candidates.map(intern);
|
|
3158
|
+
const liveIds = live.map(intern);
|
|
3159
|
+
const separator = ids.size + 1;
|
|
3160
|
+
const sequence = [...candidateIds, separator, ...liveIds];
|
|
3161
|
+
const suffixArray = buildSuffixArray(sequence);
|
|
3162
|
+
const lcp = buildLcp(sequence, suffixArray);
|
|
3163
|
+
const candidateLength = candidates.length;
|
|
3164
|
+
const liveOffset = candidateLength + 1;
|
|
3165
|
+
const sourceOf = (suffix) => {
|
|
3166
|
+
if (suffix < candidateLength) return 0;
|
|
3167
|
+
if (suffix >= liveOffset) return 1;
|
|
3168
|
+
return void 0;
|
|
3169
|
+
};
|
|
3170
|
+
let bestLength = 0;
|
|
3171
|
+
for (let index = 1; index < suffixArray.length; index++) {
|
|
3172
|
+
const leftSource = sourceOf(suffixArray[index - 1]);
|
|
3173
|
+
const rightSource = sourceOf(suffixArray[index]);
|
|
3174
|
+
if (leftSource === void 0 || rightSource === void 0 || leftSource === rightSource) continue;
|
|
3175
|
+
bestLength = Math.max(bestLength, lcp[index]);
|
|
3176
|
+
}
|
|
3177
|
+
if (bestLength === 0) return void 0;
|
|
3178
|
+
let pairCount = 0;
|
|
3179
|
+
let uniqueCandidateStart = -1;
|
|
3180
|
+
let uniqueLiveStart = -1;
|
|
3181
|
+
for (let start = 0; start < suffixArray.length; ) {
|
|
3182
|
+
let end = start;
|
|
3183
|
+
while (end + 1 < suffixArray.length && lcp[end + 1] >= bestLength) end++;
|
|
3184
|
+
if (end > start) {
|
|
3185
|
+
const candidateStarts = [];
|
|
3186
|
+
const liveStarts = [];
|
|
3187
|
+
for (let index = start; index <= end; index++) {
|
|
3188
|
+
const suffix = suffixArray[index];
|
|
3189
|
+
const source = sourceOf(suffix);
|
|
3190
|
+
if (source === 0) candidateStarts.push(suffix);
|
|
3191
|
+
else if (source === 1) liveStarts.push(suffix - liveOffset);
|
|
3192
|
+
}
|
|
3193
|
+
const groupPairs = candidateStarts.length * liveStarts.length;
|
|
3194
|
+
pairCount += groupPairs;
|
|
3195
|
+
if (groupPairs === 1) {
|
|
3196
|
+
uniqueCandidateStart = candidateStarts[0];
|
|
3197
|
+
uniqueLiveStart = liveStarts[0];
|
|
3198
|
+
}
|
|
3199
|
+
if (pairCount > 1) return void 0;
|
|
3200
|
+
}
|
|
3201
|
+
start = end + 1;
|
|
3202
|
+
}
|
|
3203
|
+
return pairCount === 1 ? { candidateStart: uniqueCandidateStart, liveStart: uniqueLiveStart, length: bestLength } : void 0;
|
|
3204
|
+
}
|
|
3205
|
+
function buildSuffixArray(sequence) {
|
|
3206
|
+
const suffixArray = sequence.map((_, index) => index);
|
|
3207
|
+
let ranks = [...sequence];
|
|
3208
|
+
for (let width = 1; width < sequence.length; width *= 2) {
|
|
3209
|
+
suffixArray.sort((left, right) => ranks[left] - ranks[right] || (ranks[left + width] ?? -1) - (ranks[right + width] ?? -1));
|
|
3210
|
+
const nextRanks = Array(sequence.length);
|
|
3211
|
+
nextRanks[suffixArray[0]] = 0;
|
|
3212
|
+
for (let index = 1; index < suffixArray.length; index++) {
|
|
3213
|
+
const previous = suffixArray[index - 1];
|
|
3214
|
+
const current = suffixArray[index];
|
|
3215
|
+
const differs = ranks[previous] !== ranks[current] || (ranks[previous + width] ?? -1) !== (ranks[current + width] ?? -1);
|
|
3216
|
+
nextRanks[current] = nextRanks[previous] + (differs ? 1 : 0);
|
|
3217
|
+
}
|
|
3218
|
+
ranks = nextRanks;
|
|
3219
|
+
if (ranks[suffixArray.at(-1)] === sequence.length - 1) break;
|
|
3220
|
+
}
|
|
3221
|
+
return suffixArray;
|
|
3222
|
+
}
|
|
3223
|
+
function buildLcp(sequence, suffixArray) {
|
|
3224
|
+
const positions = Array(sequence.length);
|
|
3225
|
+
for (let index = 0; index < suffixArray.length; index++) positions[suffixArray[index]] = index;
|
|
3226
|
+
const lcp = Array(sequence.length).fill(0);
|
|
3227
|
+
let length = 0;
|
|
3228
|
+
for (let start = 0; start < sequence.length; start++) {
|
|
3229
|
+
const position = positions[start];
|
|
3230
|
+
if (position === 0) continue;
|
|
3231
|
+
const previous = suffixArray[position - 1];
|
|
3232
|
+
while (start + length < sequence.length && previous + length < sequence.length && sequence[start + length] === sequence[previous + length]) length++;
|
|
3233
|
+
lcp[position] = length;
|
|
3234
|
+
if (length > 0) length--;
|
|
3235
|
+
}
|
|
3236
|
+
return lcp;
|
|
3237
|
+
}
|
|
3238
|
+
|
|
2930
3239
|
// src/runtime.ts
|
|
2931
3240
|
function readContextEntries(sm) {
|
|
2932
3241
|
const source = sm;
|
|
@@ -2940,15 +3249,17 @@ function isPiHost(sm) {
|
|
|
2940
3249
|
}
|
|
2941
3250
|
function mergeLiveEntries(entries, live, state, origins) {
|
|
2942
3251
|
const persisted = entries.filter((e) => e.type === "message");
|
|
2943
|
-
const
|
|
2944
|
-
const
|
|
3252
|
+
const liveIdentities = live.map(messageIdentity);
|
|
3253
|
+
const persistedIdentities = persisted.map((entry) => messageIdentity(entry.message));
|
|
3254
|
+
const persistedRange = findUniqueLongestRun(persistedIdentities, normalizePersistedMatchKeys(persisted, persistedIdentities, live, liveIdentities));
|
|
3255
|
+
const originRange = findUniqueLongestRun(origins.map((origin) => origin.identity), liveIdentities);
|
|
2945
3256
|
const out = [];
|
|
2946
3257
|
const nextOrigins = [];
|
|
2947
3258
|
const usedIds = /* @__PURE__ */ new Set();
|
|
2948
3259
|
for (let i = 0; i < live.length; i++) {
|
|
2949
3260
|
const msg = live[i];
|
|
2950
|
-
const entry =
|
|
2951
|
-
const origin =
|
|
3261
|
+
const entry = valueInRange(persisted, persistedRange, i);
|
|
3262
|
+
const origin = valueInRange(origins, originRange, i);
|
|
2952
3263
|
if (entry) {
|
|
2953
3264
|
if (origin) migrateLiveRefs(state, origin.rawId, entry.id);
|
|
2954
3265
|
else migrateTaggedRef(state, msg, entry.id);
|
|
@@ -2958,23 +3269,13 @@ function mergeLiveEntries(entries, live, state, origins) {
|
|
|
2958
3269
|
const id = origin?.rawId ?? nextLiveId(state, usedIds, i);
|
|
2959
3270
|
usedIds.add(id);
|
|
2960
3271
|
out.push({ type: "message", id, parentId: null, timestamp: String(msg.timestamp ?? Date.now()), message: msg });
|
|
2961
|
-
nextOrigins.push({ rawId: id, identity:
|
|
3272
|
+
nextOrigins.push({ rawId: id, identity: liveIdentities[i] });
|
|
2962
3273
|
}
|
|
2963
3274
|
origins.splice(0, origins.length, ...nextOrigins);
|
|
2964
|
-
const unmatched = live.length -
|
|
3275
|
+
const unmatched = live.length - (persistedRange?.length ?? 0);
|
|
2965
3276
|
if (unmatched > 0) logInfo("runtime", { event: "merge-live-entries", live: live.length, unmatched });
|
|
2966
3277
|
return out;
|
|
2967
3278
|
}
|
|
2968
|
-
function matchOrigins(origins, live) {
|
|
2969
|
-
for (let start = 0; start < origins.length; start++) {
|
|
2970
|
-
const count = origins.length - start;
|
|
2971
|
-
if (count > live.length) continue;
|
|
2972
|
-
if (origins.slice(start).every((origin, index) => origin.identity === messageIdentity(live[index]))) {
|
|
2973
|
-
return [...origins.slice(start), ...Array(live.length - count)];
|
|
2974
|
-
}
|
|
2975
|
-
}
|
|
2976
|
-
return Array(live.length);
|
|
2977
|
-
}
|
|
2978
3279
|
function nextLiveId(state, used, index) {
|
|
2979
3280
|
let id = `live-${index}`;
|
|
2980
3281
|
let suffix = index;
|
|
@@ -3001,31 +3302,39 @@ function migrateLiveRefs(state, liveId, stableId) {
|
|
|
3001
3302
|
delete state.messageRefs.byRaw[rawId];
|
|
3002
3303
|
}
|
|
3003
3304
|
}
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
const
|
|
3009
|
-
if (
|
|
3010
|
-
|
|
3011
|
-
|
|
3305
|
+
var NO_PERSISTED_MATCH = /* @__PURE__ */ Symbol("no-persisted-match");
|
|
3306
|
+
function normalizePersistedMatchKeys(persisted, persistedIdentities, live, liveIdentities) {
|
|
3307
|
+
const persistedByStructure = /* @__PURE__ */ new Map();
|
|
3308
|
+
for (let index = 0; index < persisted.length; index++) {
|
|
3309
|
+
const key = toolResultStructureKey(persisted[index].message);
|
|
3310
|
+
if (key === void 0) continue;
|
|
3311
|
+
persistedByStructure.set(key, persistedByStructure.has(key) ? -1 : index);
|
|
3312
|
+
}
|
|
3313
|
+
return live.map((message, liveIndex) => {
|
|
3314
|
+
const key = toolResultStructureKey(message);
|
|
3315
|
+
const candidateIndex = key === void 0 ? void 0 : persistedByStructure.get(key);
|
|
3316
|
+
if (candidateIndex === void 0) return liveIdentities[liveIndex];
|
|
3317
|
+
if (candidateIndex < 0) return NO_PERSISTED_MATCH;
|
|
3318
|
+
return sameToolResult(persisted[candidateIndex].message, message) ? persistedIdentities[candidateIndex] : liveIdentities[liveIndex];
|
|
3319
|
+
});
|
|
3012
3320
|
}
|
|
3013
|
-
function
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
return a === b;
|
|
3025
|
-
}
|
|
3321
|
+
function toolResultStructureKey(message) {
|
|
3322
|
+
if (message.role !== "toolResult") return void 0;
|
|
3323
|
+
return `${message.toolName}\0${message.toolCallId}`;
|
|
3324
|
+
}
|
|
3325
|
+
function valueInRange(values, range, liveIndex) {
|
|
3326
|
+
if (!range || liveIndex < range.liveStart || liveIndex >= range.liveStart + range.length) return void 0;
|
|
3327
|
+
return values[range.candidateStart + liveIndex - range.liveStart];
|
|
3328
|
+
}
|
|
3329
|
+
function sameToolResult(stored, visible) {
|
|
3330
|
+
if (stored.role !== "toolResult" || visible.role !== "toolResult") return false;
|
|
3331
|
+
return sameNonTextBlocks(stored.content, visible.content) && matchesStoredText(extractText(stored.content), extractText(visible.content));
|
|
3026
3332
|
}
|
|
3027
3333
|
function sameNonTextBlocks(a, b) {
|
|
3028
|
-
const nonText = (blocks) => blocks.filter((block) =>
|
|
3334
|
+
const nonText = (blocks) => blocks.filter((block) => {
|
|
3335
|
+
if (!block || typeof block !== "object" || !("type" in block)) return true;
|
|
3336
|
+
return block.type !== "text";
|
|
3337
|
+
});
|
|
3029
3338
|
try {
|
|
3030
3339
|
const na = Array.isArray(a) ? nonText(a) : [];
|
|
3031
3340
|
const nb = Array.isArray(b) ? nonText(b) : [];
|
|
@@ -3053,6 +3362,7 @@ function createRuntime(adapter) {
|
|
|
3053
3362
|
const store = new SessionStateStore();
|
|
3054
3363
|
const locks = /* @__PURE__ */ new Map();
|
|
3055
3364
|
let adapterRef = adapter;
|
|
3365
|
+
let promptsRef = defaultPrompts;
|
|
3056
3366
|
const nudgeShownTurns = /* @__PURE__ */ new Set();
|
|
3057
3367
|
async function acquireLock(sid) {
|
|
3058
3368
|
const prev = locks.get(sid) ?? Promise.resolve();
|
|
@@ -3101,6 +3411,10 @@ function createRuntime(adapter) {
|
|
|
3101
3411
|
return adapterRef;
|
|
3102
3412
|
}, setAdapter: (a) => {
|
|
3103
3413
|
adapterRef = a;
|
|
3414
|
+
}, get prompts() {
|
|
3415
|
+
return promptsRef;
|
|
3416
|
+
}, setPrompts: (p) => {
|
|
3417
|
+
promptsRef = p;
|
|
3104
3418
|
}, markNudgeShown: (k) => {
|
|
3105
3419
|
nudgeShownTurns.add(k);
|
|
3106
3420
|
}, nudgeShownFor: (k) => nudgeShownTurns.has(k), clearNudgeTracking: () => {
|
|
@@ -7715,6 +8029,18 @@ function findMessageContent(ref, ctx) {
|
|
|
7715
8029
|
}
|
|
7716
8030
|
return null;
|
|
7717
8031
|
}
|
|
8032
|
+
function resolveBlockMessages(block, coreMessages, ctx) {
|
|
8033
|
+
const neededBaseIds = new Set(block.effectiveMessageIds.map((id) => id.split("#")[0]));
|
|
8034
|
+
const presentBaseIds = new Set(coreMessages.map((m) => m.id.split("#")[0]));
|
|
8035
|
+
const missingBaseIds = [...neededBaseIds].filter((id) => !presentBaseIds.has(id));
|
|
8036
|
+
if (missingBaseIds.length === 0) return coreMessages;
|
|
8037
|
+
const extra = [];
|
|
8038
|
+
for (const baseId of missingBaseIds) {
|
|
8039
|
+
const entry = ctx.sessionManager.getEntry(baseId);
|
|
8040
|
+
if (entry) extra.push(...entriesToCoreMessages([entry]));
|
|
8041
|
+
}
|
|
8042
|
+
return [...coreMessages, ...extra];
|
|
8043
|
+
}
|
|
7718
8044
|
async function handleMessageRef(ref, ownerBlockId, args, ctx) {
|
|
7719
8045
|
const found = findMessageContent(ref, ctx);
|
|
7720
8046
|
if (!found || !found.text) {
|
|
@@ -7762,7 +8088,8 @@ async function handleDecompress(args, runtime, ctx) {
|
|
|
7762
8088
|
return `Block ${blockId} not found. Active blocks: ${active || "(none)"}.`;
|
|
7763
8089
|
}
|
|
7764
8090
|
const full = args.full ?? false;
|
|
7765
|
-
const
|
|
8091
|
+
const resolved = resolveBlockMessages(block, coreMessages, ctx);
|
|
8092
|
+
const { text, count } = collectBlockContent(state, block, resolved, { full });
|
|
7766
8093
|
if (count === 0) return `Block ${blockId} has no restorable message content.`;
|
|
7767
8094
|
if (args.inline === true && !args.toFile) {
|
|
7768
8095
|
debug.event("decompress", { blockId, full, count, mode: "inline" });
|
|
@@ -7924,10 +8251,10 @@ function formatSize(tokens) {
|
|
|
7924
8251
|
import {
|
|
7925
8252
|
spawn
|
|
7926
8253
|
} from "child_process";
|
|
7927
|
-
import { createWriteStream } from "fs";
|
|
8254
|
+
import { createWriteStream, existsSync as existsSync2 } from "fs";
|
|
7928
8255
|
import { mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, appendFile } from "fs/promises";
|
|
7929
8256
|
import { tmpdir as tmpdir2 } from "os";
|
|
7930
|
-
import { join as join4 } from "path";
|
|
8257
|
+
import { dirname as dirname3, join as join4, resolve as resolvePath } from "path";
|
|
7931
8258
|
|
|
7932
8259
|
// src/footer-status.ts
|
|
7933
8260
|
var FOOTER_STATUS_KEY = "billion-context-pi";
|
|
@@ -8340,6 +8667,44 @@ function delegateSpawnOptions(cwd, env) {
|
|
|
8340
8667
|
shell: false
|
|
8341
8668
|
};
|
|
8342
8669
|
}
|
|
8670
|
+
var PI_CLI_ENTRY_RE = /[\\/]pi-coding-agent[\\/]dist[\\/]cli\.js$/;
|
|
8671
|
+
var PI_PACKAGE_REL = join4("@earendil-works", "pi-coding-agent", "dist", "cli.js");
|
|
8672
|
+
function probeUpFromArgv(argv1) {
|
|
8673
|
+
let dir = resolvePath(dirname3(argv1) || process.cwd());
|
|
8674
|
+
for (; ; ) {
|
|
8675
|
+
const candidate = join4(dir, "node_modules", PI_PACKAGE_REL);
|
|
8676
|
+
if (existsSync2(candidate)) return candidate;
|
|
8677
|
+
const parent = dirname3(dir);
|
|
8678
|
+
if (parent === dir) return null;
|
|
8679
|
+
dir = parent;
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
function piCliGlobalCandidates(env) {
|
|
8683
|
+
const candidates = [];
|
|
8684
|
+
if (process.platform === "win32") {
|
|
8685
|
+
if (env.APPDATA) candidates.push(join4(env.APPDATA, "npm", "node_modules", PI_PACKAGE_REL));
|
|
8686
|
+
} else {
|
|
8687
|
+
const home = env.HOME ?? env.USERPROFILE;
|
|
8688
|
+
if (home) candidates.push(join4(home, ".local", "lib", "node_modules", PI_PACKAGE_REL));
|
|
8689
|
+
candidates.push(join4("/usr/local", "lib", "node_modules", PI_PACKAGE_REL));
|
|
8690
|
+
candidates.push(join4("/usr", "lib", "node_modules", PI_PACKAGE_REL));
|
|
8691
|
+
}
|
|
8692
|
+
return candidates;
|
|
8693
|
+
}
|
|
8694
|
+
function resolvePiCliEntry(argv1, env = process.env, piHost = true) {
|
|
8695
|
+
const explicit = env.PI_CLI_PATH;
|
|
8696
|
+
if (explicit) return explicit;
|
|
8697
|
+
if (argv1 && PI_CLI_ENTRY_RE.test(argv1)) return argv1;
|
|
8698
|
+
if (piHost) {
|
|
8699
|
+
const probed = probeUpFromArgv(argv1);
|
|
8700
|
+
if (probed) return probed;
|
|
8701
|
+
for (const candidate of piCliGlobalCandidates(env)) {
|
|
8702
|
+
if (existsSync2(candidate)) return candidate;
|
|
8703
|
+
}
|
|
8704
|
+
logWarn("delegate", { event: "cli-entry-unresolved", argv1, fallback: "argv[1]" });
|
|
8705
|
+
}
|
|
8706
|
+
return argv1;
|
|
8707
|
+
}
|
|
8343
8708
|
var ACP_TOOLS = ["compress", "decompress", "search_context", "acp_status"];
|
|
8344
8709
|
var RESTRICTED_TOOLS = "read,bash,grep,find,ls";
|
|
8345
8710
|
var AGENTS = {
|
|
@@ -8761,7 +9126,7 @@ async function runDelegate(pi, args, ctx, signal) {
|
|
|
8761
9126
|
logInfo("delegate", { event: "spawn", agent: args.agent, runId, cwd, async: isAsync, useJsonStream, mode: ctx.mode, parentDepth });
|
|
8762
9127
|
const child = spawn(
|
|
8763
9128
|
process.execPath,
|
|
8764
|
-
[process.argv[1], ...cliArgs],
|
|
9129
|
+
[resolvePiCliEntry(process.argv[1] ?? "", process.env, isPiHost(ctx.sessionManager)), ...cliArgs],
|
|
8765
9130
|
delegateSpawnOptions(cwd, childEnv)
|
|
8766
9131
|
);
|
|
8767
9132
|
child.stdin?.once("error", (e) => {
|
|
@@ -9172,7 +9537,7 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
9172
9537
|
const costStr = cost > 0 ? ` ($${cost.toFixed(4)})` : "";
|
|
9173
9538
|
extra.push("\u2500\u2500 Session delegate usage (excluded from main totals) \u2500\u2500");
|
|
9174
9539
|
extra.push(`Tokens: ${delegateUsage.input.toLocaleString()} in, ${delegateUsage.output.toLocaleString()} out (${delegateUsage.totalTokens.toLocaleString()} total)${costStr}`);
|
|
9175
|
-
} else if (runtime.adapter.displayUsage === "merged") {
|
|
9540
|
+
} else if (resolveDelegate(runtime.adapter).displayUsage === "merged") {
|
|
9176
9541
|
extra.push("");
|
|
9177
9542
|
extra.push("merged mode: delegate usage is included in main session totals.");
|
|
9178
9543
|
} else {
|
|
@@ -9293,7 +9658,7 @@ async function statusReport(runtime, ctx) {
|
|
|
9293
9658
|
const activeBlocksList = state.blocks.filter((b) => b.active);
|
|
9294
9659
|
const totalBlocksList = state.blocks;
|
|
9295
9660
|
const lines = [];
|
|
9296
|
-
const versionStr = "0.1.
|
|
9661
|
+
const versionStr = "0.1.37" ? `billion-context-pi@${"0.1.37"}` : "";
|
|
9297
9662
|
lines.push("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E");
|
|
9298
9663
|
lines.push("\u2502 ACP Context Analysis \u2502");
|
|
9299
9664
|
lines.push("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F");
|
|
@@ -9370,7 +9735,8 @@ async function statusReport(runtime, ctx) {
|
|
|
9370
9735
|
}
|
|
9371
9736
|
|
|
9372
9737
|
// src/system-prompt.ts
|
|
9373
|
-
|
|
9738
|
+
function buildAcpSystemPrompt(prompts) {
|
|
9739
|
+
return `
|
|
9374
9740
|
ACP context management
|
|
9375
9741
|
|
|
9376
9742
|
ACP TAGS
|
|
@@ -9394,7 +9760,7 @@ You have four context-management tools:
|
|
|
9394
9760
|
- search_context \u2014 Search compressed block summaries (and optionally visible messages) by keyword. Use BEFORE decompressing to find the right block. Example: search_context({ query: "auth token refresh" }).
|
|
9395
9761
|
- acp_status \u2014 Context status with compressible ranges. No args = overview + totals. scope:"uncompressed" for range view; add view:"messages" for per-message listing. scope:"compressed" for block details.
|
|
9396
9762
|
|
|
9397
|
-
${
|
|
9763
|
+
${prompts.compressPhilosophy}
|
|
9398
9764
|
|
|
9399
9765
|
WHEN TO COMPRESS
|
|
9400
9766
|
|
|
@@ -9412,7 +9778,7 @@ WHEN NOT TO COMPRESS
|
|
|
9412
9778
|
- Important user messages \u2014 preserve their exact intent, constraints, and acceptance criteria. If a message in the range must stay verbatim, exclude it from the compress range instead of compressing it.
|
|
9413
9779
|
- Protected tool outputs \u2014 hard-excluded from compression ranges, survive intact in visible context.
|
|
9414
9780
|
|
|
9415
|
-
${
|
|
9781
|
+
${prompts.howToCompressRules}
|
|
9416
9782
|
|
|
9417
9783
|
MULTI-TIER COMPRESSION
|
|
9418
9784
|
|
|
@@ -9420,9 +9786,9 @@ Summaries accumulate as the session grows. When tier-1 summaries pile up, the sy
|
|
|
9420
9786
|
|
|
9421
9787
|
To compress blocks: use block IDs as boundaries: compress({ content: [{ startId: "b3", endId: "b15", summary: "..." }] }). This deactivates the consumed blocks and creates a new higher-tier block.
|
|
9422
9788
|
|
|
9423
|
-
${
|
|
9789
|
+
${prompts.tier2DistillRules}
|
|
9424
9790
|
|
|
9425
|
-
${
|
|
9791
|
+
${prompts.tier3CondenseRules}
|
|
9426
9792
|
|
|
9427
9793
|
THE PHILOSOPHY OF DECOMPRESS
|
|
9428
9794
|
|
|
@@ -9432,6 +9798,7 @@ CONTEXT BREAKDOWN
|
|
|
9432
9798
|
|
|
9433
9799
|
When context usage passes a threshold, the system appends a breakdown showing where tokens are spent. Compress the largest ranges first when the current step no longer needs them.
|
|
9434
9800
|
`;
|
|
9801
|
+
}
|
|
9435
9802
|
var ACP_DELEGATE_PROMPT = `
|
|
9436
9803
|
ACP_DELEGATE NOTIFICATIONS
|
|
9437
9804
|
|
|
@@ -9560,7 +9927,7 @@ function wireToolGuardrails(pi, runtime) {
|
|
|
9560
9927
|
|
|
9561
9928
|
// src/update.ts
|
|
9562
9929
|
import { readFile, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
9563
|
-
import { join as join5, dirname as
|
|
9930
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
9564
9931
|
import { fileURLToPath } from "url";
|
|
9565
9932
|
import { execFile } from "child_process";
|
|
9566
9933
|
import { homedir as homedir3 } from "os";
|
|
@@ -9593,7 +9960,7 @@ async function readLastCheck() {
|
|
|
9593
9960
|
}
|
|
9594
9961
|
async function writeLastCheck(timestamp) {
|
|
9595
9962
|
try {
|
|
9596
|
-
await mkdir3(
|
|
9963
|
+
await mkdir3(dirname4(THROTTLE_FILE), { recursive: true });
|
|
9597
9964
|
await writeFile3(THROTTLE_FILE, String(timestamp), "utf-8");
|
|
9598
9965
|
} catch {
|
|
9599
9966
|
}
|
|
@@ -9607,20 +9974,20 @@ async function readPackageJson(path4) {
|
|
|
9607
9974
|
}
|
|
9608
9975
|
}
|
|
9609
9976
|
function findNpmRoot(extDir) {
|
|
9610
|
-
let dir =
|
|
9977
|
+
let dir = dirname4(extDir);
|
|
9611
9978
|
for (; ; ) {
|
|
9612
|
-
if (dir.endsWith("node_modules")) return
|
|
9613
|
-
const parent =
|
|
9979
|
+
if (dir.endsWith("node_modules")) return dirname4(dir);
|
|
9980
|
+
const parent = dirname4(dir);
|
|
9614
9981
|
if (parent === dir) return void 0;
|
|
9615
9982
|
dir = parent;
|
|
9616
9983
|
}
|
|
9617
9984
|
}
|
|
9618
9985
|
async function findExtensionDir() {
|
|
9619
|
-
let dir =
|
|
9986
|
+
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
9620
9987
|
for (; ; ) {
|
|
9621
9988
|
const pkg = await readPackageJson(join5(dir, "package.json"));
|
|
9622
9989
|
if (pkg?.name === PACKAGE_NAME) return dir;
|
|
9623
|
-
const parent =
|
|
9990
|
+
const parent = dirname4(dir);
|
|
9624
9991
|
if (parent === dir) return void 0;
|
|
9625
9992
|
dir = parent;
|
|
9626
9993
|
}
|
|
@@ -9669,7 +10036,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
9669
10036
|
const data = await res.json();
|
|
9670
10037
|
const latest = data.version;
|
|
9671
10038
|
if (!latest) return;
|
|
9672
|
-
const current = runtimeVersion ?? "0.1.
|
|
10039
|
+
const current = runtimeVersion ?? "0.1.37";
|
|
9673
10040
|
const hasUpdate = isNewer(latest, current);
|
|
9674
10041
|
debug.event("update-check", {
|
|
9675
10042
|
current,
|
|
@@ -9705,7 +10072,7 @@ async function getRuntimeVersion() {
|
|
|
9705
10072
|
|
|
9706
10073
|
// src/setup-subagent-tools.ts
|
|
9707
10074
|
import { readFile as readFile2, writeFile as writeFile4, stat, copyFile, rename } from "fs/promises";
|
|
9708
|
-
import { existsSync as
|
|
10075
|
+
import { existsSync as existsSync3 } from "fs";
|
|
9709
10076
|
import { homedir as homedir4 } from "os";
|
|
9710
10077
|
import { join as join6 } from "path";
|
|
9711
10078
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@earendil-works/pi-coding-agent";
|
|
@@ -9770,7 +10137,7 @@ async function ensureSubagentAcpTools(settingsPath) {
|
|
|
9770
10137
|
return { path: path4, action: "skipped", reason: "all builtin agents already have ACP tools" };
|
|
9771
10138
|
}
|
|
9772
10139
|
const backupPath = `${path4}.acp-bak`;
|
|
9773
|
-
if (!
|
|
10140
|
+
if (!existsSync3(backupPath)) {
|
|
9774
10141
|
try {
|
|
9775
10142
|
await copyFile(path4, backupPath);
|
|
9776
10143
|
} catch {
|
|
@@ -9862,7 +10229,18 @@ async function loadUserConfig(cwd) {
|
|
|
9862
10229
|
function join8(...parts) {
|
|
9863
10230
|
return path3.join(...parts);
|
|
9864
10231
|
}
|
|
9865
|
-
var KNOWN = /* @__PURE__ */ new Set([
|
|
10232
|
+
var KNOWN = /* @__PURE__ */ new Set([
|
|
10233
|
+
"debug",
|
|
10234
|
+
"autoUpdate",
|
|
10235
|
+
"modelContextLimit",
|
|
10236
|
+
"toolBashDefaultTimeout",
|
|
10237
|
+
"toolOutputMaxBytes",
|
|
10238
|
+
"delegate",
|
|
10239
|
+
"compress",
|
|
10240
|
+
"displayUsage",
|
|
10241
|
+
"prompts",
|
|
10242
|
+
"acknowledgePromptsRisk"
|
|
10243
|
+
]);
|
|
9866
10244
|
function pickKnown(parsed) {
|
|
9867
10245
|
const out = {};
|
|
9868
10246
|
for (const [k, v] of Object.entries(parsed)) {
|
|
@@ -9874,8 +10252,6 @@ function applyUserConfig(adapter, user) {
|
|
|
9874
10252
|
return {
|
|
9875
10253
|
...adapter,
|
|
9876
10254
|
...user,
|
|
9877
|
-
// coreOverrides / protectedTools / preserveRecentMessages are not overridable
|
|
9878
|
-
// from acp.json (keep them from the factory config).
|
|
9879
10255
|
coreOverrides: adapter.coreOverrides,
|
|
9880
10256
|
protectedTools: adapter.protectedTools,
|
|
9881
10257
|
preserveRecentMessages: adapter.preserveRecentMessages
|
|
@@ -9911,16 +10287,22 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
9911
10287
|
resetDelegateUsage();
|
|
9912
10288
|
setDelegateDisplayUsage("separate");
|
|
9913
10289
|
const sid = ctx.sessionManager.getSessionId();
|
|
9914
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
10290
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.37" : null });
|
|
9915
10291
|
try {
|
|
9916
10292
|
const user = await loadUserConfig(ctx.cwd);
|
|
9917
10293
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
9918
|
-
setDelegateDisplayUsage(runtime.adapter.displayUsage
|
|
10294
|
+
setDelegateDisplayUsage(resolveDelegate(runtime.adapter).displayUsage);
|
|
9919
10295
|
if (runtime.adapter.debug !== void 0) setDebugEnabled(runtime.adapter.debug);
|
|
9920
10296
|
} catch (e) {
|
|
9921
10297
|
logThrow("config", e, { sid, phase: "session_start" });
|
|
9922
10298
|
}
|
|
9923
|
-
|
|
10299
|
+
try {
|
|
10300
|
+
runtime.setPrompts(resolvePrompts(runtime.adapter.prompts, { acknowledgeRisk: runtime.adapter.acknowledgePromptsRisk === true }));
|
|
10301
|
+
} catch (e) {
|
|
10302
|
+
logWarn("config", { event: "prompts-resolve-failed", error: e instanceof Error ? e.message : String(e) });
|
|
10303
|
+
runtime.setPrompts(defaultPrompts);
|
|
10304
|
+
}
|
|
10305
|
+
if (resolveDelegate(runtime.adapter).enabled) {
|
|
9924
10306
|
pi.registerTool(makeDelegateTool(pi));
|
|
9925
10307
|
pi.registerTool(makeDelegateWaitTool(pi));
|
|
9926
10308
|
pi.registerTool(makeDelegateCancelTool(pi));
|
|
@@ -9980,7 +10362,7 @@ function wireContextTransform(pi, runtime) {
|
|
|
9980
10362
|
prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
9981
10363
|
nudgeShouldInject: turn.nudge?.shouldInject ?? false,
|
|
9982
10364
|
nudgeReason: turn.nudge?.reason ?? null,
|
|
9983
|
-
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge).voice : null,
|
|
10365
|
+
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge, runtime.prompts).voice : null,
|
|
9984
10366
|
nudgePct: turn.nudge ? Math.round(turn.nudge.contextUsage * 100) : null,
|
|
9985
10367
|
nudgeTier: turn.nudge?.tier ?? null,
|
|
9986
10368
|
nudgeCompressibleCount: turn.nudge?.compressibleRanges.length ?? 0,
|
|
@@ -9997,8 +10379,8 @@ function wireContextTransform(pi, runtime) {
|
|
|
9997
10379
|
const turnKey = lastUserMessageId(entries) ?? sid;
|
|
9998
10380
|
const alreadyShown = !emergency && runtime.nudgeShownFor(turnKey);
|
|
9999
10381
|
if (!alreadyShown) {
|
|
10000
|
-
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active)));
|
|
10001
|
-
const rendered = renderNudgeText(turn.nudge);
|
|
10382
|
+
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts));
|
|
10383
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
10002
10384
|
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
10003
10385
|
const example = top ? `
|
|
10004
10386
|
|
|
@@ -10032,8 +10414,9 @@ ${rendered.text}${example}`);
|
|
|
10032
10414
|
function wireSystemPrompt(pi, runtime) {
|
|
10033
10415
|
pi.on("before_agent_start", (event) => {
|
|
10034
10416
|
const delegate = runtime.adapter.delegate !== false;
|
|
10035
|
-
const
|
|
10036
|
-
|
|
10417
|
+
const acp = buildAcpSystemPrompt(runtime.prompts);
|
|
10418
|
+
const prompt = delegate ? `${acp}
|
|
10419
|
+
${ACP_DELEGATE_PROMPT}` : acp;
|
|
10037
10420
|
return { systemPrompt: formatSystemPromptForEvent(event.systemPrompt, prompt) };
|
|
10038
10421
|
});
|
|
10039
10422
|
}
|
|
@@ -10049,8 +10432,8 @@ function collectOriginals(entries) {
|
|
|
10049
10432
|
}
|
|
10050
10433
|
return map;
|
|
10051
10434
|
}
|
|
10052
|
-
function nudgeMessage(nudge, blocks) {
|
|
10053
|
-
const rendered = renderNudgeText(nudge);
|
|
10435
|
+
function nudgeMessage(nudge, blocks, prompts) {
|
|
10436
|
+
const rendered = renderNudgeText(nudge, prompts);
|
|
10054
10437
|
const lines = [rendered.text];
|
|
10055
10438
|
if (blocks.length > 0) {
|
|
10056
10439
|
const totalSummary = blocks.reduce((s, b) => s + Math.ceil((b.summary || "").length / 4), 0);
|