Show workflows in the subagent track (#2933)
* 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
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { mkdtempSync, realpathSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { expect, test } from "../support/fixtures";
|
||||
import { submitMessage } from "../support/helpers/composer";
|
||||
import { cleanupRewindFlow, launchAgent, type AgentHandle } from "../support/helpers/rewind-flow";
|
||||
import { openSubagentsTrack } from "../support/helpers/subagents";
|
||||
|
||||
process.env.CLAUDE_CODE_WORKFLOWS = "1";
|
||||
|
||||
const WORKFLOW_SCRIPT = path.resolve(__dirname, "../fixtures/claude-workflow/inspect-project.js");
|
||||
const REPO_ROOT = path.resolve(__dirname, "../../../..");
|
||||
const DESCRIPTION = "Inspect Paseo and explain what the project does";
|
||||
|
||||
test.describe("real Claude project-inspection workflow", () => {
|
||||
test.setTimeout(300_000);
|
||||
|
||||
test("shows the workflow's actual report in the generic detail pane", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
const cwd = realpathSync(mkdtempSync(path.join(tmpdir(), "paseo-project-inspection-")));
|
||||
let handle: AgentHandle | undefined;
|
||||
|
||||
try {
|
||||
handle = await launchAgent({ page, provider: "claude", cwd, mode: "full-access" });
|
||||
await submitMessage(
|
||||
page,
|
||||
`Use Claude Code's Workflow tool exactly once with scriptPath ${JSON.stringify(
|
||||
WORKFLOW_SCRIPT,
|
||||
)} and args ${JSON.stringify({ repoPath: REPO_ROOT })}. Wait for completion, then reply with exactly PROJECT_INSPECTION_DONE.`,
|
||||
);
|
||||
|
||||
await expect(page.getByTestId("subagents-track-header")).toBeVisible({ timeout: 60_000 });
|
||||
await openSubagentsTrack(page);
|
||||
const row = page
|
||||
.getByTestId(/^subagents-track-row-/)
|
||||
.filter({ has: page.getByText(DESCRIPTION, { exact: true }) });
|
||||
await expect(row).toBeVisible({ timeout: 60_000 });
|
||||
await expect(row.getByRole("progressbar", { name: "Agent running" })).toHaveCount(0, {
|
||||
timeout: 180_000,
|
||||
});
|
||||
await expect(
|
||||
page.getByTestId("assistant-message").filter({ hasText: "PROJECT_INSPECTION_DONE" }).last(),
|
||||
).toBeVisible({ timeout: 180_000 });
|
||||
|
||||
await row.click();
|
||||
const panel = page.getByTestId("provider-subagent-panel");
|
||||
await expect(panel).toBeVisible({ timeout: 30_000 });
|
||||
for (const heading of [
|
||||
"What Paseo is",
|
||||
"Who it is for",
|
||||
"How it works",
|
||||
"Repository structure",
|
||||
]) {
|
||||
await expect(panel.getByText(heading, { exact: true })).toBeVisible();
|
||||
}
|
||||
await expect(panel.getByText("export const meta", { exact: false })).toHaveCount(0);
|
||||
await expect(
|
||||
panel.getByText("Inspect the Paseo repository at", { exact: false }),
|
||||
).toHaveCount(0);
|
||||
await page.screenshot({ path: testInfo.outputPath("project-workflow-pane.png") });
|
||||
} finally {
|
||||
await cleanupRewindFlow({ handle, cwd });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { mkdtempSync, realpathSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { test } from "../support/fixtures";
|
||||
import {
|
||||
askClaudeToRunWorkflow,
|
||||
expectSingleWorkflowParentCard,
|
||||
expectWorkflowCompleted,
|
||||
expectWorkflowRunning,
|
||||
openWorkflowTimeline,
|
||||
releaseWorkflow,
|
||||
} from "../support/helpers/claude-workflow";
|
||||
import { cleanupRewindFlow, launchAgent, type AgentHandle } from "../support/helpers/rewind-flow";
|
||||
|
||||
// Claude Code currently gates the Workflow tool behind its own rollout. The real-provider test
|
||||
// opts in explicitly so it tests Paseo's integration rather than the account's rollout cohort.
|
||||
process.env.CLAUDE_CODE_WORKFLOWS = "1";
|
||||
|
||||
const WORKFLOW_SCRIPT = path.resolve(__dirname, "../fixtures/claude-workflow/one-child.js");
|
||||
|
||||
test.describe("real Claude workflow subagent row", () => {
|
||||
test.setTimeout(300_000);
|
||||
|
||||
test("shows a real Claude workflow running and completed through the generic subagent UI", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
const cwd = realpathSync(mkdtempSync(path.join(tmpdir(), "paseo-claude-workflow-row-")));
|
||||
const gatePath = path.join(cwd, "release-workflow");
|
||||
let handle: AgentHandle | undefined;
|
||||
|
||||
try {
|
||||
handle = await launchAgent({ page, provider: "claude", cwd, mode: "full-access" });
|
||||
|
||||
await test.step("ask Claude to run the workflow", async () => {
|
||||
await askClaudeToRunWorkflow(handle!, WORKFLOW_SCRIPT, gatePath);
|
||||
await expectWorkflowRunning(page);
|
||||
await page.screenshot({ path: testInfo.outputPath("workflow-running.png") });
|
||||
releaseWorkflow(gatePath);
|
||||
});
|
||||
|
||||
await test.step("see the workflow finish without leaving a running row", async () => {
|
||||
await expectWorkflowCompleted(page);
|
||||
await expectSingleWorkflowParentCard(page);
|
||||
await page.screenshot({ path: testInfo.outputPath("workflow-completed.png") });
|
||||
});
|
||||
|
||||
await test.step("open the workflow through the existing provider-subagent pane", async () => {
|
||||
await openWorkflowTimeline(page);
|
||||
await page.screenshot({ path: testInfo.outputPath("workflow-timeline.png") });
|
||||
});
|
||||
} finally {
|
||||
await cleanupRewindFlow({ handle, cwd });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user