billion-context-pi 0.1.36 → 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 +12 -2
- package/README.zh-CN.md +12 -2
- package/dist/config.d.ts +10 -1
- package/dist/delegate-tool.d.ts +5 -0
- package/dist/index.js +451 -128
- 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 +3 -0
- 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 = [];
|
|
@@ -343,6 +363,18 @@ function parseBoundary(ref) {
|
|
|
343
363
|
}
|
|
344
364
|
return null;
|
|
345
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
|
+
};
|
|
346
378
|
function resolveBoundaries(input) {
|
|
347
379
|
const start = parseBoundary(input.startRef);
|
|
348
380
|
const end = parseBoundary(input.endRef);
|
|
@@ -355,13 +387,8 @@ function resolveBoundaries(input) {
|
|
|
355
387
|
input.messages.forEach(
|
|
356
388
|
(message, index) => indexByRawId.set(message.id, index)
|
|
357
389
|
);
|
|
358
|
-
let startIndex = resolveAnchorIndex(start, input.state, indexByRawId);
|
|
359
|
-
let endIndex = resolveAnchorIndex(end, input.state, indexByRawId);
|
|
360
|
-
if (startIndex === null || endIndex === null) {
|
|
361
|
-
throw new Error(
|
|
362
|
-
`Boundary not found in visible context (likely consumed by an existing block). startId="${input.startRef}", endId="${input.endRef}".`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
390
|
+
let startIndex = resolveAnchorIndex(start, input.state, indexByRawId, "start");
|
|
391
|
+
let endIndex = resolveAnchorIndex(end, input.state, indexByRawId, "end");
|
|
365
392
|
if (startIndex > endIndex) {
|
|
366
393
|
[startIndex, endIndex] = [endIndex, startIndex];
|
|
367
394
|
}
|
|
@@ -392,16 +419,51 @@ function resolveBoundaries(input) {
|
|
|
392
419
|
protectedGaps
|
|
393
420
|
};
|
|
394
421
|
}
|
|
395
|
-
function resolveAnchorIndex(boundary, state, indexByRawId) {
|
|
422
|
+
function resolveAnchorIndex(boundary, state, indexByRawId, endpoint) {
|
|
423
|
+
const label = endpoint === "start" ? "startId" : "endId";
|
|
396
424
|
if (boundary.kind === "message") {
|
|
397
425
|
const rawId = state.messageRefs.byRef[boundary.raw] ?? state.messageRefs.byRef[formatPaddedRef(boundary.numericId)];
|
|
398
|
-
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
|
+
}
|
|
399
433
|
const index = indexByRawId.get(rawId);
|
|
400
|
-
|
|
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;
|
|
401
442
|
}
|
|
402
443
|
const block = blockById(state, `b${boundary.numericId}`);
|
|
403
|
-
if (!block
|
|
404
|
-
|
|
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;
|
|
405
467
|
}
|
|
406
468
|
function formatPaddedRef(index) {
|
|
407
469
|
return `m${String(index).padStart(5, "0")}`;
|
|
@@ -778,6 +840,38 @@ function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan =
|
|
|
778
840
|
}
|
|
779
841
|
return { startIndex: newStartIndex, endIndex: newEndIndex };
|
|
780
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
|
+
}
|
|
781
875
|
function refNum(ref) {
|
|
782
876
|
const n = parseInt(ref.slice(1), 10);
|
|
783
877
|
return Number.isNaN(n) ? -1 : n;
|
|
@@ -932,6 +1026,9 @@ function runPipeline(nodes, initial, ctx) {
|
|
|
932
1026
|
}
|
|
933
1027
|
return io;
|
|
934
1028
|
}
|
|
1029
|
+
function rangeError(spec, message) {
|
|
1030
|
+
return `range ${spec.startRef}..${spec.endRef}: ${message}`;
|
|
1031
|
+
}
|
|
935
1032
|
function createCore(ports = {}) {
|
|
936
1033
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
937
1034
|
function applyCompression(input) {
|
|
@@ -943,20 +1040,44 @@ function createCore(ports = {}) {
|
|
|
943
1040
|
const warnings = [];
|
|
944
1041
|
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config, countTokens);
|
|
945
1042
|
const preExistingCoverage = collectCoverage(state);
|
|
946
|
-
const
|
|
1043
|
+
const classifications = /* @__PURE__ */ new Map();
|
|
1044
|
+
const classificationErrors = [];
|
|
1045
|
+
const consumedRanges = [];
|
|
947
1046
|
for (const spec of input.ranges) {
|
|
948
|
-
let resolved;
|
|
949
1047
|
try {
|
|
950
|
-
resolved = resolveBoundaries({
|
|
1048
|
+
const resolved = resolveBoundaries({
|
|
951
1049
|
startRef: spec.startRef,
|
|
952
1050
|
endRef: spec.endRef,
|
|
953
1051
|
messages: input.messages,
|
|
954
1052
|
state
|
|
955
1053
|
});
|
|
956
|
-
|
|
957
|
-
|
|
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
|
+
}
|
|
958
1075
|
}
|
|
959
|
-
|
|
1076
|
+
}
|
|
1077
|
+
const rangeIndexSets = [];
|
|
1078
|
+
for (const [spec, resolution] of classifications) {
|
|
1079
|
+
if (resolution.status !== "ok") continue;
|
|
1080
|
+
const indices = resolution.resolved.messageIds.map(
|
|
960
1081
|
(id) => input.messages.findIndex((m) => m.id === id)
|
|
961
1082
|
).filter((i) => i >= 0);
|
|
962
1083
|
rangeIndexSets.push({ spec, indices });
|
|
@@ -983,37 +1104,27 @@ function createCore(ports = {}) {
|
|
|
983
1104
|
if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
|
|
984
1105
|
let totalRangeChars = 0;
|
|
985
1106
|
let hasBlockBoundaryRange = false;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
resolved = resolveBoundaries({
|
|
991
|
-
startRef: spec.startRef,
|
|
992
|
-
endRef: spec.endRef,
|
|
993
|
-
messages: input.messages,
|
|
994
|
-
state
|
|
995
|
-
});
|
|
996
|
-
} catch {
|
|
997
|
-
continue;
|
|
998
|
-
}
|
|
999
|
-
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") {
|
|
1000
1111
|
hasBlockBoundaryRange = true;
|
|
1001
1112
|
continue;
|
|
1002
1113
|
}
|
|
1003
|
-
|
|
1114
|
+
countedRanges++;
|
|
1115
|
+
for (const id of resolution.resolved.messageIds) {
|
|
1004
1116
|
const msg = input.messages.find((m) => m.id === id);
|
|
1005
1117
|
totalRangeChars += msg?.text?.length ?? 0;
|
|
1006
1118
|
}
|
|
1007
1119
|
}
|
|
1008
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.`;
|
|
1009
1122
|
return {
|
|
1010
1123
|
state: input.state,
|
|
1011
1124
|
result: {
|
|
1012
1125
|
blocksCreated: 0,
|
|
1013
1126
|
tokensCompressed: 0,
|
|
1014
|
-
errors: [
|
|
1015
|
-
`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.`
|
|
1016
|
-
],
|
|
1127
|
+
errors: [gateMessage, ...classificationErrors],
|
|
1017
1128
|
warnings: []
|
|
1018
1129
|
}
|
|
1019
1130
|
};
|
|
@@ -1021,6 +1132,18 @@ function createCore(ports = {}) {
|
|
|
1021
1132
|
}
|
|
1022
1133
|
for (const spec of input.ranges) {
|
|
1023
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
|
+
}
|
|
1024
1147
|
try {
|
|
1025
1148
|
const outcome = applySingleRange({
|
|
1026
1149
|
spec,
|
|
@@ -1036,7 +1159,7 @@ function createCore(ports = {}) {
|
|
|
1036
1159
|
tokensCompressed += outcome.tokens;
|
|
1037
1160
|
warnings.push(...outcome.warnings);
|
|
1038
1161
|
} catch (error) {
|
|
1039
|
-
errors.push(error instanceof Error ? error.message : String(error));
|
|
1162
|
+
errors.push(rangeError(spec, error instanceof Error ? error.message : String(error)));
|
|
1040
1163
|
}
|
|
1041
1164
|
}
|
|
1042
1165
|
state.stats.compressionCount += blocksCreated;
|
|
@@ -1244,7 +1367,7 @@ function applySingleRange(input) {
|
|
|
1244
1367
|
messages: input.messages,
|
|
1245
1368
|
state: input.state
|
|
1246
1369
|
});
|
|
1247
|
-
const rangeMessageIds =
|
|
1370
|
+
const rangeMessageIds = applyPairBoundaryAdjustments(
|
|
1248
1371
|
resolved,
|
|
1249
1372
|
input.messages
|
|
1250
1373
|
);
|
|
@@ -1358,20 +1481,33 @@ function applySingleRange(input) {
|
|
|
1358
1481
|
}
|
|
1359
1482
|
return { tokens: compressedTokens, warnings };
|
|
1360
1483
|
}
|
|
1361
|
-
function
|
|
1484
|
+
function applyPairBoundaryAdjustments(resolved, messages) {
|
|
1362
1485
|
if (resolved.boundaryKind === "block") {
|
|
1363
1486
|
return resolved.messageIds;
|
|
1364
1487
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
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) {
|
|
1371
1507
|
return resolved.messageIds;
|
|
1372
1508
|
}
|
|
1373
1509
|
const ids = [];
|
|
1374
|
-
for (let i =
|
|
1510
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
1375
1511
|
const msg = messages[i];
|
|
1376
1512
|
if (msg) ids.push(msg.id);
|
|
1377
1513
|
}
|
|
@@ -1727,12 +1863,39 @@ DROP:
|
|
|
1727
1863
|
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
|
|
1728
1864
|
|
|
1729
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.`;
|
|
1730
|
-
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.
|
|
1731
1891
|
|
|
1732
|
-
${
|
|
1733
|
-
|
|
1892
|
+
${prompts.compressPhilosophy}`;
|
|
1893
|
+
}
|
|
1894
|
+
function emergencyHeader(prompts) {
|
|
1895
|
+
return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
|
|
1734
1896
|
|
|
1735
|
-
${
|
|
1897
|
+
${prompts.compressPhilosophy}`;
|
|
1898
|
+
}
|
|
1736
1899
|
function formatK(n) {
|
|
1737
1900
|
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
1738
1901
|
return `${n}`;
|
|
@@ -1840,7 +2003,7 @@ function formatRanges(compressible, protectedRanges) {
|
|
|
1840
2003
|
return `Compressible ranges (${merged.length}, oldest first):
|
|
1841
2004
|
${lines.join("\n")}`;
|
|
1842
2005
|
}
|
|
1843
|
-
function renderNudgeText(decision) {
|
|
2006
|
+
function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
1844
2007
|
const breakdownStr = formatBreakdown(decision.contextBreakdown);
|
|
1845
2008
|
const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
|
|
1846
2009
|
if (decision.tier !== null && decision.tier >= 2) {
|
|
@@ -1852,7 +2015,7 @@ function renderNudgeText(decision) {
|
|
|
1852
2015
|
return {
|
|
1853
2016
|
voice: "gentle",
|
|
1854
2017
|
text: [
|
|
1855
|
-
|
|
2018
|
+
efficiencyNote(prompts),
|
|
1856
2019
|
"",
|
|
1857
2020
|
breakdownStr,
|
|
1858
2021
|
"",
|
|
@@ -1861,9 +2024,9 @@ function renderNudgeText(decision) {
|
|
|
1861
2024
|
blockList,
|
|
1862
2025
|
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
1863
2026
|
"",
|
|
1864
|
-
|
|
2027
|
+
prompts.howToCompressRules,
|
|
1865
2028
|
"",
|
|
1866
|
-
isT2 ?
|
|
2029
|
+
isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
|
|
1867
2030
|
].join("\n")
|
|
1868
2031
|
};
|
|
1869
2032
|
}
|
|
@@ -1872,11 +2035,11 @@ function renderNudgeText(decision) {
|
|
|
1872
2035
|
return {
|
|
1873
2036
|
voice: "emergency",
|
|
1874
2037
|
text: [
|
|
1875
|
-
|
|
2038
|
+
emergencyHeader(prompts),
|
|
1876
2039
|
"",
|
|
1877
2040
|
breakdownStr,
|
|
1878
2041
|
"",
|
|
1879
|
-
|
|
2042
|
+
prompts.howToCompressRules,
|
|
1880
2043
|
"",
|
|
1881
2044
|
`{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
|
|
1882
2045
|
"Only use IDs from visible messages above. Compress older work first.",
|
|
@@ -1888,11 +2051,11 @@ function renderNudgeText(decision) {
|
|
|
1888
2051
|
return {
|
|
1889
2052
|
voice: "gentle",
|
|
1890
2053
|
text: [
|
|
1891
|
-
|
|
2054
|
+
efficiencyNote(prompts),
|
|
1892
2055
|
"",
|
|
1893
2056
|
breakdownStr,
|
|
1894
2057
|
"",
|
|
1895
|
-
|
|
2058
|
+
prompts.howToCompressRules,
|
|
1896
2059
|
"",
|
|
1897
2060
|
rangesStr,
|
|
1898
2061
|
"",
|
|
@@ -2980,6 +3143,99 @@ function mergeInitialState(parsed) {
|
|
|
2980
3143
|
};
|
|
2981
3144
|
}
|
|
2982
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
|
+
|
|
2983
3239
|
// src/runtime.ts
|
|
2984
3240
|
function readContextEntries(sm) {
|
|
2985
3241
|
const source = sm;
|
|
@@ -2993,15 +3249,17 @@ function isPiHost(sm) {
|
|
|
2993
3249
|
}
|
|
2994
3250
|
function mergeLiveEntries(entries, live, state, origins) {
|
|
2995
3251
|
const persisted = entries.filter((e) => e.type === "message");
|
|
2996
|
-
const
|
|
2997
|
-
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);
|
|
2998
3256
|
const out = [];
|
|
2999
3257
|
const nextOrigins = [];
|
|
3000
3258
|
const usedIds = /* @__PURE__ */ new Set();
|
|
3001
3259
|
for (let i = 0; i < live.length; i++) {
|
|
3002
3260
|
const msg = live[i];
|
|
3003
|
-
const entry =
|
|
3004
|
-
const origin =
|
|
3261
|
+
const entry = valueInRange(persisted, persistedRange, i);
|
|
3262
|
+
const origin = valueInRange(origins, originRange, i);
|
|
3005
3263
|
if (entry) {
|
|
3006
3264
|
if (origin) migrateLiveRefs(state, origin.rawId, entry.id);
|
|
3007
3265
|
else migrateTaggedRef(state, msg, entry.id);
|
|
@@ -3011,23 +3269,13 @@ function mergeLiveEntries(entries, live, state, origins) {
|
|
|
3011
3269
|
const id = origin?.rawId ?? nextLiveId(state, usedIds, i);
|
|
3012
3270
|
usedIds.add(id);
|
|
3013
3271
|
out.push({ type: "message", id, parentId: null, timestamp: String(msg.timestamp ?? Date.now()), message: msg });
|
|
3014
|
-
nextOrigins.push({ rawId: id, identity:
|
|
3272
|
+
nextOrigins.push({ rawId: id, identity: liveIdentities[i] });
|
|
3015
3273
|
}
|
|
3016
3274
|
origins.splice(0, origins.length, ...nextOrigins);
|
|
3017
|
-
const unmatched = live.length -
|
|
3275
|
+
const unmatched = live.length - (persistedRange?.length ?? 0);
|
|
3018
3276
|
if (unmatched > 0) logInfo("runtime", { event: "merge-live-entries", live: live.length, unmatched });
|
|
3019
3277
|
return out;
|
|
3020
3278
|
}
|
|
3021
|
-
function matchOrigins(origins, live) {
|
|
3022
|
-
for (let start = 0; start < origins.length; start++) {
|
|
3023
|
-
const count = origins.length - start;
|
|
3024
|
-
if (count > live.length) continue;
|
|
3025
|
-
if (origins.slice(start).every((origin, index) => origin.identity === messageIdentity(live[index]))) {
|
|
3026
|
-
return [...origins.slice(start), ...Array(live.length - count)];
|
|
3027
|
-
}
|
|
3028
|
-
}
|
|
3029
|
-
return Array(live.length);
|
|
3030
|
-
}
|
|
3031
3279
|
function nextLiveId(state, used, index) {
|
|
3032
3280
|
let id = `live-${index}`;
|
|
3033
3281
|
let suffix = index;
|
|
@@ -3054,31 +3302,39 @@ function migrateLiveRefs(state, liveId, stableId) {
|
|
|
3054
3302
|
delete state.messageRefs.byRaw[rawId];
|
|
3055
3303
|
}
|
|
3056
3304
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
const
|
|
3062
|
-
if (
|
|
3063
|
-
|
|
3064
|
-
|
|
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
|
+
});
|
|
3065
3320
|
}
|
|
3066
|
-
function
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
return a === b;
|
|
3078
|
-
}
|
|
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));
|
|
3079
3332
|
}
|
|
3080
3333
|
function sameNonTextBlocks(a, b) {
|
|
3081
|
-
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
|
+
});
|
|
3082
3338
|
try {
|
|
3083
3339
|
const na = Array.isArray(a) ? nonText(a) : [];
|
|
3084
3340
|
const nb = Array.isArray(b) ? nonText(b) : [];
|
|
@@ -3106,6 +3362,7 @@ function createRuntime(adapter) {
|
|
|
3106
3362
|
const store = new SessionStateStore();
|
|
3107
3363
|
const locks = /* @__PURE__ */ new Map();
|
|
3108
3364
|
let adapterRef = adapter;
|
|
3365
|
+
let promptsRef = defaultPrompts;
|
|
3109
3366
|
const nudgeShownTurns = /* @__PURE__ */ new Set();
|
|
3110
3367
|
async function acquireLock(sid) {
|
|
3111
3368
|
const prev = locks.get(sid) ?? Promise.resolve();
|
|
@@ -3154,6 +3411,10 @@ function createRuntime(adapter) {
|
|
|
3154
3411
|
return adapterRef;
|
|
3155
3412
|
}, setAdapter: (a) => {
|
|
3156
3413
|
adapterRef = a;
|
|
3414
|
+
}, get prompts() {
|
|
3415
|
+
return promptsRef;
|
|
3416
|
+
}, setPrompts: (p) => {
|
|
3417
|
+
promptsRef = p;
|
|
3157
3418
|
}, markNudgeShown: (k) => {
|
|
3158
3419
|
nudgeShownTurns.add(k);
|
|
3159
3420
|
}, nudgeShownFor: (k) => nudgeShownTurns.has(k), clearNudgeTracking: () => {
|
|
@@ -7768,6 +8029,18 @@ function findMessageContent(ref, ctx) {
|
|
|
7768
8029
|
}
|
|
7769
8030
|
return null;
|
|
7770
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
|
+
}
|
|
7771
8044
|
async function handleMessageRef(ref, ownerBlockId, args, ctx) {
|
|
7772
8045
|
const found = findMessageContent(ref, ctx);
|
|
7773
8046
|
if (!found || !found.text) {
|
|
@@ -7815,7 +8088,8 @@ async function handleDecompress(args, runtime, ctx) {
|
|
|
7815
8088
|
return `Block ${blockId} not found. Active blocks: ${active || "(none)"}.`;
|
|
7816
8089
|
}
|
|
7817
8090
|
const full = args.full ?? false;
|
|
7818
|
-
const
|
|
8091
|
+
const resolved = resolveBlockMessages(block, coreMessages, ctx);
|
|
8092
|
+
const { text, count } = collectBlockContent(state, block, resolved, { full });
|
|
7819
8093
|
if (count === 0) return `Block ${blockId} has no restorable message content.`;
|
|
7820
8094
|
if (args.inline === true && !args.toFile) {
|
|
7821
8095
|
debug.event("decompress", { blockId, full, count, mode: "inline" });
|
|
@@ -7977,10 +8251,10 @@ function formatSize(tokens) {
|
|
|
7977
8251
|
import {
|
|
7978
8252
|
spawn
|
|
7979
8253
|
} from "child_process";
|
|
7980
|
-
import { createWriteStream } from "fs";
|
|
8254
|
+
import { createWriteStream, existsSync as existsSync2 } from "fs";
|
|
7981
8255
|
import { mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, appendFile } from "fs/promises";
|
|
7982
8256
|
import { tmpdir as tmpdir2 } from "os";
|
|
7983
|
-
import { join as join4 } from "path";
|
|
8257
|
+
import { dirname as dirname3, join as join4, resolve as resolvePath } from "path";
|
|
7984
8258
|
|
|
7985
8259
|
// src/footer-status.ts
|
|
7986
8260
|
var FOOTER_STATUS_KEY = "billion-context-pi";
|
|
@@ -8393,6 +8667,44 @@ function delegateSpawnOptions(cwd, env) {
|
|
|
8393
8667
|
shell: false
|
|
8394
8668
|
};
|
|
8395
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
|
+
}
|
|
8396
8708
|
var ACP_TOOLS = ["compress", "decompress", "search_context", "acp_status"];
|
|
8397
8709
|
var RESTRICTED_TOOLS = "read,bash,grep,find,ls";
|
|
8398
8710
|
var AGENTS = {
|
|
@@ -8814,7 +9126,7 @@ async function runDelegate(pi, args, ctx, signal) {
|
|
|
8814
9126
|
logInfo("delegate", { event: "spawn", agent: args.agent, runId, cwd, async: isAsync, useJsonStream, mode: ctx.mode, parentDepth });
|
|
8815
9127
|
const child = spawn(
|
|
8816
9128
|
process.execPath,
|
|
8817
|
-
[process.argv[1], ...cliArgs],
|
|
9129
|
+
[resolvePiCliEntry(process.argv[1] ?? "", process.env, isPiHost(ctx.sessionManager)), ...cliArgs],
|
|
8818
9130
|
delegateSpawnOptions(cwd, childEnv)
|
|
8819
9131
|
);
|
|
8820
9132
|
child.stdin?.once("error", (e) => {
|
|
@@ -9346,7 +9658,7 @@ async function statusReport(runtime, ctx) {
|
|
|
9346
9658
|
const activeBlocksList = state.blocks.filter((b) => b.active);
|
|
9347
9659
|
const totalBlocksList = state.blocks;
|
|
9348
9660
|
const lines = [];
|
|
9349
|
-
const versionStr = "0.1.
|
|
9661
|
+
const versionStr = "0.1.37" ? `billion-context-pi@${"0.1.37"}` : "";
|
|
9350
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");
|
|
9351
9663
|
lines.push("\u2502 ACP Context Analysis \u2502");
|
|
9352
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");
|
|
@@ -9423,7 +9735,8 @@ async function statusReport(runtime, ctx) {
|
|
|
9423
9735
|
}
|
|
9424
9736
|
|
|
9425
9737
|
// src/system-prompt.ts
|
|
9426
|
-
|
|
9738
|
+
function buildAcpSystemPrompt(prompts) {
|
|
9739
|
+
return `
|
|
9427
9740
|
ACP context management
|
|
9428
9741
|
|
|
9429
9742
|
ACP TAGS
|
|
@@ -9447,7 +9760,7 @@ You have four context-management tools:
|
|
|
9447
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" }).
|
|
9448
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.
|
|
9449
9762
|
|
|
9450
|
-
${
|
|
9763
|
+
${prompts.compressPhilosophy}
|
|
9451
9764
|
|
|
9452
9765
|
WHEN TO COMPRESS
|
|
9453
9766
|
|
|
@@ -9465,7 +9778,7 @@ WHEN NOT TO COMPRESS
|
|
|
9465
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.
|
|
9466
9779
|
- Protected tool outputs \u2014 hard-excluded from compression ranges, survive intact in visible context.
|
|
9467
9780
|
|
|
9468
|
-
${
|
|
9781
|
+
${prompts.howToCompressRules}
|
|
9469
9782
|
|
|
9470
9783
|
MULTI-TIER COMPRESSION
|
|
9471
9784
|
|
|
@@ -9473,9 +9786,9 @@ Summaries accumulate as the session grows. When tier-1 summaries pile up, the sy
|
|
|
9473
9786
|
|
|
9474
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.
|
|
9475
9788
|
|
|
9476
|
-
${
|
|
9789
|
+
${prompts.tier2DistillRules}
|
|
9477
9790
|
|
|
9478
|
-
${
|
|
9791
|
+
${prompts.tier3CondenseRules}
|
|
9479
9792
|
|
|
9480
9793
|
THE PHILOSOPHY OF DECOMPRESS
|
|
9481
9794
|
|
|
@@ -9485,6 +9798,7 @@ CONTEXT BREAKDOWN
|
|
|
9485
9798
|
|
|
9486
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.
|
|
9487
9800
|
`;
|
|
9801
|
+
}
|
|
9488
9802
|
var ACP_DELEGATE_PROMPT = `
|
|
9489
9803
|
ACP_DELEGATE NOTIFICATIONS
|
|
9490
9804
|
|
|
@@ -9613,7 +9927,7 @@ function wireToolGuardrails(pi, runtime) {
|
|
|
9613
9927
|
|
|
9614
9928
|
// src/update.ts
|
|
9615
9929
|
import { readFile, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
9616
|
-
import { join as join5, dirname as
|
|
9930
|
+
import { join as join5, dirname as dirname4 } from "path";
|
|
9617
9931
|
import { fileURLToPath } from "url";
|
|
9618
9932
|
import { execFile } from "child_process";
|
|
9619
9933
|
import { homedir as homedir3 } from "os";
|
|
@@ -9646,7 +9960,7 @@ async function readLastCheck() {
|
|
|
9646
9960
|
}
|
|
9647
9961
|
async function writeLastCheck(timestamp) {
|
|
9648
9962
|
try {
|
|
9649
|
-
await mkdir3(
|
|
9963
|
+
await mkdir3(dirname4(THROTTLE_FILE), { recursive: true });
|
|
9650
9964
|
await writeFile3(THROTTLE_FILE, String(timestamp), "utf-8");
|
|
9651
9965
|
} catch {
|
|
9652
9966
|
}
|
|
@@ -9660,20 +9974,20 @@ async function readPackageJson(path4) {
|
|
|
9660
9974
|
}
|
|
9661
9975
|
}
|
|
9662
9976
|
function findNpmRoot(extDir) {
|
|
9663
|
-
let dir =
|
|
9977
|
+
let dir = dirname4(extDir);
|
|
9664
9978
|
for (; ; ) {
|
|
9665
|
-
if (dir.endsWith("node_modules")) return
|
|
9666
|
-
const parent =
|
|
9979
|
+
if (dir.endsWith("node_modules")) return dirname4(dir);
|
|
9980
|
+
const parent = dirname4(dir);
|
|
9667
9981
|
if (parent === dir) return void 0;
|
|
9668
9982
|
dir = parent;
|
|
9669
9983
|
}
|
|
9670
9984
|
}
|
|
9671
9985
|
async function findExtensionDir() {
|
|
9672
|
-
let dir =
|
|
9986
|
+
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
9673
9987
|
for (; ; ) {
|
|
9674
9988
|
const pkg = await readPackageJson(join5(dir, "package.json"));
|
|
9675
9989
|
if (pkg?.name === PACKAGE_NAME) return dir;
|
|
9676
|
-
const parent =
|
|
9990
|
+
const parent = dirname4(dir);
|
|
9677
9991
|
if (parent === dir) return void 0;
|
|
9678
9992
|
dir = parent;
|
|
9679
9993
|
}
|
|
@@ -9722,7 +10036,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
9722
10036
|
const data = await res.json();
|
|
9723
10037
|
const latest = data.version;
|
|
9724
10038
|
if (!latest) return;
|
|
9725
|
-
const current = runtimeVersion ?? "0.1.
|
|
10039
|
+
const current = runtimeVersion ?? "0.1.37";
|
|
9726
10040
|
const hasUpdate = isNewer(latest, current);
|
|
9727
10041
|
debug.event("update-check", {
|
|
9728
10042
|
current,
|
|
@@ -9758,7 +10072,7 @@ async function getRuntimeVersion() {
|
|
|
9758
10072
|
|
|
9759
10073
|
// src/setup-subagent-tools.ts
|
|
9760
10074
|
import { readFile as readFile2, writeFile as writeFile4, stat, copyFile, rename } from "fs/promises";
|
|
9761
|
-
import { existsSync as
|
|
10075
|
+
import { existsSync as existsSync3 } from "fs";
|
|
9762
10076
|
import { homedir as homedir4 } from "os";
|
|
9763
10077
|
import { join as join6 } from "path";
|
|
9764
10078
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@earendil-works/pi-coding-agent";
|
|
@@ -9823,7 +10137,7 @@ async function ensureSubagentAcpTools(settingsPath) {
|
|
|
9823
10137
|
return { path: path4, action: "skipped", reason: "all builtin agents already have ACP tools" };
|
|
9824
10138
|
}
|
|
9825
10139
|
const backupPath = `${path4}.acp-bak`;
|
|
9826
|
-
if (!
|
|
10140
|
+
if (!existsSync3(backupPath)) {
|
|
9827
10141
|
try {
|
|
9828
10142
|
await copyFile(path4, backupPath);
|
|
9829
10143
|
} catch {
|
|
@@ -9923,7 +10237,9 @@ var KNOWN = /* @__PURE__ */ new Set([
|
|
|
9923
10237
|
"toolOutputMaxBytes",
|
|
9924
10238
|
"delegate",
|
|
9925
10239
|
"compress",
|
|
9926
|
-
"displayUsage"
|
|
10240
|
+
"displayUsage",
|
|
10241
|
+
"prompts",
|
|
10242
|
+
"acknowledgePromptsRisk"
|
|
9927
10243
|
]);
|
|
9928
10244
|
function pickKnown(parsed) {
|
|
9929
10245
|
const out = {};
|
|
@@ -9971,7 +10287,7 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
9971
10287
|
resetDelegateUsage();
|
|
9972
10288
|
setDelegateDisplayUsage("separate");
|
|
9973
10289
|
const sid = ctx.sessionManager.getSessionId();
|
|
9974
|
-
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 });
|
|
9975
10291
|
try {
|
|
9976
10292
|
const user = await loadUserConfig(ctx.cwd);
|
|
9977
10293
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -9980,6 +10296,12 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
9980
10296
|
} catch (e) {
|
|
9981
10297
|
logThrow("config", e, { sid, phase: "session_start" });
|
|
9982
10298
|
}
|
|
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
|
+
}
|
|
9983
10305
|
if (resolveDelegate(runtime.adapter).enabled) {
|
|
9984
10306
|
pi.registerTool(makeDelegateTool(pi));
|
|
9985
10307
|
pi.registerTool(makeDelegateWaitTool(pi));
|
|
@@ -10040,7 +10362,7 @@ function wireContextTransform(pi, runtime) {
|
|
|
10040
10362
|
prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
10041
10363
|
nudgeShouldInject: turn.nudge?.shouldInject ?? false,
|
|
10042
10364
|
nudgeReason: turn.nudge?.reason ?? null,
|
|
10043
|
-
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge).voice : null,
|
|
10365
|
+
nudgeVoice: turn.nudge ? renderNudgeText(turn.nudge, runtime.prompts).voice : null,
|
|
10044
10366
|
nudgePct: turn.nudge ? Math.round(turn.nudge.contextUsage * 100) : null,
|
|
10045
10367
|
nudgeTier: turn.nudge?.tier ?? null,
|
|
10046
10368
|
nudgeCompressibleCount: turn.nudge?.compressibleRanges.length ?? 0,
|
|
@@ -10057,8 +10379,8 @@ function wireContextTransform(pi, runtime) {
|
|
|
10057
10379
|
const turnKey = lastUserMessageId(entries) ?? sid;
|
|
10058
10380
|
const alreadyShown = !emergency && runtime.nudgeShownFor(turnKey);
|
|
10059
10381
|
if (!alreadyShown) {
|
|
10060
|
-
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active)));
|
|
10061
|
-
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);
|
|
10062
10384
|
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
10063
10385
|
const example = top ? `
|
|
10064
10386
|
|
|
@@ -10092,8 +10414,9 @@ ${rendered.text}${example}`);
|
|
|
10092
10414
|
function wireSystemPrompt(pi, runtime) {
|
|
10093
10415
|
pi.on("before_agent_start", (event) => {
|
|
10094
10416
|
const delegate = runtime.adapter.delegate !== false;
|
|
10095
|
-
const
|
|
10096
|
-
|
|
10417
|
+
const acp = buildAcpSystemPrompt(runtime.prompts);
|
|
10418
|
+
const prompt = delegate ? `${acp}
|
|
10419
|
+
${ACP_DELEGATE_PROMPT}` : acp;
|
|
10097
10420
|
return { systemPrompt: formatSystemPromptForEvent(event.systemPrompt, prompt) };
|
|
10098
10421
|
});
|
|
10099
10422
|
}
|
|
@@ -10109,8 +10432,8 @@ function collectOriginals(entries) {
|
|
|
10109
10432
|
}
|
|
10110
10433
|
return map;
|
|
10111
10434
|
}
|
|
10112
|
-
function nudgeMessage(nudge, blocks) {
|
|
10113
|
-
const rendered = renderNudgeText(nudge);
|
|
10435
|
+
function nudgeMessage(nudge, blocks, prompts) {
|
|
10436
|
+
const rendered = renderNudgeText(nudge, prompts);
|
|
10114
10437
|
const lines = [rendered.text];
|
|
10115
10438
|
if (blocks.length > 0) {
|
|
10116
10439
|
const totalSummary = blocks.reduce((s, b) => s + Math.ceil((b.summary || "").length / 4), 0);
|