import { n as estimateTokens } from "./utils-ngQzYzZD.js";
//#region src/chat/context-breakdown.ts
/**
* Context-usage breakdown — pure transforms.
*
* Turns a {@link ContextSnapshot} (the raw pieces the agent assembled for a
* run: system prompt, wire tools, deferred tools, MCP tools/instructions,
* skills catalog, ...) plus the real last-turn token total into a categorized
* {@link ContextBreakdown} the TUI panel / GUI popover render.
*
* No React, no node — a browser-context renderer imports these via
* `zidane/chat/pure`. The snapshot is captured main-side (TUI direct, GUI in
* `main/chat`) because building it reads provider/agent state; only the pure
* shaping lives here.
*
* Accuracy: per-category counts are estimates when no provider `countTokens`
* is available — the heuristic ({@link estimateTokens}, ~4 bytes/token) sizes
* each static segment and the bar is reconciled so it always sums to the real
* last-turn total (`conversation = realTotal - sum(static)`). When a provider
* exposes exact counts (Anthropic, OpenAI), the caller passes them in via
* {@link ContextSnapshot.exact} and the corresponding categories drop their
* `estimated` flag. Exact buckets (free space, autocompact buffer) are never
* estimated.
*/
const LABELS = {
	systemPrompt: "System prompt",
	rules: "Rules (AGENTS.md)",
	tools: "Tool definitions",
	mcpTools: "MCP tools",
	mcpInstructions: "MCP instructions",
	skills: "Skills",
	subagentDefs: "Subagent definitions",
	conversation: "Conversation",
	mcpToolsDeferred: "MCP tools (deferred)",
	toolsDeferred: "System tools (deferred)",
	autocompactBuffer: "Autocompact buffer",
	freeSpace: "Free space"
};
/** Sum the heuristic token sizes of a list of strings. */
function sumEstimate(parts) {
	let total = 0;
	for (const p of parts) total += estimateTokens(p);
	return total;
}
function mcpGroupTokens(group) {
	const items = group.tools.map((t) => ({
		id: `${group.server}:${t.name}`,
		label: t.name,
		tokens: estimateTokens(t.json),
		estimated: true
	}));
	let total = 0;
	for (const it of items) total += it.tokens;
	return {
		total,
		items
	};
}
/**
* Shape a {@link ContextSnapshot} into a render-ready {@link ContextBreakdown}.
*
* Live categories (system prompt, tools, MCP tools/instructions, skills,
* memory, subagent defs, conversation) sum to `snapshot.used` — `conversation`
* absorbs the remainder so the bar matches the provider-reported total exactly.
* Deferred + autocompact + free-space buckets follow.
*
* When `snapshot.exact` carries provider counts, the matching categories use
* them (system, tools) and drop their `estimated` flag; everything else stays
* on the heuristic.
*/
function buildContextBreakdown(snapshot) {
	const exact = snapshot.exact;
	const systemExact = typeof exact?.system === "number";
	const rulesTokens = snapshot.rulesBlock ? estimateTokens(snapshot.rulesBlock) : 0;
	const rulesItems = (snapshot.rulesFiles ?? []).map((f) => ({
		id: f.path,
		label: f.path,
		tokens: 0,
		estimated: false
	}));
	const skillsTokens = snapshot.skillsCatalog ? estimateTokens(snapshot.skillsCatalog) : 0;
	const mcpInstructionsTokens = snapshot.mcpInstructions ? estimateTokens(snapshot.mcpInstructions) : 0;
	const subagentTokens = snapshot.subagentDefs ? estimateTokens(snapshot.subagentDefs) : 0;
	const embeddedSubsections = rulesTokens + skillsTokens + mcpInstructionsTokens + subagentTokens;
	const systemTotal = systemExact ? exact.system : estimateTokens(snapshot.system);
	const baseSystemTokens = Math.max(0, systemTotal - embeddedSubsections);
	const nativeToolsEst = sumEstimate(snapshot.toolsJson);
	const mcpDisclosed = snapshot.mcpGroups.map(mcpGroupTokens);
	const mcpToolsEst = mcpDisclosed.reduce((a, g) => a + g.total, 0);
	let nativeToolsTokens = nativeToolsEst;
	let mcpToolsTokens = mcpToolsEst;
	let toolsEstimated = true;
	if (systemExact && typeof exact?.systemAndTools === "number") {
		const exactToolsTotal = Math.max(0, exact.systemAndTools - exact.system);
		const estTotal = nativeToolsEst + mcpToolsEst;
		if (estTotal > 0) {
			nativeToolsTokens = Math.round(exactToolsTotal * (nativeToolsEst / estTotal));
			mcpToolsTokens = exactToolsTotal - nativeToolsTokens;
		} else {
			nativeToolsTokens = exactToolsTotal;
			mcpToolsTokens = 0;
		}
		toolsEstimated = false;
	}
	const live = [];
	const push = (id, tokens, estimated, items) => {
		if (tokens <= 0 && !items?.length) return;
		live.push({
			id,
			label: LABELS[id],
			tokens: Math.max(0, tokens),
			estimated,
			...items?.length ? { items } : {}
		});
	};
	push("systemPrompt", baseSystemTokens, !systemExact);
	push("rules", rulesTokens, true, rulesItems);
	push("skills", skillsTokens, true);
	push("mcpInstructions", mcpInstructionsTokens, true);
	push("subagentDefs", subagentTokens, true);
	push("tools", nativeToolsTokens, toolsEstimated);
	push("mcpTools", mcpToolsTokens, toolsEstimated, mcpDisclosed.flatMap((g) => g.items));
	const accountedStatic = live.reduce((a, c) => a + c.tokens, 0);
	push("conversation", Math.max(0, snapshot.used - accountedStatic), !(systemExact && !toolsEstimated));
	const deferred = [];
	const deferredNativeTools = sumEstimate(snapshot.deferredToolsJson);
	if (deferredNativeTools > 0) deferred.push({
		id: "toolsDeferred",
		label: LABELS.toolsDeferred,
		tokens: deferredNativeTools,
		estimated: true,
		deferred: true
	});
	const deferredMcp = snapshot.deferredMcpGroups.map(mcpGroupTokens);
	const deferredMcpTotal = deferredMcp.reduce((a, g) => a + g.total, 0);
	if (deferredMcpTotal > 0) deferred.push({
		id: "mcpToolsDeferred",
		label: LABELS.mcpToolsDeferred,
		tokens: deferredMcpTotal,
		estimated: true,
		deferred: true,
		items: deferredMcp.flatMap((g) => g.items)
	});
	if (snapshot.autocompactBuffer > 0) deferred.push({
		id: "autocompactBuffer",
		label: LABELS.autocompactBuffer,
		tokens: snapshot.autocompactBuffer,
		estimated: false,
		deferred: true
	});
	const free = Math.max(0, snapshot.effectiveWindow - snapshot.used);
	deferred.push({
		id: "freeSpace",
		label: LABELS.freeSpace,
		tokens: free,
		estimated: false,
		deferred: true
	});
	const categories = [...live, ...deferred];
	const hasEstimates = live.some((c) => c.estimated && c.tokens > 0);
	const fraction = snapshot.effectiveWindow > 0 ? Math.max(0, Math.min(1, snapshot.used / snapshot.effectiveWindow)) : 0;
	return {
		modelId: snapshot.modelId,
		used: snapshot.used,
		effectiveWindow: snapshot.effectiveWindow,
		fraction,
		categories,
		hasEstimates,
		...snapshot.usage ? { usage: snapshot.usage } : {},
		...snapshot.activeSkills?.length ? { activeSkills: snapshot.activeSkills } : {}
	};
}
//#endregion
export { buildContextBreakdown as t };

//# sourceMappingURL=context-breakdown-kO-pDsay.js.map