acp-kernel 0.0.64-pr.255.103 → 0.0.64-pr.255.104

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.
@@ -80,7 +80,7 @@ function resolvePanelLabels(labels) {
80
80
  if (!labels) return out;
81
81
  for (const key of Object.keys(labels)) {
82
82
  const value = labels[key];
83
- if (typeof value === "string") out[key] = value;
83
+ if (typeof value === "string" && Object.hasOwn(DEFAULT_PANEL_LABELS, key)) out[key] = value;
84
84
  }
85
85
  return out;
86
86
  }
@@ -1 +1 @@
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/** Merge a partial label pack over the English defaults, ignoring entries\n * whose value is not a string (explicit `undefined` from a config merge,\n * `null`, …). A plain `{ ...defaults, ...pack }` spread lets one undefined\n * key blank out its default and crash the display-width renderers. */\nexport function resolvePanelLabels(labels?: Partial<PanelLabels>): PanelLabels {\n const out: PanelLabels = { ...DEFAULT_PANEL_LABELS };\n if (!labels) return out;\n for (const key of Object.keys(labels) as Array<keyof PanelLabels>) {\n const value = labels[key];\n if (typeof value === \"string\") out[key] = value;\n }\n return out;\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 { fill, padLabel, renderTitleBox, resolvePanelLabels, 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 = resolvePanelLabels(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":";;;;;;;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;;;ACpBO,IAAM,uBAAoC;AAAA,EAC/C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aAAa;AAAA,EACb,WAAW;AAAA,EACX,SAAS;AAAA,EACT,cAAc;AAAA,EACd,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,eAAe;AACjB;AAMO,SAAS,mBAAmB,QAA4C;AAC7E,QAAM,MAAmB,EAAE,GAAG,qBAAqB;AACnD,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,OAAO,OAAO,KAAK,MAAM,GAA+B;AACjE,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,SAAU,KAAI,GAAG,IAAI;AAAA,EAC5C;AACA,SAAO;AACT;AAKO,SAAS,KAAK,UAAkB,QAAiD;AACtF,SAAO,SAAS,QAAQ,gCAAgC,CAAC,GAAG,MAAe,KAAK,SAAS,OAAO,OAAO,CAAC,CAAC,IAAI,CAAE;AACjH;AAIA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,kBAAkB;AAExB,SAAS,iBAAiB,IAAoB;AAC5C,QAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,QAAM,OACH,MAAM,QAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACvB,MAAM;AACR,SAAO,OAAO,IAAI;AACpB;AAEO,SAAS,aAAa,GAAmB;AAC9C,MAAI,IAAI;AACR,aAAW,MAAM,EAAG,MAAK,iBAAiB,EAAE;AAC5C,SAAO;AACT;AAKO,SAAS,SAAS,GAAW,OAAuB;AACzD,QAAM,KAAK,aAAa,CAAC;AACzB,SAAO,MAAM,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,EAAE;AACpD;AAMO,SAAS,eAAe,OAAyB;AACtD,MAAI,UAAU,qBAAqB,MAAO,QAAO;AACjD,QAAM,KAAK,aAAa,KAAK;AAC7B,QAAM,QAAQ,KAAK,IAAI,iBAAiB,KAAK,CAAC;AAC9C,QAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,CAAC;AACxC,QAAM,OAAO,QAAQ,KAAK;AAC1B,SAAO,CAAC,WAAM,SAAI,OAAO,KAAK,IAAI,UAAK,SAAI,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,OAAO,IAAI,CAAC,UAAK,WAAM,SAAI,OAAO,KAAK,IAAI,QAAG;AAC1H;;;AC1FA,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,IAAI,mBAAmB,MAAM,MAAM;AACzC,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,GAAG,eAAe,EAAE,KAAK,CAAC;AACrC,MAAI,MAAM,QAAS,OAAM,KAAK,MAAM,OAAO;AAC3C,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,KAAK,EAAE,SAAS,EAAE,KAAK,YAAY,MAAM,IAAI,YAAY,GAAG,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;AAE3F,MAAI,SAAS,IAAI;AACf,UAAM,SAAS,GAAG;AAClB,QAAI,SAAS,KAAK,eAAe,GAAG;AAClC,YAAM,KAAK,KAAK,EAAE,QAAQ,EAAE,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC;AAAA,IACpD;AACA,UAAM,KAAK,EAAE;AACb,QAAI,WAAW,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC;AACpD,QAAI,QAAQ,EAAG,aAAY,KAAK,EAAE,aAAa,EAAE,KAAK,QAAQ,CAAC;AAC/D,UAAM,KAAK,QAAQ;AACnB,QAAI,MAAM,mBAAmB,UAAa,cAAc,GAAG;AACzD,YAAM,KAAK,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,WAAW,EAAE,CAAC,CAAC;AAAA,IAC5D;AACA,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,EAAE,SAAS;AAEtB,UAAM,aAAsD;AAAA,MAC1D,EAAE,OAAO,EAAE,SAAS,OAAO,GAAG,KAAK;AAAA,MACnC,EAAE,OAAO,EAAE,cAAc,OAAO,mBAAmB;AAAA,MACnD,EAAE,OAAO,EAAE,SAAS,OAAO,GAAG,KAAK;AAAA,MACnC,EAAE,OAAO,EAAE,SAAS,OAAO,GAAG,KAAK;AAAA,MACnC,EAAE,OAAO,EAAE,cAAc,OAAO,GAAG,UAAU;AAAA,IAC/C;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,SAAS,IAAI,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,EAAE,SAAS,CAAC,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,EAAE;AAAA,IAC/F;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,KAAK,EAAE,aAAa;AAAA,UAClB,MAAM,cAAc,MAAM,IAAI;AAAA,UAC9B,SAAS,cAAc,MAAM,OAAO;AAAA,UACpC,MAAM,IAAI,MAAM,SAAS;AAAA,UACzB,QAAQ,IAAI,MAAM,YAAY;AAAA,UAC9B,UAAU,MAAM;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,EAAE;AAEb,MAAI,OAAO;AACT,QAAI,MAAM,cAAc;AACtB,YAAM,WAAW,MAAM,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,KAAK,CAAC,IAAI;AAC5E,YAAM,KAAK,KAAK,EAAE,aAAa,EAAE,UAAU,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IACpE,OAAO;AACL,YAAM,KAAK,KAAK,EAAE,WAAW,EAAE,QAAQ,MAAM,OAAO,CAAC,CAAC;AAAA,IACxD;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,KAAK,EAAE,cAAc,EAAE,QAAQ,iBAAiB,QAAQ,OAAO,gBAAgB,QAAQ,QAAQ,IAAI,MAAM,MAAM,gBAAgB,EAAE,CAAC,CAAC;AAC9I,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,KAAK,EAAE,cAAc,EAAE,QAAQ,GAAG,OAAO,gBAAgB,QAAQ,QAAQ,IAAI,MAAM,MAAM,gBAAgB,EAAE,CAAC,CAAC;AAAA,EAC1H,OAAO;AACL,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,EAAE,UAAU;AAAA,EACzB;AAEA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,EAAE,aAAa;AAE1B,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/** Merge a partial label pack over the English defaults, ignoring entries\n * whose value is not a string (explicit `undefined` from a config merge,\n * `null`, …). A plain `{ ...defaults, ...pack }` spread lets one undefined\n * key blank out its default and crash the display-width renderers. */\nexport function resolvePanelLabels(labels?: Partial<PanelLabels>): PanelLabels {\n const out: PanelLabels = { ...DEFAULT_PANEL_LABELS };\n if (!labels) return out;\n for (const key of Object.keys(labels) as Array<keyof PanelLabels>) {\n const value = labels[key];\n if (typeof value === \"string\" && Object.hasOwn(DEFAULT_PANEL_LABELS, key)) out[key] = value;\n }\n return out;\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 { fill, padLabel, renderTitleBox, resolvePanelLabels, 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 = resolvePanelLabels(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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acp-kernel",
3
- "version": "0.0.64-pr.255.103",
3
+ "version": "0.0.64-pr.255.104",
4
4
  "description": "Framework-agnostic context-compression engine (model-driven, 3-tier LSM). Pure core: no host dependency.",
5
5
  "license": "MIT",
6
6
  "author": "ranxianglei",