Files
oh-my-pi/packages/coding-agent/test/task/render-nested-live.test.ts
T
can1357 a5275b17ce feat(coding-agent): added hashline inline |TEXT matching for BOF/EOF
- Added inline `|TEXT` payload parsing for `"/"` before/after inserts, including BOF/EOF usage.
- Fixed inline anchor handling by resolving matching `|TEXT` bodies and handling whitespace-containing payloads.
- Added parser tests for `applyDiff` and `parseHashline` covering whitespace, matching, and non-matching inline `|TEXT`.
- Added nested live task fixtures and snapshot tests for ordered in-flight and completed child rendering.
- Documented hashline inline `|TEXT` behavior updates in CHANGELOG.
2026-05-25 20:24:44 +02:00

175 lines
5.0 KiB
TypeScript

import { beforeAll, describe, expect, it } from "bun:test";
import { getThemeByName, setThemeInstance } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
import type { AgentProgress, SingleResult, TaskToolDetails } from "@oh-my-pi/pi-coding-agent/task";
import { taskToolRenderer } from "@oh-my-pi/pi-coding-agent/task/render";
// Defends the live-rendering contract for the `task` tool: while a Level-1
// subagent is still mid-flight, any nested `task` activity it has produced
// (already-completed sub-calls in `extractedToolData.task`, plus the in-flight
// snapshot in `inflightTaskDetails`) MUST surface in the parent's streaming
// output — same way it surfaces in the finished result.
describe("task renderer: nested live rendering", () => {
beforeAll(async () => {
const theme = await getThemeByName("dark");
expect(theme).toBeDefined();
setThemeInstance(theme!);
});
function makeRunningProgress(overrides: Partial<AgentProgress>): AgentProgress {
return {
index: 0,
id: "parent",
agent: "task",
agentSource: "bundled",
status: "running",
task: "parent assignment",
assignment: "parent assignment",
description: "Parent Level 1 work",
recentTools: [],
recentOutput: [],
toolCount: 1,
tokens: 1000,
cost: 0,
durationMs: 1234,
...overrides,
};
}
function makeCompletedSubResult(id: string, description: string): SingleResult {
return {
index: 0,
id,
agent: "task",
agentSource: "bundled",
task: "sub assignment",
assignment: "sub assignment",
description,
exitCode: 0,
output: "sub-final-output",
stderr: "",
truncated: false,
durationMs: 500,
tokens: 200,
};
}
function makeRunningSubProgress(id: string, description: string): AgentProgress {
return {
index: 0,
id,
agent: "task",
agentSource: "bundled",
status: "running",
task: "sub assignment",
assignment: "sub assignment",
description,
recentTools: [],
recentOutput: [],
toolCount: 0,
tokens: 0,
cost: 0,
durationMs: 0,
};
}
async function render(progress: AgentProgress): Promise<string> {
const theme = (await getThemeByName("dark"))!;
const details: TaskToolDetails = {
projectAgentsDir: null,
results: [],
totalDurationMs: 1234,
progress: [progress],
};
const component = taskToolRenderer.renderResult(
{ content: [{ type: "text", text: "Running 1 agents..." }], details },
{ expanded: false, isPartial: true, spinnerFrame: 0 },
theme,
);
return Bun.stripANSI(component.render(160).join("\n"));
}
it("renders completed nested task results stored in extractedToolData.task while parent is in-progress", async () => {
const parent = makeRunningProgress({
recentTools: [{ tool: "task", args: "", endMs: Date.now() }],
extractedToolData: {
task: [
{
projectAgentsDir: null,
results: [
makeCompletedSubResult("1.0-AlphaSub", "Alpha child"),
makeCompletedSubResult("1.1-BetaSub", "Beta child"),
],
totalDurationMs: 1000,
} satisfies TaskToolDetails,
],
},
});
const text = await render(parent);
// Parent label is intact.
expect(text).toContain("Parent Level 1 work");
// Both nested completed children labels surface.
expect(text).toContain("Alpha child");
expect(text).toContain("Beta child");
expect(text).toContain("1.0 AlphaSub");
expect(text).toContain("1.1 BetaSub");
});
it("renders the in-flight nested task snapshot (progress[]) before the call ends", async () => {
const inflight: TaskToolDetails = {
projectAgentsDir: null,
results: [],
totalDurationMs: 0,
progress: [
makeRunningSubProgress("2.0-GammaSub", "Gamma child running"),
makeRunningSubProgress("2.1-DeltaSub", "Delta child running"),
],
};
const parent = makeRunningProgress({
currentTool: "task",
currentToolStartMs: Date.now(),
inflightTaskDetails: inflight,
});
const text = await render(parent);
expect(text).toContain("Parent Level 1 work");
expect(text).toContain("Gamma child running");
expect(text).toContain("Delta child running");
expect(text).toContain("2.0 GammaSub");
expect(text).toContain("2.1 DeltaSub");
});
it("combines completed and in-flight nested snapshots in one tree", async () => {
const parent = makeRunningProgress({
currentTool: "task",
extractedToolData: {
task: [
{
projectAgentsDir: null,
results: [makeCompletedSubResult("3.0-EpsilonSub", "Epsilon done")],
totalDurationMs: 1000,
} satisfies TaskToolDetails,
],
},
inflightTaskDetails: {
projectAgentsDir: null,
results: [],
totalDurationMs: 0,
progress: [makeRunningSubProgress("3.1-ZetaSub", "Zeta running")],
},
});
const text = await render(parent);
expect(text).toContain("Epsilon done");
expect(text).toContain("Zeta running");
// Completed entry shows "done" badge, in-flight does not.
const epsilonIdx = text.indexOf("Epsilon done");
const zetaIdx = text.indexOf("Zeta running");
// Completed entries are emitted before the in-flight snapshot.
expect(epsilonIdx).toBeLessThan(zetaIdx);
});
});