acp-kernel 0.0.64-pr.254.101 → 0.0.64-pr.255.102
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/panel/index.d.ts +2 -0
- package/dist/panel/index.d.ts.map +1 -1
- package/dist/panel/index.js +95 -22
- package/dist/panel/index.js.map +1 -1
- package/dist/panel/labels.d.ts +63 -0
- package/dist/panel/labels.d.ts.map +1 -0
- package/dist/panel/panel.d.ts +5 -0
- package/dist/panel/panel.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/panel/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { topicFallback } from "./topic.js";
|
|
|
2
2
|
export { formatCompactTokens } from "./format.js";
|
|
3
3
|
export { buildStatusPanel } from "./panel.js";
|
|
4
4
|
export type { StatusPanelInput } from "./panel.js";
|
|
5
|
+
export { DEFAULT_PANEL_LABELS, displayWidth, fill, padLabel, renderTitleBox } from "./labels.js";
|
|
6
|
+
export type { PanelLabels } from "./labels.js";
|
|
5
7
|
export { cacheHitStats, formatHitRate } from "./cache.js";
|
|
6
8
|
export type { CacheUsageSample, CacheHitSummary } from "./cache.js";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/panel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/panel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/panel/index.js
CHANGED
|
@@ -53,6 +53,67 @@ function formatHitRate(rate) {
|
|
|
53
53
|
return `${(Math.max(0, Math.min(1, rate)) * 100).toFixed(1)}%`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// src/panel/labels.ts
|
|
57
|
+
var DEFAULT_PANEL_LABELS = {
|
|
58
|
+
title: "ACP Context Analysis",
|
|
59
|
+
context: "Context (session accounting, host footer scale): {pct}% ({used} / {limit}) \u2014 includes compressed originals; shrinks slower than the sent view",
|
|
60
|
+
growth: "Growth: +{growth} since last nudge",
|
|
61
|
+
sent: "Sent to LLM (after compression, est.): {sent}",
|
|
62
|
+
sentOfLimit: " ({pct}% of limit)",
|
|
63
|
+
sessionOnly: "Session-only (compressed originals, est.): {only} \u2014 pruned from every request; the footer/nudge still count them",
|
|
64
|
+
breakdown: "Token Breakdown (sent view):",
|
|
65
|
+
catTool: "Tool",
|
|
66
|
+
catSysPrompt: "SysPrompt",
|
|
67
|
+
catText: "Text",
|
|
68
|
+
catCode: "Code",
|
|
69
|
+
catSummaries: "Summaries",
|
|
70
|
+
promptCache: "Prompt cache (provider-reported): {last} last \xB7 {session} session avg \u2014 {read} of {billed} billed prompt tokens served from cache ({requests} req)",
|
|
71
|
+
nudgeActive: "Nudge: ACTIVE{tierInfo} \u2014 {reason}",
|
|
72
|
+
nudgeTierInfo: " [T{tier} distillation]",
|
|
73
|
+
nudgeIdle: "Nudge: idle \u2014 {reason}",
|
|
74
|
+
blocksHeader: "Blocks: {active} active / {total} total ({tokens} tokens compressed, cumulative)",
|
|
75
|
+
blocksNone: "Blocks: none (nothing compressed yet)",
|
|
76
|
+
tagVisibility: "Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal."
|
|
77
|
+
};
|
|
78
|
+
function fill(template, params) {
|
|
79
|
+
return template.replace(/\{([A-Za-z][A-Za-z0-9_]*)\}/g, (m, k) => k in params ? String(params[k]) : m);
|
|
80
|
+
}
|
|
81
|
+
var LEGACY_TITLE_BOX = [
|
|
82
|
+
"\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",
|
|
83
|
+
"\u2502 ACP Context Analysis \u2502",
|
|
84
|
+
"\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"
|
|
85
|
+
];
|
|
86
|
+
var MIN_INNER_WIDTH = 45;
|
|
87
|
+
function charDisplayWidth(ch) {
|
|
88
|
+
const cp = ch.codePointAt(0);
|
|
89
|
+
const wide = cp >= 4352 && cp <= 4447 || // Hangul Jamo
|
|
90
|
+
cp >= 11904 && cp <= 42191 || // CJK radicals .. Yi syllables
|
|
91
|
+
cp >= 44032 && cp <= 55203 || // Hangul syllables
|
|
92
|
+
cp >= 63744 && cp <= 64255 || // CJK compatibility ideographs
|
|
93
|
+
cp >= 65072 && cp <= 65103 || // CJK compatibility forms
|
|
94
|
+
cp >= 65280 && cp <= 65376 || // fullwidth forms
|
|
95
|
+
cp >= 65504 && cp <= 65510 || // halfwidth/fullwidth forms
|
|
96
|
+
cp >= 131072;
|
|
97
|
+
return wide ? 2 : 1;
|
|
98
|
+
}
|
|
99
|
+
function displayWidth(s) {
|
|
100
|
+
let w = 0;
|
|
101
|
+
for (const ch of s) w += charDisplayWidth(ch);
|
|
102
|
+
return w;
|
|
103
|
+
}
|
|
104
|
+
function padLabel(s, width) {
|
|
105
|
+
const dw = displayWidth(s);
|
|
106
|
+
return dw >= width ? s : s + " ".repeat(width - dw);
|
|
107
|
+
}
|
|
108
|
+
function renderTitleBox(title) {
|
|
109
|
+
if (title === DEFAULT_PANEL_LABELS.title) return LEGACY_TITLE_BOX;
|
|
110
|
+
const dw = displayWidth(title);
|
|
111
|
+
const inner = Math.max(MIN_INNER_WIDTH, dw + 2);
|
|
112
|
+
const padL = Math.floor((inner - dw) / 2);
|
|
113
|
+
const padR = inner - dw - padL;
|
|
114
|
+
return ["\u256D" + "\u2500".repeat(inner) + "\u256E", `\u2502${" ".repeat(padL)}${title}${" ".repeat(padR)}\u2502`, "\u2570" + "\u2500".repeat(inner) + "\u256F"];
|
|
115
|
+
}
|
|
116
|
+
|
|
56
117
|
// src/panel/panel.ts
|
|
57
118
|
function bar(value, total, width = 20) {
|
|
58
119
|
if (total === 0) return "";
|
|
@@ -61,6 +122,7 @@ function bar(value, total, width = 20) {
|
|
|
61
122
|
}
|
|
62
123
|
function buildStatusPanel(input) {
|
|
63
124
|
const { tokenCount, state, nudge, modelContextLimit } = input;
|
|
125
|
+
const L = { ...DEFAULT_PANEL_LABELS, ...input.labels };
|
|
64
126
|
const fmt = input.fmtTokens ?? formatCompactTokens;
|
|
65
127
|
const bd = nudge?.contextBreakdown;
|
|
66
128
|
const limit = modelContextLimit;
|
|
@@ -74,36 +136,36 @@ function buildStatusPanel(input) {
|
|
|
74
136
|
const activeBlocksList = state.blocks.filter((b) => b.active);
|
|
75
137
|
const totalBlocksList = state.blocks;
|
|
76
138
|
const lines = [];
|
|
77
|
-
lines.push(
|
|
78
|
-
lines.push("\u2502 ACP Context Analysis \u2502");
|
|
79
|
-
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");
|
|
139
|
+
lines.push(...renderTitleBox(L.title));
|
|
80
140
|
if (input.version) lines.push(input.version);
|
|
81
141
|
lines.push("");
|
|
82
|
-
lines.push(
|
|
142
|
+
lines.push(fill(L.context, { pct: displayPct, used: fmt(displayTotal), limit: fmt(limit) }));
|
|
83
143
|
if (nudge && bd) {
|
|
84
144
|
const growth = bd.growth;
|
|
85
145
|
if (growth > 0 && displayTotal > 0) {
|
|
86
|
-
lines.push(
|
|
146
|
+
lines.push(fill(L.growth, { growth: fmt(growth) }));
|
|
87
147
|
}
|
|
88
148
|
lines.push("");
|
|
89
|
-
|
|
149
|
+
let sentLine = fill(L.sent, { sent: fmt(sentTotal) });
|
|
150
|
+
if (limit > 0) sentLine += fill(L.sentOfLimit, { pct: sentPct });
|
|
151
|
+
lines.push(sentLine);
|
|
90
152
|
if (input.unprunedTokens !== void 0 && sessionOnly > 0) {
|
|
91
|
-
lines.push(
|
|
153
|
+
lines.push(fill(L.sessionOnly, { only: fmt(sessionOnly) }));
|
|
92
154
|
}
|
|
93
155
|
lines.push("");
|
|
94
|
-
lines.push(
|
|
156
|
+
lines.push(L.breakdown);
|
|
95
157
|
const categories = [
|
|
96
|
-
{ label:
|
|
97
|
-
{ label:
|
|
98
|
-
{ label:
|
|
99
|
-
{ label:
|
|
100
|
-
{ label:
|
|
158
|
+
{ label: L.catTool, value: bd.tool },
|
|
159
|
+
{ label: L.catSysPrompt, value: systemPromptTokens },
|
|
160
|
+
{ label: L.catText, value: bd.text },
|
|
161
|
+
{ label: L.catCode, value: bd.code },
|
|
162
|
+
{ label: L.catSummaries, value: bd.summaries }
|
|
101
163
|
];
|
|
102
164
|
for (const cat of categories) {
|
|
103
165
|
if (cat.value <= 0) continue;
|
|
104
166
|
const pct = sentTotal > 0 ? Math.round(cat.value / sentTotal * 100) : 0;
|
|
105
167
|
const b = bar(cat.value, sentTotal);
|
|
106
|
-
lines.push(` ${cat.label
|
|
168
|
+
lines.push(` ${padLabel(cat.label, 10)} ${b} ${String(pct).padStart(3)}% ${fmt(cat.value)}`);
|
|
107
169
|
}
|
|
108
170
|
}
|
|
109
171
|
if (input.cacheUsages) {
|
|
@@ -111,17 +173,23 @@ function buildStatusPanel(input) {
|
|
|
111
173
|
if (cache.requests > 0 && cache.session !== void 0 && cache.last !== void 0) {
|
|
112
174
|
lines.push("");
|
|
113
175
|
lines.push(
|
|
114
|
-
|
|
176
|
+
fill(L.promptCache, {
|
|
177
|
+
last: formatHitRate(cache.last),
|
|
178
|
+
session: formatHitRate(cache.session),
|
|
179
|
+
read: fmt(cache.cacheRead),
|
|
180
|
+
billed: fmt(cache.billedPrompt),
|
|
181
|
+
requests: cache.requests
|
|
182
|
+
})
|
|
115
183
|
);
|
|
116
184
|
}
|
|
117
185
|
}
|
|
118
186
|
lines.push("");
|
|
119
187
|
if (nudge) {
|
|
120
188
|
if (nudge.shouldInject) {
|
|
121
|
-
const tierInfo = nudge.tier ?
|
|
122
|
-
lines.push(
|
|
189
|
+
const tierInfo = nudge.tier ? fill(L.nudgeTierInfo, { tier: nudge.tier }) : "";
|
|
190
|
+
lines.push(fill(L.nudgeActive, { tierInfo, reason: nudge.reason }));
|
|
123
191
|
} else {
|
|
124
|
-
lines.push(
|
|
192
|
+
lines.push(fill(L.nudgeIdle, { reason: nudge.reason }));
|
|
125
193
|
}
|
|
126
194
|
}
|
|
127
195
|
const ranges = viableRanges(nudge?.compressibleRanges ?? []);
|
|
@@ -132,7 +200,7 @@ function buildStatusPanel(input) {
|
|
|
132
200
|
}
|
|
133
201
|
if (activeBlocksList.length > 0) {
|
|
134
202
|
lines.push("");
|
|
135
|
-
lines.push(
|
|
203
|
+
lines.push(fill(L.blocksHeader, { active: activeBlocksList.length, total: totalBlocksList.length, tokens: fmt(state.stats.tokensCompressed) }));
|
|
136
204
|
for (const b of activeBlocksList) {
|
|
137
205
|
const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || "")}`;
|
|
138
206
|
const summaryTok = defaultCountTokens(b.summary || "");
|
|
@@ -141,20 +209,25 @@ function buildStatusPanel(input) {
|
|
|
141
209
|
}
|
|
142
210
|
} else if (totalBlocksList.length > 0) {
|
|
143
211
|
lines.push("");
|
|
144
|
-
lines.push(
|
|
212
|
+
lines.push(fill(L.blocksHeader, { active: 0, total: totalBlocksList.length, tokens: fmt(state.stats.tokensCompressed) }));
|
|
145
213
|
} else {
|
|
146
214
|
lines.push("");
|
|
147
|
-
lines.push(
|
|
215
|
+
lines.push(L.blocksNone);
|
|
148
216
|
}
|
|
149
217
|
lines.push("");
|
|
150
|
-
lines.push(
|
|
218
|
+
lines.push(L.tagVisibility);
|
|
151
219
|
return lines.join("\n");
|
|
152
220
|
}
|
|
153
221
|
export {
|
|
222
|
+
DEFAULT_PANEL_LABELS,
|
|
154
223
|
buildStatusPanel,
|
|
155
224
|
cacheHitStats,
|
|
225
|
+
displayWidth,
|
|
226
|
+
fill,
|
|
156
227
|
formatCompactTokens,
|
|
157
228
|
formatHitRate,
|
|
229
|
+
padLabel,
|
|
230
|
+
renderTitleBox,
|
|
158
231
|
topicFallback
|
|
159
232
|
};
|
|
160
233
|
//# sourceMappingURL=index.js.map
|
package/dist/panel/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/panel/topic.ts","../../src/panel/format.ts","../../src/panel/cache.ts","../../src/panel/panel.ts"],"sourcesContent":["/** Short topic for a block when the model did not provide one: first sentence\n * segment, leading quotes stripped, truncated to 30 chars with an ellipsis.\n * Used wherever block lists are rendered (panels, search results). */\nexport function topicFallback(summary: string): string {\n const first = summary.split(/[.\\n]/)[0] ?? \"\";\n const t = first.trim().replace(/^[\"'`]+/, \"\").trim();\n return t.length <= 30 ? t : `${t.slice(0, 30).trimEnd()}…`;\n}\n","/** Compact token formatting for user-facing panels: lowercase k/M with the\n * same thresholds as the hosts' footers (<1000 → raw, <10000 → one decimal\n * k, <1e6 → rounded k, <1e7 → one decimal M). */\nexport function formatCompactTokens(count: number): string {\n if (count < 1000) return count.toString();\n if (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n if (count < 1000000) return `${Math.round(count / 1000)}k`;\n if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n return `${Math.round(count / 1000000)}M`;\n}\n","/** Cache hit-rate math for the /acp status panel.\n *\n * Hosts feed per-request prompt-cache usage samples (from assistant\n * messages' provider-reported `usage`); the kernel derives the display\n * numbers. A request COUNTS only when the provider reported cache\n * activity for it (`cacheRead + cacheWrite > 0`): providers without\n * prompt-cache reporting (0/0 with plain `input`) must not drag the\n * average to a fabricated 0%. */\n\nexport interface CacheUsageSample {\n /** Non-cached conversation input tokens billed for the request. */\n input: number;\n /** Conversation tokens served from the prompt cache. */\n cacheRead: number;\n /** Conversation tokens written to the prompt cache (cache creation). */\n cacheWrite: number;\n}\n\nexport interface CacheHitSummary {\n /** Session hit rate (0..1), weighted by billed prompt tokens.\n * Undefined when no request reported cache activity. */\n session: number | undefined;\n /** Hit rate (0..1) of the most recent request that reported cache\n * activity. Undefined when no request reported any. */\n last: number | undefined;\n /** Counted requests (provider reported cache activity). */\n requests: number;\n /** Cumulative tokens served from cache. */\n cacheRead: number;\n /** Cumulative billed prompt tokens (input + cacheRead + cacheWrite). */\n billedPrompt: number;\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" && Number.isFinite(v) && v > 0 ? v : 0;\n}\n\n/** Aggregate per-request cache usage into panel display numbers. */\nexport function cacheHitStats(usages: readonly CacheUsageSample[]): CacheHitSummary {\n let cacheRead = 0;\n let billedPrompt = 0;\n let requests = 0;\n let last: number | undefined;\n for (const u of usages) {\n const read = num(u?.cacheRead);\n const write = num(u?.cacheWrite);\n const input = num(u?.input);\n // No cache signal on this request → excluded entirely (see header).\n if (read + write <= 0) continue;\n const total = read + write + input;\n if (total <= 0) continue;\n cacheRead += read;\n billedPrompt += total;\n requests += 1;\n last = read / total;\n }\n return {\n session: requests > 0 ? cacheRead / billedPrompt : undefined,\n last,\n requests,\n cacheRead,\n billedPrompt,\n };\n}\n\n/** Format a 0..1 rate as a percentage with one decimal (\"92.3%\"). */\nexport function formatHitRate(rate: number): string {\n return `${(Math.max(0, Math.min(1, rate)) * 100).toFixed(1)}%`;\n}\n","import { defaultCountTokens } from \"../tokenize.js\";\nimport { formatRanges } from \"../nudge-text.js\";\nimport type { CompressionState, NudgeDecision } from \"../types.js\";\nimport { formatCompactTokens } from \"./format.js\";\nimport { topicFallback } from \"./topic.js\";\nimport { viableRanges } from \"../viable.js\";\nimport { cacheHitStats, formatHitRate, type CacheUsageSample } from \"./cache.js\";\n\nexport interface StatusPanelInput {\n /** Adapter identifier for the header, e.g. \"billion-context-omp@0.1.6\".\n * Omit to hide the version line. */\n version?: string;\n /** Host session accounting — the SAME number the host footer displays.\n * It includes compressed originals (summaries stay in the window), so it\n * shrinks slower than the sent view when compression prunes the\n * per-request projection. */\n tokenCount: number;\n /** Measured token count of the host system prompt (host-specific to\n * obtain; the kernel breakdown does not see it). */\n systemPromptTokens: number;\n /** Kernel state (blocks drive the Blocks section). */\n state: CompressionState;\n /** The nudge decision from core.processTurn for this turn, if any. The\n * panel applies the viability filter to compressibleRanges itself. */\n nudge: NudgeDecision | undefined;\n /** Configured model context window, in tokens. */\n modelContextLimit: number;\n /** Estimate of the FULL (unpruned) core-message projection, computed\n * with the SAME countTokens the core uses (kernel default = CJK-aware\n * defaultCountTokens) — the same estimation scale as the kernel\n * breakdown. When provided, the panel derives `Session-only` on that\n * scale (unpruned − sent). Without it the line is omitted: subtracting\n * the host's provider-scale number from an estimate-scale number\n * invents a third, meaningless scale (issue #18 \"看板统计的和拆分的有差异\"). */\n unprunedTokens?: number;\n /** Per-request prompt-cache usage (from assistant messages' provider-\n * reported `usage`). Requests without cache reporting are excluded by\n * cacheHitStats; when no counted request remains, the section is\n * omitted entirely. Omit the field to hide the section. */\n cacheUsages?: ReadonlyArray<CacheUsageSample>;\n /** Token formatter override (defaults to formatCompactTokens). */\n fmtTokens?: (n: number) => string;\n}\n\nfunction bar(value: number, total: number, width: number = 20): string {\n if (total === 0) return \"\";\n const filled = Math.max(0, Math.min(width, Math.round((value / total) * width)));\n return \"█\".repeat(filled) + \"░\".repeat(width - filled);\n}\n\n/** Render the /acp status panel. Three token numbers, each labeled with\n * its own scale, never mixed in arithmetic:\n * - Session accounting (host footer scale): the host's reported context\n * size INCLUDING compressed originals (summaries stay in the window), so\n * it shrinks slower than the sent view when compression prunes the\n * per-request projection.\n * - Sent view (estimated, kernel countTokens scale): what actually\n * reaches the LLM after compression (kernel's classification over the\n * pruned projection + measured system prompt). This is the number\n * compression controls.\n * - Session-only (estimated, kernel countTokens scale): unpruned\n * projection − sent view; the compressed originals pruned from every\n * request.\n * Subtracting the host number from an estimate produced numbers that\n * reconciled with neither scale (\"Framework 390K\", \"session-only 29k vs\n * 112k compressed\") — that is what issue #18 reported. */\nexport function buildStatusPanel(input: StatusPanelInput): string {\n const { tokenCount, state, nudge, modelContextLimit } = input;\n const fmt = input.fmtTokens ?? formatCompactTokens;\n const bd = nudge?.contextBreakdown;\n const limit = modelContextLimit;\n const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;\n const systemPromptTokens = input.systemPromptTokens;\n const sentTotal = classified + systemPromptTokens;\n // Same-scale derivation only: both sides use the core's countTokens\n // estimate (kernel default = CJK-aware defaultCountTokens). The host\n // footer's tokenCount (provider-anchored, session-tree) is displayed as\n // its own line and never fed into an arithmetic difference with these.\n const sessionOnly = input.unprunedTokens !== undefined ? Math.max(0, input.unprunedTokens - sentTotal) : 0;\n const displayTotal = tokenCount;\n const displayPct = limit > 0 ? Math.round((displayTotal / limit) * 100) : 0;\n const sentPct = limit > 0 ? Math.round((sentTotal / limit) * 100) : 0;\n const activeBlocksList = state.blocks.filter((b) => b.active);\n const totalBlocksList = state.blocks;\n\n const lines: string[] = [];\n\n lines.push(\"╭─────────────────────────────────────────────╮\");\n lines.push(\"│ ACP Context Analysis │\");\n lines.push(\"╰─────────────────────────────────────────────╯\");\n if (input.version) lines.push(input.version);\n lines.push(\"\");\n lines.push(`Context (session accounting, host footer scale): ${displayPct}% (${fmt(displayTotal)} / ${fmt(limit)}) — includes compressed originals; shrinks slower than the sent view`);\n\n if (nudge && bd) {\n const growth = bd.growth;\n if (growth > 0 && displayTotal > 0) {\n lines.push(`Growth: +${fmt(growth)} since last nudge`);\n }\n lines.push(\"\");\n lines.push(`Sent to LLM (after compression, est.): ${fmt(sentTotal)}${limit > 0 ? ` (${sentPct}% of limit)` : \"\"}`);\n if (input.unprunedTokens !== undefined && sessionOnly > 0) {\n lines.push(`Session-only (compressed originals, est.): ${fmt(sessionOnly)} — pruned from every request; the footer/nudge still count them`);\n }\n lines.push(\"\");\n lines.push(\"Token Breakdown (sent view):\");\n\n const categories: Array<{ label: string; value: number }> = [\n { label: \"Tool\", value: bd.tool },\n { label: \"SysPrompt\", value: systemPromptTokens },\n { label: \"Text\", value: bd.text },\n { label: \"Code\", value: bd.code },\n { label: \"Summaries\", value: bd.summaries },\n ];\n\n for (const cat of categories) {\n if (cat.value <= 0) continue;\n const pct = sentTotal > 0 ? Math.round((cat.value / sentTotal) * 100) : 0;\n const b = bar(cat.value, sentTotal);\n lines.push(` ${cat.label.padEnd(10)} ${b} ${String(pct).padStart(3)}% ${fmt(cat.value)}`);\n }\n }\n\n if (input.cacheUsages) {\n const cache = cacheHitStats(input.cacheUsages);\n if (cache.requests > 0 && cache.session !== undefined && cache.last !== undefined) {\n lines.push(\"\");\n lines.push(\n `Prompt cache (provider-reported): ${formatHitRate(cache.last)} last · ${formatHitRate(cache.session)} session avg — ${fmt(cache.cacheRead)} of ${fmt(cache.billedPrompt)} billed prompt tokens served from cache (${cache.requests} req)`,\n );\n }\n }\n\n lines.push(\"\");\n\n if (nudge) {\n if (nudge.shouldInject) {\n const tierInfo = nudge.tier ? ` [T${nudge.tier} distillation]` : \"\";\n lines.push(`Nudge: ACTIVE${tierInfo} — ${nudge.reason}`);\n } else {\n lines.push(`Nudge: idle — ${nudge.reason}`);\n }\n }\n\n const ranges = viableRanges(nudge?.compressibleRanges ?? []);\n const protectedRanges = nudge?.protectedRanges ?? [];\n if (ranges.length > 0 || protectedRanges.length > 0) {\n lines.push(\"\");\n lines.push(formatRanges(ranges, protectedRanges));\n }\n\n if (activeBlocksList.length > 0) {\n lines.push(\"\");\n lines.push(`Blocks: ${activeBlocksList.length} active / ${totalBlocksList.length} total (${fmt(state.stats.tokensCompressed)} tokens compressed, cumulative)`);\n for (const b of activeBlocksList) {\n const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || \"\")}`;\n const summaryTok = defaultCountTokens(b.summary || \"\");\n const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;\n lines.push(` [${b.blockId}] T${b.tier} ${fmt(origTok)}→${fmt(summaryTok)}${topic}`);\n }\n } else if (totalBlocksList.length > 0) {\n lines.push(\"\");\n lines.push(`Blocks: 0 active / ${totalBlocksList.length} total (${fmt(state.stats.tokensCompressed)} tokens compressed, cumulative)`);\n } else {\n lines.push(\"\");\n lines.push(\"Blocks: none (nothing compressed yet)\");\n }\n\n lines.push(\"\");\n lines.push(\"Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.\");\n\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;AAGO,SAAS,cAAc,SAAyB;AACrD,QAAM,QAAQ,QAAQ,MAAM,OAAO,EAAE,CAAC,KAAK;AAC3C,QAAM,IAAI,MAAM,KAAK,EAAE,QAAQ,WAAW,EAAE,EAAE,KAAK;AACnD,SAAO,EAAE,UAAU,KAAK,IAAI,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,QAAQ,CAAC;AACzD;;;ACJO,SAAS,oBAAoB,OAAuB;AACzD,MAAI,QAAQ,IAAM,QAAO,MAAM,SAAS;AACxC,MAAI,QAAQ,IAAO,QAAO,IAAI,QAAQ,KAAM,QAAQ,CAAC,CAAC;AACtD,MAAI,QAAQ,IAAS,QAAO,GAAG,KAAK,MAAM,QAAQ,GAAI,CAAC;AACvD,MAAI,QAAQ,IAAU,QAAO,IAAI,QAAQ,KAAS,QAAQ,CAAC,CAAC;AAC5D,SAAO,GAAG,KAAK,MAAM,QAAQ,GAAO,CAAC;AACvC;;;ACwBA,SAAS,IAAI,GAAoB;AAC/B,SAAO,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AACpE;AAGO,SAAS,cAAc,QAAsD;AAClF,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,MAAI,WAAW;AACf,MAAI;AACJ,aAAW,KAAK,QAAQ;AACtB,UAAM,OAAO,IAAI,GAAG,SAAS;AAC7B,UAAM,QAAQ,IAAI,GAAG,UAAU;AAC/B,UAAM,QAAQ,IAAI,GAAG,KAAK;AAE1B,QAAI,OAAO,SAAS,EAAG;AACvB,UAAM,QAAQ,OAAO,QAAQ;AAC7B,QAAI,SAAS,EAAG;AAChB,iBAAa;AACb,oBAAgB;AAChB,gBAAY;AACZ,WAAO,OAAO;AAAA,EAChB;AACA,SAAO;AAAA,IACL,SAAS,WAAW,IAAI,YAAY,eAAe;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGO,SAAS,cAAc,MAAsB;AAClD,SAAO,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC7D;;;ACxBA,SAAS,IAAI,OAAe,OAAe,QAAgB,IAAY;AACrE,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,MAAO,QAAQ,QAAS,KAAK,CAAC,CAAC;AAC/E,SAAO,SAAI,OAAO,MAAM,IAAI,SAAI,OAAO,QAAQ,MAAM;AACvD;AAkBO,SAAS,iBAAiB,OAAiC;AAChE,QAAM,EAAE,YAAY,OAAO,OAAO,kBAAkB,IAAI;AACxD,QAAM,MAAM,MAAM,aAAa;AAC/B,QAAM,KAAK,OAAO;AAClB,QAAM,QAAQ;AACd,QAAM,aAAa,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO;AACjF,QAAM,qBAAqB,MAAM;AACjC,QAAM,YAAY,aAAa;AAK/B,QAAM,cAAc,MAAM,mBAAmB,SAAY,KAAK,IAAI,GAAG,MAAM,iBAAiB,SAAS,IAAI;AACzG,QAAM,eAAe;AACrB,QAAM,aAAa,QAAQ,IAAI,KAAK,MAAO,eAAe,QAAS,GAAG,IAAI;AAC1E,QAAM,UAAU,QAAQ,IAAI,KAAK,MAAO,YAAY,QAAS,GAAG,IAAI;AACpE,QAAM,mBAAmB,MAAM,OAAO,OAAO,CAAC,MAAM,EAAE,MAAM;AAC5D,QAAM,kBAAkB,MAAM;AAE9B,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,4RAAiD;AAC5D,QAAM,KAAK,2DAAiD;AAC5D,QAAM,KAAK,4RAAiD;AAC5D,MAAI,MAAM,QAAS,OAAM,KAAK,MAAM,OAAO;AAC3C,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,oDAAoD,UAAU,MAAM,IAAI,YAAY,CAAC,MAAM,IAAI,KAAK,CAAC,2EAAsE;AAEtL,MAAI,SAAS,IAAI;AACf,UAAM,SAAS,GAAG;AAClB,QAAI,SAAS,KAAK,eAAe,GAAG;AAClC,YAAM,KAAK,YAAY,IAAI,MAAM,CAAC,mBAAmB;AAAA,IACvD;AACA,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,0CAA0C,IAAI,SAAS,CAAC,GAAG,QAAQ,IAAI,KAAK,OAAO,gBAAgB,EAAE,EAAE;AAClH,QAAI,MAAM,mBAAmB,UAAa,cAAc,GAAG;AACzD,YAAM,KAAK,8CAA8C,IAAI,WAAW,CAAC,sEAAiE;AAAA,IAC5I;AACA,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,8BAA8B;AAEzC,UAAM,aAAsD;AAAA,MAC1D,EAAE,OAAO,QAAQ,OAAO,GAAG,KAAK;AAAA,MAChC,EAAE,OAAO,aAAa,OAAO,mBAAmB;AAAA,MAChD,EAAE,OAAO,QAAQ,OAAO,GAAG,KAAK;AAAA,MAChC,EAAE,OAAO,QAAQ,OAAO,GAAG,KAAK;AAAA,MAChC,EAAE,OAAO,aAAa,OAAO,GAAG,UAAU;AAAA,IAC5C;AAEA,eAAW,OAAO,YAAY;AAC5B,UAAI,IAAI,SAAS,EAAG;AACpB,YAAM,MAAM,YAAY,IAAI,KAAK,MAAO,IAAI,QAAQ,YAAa,GAAG,IAAI;AACxE,YAAM,IAAI,IAAI,IAAI,OAAO,SAAS;AAClC,YAAM,KAAK,KAAK,IAAI,MAAM,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,EAAE,SAAS,CAAC,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,EAAE;AAAA,IAC5F;AAAA,EACF;AAEA,MAAI,MAAM,aAAa;AACrB,UAAM,QAAQ,cAAc,MAAM,WAAW;AAC7C,QAAI,MAAM,WAAW,KAAK,MAAM,YAAY,UAAa,MAAM,SAAS,QAAW;AACjF,YAAM,KAAK,EAAE;AACb,YAAM;AAAA,QACJ,qCAAqC,cAAc,MAAM,IAAI,CAAC,cAAW,cAAc,MAAM,OAAO,CAAC,uBAAkB,IAAI,MAAM,SAAS,CAAC,OAAO,IAAI,MAAM,YAAY,CAAC,4CAA4C,MAAM,QAAQ;AAAA,MACrO;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,EAAE;AAEb,MAAI,OAAO;AACT,QAAI,MAAM,cAAc;AACtB,YAAM,WAAW,MAAM,OAAO,MAAM,MAAM,IAAI,mBAAmB;AACjE,YAAM,KAAK,gBAAgB,QAAQ,WAAM,MAAM,MAAM,EAAE;AAAA,IACzD,OAAO;AACL,YAAM,KAAK,sBAAiB,MAAM,MAAM,EAAE;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,SAAS,aAAa,OAAO,sBAAsB,CAAC,CAAC;AAC3D,QAAM,kBAAkB,OAAO,mBAAmB,CAAC;AACnD,MAAI,OAAO,SAAS,KAAK,gBAAgB,SAAS,GAAG;AACnD,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,aAAa,QAAQ,eAAe,CAAC;AAAA,EAClD;AAEA,MAAI,iBAAiB,SAAS,GAAG;AAC/B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,WAAW,iBAAiB,MAAM,aAAa,gBAAgB,MAAM,WAAW,IAAI,MAAM,MAAM,gBAAgB,CAAC,iCAAiC;AAC7J,eAAW,KAAK,kBAAkB;AAChC,YAAM,QAAQ,EAAE,QAAQ,KAAK,EAAE,KAAK,KAAK,KAAK,cAAc,EAAE,WAAW,EAAE,CAAC;AAC5E,YAAM,aAAa,mBAAmB,EAAE,WAAW,EAAE;AACrD,YAAM,UAAU,EAAE,mBAAmB,IAAI,EAAE,mBAAmB;AAC9D,YAAM,KAAK,MAAM,EAAE,OAAO,MAAM,EAAE,IAAI,IAAI,IAAI,OAAO,CAAC,SAAI,IAAI,UAAU,CAAC,GAAG,KAAK,EAAE;AAAA,IACrF;AAAA,EACF,WAAW,gBAAgB,SAAS,GAAG;AACrC,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,sBAAsB,gBAAgB,MAAM,WAAW,IAAI,MAAM,MAAM,gBAAgB,CAAC,iCAAiC;AAAA,EACtI,OAAO;AACL,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,uCAAuC;AAAA,EACpD;AAEA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,yGAAyG;AAEpH,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/panel/topic.ts","../../src/panel/format.ts","../../src/panel/cache.ts","../../src/panel/labels.ts","../../src/panel/panel.ts"],"sourcesContent":["/** Short topic for a block when the model did not provide one: first sentence\n * segment, leading quotes stripped, truncated to 30 chars with an ellipsis.\n * Used wherever block lists are rendered (panels, search results). */\nexport function topicFallback(summary: string): string {\n const first = summary.split(/[.\\n]/)[0] ?? \"\";\n const t = first.trim().replace(/^[\"'`]+/, \"\").trim();\n return t.length <= 30 ? t : `${t.slice(0, 30).trimEnd()}…`;\n}\n","/** Compact token formatting for user-facing panels: lowercase k/M with the\n * same thresholds as the hosts' footers (<1000 → raw, <10000 → one decimal\n * k, <1e6 → rounded k, <1e7 → one decimal M). */\nexport function formatCompactTokens(count: number): string {\n if (count < 1000) return count.toString();\n if (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n if (count < 1000000) return `${Math.round(count / 1000)}k`;\n if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n return `${Math.round(count / 1000000)}M`;\n}\n","/** Cache hit-rate math for the /acp status panel.\n *\n * Hosts feed per-request prompt-cache usage samples (from assistant\n * messages' provider-reported `usage`); the kernel derives the display\n * numbers. A request COUNTS only when the provider reported cache\n * activity for it (`cacheRead + cacheWrite > 0`): providers without\n * prompt-cache reporting (0/0 with plain `input`) must not drag the\n * average to a fabricated 0%. */\n\nexport interface CacheUsageSample {\n /** Non-cached conversation input tokens billed for the request. */\n input: number;\n /** Conversation tokens served from the prompt cache. */\n cacheRead: number;\n /** Conversation tokens written to the prompt cache (cache creation). */\n cacheWrite: number;\n}\n\nexport interface CacheHitSummary {\n /** Session hit rate (0..1), weighted by billed prompt tokens.\n * Undefined when no request reported cache activity. */\n session: number | undefined;\n /** Hit rate (0..1) of the most recent request that reported cache\n * activity. Undefined when no request reported any. */\n last: number | undefined;\n /** Counted requests (provider reported cache activity). */\n requests: number;\n /** Cumulative tokens served from cache. */\n cacheRead: number;\n /** Cumulative billed prompt tokens (input + cacheRead + cacheWrite). */\n billedPrompt: number;\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" && Number.isFinite(v) && v > 0 ? v : 0;\n}\n\n/** Aggregate per-request cache usage into panel display numbers. */\nexport function cacheHitStats(usages: readonly CacheUsageSample[]): CacheHitSummary {\n let cacheRead = 0;\n let billedPrompt = 0;\n let requests = 0;\n let last: number | undefined;\n for (const u of usages) {\n const read = num(u?.cacheRead);\n const write = num(u?.cacheWrite);\n const input = num(u?.input);\n // No cache signal on this request → excluded entirely (see header).\n if (read + write <= 0) continue;\n const total = read + write + input;\n if (total <= 0) continue;\n cacheRead += read;\n billedPrompt += total;\n requests += 1;\n last = read / total;\n }\n return {\n session: requests > 0 ? cacheRead / billedPrompt : undefined,\n last,\n requests,\n cacheRead,\n billedPrompt,\n };\n}\n\n/** Format a 0..1 rate as a percentage with one decimal (\"92.3%\"). */\nexport function formatHitRate(rate: number): string {\n return `${(Math.max(0, Math.min(1, rate)) * 100).toFixed(1)}%`;\n}\n","/** Display labels for buildStatusPanel. The English defaults below are the\n * key source of truth; hosts pass Partial<PanelLabels> to localize the panel\n * (billion-context-pi issue #153). This is a DISPLAY-ONLY surface: these\n * strings render in the terminal and never reach the model, so translating\n * them cannot affect compression/nudge behavior.\n *\n * Templates use `{name}` placeholders filled by fill(). Keys omitted by a\n * host pack fall back to the English default per key (partial packs degrade\n * gracefully instead of failing wholesale). */\nexport interface PanelLabels {\n /** Title rendered inside the decorative box. */\n title: string;\n /** Params: pct (number), used / limit (formatted token counts). */\n context: string;\n /** Params: growth (formatted). Line rendered only when growth > 0. */\n growth: string;\n /** Params: sent (formatted). */\n sent: string;\n /** Params: pct (number). Appended to the sent line only when a context\n * limit is configured; omit the placeholder in a pack to drop it. */\n sentOfLimit: string;\n /** Params: only (formatted). Line rendered only when unprunedTokens is\n * provided and the derived value is positive. */\n sessionOnly: string;\n breakdown: string;\n catTool: string;\n catSysPrompt: string;\n catText: string;\n catCode: string;\n catSummaries: string;\n /** Params: last / session (hit-rate strings), read / billed (formatted),\n * requests (number). */\n promptCache: string;\n /** Params: tierInfo (string, empty when no tier), reason (verbatim model-\n * facing text — keep it in your template verbatim). */\n nudgeActive: string;\n /** Params: tier (number). */\n nudgeTierInfo: string;\n /** Params: reason (verbatim model-facing text). */\n nudgeIdle: string;\n /** Params: active / total (numbers), tokens (formatted). Used for both the\n * active and zero-active block counts. */\n blocksHeader: string;\n /** Rendered when no blocks exist at all. */\n blocksNone: string;\n tagVisibility: string;\n}\n\nexport const DEFAULT_PANEL_LABELS: PanelLabels = {\n title: \"ACP Context Analysis\",\n context: \"Context (session accounting, host footer scale): {pct}% ({used} / {limit}) — includes compressed originals; shrinks slower than the sent view\",\n growth: \"Growth: +{growth} since last nudge\",\n sent: \"Sent to LLM (after compression, est.): {sent}\",\n sentOfLimit: \" ({pct}% of limit)\",\n sessionOnly: \"Session-only (compressed originals, est.): {only} — pruned from every request; the footer/nudge still count them\",\n breakdown: \"Token Breakdown (sent view):\",\n catTool: \"Tool\",\n catSysPrompt: \"SysPrompt\",\n catText: \"Text\",\n catCode: \"Code\",\n catSummaries: \"Summaries\",\n promptCache: \"Prompt cache (provider-reported): {last} last · {session} session avg — {read} of {billed} billed prompt tokens served from cache ({requests} req)\",\n nudgeActive: \"Nudge: ACTIVE{tierInfo} — {reason}\",\n nudgeTierInfo: \" [T{tier} distillation]\",\n nudgeIdle: \"Nudge: idle — {reason}\",\n blocksHeader: \"Blocks: {active} active / {total} total ({tokens} tokens compressed, cumulative)\",\n blocksNone: \"Blocks: none (nothing compressed yet)\",\n tagVisibility: \"Tag visibility: tags injected to LLM only (deep copy), not persisted in session, not shown in terminal.\",\n};\n\n/** Substitute `{name}` placeholders from params. Unknown placeholders are\n * left intact so a typo in a host pack is visible on screen instead of\n * silently dropping information. */\nexport function fill(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{([A-Za-z][A-Za-z0-9_]*)\\}/g, (m, k: string) => (k in params ? String(params[k]) : m));\n}\n\n// Legacy fixed-width title box (inner width 45). Frozen so the default\n// output stays byte-identical for hosts that don't pass labels.\nconst LEGACY_TITLE_BOX = [\n \"╭─────────────────────────────────────────────╮\",\n \"│ ACP Context Analysis │\",\n \"╰─────────────────────────────────────────────╯\",\n];\nconst MIN_INNER_WIDTH = 45;\n\nfunction charDisplayWidth(ch: string): number {\n const cp = ch.codePointAt(0)!;\n const wide =\n (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo\n (cp >= 0x2e80 && cp <= 0xa4cf) || // CJK radicals .. Yi syllables\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul syllables\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK compatibility ideographs\n (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK compatibility forms\n (cp >= 0xff00 && cp <= 0xff60) || // fullwidth forms\n (cp >= 0xffe0 && cp <= 0xffe6) || // halfwidth/fullwidth forms\n cp >= 0x20000; // CJK extensions and beyond\n return wide ? 2 : 1;\n}\n\nexport function displayWidth(s: string): number {\n let w = 0;\n for (const ch of s) w += charDisplayWidth(ch);\n return w;\n}\n\n/** Pad s with trailing spaces to a DISPLAY width (wide chars count as two\n * columns), keeping bar-chart columns aligned for CJK category labels.\n * Identical to String.prototype.padEnd for ASCII input. */\nexport function padLabel(s: string, width: number): string {\n const dw = displayWidth(s);\n return dw >= width ? s : s + \" \".repeat(width - dw);\n}\n\n/** Decorative title box, width-aware so CJK titles stay aligned (wide chars\n * occupy two terminal columns). The exact default title renders the legacy\n * fixed box; any other title gets a computed box that widens past\n * MIN_INNER_WIDTH when the title is long. */\nexport function renderTitleBox(title: string): string[] {\n if (title === DEFAULT_PANEL_LABELS.title) return LEGACY_TITLE_BOX;\n const dw = displayWidth(title);\n const inner = Math.max(MIN_INNER_WIDTH, dw + 2);\n const padL = Math.floor((inner - dw) / 2);\n const padR = inner - dw - padL;\n return [\"╭\" + \"─\".repeat(inner) + \"╮\", `│${\" \".repeat(padL)}${title}${\" \".repeat(padR)}│`, \"╰\" + \"─\".repeat(inner) + \"╯\"];\n}\n","import { defaultCountTokens } from \"../tokenize.js\";\nimport { formatRanges } from \"../nudge-text.js\";\nimport type { CompressionState, NudgeDecision } from \"../types.js\";\nimport { formatCompactTokens } from \"./format.js\";\nimport { topicFallback } from \"./topic.js\";\nimport { viableRanges } from \"../viable.js\";\nimport { cacheHitStats, formatHitRate, type CacheUsageSample } from \"./cache.js\";\nimport { DEFAULT_PANEL_LABELS, fill, padLabel, renderTitleBox, type PanelLabels } from \"./labels.js\";\n\nexport interface StatusPanelInput {\n /** Adapter identifier for the header, e.g. \"billion-context-omp@0.1.6\".\n * Omit to hide the version line. */\n version?: string;\n /** Host session accounting — the SAME number the host footer displays.\n * It includes compressed originals (summaries stay in the window), so it\n * shrinks slower than the sent view when compression prunes the\n * per-request projection. */\n tokenCount: number;\n /** Measured token count of the host system prompt (host-specific to\n * obtain; the kernel breakdown does not see it). */\n systemPromptTokens: number;\n /** Kernel state (blocks drive the Blocks section). */\n state: CompressionState;\n /** The nudge decision from core.processTurn for this turn, if any. The\n * panel applies the viability filter to compressibleRanges itself. */\n nudge: NudgeDecision | undefined;\n /** Configured model context window, in tokens. */\n modelContextLimit: number;\n /** Estimate of the FULL (unpruned) core-message projection, computed\n * with the SAME countTokens the core uses (kernel default = CJK-aware\n * defaultCountTokens) — the same estimation scale as the kernel\n * breakdown. When provided, the panel derives `Session-only` on that\n * scale (unpruned − sent). Without it the line is omitted: subtracting\n * the host's provider-scale number from an estimate-scale number\n * invents a third, meaningless scale (issue #18 \"看板统计的和拆分的有差异\"). */\n unprunedTokens?: number;\n /** Localized display labels; every key optional, per-key fallback to the\n * English defaults (DEFAULT_PANEL_LABELS). Display-only — model-facing\n * text is unaffected. */\n labels?: Partial<PanelLabels>;\n /** Per-request prompt-cache usage (from assistant messages' provider-\n * reported `usage`). Requests without cache reporting are excluded by\n * cacheHitStats; when no counted request remains, the section is\n * omitted entirely. Omit the field to hide the section. */\n cacheUsages?: ReadonlyArray<CacheUsageSample>;\n /** Token formatter override (defaults to formatCompactTokens). */\n fmtTokens?: (n: number) => string;\n}\n\nfunction bar(value: number, total: number, width: number = 20): string {\n if (total === 0) return \"\";\n const filled = Math.max(0, Math.min(width, Math.round((value / total) * width)));\n return \"█\".repeat(filled) + \"░\".repeat(width - filled);\n}\n\n/** Render the /acp status panel. Three token numbers, each labeled with\n * its own scale, never mixed in arithmetic:\n * - Session accounting (host footer scale): the host's reported context\n * size INCLUDING compressed originals (summaries stay in the window), so\n * it shrinks slower than the sent view when compression prunes the\n * per-request projection.\n * - Sent view (estimated, kernel countTokens scale): what actually\n * reaches the LLM after compression (kernel's classification over the\n * pruned projection + measured system prompt). This is the number\n * compression controls.\n * - Session-only (estimated, kernel countTokens scale): unpruned\n * projection − sent view; the compressed originals pruned from every\n * request.\n * Subtracting the host number from an estimate produced numbers that\n * reconciled with neither scale (\"Framework 390K\", \"session-only 29k vs\n * 112k compressed\") — that is what issue #18 reported. */\nexport function buildStatusPanel(input: StatusPanelInput): string {\n const { tokenCount, state, nudge, modelContextLimit } = input;\n const L: PanelLabels = { ...DEFAULT_PANEL_LABELS, ...input.labels };\n const fmt = input.fmtTokens ?? formatCompactTokens;\n const bd = nudge?.contextBreakdown;\n const limit = modelContextLimit;\n const classified = bd ? bd.system + bd.tool + bd.summaries + bd.code + bd.text : 0;\n const systemPromptTokens = input.systemPromptTokens;\n const sentTotal = classified + systemPromptTokens;\n // Same-scale derivation only: both sides use the core's countTokens\n // estimate (kernel default = CJK-aware defaultCountTokens). The host\n // footer's tokenCount (provider-anchored, session-tree) is displayed as\n // its own line and never fed into an arithmetic difference with these.\n const sessionOnly = input.unprunedTokens !== undefined ? Math.max(0, input.unprunedTokens - sentTotal) : 0;\n const displayTotal = tokenCount;\n const displayPct = limit > 0 ? Math.round((displayTotal / limit) * 100) : 0;\n const sentPct = limit > 0 ? Math.round((sentTotal / limit) * 100) : 0;\n const activeBlocksList = state.blocks.filter((b) => b.active);\n const totalBlocksList = state.blocks;\n\n const lines: string[] = [];\n\n lines.push(...renderTitleBox(L.title));\n if (input.version) lines.push(input.version);\n lines.push(\"\");\n lines.push(fill(L.context, { pct: displayPct, used: fmt(displayTotal), limit: fmt(limit) }));\n\n if (nudge && bd) {\n const growth = bd.growth;\n if (growth > 0 && displayTotal > 0) {\n lines.push(fill(L.growth, { growth: fmt(growth) }));\n }\n lines.push(\"\");\n let sentLine = fill(L.sent, { sent: fmt(sentTotal) });\n if (limit > 0) sentLine += fill(L.sentOfLimit, { pct: sentPct });\n lines.push(sentLine);\n if (input.unprunedTokens !== undefined && sessionOnly > 0) {\n lines.push(fill(L.sessionOnly, { only: fmt(sessionOnly) }));\n }\n lines.push(\"\");\n lines.push(L.breakdown);\n\n const categories: Array<{ label: string; value: number }> = [\n { label: L.catTool, value: bd.tool },\n { label: L.catSysPrompt, value: systemPromptTokens },\n { label: L.catText, value: bd.text },\n { label: L.catCode, value: bd.code },\n { label: L.catSummaries, value: bd.summaries },\n ];\n\n for (const cat of categories) {\n if (cat.value <= 0) continue;\n const pct = sentTotal > 0 ? Math.round((cat.value / sentTotal) * 100) : 0;\n const b = bar(cat.value, sentTotal);\n lines.push(` ${padLabel(cat.label, 10)} ${b} ${String(pct).padStart(3)}% ${fmt(cat.value)}`);\n }\n }\n\n if (input.cacheUsages) {\n const cache = cacheHitStats(input.cacheUsages);\n if (cache.requests > 0 && cache.session !== undefined && cache.last !== undefined) {\n lines.push(\"\");\n lines.push(\n fill(L.promptCache, {\n last: formatHitRate(cache.last),\n session: formatHitRate(cache.session),\n read: fmt(cache.cacheRead),\n billed: fmt(cache.billedPrompt),\n requests: cache.requests,\n }),\n );\n }\n }\n\n lines.push(\"\");\n\n if (nudge) {\n if (nudge.shouldInject) {\n const tierInfo = nudge.tier ? fill(L.nudgeTierInfo, { tier: nudge.tier }) : \"\";\n lines.push(fill(L.nudgeActive, { tierInfo, reason: nudge.reason }));\n } else {\n lines.push(fill(L.nudgeIdle, { reason: nudge.reason }));\n }\n }\n\n const ranges = viableRanges(nudge?.compressibleRanges ?? []);\n const protectedRanges = nudge?.protectedRanges ?? [];\n if (ranges.length > 0 || protectedRanges.length > 0) {\n lines.push(\"\");\n lines.push(formatRanges(ranges, protectedRanges));\n }\n\n if (activeBlocksList.length > 0) {\n lines.push(\"\");\n lines.push(fill(L.blocksHeader, { active: activeBlocksList.length, total: totalBlocksList.length, tokens: fmt(state.stats.tokensCompressed) }));\n for (const b of activeBlocksList) {\n const topic = b.topic ? `: ${b.topic}` : `: ${topicFallback(b.summary || \"\")}`;\n const summaryTok = defaultCountTokens(b.summary || \"\");\n const origTok = b.compressedTokens > 0 ? b.compressedTokens : summaryTok;\n lines.push(` [${b.blockId}] T${b.tier} ${fmt(origTok)}→${fmt(summaryTok)}${topic}`);\n }\n } else if (totalBlocksList.length > 0) {\n lines.push(\"\");\n lines.push(fill(L.blocksHeader, { active: 0, total: totalBlocksList.length, tokens: fmt(state.stats.tokensCompressed) }));\n } else {\n lines.push(\"\");\n lines.push(L.blocksNone);\n }\n\n lines.push(\"\");\n lines.push(L.tagVisibility);\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Display labels for buildStatusPanel. The English defaults below are the
|
|
2
|
+
* key source of truth; hosts pass Partial<PanelLabels> to localize the panel
|
|
3
|
+
* (billion-context-pi issue #153). This is a DISPLAY-ONLY surface: these
|
|
4
|
+
* strings render in the terminal and never reach the model, so translating
|
|
5
|
+
* them cannot affect compression/nudge behavior.
|
|
6
|
+
*
|
|
7
|
+
* Templates use `{name}` placeholders filled by fill(). Keys omitted by a
|
|
8
|
+
* host pack fall back to the English default per key (partial packs degrade
|
|
9
|
+
* gracefully instead of failing wholesale). */
|
|
10
|
+
export interface PanelLabels {
|
|
11
|
+
/** Title rendered inside the decorative box. */
|
|
12
|
+
title: string;
|
|
13
|
+
/** Params: pct (number), used / limit (formatted token counts). */
|
|
14
|
+
context: string;
|
|
15
|
+
/** Params: growth (formatted). Line rendered only when growth > 0. */
|
|
16
|
+
growth: string;
|
|
17
|
+
/** Params: sent (formatted). */
|
|
18
|
+
sent: string;
|
|
19
|
+
/** Params: pct (number). Appended to the sent line only when a context
|
|
20
|
+
* limit is configured; omit the placeholder in a pack to drop it. */
|
|
21
|
+
sentOfLimit: string;
|
|
22
|
+
/** Params: only (formatted). Line rendered only when unprunedTokens is
|
|
23
|
+
* provided and the derived value is positive. */
|
|
24
|
+
sessionOnly: string;
|
|
25
|
+
breakdown: string;
|
|
26
|
+
catTool: string;
|
|
27
|
+
catSysPrompt: string;
|
|
28
|
+
catText: string;
|
|
29
|
+
catCode: string;
|
|
30
|
+
catSummaries: string;
|
|
31
|
+
/** Params: last / session (hit-rate strings), read / billed (formatted),
|
|
32
|
+
* requests (number). */
|
|
33
|
+
promptCache: string;
|
|
34
|
+
/** Params: tierInfo (string, empty when no tier), reason (verbatim model-
|
|
35
|
+
* facing text — keep it in your template verbatim). */
|
|
36
|
+
nudgeActive: string;
|
|
37
|
+
/** Params: tier (number). */
|
|
38
|
+
nudgeTierInfo: string;
|
|
39
|
+
/** Params: reason (verbatim model-facing text). */
|
|
40
|
+
nudgeIdle: string;
|
|
41
|
+
/** Params: active / total (numbers), tokens (formatted). Used for both the
|
|
42
|
+
* active and zero-active block counts. */
|
|
43
|
+
blocksHeader: string;
|
|
44
|
+
/** Rendered when no blocks exist at all. */
|
|
45
|
+
blocksNone: string;
|
|
46
|
+
tagVisibility: string;
|
|
47
|
+
}
|
|
48
|
+
export declare const DEFAULT_PANEL_LABELS: PanelLabels;
|
|
49
|
+
/** Substitute `{name}` placeholders from params. Unknown placeholders are
|
|
50
|
+
* left intact so a typo in a host pack is visible on screen instead of
|
|
51
|
+
* silently dropping information. */
|
|
52
|
+
export declare function fill(template: string, params: Record<string, string | number>): string;
|
|
53
|
+
export declare function displayWidth(s: string): number;
|
|
54
|
+
/** Pad s with trailing spaces to a DISPLAY width (wide chars count as two
|
|
55
|
+
* columns), keeping bar-chart columns aligned for CJK category labels.
|
|
56
|
+
* Identical to String.prototype.padEnd for ASCII input. */
|
|
57
|
+
export declare function padLabel(s: string, width: number): string;
|
|
58
|
+
/** Decorative title box, width-aware so CJK titles stay aligned (wide chars
|
|
59
|
+
* occupy two terminal columns). The exact default title renders the legacy
|
|
60
|
+
* fixed box; any other title gets a computed box that widens past
|
|
61
|
+
* MIN_INNER_WIDTH when the title is long. */
|
|
62
|
+
export declare function renderTitleBox(title: string): string[];
|
|
63
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/panel/labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;gDAQgD;AAChD,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb;0EACsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB;sDACkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB;6BACyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB;4DACwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB;+CAC2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAC;AAEF;;qCAEqC;AACrC,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAEtF;AAyBD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED;;4DAE4D;AAC5D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;8CAG8C;AAC9C,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAOtD"}
|
package/dist/panel/panel.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CompressionState, NudgeDecision } from "../types.js";
|
|
2
2
|
import { type CacheUsageSample } from "./cache.js";
|
|
3
|
+
import { type PanelLabels } from "./labels.js";
|
|
3
4
|
export interface StatusPanelInput {
|
|
4
5
|
/** Adapter identifier for the header, e.g. "billion-context-omp@0.1.6".
|
|
5
6
|
* Omit to hide the version line. */
|
|
@@ -27,6 +28,10 @@ export interface StatusPanelInput {
|
|
|
27
28
|
* the host's provider-scale number from an estimate-scale number
|
|
28
29
|
* invents a third, meaningless scale (issue #18 "看板统计的和拆分的有差异"). */
|
|
29
30
|
unprunedTokens?: number;
|
|
31
|
+
/** Localized display labels; every key optional, per-key fallback to the
|
|
32
|
+
* English defaults (DEFAULT_PANEL_LABELS). Display-only — model-facing
|
|
33
|
+
* text is unaffected. */
|
|
34
|
+
labels?: Partial<PanelLabels>;
|
|
30
35
|
/** Per-request prompt-cache usage (from assistant messages' provider-
|
|
31
36
|
* reported `usage`). Requests without cache reporting are excluded by
|
|
32
37
|
* cacheHitStats; when no counted request remains, the section is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/panel/panel.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,OAAO,EAAgC,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/panel/panel.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,OAAO,EAAgC,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAwD,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAErG,MAAM,WAAW,gBAAgB;IAC/B;yCACqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;kCAG8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB;yDACqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,KAAK,EAAE,gBAAgB,CAAC;IACxB;2EACuE;IACvE,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;;;yEAMqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9B;;;gEAG4D;IAC5D,WAAW,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC9C,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AAQD;;;;;;;;;;;;;;;2DAe2D;AAC3D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAiHhE"}
|
package/package.json
CHANGED