Files
paseo/packages/app/src/stores/workspace-subagents-integration.test.ts
T
Mohamed Boudra ce905d9483 fix(app): open new tabs in the focused pane
Preserve ambient placement for reconciliation and background opens while routing user-directed tab creation through the focused visible pane. Defer terminal reconciliation during creates so a push cannot preempt the intended pane placement.
2026-08-19 15:52:51 +02:00

316 lines
9.4 KiB
TypeScript

import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
buildWorkspaceTabSnapshot,
deriveWorkspaceAgentVisibility,
type WorkspaceAgentVisibility,
} from "@/workspace-tabs/agent-visibility";
import { buildWorkspaceTabPersistenceKey } from "@/workspace-tabs/model";
import { selectSubagentsForParent } from "@/subagents/select";
import { findPaneById, findPaneContainingTab } from "./workspace-layout-actions";
import { useWorkspaceLayoutStore } from "./workspace-layout-store";
import { useSessionStore, type Agent } from "./session-store";
vi.mock("@react-native-async-storage/async-storage", () => {
const storage = new Map<string, string>();
return {
default: {
getItem: vi.fn(async (key: string) => storage.get(key) ?? null),
setItem: vi.fn(async (key: string, value: string) => {
storage.set(key, value);
}),
removeItem: vi.fn(async (key: string) => {
storage.delete(key);
}),
},
};
});
const SERVER_ID = "server-1";
const WORKSPACE_ID = "ws-main";
const WORKSPACE_DIRECTORY = "/repo/worktree";
const AGENT_TIMESTAMP = new Date("2026-04-21T10:00:00.000Z");
const AGENT_DEFAULTS: Agent = {
serverId: SERVER_ID,
id: "agent",
provider: "codex",
status: "idle",
activeTurn: null,
createdAt: AGENT_TIMESTAMP,
updatedAt: AGENT_TIMESTAMP,
lastUserMessageAt: null,
lastActivityAt: AGENT_TIMESTAMP,
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
runtimeInfo: undefined,
lastUsage: undefined,
lastError: null,
title: "Agent",
cwd: WORKSPACE_DIRECTORY,
workspaceId: WORKSPACE_ID,
model: null,
features: undefined,
thinkingOptionId: undefined,
requiresAttention: false,
attentionReason: null,
attentionTimestamp: null,
archivedAt: null,
parentAgentId: null,
labels: {},
projectPlacement: null,
};
function makeAgent(input: Partial<Agent> & Pick<Agent, "id">): Agent {
return { ...AGENT_DEFAULTS, ...input };
}
function initializeAgents(agents: Agent[]): void {
useSessionStore.getState().initializeSession(SERVER_ID, null as unknown as DaemonClient);
useSessionStore
.getState()
.setAgents(SERVER_ID, new Map(agents.map((agent) => [agent.id, agent])));
}
function appendAgent(agent: Agent): void {
useSessionStore.getState().setAgents(SERVER_ID, (agents) => {
const nextAgents = new Map(agents);
nextAgents.set(agent.id, agent);
return nextAgents;
});
}
function deriveVisibilityFromSession(): WorkspaceAgentVisibility {
const sessionAgents = useSessionStore.getState().sessions[SERVER_ID]?.agents ?? new Map();
return deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId: WORKSPACE_ID,
});
}
function reconcileWorkspaceTabs(workspaceKey: string, visibility: WorkspaceAgentVisibility): void {
useWorkspaceLayoutStore.getState().reconcileTabs(
workspaceKey,
buildWorkspaceTabSnapshot({
agentVisibility: visibility,
agentsHydrated: true,
terminalsHydrated: true,
knownTerminalIds: [],
standaloneTerminalIds: [],
hasActivePendingTerminalCreate: false,
hasActivePendingDraftCreate: false,
}),
);
}
function getWorkspaceTabIds(workspaceKey: string): string[] {
return useWorkspaceLayoutStore
.getState()
.getWorkspaceTabs(workspaceKey)
.map((tab) => tab.tabId);
}
afterEach(() => {
useSessionStore.getState().clearSession(SERVER_ID);
useWorkspaceLayoutStore.setState({
layoutByWorkspace: {},
splitSizesByWorkspace: {},
pinnedAgentIdsByWorkspace: {},
hiddenAgentIdsByWorkspace: {},
});
});
describe("workspace subagents integration", () => {
it("keeps a child ingested before its parent out of auto-tabs, then exposes it in the parent section", () => {
const workspaceKey = buildWorkspaceTabPersistenceKey({
serverId: SERVER_ID,
workspaceId: WORKSPACE_ID,
});
expect(workspaceKey).toBeTruthy();
const child = makeAgent({
id: "child-agent",
parentAgentId: "parent-agent",
title: "Child agent",
});
const parent = makeAgent({
id: "parent-agent",
title: "Parent agent",
});
initializeAgents([child]);
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
expect(getWorkspaceTabIds(workspaceKey!)).toEqual([]);
appendAgent(parent);
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
expect(getWorkspaceTabIds(workspaceKey!)).toEqual(["agent_parent-agent"]);
expect(
selectSubagentsForParent(
useSessionStore.getState(),
{
serverId: SERVER_ID,
parentAgentId: "parent-agent",
},
new Set(),
).map((row) => row.id),
).toEqual(["child-agent"]);
});
it("moves a detached child out of the parent section and back into normal workspace tabs", () => {
const workspaceKey = buildWorkspaceTabPersistenceKey({
serverId: SERVER_ID,
workspaceId: WORKSPACE_ID,
});
expect(workspaceKey).toBeTruthy();
const parent = makeAgent({
id: "parent-agent",
title: "Parent agent",
});
const child = makeAgent({
id: "child-agent",
parentAgentId: "parent-agent",
title: "Child agent",
});
initializeAgents([parent, child]);
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
expect(getWorkspaceTabIds(workspaceKey!)).toEqual(["agent_parent-agent"]);
expect(
selectSubagentsForParent(
useSessionStore.getState(),
{
serverId: SERVER_ID,
parentAgentId: "parent-agent",
},
new Set(),
).map((row) => row.id),
).toEqual(["child-agent"]);
appendAgent({ ...child, parentAgentId: null, labels: {} });
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
expect(getWorkspaceTabIds(workspaceKey!)).toEqual(["agent_parent-agent", "agent_child-agent"]);
expect(
selectSubagentsForParent(
useSessionStore.getState(),
{
serverId: SERVER_ID,
parentAgentId: "parent-agent",
},
new Set(),
),
).toEqual([]);
});
it("auto-opens a cross-workspace child while retaining it in the parent section", () => {
const workspaceKey = buildWorkspaceTabPersistenceKey({
serverId: SERVER_ID,
workspaceId: WORKSPACE_ID,
});
expect(workspaceKey).toBeTruthy();
const parent = makeAgent({
id: "parent-agent",
workspaceId: "ws-parent",
title: "Parent agent",
});
const child = makeAgent({
id: "child-agent",
parentAgentId: parent.id,
title: "Cross-workspace child",
});
initializeAgents([parent, child]);
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
expect(getWorkspaceTabIds(workspaceKey!)).toEqual(["agent_child-agent"]);
expect(
selectSubagentsForParent(
useSessionStore.getState(),
{
serverId: SERVER_ID,
parentAgentId: parent.id,
},
new Set(),
).map((row) => row.id),
).toEqual([child.id]);
});
it("opens a subagent in the explorer pane when focused", () => {
const workspaceKey = buildWorkspaceTabPersistenceKey({
serverId: SERVER_ID,
workspaceId: WORKSPACE_ID,
});
expect(workspaceKey).toBeTruthy();
const parent = makeAgent({
id: "parent-agent",
title: "Parent agent",
});
const child = makeAgent({
id: "child-agent",
parentAgentId: parent.id,
title: "Child agent",
});
initializeAgents([parent, child]);
reconcileWorkspaceTabs(workspaceKey!, deriveVisibilityFromSession());
const store = useWorkspaceLayoutStore.getState();
const tabId = store.openTabInExplorerPaneFocused(workspaceKey!, {
target: { kind: "agent", agentId: child.id },
parentTabId: `agent_${parent.id}`,
});
const state = useWorkspaceLayoutStore.getState();
const layout = state.layoutByWorkspace[workspaceKey!];
const explorerPaneId = state.explorerPaneIdByWorkspace[workspaceKey!];
expect(explorerPaneId).toBeTruthy();
expect(findPaneContainingTab(layout.root, tabId!)?.id).toBe(explorerPaneId);
expect(findPaneById(layout.root, explorerPaneId!)?.hidden).toBeUndefined();
expect(layout.focusedPaneId).toBe(explorerPaneId);
});
it("opens a provider subagent in the explorer pane when focused", () => {
const workspaceKey = buildWorkspaceTabPersistenceKey({
serverId: SERVER_ID,
workspaceId: WORKSPACE_ID,
});
expect(workspaceKey).toBeTruthy();
const store = useWorkspaceLayoutStore.getState();
const tabId = store.openTabInExplorerPaneFocused(workspaceKey!, {
target: { kind: "provider_subagent", parentAgentId: "parent-agent", subagentId: "task-1" },
parentTabId: "agent_parent-agent",
});
const state = useWorkspaceLayoutStore.getState();
const layout = state.layoutByWorkspace[workspaceKey!];
const explorerPaneId = state.explorerPaneIdByWorkspace[workspaceKey!];
expect(explorerPaneId).toBeTruthy();
expect(findPaneContainingTab(layout.root, tabId!)?.id).toBe(explorerPaneId);
expect(findPaneById(layout.root, explorerPaneId!)?.hidden).toBeUndefined();
expect(layout.focusedPaneId).toBe(explorerPaneId);
});
});