billion-context-pi 0.1.38 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +33 -4
- package/dist/delegate-tool.d.ts +5 -0
- package/dist/density.d.ts +19 -0
- package/dist/index.js +379 -568
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +16 -1
- package/dist/tokens.d.ts +6 -0
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -76,6 +76,7 @@ function createInitialState() {
|
|
|
76
76
|
return {
|
|
77
77
|
blocks: [],
|
|
78
78
|
messageRefs: { byRaw: {}, byRef: {} },
|
|
79
|
+
tokenSnapshot: {},
|
|
79
80
|
nudge: {
|
|
80
81
|
lastPerMessageNudgeTokens: 0,
|
|
81
82
|
lastNudgeShownTokens: 0,
|
|
@@ -245,11 +246,23 @@ function syncBlocks(messages, state) {
|
|
|
245
246
|
byRaw: { ...state.messageRefs.byRaw },
|
|
246
247
|
byRef: { ...state.messageRefs.byRef }
|
|
247
248
|
},
|
|
249
|
+
// Snapshot is keyed by ref with primitive values — shallow copy suffices.
|
|
250
|
+
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
248
251
|
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
249
252
|
stats: { ...state.stats },
|
|
250
253
|
nextBlockId: state.nextBlockId,
|
|
251
254
|
nextRunId: state.nextRunId
|
|
252
255
|
};
|
|
256
|
+
const liveRefs = new Set(
|
|
257
|
+
messages.map((m) => result.messageRefs.byRaw[m.id]).filter((r) => typeof r === "string")
|
|
258
|
+
);
|
|
259
|
+
if (Object.keys(result.tokenSnapshot).length !== liveRefs.size) {
|
|
260
|
+
const pruned = {};
|
|
261
|
+
for (const [ref, n] of Object.entries(result.tokenSnapshot)) {
|
|
262
|
+
if (liveRefs.has(ref)) pruned[ref] = n;
|
|
263
|
+
}
|
|
264
|
+
result.tokenSnapshot = pruned;
|
|
265
|
+
}
|
|
253
266
|
const consumedBlockIds = /* @__PURE__ */ new Set();
|
|
254
267
|
for (const block of result.blocks) {
|
|
255
268
|
for (const consumedId of block.directBlockIds) {
|
|
@@ -724,7 +737,7 @@ var TAG_CLOSE = LT + "/acp" + GT;
|
|
|
724
737
|
function acpTag(ref, tokens, type) {
|
|
725
738
|
return TAG_OPEN + 'tokens="' + formatTokens(tokens) + '" type="' + type + '"' + GT + ref + TAG_CLOSE;
|
|
726
739
|
}
|
|
727
|
-
function renderMessage(message, map, countTokens, strategy) {
|
|
740
|
+
function renderMessage(message, map, countTokens, strategy, snapshot = null) {
|
|
728
741
|
const ref = refForRaw(map, message.id);
|
|
729
742
|
if (!ref || ref === BLOCKED_REF) return message;
|
|
730
743
|
if (strategy === "none") return message;
|
|
@@ -735,26 +748,33 @@ function renderMessage(message, map, countTokens, strategy) {
|
|
|
735
748
|
"^" + escapeRegex(TAG_OPEN) + "[^>]*" + GT + escapeRegex(ref) + escapeRegex(TAG_CLOSE) + "\\n?"
|
|
736
749
|
);
|
|
737
750
|
const cleanText = (message.text || "").replace(ownTagRe, "");
|
|
738
|
-
const tokens = countTokens(cleanText);
|
|
751
|
+
const tokens = snapshot ? snapshot[ref] ?? (snapshot[ref] = countTokens(cleanText)) : countTokens(cleanText);
|
|
739
752
|
const type = classifyType(message);
|
|
740
753
|
const prefix = acpTag(ref, tokens, type) + "\n";
|
|
741
754
|
if (!cleanText) return { ...message, text: prefix };
|
|
742
755
|
return { ...message, text: prefix + cleanText };
|
|
743
756
|
}
|
|
744
|
-
function
|
|
757
|
+
function renderWithSnapshot(messages, state, countTokens = (text) => Math.ceil(text.length / 4), strategy = "all") {
|
|
745
758
|
const map = state.messageRefs;
|
|
746
|
-
|
|
747
|
-
|
|
759
|
+
const snapshot = { ...state.tokenSnapshot ?? {} };
|
|
760
|
+
const rendered = messages.map(
|
|
761
|
+
(message) => renderMessage(message, map, countTokens, strategy, snapshot)
|
|
748
762
|
);
|
|
763
|
+
return { messages: rendered, tokenSnapshot: snapshot };
|
|
749
764
|
}
|
|
750
765
|
function createRenderRefsNode(strategy) {
|
|
751
766
|
return {
|
|
752
767
|
name: "render-refs",
|
|
753
768
|
run(io, ctx) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
769
|
+
const { messages, tokenSnapshot } = renderWithSnapshot(
|
|
770
|
+
io.messages,
|
|
771
|
+
io.state,
|
|
772
|
+
ctx.countTokens,
|
|
773
|
+
strategy
|
|
774
|
+
);
|
|
775
|
+
const prev = io.state.tokenSnapshot;
|
|
776
|
+
const changed = !prev || Object.keys(tokenSnapshot).length !== Object.keys(prev).length;
|
|
777
|
+
return changed ? { ...io, messages, state: { ...io.state, tokenSnapshot } } : { ...io, messages };
|
|
758
778
|
}
|
|
759
779
|
};
|
|
760
780
|
}
|
|
@@ -950,6 +970,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
950
970
|
ref,
|
|
951
971
|
refNum: rn,
|
|
952
972
|
tokens: countTokens(msg.text ?? ""),
|
|
973
|
+
chars: (msg.text ?? "").length,
|
|
953
974
|
isTool: isToolMessage(msg),
|
|
954
975
|
isUser: msg.role === "user"
|
|
955
976
|
});
|
|
@@ -970,6 +991,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
970
991
|
endRef: info.ref,
|
|
971
992
|
count: 1,
|
|
972
993
|
tokens: info.tokens,
|
|
994
|
+
chars: info.chars,
|
|
973
995
|
toolPct: info.isTool ? 100 : 0,
|
|
974
996
|
textPct: info.isTool ? 0 : 100
|
|
975
997
|
};
|
|
@@ -977,6 +999,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
|
|
|
977
999
|
cur.endRef = info.ref;
|
|
978
1000
|
cur.count++;
|
|
979
1001
|
cur.tokens += info.tokens;
|
|
1002
|
+
cur.chars = (cur.chars ?? 0) + info.chars;
|
|
980
1003
|
if (info.isTool) {
|
|
981
1004
|
cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
|
|
982
1005
|
} else {
|
|
@@ -1024,6 +1047,7 @@ function mergeBatch(batch) {
|
|
|
1024
1047
|
const last = batch[batch.length - 1];
|
|
1025
1048
|
const count = batch.reduce((s, r) => s + r.count, 0);
|
|
1026
1049
|
const tokens = batch.reduce((s, r) => s + r.tokens, 0);
|
|
1050
|
+
const chars = batch.reduce((s, r) => s + rangeChars(r), 0);
|
|
1027
1051
|
const toolPct = Math.round(
|
|
1028
1052
|
batch.reduce((s, r) => s + r.toolPct * r.count, 0) / count
|
|
1029
1053
|
);
|
|
@@ -1032,6 +1056,7 @@ function mergeBatch(batch) {
|
|
|
1032
1056
|
endRef: last.endRef,
|
|
1033
1057
|
count,
|
|
1034
1058
|
tokens,
|
|
1059
|
+
chars,
|
|
1035
1060
|
toolPct,
|
|
1036
1061
|
textPct: 100 - toolPct
|
|
1037
1062
|
};
|
|
@@ -1040,16 +1065,21 @@ function mergeBatch(batch) {
|
|
|
1040
1065
|
}
|
|
1041
1066
|
return merged;
|
|
1042
1067
|
}
|
|
1068
|
+
function rangeChars(r) {
|
|
1069
|
+
return r.chars ?? r.tokens * 4;
|
|
1070
|
+
}
|
|
1043
1071
|
function mergeRangesToThreshold(ranges, minChars) {
|
|
1044
1072
|
if (minChars <= 0 || ranges.length === 0) return ranges;
|
|
1045
1073
|
const result = [];
|
|
1046
1074
|
let batch = [];
|
|
1075
|
+
let batchChars = 0;
|
|
1047
1076
|
for (const r of ranges) {
|
|
1048
1077
|
batch.push(r);
|
|
1049
|
-
|
|
1050
|
-
if (
|
|
1078
|
+
batchChars += rangeChars(r);
|
|
1079
|
+
if (batchChars >= minChars) {
|
|
1051
1080
|
result.push(mergeBatch(batch));
|
|
1052
1081
|
batch = [];
|
|
1082
|
+
batchChars = 0;
|
|
1053
1083
|
}
|
|
1054
1084
|
}
|
|
1055
1085
|
if (batch.length > 0) {
|
|
@@ -1637,7 +1667,7 @@ function resolveAdaptiveGrowth(modelContextLimit, nudge) {
|
|
|
1637
1667
|
function pendingByTier(state, recommendation, countTokens, minCompressRange) {
|
|
1638
1668
|
const out = {};
|
|
1639
1669
|
const merged = recommendation?.recommendedRanges ?? [];
|
|
1640
|
-
const effective = minCompressRange > 0 ? merged.filter((r) => r.tokens * 4 >= minCompressRange) : merged;
|
|
1670
|
+
const effective = minCompressRange > 0 ? merged.filter((r) => (r.chars ?? r.tokens * 4) >= minCompressRange) : merged;
|
|
1641
1671
|
out[1] = { pending: effective.reduce((s, r) => s + r.tokens, 0), targetBlocks: [] };
|
|
1642
1672
|
const active = activeBlocks(state);
|
|
1643
1673
|
const t1 = active.filter((b) => b.tier === 1);
|
|
@@ -1799,6 +1829,7 @@ function cloneState(state) {
|
|
|
1799
1829
|
byRaw: { ...state.messageRefs.byRaw },
|
|
1800
1830
|
byRef: { ...state.messageRefs.byRef }
|
|
1801
1831
|
},
|
|
1832
|
+
tokenSnapshot: { ...state.tokenSnapshot ?? {} },
|
|
1802
1833
|
nudge: { ...state.nudge, anchors: { ...state.nudge.anchors } },
|
|
1803
1834
|
stats: { ...state.stats },
|
|
1804
1835
|
nextBlockId: state.nextBlockId,
|
|
@@ -2464,9 +2495,9 @@ function charBigrams(text) {
|
|
|
2464
2495
|
}
|
|
2465
2496
|
return grams;
|
|
2466
2497
|
}
|
|
2467
|
-
function tfMap(text,
|
|
2498
|
+
function tfMap(text, stem2) {
|
|
2468
2499
|
const m = /* @__PURE__ */ new Map();
|
|
2469
|
-
for (const t of tokenize(text, { stem:
|
|
2500
|
+
for (const t of tokenize(text, { stem: stem2 })) m.set(t, (m.get(t) ?? 0) + 1);
|
|
2470
2501
|
return m;
|
|
2471
2502
|
}
|
|
2472
2503
|
var bm25Algorithm = {
|
|
@@ -2671,7 +2702,20 @@ function resolveDelegate(adapter) {
|
|
|
2671
2702
|
displayUsage: adapter.displayUsage ?? "separate"
|
|
2672
2703
|
};
|
|
2673
2704
|
}
|
|
2674
|
-
function
|
|
2705
|
+
function mergeCompress(global, provider, model) {
|
|
2706
|
+
return {
|
|
2707
|
+
maxContextLimit: model?.maxContextLimit ?? provider?.maxContextLimit ?? global?.maxContextLimit,
|
|
2708
|
+
emergencyThresholdPercent: model?.emergencyThresholdPercent ?? provider?.emergencyThresholdPercent ?? global?.emergencyThresholdPercent,
|
|
2709
|
+
nudgeGrowthTokens: model?.nudgeGrowthTokens ?? provider?.nudgeGrowthTokens ?? global?.nudgeGrowthTokens
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
function resolveCompress(compress, provider, modelId) {
|
|
2713
|
+
if (!compress) return {};
|
|
2714
|
+
const prov = provider ? compress.providers?.[provider] : void 0;
|
|
2715
|
+
const model = prov && modelId ? prov.models?.[modelId] : void 0;
|
|
2716
|
+
return mergeCompress(compress, prov, model);
|
|
2717
|
+
}
|
|
2718
|
+
function resolveConfig(adapter, liveContextLimit, provider, modelId) {
|
|
2675
2719
|
const envLimit = process.env.ACP_MODEL_CONTEXT_LIMIT;
|
|
2676
2720
|
const envLimitNum = envLimit ? Number(envLimit) : NaN;
|
|
2677
2721
|
const FALLBACK_LIMIT = 15e4;
|
|
@@ -2681,14 +2725,14 @@ function resolveConfig(adapter, liveContextLimit) {
|
|
|
2681
2725
|
preserveRecentMessages: adapter.preserveRecentMessages ?? 5,
|
|
2682
2726
|
...adapter.coreOverrides
|
|
2683
2727
|
});
|
|
2684
|
-
const c = adapter.compress;
|
|
2685
|
-
if (c
|
|
2686
|
-
if (c
|
|
2728
|
+
const c = resolveCompress(adapter.compress, provider, modelId);
|
|
2729
|
+
if (c.maxContextLimit !== void 0) config.nudge.maxContextLimitPct = parsePercent(c.maxContextLimit);
|
|
2730
|
+
if (c.emergencyThresholdPercent !== void 0) {
|
|
2687
2731
|
const pct2 = parsePercent(c.emergencyThresholdPercent);
|
|
2688
2732
|
config.nudge.emergencyThresholdPct = pct2;
|
|
2689
2733
|
config.truncate.threshold = pct2;
|
|
2690
2734
|
}
|
|
2691
|
-
if (c
|
|
2735
|
+
if (c.nudgeGrowthTokens !== void 0) {
|
|
2692
2736
|
config.nudge.growthFloor = c.nudgeGrowthTokens;
|
|
2693
2737
|
config.nudge.growthCap = c.nudgeGrowthTokens;
|
|
2694
2738
|
}
|
|
@@ -2701,6 +2745,89 @@ function parsePercent(v) {
|
|
|
2701
2745
|
return Number(s);
|
|
2702
2746
|
}
|
|
2703
2747
|
|
|
2748
|
+
// src/density.ts
|
|
2749
|
+
var DENSITY_MIN = 0.5;
|
|
2750
|
+
var DENSITY_MAX = 2.5;
|
|
2751
|
+
var MIN_DELTA_EST = 50;
|
|
2752
|
+
var CONFIRM_RATIO = 0.2;
|
|
2753
|
+
var INITIAL_DENSITY = 1;
|
|
2754
|
+
var DensityEstimator = class {
|
|
2755
|
+
models = /* @__PURE__ */ new Map();
|
|
2756
|
+
/** 重置指定模型(模型切换/会话开始时调用)。 */
|
|
2757
|
+
resetModel(modelId) {
|
|
2758
|
+
this.models.delete(modelId);
|
|
2759
|
+
}
|
|
2760
|
+
/** 返回当前密度系数(未知模型返回初始 1)。 */
|
|
2761
|
+
densityFor(modelId) {
|
|
2762
|
+
return this.models.get(modelId)?.density ?? INITIAL_DENSITY;
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* 每轮 context 事件调用。realTotal 为 provider 真实 usage(可空),
|
|
2766
|
+
* estTotal 为本地估算总 token。postCompression 为压缩刚发生标志。
|
|
2767
|
+
*/
|
|
2768
|
+
update(modelId, realTotal, estTotal, postCompression = false) {
|
|
2769
|
+
if (realTotal === null) return;
|
|
2770
|
+
let est = this.models.get(modelId);
|
|
2771
|
+
if (!est) {
|
|
2772
|
+
est = {
|
|
2773
|
+
density: INITIAL_DENSITY,
|
|
2774
|
+
anchorReal: null,
|
|
2775
|
+
anchorEst: null,
|
|
2776
|
+
pendingDensity: null,
|
|
2777
|
+
confirmCount: 0,
|
|
2778
|
+
postCompressionSkip: false
|
|
2779
|
+
};
|
|
2780
|
+
this.models.set(modelId, est);
|
|
2781
|
+
}
|
|
2782
|
+
if (postCompression) {
|
|
2783
|
+
est.postCompressionSkip = true;
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
if (est.postCompressionSkip) {
|
|
2787
|
+
est.postCompressionSkip = false;
|
|
2788
|
+
est.anchorReal = realTotal;
|
|
2789
|
+
est.anchorEst = estTotal;
|
|
2790
|
+
est.pendingDensity = null;
|
|
2791
|
+
est.confirmCount = 0;
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (est.anchorReal === null || est.anchorEst === null) {
|
|
2795
|
+
est.anchorReal = realTotal;
|
|
2796
|
+
est.anchorEst = estTotal;
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
const dReal = realTotal - est.anchorReal;
|
|
2800
|
+
const dEst = estTotal - est.anchorEst;
|
|
2801
|
+
if (dEst < MIN_DELTA_EST) return;
|
|
2802
|
+
est.anchorReal = realTotal;
|
|
2803
|
+
est.anchorEst = estTotal;
|
|
2804
|
+
const instant = clamp(dReal / dEst, DENSITY_MIN, DENSITY_MAX);
|
|
2805
|
+
if (est.pendingDensity === null) {
|
|
2806
|
+
est.pendingDensity = instant;
|
|
2807
|
+
est.confirmCount = 1;
|
|
2808
|
+
} else if (Math.abs(instant - est.pendingDensity) / est.pendingDensity <= CONFIRM_RATIO) {
|
|
2809
|
+
est.confirmCount += 1;
|
|
2810
|
+
} else {
|
|
2811
|
+
est.pendingDensity = instant;
|
|
2812
|
+
est.confirmCount = 1;
|
|
2813
|
+
}
|
|
2814
|
+
if (est.confirmCount >= 2) {
|
|
2815
|
+
est.density = est.pendingDensity;
|
|
2816
|
+
est.confirmCount = 0;
|
|
2817
|
+
est.pendingDensity = null;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
/** 注入器:估算文本 token = defaultCountTokens × density。 */
|
|
2821
|
+
estimateWithDensity(modelId, text) {
|
|
2822
|
+
const d = this.densityFor(modelId);
|
|
2823
|
+
if (d === 1) return defaultCountTokens(text);
|
|
2824
|
+
return Math.round(defaultCountTokens(text) * d);
|
|
2825
|
+
}
|
|
2826
|
+
};
|
|
2827
|
+
function clamp(v, lo, hi) {
|
|
2828
|
+
return v < lo ? lo : v > hi ? hi : v;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2704
2831
|
// src/messages.ts
|
|
2705
2832
|
var REF_TAG_SOURCE = "(?:<acp\\s[^>]*>m\\d{5}</acp>|\\[m\\d{1,5}\\])";
|
|
2706
2833
|
var REF_TAG = new RegExp(`^${REF_TAG_SOURCE}\\s?\\n?`);
|
|
@@ -3206,6 +3333,7 @@ function mergeInitialState(parsed) {
|
|
|
3206
3333
|
return {
|
|
3207
3334
|
blocks: parsed.blocks ?? fresh.blocks,
|
|
3208
3335
|
messageRefs: parsed.messageRefs ?? fresh.messageRefs,
|
|
3336
|
+
tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot,
|
|
3209
3337
|
nudge: { ...fresh.nudge, ...parsed.nudge ?? {} },
|
|
3210
3338
|
stats: { ...fresh.stats, ...parsed.stats ?? {} },
|
|
3211
3339
|
nextBlockId: parsed.nextBlockId ?? fresh.nextBlockId,
|
|
@@ -3213,6 +3341,64 @@ function mergeInitialState(parsed) {
|
|
|
3213
3341
|
};
|
|
3214
3342
|
}
|
|
3215
3343
|
|
|
3344
|
+
// src/user-config.ts
|
|
3345
|
+
import { promises as fs2 } from "fs";
|
|
3346
|
+
import * as path3 from "path";
|
|
3347
|
+
import { homedir as homedir2 } from "os";
|
|
3348
|
+
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME2 } from "@earendil-works/pi-coding-agent";
|
|
3349
|
+
async function loadUserConfig(cwd) {
|
|
3350
|
+
const home = homedir2();
|
|
3351
|
+
const merged = {};
|
|
3352
|
+
for (const base of [join4(home, CONFIG_DIR_NAME2), join4(cwd, CONFIG_DIR_NAME2)]) {
|
|
3353
|
+
const file = join4(base, "acp.json");
|
|
3354
|
+
try {
|
|
3355
|
+
const raw = await fs2.readFile(file, "utf8");
|
|
3356
|
+
const parsed = JSON.parse(raw);
|
|
3357
|
+
if (parsed && typeof parsed === "object") {
|
|
3358
|
+
Object.assign(merged, pickKnown(parsed));
|
|
3359
|
+
debug.event("config-loaded", { file });
|
|
3360
|
+
}
|
|
3361
|
+
} catch (e) {
|
|
3362
|
+
const code = e.code;
|
|
3363
|
+
if (code !== "ENOENT") {
|
|
3364
|
+
logWarn("config", { event: "load-failed", file, error: e instanceof Error ? e.message : String(e) });
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
return merged;
|
|
3369
|
+
}
|
|
3370
|
+
function join4(...parts) {
|
|
3371
|
+
return path3.join(...parts);
|
|
3372
|
+
}
|
|
3373
|
+
var KNOWN = /* @__PURE__ */ new Set([
|
|
3374
|
+
"debug",
|
|
3375
|
+
"autoUpdate",
|
|
3376
|
+
"modelContextLimit",
|
|
3377
|
+
"toolBashDefaultTimeout",
|
|
3378
|
+
"toolOutputMaxBytes",
|
|
3379
|
+
"delegate",
|
|
3380
|
+
"compress",
|
|
3381
|
+
"displayUsage",
|
|
3382
|
+
"prompts",
|
|
3383
|
+
"acknowledgePromptsRisk"
|
|
3384
|
+
]);
|
|
3385
|
+
function pickKnown(parsed) {
|
|
3386
|
+
const out = {};
|
|
3387
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
3388
|
+
if (KNOWN.has(k)) out[k] = v;
|
|
3389
|
+
}
|
|
3390
|
+
return out;
|
|
3391
|
+
}
|
|
3392
|
+
function applyUserConfig(adapter, user) {
|
|
3393
|
+
return {
|
|
3394
|
+
...adapter,
|
|
3395
|
+
...user,
|
|
3396
|
+
coreOverrides: adapter.coreOverrides,
|
|
3397
|
+
protectedTools: adapter.protectedTools,
|
|
3398
|
+
preserveRecentMessages: adapter.preserveRecentMessages
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3216
3402
|
// src/sequence-match.ts
|
|
3217
3403
|
function findUniqueLongestRun(candidates, live) {
|
|
3218
3404
|
if (candidates.length === 0 || live.length === 0) return void 0;
|
|
@@ -3428,10 +3614,18 @@ function pruneOrphanRefs(state, messages) {
|
|
|
3428
3614
|
}
|
|
3429
3615
|
}
|
|
3430
3616
|
function createRuntime(adapter) {
|
|
3431
|
-
const
|
|
3617
|
+
const density = new DensityEstimator();
|
|
3618
|
+
let countModelId = "default";
|
|
3619
|
+
const core = createCore({
|
|
3620
|
+
// 密度校准版 countTokens(Phase 2):默认回落 defaultCountTokens(density=1)
|
|
3621
|
+
countTokens: (text) => density.estimateWithDensity(countModelId, text)
|
|
3622
|
+
});
|
|
3432
3623
|
const store = new SessionStateStore();
|
|
3624
|
+
const lastActiveBlockIds = /* @__PURE__ */ new Map();
|
|
3433
3625
|
const locks = /* @__PURE__ */ new Map();
|
|
3626
|
+
const factoryAdapter = adapter;
|
|
3434
3627
|
let adapterRef = adapter;
|
|
3628
|
+
let lastUserConfigKey;
|
|
3435
3629
|
let promptsRef = defaultPrompts;
|
|
3436
3630
|
const nudgeShownTurns = /* @__PURE__ */ new Set();
|
|
3437
3631
|
async function acquireLock(sid) {
|
|
@@ -3454,7 +3648,27 @@ function createRuntime(adapter) {
|
|
|
3454
3648
|
return m?.contextWindow ?? 0;
|
|
3455
3649
|
}
|
|
3456
3650
|
function configFor(ctx) {
|
|
3457
|
-
|
|
3651
|
+
const m = ctx.model;
|
|
3652
|
+
return resolveConfig(adapterRef, liveContextLimit(ctx), m?.provider, m?.id);
|
|
3653
|
+
}
|
|
3654
|
+
async function reloadConfig(cwd) {
|
|
3655
|
+
let user;
|
|
3656
|
+
try {
|
|
3657
|
+
user = await loadUserConfig(cwd);
|
|
3658
|
+
} catch (e) {
|
|
3659
|
+
logWarn("runtime", { event: "config-reload-failed", error: e instanceof Error ? e.message : String(e) });
|
|
3660
|
+
return;
|
|
3661
|
+
}
|
|
3662
|
+
try {
|
|
3663
|
+
const key = JSON.stringify(user);
|
|
3664
|
+
if (key === lastUserConfigKey) return;
|
|
3665
|
+
lastUserConfigKey = key;
|
|
3666
|
+
adapterRef = applyUserConfig(factoryAdapter, user);
|
|
3667
|
+
if (adapterRef.debug !== void 0) setDebugEnabled(adapterRef.debug);
|
|
3668
|
+
logInfo("runtime", { event: "config-reloaded", limit: adapterRef.modelContextLimit ?? null });
|
|
3669
|
+
} catch (e) {
|
|
3670
|
+
logWarn("runtime", { event: "config-reload-failed", error: e instanceof Error ? e.message : String(e) });
|
|
3671
|
+
}
|
|
3458
3672
|
}
|
|
3459
3673
|
async function stateFor(ctx, liveMessages) {
|
|
3460
3674
|
const sm = ctx.sessionManager;
|
|
@@ -3477,10 +3691,20 @@ function createRuntime(adapter) {
|
|
|
3477
3691
|
const sm = ctx.sessionManager;
|
|
3478
3692
|
await store.save(state, sm.getSessionFile() ?? void 0, sm.getSessionId());
|
|
3479
3693
|
}
|
|
3480
|
-
|
|
3694
|
+
function noteActiveBlocks(sid, activeBlockIds) {
|
|
3695
|
+
const current = new Set(activeBlockIds);
|
|
3696
|
+
const prev = lastActiveBlockIds.get(sid);
|
|
3697
|
+
const isNew = prev !== void 0 && activeBlockIds.some((id) => !prev.has(id));
|
|
3698
|
+
lastActiveBlockIds.set(sid, current);
|
|
3699
|
+
return isNew;
|
|
3700
|
+
}
|
|
3701
|
+
function clearSessionTracking(sid) {
|
|
3702
|
+
lastActiveBlockIds.delete(sid);
|
|
3703
|
+
}
|
|
3704
|
+
return { core, store, density, setCountModel: (m) => {
|
|
3705
|
+
countModelId = m;
|
|
3706
|
+
}, noteActiveBlocks, clearSessionTracking, get adapter() {
|
|
3481
3707
|
return adapterRef;
|
|
3482
|
-
}, setAdapter: (a) => {
|
|
3483
|
-
adapterRef = a;
|
|
3484
3708
|
}, get prompts() {
|
|
3485
3709
|
return promptsRef;
|
|
3486
3710
|
}, setPrompts: (p) => {
|
|
@@ -3489,7 +3713,7 @@ function createRuntime(adapter) {
|
|
|
3489
3713
|
nudgeShownTurns.add(k);
|
|
3490
3714
|
}, nudgeShownFor: (k) => nudgeShownTurns.has(k), clearNudgeTracking: () => {
|
|
3491
3715
|
nudgeShownTurns.clear();
|
|
3492
|
-
}, liveContextLimit, configFor, stateFor, save, acquireLock };
|
|
3716
|
+
}, liveContextLimit, configFor, reloadConfig, stateFor, save, acquireLock };
|
|
3493
3717
|
}
|
|
3494
3718
|
|
|
3495
3719
|
// node_modules/typebox/build/system/memory/memory.mjs
|
|
@@ -7904,6 +8128,9 @@ function estimateTokens(messages, coveredIds) {
|
|
|
7904
8128
|
}
|
|
7905
8129
|
return tokens;
|
|
7906
8130
|
}
|
|
8131
|
+
function calibrateTokens(estimate, density) {
|
|
8132
|
+
return density === 1 ? estimate : Math.round(estimate * density);
|
|
8133
|
+
}
|
|
7907
8134
|
function lastUserMessageId(entries) {
|
|
7908
8135
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
7909
8136
|
const e = entries[i];
|
|
@@ -7974,6 +8201,7 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
7974
8201
|
if (ranges.length === 0) return "No ranges provided.";
|
|
7975
8202
|
const { state: initialState, coreMessages } = await runtime.stateFor(ctx);
|
|
7976
8203
|
const config = runtime.configFor(ctx);
|
|
8204
|
+
const modelId = ctx.model?.id ?? "default";
|
|
7977
8205
|
const systemPromptText = getSystemPromptText(ctx);
|
|
7978
8206
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
7979
8207
|
const sentTokens = estimateTokens(coreMessages, collectCoveredMessageIds(initialState)) + systemPromptTokens;
|
|
@@ -7981,15 +8209,18 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
7981
8209
|
messages: coreMessages,
|
|
7982
8210
|
state: initialState,
|
|
7983
8211
|
config,
|
|
7984
|
-
tokenCount: sentTokens
|
|
8212
|
+
tokenCount: calibrateTokens(sentTokens, runtime.density.densityFor(modelId))
|
|
7985
8213
|
});
|
|
7986
8214
|
const state = turn.state;
|
|
7987
8215
|
const messages = turn.messages;
|
|
7988
|
-
const
|
|
8216
|
+
const density = runtime.density.densityFor(modelId);
|
|
8217
|
+
const beforeTokens = calibrateTokens(estimateTokens(messages, collectCoveredMessageIds(state)), density);
|
|
7989
8218
|
const summaryMaxChars = args.summaryMaxChars;
|
|
7990
8219
|
const topLevelTopic = args.topic;
|
|
7991
8220
|
debug.event("compress-in", {
|
|
7992
8221
|
sid: ctx.sessionManager.getSessionId(),
|
|
8222
|
+
modelId,
|
|
8223
|
+
density,
|
|
7993
8224
|
ranges: ranges.length,
|
|
7994
8225
|
spans: ranges.map((r) => ({ span: `${r.startId}..${r.endId}`, summaryLen: r.summary.length, summary: r.summary, topic: r.topic ?? topLevelTopic ?? null })),
|
|
7995
8226
|
blocksBefore: state.blocks.length,
|
|
@@ -8005,7 +8236,14 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
8005
8236
|
});
|
|
8006
8237
|
await runtime.save(applied.state, ctx);
|
|
8007
8238
|
const { blocksCreated, tokensCompressed, errors, warnings } = applied.result;
|
|
8008
|
-
const
|
|
8239
|
+
const afterTurn = runtime.core.processTurn({
|
|
8240
|
+
messages: coreMessages,
|
|
8241
|
+
state: applied.state,
|
|
8242
|
+
config,
|
|
8243
|
+
tokenCount: calibrateTokens(sentTokens, density)
|
|
8244
|
+
});
|
|
8245
|
+
const afterTokens = calibrateTokens(estimateTokens(afterTurn.messages, collectCoveredMessageIds(applied.state)), density);
|
|
8246
|
+
const reclaimed = Math.max(0, beforeTokens - afterTokens);
|
|
8009
8247
|
const newBlocks = applied.state.blocks.slice(-blocksCreated);
|
|
8010
8248
|
debug.event("compress-out", {
|
|
8011
8249
|
sid: ctx.sessionManager.getSessionId(),
|
|
@@ -8036,9 +8274,9 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
8036
8274
|
logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "errors", count: errors.length, errors: errors.slice(0, 5) });
|
|
8037
8275
|
}
|
|
8038
8276
|
if (warnings.length > 0) {
|
|
8039
|
-
|
|
8277
|
+
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "warnings", count: warnings.length, warnings: warnings.slice(0, 5) });
|
|
8040
8278
|
}
|
|
8041
|
-
const lines = [`\u25A3 ACP | ${formatK2(beforeTokens)} \u2192 ${formatK2(afterTokens)} tokens (~${formatK2(
|
|
8279
|
+
const lines = [`\u25A3 ACP | ${formatK2(beforeTokens)} \u2192 ${formatK2(afterTokens)} tokens (~${formatK2(reclaimed)} reclaimed, ${blocksCreated} block${blocksCreated > 1 ? "s" : ""})`];
|
|
8042
8280
|
if (warnings.length > 0) lines.push("\u26A0\uFE0F " + warnings.join("; "));
|
|
8043
8281
|
if (errors.length > 0) lines.push("Errors: " + errors.join("; "));
|
|
8044
8282
|
return lines.join("\n");
|
|
@@ -8046,9 +8284,10 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
8046
8284
|
|
|
8047
8285
|
// src/decompress-tool.ts
|
|
8048
8286
|
import { writeFile, mkdir } from "fs/promises";
|
|
8049
|
-
import {
|
|
8050
|
-
import {
|
|
8051
|
-
|
|
8287
|
+
import { existsSync as existsSync2, lstatSync, readlinkSync, realpathSync } from "fs";
|
|
8288
|
+
import { resolve, relative, isAbsolute, join as join5, basename as basename2, dirname as dirname3 } from "path";
|
|
8289
|
+
import { tmpdir, homedir as homedir3 } from "os";
|
|
8290
|
+
var AUTO_DIR = join5(homedir3() || tmpdir(), ".cache", "pi", "acp-decompress");
|
|
8052
8291
|
var PREVIEW_CHARS = 600;
|
|
8053
8292
|
var MESSAGE_INLINE_THRESHOLD = 2e3;
|
|
8054
8293
|
var DecompressParams = typebox_exports.Object({
|
|
@@ -8084,23 +8323,48 @@ function makeDecompressTool(runtime) {
|
|
|
8084
8323
|
}
|
|
8085
8324
|
var ALLOWED_DIRS = [
|
|
8086
8325
|
tmpdir(),
|
|
8087
|
-
|
|
8088
|
-
|
|
8326
|
+
join5(homedir3(), ".cache", "opencode"),
|
|
8327
|
+
join5(homedir3(), ".cache", "pi")
|
|
8089
8328
|
];
|
|
8090
8329
|
function resolveToFilePath(targetPath) {
|
|
8091
|
-
const expanded = targetPath.startsWith("~/") ?
|
|
8330
|
+
const expanded = targetPath.startsWith("~/") ? join5(homedir3(), targetPath.slice(2)) : targetPath;
|
|
8092
8331
|
const resolved = resolve(expanded);
|
|
8093
|
-
|
|
8094
|
-
|
|
8332
|
+
let probe = resolved;
|
|
8333
|
+
const suffix = [];
|
|
8334
|
+
while (!existsSync2(probe) && probe !== dirname3(probe)) {
|
|
8335
|
+
suffix.unshift(basename2(probe));
|
|
8336
|
+
probe = dirname3(probe);
|
|
8337
|
+
}
|
|
8338
|
+
const real = existsSync2(probe) ? realpathSync(probe) : probe;
|
|
8339
|
+
let checked = real;
|
|
8340
|
+
for (const part of suffix) {
|
|
8341
|
+
checked = join5(checked, part);
|
|
8342
|
+
try {
|
|
8343
|
+
if (lstatSync(checked).isSymbolicLink()) {
|
|
8344
|
+
const target = readlinkSync(checked);
|
|
8345
|
+
checked = isAbsolute(target) ? resolve(target) : resolve(dirname3(checked), target);
|
|
8346
|
+
}
|
|
8347
|
+
} catch {
|
|
8348
|
+
}
|
|
8349
|
+
}
|
|
8350
|
+
const allowed = ALLOWED_DIRS.map((d) => {
|
|
8351
|
+
try {
|
|
8352
|
+
return realpathSync(d);
|
|
8353
|
+
} catch {
|
|
8354
|
+
return d;
|
|
8355
|
+
}
|
|
8356
|
+
});
|
|
8357
|
+
const isAllowed = allowed.some((dir) => {
|
|
8358
|
+
const rel = relative(dir, checked);
|
|
8095
8359
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
8096
8360
|
});
|
|
8097
8361
|
if (!isAllowed) {
|
|
8098
8362
|
return { error: `Error: toFile path must be under ${tmpdir()}, ~/.cache/opencode, or ~/.cache/pi. Got: ${targetPath}` };
|
|
8099
8363
|
}
|
|
8100
|
-
return
|
|
8364
|
+
return checked;
|
|
8101
8365
|
}
|
|
8102
8366
|
function autoFilePath(blockId) {
|
|
8103
|
-
return
|
|
8367
|
+
return join5(AUTO_DIR, `${blockId}-${Date.now()}.txt`);
|
|
8104
8368
|
}
|
|
8105
8369
|
function headPreview(text) {
|
|
8106
8370
|
if (text.length <= PREVIEW_CHARS) return text;
|
|
@@ -8163,7 +8427,7 @@ ${text}`;
|
|
|
8163
8427
|
}
|
|
8164
8428
|
async function handleDecompress(args, runtime, ctx) {
|
|
8165
8429
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
8166
|
-
const arg = args.blockId.trim();
|
|
8430
|
+
const arg = (args.blockId ?? "").trim();
|
|
8167
8431
|
const owner = state.blocks.find((b) => b.effectiveMessageIds.includes(arg));
|
|
8168
8432
|
if (owner) {
|
|
8169
8433
|
return handleMessageRef(arg, owner.blockId, args, ctx);
|
|
@@ -8335,420 +8599,6 @@ function formatSize(tokens) {
|
|
|
8335
8599
|
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
8336
8600
|
}
|
|
8337
8601
|
|
|
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;
|
|
8343
|
-
}
|
|
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);
|
|
8350
|
-
}
|
|
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";
|
|
8355
|
-
}
|
|
8356
|
-
function classifyType2(message) {
|
|
8357
|
-
if (message.contentType === "tool-call" || message.contentType === "tool-result") {
|
|
8358
|
-
return message.toolName || "tool";
|
|
8359
|
-
}
|
|
8360
|
-
return message.contentType;
|
|
8361
|
-
}
|
|
8362
|
-
function escapeRegex2(s) {
|
|
8363
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8364
|
-
}
|
|
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;
|
|
8371
|
-
}
|
|
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;
|
|
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 };
|
|
8388
|
-
}
|
|
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
|
-
);
|
|
8394
|
-
}
|
|
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
|
-
};
|
|
8403
|
-
}
|
|
8404
|
-
};
|
|
8405
|
-
}
|
|
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
|
|
8413
|
-
|
|
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
8602
|
// node_modules/billion-context-kit/dist/index.js
|
|
8753
8603
|
var VIABLE_RANGE_MIN_TOKENS = 200;
|
|
8754
8604
|
function viableRanges(ranges) {
|
|
@@ -8831,14 +8681,14 @@ function buildStatusPanel(input) {
|
|
|
8831
8681
|
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
8832
8682
|
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
8833
8683
|
lines.push("");
|
|
8834
|
-
lines.push(
|
|
8684
|
+
lines.push(formatRanges(ranges, protectedRanges));
|
|
8835
8685
|
}
|
|
8836
8686
|
if (activeBlocksList.length > 0) {
|
|
8837
8687
|
lines.push("");
|
|
8838
8688
|
lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmt2(state.stats.tokensCompressed)} tokens compressed)`);
|
|
8839
8689
|
for (const b of activeBlocksList) {
|
|
8840
8690
|
const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || "")}`;
|
|
8841
|
-
const summaryTok =
|
|
8691
|
+
const summaryTok = defaultCountTokens(b.summary || "");
|
|
8842
8692
|
const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;
|
|
8843
8693
|
lines.push(` [${b.blockId}] T${b.tier} ${fmt2(origTok)}\u2192${fmt2(summaryTok)}${topic}`);
|
|
8844
8694
|
}
|
|
@@ -8858,10 +8708,10 @@ function buildStatusPanel(input) {
|
|
|
8858
8708
|
import {
|
|
8859
8709
|
spawn
|
|
8860
8710
|
} from "child_process";
|
|
8861
|
-
import { createWriteStream, existsSync as
|
|
8711
|
+
import { createWriteStream, existsSync as existsSync3 } from "fs";
|
|
8862
8712
|
import { mkdir as mkdir2, mkdtemp, writeFile as writeFile2, rm, appendFile } from "fs/promises";
|
|
8863
8713
|
import { tmpdir as tmpdir2 } from "os";
|
|
8864
|
-
import { dirname as
|
|
8714
|
+
import { dirname as dirname4, join as join6, resolve as resolvePath } from "path";
|
|
8865
8715
|
|
|
8866
8716
|
// src/footer-status.ts
|
|
8867
8717
|
var FOOTER_STATUS_KEY = "billion-context-pi";
|
|
@@ -9265,7 +9115,7 @@ var IDLE_GRACE_MS = 5 * 6e4;
|
|
|
9265
9115
|
var ASYNC_TIMEOUT_MS = 30 * 6e4;
|
|
9266
9116
|
var KILL_GRACE_MS = 1e4;
|
|
9267
9117
|
var RESULT_SUMMARY_CHARS = 500;
|
|
9268
|
-
var OUT_DIR =
|
|
9118
|
+
var OUT_DIR = join6(tmpdir2(), "acp-delegate");
|
|
9269
9119
|
function delegateSpawnOptions(cwd, env) {
|
|
9270
9120
|
return {
|
|
9271
9121
|
cwd,
|
|
@@ -9275,13 +9125,13 @@ function delegateSpawnOptions(cwd, env) {
|
|
|
9275
9125
|
};
|
|
9276
9126
|
}
|
|
9277
9127
|
var PI_CLI_ENTRY_RE = /[\\/]pi-coding-agent[\\/]dist[\\/]cli\.js$/;
|
|
9278
|
-
var PI_PACKAGE_REL =
|
|
9128
|
+
var PI_PACKAGE_REL = join6("@earendil-works", "pi-coding-agent", "dist", "cli.js");
|
|
9279
9129
|
function probeUpFromArgv(argv1) {
|
|
9280
|
-
let dir = resolvePath(
|
|
9130
|
+
let dir = resolvePath(dirname4(argv1) || process.cwd());
|
|
9281
9131
|
for (; ; ) {
|
|
9282
|
-
const candidate =
|
|
9283
|
-
if (
|
|
9284
|
-
const parent =
|
|
9132
|
+
const candidate = join6(dir, "node_modules", PI_PACKAGE_REL);
|
|
9133
|
+
if (existsSync3(candidate)) return candidate;
|
|
9134
|
+
const parent = dirname4(dir);
|
|
9285
9135
|
if (parent === dir) return null;
|
|
9286
9136
|
dir = parent;
|
|
9287
9137
|
}
|
|
@@ -9289,12 +9139,12 @@ function probeUpFromArgv(argv1) {
|
|
|
9289
9139
|
function piCliGlobalCandidates(env) {
|
|
9290
9140
|
const candidates = [];
|
|
9291
9141
|
if (process.platform === "win32") {
|
|
9292
|
-
if (env.APPDATA) candidates.push(
|
|
9142
|
+
if (env.APPDATA) candidates.push(join6(env.APPDATA, "npm", "node_modules", PI_PACKAGE_REL));
|
|
9293
9143
|
} else {
|
|
9294
9144
|
const home = env.HOME ?? env.USERPROFILE;
|
|
9295
|
-
if (home) candidates.push(
|
|
9296
|
-
candidates.push(
|
|
9297
|
-
candidates.push(
|
|
9145
|
+
if (home) candidates.push(join6(home, ".local", "lib", "node_modules", PI_PACKAGE_REL));
|
|
9146
|
+
candidates.push(join6("/usr/local", "lib", "node_modules", PI_PACKAGE_REL));
|
|
9147
|
+
candidates.push(join6("/usr", "lib", "node_modules", PI_PACKAGE_REL));
|
|
9298
9148
|
}
|
|
9299
9149
|
return candidates;
|
|
9300
9150
|
}
|
|
@@ -9306,7 +9156,7 @@ function resolvePiCliEntry(argv1, env = process.env, piHost = true) {
|
|
|
9306
9156
|
const probed = probeUpFromArgv(argv1);
|
|
9307
9157
|
if (probed) return probed;
|
|
9308
9158
|
for (const candidate of piCliGlobalCandidates(env)) {
|
|
9309
|
-
if (
|
|
9159
|
+
if (existsSync3(candidate)) return candidate;
|
|
9310
9160
|
}
|
|
9311
9161
|
logWarn("delegate", { event: "cli-entry-unresolved", argv1, fallback: "argv[1]" });
|
|
9312
9162
|
}
|
|
@@ -9446,6 +9296,11 @@ function makeEventApplier(opts, writers) {
|
|
|
9446
9296
|
}
|
|
9447
9297
|
var WAIT_TIMEOUT_MS_DEFAULT = 1e4;
|
|
9448
9298
|
var WAIT_TIMEOUT_MS_MAX = 3e5;
|
|
9299
|
+
function resolveWaitTimeoutMs(raw) {
|
|
9300
|
+
if (raw === void 0) return WAIT_TIMEOUT_MS_DEFAULT;
|
|
9301
|
+
const ms = raw < 1e3 ? raw * 1e3 : raw;
|
|
9302
|
+
return Math.min(Math.max(ms, 1e3), WAIT_TIMEOUT_MS_MAX);
|
|
9303
|
+
}
|
|
9449
9304
|
var DelegateParams = typebox_exports.Object({
|
|
9450
9305
|
agent: typebox_exports.String({
|
|
9451
9306
|
description: `Role of the delegate. One of: ${AGENT_NAMES.join(", ")}. See tool description for what each does.`
|
|
@@ -9477,7 +9332,7 @@ var WaitParams = typebox_exports.Object({
|
|
|
9477
9332
|
runId: typebox_exports.String({ description: "The runId returned by acp_delegate to wait for." }),
|
|
9478
9333
|
timeout: typebox_exports.Optional(
|
|
9479
9334
|
typebox_exports.Integer({
|
|
9480
|
-
description: `Maximum
|
|
9335
|
+
description: `Maximum time to block waiting for the result, in milliseconds. Default ${WAIT_TIMEOUT_MS_DEFAULT} (10s); max ${WAIT_TIMEOUT_MS_MAX} (300s). Values below 1000 are treated as seconds (so 180 means 180s, not 180ms). If the delegate does not finish in time, returns "failed (not ready)" \u2014 do NOT keep waiting or retry; go do other work, and a completion notification will still be injected when it completes.`
|
|
9481
9336
|
})
|
|
9482
9337
|
)
|
|
9483
9338
|
});
|
|
@@ -9636,10 +9491,7 @@ function makeDelegateWaitTool(_pi) {
|
|
|
9636
9491
|
}
|
|
9637
9492
|
return buildWaitResult(run, formatRunResult(run), displayMode);
|
|
9638
9493
|
}
|
|
9639
|
-
const timeoutMs =
|
|
9640
|
-
Math.max(args.timeout ?? WAIT_TIMEOUT_MS_DEFAULT, 1e3),
|
|
9641
|
-
WAIT_TIMEOUT_MS_MAX
|
|
9642
|
-
);
|
|
9494
|
+
const timeoutMs = resolveWaitTimeoutMs(args.timeout);
|
|
9643
9495
|
if (run.waiter) {
|
|
9644
9496
|
return { details: void 0, content: [{ type: "text", text: `Delegate \`${args.runId}\` already has a wait in progress; do not wait on it twice.` }] };
|
|
9645
9497
|
}
|
|
@@ -9760,8 +9612,8 @@ async function runDelegate(pi, args, ctx, signal) {
|
|
|
9760
9612
|
},
|
|
9761
9613
|
{ eofGraceMs: EOF_GRACE_MS, idleMs: IDLE_GRACE_MS, timeoutMs: ASYNC_TIMEOUT_MS, killGraceMs: KILL_GRACE_MS }
|
|
9762
9614
|
);
|
|
9763
|
-
const replyFile =
|
|
9764
|
-
const activityFile =
|
|
9615
|
+
const replyFile = join6(OUT_DIR, `${runId}.out`);
|
|
9616
|
+
const activityFile = join6(OUT_DIR, `${runId}.activity`);
|
|
9765
9617
|
await mkdir2(OUT_DIR, { recursive: true });
|
|
9766
9618
|
const replyStream = createWriteStream(replyFile, { flags: "a" });
|
|
9767
9619
|
const activityStream = useJsonStream ? createWriteStream(activityFile, { flags: "a" }) : null;
|
|
@@ -9911,8 +9763,8 @@ async function runDelegate(pi, args, ctx, signal) {
|
|
|
9911
9763
|
return formatSyncResult(args.agent, runId, args.task, result, file);
|
|
9912
9764
|
}
|
|
9913
9765
|
async function buildChildArgs(args, rolePrompt, ctx) {
|
|
9914
|
-
const tmpDir = await mkdtemp(
|
|
9915
|
-
const promptFile =
|
|
9766
|
+
const tmpDir = await mkdtemp(join6(tmpdir2(), "acp-delegate-"));
|
|
9767
|
+
const promptFile = join6(tmpDir, "role.md");
|
|
9916
9768
|
await writeFile2(promptFile, `${rolePrompt}
|
|
9917
9769
|
|
|
9918
9770
|
---
|
|
@@ -10049,7 +9901,7 @@ async function persistResult(runId, body) {
|
|
|
10049
9901
|
await mkdir2(OUT_DIR, { recursive: true });
|
|
10050
9902
|
} catch {
|
|
10051
9903
|
}
|
|
10052
|
-
const file =
|
|
9904
|
+
const file = join6(OUT_DIR, `${runId}.out`);
|
|
10053
9905
|
try {
|
|
10054
9906
|
await writeFile2(file, body, "utf8");
|
|
10055
9907
|
return file;
|
|
@@ -10107,6 +9959,7 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
10107
9959
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
10108
9960
|
const config = runtime.configFor(ctx);
|
|
10109
9961
|
const coveredIds = collectCoveredMessageIds(state);
|
|
9962
|
+
const modelId = ctx.model?.id ?? "default";
|
|
10110
9963
|
const systemPromptText = getSystemPromptText(ctx);
|
|
10111
9964
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
10112
9965
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
@@ -10114,7 +9967,7 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
10114
9967
|
messages: coreMessages,
|
|
10115
9968
|
state,
|
|
10116
9969
|
config,
|
|
10117
|
-
tokenCount: sentTokens
|
|
9970
|
+
tokenCount: calibrateTokens(sentTokens, runtime.density.densityFor(modelId))
|
|
10118
9971
|
});
|
|
10119
9972
|
const processed = turn.messages;
|
|
10120
9973
|
const base = buildStatusReport(turn.state, processed, defaultCountTokens, {
|
|
@@ -10232,9 +10085,10 @@ async function statusReport(runtime, ctx) {
|
|
|
10232
10085
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
10233
10086
|
const sessionTokens = realUsage?.tokens && realUsage.tokens > 0 ? realUsage.tokens : defaultCountTokens(coreMessages.map((m) => m.text ?? "").join("\n"));
|
|
10234
10087
|
const coveredIds = collectCoveredMessageIds(state);
|
|
10088
|
+
const modelId = ctx.model?.id ?? "default";
|
|
10235
10089
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
10236
|
-
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
10237
|
-
const versionStr = "0.1.
|
|
10090
|
+
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: calibrateTokens(sentTokens, runtime.density.densityFor(modelId)) });
|
|
10091
|
+
const versionStr = "0.1.40" ? `billion-context-pi@${"0.1.40"}` : void 0;
|
|
10238
10092
|
let text = buildStatusPanel({
|
|
10239
10093
|
version: versionStr,
|
|
10240
10094
|
tokenCount: sessionTokens,
|
|
@@ -10447,16 +10301,16 @@ function wireToolGuardrails(pi, runtime) {
|
|
|
10447
10301
|
|
|
10448
10302
|
// src/update.ts
|
|
10449
10303
|
import { readFile, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
10450
|
-
import { join as
|
|
10304
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
10451
10305
|
import { fileURLToPath } from "url";
|
|
10452
10306
|
import { execFile } from "child_process";
|
|
10453
|
-
import { homedir as
|
|
10454
|
-
import { CONFIG_DIR_NAME as
|
|
10307
|
+
import { homedir as homedir4 } from "os";
|
|
10308
|
+
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@earendil-works/pi-coding-agent";
|
|
10455
10309
|
var PACKAGE_NAME = "billion-context-pi";
|
|
10456
10310
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
10457
10311
|
var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/;
|
|
10458
10312
|
var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
|
|
10459
|
-
var THROTTLE_FILE =
|
|
10313
|
+
var THROTTLE_FILE = join7(homedir4(), CONFIG_DIR_NAME3, "agent", ".billion-context-pi-update-check");
|
|
10460
10314
|
var updateInFlight = false;
|
|
10461
10315
|
function parseVersion(v) {
|
|
10462
10316
|
return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -10480,7 +10334,7 @@ async function readLastCheck() {
|
|
|
10480
10334
|
}
|
|
10481
10335
|
async function writeLastCheck(timestamp) {
|
|
10482
10336
|
try {
|
|
10483
|
-
await mkdir3(
|
|
10337
|
+
await mkdir3(dirname5(THROTTLE_FILE), { recursive: true });
|
|
10484
10338
|
await writeFile3(THROTTLE_FILE, String(timestamp), "utf-8");
|
|
10485
10339
|
} catch {
|
|
10486
10340
|
}
|
|
@@ -10494,20 +10348,20 @@ async function readPackageJson(path4) {
|
|
|
10494
10348
|
}
|
|
10495
10349
|
}
|
|
10496
10350
|
function findNpmRoot(extDir) {
|
|
10497
|
-
let dir =
|
|
10351
|
+
let dir = dirname5(extDir);
|
|
10498
10352
|
for (; ; ) {
|
|
10499
|
-
if (dir.endsWith("node_modules")) return
|
|
10500
|
-
const parent =
|
|
10353
|
+
if (dir.endsWith("node_modules")) return dirname5(dir);
|
|
10354
|
+
const parent = dirname5(dir);
|
|
10501
10355
|
if (parent === dir) return void 0;
|
|
10502
10356
|
dir = parent;
|
|
10503
10357
|
}
|
|
10504
10358
|
}
|
|
10505
10359
|
async function findExtensionDir() {
|
|
10506
|
-
let dir =
|
|
10360
|
+
let dir = dirname5(fileURLToPath(import.meta.url));
|
|
10507
10361
|
for (; ; ) {
|
|
10508
|
-
const pkg = await readPackageJson(
|
|
10362
|
+
const pkg = await readPackageJson(join7(dir, "package.json"));
|
|
10509
10363
|
if (pkg?.name === PACKAGE_NAME) return dir;
|
|
10510
|
-
const parent =
|
|
10364
|
+
const parent = dirname5(dir);
|
|
10511
10365
|
if (parent === dir) return void 0;
|
|
10512
10366
|
dir = parent;
|
|
10513
10367
|
}
|
|
@@ -10556,7 +10410,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
10556
10410
|
const data = await res.json();
|
|
10557
10411
|
const latest = data.version;
|
|
10558
10412
|
if (!latest) return;
|
|
10559
|
-
const current = runtimeVersion ?? "0.1.
|
|
10413
|
+
const current = runtimeVersion ?? "0.1.40";
|
|
10560
10414
|
const hasUpdate = isNewer(latest, current);
|
|
10561
10415
|
debug.event("update-check", {
|
|
10562
10416
|
current,
|
|
@@ -10586,16 +10440,16 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
10586
10440
|
async function getRuntimeVersion() {
|
|
10587
10441
|
const extDir = await findExtensionDir();
|
|
10588
10442
|
if (!extDir) return void 0;
|
|
10589
|
-
const pkg = await readPackageJson(
|
|
10443
|
+
const pkg = await readPackageJson(join7(extDir, "package.json"));
|
|
10590
10444
|
return pkg?.version;
|
|
10591
10445
|
}
|
|
10592
10446
|
|
|
10593
10447
|
// src/setup-subagent-tools.ts
|
|
10594
10448
|
import { readFile as readFile2, writeFile as writeFile4, stat, copyFile, rename } from "fs/promises";
|
|
10595
|
-
import { existsSync as
|
|
10596
|
-
import { homedir as
|
|
10597
|
-
import { join as
|
|
10598
|
-
import { CONFIG_DIR_NAME as
|
|
10449
|
+
import { existsSync as existsSync4 } from "fs";
|
|
10450
|
+
import { homedir as homedir5 } from "os";
|
|
10451
|
+
import { join as join8 } from "path";
|
|
10452
|
+
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@earendil-works/pi-coding-agent";
|
|
10599
10453
|
var ACP_TOOLS2 = ["compress", "decompress", "search_context", "acp_status"];
|
|
10600
10454
|
var BUILTIN_DEFAULT_TOOLS = {
|
|
10601
10455
|
advisor: ["read", "grep", "find", "ls", "bash", "intercom"],
|
|
@@ -10610,9 +10464,9 @@ var BUILTIN_DEFAULT_TOOLS = {
|
|
|
10610
10464
|
};
|
|
10611
10465
|
function resolveAgentDir() {
|
|
10612
10466
|
const configured = process.env.PI_CODING_AGENT_DIR;
|
|
10613
|
-
if (configured === "~") return
|
|
10614
|
-
if (configured?.startsWith("~/")) return
|
|
10615
|
-
return configured ||
|
|
10467
|
+
if (configured === "~") return homedir5();
|
|
10468
|
+
if (configured?.startsWith("~/")) return join8(homedir5(), configured.slice(2));
|
|
10469
|
+
return configured || join8(homedir5(), CONFIG_DIR_NAME4, "agent");
|
|
10616
10470
|
}
|
|
10617
10471
|
function desiredTools(existing, name) {
|
|
10618
10472
|
const base = Array.isArray(existing?.tools) && existing.tools.length > 0 ? [...existing.tools] : [...BUILTIN_DEFAULT_TOOLS[name] ?? []];
|
|
@@ -10622,7 +10476,7 @@ function desiredTools(existing, name) {
|
|
|
10622
10476
|
return { tools: base, changed: true };
|
|
10623
10477
|
}
|
|
10624
10478
|
async function ensureSubagentAcpTools(settingsPath) {
|
|
10625
|
-
const path4 = settingsPath ??
|
|
10479
|
+
const path4 = settingsPath ?? join8(resolveAgentDir(), "settings.json");
|
|
10626
10480
|
let raw;
|
|
10627
10481
|
let mtimeMs;
|
|
10628
10482
|
try {
|
|
@@ -10657,7 +10511,7 @@ async function ensureSubagentAcpTools(settingsPath) {
|
|
|
10657
10511
|
return { path: path4, action: "skipped", reason: "all builtin agents already have ACP tools" };
|
|
10658
10512
|
}
|
|
10659
10513
|
const backupPath = `${path4}.acp-bak`;
|
|
10660
|
-
if (!
|
|
10514
|
+
if (!existsSync4(backupPath)) {
|
|
10661
10515
|
try {
|
|
10662
10516
|
await copyFile(path4, backupPath);
|
|
10663
10517
|
} catch {
|
|
@@ -10720,64 +10574,6 @@ async function runSetupAndNotify(notify) {
|
|
|
10720
10574
|
}
|
|
10721
10575
|
}
|
|
10722
10576
|
|
|
10723
|
-
// src/user-config.ts
|
|
10724
|
-
import { promises as fs2 } from "fs";
|
|
10725
|
-
import * as path3 from "path";
|
|
10726
|
-
import { homedir as homedir5 } from "os";
|
|
10727
|
-
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@earendil-works/pi-coding-agent";
|
|
10728
|
-
async function loadUserConfig(cwd) {
|
|
10729
|
-
const home = homedir5();
|
|
10730
|
-
const merged = {};
|
|
10731
|
-
for (const base of [join8(home, CONFIG_DIR_NAME4), join8(cwd, CONFIG_DIR_NAME4)]) {
|
|
10732
|
-
const file = join8(base, "acp.json");
|
|
10733
|
-
try {
|
|
10734
|
-
const raw = await fs2.readFile(file, "utf8");
|
|
10735
|
-
const parsed = JSON.parse(raw);
|
|
10736
|
-
if (parsed && typeof parsed === "object") {
|
|
10737
|
-
Object.assign(merged, pickKnown(parsed));
|
|
10738
|
-
debug.event("config-loaded", { file });
|
|
10739
|
-
}
|
|
10740
|
-
} catch (e) {
|
|
10741
|
-
const code = e.code;
|
|
10742
|
-
if (code !== "ENOENT") {
|
|
10743
|
-
logWarn("config", { event: "load-failed", file, error: e instanceof Error ? e.message : String(e) });
|
|
10744
|
-
}
|
|
10745
|
-
}
|
|
10746
|
-
}
|
|
10747
|
-
return merged;
|
|
10748
|
-
}
|
|
10749
|
-
function join8(...parts) {
|
|
10750
|
-
return path3.join(...parts);
|
|
10751
|
-
}
|
|
10752
|
-
var KNOWN = /* @__PURE__ */ new Set([
|
|
10753
|
-
"debug",
|
|
10754
|
-
"autoUpdate",
|
|
10755
|
-
"modelContextLimit",
|
|
10756
|
-
"toolBashDefaultTimeout",
|
|
10757
|
-
"toolOutputMaxBytes",
|
|
10758
|
-
"delegate",
|
|
10759
|
-
"compress",
|
|
10760
|
-
"displayUsage",
|
|
10761
|
-
"prompts",
|
|
10762
|
-
"acknowledgePromptsRisk"
|
|
10763
|
-
]);
|
|
10764
|
-
function pickKnown(parsed) {
|
|
10765
|
-
const out = {};
|
|
10766
|
-
for (const [k, v] of Object.entries(parsed)) {
|
|
10767
|
-
if (KNOWN.has(k)) out[k] = v;
|
|
10768
|
-
}
|
|
10769
|
-
return out;
|
|
10770
|
-
}
|
|
10771
|
-
function applyUserConfig(adapter, user) {
|
|
10772
|
-
return {
|
|
10773
|
-
...adapter,
|
|
10774
|
-
...user,
|
|
10775
|
-
coreOverrides: adapter.coreOverrides,
|
|
10776
|
-
protectedTools: adapter.protectedTools,
|
|
10777
|
-
preserveRecentMessages: adapter.preserveRecentMessages
|
|
10778
|
-
};
|
|
10779
|
-
}
|
|
10780
|
-
|
|
10781
10577
|
// src/index.ts
|
|
10782
10578
|
function createAcpExtension(adapter = {}) {
|
|
10783
10579
|
return (pi) => {
|
|
@@ -10804,15 +10600,17 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
10804
10600
|
pi.on("session_start", async (_event, ctx) => {
|
|
10805
10601
|
runtime.store.invalidate();
|
|
10806
10602
|
runtime.clearNudgeTracking();
|
|
10603
|
+
const modelId = ctx.model?.id ?? "default";
|
|
10604
|
+
runtime.density.resetModel(modelId);
|
|
10807
10605
|
resetDelegateUsage();
|
|
10808
10606
|
setDelegateDisplayUsage("separate");
|
|
10809
10607
|
const sid = ctx.sessionManager.getSessionId();
|
|
10810
|
-
|
|
10608
|
+
runtime.clearSessionTracking(sid);
|
|
10609
|
+
const modelInfo = ctx.model;
|
|
10610
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.40" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
|
|
10811
10611
|
try {
|
|
10812
|
-
|
|
10813
|
-
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
10612
|
+
await runtime.reloadConfig(ctx.cwd);
|
|
10814
10613
|
setDelegateDisplayUsage(resolveDelegate(runtime.adapter).displayUsage);
|
|
10815
|
-
if (runtime.adapter.debug !== void 0) setDebugEnabled(runtime.adapter.debug);
|
|
10816
10614
|
} catch (e) {
|
|
10817
10615
|
logThrow("config", e, { sid, phase: "session_start" });
|
|
10818
10616
|
}
|
|
@@ -10843,6 +10641,9 @@ function wireContextTransform(pi, runtime) {
|
|
|
10843
10641
|
const sid = ctx.sessionManager.getSessionId();
|
|
10844
10642
|
const release = await runtime.acquireLock(sid);
|
|
10845
10643
|
try {
|
|
10644
|
+
await runtime.reloadConfig(ctx.cwd);
|
|
10645
|
+
const modelId = ctx.model?.id ?? "default";
|
|
10646
|
+
runtime.setCountModel(modelId);
|
|
10846
10647
|
const { state, coreMessages, entries } = await runtime.stateFor(ctx, event.messages);
|
|
10847
10648
|
const config = runtime.configFor(ctx);
|
|
10848
10649
|
const coveredIds = collectCoveredMessageIds(state);
|
|
@@ -10850,9 +10651,15 @@ function wireContextTransform(pi, runtime) {
|
|
|
10850
10651
|
const systemPromptText = getSystemPromptText(ctx);
|
|
10851
10652
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
10852
10653
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
10853
|
-
const tokenCount = sentTokens;
|
|
10654
|
+
const tokenCount = calibrateTokens(sentTokens, runtime.density.densityFor(modelId));
|
|
10655
|
+
const postCompression = runtime.noteActiveBlocks(
|
|
10656
|
+
sid,
|
|
10657
|
+
state.blocks.filter((b) => b.active).map((b) => b.blockId)
|
|
10658
|
+
);
|
|
10854
10659
|
debug.event("context-in", {
|
|
10855
10660
|
sid,
|
|
10661
|
+
modelId,
|
|
10662
|
+
density: runtime.density.densityFor(modelId),
|
|
10856
10663
|
eventMsgs: event.messages?.length ?? 0,
|
|
10857
10664
|
entries: entries.length,
|
|
10858
10665
|
coreMsgs: coreMessages.length,
|
|
@@ -10864,8 +10671,10 @@ function wireContextTransform(pi, runtime) {
|
|
|
10864
10671
|
});
|
|
10865
10672
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount });
|
|
10866
10673
|
await runtime.save(turn.state, ctx);
|
|
10674
|
+
runtime.density.update(modelId, realUsage?.tokens ?? null, sentTokens, postCompression);
|
|
10867
10675
|
logInfo("turn", {
|
|
10868
10676
|
sid,
|
|
10677
|
+
model: ctx.model?.id ?? null,
|
|
10869
10678
|
inMsgs: coreMessages.length,
|
|
10870
10679
|
outMsgs: turn.messages.length,
|
|
10871
10680
|
tokens: tokenCount,
|
|
@@ -10877,6 +10686,8 @@ function wireContextTransform(pi, runtime) {
|
|
|
10877
10686
|
activeBlocks: turn.state.blocks.filter((b) => b.active).length
|
|
10878
10687
|
});
|
|
10879
10688
|
debug.event("processTurn", {
|
|
10689
|
+
modelId,
|
|
10690
|
+
density: runtime.density.densityFor(modelId),
|
|
10880
10691
|
outMsgs: turn.messages.length,
|
|
10881
10692
|
summaryMsgs: turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|
|
10882
10693
|
prunedMsgs: coreMessages.length - turn.messages.length + turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
|