Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/ultra/call.ts

Raw
// Streaming arguments are unvalidated and may stop at any field.
// This view only reads received data; it never resolves or executes a workflow.
import { stripVTControlCharacters } from "node:util";
import {
	keyHint,
	type Theme,
	type ToolDefinition,
} from "@earendil-works/pi-coding-agent";
import {
	type Component,
	truncateToWidth,
	wrapTextWithAnsi,
} from "@earendil-works/pi-tui";

type CallOptions = Pick<
	Parameters<NonNullable<ToolDefinition["renderCall"]>>[2],
	"expanded" | "argsComplete" | "executionStarted" | "isPartial" | "isError"
>;

function record(value: unknown): Record<string, unknown> | undefined {
	return value !== null && typeof value === "object" && !Array.isArray(value)
		? (value as Record<string, unknown>)
		: undefined;
}

function plain(value: string): string {
	return stripVTControlCharacters(value)
		.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "")
		.replaceAll("\t", "    ");
}

function line(value: unknown): string {
	return typeof value === "string"
		? plain(value).replace(/\s+/g, " ").trim()
		: "";
}

/** Like Pi's write preview, text advances with argument updates, without timers. */
export function renderUltraCall(
	args: unknown,
	theme: Theme,
	options: CallOptions,
): Component {
	return {
		render(width) {
			const w = Math.max(1, width);
			const input = record(args);
			const inline = input?.spec !== undefined;
			const spec = record(input?.spec);
			const name = line(inline ? spec?.name : input?.name);
			const phases = Array.isArray(spec?.phases) ? spec.phases : [];
			const started =
				options.executionStarted || options.isError || !options.isPartial;
			const receiving = !started && !options.argsComplete;
			const rows: string[] = [];
			const add = (text: string, color: Parameters<Theme["fg"]>[0] = "muted") =>
				rows.push(theme.fg(color, truncateToWidth(text, w)));
			const block = (text: string) => {
				for (const row of wrapTextWithAnsi(plain(text), Math.max(1, w - 3)))
					add(`   ${row}`, "toolOutput");
			};
			add(
				`${theme.bold("run_workflow")}${name ? ` ${name}` : ""}`,
				"toolTitle",
			);
			// The execution board owns the collapsed row after handoff, including errors.
			if (started && !options.expanded) return rows;

			const status = started
				? "Received workflow"
				: receiving
					? "Drafting workflow"
					: "Arguments received";
			if (inline) {
				add(
					`${status} · ${phases.length} phase${phases.length === 1 ? "" : "s"} received${receiving ? " · no agents running" : ""}`,
				);
			} else if (name) {
				const inputs = record(input?.args);
				const count = inputs ? Object.keys(inputs).length : 0;
				add(
					`Saved workflow · ${count} input${count === 1 ? "" : "s"} received`,
				);
			} else
				add(receiving ? "Receiving workflow arguments…" : "Arguments received");
			if (!started && !receiving)
				add("Awaiting validation and execution.", "dim");
			if (options.expanded && line(spec?.description))
				block(line(spec?.description));

			// Keep the receiving end visible when a workflow has many phases.
			const first = options.expanded ? 0 : Math.max(0, phases.length - 3);
			if (first > 0)
				add(`… ${first} earlier phase${first === 1 ? "" : "s"}`, "dim");
			for (let index = first; index < phases.length; index++) {
				const phase = record(phases[index]);
				const step = record(phase?.step);
				const kind = line(phase?.kind);
				const fanout =
					kind === "fanout"
						? Array.isArray(phase?.over)
							? `${phase.over.length} items received`
							: line(phase?.over)
								? "dynamic items"
								: "items not received"
						: "";
				add(
					[
						`${index + 1}  ${line(phase?.id) || "unnamed phase"}`,
						kind || "kind not received",
						fanout,
						line(step?.model),
						line(phase?.when) ? "conditional" : "",
					]
						.filter(Boolean)
						.join(" · "),
					"accent",
				);
				add(
					`   ${line(step?.summary) || "Summary not received"}`,
					"toolOutput",
				);
				if (!options.expanded) continue;
				const tools = Array.isArray(step?.tools)
					? step.tools.map(line).filter(Boolean)
					: undefined;
				if (tools) block(`tools: ${tools.join(", ") || "none"}`);
				if (line(step?.thinkingLevel))
					block(`thinking: ${line(step?.thinkingLevel)}`);
				if (kind === "fanout" && line(phase?.over))
					block(`over: ${line(phase?.over)}`);
				if (line(phase?.when)) block(`when: ${line(phase?.when)}`);
				if (typeof step?.prompt === "string") {
					add("   Received prompt", "dim");
					block(step.prompt);
				}
			}

			if (options.expanded && line(spec?.return))
				block(`return: ${line(spec?.return)}`);
			if (!options.expanded && phases.length > 0)
				add(keyHint("app.tools.expand", "to expand"), "dim");
			return rows;
		},
		invalidate() {},
	};
}