billion-context-pi 0.1.37 → 0.1.38
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 +2 -51
- package/README.zh-CN.md +2 -51
- package/dist/index.js +857 -336
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -293,7 +293,8 @@ function defaultConfig(modelContextLimit, overrides = {}) {
|
|
|
293
293
|
growthCap: 5e4,
|
|
294
294
|
minGrowthFloor: 2e4,
|
|
295
295
|
minGrowthRatio: 0.45,
|
|
296
|
-
emergencyThresholdPct: 0.95
|
|
296
|
+
emergencyThresholdPct: 0.95,
|
|
297
|
+
tier2GrowthMultiplier: 1.5
|
|
297
298
|
},
|
|
298
299
|
promotionThreshold: 5,
|
|
299
300
|
truncate: { threshold: 0.95 },
|
|
@@ -1018,6 +1019,44 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
1018
1019
|
protected: protectedRanges
|
|
1019
1020
|
};
|
|
1020
1021
|
}
|
|
1022
|
+
function mergeBatch(batch) {
|
|
1023
|
+
const first = batch[0];
|
|
1024
|
+
const last = batch[batch.length - 1];
|
|
1025
|
+
const count = batch.reduce((s, r) => s + r.count, 0);
|
|
1026
|
+
const tokens = batch.reduce((s, r) => s + r.tokens, 0);
|
|
1027
|
+
const toolPct = Math.round(
|
|
1028
|
+
batch.reduce((s, r) => s + r.toolPct * r.count, 0) / count
|
|
1029
|
+
);
|
|
1030
|
+
const merged = {
|
|
1031
|
+
startRef: first.startRef,
|
|
1032
|
+
endRef: last.endRef,
|
|
1033
|
+
count,
|
|
1034
|
+
tokens,
|
|
1035
|
+
toolPct,
|
|
1036
|
+
textPct: 100 - toolPct
|
|
1037
|
+
};
|
|
1038
|
+
if (batch.some((r) => r.dangerous === true)) {
|
|
1039
|
+
merged.dangerous = true;
|
|
1040
|
+
}
|
|
1041
|
+
return merged;
|
|
1042
|
+
}
|
|
1043
|
+
function mergeRangesToThreshold(ranges, minChars) {
|
|
1044
|
+
if (minChars <= 0 || ranges.length === 0) return ranges;
|
|
1045
|
+
const result = [];
|
|
1046
|
+
let batch = [];
|
|
1047
|
+
for (const r of ranges) {
|
|
1048
|
+
batch.push(r);
|
|
1049
|
+
const batchTokens = batch.reduce((s, x) => s + x.tokens, 0);
|
|
1050
|
+
if (batchTokens * 4 >= minChars) {
|
|
1051
|
+
result.push(mergeBatch(batch));
|
|
1052
|
+
batch = [];
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (batch.length > 0) {
|
|
1056
|
+
result.push(mergeBatch(batch));
|
|
1057
|
+
}
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1021
1060
|
function runPipeline(nodes, initial, ctx) {
|
|
1022
1061
|
let io = initial;
|
|
1023
1062
|
for (const node of nodes) {
|
|
@@ -1297,7 +1336,10 @@ var recommendNode = {
|
|
|
1297
1336
|
const nothingToCompress = contextRanges.compressible.length === 0;
|
|
1298
1337
|
const recommendation = {
|
|
1299
1338
|
contextRanges,
|
|
1300
|
-
recommendedRanges:
|
|
1339
|
+
recommendedRanges: mergeRangesToThreshold(
|
|
1340
|
+
contextRanges.compressible,
|
|
1341
|
+
ctx.config.compress.minCompressRange
|
|
1342
|
+
),
|
|
1301
1343
|
nothingToCompress
|
|
1302
1344
|
};
|
|
1303
1345
|
return { ...io, effects: { ...io.effects, recommendation } };
|
|
@@ -1323,6 +1365,7 @@ var nudgeNode = {
|
|
|
1323
1365
|
if (baseline > 0 && ctx.tokenCount < baseline - nudgeGrowthTokens) {
|
|
1324
1366
|
stamped.lastPerMessageNudgeTokens = ctx.tokenCount;
|
|
1325
1367
|
stamped.lastNudgeShownTokens = 0;
|
|
1368
|
+
stamped.lastShownByTier = {};
|
|
1326
1369
|
}
|
|
1327
1370
|
if (stamped.lastPerMessageNudgeTokens === 0) {
|
|
1328
1371
|
stamped.lastPerMessageNudgeTokens = ctx.tokenCount;
|
|
@@ -1591,10 +1634,11 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
|
|
|
1591
1634
|
)
|
|
1592
1635
|
);
|
|
1593
1636
|
}
|
|
1594
|
-
function pendingByTier(state, recommendation, countTokens) {
|
|
1637
|
+
function pendingByTier(state, recommendation, countTokens, minCompressRange) {
|
|
1595
1638
|
const out = {};
|
|
1596
|
-
const
|
|
1597
|
-
|
|
1639
|
+
const merged = recommendation?.recommendedRanges ?? [];
|
|
1640
|
+
const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
|
|
1641
|
+
out[1] = { pending: effective.reduce((s, r) => s + r.tokens, 0), targetBlocks: [] };
|
|
1598
1642
|
const active = activeBlocks(state);
|
|
1599
1643
|
const t1 = active.filter((b) => b.tier === 1);
|
|
1600
1644
|
const t2 = active.filter((b) => b.tier === 2);
|
|
@@ -1609,6 +1653,7 @@ function decideNudge(input) {
|
|
|
1609
1653
|
const nudgeGrowthTokens = resolveAdaptiveGrowth(limit, config.nudge);
|
|
1610
1654
|
const overLimit = usage >= config.nudge.maxContextLimitPct;
|
|
1611
1655
|
const emergencyOverride = usage >= config.nudge.emergencyThresholdPct;
|
|
1656
|
+
const pressure = overLimit || emergencyOverride;
|
|
1612
1657
|
const baseline = state.nudge.lastPerMessageNudgeTokens;
|
|
1613
1658
|
const hadPendingNudge = state.nudge.lastNudgeShownTokens > 0;
|
|
1614
1659
|
const hasPendingNudge = hadPendingNudge;
|
|
@@ -1620,44 +1665,67 @@ function decideNudge(input) {
|
|
|
1620
1665
|
);
|
|
1621
1666
|
const growthSinceReference = tokenCount - growthReference;
|
|
1622
1667
|
const rec = recommendation;
|
|
1623
|
-
const tiers = pendingByTier(
|
|
1668
|
+
const tiers = pendingByTier(
|
|
1669
|
+
state,
|
|
1670
|
+
rec,
|
|
1671
|
+
countTokens,
|
|
1672
|
+
config.compress.minCompressRange
|
|
1673
|
+
);
|
|
1674
|
+
const tier2Threshold = Math.round(
|
|
1675
|
+
nudgeGrowthTokens * (config.nudge.tier2GrowthMultiplier ?? 1.5)
|
|
1676
|
+
);
|
|
1624
1677
|
let injectedTier = null;
|
|
1625
1678
|
let injectedReason = "";
|
|
1626
1679
|
const growthReady = growthSinceReference >= growthFloor;
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1680
|
+
const t1Eff = tiers[1]?.pending ?? 0;
|
|
1681
|
+
const t2Pen = tiers[2]?.pending ?? 0;
|
|
1682
|
+
const t3Pen = tiers[3]?.pending ?? 0;
|
|
1683
|
+
if (pressure) {
|
|
1684
|
+
const candidates = [1];
|
|
1685
|
+
if (config.tiers.enabled) {
|
|
1686
|
+
candidates.push(2, 3);
|
|
1687
|
+
}
|
|
1688
|
+
let best = null;
|
|
1689
|
+
let bestPending = 0;
|
|
1690
|
+
for (const t of candidates) {
|
|
1691
|
+
const p = tiers[t]?.pending ?? 0;
|
|
1692
|
+
if (p > bestPending) {
|
|
1693
|
+
bestPending = p;
|
|
1694
|
+
best = t;
|
|
1695
|
+
}
|
|
1638
1696
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1697
|
+
if (best !== null && bestPending > 0) {
|
|
1698
|
+
injectedTier = best;
|
|
1699
|
+
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
1700
|
+
injectedReason = best === 1 ? `${label} T1: max effective pending ${bestPending}, usage ${Math.round(usage * 100)}%` : `${label} T${best} distill: max pending ${bestPending} (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}), usage ${Math.round(usage * 100)}%`;
|
|
1701
|
+
}
|
|
1702
|
+
} else if (growthReady) {
|
|
1703
|
+
if (t1Eff >= nudgeGrowthTokens) {
|
|
1704
|
+
injectedTier = 1;
|
|
1705
|
+
injectedReason = `T1 effective ${t1Eff} >= ${nudgeGrowthTokens}, growth ${growthSinceReference}, usage ${Math.round(usage * 100)}%`;
|
|
1706
|
+
} else if (config.tiers.enabled && t2Pen >= tier2Threshold && t2Pen > t1Eff) {
|
|
1707
|
+
const lastShown = state.nudge.lastShownByTier[2] ?? 0;
|
|
1708
|
+
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
1709
|
+
if (cadenceMet) {
|
|
1710
|
+
injectedTier = 2;
|
|
1711
|
+
injectedReason = `T2 distill ready: ${tiers[2].targetBlocks.length} tier-1 blocks (${t2Pen} tokens) >= ${tier2Threshold} (1.5x) and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
1712
|
+
}
|
|
1713
|
+
} else if (config.tiers.enabled && t3Pen >= tier2Threshold && t3Pen > t2Pen && t3Pen > t1Eff) {
|
|
1714
|
+
const lastShown = state.nudge.lastShownByTier[3] ?? 0;
|
|
1715
|
+
const cadenceMet = lastShown === 0 || tokenCount - lastShown >= growthFloor;
|
|
1716
|
+
if (cadenceMet) {
|
|
1717
|
+
injectedTier = 3;
|
|
1718
|
+
injectedReason = `T3 condense ready: ${tiers[3].targetBlocks.length} tier-2 blocks (${t3Pen} tokens) >= ${tier2Threshold} (1.5x) and > T2 ${t2Pen} and > T1 effective ${t1Eff}, usage ${Math.round(usage * 100)}%`;
|
|
1719
|
+
}
|
|
1647
1720
|
}
|
|
1648
1721
|
}
|
|
1649
|
-
const shouldInject = injectedTier !== null
|
|
1722
|
+
const shouldInject = injectedTier !== null;
|
|
1650
1723
|
let reason;
|
|
1651
|
-
if (
|
|
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)`;
|
|
1659
|
-
} else if (injectedTier !== null) {
|
|
1724
|
+
if (injectedTier !== null) {
|
|
1660
1725
|
reason = injectedReason;
|
|
1726
|
+
} else if (pressure) {
|
|
1727
|
+
const label = emergencyOverride ? "EMERGENCY" : "OVER-LIMIT";
|
|
1728
|
+
reason = `${label}: usage ${Math.round(usage * 100)}% but no tier has effective compressible content (T1 effective ${t1Eff}, T2 ${t2Pen}, T3 ${t3Pen}) \u2014 nudge suppressed to avoid offering ranges below minCompressRange`;
|
|
1661
1729
|
} else {
|
|
1662
1730
|
const tiersList = [1, 2, 3];
|
|
1663
1731
|
const eligible = tiersList.filter((t) => config.tiers.enabled || t === 1);
|
|
@@ -2006,20 +2074,23 @@ ${lines.join("\n")}`;
|
|
|
2006
2074
|
function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
2007
2075
|
const breakdownStr = formatBreakdown(decision.contextBreakdown);
|
|
2008
2076
|
const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
|
|
2077
|
+
const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
|
|
2009
2078
|
if (decision.tier !== null && decision.tier >= 2) {
|
|
2010
2079
|
const isT2 = decision.tier === 2;
|
|
2011
2080
|
const targets = decision.tierTargetBlocks ?? [];
|
|
2012
2081
|
const blockList = formatTierTargetBlocks(targets);
|
|
2013
2082
|
const startId = targets[0]?.blockId ?? "b1";
|
|
2014
2083
|
const endId = targets[targets.length - 1]?.blockId ?? "b5";
|
|
2084
|
+
const voice = isEmergency ? "emergency" : "gentle";
|
|
2085
|
+
const triggerLine = isEmergency ? `[EMERGENCY \u2014 TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"}] Context limit reached \u2014 distill NOW into a denser summary to reclaim tokens.` : `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`;
|
|
2015
2086
|
return {
|
|
2016
|
-
voice
|
|
2087
|
+
voice,
|
|
2017
2088
|
text: [
|
|
2018
2089
|
efficiencyNote(prompts),
|
|
2019
2090
|
"",
|
|
2020
2091
|
breakdownStr,
|
|
2021
2092
|
"",
|
|
2022
|
-
|
|
2093
|
+
triggerLine,
|
|
2023
2094
|
isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
|
|
2024
2095
|
blockList,
|
|
2025
2096
|
`Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
|
|
@@ -2030,7 +2101,6 @@ function renderNudgeText(decision, prompts = defaultPrompts) {
|
|
|
2030
2101
|
].join("\n")
|
|
2031
2102
|
};
|
|
2032
2103
|
}
|
|
2033
|
-
const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
|
|
2034
2104
|
if (isEmergency) {
|
|
2035
2105
|
return {
|
|
2036
2106
|
voice: "emergency",
|
|
@@ -2394,9 +2464,9 @@ function charBigrams(text) {
|
|
|
2394
2464
|
}
|
|
2395
2465
|
return grams;
|
|
2396
2466
|
}
|
|
2397
|
-
function tfMap(text,
|
|
2467
|
+
function tfMap(text, stem22) {
|
|
2398
2468
|
const m = /* @__PURE__ */ new Map();
|
|
2399
|
-
for (const t of tokenize(text, { stem:
|
|
2469
|
+
for (const t of tokenize(text, { stem: stem22 })) m.set(t, (m.get(t) ?? 0) + 1);
|
|
2400
2470
|
return m;
|
|
2401
2471
|
}
|
|
2402
2472
|
var bm25Algorithm = {
|
|
@@ -7842,6 +7912,23 @@ function lastUserMessageId(entries) {
|
|
|
7842
7912
|
return void 0;
|
|
7843
7913
|
}
|
|
7844
7914
|
|
|
7915
|
+
// src/compat.ts
|
|
7916
|
+
function normalizeSystemPrompt(input) {
|
|
7917
|
+
if (input === void 0) return "";
|
|
7918
|
+
if (Array.isArray(input)) return input.join("\n");
|
|
7919
|
+
return input;
|
|
7920
|
+
}
|
|
7921
|
+
function formatSystemPromptForEvent(base, append) {
|
|
7922
|
+
const normalized = normalizeSystemPrompt(base);
|
|
7923
|
+
return `${normalized}
|
|
7924
|
+
|
|
7925
|
+
${append}`;
|
|
7926
|
+
}
|
|
7927
|
+
function getSystemPromptText(ctx) {
|
|
7928
|
+
const result = ctx.getSystemPrompt?.();
|
|
7929
|
+
return normalizeSystemPrompt(result);
|
|
7930
|
+
}
|
|
7931
|
+
|
|
7845
7932
|
// src/compress-tool.ts
|
|
7846
7933
|
function formatK2(n) {
|
|
7847
7934
|
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
@@ -7887,13 +7974,14 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
7887
7974
|
if (ranges.length === 0) return "No ranges provided.";
|
|
7888
7975
|
const { state: initialState, coreMessages } = await runtime.stateFor(ctx);
|
|
7889
7976
|
const config = runtime.configFor(ctx);
|
|
7890
|
-
const
|
|
7891
|
-
const
|
|
7977
|
+
const systemPromptText = getSystemPromptText(ctx);
|
|
7978
|
+
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
7979
|
+
const sentTokens = estimateTokens(coreMessages, collectCoveredMessageIds(initialState)) + systemPromptTokens;
|
|
7892
7980
|
const turn = runtime.core.processTurn({
|
|
7893
7981
|
messages: coreMessages,
|
|
7894
7982
|
state: initialState,
|
|
7895
7983
|
config,
|
|
7896
|
-
tokenCount:
|
|
7984
|
+
tokenCount: sentTokens
|
|
7897
7985
|
});
|
|
7898
7986
|
const state = turn.state;
|
|
7899
7987
|
const messages = turn.messages;
|
|
@@ -8247,195 +8335,714 @@ function formatSize(tokens) {
|
|
|
8247
8335
|
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
8248
8336
|
}
|
|
8249
8337
|
|
|
8250
|
-
//
|
|
8251
|
-
import {
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
import { mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, appendFile } from "fs/promises";
|
|
8256
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
8257
|
-
import { dirname as dirname3, join as join4, resolve as resolvePath } from "path";
|
|
8258
|
-
|
|
8259
|
-
// src/footer-status.ts
|
|
8260
|
-
var FOOTER_STATUS_KEY = "billion-context-pi";
|
|
8261
|
-
var ui;
|
|
8262
|
-
var lastFooterText = "";
|
|
8263
|
-
function formatCompactTokens(count) {
|
|
8264
|
-
if (count < 1e3) return count.toString();
|
|
8265
|
-
if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
|
|
8266
|
-
if (count < 1e6) return `${Math.round(count / 1e3)}k`;
|
|
8267
|
-
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
8268
|
-
return `${Math.round(count / 1e6)}M`;
|
|
8338
|
+
// node_modules/billion-context-kit/node_modules/acp-kernel/dist/index.js
|
|
8339
|
+
import { createRequire as createRequire2 } from "module";
|
|
8340
|
+
var BLOCKED_REF2 = "BLOCKED";
|
|
8341
|
+
function refForRaw2(map, rawId) {
|
|
8342
|
+
return map.byRaw[rawId] ?? null;
|
|
8269
8343
|
}
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8344
|
+
var require22 = createRequire2(import.meta.url);
|
|
8345
|
+
function defaultCountTokens2(text) {
|
|
8346
|
+
if (!text) return 0;
|
|
8347
|
+
const cjk = text.match(/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/g);
|
|
8348
|
+
const cjkCount = cjk?.length ?? 0;
|
|
8349
|
+
return cjkCount + Math.ceil((text.length - cjkCount) / 4);
|
|
8273
8350
|
}
|
|
8274
|
-
function
|
|
8275
|
-
if (
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
if (usage && usage.totalTokens > 0) {
|
|
8279
|
-
const costStr = usage.cost.total > 0 ? ` ($${usage.cost.total.toFixed(4)})` : "";
|
|
8280
|
-
text = `sub-agents \u2191${formatCompactTokens(usage.input)} \u2193${formatCompactTokens(usage.output)}${costStr}`;
|
|
8281
|
-
}
|
|
8282
|
-
if ((text ?? "") === lastFooterText) return;
|
|
8283
|
-
lastFooterText = text ?? "";
|
|
8284
|
-
try {
|
|
8285
|
-
ui.setStatus(FOOTER_STATUS_KEY, text);
|
|
8286
|
-
} catch {
|
|
8287
|
-
}
|
|
8351
|
+
function formatTokens3(tokens) {
|
|
8352
|
+
if (tokens < 1e3) return String(tokens);
|
|
8353
|
+
if (tokens < 1e4) return (tokens / 1e3).toFixed(1) + "K";
|
|
8354
|
+
return Math.round(tokens / 1e3) + "K";
|
|
8288
8355
|
}
|
|
8289
|
-
function
|
|
8290
|
-
if (
|
|
8291
|
-
|
|
8292
|
-
ui.setStatus(FOOTER_STATUS_KEY, void 0);
|
|
8293
|
-
} catch {
|
|
8294
|
-
}
|
|
8356
|
+
function classifyType2(message) {
|
|
8357
|
+
if (message.contentType === "tool-call" || message.contentType === "tool-result") {
|
|
8358
|
+
return message.toolName || "tool";
|
|
8295
8359
|
}
|
|
8296
|
-
|
|
8297
|
-
lastFooterText = "";
|
|
8298
|
-
}
|
|
8299
|
-
|
|
8300
|
-
// src/fleet-widget.ts
|
|
8301
|
-
var DELEGATE_WIDGET_KEY = "billion-context-pi-delegates";
|
|
8302
|
-
var REFRESH_MS = 500;
|
|
8303
|
-
var MAX_TASK_LEN = 48;
|
|
8304
|
-
var ui2;
|
|
8305
|
-
var timer;
|
|
8306
|
-
var lastRenderKey = "";
|
|
8307
|
-
var runsSnapshot;
|
|
8308
|
-
function truncateTask(task) {
|
|
8309
|
-
const oneLine = task.replace(/\n/g, " ").trim();
|
|
8310
|
-
if (oneLine.length <= MAX_TASK_LEN) return oneLine;
|
|
8311
|
-
return `${oneLine.slice(0, MAX_TASK_LEN - 1)}\u2026`;
|
|
8360
|
+
return message.contentType;
|
|
8312
8361
|
}
|
|
8313
|
-
function
|
|
8314
|
-
|
|
8315
|
-
const now = Date.now();
|
|
8316
|
-
const header = runs2.length === 1 ? `acp_delegate \xB7 1 running` : `acp_delegate \xB7 ${runs2.length} running`;
|
|
8317
|
-
const rows = runs2.map((r) => {
|
|
8318
|
-
const elapsed = Math.max(0, Math.round((now - r.startedAt) / 1e3));
|
|
8319
|
-
return ` \u25CF ${r.agent} (${elapsed}s) \u2014 ${truncateTask(r.task)}`;
|
|
8320
|
-
});
|
|
8321
|
-
return [header, ...rows];
|
|
8362
|
+
function escapeRegex2(s) {
|
|
8363
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8322
8364
|
}
|
|
8323
|
-
|
|
8324
|
-
|
|
8365
|
+
var LT2 = "<";
|
|
8366
|
+
var GT2 = ">";
|
|
8367
|
+
var TAG_OPEN2 = LT2 + "acp ";
|
|
8368
|
+
var TAG_CLOSE2 = LT2 + "/acp" + GT2;
|
|
8369
|
+
function acpTag2(ref, tokens, type) {
|
|
8370
|
+
return TAG_OPEN2 + 'tokens="' + formatTokens3(tokens) + '" type="' + type + '"' + GT2 + ref + TAG_CLOSE2;
|
|
8325
8371
|
}
|
|
8326
|
-
function
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8372
|
+
function renderMessage2(message, map, countTokens, strategy) {
|
|
8373
|
+
const ref = refForRaw2(map, message.id);
|
|
8374
|
+
if (!ref || ref === BLOCKED_REF2) return message;
|
|
8375
|
+
if (strategy === "none") return message;
|
|
8376
|
+
if (strategy === "text-only" && message.contentType !== "text") {
|
|
8377
|
+
return message;
|
|
8330
8378
|
}
|
|
8379
|
+
const ownTagRe = new RegExp(
|
|
8380
|
+
"^" + escapeRegex2(TAG_OPEN2) + "[^>]*" + GT2 + escapeRegex2(ref) + escapeRegex2(TAG_CLOSE2) + "\\n?"
|
|
8381
|
+
);
|
|
8382
|
+
const cleanText = (message.text || "").replace(ownTagRe, "");
|
|
8383
|
+
const tokens = countTokens(cleanText);
|
|
8384
|
+
const type = classifyType2(message);
|
|
8385
|
+
const prefix = acpTag2(ref, tokens, type) + "\n";
|
|
8386
|
+
if (!cleanText) return { ...message, text: prefix };
|
|
8387
|
+
return { ...message, text: prefix + cleanText };
|
|
8331
8388
|
}
|
|
8332
|
-
function
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
}
|
|
8389
|
+
function renderVisibleRefs2(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
|
|
8390
|
+
const map = state.messageRefs;
|
|
8391
|
+
return messages.map(
|
|
8392
|
+
(message) => renderMessage2(message, map, countTokens, strategy)
|
|
8393
|
+
);
|
|
8338
8394
|
}
|
|
8339
|
-
function
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8395
|
+
function createRenderRefsNode2(strategy) {
|
|
8396
|
+
return {
|
|
8397
|
+
name: "render-refs",
|
|
8398
|
+
run(io, ctx) {
|
|
8399
|
+
return {
|
|
8400
|
+
...io,
|
|
8401
|
+
messages: renderVisibleRefs2(io.messages, io.state, ctx.countTokens, strategy)
|
|
8402
|
+
};
|
|
8346
8403
|
}
|
|
8347
|
-
|
|
8348
|
-
stopTimer();
|
|
8349
|
-
return;
|
|
8350
|
-
}
|
|
8351
|
-
const sorted = [...runs2].sort((a, b) => a.startedAt - b.startedAt);
|
|
8352
|
-
const renderKey = renderKeyFor(sorted);
|
|
8353
|
-
if (renderKey === lastRenderKey) return;
|
|
8354
|
-
lastRenderKey = renderKey;
|
|
8355
|
-
const lines = renderLines(sorted);
|
|
8356
|
-
try {
|
|
8357
|
-
ui2.setWidget(DELEGATE_WIDGET_KEY, lines, { placement: "belowEditor" });
|
|
8358
|
-
} catch {
|
|
8359
|
-
ui2 = void 0;
|
|
8360
|
-
stopTimer();
|
|
8361
|
-
}
|
|
8362
|
-
updateFooterStatus();
|
|
8404
|
+
};
|
|
8363
8405
|
}
|
|
8364
|
-
var
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
timer = setInterval(refresh, REFRESH_MS);
|
|
8372
|
-
timer.unref?.();
|
|
8373
|
-
}
|
|
8374
|
-
refresh();
|
|
8375
|
-
},
|
|
8376
|
-
dispose() {
|
|
8377
|
-
stopTimer();
|
|
8378
|
-
clearWidget();
|
|
8379
|
-
disposeFooterStatus();
|
|
8380
|
-
ui2 = void 0;
|
|
8381
|
-
lastRenderKey = "";
|
|
8382
|
-
},
|
|
8383
|
-
poke() {
|
|
8384
|
-
if (ui2 && !timer) {
|
|
8385
|
-
timer = setInterval(refresh, REFRESH_MS);
|
|
8386
|
-
timer.unref?.();
|
|
8387
|
-
}
|
|
8388
|
-
refresh();
|
|
8389
|
-
}
|
|
8390
|
-
};
|
|
8406
|
+
var renderRefsNode2 = createRenderRefsNode2("all");
|
|
8407
|
+
var COMPRESS_PHILOSOPHY2 = `Compression Philosophy:
|
|
8408
|
+
- All compression serves the primary task, but be frugal.
|
|
8409
|
+
- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.
|
|
8410
|
+
- Compress by need, not by percentage.
|
|
8411
|
+
- Work from summaries, not raw tool outputs. All listed ranges (user prompts, tool outputs, code, logs, exploration, intermediate steps) should be compressed to summary format \u2014 the ONLY exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;
|
|
8412
|
+
var HOW_TO_COMPRESS_RULES2 = `HOW TO COMPRESS
|
|
8391
8413
|
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8414
|
+
When you call \`compress\`, the summary you write becomes the only record of the replaced conversation. Make it self-contained and complete: every user request, experiment purpose, and work task in the range must be accurately captured. A later reader (or you, after decompressing) should be able to continue the task WITHOUT needing the original.
|
|
8415
|
+
|
|
8416
|
+
KEEP VERBATIM \u2014 never paraphrase or abbreviate these:
|
|
8417
|
+
- Full file paths with line numbers, directory prefix on every mention (\`lib/hooks.ts:347\`, \`src/index.ts:12-18\`, \`gatenet_v3/model.py:45\`). Never abbreviate to a bare filename (\`hooks.ts\`, \`model.py\`) \u2014 they are ambiguous and cannot be grepped or decompressed-to later.
|
|
8418
|
+
- Function, class, and type signatures (exact names, params, return types) AND critical code lines that encode logic \u2014 the line that IS the finding, not just the function name (e.g. \`kv_keys += define_gate * a_key[i](emb)\` is more useful than "see model_kvnet.py").
|
|
8419
|
+
- Error messages and stack traces (exact text \u2014 you need the literal string to grep for it later).
|
|
8420
|
+
- Key details from reports and analyses \u2014 not just the conclusion. Keep the comparison numbers and the mechanism, not "X is worse" alone (write "1.76\xD7 PPL gap because KV store is static", not "KVNet underperforms").
|
|
8421
|
+
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing; without it the decision looks arbitrary).
|
|
8422
|
+
- Constraints discovered ("must support Node 22", "no new dependencies", "AGENTS.md forbids \`as any\`").
|
|
8423
|
+
- Exact values: versions, config keys, thresholds, magic numbers.
|
|
8424
|
+
- User intent \u2014 quote short user messages verbatim. When the message is too long to quote, preserve intent with extra care: do not change scope, constraints, priorities, acceptance criteria, or requested outcomes. Mark them clearly as past quotes (e.g., "User said: ..."), not as current directives. Losing these changes the task itself.
|
|
8425
|
+
- The user's overall goal and any changes to it \u2014 the big-picture objective plus how it evolved during the compressed range. Each summary must reflect the goal as it stood at the end of the range, including pivots (e.g., "initially: fix bug X \u2192 pivoted to: refactor module Y after discovering root cause"). Losing the goal or its evolution makes all subsequent work appear unmotivated.
|
|
8426
|
+
- Purpose behind each significant action \u2014 preserve not just what was done but why: the hypothesis behind each experiment, the question behind each exploration, the task goal behind each work action. Without purpose, the summary reads as disconnected technical steps with no through-line.
|
|
8427
|
+
- Open questions and unresolved TODOs \u2014 losing these changes what work appears to remain.
|
|
8428
|
+
- Message refs of key anchors (\`m00420\`, \`m00510\u2013m00520\`) \u2014 they let you or a later reader jump back via decompress to the exact original.
|
|
8429
|
+
|
|
8430
|
+
DROP \u2014 extract the signal, discard the vessel:
|
|
8431
|
+
- Verbose logs (build/test/\`npm\` output) once you have captured the error line or the result.
|
|
8432
|
+
- Duplicate file reads once the needed content is recorded.
|
|
8433
|
+
- Consumed exploration \u2014 search hits, agent return values, successful tool outputs \u2014 once you have extracted the facts you need (same rule as dead-ends, but nothing went wrong; the content is simply spent).
|
|
8434
|
+
- Dead-end exploration \u2014 but PRESERVE the lesson in one line: "tried X, failed because Y".
|
|
8435
|
+
- Back-and-forth discussion and self-corrections once the final position is captured (keep the outcome, drop the journey to it).
|
|
8436
|
+
- Repeated status checks (\`git status\`, \`ls\`) once state is known.
|
|
8437
|
+
|
|
8438
|
+
For each significant item you DROP (scripts, reports, large analyses, long tool outputs), add a one-line CONTENT description of what it covers \u2014 not where it lives. Bad: "probe script at /path/probe_kvnet.py". Good: "probe_kvnet.py: tests n-gram baseline, generation quality, long-range dependency, position sensitivity, op pipeline, QUERY attention." This lets a later decompress target the right block by relevance, not by guessing locations.
|
|
8439
|
+
|
|
8440
|
+
PRIORITY \u2014 when the summary must be compact, preserve in this order:
|
|
8441
|
+
1. User's overall goal, goal evolution, intent, and hard constraints (losing these changes the task).
|
|
8442
|
+
2. Decisions and rationale.
|
|
8443
|
+
3. Exact technical artifacts: paths, signatures, errors, values.
|
|
8444
|
+
4. Conclusions and key findings.
|
|
8445
|
+
5. Lessons learned: what failed and why.
|
|
8446
|
+
|
|
8447
|
+
Write dense, scannable bullets \u2014 not narrative prose. If the range spans distinct concerns (request \u2192 findings \u2192 decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;
|
|
8448
|
+
var TIER2_DISTILL_RULES2 = `TIER 2 COMPRESSION \u2014 DISTILLATION
|
|
8449
|
+
|
|
8450
|
+
You are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: extract only what matters for future work, discard the process.
|
|
8451
|
+
|
|
8452
|
+
KEEP \u2014 these are the only things that survive distillation:
|
|
8453
|
+
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing).
|
|
8454
|
+
- Final outcomes: version numbers shipped, PR numbers merged/closed, bugs fixed or deferred.
|
|
8455
|
+
- Key lessons: what failed and why ("tried X, failed because Y"). These prevent repeating mistakes.
|
|
8456
|
+
- Critical constraints discovered ("must support Node 22", "AGENTS.md forbids as any").
|
|
8457
|
+
- Design decisions with architectural impact ("chose compress-as-anchor over synthetic messages because prefix cache").
|
|
8458
|
+
- Whether content is OBSOLETE or SUPERSEDED \u2014 mark with one line: "[SUPERSEDED by PR #NNN]" or "[OBSOLETE: deleted in vX.Y.Z]". Do NOT keep the obsolete content's details \u2014 just the marker and reason.
|
|
8459
|
+
- Function/class/type names and module paths that are the SUBJECT of the work \u2014 e.g., "fixed filterCompressedRanges in prune.ts", "added SessionStateRegistry in state.ts". Not exact line numbers or full signatures \u2014 just enough to LOCATE the code without searching.
|
|
8460
|
+
- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line ("explored X, not viable because Y"). Do not keep the exploration process.
|
|
8461
|
+
|
|
8462
|
+
DROP \u2014 these were useful during the work but are no longer needed:
|
|
8463
|
+
- Exact line numbers, diffs, verbose function signatures, full code listings.
|
|
8464
|
+
- Build/deploy process details, test execution steps.
|
|
8465
|
+
- Review process details (who reviewed, what rounds, test counts).
|
|
8466
|
+
- Verbose logs, command output, intermediate debugging steps.
|
|
8467
|
+
|
|
8468
|
+
FORMAT:
|
|
8469
|
+
- Start each distilled block with a source header line:
|
|
8470
|
+
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
8471
|
+
Example: \`Source: b5+b7 (56K+44K\u2192268 tok, 375x). [Tool-result recap + publish]\`
|
|
8472
|
+
- 3-5 bullet points per source block, each a self-contained fact.
|
|
8473
|
+
- Dense, scannable \u2014 no narrative prose.
|
|
8474
|
+
- Start with the outcome, not the process: "v1.13.0 shipped (7 PRs bundled)" not "implemented 7 PRs then reviewed then merged".
|
|
8475
|
+
- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks \u2014 keep it once under the most relevant source header.
|
|
8476
|
+
|
|
8477
|
+
SIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by "[no actionable content]."`;
|
|
8478
|
+
var TIER3_CONDENSE_RULES2 = `TIER 3 COMPRESSION \u2014 ULTRA-CONDENSATION
|
|
8479
|
+
|
|
8480
|
+
You are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.
|
|
8481
|
+
|
|
8482
|
+
PRIORITY \u2014 when a source block has more facts than the size target allows, keep in this order:
|
|
8483
|
+
1. Shipped outcomes (versions released, PRs merged) \u2014 these are permanent record.
|
|
8484
|
+
2. Open work (PRs/issues still pending) \u2014 these may need follow-up.
|
|
8485
|
+
3. Key decisions with architectural impact ("chose X over Y because Z").
|
|
8486
|
+
4. Critical constraints ("must support Node 22").
|
|
8487
|
+
Drop everything else. Tier 3 is a lookup index, not a knowledge base.
|
|
8488
|
+
|
|
8489
|
+
FORMAT:
|
|
8490
|
+
- Start with a source header line:
|
|
8491
|
+
\`Source: bN+bM+... (XK\u2192YK tok, Zx). [original topic]\`
|
|
8492
|
+
- Output 1-3 facts per source block. Each fact is a single line: subject + outcome.
|
|
8493
|
+
- No explanations, no rationale, no process \u2014 just the fact.
|
|
8494
|
+
- Format: "[PR/Issue/Version] \u2014 [outcome in \u22648 words]"
|
|
8495
|
+
- Merge related facts from different source blocks if they concern the same topic.
|
|
8496
|
+
|
|
8497
|
+
EXAMPLES:
|
|
8498
|
+
- "v1.13.0 shipped \u2014 quality gate + GC fix (7 PRs)"
|
|
8499
|
+
- "PR #196 merged \u2014 preserve-first-user (supersedes #169)"
|
|
8500
|
+
- "Bug 1214 fixed \u2014 compress consumed all user messages"
|
|
8501
|
+
- "Chose compress-as-anchor \u2014 prefix cache benefit over synthetic injection"
|
|
8502
|
+
- "Constraint: AGENTS.md forbids as any \u2014 never suppress types"
|
|
8503
|
+
|
|
8504
|
+
DROP:
|
|
8505
|
+
- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.
|
|
8506
|
+
- Lessons learned ("tried X, failed because Y") \u2014 drop UNLESS the failure is likely to recur and the block is <30 days old.
|
|
8507
|
+
- Design rationale details \u2014 keep the decision, drop the "because" unless it's a critical constraint.
|
|
8508
|
+
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" in the summary.
|
|
8509
|
+
|
|
8510
|
+
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.`;
|
|
8511
|
+
var defaultPrompts2 = Object.freeze({
|
|
8512
|
+
compressPhilosophy: COMPRESS_PHILOSOPHY2,
|
|
8513
|
+
howToCompressRules: HOW_TO_COMPRESS_RULES2,
|
|
8514
|
+
tier2DistillRules: TIER2_DISTILL_RULES2,
|
|
8515
|
+
tier3CondenseRules: TIER3_CONDENSE_RULES2
|
|
8516
|
+
});
|
|
8517
|
+
function formatK3(n) {
|
|
8518
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
8519
|
+
return `${n}`;
|
|
8520
|
+
}
|
|
8521
|
+
function formatRanges2(compressible, protectedRanges) {
|
|
8522
|
+
if (compressible.length === 0 && protectedRanges.length === 0) {
|
|
8523
|
+
return "[No specific ranges detected \u2014 compress any consumed content.]";
|
|
8524
|
+
}
|
|
8525
|
+
const refNum2 = (ref) => {
|
|
8526
|
+
const m = ref.match(/\d+/);
|
|
8527
|
+
return m ? parseInt(m[0], 10) : 0;
|
|
8528
|
+
};
|
|
8529
|
+
const entries = [];
|
|
8530
|
+
for (const r of compressible) {
|
|
8531
|
+
entries.push({
|
|
8532
|
+
startRef: r.startRef,
|
|
8533
|
+
endRef: r.endRef,
|
|
8534
|
+
startNum: refNum2(r.startRef),
|
|
8535
|
+
endNum: refNum2(r.endRef),
|
|
8536
|
+
count: r.count,
|
|
8537
|
+
tokens: r.tokens,
|
|
8538
|
+
toolPct: r.toolPct,
|
|
8539
|
+
textPct: r.textPct,
|
|
8540
|
+
compressibleTokens: r.tokens,
|
|
8541
|
+
compressibleCount: r.count,
|
|
8542
|
+
protectedTokens: 0,
|
|
8543
|
+
protectedCount: 0,
|
|
8544
|
+
protectedTools: [],
|
|
8545
|
+
dangerous: r.dangerous ?? false
|
|
8546
|
+
});
|
|
8547
|
+
}
|
|
8548
|
+
for (const r of protectedRanges) {
|
|
8549
|
+
entries.push({
|
|
8550
|
+
startRef: r.startRef,
|
|
8551
|
+
endRef: r.endRef,
|
|
8552
|
+
startNum: refNum2(r.startRef),
|
|
8553
|
+
endNum: refNum2(r.endRef),
|
|
8554
|
+
count: r.count,
|
|
8555
|
+
tokens: r.tokens,
|
|
8556
|
+
toolPct: 0,
|
|
8557
|
+
textPct: 0,
|
|
8558
|
+
compressibleTokens: 0,
|
|
8559
|
+
compressibleCount: 0,
|
|
8560
|
+
protectedTokens: r.tokens,
|
|
8561
|
+
protectedCount: r.count,
|
|
8562
|
+
protectedTools: [...r.tools],
|
|
8563
|
+
dangerous: false
|
|
8564
|
+
});
|
|
8565
|
+
}
|
|
8566
|
+
entries.sort((a, b) => a.startNum - b.startNum);
|
|
8567
|
+
const merged = [];
|
|
8568
|
+
for (const e of entries) {
|
|
8569
|
+
const last = merged[merged.length - 1];
|
|
8570
|
+
if (last && e.startNum <= last.endNum + 1) {
|
|
8571
|
+
last.endRef = e.endRef;
|
|
8572
|
+
last.endNum = Math.max(last.endNum, e.endNum);
|
|
8573
|
+
last.count += e.count;
|
|
8574
|
+
last.tokens += e.tokens;
|
|
8575
|
+
last.compressibleTokens += e.compressibleTokens;
|
|
8576
|
+
last.compressibleCount += e.compressibleCount;
|
|
8577
|
+
last.protectedTokens += e.protectedTokens;
|
|
8578
|
+
last.protectedCount += e.protectedCount;
|
|
8579
|
+
if (e.dangerous) last.dangerous = true;
|
|
8580
|
+
for (const t of e.protectedTools) {
|
|
8581
|
+
if (!last.protectedTools.includes(t)) last.protectedTools.push(t);
|
|
8582
|
+
}
|
|
8583
|
+
} else {
|
|
8584
|
+
merged.push({ ...e });
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
const lines = merged.map((e) => {
|
|
8588
|
+
const suffix = e.dangerous && e.compressibleTokens > 0 ? " \u26A0\uFE0F NOT recommended unless you are certain." : "";
|
|
8589
|
+
if (e.protectedTokens > 0 && e.compressibleTokens === 0) {
|
|
8590
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK3(e.tokens)} [PROTECTED: ${e.protectedTools.join(", ")} \u2014 not compressible]${suffix}`;
|
|
8591
|
+
}
|
|
8592
|
+
if (e.protectedTokens > 0 && e.compressibleTokens > 0) {
|
|
8593
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK3(e.tokens)} [${formatK3(e.compressibleTokens)} compressible | ${formatK3(e.protectedTokens)} protected: ${e.protectedTools.join(", ")}]${suffix}`;
|
|
8594
|
+
}
|
|
8595
|
+
return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK3(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${suffix}`;
|
|
8596
|
+
});
|
|
8597
|
+
return `Compressible ranges (${merged.length}, oldest first):
|
|
8598
|
+
${lines.join("\n")}`;
|
|
8599
|
+
}
|
|
8600
|
+
var substringAlgorithm2 = {
|
|
8601
|
+
name: "substring",
|
|
8602
|
+
description: "Exact substring counting (original baseline). Predictable, no normalization.",
|
|
8603
|
+
score(docs, query) {
|
|
8604
|
+
const terms = query.toLowerCase().trim().split(/\s+/).filter((t) => t.length > 0);
|
|
8605
|
+
if (terms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
8606
|
+
return docs.map((d) => {
|
|
8607
|
+
const haystack = d.text.toLowerCase();
|
|
8608
|
+
let score = 0;
|
|
8609
|
+
for (const term of terms) score += countOccurrences22(haystack, term);
|
|
8610
|
+
return { ref: d.ref, score };
|
|
8611
|
+
});
|
|
8612
|
+
}
|
|
8613
|
+
};
|
|
8614
|
+
function countOccurrences22(haystack, needle) {
|
|
8615
|
+
if (!needle) return 0;
|
|
8616
|
+
return haystack.split(needle).length - 1;
|
|
8617
|
+
}
|
|
8618
|
+
function stem2(word) {
|
|
8619
|
+
let w = word;
|
|
8620
|
+
if (w.length <= 3) return w;
|
|
8621
|
+
if (w.endsWith("ies")) w = w.slice(0, -3) + "y";
|
|
8622
|
+
else if (w.endsWith("ses") || w.endsWith("xes") || w.endsWith("zes")) w = w.slice(0, -2);
|
|
8623
|
+
else if (w.endsWith("ches") || w.endsWith("shes")) w = w.slice(0, -2);
|
|
8624
|
+
else if (w.endsWith("s") && !w.endsWith("ss")) w = w.slice(0, -1);
|
|
8625
|
+
if (w.endsWith("ing") && w.length > 5) w = w.slice(0, -3);
|
|
8626
|
+
if (w.endsWith("ed") && w.length > 4) w = w.slice(0, -2);
|
|
8627
|
+
if (w.endsWith("ation") && w.length > 6) w = w.slice(0, -3);
|
|
8628
|
+
else if (w.endsWith("tion") && w.length > 5) w = w.slice(0, -4) + "t";
|
|
8629
|
+
else if (w.endsWith("ion") && w.length > 4) w = w.slice(0, -3);
|
|
8630
|
+
if (w.endsWith("ment") && w.length > 6) w = w.slice(0, -4);
|
|
8631
|
+
if (w.endsWith("ness") && w.length > 6) w = w.slice(0, -4);
|
|
8632
|
+
if (w.endsWith("ly") && w.length > 4) w = w.slice(0, -2);
|
|
8633
|
+
return w;
|
|
8634
|
+
}
|
|
8635
|
+
var CJK2 = /[\u3400-\u9fff\uf900-\ufaff\u3040-\u30ff\uac00-\ud7af]/;
|
|
8636
|
+
var CJK_RUN2 = new RegExp(`${CJK2.source}+`, "g");
|
|
8637
|
+
var LATIN_WORD2 = /[a-z][a-z0-9_]*[a-z0-9]|[a-z0-9]/g;
|
|
8638
|
+
function tokenize2(text, opts = {}) {
|
|
8639
|
+
const lower = text.toLowerCase();
|
|
8640
|
+
const tokens = [];
|
|
8641
|
+
const latin = lower.match(LATIN_WORD2) ?? [];
|
|
8642
|
+
for (let w of latin) {
|
|
8643
|
+
if (w.length >= 2) {
|
|
8644
|
+
if (opts.stem) w = stem2(w);
|
|
8645
|
+
tokens.push(w);
|
|
8646
|
+
}
|
|
8647
|
+
}
|
|
8648
|
+
const cjkRuns = lower.match(CJK_RUN2) ?? [];
|
|
8649
|
+
for (const run of cjkRuns) {
|
|
8650
|
+
if (run.length === 1) {
|
|
8651
|
+
tokens.push(run);
|
|
8652
|
+
} else {
|
|
8653
|
+
for (let i = 0; i < run.length - 1; i++) tokens.push(run.slice(i, i + 2));
|
|
8654
|
+
for (const ch of run) tokens.push(ch);
|
|
8655
|
+
}
|
|
8656
|
+
}
|
|
8657
|
+
return tokens;
|
|
8658
|
+
}
|
|
8659
|
+
function charBigrams2(text) {
|
|
8660
|
+
const grams = [];
|
|
8661
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
8662
|
+
const pair = text.slice(i, i + 2);
|
|
8663
|
+
if (pair.trim().length === pair.length) grams.push(pair);
|
|
8664
|
+
}
|
|
8665
|
+
return grams;
|
|
8666
|
+
}
|
|
8667
|
+
function tfMap2(text, stem22) {
|
|
8668
|
+
const m = /* @__PURE__ */ new Map();
|
|
8669
|
+
for (const t of tokenize2(text, { stem: stem22 })) m.set(t, (m.get(t) ?? 0) + 1);
|
|
8670
|
+
return m;
|
|
8671
|
+
}
|
|
8672
|
+
var bm25Algorithm2 = {
|
|
8673
|
+
name: "bm25",
|
|
8674
|
+
description: "BM25 with stemming + CJK bigram tokenization. IR-standard relevance ranking.",
|
|
8675
|
+
score(docs, query) {
|
|
8676
|
+
const N = docs.length;
|
|
8677
|
+
const k1 = 1.2;
|
|
8678
|
+
const b = 0.75;
|
|
8679
|
+
const parsed = docs.map((d) => {
|
|
8680
|
+
const text = d.text;
|
|
8681
|
+
const tf = tfMap2(text, true);
|
|
8682
|
+
let len = 0;
|
|
8683
|
+
for (const v of tf.values()) len += v;
|
|
8684
|
+
return { id: d.ref, tf, len };
|
|
8685
|
+
});
|
|
8686
|
+
const avgdl = parsed.reduce((s, d) => s + d.len, 0) / (N || 1);
|
|
8687
|
+
const qTerms = tokenize2(query, { stem: true });
|
|
8688
|
+
if (qTerms.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
8689
|
+
const idf = /* @__PURE__ */ new Map();
|
|
8690
|
+
for (const t of new Set(qTerms)) {
|
|
8691
|
+
let df = 0;
|
|
8692
|
+
for (const d of parsed) if (d.tf.has(t)) df++;
|
|
8693
|
+
idf.set(t, Math.log(1 + (N - df + 0.5) / (df + 0.5)));
|
|
8694
|
+
}
|
|
8695
|
+
return parsed.map((d) => {
|
|
8696
|
+
let score = 0;
|
|
8697
|
+
for (const t of qTerms) {
|
|
8698
|
+
const f = d.tf.get(t) ?? 0;
|
|
8699
|
+
if (f === 0) continue;
|
|
8700
|
+
const idfT = idf.get(t) ?? 0;
|
|
8701
|
+
score += idfT * (f * (k1 + 1)) / (f + k1 * (1 - b + b * d.len / (avgdl || 1)));
|
|
8702
|
+
}
|
|
8703
|
+
return { ref: d.id, score };
|
|
8704
|
+
});
|
|
8705
|
+
}
|
|
8706
|
+
};
|
|
8707
|
+
var fuzzyAlgorithm2 = {
|
|
8708
|
+
name: "fuzzy",
|
|
8709
|
+
description: "Character bigram overlap. Typo-tolerant, script-agnostic, high recall.",
|
|
8710
|
+
score(docs, query) {
|
|
8711
|
+
const qTokens = query.toLowerCase().split(/[\s,]+/).filter((t) => t.length >= 4);
|
|
8712
|
+
if (qTokens.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
8713
|
+
const qGrams = /* @__PURE__ */ new Set();
|
|
8714
|
+
for (const t of qTokens) for (const g of charBigrams2(t)) qGrams.add(g);
|
|
8715
|
+
if (qGrams.size === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
|
|
8716
|
+
return docs.map((d) => {
|
|
8717
|
+
const haystack = d.text.toLowerCase();
|
|
8718
|
+
const docGrams = new Set(charBigrams2(haystack));
|
|
8719
|
+
let hits = 0;
|
|
8720
|
+
for (const g of qGrams) if (docGrams.has(g)) hits++;
|
|
8721
|
+
return { ref: d.ref, score: hits / qGrams.size };
|
|
8722
|
+
});
|
|
8723
|
+
}
|
|
8724
|
+
};
|
|
8725
|
+
var W_BM252 = 0.7;
|
|
8726
|
+
var W_FUZZY2 = 0.3;
|
|
8727
|
+
var hybridAlgorithm2 = {
|
|
8728
|
+
name: "hybrid",
|
|
8729
|
+
description: "Weighted BM25(stem) + fuzzy n-gram. Default \u2014 best precision + recall.",
|
|
8730
|
+
score(docs, query) {
|
|
8731
|
+
const bm = bm25Algorithm2.score(docs, query);
|
|
8732
|
+
const fz = fuzzyAlgorithm2.score(docs, query);
|
|
8733
|
+
const maxBm = Math.max(...bm.map((r) => r.score), 1e-9);
|
|
8734
|
+
const maxFz = Math.max(...fz.map((r) => r.score), 1e-9);
|
|
8735
|
+
const bmMap = new Map(bm.map((r) => [r.ref, r.score / maxBm]));
|
|
8736
|
+
const fzMap = new Map(fz.map((r) => [r.ref, r.score / maxFz]));
|
|
8737
|
+
return docs.map((d) => ({
|
|
8738
|
+
ref: d.ref,
|
|
8739
|
+
score: W_BM252 * (bmMap.get(d.ref) ?? 0) + W_FUZZY2 * (fzMap.get(d.ref) ?? 0)
|
|
8740
|
+
}));
|
|
8741
|
+
}
|
|
8742
|
+
};
|
|
8743
|
+
var registry22 = /* @__PURE__ */ new Map();
|
|
8744
|
+
function registerSearchAlgorithm2(algo) {
|
|
8745
|
+
registry22.set(algo.name, algo);
|
|
8746
|
+
}
|
|
8747
|
+
registerSearchAlgorithm2(substringAlgorithm2);
|
|
8748
|
+
registerSearchAlgorithm2(bm25Algorithm2);
|
|
8749
|
+
registerSearchAlgorithm2(fuzzyAlgorithm2);
|
|
8750
|
+
registerSearchAlgorithm2(hybridAlgorithm2);
|
|
8751
|
+
|
|
8752
|
+
// node_modules/billion-context-kit/dist/index.js
|
|
8753
|
+
var VIABLE_RANGE_MIN_TOKENS = 200;
|
|
8754
|
+
function viableRanges(ranges) {
|
|
8755
|
+
return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);
|
|
8756
|
+
}
|
|
8757
|
+
function topicFallback(summary) {
|
|
8758
|
+
const first = summary.split(/[.\n]/)[0] ?? "";
|
|
8759
|
+
const t = first.trim().replace(/^["'`]+/, "").trim();
|
|
8760
|
+
return t.length <= 30 ? t : `${t.slice(0, 30).trimEnd()}\u2026`;
|
|
8761
|
+
}
|
|
8762
|
+
function formatCompactTokens(count) {
|
|
8763
|
+
if (count < 1e3) return count.toString();
|
|
8764
|
+
if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
|
|
8765
|
+
if (count < 1e6) return `${Math.round(count / 1e3)}k`;
|
|
8766
|
+
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
8767
|
+
return `${Math.round(count / 1e6)}M`;
|
|
8768
|
+
}
|
|
8769
|
+
function bar(value, total, width = 20) {
|
|
8770
|
+
if (total === 0) return "";
|
|
8771
|
+
const filled = Math.max(0, Math.min(width, Math.round(value / total * width)));
|
|
8772
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
8773
|
+
}
|
|
8774
|
+
function buildStatusPanel(input) {
|
|
8775
|
+
const { tokenCount, state, nudge, modelContextLimit } = input;
|
|
8776
|
+
const fmt2 = input.fmtTokens ?? formatCompactTokens;
|
|
8777
|
+
const bd = nudge?.contextBreakdown;
|
|
8778
|
+
const limit = modelContextLimit;
|
|
8779
|
+
const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;
|
|
8780
|
+
const systemPromptTokens = input.systemPromptTokens;
|
|
8781
|
+
const sentTotal = classified + systemPromptTokens;
|
|
8782
|
+
const sessionOnly = input.unprunedTokens !== void 0 ? Math.max(0, input.unprunedTokens - sentTotal) : 0;
|
|
8783
|
+
const displayTotal = tokenCount;
|
|
8784
|
+
const displayPct = limit > 0 ? Math.round(displayTotal / limit * 100) : 0;
|
|
8785
|
+
const sentPct = limit > 0 ? Math.round(sentTotal / limit * 100) : 0;
|
|
8786
|
+
const activeBlocksList = state.blocks.filter((b) => b.active);
|
|
8787
|
+
const totalBlocksList = state.blocks;
|
|
8788
|
+
const lines = [];
|
|
8789
|
+
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");
|
|
8790
|
+
lines.push("\u2502 ACP Context Analysis \u2502");
|
|
8791
|
+
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");
|
|
8792
|
+
if (input.version) lines.push(input.version);
|
|
8793
|
+
lines.push("");
|
|
8794
|
+
lines.push(`Context (session accounting, host footer scale): ${displayPct}% (${fmt2(displayTotal)} / ${fmt2(limit)}) \u2014 never shrinks; includes compressed originals`);
|
|
8795
|
+
if (nudge && bd) {
|
|
8796
|
+
const growth = bd.growth;
|
|
8797
|
+
if (growth > 0 && displayTotal > 0) {
|
|
8798
|
+
lines.push(`Growth: +${fmt2(growth)} since last nudge`);
|
|
8799
|
+
}
|
|
8800
|
+
lines.push("");
|
|
8801
|
+
lines.push(`Sent to LLM (after compression, est.): ${fmt2(sentTotal)}${limit > 0 ? ` (${sentPct}% of limit)` : ""}`);
|
|
8802
|
+
if (input.unprunedTokens !== void 0 && sessionOnly > 0) {
|
|
8803
|
+
lines.push(`Session-only (compressed originals, est.): ${fmt2(sessionOnly)} \u2014 pruned from every request; the footer/nudge still count them`);
|
|
8804
|
+
}
|
|
8805
|
+
lines.push("");
|
|
8806
|
+
lines.push("Token Breakdown (sent view):");
|
|
8807
|
+
const categories = [
|
|
8808
|
+
{ label: "Tool", value: bd.tool },
|
|
8809
|
+
{ label: "SysPrompt", value: systemPromptTokens },
|
|
8810
|
+
{ label: "Text", value: bd.text },
|
|
8811
|
+
{ label: "Code", value: bd.code },
|
|
8812
|
+
{ label: "Summaries", value: bd.summaries }
|
|
8813
|
+
];
|
|
8814
|
+
for (const cat of categories) {
|
|
8815
|
+
if (cat.value <= 0) continue;
|
|
8816
|
+
const pct2 = sentTotal > 0 ? Math.round(cat.value / sentTotal * 100) : 0;
|
|
8817
|
+
const b = bar(cat.value, sentTotal);
|
|
8818
|
+
lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct2).padStart(3)}% ${fmt2(cat.value)}`);
|
|
8819
|
+
}
|
|
8820
|
+
}
|
|
8821
|
+
lines.push("");
|
|
8822
|
+
if (nudge) {
|
|
8823
|
+
if (nudge.shouldInject) {
|
|
8824
|
+
const tierInfo = nudge.tier ? ` [T${nudge.tier} distillation]` : "";
|
|
8825
|
+
lines.push(`Nudge: ACTIVE${tierInfo} \u2014 ${nudge.reason}`);
|
|
8826
|
+
} else {
|
|
8827
|
+
lines.push(`Nudge: idle \u2014 ${nudge.reason}`);
|
|
8828
|
+
}
|
|
8829
|
+
}
|
|
8830
|
+
const ranges = viableRanges(nudge?.compressibleRanges ?? []);
|
|
8831
|
+
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
8832
|
+
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
8833
|
+
lines.push("");
|
|
8834
|
+
lines.push(formatRanges2(ranges, protectedRanges));
|
|
8835
|
+
}
|
|
8836
|
+
if (activeBlocksList.length > 0) {
|
|
8837
|
+
lines.push("");
|
|
8838
|
+
lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmt2(state.stats.tokensCompressed)} tokens compressed)`);
|
|
8839
|
+
for (const b of activeBlocksList) {
|
|
8840
|
+
const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || "")}`;
|
|
8841
|
+
const summaryTok = defaultCountTokens2(b.summary || "");
|
|
8842
|
+
const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;
|
|
8843
|
+
lines.push(` [${b.blockId}] T${b.tier} ${fmt2(origTok)}\u2192${fmt2(summaryTok)}${topic}`);
|
|
8844
|
+
}
|
|
8845
|
+
} else if (totalBlocksList.length > 0) {
|
|
8846
|
+
lines.push("");
|
|
8847
|
+
lines.push(`Blocks: 0 active / ${totalBlocksList.length} total (${fmt2(state.stats.tokensCompressed)} tokens compressed)`);
|
|
8848
|
+
} else {
|
|
8849
|
+
lines.push("");
|
|
8850
|
+
lines.push("Blocks: none (nothing compressed yet)");
|
|
8851
|
+
}
|
|
8852
|
+
lines.push("");
|
|
8853
|
+
lines.push("Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.");
|
|
8854
|
+
return lines.join("\n");
|
|
8855
|
+
}
|
|
8856
|
+
|
|
8857
|
+
// src/delegate-tool.ts
|
|
8858
|
+
import {
|
|
8859
|
+
spawn
|
|
8860
|
+
} from "child_process";
|
|
8861
|
+
import { createWriteStream, existsSync as existsSync2 } from "fs";
|
|
8862
|
+
import { mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, appendFile } from "fs/promises";
|
|
8863
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
8864
|
+
import { dirname as dirname3, join as join4, resolve as resolvePath } from "path";
|
|
8865
|
+
|
|
8866
|
+
// src/footer-status.ts
|
|
8867
|
+
var FOOTER_STATUS_KEY = "billion-context-pi";
|
|
8868
|
+
var ui;
|
|
8869
|
+
var lastFooterText = "";
|
|
8870
|
+
function formatCompactTokens2(count) {
|
|
8871
|
+
if (count < 1e3) return count.toString();
|
|
8872
|
+
if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
|
|
8873
|
+
if (count < 1e6) return `${Math.round(count / 1e3)}k`;
|
|
8874
|
+
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
8875
|
+
return `${Math.round(count / 1e6)}M`;
|
|
8876
|
+
}
|
|
8877
|
+
function initFooterStatus(ctx) {
|
|
8878
|
+
ui = ctx.ui;
|
|
8879
|
+
lastFooterText = void 0;
|
|
8880
|
+
}
|
|
8881
|
+
function updateFooterStatus() {
|
|
8882
|
+
if (!ui) return;
|
|
8883
|
+
const usage = getDelegateUsage();
|
|
8884
|
+
let text;
|
|
8885
|
+
if (usage && usage.totalTokens > 0) {
|
|
8886
|
+
const costStr = usage.cost.total > 0 ? ` ($${usage.cost.total.toFixed(4)})` : "";
|
|
8887
|
+
text = `sub-agents \u2191${formatCompactTokens2(usage.input)} \u2193${formatCompactTokens2(usage.output)}${costStr}`;
|
|
8888
|
+
}
|
|
8889
|
+
if ((text ?? "") === lastFooterText) return;
|
|
8890
|
+
lastFooterText = text ?? "";
|
|
8891
|
+
try {
|
|
8892
|
+
ui.setStatus(FOOTER_STATUS_KEY, text);
|
|
8893
|
+
} catch {
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
function disposeFooterStatus() {
|
|
8897
|
+
if (ui) {
|
|
8898
|
+
try {
|
|
8899
|
+
ui.setStatus(FOOTER_STATUS_KEY, void 0);
|
|
8900
|
+
} catch {
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8903
|
+
ui = void 0;
|
|
8904
|
+
lastFooterText = "";
|
|
8905
|
+
}
|
|
8906
|
+
|
|
8907
|
+
// src/fleet-widget.ts
|
|
8908
|
+
var DELEGATE_WIDGET_KEY = "billion-context-pi-delegates";
|
|
8909
|
+
var REFRESH_MS = 500;
|
|
8910
|
+
var MAX_TASK_LEN = 48;
|
|
8911
|
+
var ui2;
|
|
8912
|
+
var timer;
|
|
8913
|
+
var lastRenderKey = "";
|
|
8914
|
+
var runsSnapshot;
|
|
8915
|
+
function truncateTask(task) {
|
|
8916
|
+
const oneLine = task.replace(/\n/g, " ").trim();
|
|
8917
|
+
if (oneLine.length <= MAX_TASK_LEN) return oneLine;
|
|
8918
|
+
return `${oneLine.slice(0, MAX_TASK_LEN - 1)}\u2026`;
|
|
8919
|
+
}
|
|
8920
|
+
function renderLines(runs2) {
|
|
8921
|
+
if (runs2.length === 0) return void 0;
|
|
8922
|
+
const now = Date.now();
|
|
8923
|
+
const header = runs2.length === 1 ? `acp_delegate \xB7 1 running` : `acp_delegate \xB7 ${runs2.length} running`;
|
|
8924
|
+
const rows = runs2.map((r) => {
|
|
8925
|
+
const elapsed = Math.max(0, Math.round((now - r.startedAt) / 1e3));
|
|
8926
|
+
return ` \u25CF ${r.agent} (${elapsed}s) \u2014 ${truncateTask(r.task)}`;
|
|
8927
|
+
});
|
|
8928
|
+
return [header, ...rows];
|
|
8929
|
+
}
|
|
8930
|
+
function renderKeyFor(runs2) {
|
|
8931
|
+
return runs2.map((r) => `${r.agent}:${Math.round((Date.now() - r.startedAt) / 1e3)}:${truncateTask(r.task)}`).join("|");
|
|
8932
|
+
}
|
|
8933
|
+
function stopTimer() {
|
|
8934
|
+
if (timer) {
|
|
8935
|
+
clearInterval(timer);
|
|
8936
|
+
timer = void 0;
|
|
8937
|
+
}
|
|
8938
|
+
}
|
|
8939
|
+
function clearWidget() {
|
|
8940
|
+
if (!ui2) return;
|
|
8941
|
+
try {
|
|
8942
|
+
ui2.setWidget(DELEGATE_WIDGET_KEY, void 0);
|
|
8943
|
+
} catch {
|
|
8944
|
+
}
|
|
8945
|
+
}
|
|
8946
|
+
function refresh() {
|
|
8947
|
+
if (!ui2) return;
|
|
8948
|
+
const runs2 = runsSnapshot ? runsSnapshot() : [];
|
|
8949
|
+
if (runs2.length === 0) {
|
|
8950
|
+
if (lastRenderKey !== "") {
|
|
8951
|
+
lastRenderKey = "";
|
|
8952
|
+
clearWidget();
|
|
8953
|
+
}
|
|
8954
|
+
updateFooterStatus();
|
|
8955
|
+
stopTimer();
|
|
8956
|
+
return;
|
|
8957
|
+
}
|
|
8958
|
+
const sorted = [...runs2].sort((a, b) => a.startedAt - b.startedAt);
|
|
8959
|
+
const renderKey = renderKeyFor(sorted);
|
|
8960
|
+
if (renderKey === lastRenderKey) return;
|
|
8961
|
+
lastRenderKey = renderKey;
|
|
8962
|
+
const lines = renderLines(sorted);
|
|
8963
|
+
try {
|
|
8964
|
+
ui2.setWidget(DELEGATE_WIDGET_KEY, lines, { placement: "belowEditor" });
|
|
8965
|
+
} catch {
|
|
8966
|
+
ui2 = void 0;
|
|
8967
|
+
stopTimer();
|
|
8968
|
+
}
|
|
8969
|
+
updateFooterStatus();
|
|
8970
|
+
}
|
|
8971
|
+
var delegateStatusWidget = {
|
|
8972
|
+
setContext(ctx, snapshot) {
|
|
8973
|
+
if (ctx.mode !== "tui") return;
|
|
8974
|
+
initFooterStatus(ctx);
|
|
8975
|
+
ui2 = ctx.ui;
|
|
8976
|
+
runsSnapshot = snapshot;
|
|
8977
|
+
if (!timer) {
|
|
8978
|
+
timer = setInterval(refresh, REFRESH_MS);
|
|
8979
|
+
timer.unref?.();
|
|
8980
|
+
}
|
|
8981
|
+
refresh();
|
|
8982
|
+
},
|
|
8983
|
+
dispose() {
|
|
8984
|
+
stopTimer();
|
|
8985
|
+
clearWidget();
|
|
8986
|
+
disposeFooterStatus();
|
|
8987
|
+
ui2 = void 0;
|
|
8988
|
+
lastRenderKey = "";
|
|
8989
|
+
},
|
|
8990
|
+
poke() {
|
|
8991
|
+
if (ui2 && !timer) {
|
|
8992
|
+
timer = setInterval(refresh, REFRESH_MS);
|
|
8993
|
+
timer.unref?.();
|
|
8994
|
+
}
|
|
8995
|
+
refresh();
|
|
8996
|
+
}
|
|
8997
|
+
};
|
|
8998
|
+
|
|
8999
|
+
// src/delegate-watchdog.ts
|
|
9000
|
+
function attachWatchdogs(child, hooks, opts) {
|
|
9001
|
+
let idleTimer;
|
|
9002
|
+
let eofTimer;
|
|
9003
|
+
let killGraceTimer;
|
|
9004
|
+
let timeoutTimer;
|
|
9005
|
+
let settledGraceTimer;
|
|
9006
|
+
const clearTimers = () => {
|
|
9007
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
9008
|
+
if (eofTimer) clearTimeout(eofTimer);
|
|
9009
|
+
if (killGraceTimer) clearTimeout(killGraceTimer);
|
|
9010
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
9011
|
+
if (settledGraceTimer) clearTimeout(settledGraceTimer);
|
|
9012
|
+
};
|
|
9013
|
+
const killByWatchdog = (reason) => {
|
|
9014
|
+
if (hooks.isSettled()) return;
|
|
9015
|
+
hooks.onKill(reason);
|
|
9016
|
+
try {
|
|
9017
|
+
child.kill("SIGTERM");
|
|
9018
|
+
} catch {
|
|
9019
|
+
}
|
|
9020
|
+
killGraceTimer = setTimeout(() => {
|
|
9021
|
+
if (hooks.isSettled()) return;
|
|
9022
|
+
try {
|
|
9023
|
+
child.kill("SIGKILL");
|
|
9024
|
+
} catch {
|
|
9025
|
+
}
|
|
9026
|
+
}, opts.killGraceMs);
|
|
9027
|
+
killGraceTimer.unref?.();
|
|
9028
|
+
};
|
|
9029
|
+
const settledGrace = (graceMs, _killGraceMs, reason) => {
|
|
9030
|
+
if (hooks.isSettled() || settledGraceTimer) return;
|
|
9031
|
+
settledGraceTimer = setTimeout(() => {
|
|
9032
|
+
settledGraceTimer = void 0;
|
|
9033
|
+
killByWatchdog(reason);
|
|
9034
|
+
}, graceMs);
|
|
9035
|
+
settledGraceTimer.unref?.();
|
|
9036
|
+
};
|
|
9037
|
+
const poke = () => {
|
|
9038
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
9039
|
+
idleTimer = setTimeout(() => killByWatchdog(`no output for ${opts.idleMs / 6e4}m`), opts.idleMs);
|
|
9040
|
+
idleTimer.unref?.();
|
|
9041
|
+
};
|
|
9042
|
+
poke();
|
|
9043
|
+
timeoutTimer = setTimeout(() => killByWatchdog(`${opts.timeoutMs / 6e4}m limit`), opts.timeoutMs);
|
|
9044
|
+
timeoutTimer.unref?.();
|
|
9045
|
+
const onStdoutEnd = () => {
|
|
8439
9046
|
if (hooks.isSettled()) return;
|
|
8440
9047
|
eofTimer = setTimeout(() => {
|
|
8441
9048
|
if (hooks.isSettled()) return;
|
|
@@ -9499,13 +10106,15 @@ function makeStatusTool(runtime) {
|
|
|
9499
10106
|
async function handleStatus(args, runtime, ctx) {
|
|
9500
10107
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
9501
10108
|
const config = runtime.configFor(ctx);
|
|
9502
|
-
const
|
|
9503
|
-
const
|
|
10109
|
+
const coveredIds = collectCoveredMessageIds(state);
|
|
10110
|
+
const systemPromptText = getSystemPromptText(ctx);
|
|
10111
|
+
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
10112
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
9504
10113
|
const turn = runtime.core.processTurn({
|
|
9505
10114
|
messages: coreMessages,
|
|
9506
10115
|
state,
|
|
9507
10116
|
config,
|
|
9508
|
-
tokenCount:
|
|
10117
|
+
tokenCount: sentTokens
|
|
9509
10118
|
});
|
|
9510
10119
|
const processed = turn.messages;
|
|
9511
10120
|
const base = buildStatusReport(turn.state, processed, defaultCountTokens, {
|
|
@@ -9517,7 +10126,7 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
9517
10126
|
});
|
|
9518
10127
|
if (args.scope) return base;
|
|
9519
10128
|
const nudge = turn.nudge;
|
|
9520
|
-
const ranges = nudge?.compressibleRanges ?? [];
|
|
10129
|
+
const ranges = viableRanges(nudge?.compressibleRanges ?? []);
|
|
9521
10130
|
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
9522
10131
|
const extra = [];
|
|
9523
10132
|
if (nudge) {
|
|
@@ -9548,23 +10157,6 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
9548
10157
|
${extra.join("\n")}` : base;
|
|
9549
10158
|
}
|
|
9550
10159
|
|
|
9551
|
-
// src/compat.ts
|
|
9552
|
-
function normalizeSystemPrompt(input) {
|
|
9553
|
-
if (input === void 0) return "";
|
|
9554
|
-
if (Array.isArray(input)) return input.join("\n");
|
|
9555
|
-
return input;
|
|
9556
|
-
}
|
|
9557
|
-
function formatSystemPromptForEvent(base, append) {
|
|
9558
|
-
const normalized = normalizeSystemPrompt(base);
|
|
9559
|
-
return `${normalized}
|
|
9560
|
-
|
|
9561
|
-
${append}`;
|
|
9562
|
-
}
|
|
9563
|
-
function getSystemPromptText(ctx) {
|
|
9564
|
-
const result = ctx.getSystemPrompt?.();
|
|
9565
|
-
return normalizeSystemPrompt(result);
|
|
9566
|
-
}
|
|
9567
|
-
|
|
9568
10160
|
// src/commands.ts
|
|
9569
10161
|
function makeCommands(runtime) {
|
|
9570
10162
|
return [
|
|
@@ -9632,106 +10224,34 @@ ${text}`);
|
|
|
9632
10224
|
}
|
|
9633
10225
|
];
|
|
9634
10226
|
}
|
|
9635
|
-
function fmtTokens(n) {
|
|
9636
|
-
return formatCompactTokens(n);
|
|
9637
|
-
}
|
|
9638
|
-
function bar(value, total, width = 20) {
|
|
9639
|
-
if (total === 0) return "";
|
|
9640
|
-
const filled = Math.max(0, Math.min(width, Math.round(value / total * width)));
|
|
9641
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
9642
|
-
}
|
|
9643
10227
|
async function statusReport(runtime, ctx) {
|
|
9644
10228
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
9645
10229
|
const config = runtime.configFor(ctx);
|
|
9646
10230
|
const realUsage = ctx.getContextUsage?.();
|
|
9647
|
-
const tokenCount = realUsage?.tokens && realUsage.tokens > 0 ? realUsage.tokens : defaultCountTokens(coreMessages.map((m) => m.text ?? "").join("\n"));
|
|
9648
|
-
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount });
|
|
9649
|
-
const nudge = turn.nudge;
|
|
9650
|
-
const bd = nudge?.contextBreakdown;
|
|
9651
|
-
const limit = config.modelContextLimit;
|
|
9652
|
-
const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;
|
|
9653
10231
|
const systemPromptText = getSystemPromptText(ctx);
|
|
9654
10232
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
9655
|
-
const
|
|
9656
|
-
const
|
|
9657
|
-
const
|
|
9658
|
-
const
|
|
9659
|
-
const
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
const growth = bd.growth;
|
|
9670
|
-
if (growth > 0 && displayTotal > 0) {
|
|
9671
|
-
lines.push(`Growth: +${fmtTokens(growth)} since last nudge`);
|
|
9672
|
-
}
|
|
9673
|
-
if (displayTotal > 0) {
|
|
9674
|
-
lines.push("");
|
|
9675
|
-
lines.push("Token Breakdown:");
|
|
9676
|
-
const categories = [
|
|
9677
|
-
{ label: "Tool", value: bd.tool },
|
|
9678
|
-
{ label: "SysPrompt", value: systemPromptTokens },
|
|
9679
|
-
{ label: "Framework", value: framework },
|
|
9680
|
-
{ label: "Text", value: bd.text },
|
|
9681
|
-
{ label: "Code", value: bd.code },
|
|
9682
|
-
{ label: "Summaries", value: bd.summaries }
|
|
9683
|
-
];
|
|
9684
|
-
for (const cat of categories) {
|
|
9685
|
-
if (cat.value <= 0) continue;
|
|
9686
|
-
const pct2 = displayTotal > 0 ? Math.round(cat.value / displayTotal * 100) : 0;
|
|
9687
|
-
const b = bar(cat.value, displayTotal);
|
|
9688
|
-
lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct2).padStart(3)}% ${fmtTokens(cat.value)}`);
|
|
9689
|
-
}
|
|
9690
|
-
}
|
|
9691
|
-
}
|
|
9692
|
-
lines.push("");
|
|
9693
|
-
if (nudge) {
|
|
9694
|
-
if (nudge.shouldInject) {
|
|
9695
|
-
const tierInfo = nudge.tier ? ` [T${nudge.tier} distillation]` : "";
|
|
9696
|
-
lines.push(`Nudge: ACTIVE${tierInfo} \u2014 ${nudge.reason}`);
|
|
9697
|
-
} else {
|
|
9698
|
-
lines.push(`Nudge: idle \u2014 ${nudge.reason}`);
|
|
9699
|
-
}
|
|
9700
|
-
}
|
|
9701
|
-
const ranges = nudge?.compressibleRanges ?? [];
|
|
9702
|
-
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
9703
|
-
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
9704
|
-
lines.push("");
|
|
9705
|
-
lines.push(formatRanges(ranges, protectedRanges));
|
|
9706
|
-
}
|
|
9707
|
-
if (activeBlocksList.length > 0) {
|
|
9708
|
-
lines.push("");
|
|
9709
|
-
lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmtTokens(state.stats.tokensCompressed)} tokens compressed)`);
|
|
9710
|
-
for (const b of activeBlocksList) {
|
|
9711
|
-
const topic = b.topic ? `: ${b.topic}` : "";
|
|
9712
|
-
const summaryTok = defaultCountTokens(b.summary || "");
|
|
9713
|
-
const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;
|
|
9714
|
-
lines.push(` [${b.blockId}] T${b.tier} ${fmtTokens(origTok)}\u2192${fmtTokens(summaryTok)}${topic}`);
|
|
9715
|
-
}
|
|
9716
|
-
} else if (totalBlocksList.length > 0) {
|
|
9717
|
-
lines.push("");
|
|
9718
|
-
lines.push(`Blocks: 0 active / ${totalBlocksList.length} total (${fmtTokens(state.stats.tokensCompressed)} tokens compressed)`);
|
|
9719
|
-
} else {
|
|
9720
|
-
lines.push("");
|
|
9721
|
-
lines.push("Blocks: none (nothing compressed yet)");
|
|
9722
|
-
}
|
|
9723
|
-
lines.push("");
|
|
10233
|
+
const sessionTokens = realUsage?.tokens && realUsage.tokens > 0 ? realUsage.tokens : defaultCountTokens(coreMessages.map((m) => m.text ?? "").join("\n"));
|
|
10234
|
+
const coveredIds = collectCoveredMessageIds(state);
|
|
10235
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
10236
|
+
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
10237
|
+
const versionStr = "0.1.38" ? `billion-context-pi@${"0.1.38"}` : void 0;
|
|
10238
|
+
let text = buildStatusPanel({
|
|
10239
|
+
version: versionStr,
|
|
10240
|
+
tokenCount: sessionTokens,
|
|
10241
|
+
systemPromptTokens,
|
|
10242
|
+
state: turn.state,
|
|
10243
|
+
nudge: turn.nudge,
|
|
10244
|
+
modelContextLimit: config.modelContextLimit,
|
|
10245
|
+
unprunedTokens: coreMessages.reduce((sum, m) => sum + defaultCountTokens(m.text ?? ""), 0)
|
|
10246
|
+
});
|
|
9724
10247
|
const delegateUsage = getDelegateUsage();
|
|
9725
10248
|
if (delegateUsage && delegateUsage.totalTokens > 0) {
|
|
9726
|
-
lines.push("");
|
|
9727
10249
|
const cost = delegateUsage.cost.total;
|
|
9728
10250
|
const costStr = cost > 0 ? ` ($${cost.toFixed(4)})` : "";
|
|
9729
|
-
|
|
9730
|
-
|
|
10251
|
+
text += "\n\n\u2500\u2500 Session delegate usage (excluded from main totals) \u2500\u2500\n";
|
|
10252
|
+
text += `Tokens: ${delegateUsage.input.toLocaleString()} in, ${delegateUsage.output.toLocaleString()} out (${delegateUsage.totalTokens.toLocaleString()} total)${costStr}`;
|
|
9731
10253
|
}
|
|
9732
|
-
|
|
9733
|
-
lines.push("Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.");
|
|
9734
|
-
return lines.join("\n");
|
|
10254
|
+
return text;
|
|
9735
10255
|
}
|
|
9736
10256
|
|
|
9737
10257
|
// src/system-prompt.ts
|
|
@@ -10036,7 +10556,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
10036
10556
|
const data = await res.json();
|
|
10037
10557
|
const latest = data.version;
|
|
10038
10558
|
if (!latest) return;
|
|
10039
|
-
const current = runtimeVersion ?? "0.1.
|
|
10559
|
+
const current = runtimeVersion ?? "0.1.38";
|
|
10040
10560
|
const hasUpdate = isNewer(latest, current);
|
|
10041
10561
|
debug.event("update-check", {
|
|
10042
10562
|
current,
|
|
@@ -10287,7 +10807,7 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
10287
10807
|
resetDelegateUsage();
|
|
10288
10808
|
setDelegateDisplayUsage("separate");
|
|
10289
10809
|
const sid = ctx.sessionManager.getSessionId();
|
|
10290
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
10810
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.38" : null });
|
|
10291
10811
|
try {
|
|
10292
10812
|
const user = await loadUserConfig(ctx.cwd);
|
|
10293
10813
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -10327,17 +10847,17 @@ function wireContextTransform(pi, runtime) {
|
|
|
10327
10847
|
const config = runtime.configFor(ctx);
|
|
10328
10848
|
const coveredIds = collectCoveredMessageIds(state);
|
|
10329
10849
|
const realUsage = ctx.getContextUsage?.();
|
|
10330
|
-
const
|
|
10331
|
-
const
|
|
10850
|
+
const systemPromptText = getSystemPromptText(ctx);
|
|
10851
|
+
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
10852
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
10853
|
+
const tokenCount = sentTokens;
|
|
10332
10854
|
debug.event("context-in", {
|
|
10333
10855
|
sid,
|
|
10334
10856
|
eventMsgs: event.messages?.length ?? 0,
|
|
10335
10857
|
entries: entries.length,
|
|
10336
10858
|
coreMsgs: coreMessages.length,
|
|
10337
10859
|
tokenCount,
|
|
10338
|
-
|
|
10339
|
-
realTokens: realUsage?.tokens ?? null,
|
|
10340
|
-
realPercent: realUsage?.percent ?? null,
|
|
10860
|
+
sessionTokens: realUsage?.tokens ?? null,
|
|
10341
10861
|
limit: config.modelContextLimit,
|
|
10342
10862
|
blocksBefore: state.blocks.length,
|
|
10343
10863
|
activeBefore: state.blocks.filter((b) => b.active).length
|
|
@@ -10376,6 +10896,7 @@ function wireContextTransform(pi, runtime) {
|
|
|
10376
10896
|
const debugOn2 = debug.enabled;
|
|
10377
10897
|
if (turn.nudge?.shouldInject) {
|
|
10378
10898
|
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
10899
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
10379
10900
|
const turnKey = lastUserMessageId(entries) ?? sid;
|
|
10380
10901
|
const alreadyShown = !emergency && runtime.nudgeShownFor(turnKey);
|
|
10381
10902
|
if (!alreadyShown) {
|