// 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>[2], "expanded" | "argsComplete" | "executionStarted" | "isPartial" | "isError" >; function record(value: unknown): Record | undefined { return value !== null && typeof value === "object" && !Array.isArray(value) ? (value as Record) : 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[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() {}, }; }