* feat(server): show provider workflows in subagent track Normalize workflow tasks into the provider-subagent contract so shared clients need no workflow awareness. Persisted nonterminal runs replay as failed because their original runtime can no longer deliver a terminal update. * fix(server): preserve workflow replay and tool cards * fix(server): suppress workflow notification cards * fix(server): replay workflow child timelines * fix(server): fail tasks when Claude exits * fix(server): preserve workflow replay parity * fix(server): fail tasks on Claude restart * fix(server): hide workflow child prompts on replay * fix: terminalize workflows on session reload * fix(server): own workflow notification routing * feat(server): show workflow results in detail pane * fix(server): preserve unrestored task notifications * fix(server): normalize replay task notifications
76 lines
3.1 KiB
TypeScript
76 lines
3.1 KiB
TypeScript
import { writeFileSync } from "node:fs";
|
|
import { expect, type Page } from "@playwright/test";
|
|
import { submitMessage } from "./composer";
|
|
import type { AgentHandle } from "./rewind-flow";
|
|
import { openSubagentsTrack } from "./subagents";
|
|
|
|
export const WORKFLOW_ROW_DESCRIPTION = "Verify the workflow row lifecycle";
|
|
export const WORKFLOW_ROW_MARKER = "PASEO_WORKFLOW_ROW_OK";
|
|
|
|
export async function askClaudeToRunWorkflow(
|
|
handle: AgentHandle,
|
|
scriptPath: string,
|
|
gatePath: string,
|
|
): Promise<void> {
|
|
await submitMessage(
|
|
handle.page,
|
|
`Use Claude Code's Workflow tool exactly once with scriptPath ${JSON.stringify(
|
|
scriptPath,
|
|
)} and args ${JSON.stringify({ gatePath })}. Wait for its completion notification, then reply with exactly ${WORKFLOW_ROW_MARKER}.`,
|
|
);
|
|
await expect(handle.page.getByTestId("subagents-track-header")).toBeVisible({ timeout: 60_000 });
|
|
await openSubagentsTrack(handle.page);
|
|
}
|
|
|
|
export function releaseWorkflow(gatePath: string): void {
|
|
writeFileSync(gatePath, "release\n", "utf8");
|
|
}
|
|
|
|
export function workflowRow(page: Page) {
|
|
// The parent transcript's Workflow tool card has the same accessible name. The track has no
|
|
// separate landmark, so its established row test id is the only unambiguous scope.
|
|
return page
|
|
.getByTestId(/^subagents-track-row-/)
|
|
.filter({ has: page.getByText(WORKFLOW_ROW_DESCRIPTION, { exact: true }) });
|
|
}
|
|
|
|
export async function expectWorkflowRunning(page: Page): Promise<void> {
|
|
const row = workflowRow(page);
|
|
await expect(row).toBeVisible({ timeout: 60_000 });
|
|
await expect(row).toContainText(WORKFLOW_ROW_DESCRIPTION);
|
|
await expect(row).toContainText(/Workflow(?: · .+)?/);
|
|
await expect(row.getByRole("progressbar", { name: "Agent running" })).toBeVisible();
|
|
}
|
|
|
|
export async function expectWorkflowCompleted(page: Page): Promise<void> {
|
|
const row = workflowRow(page);
|
|
await expect(row.getByRole("progressbar", { name: "Agent running" })).toHaveCount(0, {
|
|
timeout: 120_000,
|
|
});
|
|
await expect(page.getByTestId("subagents-track-archive-finished")).toBeVisible();
|
|
await expect(
|
|
page.getByTestId("assistant-message").filter({ hasText: WORKFLOW_ROW_MARKER }).last(),
|
|
).toBeVisible({ timeout: 120_000 });
|
|
}
|
|
|
|
export async function expectSingleWorkflowParentCard(page: Page): Promise<void> {
|
|
const parentToolCards = page.getByTestId("tool-call-badge");
|
|
await expect(
|
|
parentToolCards.filter({ has: page.getByText("Workflow", { exact: true }) }),
|
|
).toHaveCount(1);
|
|
await expect(parentToolCards.filter({ hasText: "Task Notification" })).toHaveCount(0);
|
|
}
|
|
|
|
export async function openWorkflowTimeline(page: Page): Promise<void> {
|
|
await workflowRow(page).click();
|
|
const panel = page.getByTestId("provider-subagent-panel");
|
|
await expect(panel).toBeVisible({ timeout: 30_000 });
|
|
await expect(page.getByTestId("provider-subagent-pane-subtitle")).toHaveText(
|
|
/Workflow(?: · .+)?/,
|
|
);
|
|
await expect(panel.getByText("Start chatting with this agent...", { exact: true })).toHaveCount(
|
|
0,
|
|
);
|
|
await expect(panel.getByText(WORKFLOW_ROW_MARKER, { exact: true })).toBeVisible();
|
|
}
|