billion-context-omp 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -9
- package/README.zh-CN.md +12 -9
- package/dist/compress-tool.d.ts +0 -1
- package/dist/index.js +230 -147
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +0 -11
- package/dist/tokens.d.ts +13 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -90,20 +90,23 @@ Rich status display for the user:
|
|
|
90
90
|
╭─────────────────────────────────────────────╮
|
|
91
91
|
│ ACP Context Analysis │
|
|
92
92
|
╰─────────────────────────────────────────────╯
|
|
93
|
-
billion-context-omp@0.
|
|
93
|
+
billion-context-omp@0.2.0
|
|
94
94
|
|
|
95
|
-
Context:
|
|
95
|
+
Context (session accounting, host footer scale): 9% (93k / 1.0M) — never shrinks; includes compressed originals
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Framework ██████████████████░░ 90% 51k
|
|
97
|
+
Sent to LLM (after compression, est.): 63k (6% of limit)
|
|
98
|
+
Session-only (compressed originals, est.): 110k — pruned from every request; the footer/nudge still count them
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
Token Breakdown (sent view):
|
|
101
|
+
Tool ██████████████████░░ 88% 55k
|
|
102
|
+
SysPrompt ██░░░░░░░░░░░░░░░░░ 9% 5.9k
|
|
103
|
+
Text ░░░░░░░░░░░░░░░░░░░░ 1% 553
|
|
104
|
+
Summaries ░░░░░░░░░░░░░░░░░░░░ 2% 1.5k
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
[b1] T1 5.1k→1.0k: Fold architecture port
|
|
105
|
-
[b2] T1 15k→799: Replay guard hardening
|
|
106
|
+
Nudge: idle — growth 0 < floor 20000, ready: T1 50394
|
|
106
107
|
|
|
108
|
+
Blocks: 1 active / 1 total (112k tokens compressed)
|
|
109
|
+
[b1] T1 112k→1.5k: PR141 review + follow-up fixes
|
|
107
110
|
Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.
|
|
108
111
|
```
|
|
109
112
|
|
package/README.zh-CN.md
CHANGED
|
@@ -90,20 +90,23 @@ omp 内置的 `/compact` 被拦截,替换为 ACP 模型摘要式 compaction,
|
|
|
90
90
|
╭─────────────────────────────────────────────╮
|
|
91
91
|
│ ACP Context Analysis │
|
|
92
92
|
╰─────────────────────────────────────────────╯
|
|
93
|
-
billion-context-omp@0.
|
|
93
|
+
billion-context-omp@0.2.0
|
|
94
94
|
|
|
95
|
-
Context:
|
|
95
|
+
Context (session accounting, host footer scale): 9% (93k / 1.0M) — never shrinks; includes compressed originals
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Framework ██████████████████░░ 90% 51k
|
|
97
|
+
Sent to LLM (after compression, est.): 63k (6% of limit)
|
|
98
|
+
Session-only (compressed originals, est.): 110k — pruned from every request; the footer/nudge still count them
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
Token Breakdown (sent view):
|
|
101
|
+
Tool ██████████████████░░ 88% 55k
|
|
102
|
+
SysPrompt ██░░░░░░░░░░░░░░░░░ 9% 5.9k
|
|
103
|
+
Text ░░░░░░░░░░░░░░░░░░░░ 1% 553
|
|
104
|
+
Summaries ░░░░░░░░░░░░░░░░░░░░ 2% 1.5k
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
[b1] T1 5.1k→1.0k: Fold architecture port
|
|
105
|
-
[b2] T1 15k→799: Replay guard hardening
|
|
106
|
+
Nudge: idle — growth 0 < floor 20000, ready: T1 50394
|
|
106
107
|
|
|
108
|
+
Blocks: 1 active / 1 total (112k tokens compressed)
|
|
109
|
+
[b1] T1 112k→1.5k: PR141 review + follow-up fixes
|
|
107
110
|
Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.
|
|
108
111
|
```
|
|
109
112
|
|
package/dist/compress-tool.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { AcpRuntime } from "./runtime.js";
|
|
|
3
3
|
/** Label shown for a block when the model did not pass a topic: first
|
|
4
4
|
* sentence-ish slice of the summary (≤30 chars). Decorative only — never
|
|
5
5
|
* blocks compression. */
|
|
6
|
-
export declare function topicFallback(summary: string): string;
|
|
7
6
|
declare const CompressParams: import("@oh-my-pi/omptype").FluentType<{
|
|
8
7
|
content: {
|
|
9
8
|
endId: string;
|
package/dist/index.js
CHANGED
|
@@ -126,9 +126,11 @@ function prune(messages, state, options = {}) {
|
|
|
126
126
|
const indexById = /* @__PURE__ */ new Map();
|
|
127
127
|
messages.forEach((message, index) => indexById.set(message.id, index));
|
|
128
128
|
const anchors = inject ? collectSummaryAnchors(state, indexById) : [];
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
return stripOrphanedReasoning(
|
|
130
|
+
stripOrphanedToolResults(
|
|
131
|
+
stripOrphanedToolCalls(
|
|
132
|
+
rebuildMessages(messages, covered, firstUserIndex, anchors)
|
|
133
|
+
)
|
|
132
134
|
)
|
|
133
135
|
);
|
|
134
136
|
}
|
|
@@ -205,6 +207,24 @@ function stripOrphanedToolCalls(messages) {
|
|
|
205
207
|
(m) => m.contentType !== "tool-call" || !m.toolCallId || m.toolName === "compress" || knownResultIds.has(m.toolCallId)
|
|
206
208
|
);
|
|
207
209
|
}
|
|
210
|
+
function stripOrphanedReasoning(messages) {
|
|
211
|
+
const drop = /* @__PURE__ */ new Set();
|
|
212
|
+
for (let i = 0; i < messages.length; i++) {
|
|
213
|
+
if (drop.has(i)) continue;
|
|
214
|
+
if (messages[i].contentType !== "reasoning") continue;
|
|
215
|
+
let j = i;
|
|
216
|
+
while (j + 1 < messages.length && messages[j + 1].contentType === "reasoning") {
|
|
217
|
+
j++;
|
|
218
|
+
}
|
|
219
|
+
const companion = messages[j + 1];
|
|
220
|
+
const hasCompanion = companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call");
|
|
221
|
+
if (!hasCompanion) {
|
|
222
|
+
for (let k = i; k <= j; k++) drop.add(k);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (drop.size === 0) return messages;
|
|
226
|
+
return messages.filter((_, i) => !drop.has(i));
|
|
227
|
+
}
|
|
208
228
|
function syncBlocks(messages, state) {
|
|
209
229
|
const presentIds = new Set(messages.map((message) => message.id));
|
|
210
230
|
const deactivated = [];
|
|
@@ -814,6 +834,38 @@ function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan =
|
|
|
814
834
|
}
|
|
815
835
|
return { startIndex: newStartIndex, endIndex: newEndIndex };
|
|
816
836
|
}
|
|
837
|
+
function adjustBoundariesForReasoningPairs(startIndex, endIndex, messages) {
|
|
838
|
+
if (startIndex > endIndex) {
|
|
839
|
+
return { startIndex, endIndex };
|
|
840
|
+
}
|
|
841
|
+
let newStartIndex = startIndex;
|
|
842
|
+
let newEndIndex = endIndex;
|
|
843
|
+
for (let i = startIndex; i <= endIndex && i < messages.length; i++) {
|
|
844
|
+
const msg = messages[i];
|
|
845
|
+
if (!msg) continue;
|
|
846
|
+
if (msg.contentType === "reasoning") {
|
|
847
|
+
let j = i;
|
|
848
|
+
while (j + 1 < messages.length && messages[j + 1].contentType === "reasoning") {
|
|
849
|
+
j++;
|
|
850
|
+
}
|
|
851
|
+
const companion = messages[j + 1];
|
|
852
|
+
if (companion !== void 0 && companion.role === "assistant" && (companion.contentType === "text" || companion.contentType === "tool-call") && j + 1 > newEndIndex) {
|
|
853
|
+
newEndIndex = j + 1;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (msg.role === "assistant" && (msg.contentType === "text" || msg.contentType === "tool-call")) {
|
|
857
|
+
let k = i - 1;
|
|
858
|
+
while (k >= 0 && messages[k].contentType === "reasoning") {
|
|
859
|
+
k--;
|
|
860
|
+
}
|
|
861
|
+
const runStart = k + 1;
|
|
862
|
+
if (runStart < i && runStart >= 0 && messages[runStart].contentType === "reasoning" && runStart < newStartIndex) {
|
|
863
|
+
newStartIndex = runStart;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return { startIndex: newStartIndex, endIndex: newEndIndex };
|
|
868
|
+
}
|
|
817
869
|
function refNum(ref) {
|
|
818
870
|
const n = parseInt(ref.slice(1), 10);
|
|
819
871
|
return Number.isNaN(n) ? -1 : n;
|
|
@@ -1309,7 +1361,7 @@ function applySingleRange(input) {
|
|
|
1309
1361
|
messages: input.messages,
|
|
1310
1362
|
state: input.state
|
|
1311
1363
|
});
|
|
1312
|
-
const rangeMessageIds =
|
|
1364
|
+
const rangeMessageIds = applyPairBoundaryAdjustments(
|
|
1313
1365
|
resolved,
|
|
1314
1366
|
input.messages
|
|
1315
1367
|
);
|
|
@@ -1423,20 +1475,33 @@ function applySingleRange(input) {
|
|
|
1423
1475
|
}
|
|
1424
1476
|
return { tokens: compressedTokens, warnings };
|
|
1425
1477
|
}
|
|
1426
|
-
function
|
|
1478
|
+
function applyPairBoundaryAdjustments(resolved, messages) {
|
|
1427
1479
|
if (resolved.boundaryKind === "block") {
|
|
1428
1480
|
return resolved.messageIds;
|
|
1429
1481
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1482
|
+
let startIndex = resolved.startIndex;
|
|
1483
|
+
let endIndex = resolved.endIndex;
|
|
1484
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
1485
|
+
const reasoningAdjusted = adjustBoundariesForReasoningPairs(
|
|
1486
|
+
startIndex,
|
|
1487
|
+
endIndex,
|
|
1488
|
+
messages
|
|
1489
|
+
);
|
|
1490
|
+
const toolAdjusted = adjustBoundariesForToolPairs(
|
|
1491
|
+
reasoningAdjusted.startIndex,
|
|
1492
|
+
reasoningAdjusted.endIndex,
|
|
1493
|
+
messages
|
|
1494
|
+
);
|
|
1495
|
+
const changed = toolAdjusted.startIndex !== startIndex || toolAdjusted.endIndex !== endIndex;
|
|
1496
|
+
startIndex = toolAdjusted.startIndex;
|
|
1497
|
+
endIndex = toolAdjusted.endIndex;
|
|
1498
|
+
if (!changed) break;
|
|
1499
|
+
}
|
|
1500
|
+
if (startIndex === resolved.startIndex && endIndex === resolved.endIndex) {
|
|
1436
1501
|
return resolved.messageIds;
|
|
1437
1502
|
}
|
|
1438
1503
|
const ids = [];
|
|
1439
|
-
for (let i =
|
|
1504
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
1440
1505
|
const msg = messages[i];
|
|
1441
1506
|
if (msg) ids.push(msg.id);
|
|
1442
1507
|
}
|
|
@@ -3003,10 +3068,6 @@ function boundaryRaw(ref, byRef, blocks, pick) {
|
|
|
3003
3068
|
const pos = (id) => rawPos(byRef[id] ?? id);
|
|
3004
3069
|
return pick === "min" ? ids.reduce((a, b) => pos(a) <= pos(b) ? a : b) : ids.reduce((a, b) => pos(a) >= pos(b) ? a : b);
|
|
3005
3070
|
}
|
|
3006
|
-
var VIABLE_RANGE_MIN_TOKENS = 200;
|
|
3007
|
-
function viableRanges(ranges) {
|
|
3008
|
-
return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);
|
|
3009
|
-
}
|
|
3010
3071
|
function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
|
|
3011
3072
|
return ranges.map((r) => {
|
|
3012
3073
|
const start = boundaryRaw(r.startRef, byRef, blocks, "min");
|
|
@@ -3240,11 +3301,6 @@ var RangeSpec = type({
|
|
|
3240
3301
|
summary: type("string").describe("Complete technical summary replacing all content in range. Keep only essential details (conclusions, file paths, decisions, exact values, etc.)."),
|
|
3241
3302
|
"topic?": type("string").describe("Short label (3-5 words) for THIS range, e.g. 'Auth System Exploration'. Recommended for every range; omit to use top-level topic.")
|
|
3242
3303
|
});
|
|
3243
|
-
function topicFallback(summary) {
|
|
3244
|
-
const first = summary.split(/[.\n]/)[0] ?? "";
|
|
3245
|
-
const t = first.trim().replace(/^["'`]+/, "").trim();
|
|
3246
|
-
return t.length <= 30 ? t : `${t.slice(0, 30).trimEnd()}\u2026`;
|
|
3247
|
-
}
|
|
3248
3304
|
var CompressParams = type({
|
|
3249
3305
|
"topic?": type("string").describe("Fallback topic for entries without their own. Omit when each content entry specifies its own topic."),
|
|
3250
3306
|
content: RangeSpec.array().describe("One or more ranges to compress, each with start/end boundaries and a summary. When compressing multiple unrelated ranges in one call, give each its own topic."),
|
|
@@ -3623,6 +3679,130 @@ function truncate(s, n) {
|
|
|
3623
3679
|
|
|
3624
3680
|
// src/status-tool.ts
|
|
3625
3681
|
import { type as type4 } from "@oh-my-pi/omptype";
|
|
3682
|
+
|
|
3683
|
+
// src/compat.ts
|
|
3684
|
+
function normalizeSystemPrompt(input) {
|
|
3685
|
+
if (input === void 0) return "";
|
|
3686
|
+
if (Array.isArray(input)) return input.join("\n");
|
|
3687
|
+
return input;
|
|
3688
|
+
}
|
|
3689
|
+
function formatSystemPromptForEvent(base, append) {
|
|
3690
|
+
const normalized = normalizeSystemPrompt(base);
|
|
3691
|
+
return [`${normalized}
|
|
3692
|
+
|
|
3693
|
+
${append}`];
|
|
3694
|
+
}
|
|
3695
|
+
function getSystemPromptText(ctx) {
|
|
3696
|
+
const result = ctx.getSystemPrompt?.();
|
|
3697
|
+
return normalizeSystemPrompt(result);
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// node_modules/billion-context-kit/dist/index.js
|
|
3701
|
+
var VIABLE_RANGE_MIN_TOKENS = 200;
|
|
3702
|
+
function viableRanges(ranges) {
|
|
3703
|
+
return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);
|
|
3704
|
+
}
|
|
3705
|
+
function topicFallback(summary) {
|
|
3706
|
+
const first = summary.split(/[.\n]/)[0] ?? "";
|
|
3707
|
+
const t = first.trim().replace(/^["'`]+/, "").trim();
|
|
3708
|
+
return t.length <= 30 ? t : `${t.slice(0, 30).trimEnd()}\u2026`;
|
|
3709
|
+
}
|
|
3710
|
+
function formatCompactTokens(count) {
|
|
3711
|
+
if (count < 1e3) return count.toString();
|
|
3712
|
+
if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
|
|
3713
|
+
if (count < 1e6) return `${Math.round(count / 1e3)}k`;
|
|
3714
|
+
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
3715
|
+
return `${Math.round(count / 1e6)}M`;
|
|
3716
|
+
}
|
|
3717
|
+
function bar(value, total, width = 20) {
|
|
3718
|
+
if (total === 0) return "";
|
|
3719
|
+
const filled = Math.max(0, Math.min(width, Math.round(value / total * width)));
|
|
3720
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
3721
|
+
}
|
|
3722
|
+
function buildStatusPanel(input) {
|
|
3723
|
+
const { tokenCount, state, nudge, modelContextLimit } = input;
|
|
3724
|
+
const fmt2 = input.fmtTokens ?? formatCompactTokens;
|
|
3725
|
+
const bd = nudge?.contextBreakdown;
|
|
3726
|
+
const limit = modelContextLimit;
|
|
3727
|
+
const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;
|
|
3728
|
+
const systemPromptTokens = input.systemPromptTokens;
|
|
3729
|
+
const sentTotal = classified + systemPromptTokens;
|
|
3730
|
+
const sessionOnly = input.unprunedTokens !== void 0 ? Math.max(0, input.unprunedTokens - sentTotal) : 0;
|
|
3731
|
+
const displayTotal = tokenCount;
|
|
3732
|
+
const displayPct = limit > 0 ? Math.round(displayTotal / limit * 100) : 0;
|
|
3733
|
+
const sentPct = limit > 0 ? Math.round(sentTotal / limit * 100) : 0;
|
|
3734
|
+
const activeBlocksList = state.blocks.filter((b) => b.active);
|
|
3735
|
+
const totalBlocksList = state.blocks;
|
|
3736
|
+
const lines = [];
|
|
3737
|
+
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");
|
|
3738
|
+
lines.push("\u2502 ACP Context Analysis \u2502");
|
|
3739
|
+
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");
|
|
3740
|
+
if (input.version) lines.push(input.version);
|
|
3741
|
+
lines.push("");
|
|
3742
|
+
lines.push(`Context (session accounting, host footer scale): ${displayPct}% (${fmt2(displayTotal)} / ${fmt2(limit)}) \u2014 never shrinks; includes compressed originals`);
|
|
3743
|
+
if (nudge && bd) {
|
|
3744
|
+
const growth = bd.growth;
|
|
3745
|
+
if (growth > 0 && displayTotal > 0) {
|
|
3746
|
+
lines.push(`Growth: +${fmt2(growth)} since last nudge`);
|
|
3747
|
+
}
|
|
3748
|
+
lines.push("");
|
|
3749
|
+
lines.push(`Sent to LLM (after compression, est.): ${fmt2(sentTotal)}${limit > 0 ? ` (${sentPct}% of limit)` : ""}`);
|
|
3750
|
+
if (input.unprunedTokens !== void 0 && sessionOnly > 0) {
|
|
3751
|
+
lines.push(`Session-only (compressed originals, est.): ${fmt2(sessionOnly)} \u2014 pruned from every request; the footer/nudge still count them`);
|
|
3752
|
+
}
|
|
3753
|
+
lines.push("");
|
|
3754
|
+
lines.push("Token Breakdown (sent view):");
|
|
3755
|
+
const categories = [
|
|
3756
|
+
{ label: "Tool", value: bd.tool },
|
|
3757
|
+
{ label: "SysPrompt", value: systemPromptTokens },
|
|
3758
|
+
{ label: "Text", value: bd.text },
|
|
3759
|
+
{ label: "Code", value: bd.code },
|
|
3760
|
+
{ label: "Summaries", value: bd.summaries }
|
|
3761
|
+
];
|
|
3762
|
+
for (const cat of categories) {
|
|
3763
|
+
if (cat.value <= 0) continue;
|
|
3764
|
+
const pct2 = sentTotal > 0 ? Math.round(cat.value / sentTotal * 100) : 0;
|
|
3765
|
+
const b = bar(cat.value, sentTotal);
|
|
3766
|
+
lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct2).padStart(3)}% ${fmt2(cat.value)}`);
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
lines.push("");
|
|
3770
|
+
if (nudge) {
|
|
3771
|
+
if (nudge.shouldInject) {
|
|
3772
|
+
const tierInfo = nudge.tier ? ` [T${nudge.tier} distillation]` : "";
|
|
3773
|
+
lines.push(`Nudge: ACTIVE${tierInfo} \u2014 ${nudge.reason}`);
|
|
3774
|
+
} else {
|
|
3775
|
+
lines.push(`Nudge: idle \u2014 ${nudge.reason}`);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
const ranges = viableRanges(nudge?.compressibleRanges ?? []);
|
|
3779
|
+
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
3780
|
+
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
3781
|
+
lines.push("");
|
|
3782
|
+
lines.push(formatRanges(ranges, protectedRanges));
|
|
3783
|
+
}
|
|
3784
|
+
if (activeBlocksList.length > 0) {
|
|
3785
|
+
lines.push("");
|
|
3786
|
+
lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmt2(state.stats.tokensCompressed)} tokens compressed)`);
|
|
3787
|
+
for (const b of activeBlocksList) {
|
|
3788
|
+
const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || "")}`;
|
|
3789
|
+
const summaryTok = defaultCountTokens(b.summary || "");
|
|
3790
|
+
const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;
|
|
3791
|
+
lines.push(` [${b.blockId}] T${b.tier} ${fmt2(origTok)}\u2192${fmt2(summaryTok)}${topic}`);
|
|
3792
|
+
}
|
|
3793
|
+
} else if (totalBlocksList.length > 0) {
|
|
3794
|
+
lines.push("");
|
|
3795
|
+
lines.push(`Blocks: 0 active / ${totalBlocksList.length} total (${fmt2(state.stats.tokensCompressed)} tokens compressed)`);
|
|
3796
|
+
} else {
|
|
3797
|
+
lines.push("");
|
|
3798
|
+
lines.push("Blocks: none (nothing compressed yet)");
|
|
3799
|
+
}
|
|
3800
|
+
lines.push("");
|
|
3801
|
+
lines.push("Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.");
|
|
3802
|
+
return lines.join("\n");
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
// src/status-tool.ts
|
|
3626
3806
|
var StatusParams = type4({
|
|
3627
3807
|
"scope?": type4('"compressed" | "uncompressed"').describe('"compressed" = drill into blocks; "uncompressed" = show visible messages/ranges. Default: overview.'),
|
|
3628
3808
|
"view?": type4('"ranges" | "messages"').describe('For uncompressed scope: "ranges" (default) or "messages" (per-message listing).'),
|
|
@@ -3651,13 +3831,16 @@ function makeStatusTool(runtime) {
|
|
|
3651
3831
|
async function handleStatus(args, runtime, ctx) {
|
|
3652
3832
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
3653
3833
|
const config = runtime.configFor(ctx);
|
|
3654
|
-
const tokenCount = estimateTokens(coreMessages, collectCoveredMessageIds(state));
|
|
3655
|
-
const realUsage = ctx.getContextUsage?.();
|
|
3834
|
+
const tokenCount = estimateTokens(coreMessages, collectCoveredMessageIds(state)) + estimateTextTokens2(getSystemPromptText(ctx) ?? "");
|
|
3656
3835
|
const turn = runtime.core.processTurn({
|
|
3657
3836
|
messages: coreMessages,
|
|
3658
3837
|
state,
|
|
3659
3838
|
config,
|
|
3660
|
-
|
|
3839
|
+
// Sent-view scale — see src/index.ts context handler. The session-tree
|
|
3840
|
+
// number (ctx.getContextUsage) must never arbitrate emergencies for the
|
|
3841
|
+
// sent view: a tree that outgrew the model window reads as a permanent
|
|
3842
|
+
// 200%+ emergency while the real sent view is a few percent.
|
|
3843
|
+
tokenCount
|
|
3661
3844
|
});
|
|
3662
3845
|
const processed = turn.messages;
|
|
3663
3846
|
const base = buildStatusReport(turn.state, processed, defaultCountTokens, {
|
|
@@ -3686,32 +3869,6 @@ async function handleStatus(args, runtime, ctx) {
|
|
|
3686
3869
|
${extra.join("\n")}` : base;
|
|
3687
3870
|
}
|
|
3688
3871
|
|
|
3689
|
-
// src/compat.ts
|
|
3690
|
-
function normalizeSystemPrompt(input) {
|
|
3691
|
-
if (input === void 0) return "";
|
|
3692
|
-
if (Array.isArray(input)) return input.join("\n");
|
|
3693
|
-
return input;
|
|
3694
|
-
}
|
|
3695
|
-
function formatSystemPromptForEvent(base, append) {
|
|
3696
|
-
const normalized = normalizeSystemPrompt(base);
|
|
3697
|
-
return [`${normalized}
|
|
3698
|
-
|
|
3699
|
-
${append}`];
|
|
3700
|
-
}
|
|
3701
|
-
function getSystemPromptText(ctx) {
|
|
3702
|
-
const result = ctx.getSystemPrompt?.();
|
|
3703
|
-
return normalizeSystemPrompt(result);
|
|
3704
|
-
}
|
|
3705
|
-
|
|
3706
|
-
// src/footer-status.ts
|
|
3707
|
-
function formatCompactTokens(count) {
|
|
3708
|
-
if (count < 1e3) return count.toString();
|
|
3709
|
-
if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
|
|
3710
|
-
if (count < 1e6) return `${Math.round(count / 1e3)}k`;
|
|
3711
|
-
if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
|
|
3712
|
-
return `${Math.round(count / 1e6)}M`;
|
|
3713
|
-
}
|
|
3714
|
-
|
|
3715
3872
|
// src/commands.ts
|
|
3716
3873
|
function safeHandler(handler) {
|
|
3717
3874
|
return async (args, ctx) => {
|
|
@@ -3789,100 +3946,26 @@ ${text}`);
|
|
|
3789
3946
|
}
|
|
3790
3947
|
];
|
|
3791
3948
|
}
|
|
3792
|
-
function fmtTokens(n) {
|
|
3793
|
-
return formatCompactTokens(n);
|
|
3794
|
-
}
|
|
3795
|
-
function bar(value, total, width = 20) {
|
|
3796
|
-
if (total === 0) return "";
|
|
3797
|
-
const filled = Math.max(0, Math.min(width, Math.round(value / total * width)));
|
|
3798
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
3799
|
-
}
|
|
3800
3949
|
async function statusReport(runtime, ctx) {
|
|
3801
3950
|
const { state, coreMessages } = await runtime.stateFor(ctx);
|
|
3802
3951
|
const config = runtime.configFor(ctx);
|
|
3803
3952
|
const realUsage = ctx.getContextUsage?.();
|
|
3804
|
-
const
|
|
3805
|
-
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount });
|
|
3806
|
-
const nudge = turn.nudge;
|
|
3807
|
-
const bd = nudge?.contextBreakdown;
|
|
3808
|
-
const limit = config.modelContextLimit;
|
|
3809
|
-
const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;
|
|
3953
|
+
const sessionTokens = realUsage?.tokens && realUsage.tokens > 0 ? realUsage.tokens : defaultCountTokens(coreMessages.map((m) => m.text ?? "").join("\n"));
|
|
3810
3954
|
const systemPromptText = getSystemPromptText(ctx);
|
|
3811
3955
|
const systemPromptTokens = systemPromptText ? defaultCountTokens(systemPromptText) : 0;
|
|
3812
|
-
const
|
|
3813
|
-
const
|
|
3814
|
-
const
|
|
3815
|
-
const
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
lines.push(`Context (session accounting): ${displayPct}% (${fmtTokens(displayTotal)} / ${fmtTokens(limit)})`);
|
|
3826
|
-
if (nudge && bd) {
|
|
3827
|
-
const growth = bd.growth;
|
|
3828
|
-
if (growth > 0 && displayTotal > 0) {
|
|
3829
|
-
lines.push(`Growth: +${fmtTokens(growth)} since last nudge`);
|
|
3830
|
-
}
|
|
3831
|
-
lines.push("");
|
|
3832
|
-
lines.push(`Sent to LLM (after compression): ${fmtTokens(sentTotal)}`);
|
|
3833
|
-
if (sessionOnly > 0) {
|
|
3834
|
-
lines.push(`Session-only (compressed originals + host overhead): ${fmtTokens(sessionOnly)} \u2014 pruned from every request; the footer counts it`);
|
|
3835
|
-
}
|
|
3836
|
-
lines.push("");
|
|
3837
|
-
lines.push("Token Breakdown (sent view):");
|
|
3838
|
-
const categories = [
|
|
3839
|
-
{ label: "Tool", value: bd.tool },
|
|
3840
|
-
{ label: "SysPrompt", value: systemPromptTokens },
|
|
3841
|
-
{ label: "Text", value: bd.text },
|
|
3842
|
-
{ label: "Code", value: bd.code },
|
|
3843
|
-
{ label: "Summaries", value: bd.summaries }
|
|
3844
|
-
];
|
|
3845
|
-
for (const cat of categories) {
|
|
3846
|
-
if (cat.value <= 0) continue;
|
|
3847
|
-
const pct2 = sentTotal > 0 ? Math.round(cat.value / sentTotal * 100) : 0;
|
|
3848
|
-
const b = bar(cat.value, sentTotal);
|
|
3849
|
-
lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct2).padStart(3)}% ${fmtTokens(cat.value)}`);
|
|
3850
|
-
}
|
|
3851
|
-
}
|
|
3852
|
-
lines.push("");
|
|
3853
|
-
if (nudge) {
|
|
3854
|
-
if (nudge.shouldInject) {
|
|
3855
|
-
const tierInfo = nudge.tier ? ` [T${nudge.tier} distillation]` : "";
|
|
3856
|
-
lines.push(`Nudge: ACTIVE${tierInfo} \u2014 ${nudge.reason}`);
|
|
3857
|
-
} else {
|
|
3858
|
-
lines.push(`Nudge: idle \u2014 ${nudge.reason}`);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
const ranges = viableRanges(nudge?.compressibleRanges ?? []);
|
|
3862
|
-
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
3863
|
-
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
3864
|
-
lines.push("");
|
|
3865
|
-
lines.push(formatRanges(ranges, protectedRanges));
|
|
3866
|
-
}
|
|
3867
|
-
if (activeBlocksList.length > 0) {
|
|
3868
|
-
lines.push("");
|
|
3869
|
-
lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmtTokens(state.stats.tokensCompressed)} tokens compressed)`);
|
|
3870
|
-
for (const b of activeBlocksList) {
|
|
3871
|
-
const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || "")}`;
|
|
3872
|
-
const summaryTok = defaultCountTokens(b.summary || "");
|
|
3873
|
-
const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;
|
|
3874
|
-
lines.push(` [${b.blockId}] T${b.tier} ${fmtTokens(origTok)}\u2192${fmtTokens(summaryTok)}${topic}`);
|
|
3875
|
-
}
|
|
3876
|
-
} else if (totalBlocksList.length > 0) {
|
|
3877
|
-
lines.push("");
|
|
3878
|
-
lines.push(`Blocks: 0 active / ${totalBlocksList.length} total (${fmtTokens(state.stats.tokensCompressed)} tokens compressed)`);
|
|
3879
|
-
} else {
|
|
3880
|
-
lines.push("");
|
|
3881
|
-
lines.push("Blocks: none (nothing compressed yet)");
|
|
3882
|
-
}
|
|
3883
|
-
lines.push("");
|
|
3884
|
-
lines.push("Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.");
|
|
3885
|
-
return lines.join("\n");
|
|
3956
|
+
const coveredIds = collectCoveredMessageIds(state);
|
|
3957
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
3958
|
+
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
3959
|
+
const versionStr = "0.1.7" ? `billion-context-omp@${"0.1.7"}` : void 0;
|
|
3960
|
+
return buildStatusPanel({
|
|
3961
|
+
version: versionStr,
|
|
3962
|
+
tokenCount: sessionTokens,
|
|
3963
|
+
systemPromptTokens,
|
|
3964
|
+
state: turn.state,
|
|
3965
|
+
nudge: turn.nudge,
|
|
3966
|
+
modelContextLimit: config.modelContextLimit,
|
|
3967
|
+
unprunedTokens: coreMessages.reduce((sum, m) => sum + defaultCountTokens(m.text ?? ""), 0)
|
|
3968
|
+
});
|
|
3886
3969
|
}
|
|
3887
3970
|
|
|
3888
3971
|
// src/auto-compress.ts
|
|
@@ -4313,7 +4396,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
4313
4396
|
const data = await res.json();
|
|
4314
4397
|
const latest = data.version;
|
|
4315
4398
|
if (!latest) return;
|
|
4316
|
-
const current = runtimeVersion ?? "0.1.
|
|
4399
|
+
const current = runtimeVersion ?? "0.1.7";
|
|
4317
4400
|
const hasUpdate = isNewer(latest, current);
|
|
4318
4401
|
debug.event("update-check", {
|
|
4319
4402
|
current,
|
|
@@ -4601,7 +4684,7 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
4601
4684
|
function wireSessionLifecycle(pi, runtime) {
|
|
4602
4685
|
pi.on("session_start", async (_event, ctx) => {
|
|
4603
4686
|
const sid = ctx.sessionManager.getSessionId();
|
|
4604
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
4687
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.7" : null });
|
|
4605
4688
|
try {
|
|
4606
4689
|
const user = await loadUserConfig(ctx.cwd);
|
|
4607
4690
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -4642,18 +4725,17 @@ function wireContextTransform(pi, runtime) {
|
|
|
4642
4725
|
const { state, coreMessages, originalById, streamLen } = runtime.foldStream(ctx, input);
|
|
4643
4726
|
const config = runtime.configFor(ctx);
|
|
4644
4727
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4645
|
-
const
|
|
4646
|
-
const
|
|
4647
|
-
const
|
|
4728
|
+
const systemPromptTokens = estimateTextTokens2(getSystemPromptText(ctx) ?? "");
|
|
4729
|
+
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4730
|
+
const sessionTokens = ctx.getContextUsage?.()?.tokens ?? null;
|
|
4731
|
+
const tokenCount = sentTokens;
|
|
4648
4732
|
debug.event("context-in", {
|
|
4649
4733
|
sid,
|
|
4650
4734
|
eventMsgs: event.messages?.length ?? 0,
|
|
4651
4735
|
streamLen,
|
|
4652
4736
|
coreMsgs: coreMessages.length,
|
|
4653
4737
|
tokenCount,
|
|
4654
|
-
|
|
4655
|
-
realTokens: realUsage?.tokens ?? null,
|
|
4656
|
-
realPercent: realUsage?.percent ?? null,
|
|
4738
|
+
sessionTokens,
|
|
4657
4739
|
limit: config.modelContextLimit,
|
|
4658
4740
|
blocksBefore: state.blocks.length,
|
|
4659
4741
|
activeBefore: state.blocks.filter((b) => b.active).length
|
|
@@ -4665,7 +4747,8 @@ function wireContextTransform(pi, runtime) {
|
|
|
4665
4747
|
inMsgs: coreMessages.length,
|
|
4666
4748
|
outMsgs: turn.messages.length,
|
|
4667
4749
|
tokens: tokenCount,
|
|
4668
|
-
|
|
4750
|
+
sessionTokens,
|
|
4751
|
+
pct: config.modelContextLimit > 0 ? Math.round(tokenCount / config.modelContextLimit * 100) : null,
|
|
4669
4752
|
limit: config.modelContextLimit,
|
|
4670
4753
|
nudge: turn.nudge?.shouldInject ? turn.nudge.breakdown?.emergencyOverride === 1 ? "emergency" : "active" : "idle",
|
|
4671
4754
|
nudgeReason: turn.nudge?.reason ?? null,
|