Detach subagents without archiving them (#1612)
* Add detach action for subagents * Translate detached subagent connection error * Address subagent detach review feedback * Fix subagent integration test import * Focus detached subagents as tabs
This commit is contained in:
@@ -82,6 +82,7 @@ export interface SeedDaemonClient {
|
||||
thinkingOptionId?: string;
|
||||
featureValues?: Record<string, unknown>;
|
||||
initialPrompt?: string;
|
||||
labels?: Record<string, string>;
|
||||
}): Promise<{ id: string; status: string }>;
|
||||
fetchAgents(options?: { scope?: "active" }): Promise<{
|
||||
entries: Array<{
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { PARENT_AGENT_ID_LABEL } from "@getpaseo/protocol/agent-labels";
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
import type { SeededWorkspace } from "./seed-client";
|
||||
|
||||
export interface SeededSubagentPair {
|
||||
parent: {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
child: {
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
workspaceId: string;
|
||||
}
|
||||
|
||||
export async function seedParentWithSubagent(
|
||||
workspace: Pick<SeededWorkspace, "client" | "repoPath" | "workspaceId">,
|
||||
input: { parentTitle: string; childTitle: string },
|
||||
): Promise<SeededSubagentPair> {
|
||||
const parent = await workspace.client.createAgent({
|
||||
provider: "mock",
|
||||
cwd: workspace.repoPath,
|
||||
workspaceId: workspace.workspaceId,
|
||||
title: input.parentTitle,
|
||||
modeId: "load-test",
|
||||
model: "ten-second-stream",
|
||||
});
|
||||
const child = await workspace.client.createAgent({
|
||||
provider: "mock",
|
||||
cwd: workspace.repoPath,
|
||||
workspaceId: workspace.workspaceId,
|
||||
title: input.childTitle,
|
||||
modeId: "load-test",
|
||||
model: "ten-second-stream",
|
||||
labels: {
|
||||
[PARENT_AGENT_ID_LABEL]: parent.id,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
parent: {
|
||||
id: parent.id,
|
||||
title: input.parentTitle,
|
||||
},
|
||||
child: {
|
||||
id: child.id,
|
||||
title: input.childTitle,
|
||||
},
|
||||
workspaceId: workspace.workspaceId,
|
||||
};
|
||||
}
|
||||
|
||||
export async function openSubagentsTrack(page: Page): Promise<void> {
|
||||
await page.getByTestId("subagents-track-header").click();
|
||||
}
|
||||
|
||||
export async function expectSubagentRowVisible(page: Page, childId: string): Promise<void> {
|
||||
await expect(page.getByTestId(`subagents-track-row-${childId}`)).toBeVisible({
|
||||
timeout: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export async function expectSubagentRowGone(page: Page, childId: string): Promise<void> {
|
||||
await expect(page.getByTestId(`subagents-track-row-${childId}`)).toHaveCount(0, {
|
||||
timeout: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export async function detachSubagentFromTrack(page: Page, childId: string): Promise<void> {
|
||||
const row = page.getByTestId(`subagents-track-row-${childId}`);
|
||||
await expect(row).toBeVisible({ timeout: 30_000 });
|
||||
await row.hover();
|
||||
|
||||
page.once("dialog", (dialog) => {
|
||||
expect(dialog.message()).toContain("Detach subagent?");
|
||||
void dialog.accept();
|
||||
});
|
||||
|
||||
const detachButton = page.getByTestId(`subagents-track-detach-${childId}`);
|
||||
await expect(detachButton).toBeVisible({ timeout: 30_000 });
|
||||
await detachButton.click();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { test } from "./fixtures";
|
||||
import { expectWorkspaceTabVisible } from "./helpers/archive-tab";
|
||||
import { expectAgentTabActive } from "./helpers/launcher";
|
||||
import { openAgentRoute } from "./helpers/mock-agent";
|
||||
import { seedWorkspace, type SeededWorkspace } from "./helpers/seed-client";
|
||||
import {
|
||||
detachSubagentFromTrack,
|
||||
expectSubagentRowGone,
|
||||
expectSubagentRowVisible,
|
||||
openSubagentsTrack,
|
||||
seedParentWithSubagent,
|
||||
} from "./helpers/subagents";
|
||||
|
||||
test.describe("Subagent detach", () => {
|
||||
let workspace: SeededWorkspace;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
workspace = await seedWorkspace({ repoPrefix: "subagent-detach-" });
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await workspace?.cleanup();
|
||||
});
|
||||
|
||||
test("detaching a subagent focuses it as a workspace tab", async ({ page }) => {
|
||||
const agents = await seedParentWithSubagent(workspace, {
|
||||
parentTitle: "Detach parent",
|
||||
childTitle: "Detached child",
|
||||
});
|
||||
|
||||
await openAgentRoute(page, {
|
||||
workspaceId: agents.workspaceId,
|
||||
agentId: agents.parent.id,
|
||||
});
|
||||
await openSubagentsTrack(page);
|
||||
await expectSubagentRowVisible(page, agents.child.id);
|
||||
|
||||
await detachSubagentFromTrack(page, agents.child.id);
|
||||
|
||||
await expectSubagentRowGone(page, agents.child.id);
|
||||
await expectWorkspaceTabVisible(page, agents.child.id);
|
||||
await expectAgentTabActive(page, agents.child.id);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user