fix(coding-agent): fixed context usage tracking to keep status and selector totals in sync

- Added context snapshot metadata to AssistantMessage for prompt and non-message token history.
- Anchored context usage calculations on assistant snapshots and computed percent numerically.
- Updated status-line, /context, selector, and interactive mode flows to share session usage totals.
- Extended status-line cache fingerprinting and invalidation for assistant usage and prompt/tool/skill changes.
This commit is contained in:
can1357
2026-06-17 12:24:20 +02:00
parent 3f32971944
commit 48decd15d7
19 changed files with 777 additions and 202 deletions
@@ -94,7 +94,7 @@ export function computeNonMessageTokens(session: AgentSession): number {
* the status-line fast path intentionally uses the equivalent collapsed total
* in `computeNonMessageTokens`.
*/
function computeNonMessageBreakdown(session: AgentSession): {
export function computeNonMessageBreakdown(session: AgentSession): {
skillsTokens: number;
toolsTokens: number;
systemContextTokens: number;
@@ -119,21 +119,36 @@ export function computeContextBreakdown(
const model = session.model;
const contextWindow = model?.contextWindow ?? 0;
let messagesTokens = 0;
const convo = session.messages;
if (convo) {
for (const message of convo) {
messagesTokens += estimateTokens(message);
}
}
const breakdown = typeof session.getContextBreakdown === "function" ? session.getContextBreakdown() : undefined;
// The rendered system prompt already contains the skill descriptions and the
// markdown tool descriptions. To present a non-overlapping breakdown:
// System prompt = total system prompt text - skills section (tool descriptions stay)
// Tools = JSON tool schema sent separately on the wire
// Skills = the skill list embedded in the system prompt
// Messages = conversation messages
const { skillsTokens, toolsTokens, systemContextTokens, systemPromptTokens } = computeNonMessageBreakdown(session);
let messagesTokens = 0;
let skillsTokens = 0;
let toolsTokens = 0;
let systemContextTokens = 0;
let systemPromptTokens = 0;
let usedTokens = 0;
if (breakdown) {
messagesTokens = breakdown.messagesTokens;
skillsTokens = breakdown.skillsTokens;
toolsTokens = breakdown.systemToolsTokens;
systemContextTokens = breakdown.systemContextTokens;
systemPromptTokens = breakdown.systemPromptTokens;
usedTokens = breakdown.usedTokens;
} else {
const convo = session.messages;
if (convo) {
for (const message of convo) {
messagesTokens += estimateTokens(message);
}
}
const nonMessage = computeNonMessageBreakdown(session);
skillsTokens = nonMessage.skillsTokens;
toolsTokens = nonMessage.toolsTokens;
systemContextTokens = nonMessage.systemContextTokens;
systemPromptTokens = nonMessage.systemPromptTokens;
usedTokens = skillsTokens + toolsTokens + systemContextTokens + systemPromptTokens + messagesTokens;
}
const categories: CategoryInfo[] = [
{ id: "systemPrompt", label: "System prompt", tokens: systemPromptTokens, color: "accent", glyph: CELL_FILLED },
@@ -155,8 +170,6 @@ export function computeContextBreakdown(
},
];
const usedTokens = categories.reduce((sum, c) => sum + c.tokens, 0);
let autoCompactBufferTokens = 0;
if (contextWindow > 0) {
const compactionSettings = session.settings.getGroup("compaction") as CompactionSettings;