Files
paseo/packages/app/src/workspace-tabs/agent-visibility.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

424 lines
13 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { Agent } from "@/stores/session-store";
import {
buildWorkspaceTabSnapshot,
deriveWorkspaceAgentVisibility,
shouldPruneWorkspaceAgentTab,
workspaceAgentVisibilityEqual,
} from "@/workspace-tabs/agent-visibility";
function makeAgent(input: {
id: string;
cwd: string;
workspaceId?: string;
parentAgentId?: string | null;
archivedAt?: Date | null;
createdAt?: Date;
lastActivityAt?: Date;
}): Agent {
const createdAt = input.createdAt ?? new Date("2026-03-04T00:00:00.000Z");
const lastActivityAt = input.lastActivityAt ?? createdAt;
return {
serverId: "srv",
id: input.id,
provider: "codex",
status: "idle",
activeTurn: null,
createdAt,
updatedAt: createdAt,
lastUserMessageAt: null,
lastActivityAt,
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
runtimeInfo: {
provider: "codex",
sessionId: null,
},
title: null,
cwd: input.cwd,
workspaceId: input.workspaceId,
model: null,
thinkingOptionId: null,
parentAgentId: input.parentAgentId ?? null,
labels: {},
requiresAttention: false,
attentionReason: null,
attentionTimestamp: null,
archivedAt: input.archivedAt ?? null,
};
}
const WORKSPACE_ID = "ws-1";
describe("workspace agent visibility", () => {
it("keeps subagents active and known while excluding them from auto-open", () => {
const parent = makeAgent({
id: "parent-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
});
const child = makeAgent({
id: "child-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
parentAgentId: "parent-agent",
});
const result = deriveWorkspaceAgentVisibility({
sessionAgents: new Map<string, Agent>([
[parent.id, parent],
[child.id, child],
]),
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set(["parent-agent", "child-agent"]));
expect(result.autoOpenAgentIds).toEqual(new Set(["parent-agent"]));
expect(result.knownAgentIds).toEqual(new Set(["parent-agent", "child-agent"]));
});
it("keeps archived subagents known but excludes them from active and auto-open", () => {
const archivedChild = makeAgent({
id: "archived-child",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
parentAgentId: "parent-agent",
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
});
const result = deriveWorkspaceAgentVisibility({
sessionAgents: new Map<string, Agent>([[archivedChild.id, archivedChild]]),
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set<string>());
expect(result.autoOpenAgentIds).toEqual(new Set<string>());
expect(result.knownAgentIds).toEqual(new Set(["archived-child"]));
});
it("excludes a child from auto-open even when its snapshot arrives before the parent", () => {
const child = makeAgent({
id: "child-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
parentAgentId: "parent-agent",
});
const parent = makeAgent({
id: "parent-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
});
const result = deriveWorkspaceAgentVisibility({
sessionAgents: new Map<string, Agent>([
[child.id, child],
[parent.id, parent],
]),
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set(["child-agent", "parent-agent"]));
expect(result.autoOpenAgentIds).toEqual(new Set(["parent-agent"]));
expect(result.knownAgentIds).toEqual(new Set(["child-agent", "parent-agent"]));
});
it("auto-opens a subagent whose parent belongs to another workspace", () => {
const parent = makeAgent({
id: "parent-agent",
cwd: "/repo",
workspaceId: "ws-parent",
});
const child = makeAgent({
id: "child-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
parentAgentId: parent.id,
});
const result = deriveWorkspaceAgentVisibility({
sessionAgents: new Map<string, Agent>([
[parent.id, parent],
[child.id, child],
]),
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set(["child-agent"]));
expect(result.autoOpenAgentIds).toEqual(new Set(["child-agent"]));
expect(result.knownAgentIds).toEqual(new Set(["child-agent"]));
});
it("keeps archived agents out of activeAgentIds but present in knownAgentIds", () => {
const visible = makeAgent({
id: "visible-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
createdAt: new Date("2026-03-04T00:00:00.000Z"),
});
const archived = makeAgent({
id: "archived-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
createdAt: new Date("2026-03-04T00:01:00.000Z"),
});
const otherWorkspace = makeAgent({
id: "other-workspace-agent",
cwd: "/repo/other",
workspaceId: "ws-other",
});
const sessionAgents = new Map<string, Agent>([
[visible.id, visible],
[archived.id, archived],
[otherWorkspace.id, otherWorkspace],
]);
const result = deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set(["visible-agent"]));
expect(result.autoOpenAgentIds).toEqual(new Set(["visible-agent"]));
expect(result.knownAgentIds.has("visible-agent")).toBe(true);
expect(result.knownAgentIds.has("archived-agent")).toBe(true);
expect(result.knownAgentIds.has("other-workspace-agent")).toBe(false);
});
it("treats lazy historical details as known without making them active", () => {
const active = makeAgent({
id: "active-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
});
const historicalDetail = makeAgent({
id: "historical-agent",
cwd: "/repo/worktree",
workspaceId: WORKSPACE_ID,
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
});
const result = deriveWorkspaceAgentVisibility({
sessionAgents: new Map([[active.id, active]]),
agentDetails: new Map([[historicalDetail.id, historicalDetail]]),
workspaceId: WORKSPACE_ID,
});
expect(result.activeAgentIds).toEqual(new Set(["active-agent"]));
expect(result.knownAgentIds).toEqual(new Set(["active-agent", "historical-agent"]));
});
it("prunes archived agent tabs so archiving on one client closes tabs on all clients", () => {
const activeAgentIds = new Set<string>();
expect(
shouldPruneWorkspaceAgentTab({
agentId: "archived-agent",
agentsHydrated: true,
activeAgentIds,
}),
).toBe(true);
});
it("prunes pinned archived agent tabs because archive state is authoritative", () => {
expect(
shouldPruneWorkspaceAgentTab({
agentId: "archived-agent",
agentsHydrated: true,
activeAgentIds: new Set<string>(),
}),
).toBe(true);
});
it("does not prune active agent tabs", () => {
const activeAgentIds = new Set(["active-agent"]);
expect(
shouldPruneWorkspaceAgentTab({
agentId: "active-agent",
agentsHydrated: true,
activeAgentIds,
}),
).toBe(false);
});
it("prunes agent tabs once agents are hydrated and the agent is missing from activeAgentIds", () => {
expect(
shouldPruneWorkspaceAgentTab({
agentId: "missing-agent",
agentsHydrated: true,
activeAgentIds: new Set<string>(),
}),
).toBe(true);
});
it("matches agents by workspaceId regardless of cwd", () => {
const sessionAgents = new Map<string, Agent>([
[
"stamped-agent",
makeAgent({
id: "stamped-agent",
cwd: "/repo/subdir",
workspaceId: "ws-1",
}),
],
]);
const result = deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId: "ws-1",
});
expect(result.activeAgentIds).toEqual(new Set(["stamped-agent"]));
expect(result.knownAgentIds).toEqual(new Set(["stamped-agent"]));
});
it("excludes a stamped agent whose workspaceId belongs to another workspace sharing the cwd", () => {
const sessionAgents = new Map<string, Agent>([
[
"other-ws-agent",
makeAgent({
id: "other-ws-agent",
cwd: "/repo/worktree",
workspaceId: "ws-2",
}),
],
]);
const result = deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId: "ws-1",
});
expect(result.activeAgentIds).toEqual(new Set<string>());
expect(result.knownAgentIds).toEqual(new Set<string>());
});
it("excludes agents without a workspaceId", () => {
const sessionAgents = new Map<string, Agent>([
["ownerless-agent", makeAgent({ id: "ownerless-agent", cwd: "/repo/worktree" })],
]);
const result = deriveWorkspaceAgentVisibility({
sessionAgents,
workspaceId: "ws-1",
});
expect(result.activeAgentIds).toEqual(new Set<string>());
expect(result.knownAgentIds).toEqual(new Set<string>());
});
it("builds the tab reconciliation snapshot without callers unpacking agent visibility", () => {
const agentVisibility = {
activeAgentIds: new Set(["active-agent"]),
autoOpenAgentIds: new Set(["root-agent"]),
knownAgentIds: new Set(["active-agent", "archived-agent"]),
};
expect(
buildWorkspaceTabSnapshot({
agentVisibility,
agentsHydrated: true,
terminalsHydrated: true,
knownTerminalIds: ["terminal-1", "script-terminal"],
standaloneTerminalIds: ["terminal-1"],
hasActivePendingTerminalCreate: false,
hasActivePendingDraftCreate: false,
}),
).toEqual({
agentsHydrated: true,
terminalsHydrated: true,
activeAgentIds: agentVisibility.activeAgentIds,
autoOpenAgentIds: agentVisibility.autoOpenAgentIds,
knownAgentIds: agentVisibility.knownAgentIds,
knownTerminalIds: ["terminal-1", "script-terminal"],
standaloneTerminalIds: ["terminal-1"],
hasActivePendingTerminalCreate: false,
hasActivePendingDraftCreate: false,
});
});
describe("workspaceAgentVisibilityEqual", () => {
it("returns true for identical sets", () => {
const a = {
activeAgentIds: new Set(["a", "b"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a", "b", "c"]),
};
const b = {
activeAgentIds: new Set(["a", "b"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a", "b", "c"]),
};
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
});
it("returns false when activeAgentIds differ", () => {
const a = {
activeAgentIds: new Set(["a"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a"]),
};
const b = {
activeAgentIds: new Set(["b"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a"]),
};
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
});
it("returns false when autoOpenAgentIds differ", () => {
const a = {
activeAgentIds: new Set(["a", "b"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a", "b"]),
};
const b = {
activeAgentIds: new Set(["a", "b"]),
autoOpenAgentIds: new Set(["b"]),
knownAgentIds: new Set(["a", "b"]),
};
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
});
it("returns false when knownAgentIds differ", () => {
const a = {
activeAgentIds: new Set(["a"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a"]),
};
const b = {
activeAgentIds: new Set(["a"]),
autoOpenAgentIds: new Set(["a"]),
knownAgentIds: new Set(["a", "b"]),
};
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
});
it("returns true for empty sets", () => {
const a = {
activeAgentIds: new Set<string>(),
autoOpenAgentIds: new Set<string>(),
knownAgentIds: new Set<string>(),
};
const b = {
activeAgentIds: new Set<string>(),
autoOpenAgentIds: new Set<string>(),
knownAgentIds: new Set<string>(),
};
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
});
});
});