import type { DaemonClient } from "@getpaseo/client/internal/daemon-client"; import { afterEach, describe, expect, it } from "vitest"; import { selectProviderSubagentsForParent, selectSubagentsForParent } from "./select"; import { useProviderSubagentStore } from "./provider-store"; import { useSessionStore, type Agent } from "@/stores/session-store"; const SERVER_ID = "server-1"; const AGENT_TIMESTAMP = new Date("2026-03-08T10:00:00.000Z"); const EMPTY_PENDING_ARCHIVE_IDS = new Set(); 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: "/tmp/project", model: null, features: undefined, thinkingOptionId: undefined, requiresAttention: false, attentionReason: null, attentionTimestamp: null, archivedAt: null, parentAgentId: null, labels: {}, projectPlacement: null, }; function makeAgent(input: Partial & Pick): Agent { return { ...AGENT_DEFAULTS, ...input }; } function setAgents(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]))); } afterEach(() => { useSessionStore.getState().clearSession(SERVER_ID); useProviderSubagentStore.setState({ descriptors: new Map(), timelines: new Map(), hiddenFromTrack: new Set(), }); }); describe("selectSubagentsForParent", () => { it("hides cached provider children when the host does not support them", () => { useProviderSubagentStore.getState().applyUpdate(SERVER_ID, { kind: "upsert", subagent: { id: "provider-child", parentAgentId: "parent-a", provider: "codex", title: "Provider child", description: null, subtitle: "Codex worker · 4.2k tokens", status: "completed", createdAt: "2026-03-08T10:01:00.000Z", updatedAt: "2026-03-08T10:02:00.000Z", toolCallId: "call-1", }, }); const params = { serverId: SERVER_ID, parentAgentId: "parent-a" }; expect( selectProviderSubagentsForParent(useProviderSubagentStore.getState(), params, false), ).toEqual([]); expect( selectProviderSubagentsForParent(useProviderSubagentStore.getState(), params, true).map( (row) => row.id, ), ).toEqual(["provider-child"]); expect( selectProviderSubagentsForParent(useProviderSubagentStore.getState(), params, true)[0] ?.subtitle, ).toBe("Codex worker · 4.2k tokens"); }); it("hides locally dismissed provider children while retaining their descriptor", () => { const store = useProviderSubagentStore.getState(); store.applyUpdate(SERVER_ID, { kind: "upsert", subagent: { id: "provider-child", parentAgentId: "parent-a", provider: "codex", title: "Provider child", description: null, status: "completed", createdAt: "2026-03-08T10:01:00.000Z", updatedAt: "2026-03-08T10:02:00.000Z", toolCallId: "call-1", }, }); store.hideFinishedForParent(SERVER_ID, "parent-a"); expect( selectProviderSubagentsForParent( useProviderSubagentStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-a" }, true, ), ).toEqual([]); expect(useProviderSubagentStore.getState().descriptors.size).toBe(1); }); it("returns only non-archived children for the requested parent", () => { setAgents([ makeAgent({ id: "parent-a" }), makeAgent({ id: "child-a", parentAgentId: "parent-a" }), makeAgent({ id: "archived-child", parentAgentId: "parent-a", archivedAt: new Date("2026-03-08T12:00:00.000Z"), }), ]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-a", }, EMPTY_PENDING_ARCHIVE_IDS, ); expect(rows.map((row) => row.id)).toEqual(["child-a"]); }); it("excludes siblings, unrelated agents, and grandchildren", () => { setAgents([ makeAgent({ id: "parent-a" }), makeAgent({ id: "parent-b" }), makeAgent({ id: "child-a", parentAgentId: "parent-a" }), makeAgent({ id: "sibling-b", parentAgentId: "parent-b" }), makeAgent({ id: "grandchild-a", parentAgentId: "child-a" }), makeAgent({ id: "unrelated" }), ]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-a", }, EMPTY_PENDING_ARCHIVE_IDS, ); expect(rows.map((row) => row.id)).toEqual(["child-a"]); }); it("shows only direct children for each parent", () => { setAgents([ makeAgent({ id: "parent" }), makeAgent({ id: "child", parentAgentId: "parent" }), makeAgent({ id: "grandchild", parentAgentId: "child" }), ]); const parentRows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent", }, EMPTY_PENDING_ARCHIVE_IDS, ); const childRows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "child", }, EMPTY_PENDING_ARCHIVE_IDS, ); expect(parentRows.map((row) => row.id)).toEqual(["child"]); expect(childRows.map((row) => row.id)).toEqual(["grandchild"]); }); it("sorts by createdAt ascending", () => { setAgents([ makeAgent({ id: "parent" }), makeAgent({ id: "third", parentAgentId: "parent", createdAt: new Date("2026-03-08T10:03:00.000Z"), }), makeAgent({ id: "first", parentAgentId: "parent", createdAt: new Date("2026-03-08T10:01:00.000Z"), }), makeAgent({ id: "second", parentAgentId: "parent", createdAt: new Date("2026-03-08T10:02:00.000Z"), }), ]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent", }, EMPTY_PENDING_ARCHIVE_IDS, ); expect(rows.map((row) => row.id)).toEqual(["first", "second", "third"]); }); it("maps only row-rendered fields and does not expose onOpen", () => { const createdAt = new Date("2026-03-08T10:01:00.000Z"); setAgents([ makeAgent({ id: "parent" }), makeAgent({ id: "child", parentAgentId: "parent", provider: "claude", title: "Review child", status: "running", requiresAttention: true, createdAt, model: "should-not-leak", cwd: "/private/project", }), ]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent", }, EMPTY_PENDING_ARCHIVE_IDS, ); expect(rows).toEqual([ { kind: "paseo", id: "child", provider: "claude", title: "Review child", description: null, subtitle: null, status: "running", requiresAttention: true, createdAt, }, ]); expect(Object.keys(rows[0] ?? {}).sort()).toEqual([ "createdAt", "description", "id", "kind", "provider", "requiresAttention", "status", "subtitle", "title", ]); expect(rows[0]).not.toHaveProperty("onOpen"); expect(rows[0]).not.toHaveProperty("model"); expect(rows[0]).not.toHaveProperty("cwd"); }); it("moves a child when parentAgentId changes", () => { const child = makeAgent({ id: "child", parentAgentId: "parent-a" }); setAgents([makeAgent({ id: "parent-a" }), makeAgent({ id: "parent-b" }), child]); expect( selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-a", }, EMPTY_PENDING_ARCHIVE_IDS, ).map((row) => row.id), ).toEqual(["child"]); expect( selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-b", }, EMPTY_PENDING_ARCHIVE_IDS, ).map((row) => row.id), ).toEqual([]); setAgents([ makeAgent({ id: "parent-a" }), makeAgent({ id: "parent-b" }), { ...child, parentAgentId: "parent-b" }, ]); expect( selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-a", }, EMPTY_PENDING_ARCHIVE_IDS, ).map((row) => row.id), ).toEqual([]); expect( selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent-b", }, EMPTY_PENDING_ARCHIVE_IDS, ).map((row) => row.id), ).toEqual(["child"]); }); it("excludes children whose archive is pending", () => { setAgents([ makeAgent({ id: "parent" }), makeAgent({ id: "child-a", parentAgentId: "parent" }), makeAgent({ id: "child-b", parentAgentId: "parent" }), ]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent", }, new Set(["child-b"]), ); expect(rows.map((row) => row.id)).toEqual(["child-a"]); }); it("returns the shared empty array when pending archive hides the last child", () => { setAgents([makeAgent({ id: "parent" }), makeAgent({ id: "child", parentAgentId: "parent" })]); const rows = selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "parent", }, new Set(["child"]), ); expect(rows).toEqual([]); expect(rows).toBe( selectSubagentsForParent( useSessionStore.getState(), { serverId: SERVER_ID, parentAgentId: "missing-parent", }, EMPTY_PENDING_ARCHIVE_IDS, ), ); }); });