repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/ultra/__tests__/ui.test.ts
Rawimport { stripVTControlCharacters } from "node:util";
import type {
Theme,
ToolRenderResultOptions,
} from "@earendil-works/pi-coding-agent";
import { initTheme } from "@earendil-works/pi-coding-agent";
import type { TUI } from "@earendil-works/pi-tui";
import { beforeAll, describe, expect, it, vi } from "vitest";
import {
type AgentControls,
initialReducerState,
reduceProgress,
type UltraProgressEvent,
} from "../progress.ts";
import {
type OverlayAgentControls,
type OverlayHost,
renderUltraResult,
UltraOverlayComponent,
} from "../ui.ts";
// Real terminal byte sequences (verified against pi-tui's matchesKey).
const K = {
esc: "\x1b",
tab: "\t",
up: "\x1b[A",
down: "\x1b[B",
altUp: "\x1bp",
altDown: "\x1bn",
pageUp: "\x1b[5~",
pageDown: "\x1b[6~",
enter: "\r",
backspace: "\x7f",
};
function makeOverlay(opts: {
rows?: number;
now?: () => number;
throttleMs?: number;
onRunAbort?: () => void;
toolUi?: boolean;
toolsExpanded?: () => boolean;
hideToolPreviews?: boolean;
isToolsExpandKey?: (data: string) => boolean;
onToolsExpand?: () => void;
}) {
const requestRender = vi.fn();
const host: OverlayHost = {
requestRender,
terminal: { rows: opts.rows ?? 24, columns: 80 },
};
const overlay = new UltraOverlayComponent({
host,
theme,
onRunAbort: opts.onRunAbort ?? vi.fn(),
now: opts.now,
throttleMs: opts.throttleMs,
toolUi: opts.toolUi ? (host as unknown as TUI) : undefined,
toolsExpanded: opts.toolsExpanded,
hideToolPreviews: opts.hideToolPreviews,
isToolsExpandKey: opts.isToolsExpandKey,
onToolsExpand: opts.onToolsExpand,
cwd: opts.toolUi ? process.cwd() : undefined,
});
return { overlay, requestRender, host };
}
// `keyHint` (used by the collapsed tool view) reads the global theme; the host
// initialises it in prod, so the fakes-only harness must do the same once.
beforeAll(() => {
initTheme();
});
// A no-op theme: `fg`/`bold` return the text verbatim so assertions can target
// plain substrings. (`keyHint` uses the *global* theme, not this one.)
const theme = {
fg: (_c: string, t: string) => t,
bold: (t: string) => t,
} as unknown as Theme;
const pe = (
e: Partial<UltraProgressEvent> &
Pick<UltraProgressEvent, "agentId" | "phase" | "kind">,
): UltraProgressEvent => e;
function stateFrom(events: UltraProgressEvent[]) {
return events.reduce(reduceProgress, initialReducerState());
}
function renderTool(
state: ReturnType<typeof initialReducerState>,
options: ToolRenderResultOptions,
width = 80,
): string[] {
return renderUltraResult(
{ content: [], details: state },
options,
theme,
).render(width);
}
const toolText = (
state: ReturnType<typeof initialReducerState>,
options: ToolRenderResultOptions,
): string => renderTool(state, options).join("\n");
// --- Layer 7a — TOOL sink (renderResult) ---------------------------------
describe("ultra tool sink — renderUltraResult", () => {
it("renders one compact emphasized progress header", () => {
vi.useFakeTimers();
try {
vi.setSystemTime(180_000);
let state = initialReducerState({
workflowName: "spec-guidance-review",
});
state = reduceProgress(state, {
kind: "plan",
startedAt: 0,
phases: [
{
phase: "review",
status: "resolved",
agentIds: ["review#0"],
},
],
} as unknown as UltraProgressEvent);
state = reduceProgress(
state,
pe({ agentId: "review#0", phase: "review", kind: "start" }),
);
const emphasizedTheme = {
...theme,
bold: (text: string) => `__${text}__`,
} as Theme;
const lines = renderUltraResult(
{ content: [], details: state },
{ expanded: true, isPartial: true },
emphasizedTheme,
).render(120);
expect(lines[0]).toBe(
"__ultra__ · spec-guidance-review · 3m 0s elapsed · phase 1/1 · 0/1 done · 1 active",
);
expect(lines.filter((line) => line.includes("phase 1/1"))).toHaveLength(
1,
);
expect(lines.some((line) => line.includes("⏳"))).toBe(false);
} finally {
vi.useRealTimers();
}
});
it("header shows workflow name and description when supplied", () => {
const s = initialReducerState({
workflowName: "review",
workflowDescription: "Review the diff from every angle.",
});
const out = toolText(s, { expanded: false, isPartial: true });
expect(out).toContain("review");
expect(out).toContain("Review the diff from every angle.");
});
it("shows workflow wall time in the header and freezes it after completion", () => {
vi.useFakeTimers();
try {
vi.setSystemTime(0);
const planned = reduceProgress(initialReducerState(), {
kind: "plan",
startedAt: 0,
phases: [{ phase: "one", status: "resolved", agentIds: ["one#0"] }],
} as unknown as UltraProgressEvent);
vi.setSystemTime(65_000);
expect(toolText(planned, { expanded: false, isPartial: true })).toContain(
"1m 5s elapsed",
);
const done = reduceProgress(planned, {
kind: "phase",
phase: "one",
status: "done",
agentIds: ["one#0"],
} as unknown as UltraProgressEvent);
vi.setSystemTime(125_000);
const frozen = toolText(done, { expanded: true, isPartial: false });
expect(frozen).toContain("1m 5s elapsed");
expect(frozen).not.toContain("2m 5s elapsed");
} finally {
vi.useRealTimers();
}
});
it("uses phase-first disclosure: completed and future phases collapse while active and failed phases open", () => {
const s = stateFrom([
{
kind: "plan",
startedAt: Date.now(),
phases: [
{ phase: "done", status: "resolved", agentIds: ["done#0"] },
{
phase: "active",
status: "resolved",
agentIds: ["active#0", "active#1"],
},
{ phase: "queued", status: "resolved", agentIds: ["queued#0"] },
{ phase: "verify", status: "agents-pending", agentIds: [] },
{
phase: "publish",
status: "condition-pending",
agentIds: [],
},
],
} as unknown as UltraProgressEvent,
pe({ agentId: "done#0", phase: "done", kind: "start" }),
pe({ agentId: "done#0", phase: "done", kind: "end", status: "done" }),
{
kind: "phase",
phase: "done",
status: "done",
agentIds: ["done#0"],
} as UltraProgressEvent,
pe({
agentId: "active#0",
phase: "active",
kind: "prompt",
summary: "Review active path",
}),
pe({ agentId: "active#0", phase: "active", kind: "start" }),
pe({
agentId: "active#1",
phase: "active",
kind: "prompt",
summary: "Check failed path",
}),
pe({
agentId: "active#1",
phase: "active",
kind: "end",
status: "failed",
}),
]);
const expanded = toolText(s, { expanded: true, isPartial: true });
expect(expanded).toContain(" done · 1/1 done");
expect(expanded).not.toContain("done#0");
expect(expanded).toContain("▼ active");
expect(expanded).toMatch(/Review active path\n\s+active#0 · running/);
expect(expanded).toMatch(/Check failed path\n\s+active#1 · dropped/);
expect(expanded).toContain(" queued · 1 queued");
expect(expanded).not.toContain("queued#0");
expect(expanded).toContain("agents not planned yet");
expect(expanded).toContain("conditional");
const collapsed = toolText(s, { expanded: false, isPartial: true });
expect(collapsed).toContain("1 active");
expect(collapsed).toContain("1 queued");
expect(collapsed).toContain("2 later");
expect(collapsed).toContain("▶ active");
expect(collapsed).toContain("active#0 · Review active path");
expect(collapsed).toContain("active#1 · Check failed path");
expect(collapsed).not.toContain("transport: rate limited");
});
it("truncation keeps ellipsis in the surrounding style span", () => {
const s = stateFrom([
pe({ agentId: "scan#0", phase: "scan", kind: "start" }),
pe({
agentId: "scan#0",
phase: "scan",
kind: "action",
toolName: "bash",
text: JSON.stringify({
command: "python3 - <<'PY'\n" + "x".repeat(200),
}),
}),
]);
const out = renderTool(s, { expanded: true, isPartial: true }, 54).join(
"\n",
);
expect(out).toContain("...");
expect(out).not.toContain("\x1b[0m...\x1b[0m");
});
// A run with a running agent whose latest action + transcript carry distinct
// sentinels, plus a dropped agent.
const scripted = stateFrom([
pe({ agentId: "a", phase: "review", kind: "start" }),
pe({
agentId: "a",
phase: "review",
kind: "action",
toolName: "read",
text: "/SECRET-ACTION",
}),
pe({
agentId: "a",
phase: "review",
kind: "delta",
text: "SECRET-TRANSCRIPT-BODY",
}),
pe({ agentId: "b", phase: "review", kind: "start" }),
pe({
agentId: "b",
phase: "review",
kind: "end",
status: "failed",
item: { task: "scope" },
failure: {
code: "transport",
message: "rate limited",
retryable: true,
attempts: 2,
model: "openai/gpt",
thinkingLevel: "high",
transcriptTail: "bounded bad response",
},
}),
]);
it("collapsed: shows phase and run summaries without actions or transcripts", () => {
const out = toolText(scripted, { expanded: false, isPartial: true });
// aggregate counts + phase counter present
expect(out).toContain("phase 1/1");
expect(out).toContain("0/2 done");
expect(out).toContain("1✗");
// the collapsed expand affordance
expect(out).toContain("to expand");
expect(out).toContain("▶ review");
expect(out).toContain(" a");
expect(out).toContain(" b");
expect(out).not.toContain("/SECRET-ACTION");
expect(out).not.toContain("SECRET-TRANSCRIPT-BODY");
expect(out).not.toContain("rate limited");
});
it("collapsed: bounds visible phases and runs while keeping the active phase", () => {
const phases = ["prepare", "scan", "review", "verify", "publish"].map(
(phase) => ({ phase, status: "resolved" as const, agentIds: [] }),
);
let state = reduceProgress(initialReducerState(), {
kind: "plan",
startedAt: Date.now(),
phases,
});
state = reduceProgress(state, {
kind: "phase",
phase: "verify",
status: "resolved",
agentIds: ["verify#0", "verify#1", "verify#2", "verify#3", "verify#4"],
});
for (let index = 0; index < 5; index++) {
state = reduceProgress(
state,
pe({
agentId: `verify#${index}`,
phase: "verify",
kind: "prompt",
summary: `Run ${index}`,
prompt: "SECRET EXACT PROMPT",
}),
);
state = reduceProgress(
state,
pe({ agentId: `verify#${index}`, phase: "verify", kind: "start" }),
);
}
for (const width of [24, 54, 120]) {
const lines = renderTool(
state,
{ expanded: false, isPartial: true },
width,
);
const out = lines.join("\n");
expect(out).toContain("▶ verify");
expect(out).toContain("earlier phases");
expect(out).toContain("more runs");
expect(out).toContain("verify#0");
expect(out).not.toContain("SECRET EXACT PROMPT");
expect(
lines.every((line) => stripVTControlCharacters(line).length <= width),
).toBe(true);
}
});
it("expanded: shows authored summaries but never exact prompts", () => {
const state = stateFrom([
pe({
agentId: "review#0",
phase: "review",
kind: "prompt",
summary: "Review security",
prompt: "SECRET EXACT PROMPT",
}),
]);
const out = toolText(state, { expanded: true, isPartial: true });
expect(out).toContain("Review security");
expect(out).not.toContain("SECRET EXACT PROMPT");
});
it("expanded: emphasizes active and dropped work without transcript bodies", () => {
const out = renderTool(
scripted,
{ expanded: true, isPartial: true },
140,
).join("\n");
// running agent + its latest action
expect(out).toContain("a");
expect(out).toContain("running");
expect(out).toContain("/SECRET-ACTION");
// dropped agent and its bounded typed diagnostic are surfaced
expect(out).toContain("b");
expect(out).toContain("dropped");
expect(out).toContain("openai/gpt · high");
expect(out).toContain("transport: rate limited");
expect(out).toContain("retryable · 2 attempts");
// Full transcripts and verbose failure tails stay out of the compact board.
expect(out).not.toContain("bounded bad response");
expect(out).not.toContain("SECRET-TRANSCRIPT-BODY");
});
it.each(["tool", "overlay"])(
"%s: separates failure diagnostics from metadata",
(surface) => {
const events = [
pe({ agentId: "review#0", phase: "review", kind: "start" }),
pe({
agentId: "review#0",
phase: "review",
kind: "end",
status: "failed",
failure: {
code: "missing-output",
message: "no structured output produced",
retryable: false,
attempts: 1,
},
}),
];
const { overlay } = makeOverlay({ rows: 24 });
for (const event of events) overlay.handleProgress(event);
const lines =
surface === "tool"
? renderTool(stateFrom(events), { expanded: true, isPartial: true })
: overlay.render(80);
const diagnostic = lines.findIndex((line) =>
line.includes("missing-output:"),
);
expect(diagnostic).toBeGreaterThanOrEqual(0);
expect(lines[diagnostic].trim()).toBe(
"missing-output: no structured output produced",
);
expect(lines[diagnostic + 1].trim()).toBe(
"dropped · terminal · 1 attempt",
);
},
);
it.each([54, 140])(
"expanded: separates live activity from metadata at width %i",
(width) => {
let state = stateFrom([
pe({
agentId: "validate#0",
phase: "validate",
kind: "prompt",
summary: "Validate Niri handoff",
}),
pe({
agentId: "validate#0",
phase: "validate",
kind: "start",
model: "openai/gpt",
}),
]);
const options = { expanded: true, isPartial: true };
let lines = renderTool(state, options, width);
let agent = lines.findIndex((line) =>
line.includes("Validate Niri handoff"),
);
expect(agent).toBeGreaterThanOrEqual(0);
expect(lines[agent + 1]).toBe(" validate#0 · running · openai/gpt");
state = reduceProgress(
state,
pe({
agentId: "validate#0",
phase: "validate",
kind: "action",
actionSummary: "Validating Niri integration behavior and screenshots",
}),
);
lines = renderTool(state, options, width);
agent = lines.findIndex((line) => line.includes("Validate Niri handoff"));
expect(lines[agent + 1]).toMatch(/^ {4}Validating Niri/);
expect(lines[agent + 1]).not.toContain("validate#0");
expect(lines[agent + 2]).toBe(" validate#0 · running · openai/gpt");
},
);
it("expanded: separates completed agent summaries from their metadata", () => {
const s = stateFrom([
pe({
agentId: "plan#0",
phase: "plan",
kind: "prompt",
summary: "Plan implementation",
}),
pe({
agentId: "plan#0",
phase: "plan",
kind: "start",
tier: "small",
model: "openai/gpt-5-mini",
thinkingLevel: "low",
startedAt: Date.now() - 18_000,
}),
pe({
agentId: "plan#0",
phase: "plan",
kind: "action",
toolName: "read",
text: "docs/phase-1.html",
}),
pe({
agentId: "plan#0",
phase: "plan",
kind: "end",
status: "done",
usage: {
input: 1800,
output: 300,
total: 2100,
cacheRead: 0,
cacheWrite: 0,
cost: 0,
},
}),
]);
const lines = renderTool(s, { expanded: true, isPartial: false }, 140);
const out = lines.join("\n");
const agent = lines.findIndex((line) =>
line.includes("Plan implementation"),
);
expect(agent).toBeGreaterThanOrEqual(0);
expect(lines[agent]).toMatch(/Plan implementation$/);
expect(lines[agent + 1]).toMatch(/^ {4}plan#0 · done/);
expect(lines[agent + 1]).toContain("small → openai/gpt-5-mini · low");
expect(lines[agent + 1]).toContain("18s");
expect(lines[agent + 1]).toContain("$0.0000 · 300 output");
expect(lines[agent + 1]).not.toContain("2.1k tokens");
expect(out).not.toContain("docs/phase-1.html");
});
it("expanded: separates queued agent labels from their metadata", () => {
const state = stateFrom([
{
kind: "plan",
startedAt: Date.now(),
phases: [
{
phase: "review",
status: "resolved",
agentIds: ["review#0", "review#1"],
},
],
},
pe({ agentId: "review#0", phase: "review", kind: "start" }),
]);
const lines = renderTool(state, { expanded: true, isPartial: true });
const agent = lines.findIndex((line) => line.includes("review#1"));
expect(agent).toBeGreaterThanOrEqual(0);
expect(lines[agent]).toMatch(/review#1$/);
expect(lines[agent + 1]).toMatch(/^ {4}queued/);
});
it("formats token counts with three significant SI digits", () => {
for (const [tokens, expected] of [
[999, "999"],
[1000, "1k"],
[12_345, "12.3k"],
[999_000, "999k"],
[4_326_000, "4.33M"],
[1_250_000_000, "1.25G"],
] as const) {
const state = stateFrom([
pe({ agentId: "p#0", phase: "p", kind: "start" }),
pe({
agentId: "p#0",
phase: "p",
kind: "end",
status: "done",
usage: {
input: 0,
output: tokens,
total: tokens,
cacheRead: 0,
cacheWrite: 0,
cost: 0,
},
}),
]);
expect(toolText(state, { expanded: true, isPartial: false })).toContain(
`${expected} output`,
);
}
});
it("shows aggregate cost, checkpoints, and resumed steps", () => {
const s = stateFrom([
{
kind: "plan",
startedAt: 0,
phases: [{ phase: "p", status: "resolved", agentIds: ["p#0", "p#1"] }],
} as UltraProgressEvent,
pe({ agentId: "p#0", phase: "p", kind: "start", text: "cached" }),
pe({ agentId: "p#0", phase: "p", kind: "end", status: "done" }),
pe({ agentId: "p#1", phase: "p", kind: "start" }),
pe({
agentId: "p#1",
phase: "p",
kind: "end",
status: "done",
usage: {
input: 800,
output: 200,
total: 1000,
cacheRead: 0,
cacheWrite: 0,
cost: 0.125,
},
}),
]);
const out = toolText(s, { expanded: true, isPartial: true });
expect(out).toContain("checkpoint reused");
expect(out).toContain("$0.1250 · 200 output · checkpoint 2/2 · 1 resumed");
});
it.each(["tool", "overlay"])(
"%s prioritizes cost/output and expands input/cache separately",
(surface) => {
let expanded = false;
const { overlay } = makeOverlay({
rows: 40,
toolsExpanded: () => expanded,
});
const events = [
pe({ agentId: "work#0", phase: "work", kind: "start" }),
pe({
agentId: "work#0",
phase: "work",
kind: "end",
status: "done",
usage: {
input: 1000,
output: 100,
total: 3600,
cacheRead: 2000,
cacheWrite: 500,
cost: 0.125,
},
}),
pe({ agentId: "work#1", phase: "work", kind: "start" }),
pe({
agentId: "work#1",
phase: "work",
kind: "end",
status: "failed",
usage: {
input: 2000,
output: 200,
total: 4200,
cacheRead: 2000,
cacheWrite: 0,
cost: 0.25,
},
}),
];
for (const event of events) overlay.handleProgress(event);
const state = stateFrom(events);
const render = () =>
(surface === "tool"
? renderTool(state, { expanded, isPartial: true }, 140)
: overlay.render(140)
).join("\n");
let out = render();
expect(out).toContain("$0.3750 · 300 output · checkpoint 1/2");
expect(out).not.toContain("uncached input");
expect(out).not.toContain("cache read");
expect(out).not.toContain("7.8k tokens");
expanded = true;
out = render();
expect(out).toContain(
"$0.3750 · 300 output · checkpoint 1/2\n3k uncached input · 4k cache read · 500 cache write",
);
expect(out).toContain("$0.2500 · 200 output");
expect(out).not.toContain("7.8k tokens");
const narrow = renderTool(
state,
{ expanded: false, isPartial: true },
40,
);
expect(
narrow.some((line) => line.startsWith("$0.3750 · 300 output")),
).toBe(true);
},
);
it("renders the final human-readable outcome only when expanded", () => {
const base = stateFrom([
pe({ agentId: "p#0", phase: "p", kind: "end", status: "done" }),
]);
const state = {
...base,
workflowResult: {
workflow: "fix-pmd",
phases: [{ id: "p", ran: 1, ok: 1, dropped: 0 }],
phaseResults: {},
phaseFailures: {},
result: [{ success: true, summary: "All accepted findings fixed." }],
steered: false,
tokenUsage: {
input: 1,
output: 2,
total: 3,
cacheRead: 0,
cacheWrite: 0,
cost: 0,
},
},
};
const collapsed = toolText(state, { expanded: false, isPartial: false });
expect(collapsed).not.toContain("All accepted findings fixed.");
expect(collapsed).toContain("$0.0000 · 2 output");
expect(collapsed).not.toContain("uncached input");
const expanded = toolText(state, { expanded: true, isPartial: false });
expect(expanded).toContain("outcome");
expect(expanded).toContain("All accepted findings fixed.");
expect(expanded).not.toContain("phaseResults");
});
it("freezes elapsed time when an agent ends", () => {
vi.useFakeTimers();
try {
vi.setSystemTime(100_000);
const s = stateFrom([
pe({
agentId: "plan#0",
phase: "plan",
kind: "start",
startedAt: 0,
}),
pe({ agentId: "plan#0", phase: "plan", kind: "end", status: "done" }),
]);
vi.setSystemTime(200_000);
const out = toolText(s, { expanded: true, isPartial: false });
expect(out).toContain("1m 40s");
expect(out).not.toContain("3m 20s");
} finally {
vi.useRealTimers();
}
});
it("never emits bodies when expanded=false (sentinels absent collapsed, present expanded)", () => {
const collapsed = toolText(scripted, { expanded: false, isPartial: false });
const expanded = toolText(scripted, { expanded: true, isPartial: false });
expect(collapsed).not.toContain("/SECRET-ACTION");
expect(expanded).toContain("/SECRET-ACTION");
});
it("tracks fresh details — render state N then N+1, the lines change", () => {
const sN = stateFrom([
pe({ agentId: "a", phase: "p", kind: "end", status: "done" }),
]);
const sN1 = stateFrom([
pe({ agentId: "a", phase: "p", kind: "end", status: "done" }),
pe({ agentId: "b", phase: "p", kind: "end", status: "done" }),
]);
const opts: ToolRenderResultOptions = { expanded: false, isPartial: true };
const tN = toolText(sN, opts);
const tN1 = toolText(sN1, opts);
expect(tN).toContain("1/1 done");
expect(tN1).toContain("2/2 done");
expect(tN).not.toEqual(tN1); // not a frozen snapshot
});
it.each([undefined, initialReducerState()])(
"shows SDK errors instead of an empty or stale progress board (details: %s)",
(details) => {
const message =
"Invalid workflow spec: /phases/0/step: missing summary\n" +
"Repair guidance: ".repeat(30) +
"END-OF-DIAGNOSTIC";
const result = {
content: [{ type: "text" as const, text: message }],
details,
};
const errorTheme = { ...theme, fg: vi.fn(theme.fg) };
for (const width of [24, 80, 240]) {
const lines = renderUltraResult(
result,
{ expanded: false, isPartial: false },
errorTheme,
{ isError: true },
)
.render(width)
.map(stripVTControlCharacters);
expect(lines).toHaveLength(2);
expect(lines[0]).toContain("ultra · failed:");
if (width >= 80) expect(lines[0]).toContain("/phases/0/step");
expect(lines[1]).toContain("to expand");
expect(lines.join("\n")).not.toContain("END-OF-DIAGNOSTIC");
expect(lines.join("\n")).not.toContain("0/0 done");
expect(lines.every((line) => line.length <= Math.min(width, 160))).toBe(
true,
);
}
expect(errorTheme.fg).toHaveBeenCalledWith("error", expect.any(String));
const expanded = renderUltraResult(
result,
{ expanded: true, isPartial: false },
theme,
{ isError: true },
)
.render(80)
.map(stripVTControlCharacters)
.join("\n");
expect(expanded).toContain("ultra · failed");
expect(expanded).toContain("/phases/0/step: missing summary");
expect(expanded).toContain("END-OF-DIAGNOSTIC");
expect(result.content[0].text).toBe(message);
},
);
it("labels errors even when the SDK provides no text", () => {
const out = renderUltraResult(
{ content: [], details: undefined },
{ expanded: false, isPartial: false },
theme,
{ isError: true },
)
.render(80)
.join("\n");
expect(out).toContain(
"ultra · failed: Workflow failed without an error message.",
);
expect(out).not.toContain("0/0 done");
});
it("tolerates missing details (headless / no run yet)", () => {
const comp = renderUltraResult(
{
content: [],
details: undefined as unknown as ReturnType<typeof initialReducerState>,
},
{ expanded: false, isPartial: false },
theme,
);
expect(() => comp.render(80)).not.toThrow();
expect(comp.render(80).join("\n")).toContain("0/0 done");
});
// Regression: a truthy `details` that is NOT a ReducerState — a resumed or
// foreign tool result, a structured-output payload, a serialization double-wrap
// — must fall back to an empty board, NEVER throw. An uncaught throw here is
// fatal: it escapes tool-error handling and crashes the whole TUI render loop
// (`state.phases.length` on an object without `phases`).
it("tolerates a foreign (non-ReducerState) details without crashing the render loop", () => {
const foreign = { results: [{ ok: true }] } as unknown as ReturnType<
typeof initialReducerState
>;
for (const options of [
{ expanded: false, isPartial: false },
{ expanded: true, isPartial: true }, // expanded also reads `state.rows`
] as const) {
const comp = renderUltraResult(
{ content: [], details: foreign },
options,
theme,
);
expect(() => comp.render(80)).not.toThrow();
expect(comp.render(80).join("\n")).toContain("0/0 done");
}
});
});
// --- Layer 7b — COMMAND overlay: board + drill-in transcript --------------
describe("ultra command overlay — focus + drill-in transcript (7b)", () => {
it("header shows workflow name and description when supplied", () => {
const requestRender = vi.fn();
const overlay = new UltraOverlayComponent({
host: { requestRender, terminal: { rows: 24, columns: 80 } },
theme,
onRunAbort: vi.fn(),
workflowName: "review",
workflowDescription: "Review the diff from every angle.",
});
overlay.handleProgress(
pe({ agentId: "review#0", phase: "review", kind: "start" }),
);
overlay.handleProgress(
pe({ agentId: "review#0", phase: "review", kind: "end", status: "done" }),
);
const lines = overlay.render(80);
expect(lines[0]).toContain("review");
expect(lines[1]).toBe("Review the diff from every angle.");
expect(lines[2]).toBe("checkpoint 1/1");
});
it("shows planning placeholders but skips them during focus navigation", () => {
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress({
kind: "plan",
startedAt: Date.now(),
phases: [
{ phase: "a", status: "resolved", agentIds: ["a#0"] },
{ phase: "pending", status: "agents-pending", agentIds: [] },
{ phase: "b", status: "resolved", agentIds: ["b#0"] },
],
});
let out = overlay.render(80).join("\n");
expect(out).toContain("agents not planned yet");
expect(out).toContain("transcript: a#0");
overlay.handleInput(K.down);
out = overlay.render(80).join("\n");
expect(out).toContain("phase: pending");
overlay.handleInput(K.down);
overlay.handleInput(K.enter);
overlay.handleInput(K.down);
out = overlay.render(80).join("\n");
expect(out).toContain("transcript: b#0");
});
it("renders the exact resolved prompt as the first user transcript message", () => {
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "prompt",
summary: "Review security",
prompt: "Inspect every authentication path.",
}),
);
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "Working" }),
);
const out = overlay.render(80).join("\n");
expect(out).toContain("Review security");
expect(out).toContain("user");
expect(out).toContain("Inspect every authentication path.");
expect(out.indexOf("Inspect every authentication path.")).toBeLessThan(
out.indexOf("Working"),
);
});
it("renders task-first rows with live activity above metadata", () => {
const { overlay } = makeOverlay({ rows: 30 });
overlay.handleProgress(
pe({
agentId: "done#0",
phase: "p",
kind: "prompt",
summary: "Inspect persistence path",
}),
);
overlay.handleProgress(
pe({ agentId: "done#0", phase: "p", kind: "start" }),
);
overlay.handleProgress(
pe({ agentId: "done#0", phase: "p", kind: "end", status: "done" }),
);
overlay.handleProgress(
pe({
agentId: "active#0",
phase: "p",
kind: "prompt",
summary: "Verify authentication boundary",
}),
);
overlay.handleProgress(
pe({ agentId: "active#0", phase: "p", kind: "start" }),
);
overlay.handleProgress(
pe({
agentId: "active#0",
phase: "p",
kind: "action",
actionSummary: "Reading authentication source",
}),
);
const lines = overlay.render(120);
const active = lines.findIndex((line) =>
line.includes("Verify authentication boundary"),
);
expect(active).toBeGreaterThanOrEqual(0);
expect(lines[active + 1].trim()).toBe("Reading authentication source");
expect(lines[active + 2]).toMatch(/^\s+active#0 · running/);
const done = lines.findIndex((line) =>
line.includes("Inspect persistence path"),
);
expect(done).toBeGreaterThanOrEqual(0);
expect(lines[done]).toMatch(/Inspect persistence path$/);
expect(lines[done + 1]).toMatch(/^\s+done#0 · done/);
});
it("toggles phase rows with Enter and cycles visible agents with Tab", () => {
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress({
kind: "plan",
startedAt: Date.now(),
phases: [{ phase: "p", status: "resolved", agentIds: ["p#0", "p#1"] }],
});
overlay.handleInput(K.up);
overlay.handleInput(K.enter);
let out = overlay.render(80).join("\n");
expect(out).toContain("phase: p");
expect(out).not.toContain("p#0 · queued");
overlay.handleInput(K.enter);
overlay.handleInput(K.tab);
out = overlay.render(80).join("\n");
expect(out).toContain("transcript: p#0");
overlay.handleInput(K.tab);
expect(overlay.render(80).join("\n")).toContain("transcript: p#1");
});
it("focusing agent k shows its deltas and not the others'", () => {
const { overlay } = makeOverlay({ rows: 24 });
for (const id of ["a", "b", "c"]) {
overlay.handleProgress(pe({ agentId: id, phase: "p", kind: "start" }));
}
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "AAA-DELTA" }),
);
overlay.handleProgress(
pe({ agentId: "b", phase: "p", kind: "delta", text: "BBB-DELTA" }),
);
overlay.handleProgress(
pe({ agentId: "c", phase: "p", kind: "delta", text: "CCC-DELTA" }),
);
// default focus is the first agent
let out = overlay.render(60).join("\n");
expect(out).toContain("transcript: a");
expect(out).toContain("AAA-DELTA");
expect(out).not.toContain("BBB-DELTA");
expect(out).not.toContain("CCC-DELTA");
// Tab advances focus → b
overlay.handleInput(K.tab);
out = overlay.render(60).join("\n");
expect(out).toContain("transcript: b");
expect(out).toContain("BBB-DELTA");
expect(out).not.toContain("AAA-DELTA");
expect(out).not.toContain("CCC-DELTA");
// ↓ advances focus → c
overlay.handleInput(K.down);
out = overlay.render(60).join("\n");
expect(out).toContain("transcript: c");
expect(out).toContain("CCC-DELTA");
// ↑ moves focus back → b
overlay.handleInput(K.up);
out = overlay.render(60).join("\n");
expect(out).toContain("transcript: b");
expect(out).toContain("BBB-DELTA");
});
it("auto-focuses the first agent in a new phase so verifier transcripts are visible during the run", () => {
const { overlay } = makeOverlay({ rows: 12 });
for (let i = 0; i < 3; i++) {
overlay.handleProgress(
pe({ agentId: `review#${i}`, phase: "review", kind: "start" }),
);
overlay.handleProgress(
pe({
agentId: `review#${i}`,
phase: "review",
kind: "end",
status: "done",
}),
);
}
overlay.handleProgress(
pe({ agentId: "verify#0", phase: "verify", kind: "start" }),
);
overlay.handleProgress(
pe({
agentId: "verify#0",
phase: "verify",
kind: "delta",
text: "VERIFY-DELTA",
}),
);
const out = overlay.render(60).join("\n");
expect(out).toContain("transcript: verify#0");
expect(out).toContain("VERIFY-DELTA");
});
it("keeps rendered overlay within terminal height minus a 3-row safety margin", () => {
const { overlay } = makeOverlay({ rows: 12 });
for (let i = 0; i < 8; i++) {
overlay.handleProgress(
pe({ agentId: `a${i}`, phase: "p", kind: "start" }),
);
}
for (let i = 0; i < 20; i++) {
overlay.handleProgress(
pe({ agentId: "a7", phase: "p", kind: "delta", text: `L${i}\n` }),
);
}
expect(overlay.render(60).length).toBeLessThanOrEqual(9);
});
it("keeps one native Pi tool row through start, result, and expansion", () => {
let expanded = false;
const { overlay } = makeOverlay({
rows: 80,
toolUi: true,
toolsExpanded: () => expanded,
isToolsExpandKey: (data) => data === "EXPAND",
onToolsExpand: () => {
expanded = !expanded;
},
});
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "tool-1",
toolName: "read",
args: { path: "extensions/ultra/index.ts" },
}),
);
let out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("extensions/ultra/index.ts");
expect(out).not.toContain("read ...");
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "tool-1",
toolName: "read",
result: {
content: [{ type: "text", text: "PARTIAL-RESULT" }],
},
isPartial: true,
}),
);
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).not.toContain("PARTIAL-RESULT");
overlay.handleInput("EXPAND");
expect(expanded).toBe(true);
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("PARTIAL-RESULT");
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "tool-1",
toolName: "read",
result: {
content: [{ type: "text", text: "NATIVE-RESULT" }],
},
isError: false,
isPartial: false,
}),
);
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("NATIVE-RESULT");
expect(out).not.toContain("PARTIAL-RESULT");
expect(out).not.toContain("read ...");
});
it.each(["structured_output", "custom_tool"])(
"renders %s arguments and JSON results as expandable fields",
(toolName) => {
let expanded = false;
const { overlay } = makeOverlay({
rows: 80,
toolUi: true,
toolsExpanded: () => expanded,
});
const args = {
task: "Submit findings",
records: [{ file: "nested-evidence.ts", accepted: false }],
};
const result = {
content: [{ type: "text", text: '{"status":"pending","count":2}' }],
details: { findings: [{ reason: "RENDERED-EVIDENCE" }], empty: [] },
};
const original = JSON.stringify({ args, result });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "json-tool",
toolName,
args,
}),
);
let out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).not.toContain("nested-evidence.ts");
expect(out).not.toContain('"task":');
expanded = true;
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("Task: Submit findings");
expect(out).toContain("File: nested-evidence.ts");
expect(out).not.toContain('"records":');
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "json-tool",
toolName,
result,
isPartial: true,
}),
);
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("Status: pending");
expect(out).toContain("Reason: RENDERED-EVIDENCE");
expect(out).toContain("No items");
expect(out).not.toContain('"status":');
expanded = false;
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).not.toContain("RENDERED-EVIDENCE");
expect(out).not.toContain("nested-evidence.ts");
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "json-tool",
toolName,
result,
isPartial: false,
}),
);
expanded = true;
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("RENDERED-EVIDENCE");
expect(JSON.stringify({ args, result })).toBe(original);
},
);
it.each([
["read", { path: "source.ts" }],
["write", { path: "source.ts", content: "const x = 1;" }],
["edit", { path: "source.ts", edits: [{ oldText: "a", newText: "b" }] }],
["bash", { command: "echo source.ts" }],
["powershell", { command: "Write-Output source.ts" }],
["grep", { path: "source.ts", pattern: "needle" }],
["find", { path: ".", pattern: "source.ts" }],
["ls", { path: "source.ts" }],
] as const)(
"uses Pi's native %s renderer instead of JSON argument dumps",
(toolName, args) => {
const { overlay } = makeOverlay({ rows: 80, toolUi: true });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "native",
toolName,
args,
}),
);
const out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("source.ts");
expect(out).not.toContain('"path":');
expect(out).not.toContain('"command":');
expect(out).not.toContain('"edits":');
},
);
it("preserves JSON source text in native read results", () => {
const { overlay } = makeOverlay({
rows: 40,
toolUi: true,
toolsExpanded: () => true,
});
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "source",
toolName: "read",
args: { path: "source.json" },
result: { content: [{ type: "text", text: '{"setting":1}' }] },
isPartial: false,
}),
);
const out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain('{"setting":1}');
expect(out).not.toContain('"path":');
});
it("hides collapsed tool previews when action summaries are enabled", () => {
let expanded = false;
const { overlay } = makeOverlay({
rows: 80,
toolUi: true,
hideToolPreviews: true,
toolsExpanded: () => expanded,
isToolsExpandKey: (data) => data === "EXPAND",
onToolsExpand: () => {
expanded = !expanded;
},
});
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "action",
toolCallId: "tool-1",
toolName: "read",
args: { path: "HIDDEN-PREVIEW.ts" },
actionSummary: "Inspecting hidden preview",
}),
);
let out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).not.toContain("HIDDEN-PREVIEW.ts");
overlay.handleInput("EXPAND");
out = stripVTControlCharacters(overlay.render(100).join("\n"));
expect(out).toContain("HIDDEN-PREVIEW.ts");
});
it("renders transcript markdown with Pi's markdown renderer", () => {
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "**bold**" }),
);
const out = overlay.render(60).join("\n");
expect(out).toContain("bold");
expect(out).not.toContain("**bold**");
});
it("renders transcript title as a compact divider header", () => {
const { overlay } = makeOverlay({ rows: 16 });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
const lines = overlay.render(50);
const header = lines.find((l) => l.includes("transcript: a"));
expect(header).toBeDefined();
expect(header).toMatch(/─.*transcript: a.*─/);
expect(lines.some((l) => l === "transcript: a")).toBe(false);
});
it("Alt+Up/Alt+Down scroll transcript when PageUp/PageDown is intercepted by the terminal", () => {
const { overlay } = makeOverlay({ rows: 12 });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
for (let i = 0; i < 10; i++) {
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: `L${i}\n` }),
);
}
expect(overlay.render(40).join("\n")).toContain("L9");
overlay.handleInput(K.altUp);
let out = overlay.render(40).join("\n");
expect(out).toContain("L0");
expect(out).not.toContain("L9");
overlay.handleInput(K.altDown);
out = overlay.render(40).join("\n");
expect(out).toContain("L9");
});
it("follow-mode pins to the newest line until the user scrolls, then re-pins", () => {
// rows=12 with a 3-row safety margin leaves room for a transcript tail.
const { overlay } = makeOverlay({ rows: 12 });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
for (let i = 0; i < 10; i++) {
overlay.handleProgress(
pe({
agentId: "a",
phase: "p",
kind: "delta",
text: i < 9 ? `L${i}\n` : `L${i}`,
}),
);
}
// follow-mode shows the newest tail, not the head
let out = overlay.render(40).join("\n");
expect(out).toContain("L9");
expect(out).not.toContain("L0");
// scroll up → follow disengages, view moves to the head
overlay.handleInput(K.pageUp);
out = overlay.render(40).join("\n");
expect(out).toContain("L0");
expect(out).not.toContain("L9");
// new deltas arrive WHILE scrolled → view stays put (does not jump to newest)
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "\nL10" }),
);
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "\nL11" }),
);
out = overlay.render(40).join("\n");
expect(out).toContain("L0");
expect(out).not.toContain("L11");
// scroll back to the bottom → follow re-engages, newest shown again
overlay.handleInput(K.pageDown);
out = overlay.render(40).join("\n");
expect(out).toContain("L11");
});
it("throttles re-render requests to the window, but state stays fresh; terminal events bypass it", () => {
let clock = 0;
const { overlay, requestRender } = makeOverlay({
now: () => clock,
throttleMs: 80,
});
clock = 0;
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "x" }),
); // leading edge
clock = 10;
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "y" }),
); // throttled
clock = 50;
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "z" }),
); // throttled
expect(requestRender).toHaveBeenCalledTimes(1);
clock = 100;
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "delta", text: "w" }),
); // window elapsed
expect(requestRender).toHaveBeenCalledTimes(2);
clock = 110;
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "end", status: "done" }),
); // forced
expect(requestRender).toHaveBeenCalledTimes(3);
// every delta was folded despite the throttle — no lost state
expect(overlay.render(80).join("\n")).toContain("xyzw");
});
});
// --- Layer 7c — steer + per-agent abort (command overlay only) ------------
function type(overlay: UltraOverlayComponent, text: string) {
for (const ch of text) overlay.handleInput(ch);
}
describe("ultra command overlay — steer + per-agent abort (7c)", () => {
it("s+text on a RUNNING agent calls controls.steer and marks steered at enqueue time", () => {
const steer = vi.fn().mockResolvedValue(undefined);
const followUp = vi.fn().mockResolvedValue(undefined);
const controls: AgentControls = { steer, abort: vi.fn() };
(controls as OverlayAgentControls).followUp = followUp;
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "start", controls }),
);
expect(overlay.steered).toBe(false);
overlay.handleInput("s"); // enter steer sub-mode
// the message itself contains 's' and 'x' — in steer mode they are TEXT,
// not the steer/abort commands (Esc-collision guard).
type(overlay, "use bash xtools");
expect(overlay.steered).toBe(false); // not yet enqueued
overlay.handleInput(K.enter); // submit
expect(steer).toHaveBeenCalledTimes(1);
expect(steer).toHaveBeenCalledWith("use bash xtools");
expect(followUp).not.toHaveBeenCalled();
expect(overlay.steered).toBe(true); // synchronous, before the promise settles
});
it("x aborts the focused agent via controls.abort", () => {
const abort = vi.fn();
const controls: AgentControls = { steer: vi.fn(), abort };
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "start", controls }),
);
overlay.handleProgress(pe({ agentId: "b", phase: "p", kind: "start" }));
overlay.handleInput("x"); // focus is a
expect(abort).toHaveBeenCalledTimes(1);
});
it("steering a FINISHED agent routes to followUp, not steer, and does not taint the run", () => {
const steer = vi.fn();
const followUp = vi.fn().mockResolvedValue(undefined);
const controls: AgentControls = { steer, abort: vi.fn() };
(controls as OverlayAgentControls).followUp = followUp;
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "start", controls }),
);
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "end", status: "done" }),
);
overlay.handleInput("s");
type(overlay, "rerun");
overlay.handleInput(K.enter);
expect(followUp).toHaveBeenCalledWith("rerun");
expect(steer).not.toHaveBeenCalled();
expect(overlay.steered).toBe(false); // follow-up is not a steer taint
});
it("requires two Esc presses to abort the whole run", () => {
const onRunAbort = vi.fn();
const { overlay } = makeOverlay({ rows: 24, onRunAbort });
overlay.handleProgress(pe({ agentId: "a", phase: "p", kind: "start" }));
overlay.handleInput(K.esc);
expect(onRunAbort).not.toHaveBeenCalled();
expect(overlay.render(80).join("\n")).toContain(
"Press Esc again to abort all agents",
);
overlay.handleInput(K.esc);
expect(onRunAbort).toHaveBeenCalledTimes(1);
});
it("expires abort confirmation instead of treating a later Esc as confirmation", () => {
let clock = 0;
const onRunAbort = vi.fn();
const { overlay } = makeOverlay({
rows: 24,
now: () => clock,
onRunAbort,
});
overlay.handleInput(K.esc);
clock = 1600;
overlay.handleInput(K.esc);
expect(onRunAbort).not.toHaveBeenCalled();
overlay.handleInput(K.esc);
expect(onRunAbort).toHaveBeenCalledTimes(1);
});
it("Esc inside steer-input cancels the buffer; it does NOT abort the run", () => {
const onRunAbort = vi.fn();
const steer = vi.fn();
const controls: AgentControls = { steer, abort: vi.fn() };
const { overlay } = makeOverlay({ rows: 24, onRunAbort });
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "start", controls }),
);
overlay.handleInput("s"); // steer mode
type(overlay, "half typed");
overlay.handleInput(K.esc); // cancel the buffer, back to normal mode
expect(onRunAbort).not.toHaveBeenCalled();
expect(steer).not.toHaveBeenCalled();
// Two fresh Esc presses in normal mode now confirm the whole-run abort.
overlay.handleInput(K.esc);
expect(onRunAbort).not.toHaveBeenCalled();
overlay.handleInput(K.esc);
expect(onRunAbort).toHaveBeenCalledTimes(1);
});
it("a rejecting steer is caught (no floating rejection)", async () => {
const steer = vi.fn().mockRejectedValue(new Error("boom"));
const controls: AgentControls = { steer, abort: vi.fn() };
const { overlay } = makeOverlay({ rows: 24 });
overlay.handleProgress(
pe({ agentId: "a", phase: "p", kind: "start", controls }),
);
overlay.handleInput("s");
type(overlay, "go");
expect(() => overlay.handleInput(K.enter)).not.toThrow();
expect(steer).toHaveBeenCalledWith("go");
// let the rejected promise settle — the .catch() handles it, no crash
await Promise.resolve();
await Promise.resolve();
});
it("the tool sink exposes NO controls path (read-only) — render never invokes steer/abort", () => {
// Re-assert the surface split: feeding controls-bearing state to the tool
// renderer must not call them; it only draws.
const steer = vi.fn();
const abort = vi.fn();
const controls: AgentControls = { steer, abort };
const s = stateFrom([
pe({ agentId: "a", phase: "p", kind: "start", controls }),
]);
renderUltraResult(
{ content: [], details: s },
{ expanded: true, isPartial: true },
theme,
).render(80);
expect(steer).not.toHaveBeenCalled();
expect(abort).not.toHaveBeenCalled();
});
});