import { s as previewLine } from "../format-BNOXpl-1.js";
import { i as statsByModel } from "../stats-DAKBEKjc.js";
import chalk from "chalk";
//#region src/output/terminal.ts
const PARENT_KEY = "__parent__";
function indent(depth) {
	return "  ".repeat(depth);
}
/** Style child output dim so the visual hierarchy is obvious at a glance. */
function dye(depth, s) {
	if (depth === 0) return s;
	return chalk.dim(s);
}
function printTerminalHeader(agent, model, prompt, preset, title = "⚽ Zizou") {
	console.log(`\n${title}`);
	console.log(`${chalk.bold("🤖 Model:")} ${chalk.green(model)} (${agent.meta.isOAuth ? chalk.green("oauth") : chalk.red("key")})`);
	console.log(`${chalk.bold("📝 Prompt:")} ${chalk.yellow(prompt)}`);
	if (preset.name) console.log(`${chalk.bold("🔧 Preset:")} ${chalk.cyan(preset.name)}`);
	if (preset.tools) {
		const names = Object.values(preset.tools).map((t) => t.spec.name).join(", ");
		if (names) console.log(`${chalk.bold("🔧 Tools:")} ${chalk.cyan(names)}`);
	}
	console.log();
}
/**
* Wire the depth-aware terminal renderer onto an agent's hook bus.
*
* Returns a disposer that unregisters every hook this function installed —
* hosts reusing one agent across phases can detach the renderer without
* tearing the agent down. Callers that ignore the return value keep the old
* fire-and-forget behavior.
*/
async function setupTerminalOutput(agent, model, prompt, preset, options = {}) {
	if (options.printHeader !== false) printTerminalHeader(agent, model, prompt, preset, options.title);
	const unregisters = [];
	const hook = (name, fn) => {
		const unregister = agent.hooks.hook(name, fn);
		unregisters.push(unregister);
		return unregister;
	};
	const cursors = /* @__PURE__ */ new Map();
	cursors.set(PARENT_KEY, {
		depth: 0,
		isFirstDelta: true,
		hadToolCalls: false,
		isThinking: false
	});
	/** Last emitter that wrote to stdout. Drives newline-on-switch behavior. */
	let activeEmitter = PARENT_KEY;
	function noteStreamWrite(text) {
		text.endsWith("\n");
	}
	function ensureSummaryLineStart() {
		process.stdout.write("\n\n");
		activeEmitter = PARENT_KEY;
	}
	function canPrint() {
		return options.shouldPrint?.() ?? true;
	}
	function getCursor(key, depth) {
		let c = cursors.get(key);
		if (!c) {
			c = {
				depth,
				isFirstDelta: true,
				hadToolCalls: false,
				isThinking: false
			};
			cursors.set(key, c);
		}
		return c;
	}
	/**
	* Switch the active emitter; when it changes mid-stream, drop a newline +
	* indent prefix so the next write lands cleanly on its own line.
	*/
	function switchTo(key, depth) {
		const cursor = getCursor(key, depth);
		if (activeEmitter !== key) {
			process.stdout.write(`\n${indent(depth)}`);
			activeEmitter = key;
			cursor.isFirstDelta = false;
		}
		return cursor;
	}
	hook("turn:before", () => {
		const c = cursors.get(PARENT_KEY);
		c.isFirstDelta = true;
		c.isThinking = false;
	});
	hook("stream:thinking", ({ delta }) => {
		if (!canPrint()) return;
		const c = switchTo(PARENT_KEY, 0);
		if (!c.isThinking) {
			if (c.hadToolCalls) process.stdout.write("\n");
			process.stdout.write(chalk.dim.italic("🧠 "));
			noteStreamWrite("🧠 ");
			c.isThinking = true;
			c.hadToolCalls = false;
		}
		process.stdout.write(chalk.dim.italic(delta));
		noteStreamWrite(delta);
	});
	hook("stream:text", ({ delta }) => {
		if (!canPrint()) return;
		const c = switchTo(PARENT_KEY, 0);
		if (c.isFirstDelta) {
			if (c.isThinking) {
				process.stdout.write("\n");
				c.isThinking = false;
			}
			if (c.hadToolCalls) process.stdout.write("\n");
			c.isFirstDelta = false;
			c.hadToolCalls = false;
		}
		process.stdout.write(delta);
		noteStreamWrite(delta);
	});
	hook("stream:end", () => {});
	hook("tool:before", (ctx) => {
		if (!canPrint()) return;
		if (options.shouldPrintToolCall && !options.shouldPrintToolCall(ctx)) return;
		const c = switchTo(PARENT_KEY, 0);
		if (c.isThinking) {
			process.stdout.write("\n");
			c.isThinking = false;
		}
		if (!c.hadToolCalls) process.stdout.write("\n");
		c.hadToolCalls = true;
		const stringified = JSON.stringify(ctx.input);
		const args = stringified && stringified.trim() !== "{}" ? ` (${chalk.yellow(stringified)})` : "";
		console.log(` ↳ ${chalk.cyan(ctx.name)}${args}`);
		activeEmitter = PARENT_KEY;
	});
	hook("system:before", (ctx) => {
		if (!canPrint()) return;
		console.log(chalk.cyan(`System: ${chalk.yellow(ctx.system)}`));
	});
	hook("spawn:before", ({ id, task, depth }) => {
		if (!canPrint()) return;
		const d = depth ?? 1;
		if (activeEmitter !== PARENT_KEY) process.stdout.write("\n");
		const taskPreview = previewLine(task, 80);
		console.log(`${indent(d)}${chalk.dim("🌱")} ${chalk.bold(dye(d, `[${id}]`))} ${chalk.dim("depth=")}${chalk.dim(String(d))} ${dye(d, taskPreview)}`);
		activeEmitter = PARENT_KEY;
	});
	hook("spawn:complete", (ctx) => {
		if (!canPrint()) return;
		const d = ctx.depth ?? 1;
		const stats = ctx.stats;
		if (activeEmitter !== PARENT_KEY) process.stdout.write("\n");
		const status = ctx.status === "completed" ? chalk.green("✓") : ctx.status === "aborted" ? chalk.yellow("⚠") : chalk.red("✗");
		let summary = `${indent(d)}${status} ${chalk.bold(dye(d, `[${ctx.id}]`))} ${chalk.dim(`${ctx.status ?? "completed"} ·`)} ${chalk.yellow(stats.totalIn)} in / ${chalk.green(stats.totalOut)} out`;
		if (typeof stats.cost === "number" && stats.cost > 0) summary += chalk.dim(` · $${stats.cost.toFixed(6)}`);
		if (stats.children && stats.children.length > 0) summary += chalk.dim(` · ${stats.children.length} sub-spawn${stats.children.length === 1 ? "" : "s"}`);
		console.log(summary);
		cursors.delete(ctx.id);
		activeEmitter = PARENT_KEY;
	});
	hook("spawn:error", ({ id, depth, error }) => {
		if (!canPrint()) return;
		const d = depth ?? 1;
		if (activeEmitter !== PARENT_KEY) process.stdout.write("\n");
		console.log(`${indent(d)}${chalk.red("✗")} ${chalk.bold(dye(d, `[${id}]`))} ${chalk.red(`error: ${error.message}`)}`);
		cursors.delete(id);
		activeEmitter = PARENT_KEY;
	});
	hook("child:turn:after", ({ childId }) => {
		const c = cursors.get(childId);
		if (c) {
			c.isFirstDelta = true;
			c.isThinking = false;
		}
	});
	hook("child:stream:thinking", ({ delta, childId, depth }) => {
		if (!canPrint()) return;
		const c = switchTo(childId, depth);
		if (!c.isThinking) {
			if (c.hadToolCalls) process.stdout.write(`\n${indent(depth)}`);
			process.stdout.write(chalk.dim.italic("🧠 "));
			noteStreamWrite("🧠 ");
			c.isThinking = true;
			c.hadToolCalls = false;
		}
		process.stdout.write(chalk.dim.italic(delta));
		noteStreamWrite(delta);
	});
	hook("child:stream:text", ({ delta, childId, depth }) => {
		if (!canPrint()) return;
		const c = switchTo(childId, depth);
		if (c.isFirstDelta) {
			if (c.isThinking) {
				process.stdout.write(`\n${indent(depth)}`);
				c.isThinking = false;
			}
			if (c.hadToolCalls) process.stdout.write(`\n${indent(depth)}`);
			c.isFirstDelta = false;
			c.hadToolCalls = false;
		}
		process.stdout.write(dye(depth, delta));
		noteStreamWrite(delta);
	});
	hook("child:stream:end", ({ childId }) => {
		if (activeEmitter === childId) activeEmitter = PARENT_KEY;
	});
	hook("child:tool:before", ({ name, input, childId, depth }) => {
		if (!canPrint()) return;
		const c = switchTo(childId, depth);
		if (c.isThinking) {
			process.stdout.write("\n");
			c.isThinking = false;
		}
		if (!c.hadToolCalls) process.stdout.write("\n");
		c.hadToolCalls = true;
		const stringified = JSON.stringify(input);
		const args = stringified && stringified.trim() !== "{}" ? ` (${chalk.yellow(stringified)})` : "";
		console.log(`${indent(depth)} ↳ ${chalk.cyan(name)}${args}`);
		activeEmitter = childId;
	});
	hook("child:tool:error", ({ name, error, depth }) => {
		if (!canPrint()) return;
		if (activeEmitter !== PARENT_KEY) process.stdout.write("\n");
		console.log(`${indent(depth)} ${chalk.red("✗")} ${chalk.cyan(name)}: ${chalk.red(error.message)}`);
		activeEmitter = PARENT_KEY;
	});
	hook("agent:done", (stats) => {
		if (!canPrint()) return;
		if (options.shouldPrintSummary && !options.shouldPrintSummary(stats)) return;
		const { totalIn, totalOut, totalCacheRead, totalCacheCreation, turns, elapsed, cost, children } = stats;
		ensureSummaryLineStart();
		const seconds = Math.floor(elapsed / 1e3);
		const minutes = Math.floor(seconds / 60);
		const remainingSeconds = seconds % 60;
		const timeStr = minutes > 0 ? `${minutes}m ${remainingSeconds}s` : `${remainingSeconds}s`;
		let line = `🧮 Tokens: ${chalk.yellow(totalIn)} in / ${chalk.green(totalOut)} out`;
		line += ` (${chalk.magenta(turns)} parent turn${turns !== 1 ? "s" : ""})`;
		line += ` in ${chalk.green(timeStr)}`;
		if (totalCacheCreation || totalCacheRead) line += `\n   Cache: ${chalk.blue(totalCacheCreation)} created / ${chalk.blue(totalCacheRead)} read`;
		if (cost) line += `\n💸 Cost: ${chalk.yellow(`$${cost.toFixed(6)}`)}`;
		if (children && children.length > 0) {
			line += `\n   Children: ${chalk.magenta(children.length)}`;
			for (const child of children) {
				line += `\n     · [${chalk.bold(child.id)}] ${chalk.yellow(child.stats.totalIn)} in / ${chalk.green(child.stats.totalOut)} out`;
				line += chalk.dim(` (${child.stats.turns} turn${child.stats.turns === 1 ? "" : "s"}, ${child.status ?? "completed"})`);
			}
		}
		const byModel = statsByModel(stats);
		if (byModel.size > 1) {
			line += `\n   By model:`;
			for (const [modelId, usage] of byModel) {
				line += `\n     · ${chalk.cyan(modelId)} ${chalk.yellow(usage.input)} in / ${chalk.green(usage.output)} out (${usage.turns} turn${usage.turns === 1 ? "" : "s"})`;
				if (usage.cost > 0) line += chalk.dim(` · $${usage.cost.toFixed(6)}`);
			}
		}
		console.log(chalk.cyan(line));
		if (agent.session) {
			const session = agent.session;
			const totalRuns = session.runs.length;
			const recordedIn = session.runs.reduce((sum, r) => sum + (r.tokensIn ?? 0), 0);
			const recordedOut = session.runs.reduce((sum, r) => sum + (r.tokensOut ?? 0), 0);
			const recordedCost = session.runs.reduce((sum, r) => sum + (r.cost ?? 0), 0);
			let sessionLine = `📋 Session ${chalk.dim(session.id)}: ${chalk.bold(String(totalRuns))} run${totalRuns === 1 ? "" : "s"} recorded`;
			sessionLine += `, ${chalk.yellow(recordedIn)} in / ${chalk.green(recordedOut)} out`;
			if (recordedCost) sessionLine += `, ${chalk.yellow(`$${recordedCost.toFixed(6)}`)}`;
			console.log(chalk.dim(sessionLine));
			const offLedgerIn = totalIn - recordedIn;
			const offLedgerOut = totalOut - recordedOut;
			const childCount = countDescendants(children);
			if ((offLedgerIn > 0 || offLedgerOut > 0) && childCount > 0) {
				const note = `   ↳ ${chalk.magenta(childCount)} subagent${childCount === 1 ? "" : "s"} ran in-memory (+${chalk.yellow(offLedgerIn)} in / +${chalk.green(offLedgerOut)} out, not persisted)`;
				console.log(chalk.dim(note));
			}
		}
	});
	return () => {
		for (const unregister of unregisters) unregister();
	};
}
/** Recursively count every descendant in the children tree. */
function countDescendants(children) {
	if (!children) return 0;
	let n = 0;
	for (const child of children) n += 1 + countDescendants(child.stats.children);
	return n;
}
//#endregion
export { printTerminalHeader, setupTerminalOutput };

//# sourceMappingURL=terminal.js.map