mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-26 19:22:51 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65ce4b590a |
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
@@ -11,7 +11,10 @@ import {
|
||||
expectWorkspaceTabVisible,
|
||||
openWorkspaceWithAgents,
|
||||
} from "../support/helpers/archive-tab";
|
||||
import { expectAgentAbsentFromWorkspaceLayout } from "../support/helpers/workspace-layout";
|
||||
import {
|
||||
expectAgentAbsentFromWorkspaceLayout,
|
||||
seedForeignPinnedAgentTab,
|
||||
} from "../support/helpers/workspace-layout";
|
||||
import { clickNewChat } from "../support/helpers/launcher";
|
||||
import { expectComposerVisible } from "../support/helpers/composer";
|
||||
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
|
||||
@@ -105,6 +108,83 @@ async function openWorkspaceTabUrl(page: Page, route: string): Promise<void> {
|
||||
test.describe("Workspace navigation regression", () => {
|
||||
test.describe.configure({ timeout: 240_000 });
|
||||
|
||||
test("removes a foreign pinned agent when restoring a saved workspace layout", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
const serverId = getServerId();
|
||||
const firstWorkspace = await seedWorkspace({
|
||||
repoPrefix: "workspace-owner-",
|
||||
title: "Original workspace",
|
||||
});
|
||||
const secondWorkspace = await seedWorkspace({
|
||||
repoPrefix: "workspace-restored-",
|
||||
title: "Restored workspace",
|
||||
});
|
||||
try {
|
||||
const firstAgent = await createMockIdleAgent(firstWorkspace.client, {
|
||||
cwd: firstWorkspace.repoPath,
|
||||
workspaceId: firstWorkspace.workspaceId,
|
||||
title: "Original workspace agent",
|
||||
});
|
||||
const secondAgent = await createMockIdleAgent(secondWorkspace.client, {
|
||||
cwd: secondWorkspace.repoPath,
|
||||
workspaceId: secondWorkspace.workspaceId,
|
||||
title: "Restored workspace agent",
|
||||
});
|
||||
await gotoAppShell(page);
|
||||
await openWorkspaceWithAgents(page, [firstAgent, secondAgent]);
|
||||
await seedForeignPinnedAgentTab(page, {
|
||||
serverId,
|
||||
workspaceId: secondWorkspace.workspaceId,
|
||||
agentId: firstAgent.id,
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
await waitForSidebarHydration(page);
|
||||
await waitForWorkspaceTabsVisible(page);
|
||||
await expectWorkspaceHeader(page, {
|
||||
title: secondWorkspace.workspaceName,
|
||||
subtitle: secondWorkspace.projectDisplayName,
|
||||
});
|
||||
await testInfo.attach("hydrated-workspace", {
|
||||
body: await page.screenshot({ path: testInfo.outputPath("hydrated-workspace.png") }),
|
||||
contentType: "image/png",
|
||||
});
|
||||
await expectAgentAbsentFromWorkspaceLayout(page, {
|
||||
serverId,
|
||||
workspaceId: secondWorkspace.workspaceId,
|
||||
agentId: firstAgent.id,
|
||||
});
|
||||
await expectOnlyWorkspaceAgentTabsVisible(page, [secondAgent.id]);
|
||||
await testInfo.attach("restored-workspace", {
|
||||
body: await page.screenshot({ path: testInfo.outputPath("restored-workspace.png") }),
|
||||
contentType: "image/png",
|
||||
});
|
||||
|
||||
await switchWorkspaceViaSidebar({
|
||||
page,
|
||||
serverId,
|
||||
workspaceId: firstWorkspace.workspaceId,
|
||||
});
|
||||
await waitForWorkspaceTabsVisible(page);
|
||||
await expectOnlyWorkspaceAgentTabsVisible(page, [firstAgent.id]);
|
||||
const agents = await firstWorkspace.client.fetchAgents();
|
||||
expect(
|
||||
agents.entries.find(({ agent }) => agent.id === firstAgent.id)?.agent.workspaceId,
|
||||
).toBe(firstWorkspace.workspaceId);
|
||||
expect(
|
||||
agents.entries.find(({ agent }) => agent.id === secondAgent.id)?.agent.workspaceId,
|
||||
).toBe(secondWorkspace.workspaceId);
|
||||
await testInfo.attach("original-workspace", {
|
||||
body: await page.screenshot({ path: testInfo.outputPath("original-workspace.png") }),
|
||||
contentType: "image/png",
|
||||
});
|
||||
} finally {
|
||||
await secondWorkspace.cleanup();
|
||||
await firstWorkspace.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("keeps an offline notification on the agent route until the host connects", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
import { z } from "zod";
|
||||
import { WorkspaceLayoutPersistedStateSchema } from "../../../src/stores/workspace-layout-storage";
|
||||
import { openTabInLayoutFocused } from "../../../src/stores/workspace-layout-actions";
|
||||
import {
|
||||
buildWorkspaceTabPersistenceKey,
|
||||
type WorkspaceTab,
|
||||
} from "../../../src/workspace-tabs/model";
|
||||
import { getServerId } from "./server-id";
|
||||
|
||||
export async function seedForeignPinnedAgentTab(
|
||||
page: Page,
|
||||
input: { serverId: string; workspaceId: string; agentId: string },
|
||||
): Promise<void> {
|
||||
const raw = await page.evaluate(() => localStorage.getItem("workspace-layout-state"));
|
||||
if (raw === null) throw new Error("Workspace layout was not persisted");
|
||||
const stored = z
|
||||
.object({ state: WorkspaceLayoutPersistedStateSchema, version: z.number().optional() })
|
||||
.parse(JSON.parse(raw));
|
||||
const key = buildWorkspaceTabPersistenceKey(input);
|
||||
const layout = key ? stored.state.layoutByWorkspace[key] : null;
|
||||
if (!key || !layout) throw new Error(`No persisted layout for workspace ${input.workspaceId}`);
|
||||
const opened = openTabInLayoutFocused({
|
||||
layout,
|
||||
target: { kind: "agent", agentId: input.agentId },
|
||||
now: 1,
|
||||
placement: { mode: "focused" },
|
||||
explorerSidebarPaneId: stored.state.explorerSidebarPaneIdByWorkspace?.[key] ?? null,
|
||||
});
|
||||
if (!opened) throw new Error("Failed to seed foreign agent tab");
|
||||
stored.state = WorkspaceLayoutPersistedStateSchema.parse({
|
||||
...stored.state,
|
||||
layoutByWorkspace: { ...stored.state.layoutByWorkspace, [key]: opened.layout },
|
||||
pinnedAgentIdsByWorkspace: {
|
||||
...stored.state.pinnedAgentIdsByWorkspace,
|
||||
[key]: [...(stored.state.pinnedAgentIdsByWorkspace?.[key] ?? []), input.agentId],
|
||||
},
|
||||
});
|
||||
await page.evaluate(
|
||||
(value) => localStorage.setItem("workspace-layout-state", value),
|
||||
JSON.stringify(stored),
|
||||
);
|
||||
}
|
||||
|
||||
/** Seed the reported hidden Explorer-only layout with 2,250 saved agent draft tabs. */
|
||||
export async function seedCorruptedWorkspaceLayout(page: Page, workspaceId: string): Promise<void> {
|
||||
const raw = await page.evaluate(() => localStorage.getItem("workspace-layout-state"));
|
||||
|
||||
@@ -271,6 +271,7 @@ export interface WorkspaceTabSnapshot {
|
||||
terminalsHydrated: boolean;
|
||||
activeAgentIds: Iterable<string>;
|
||||
autoOpenAgentIds: Iterable<string>;
|
||||
foreignAgentIds?: Iterable<string>;
|
||||
knownTerminalIds?: Iterable<string>;
|
||||
standaloneTerminalIds: Iterable<string>;
|
||||
hasActivePendingTerminalCreate?: boolean;
|
||||
@@ -2443,13 +2444,16 @@ export function reconcileWorkspaceTabs(
|
||||
const hiddenAgentIds = new Set(state.hiddenAgentIds ?? []);
|
||||
const activeAgentIds = normalizeStringSet(snapshot.activeAgentIds);
|
||||
const autoOpenAgentIds = normalizeStringSet(snapshot.autoOpenAgentIds);
|
||||
// An explicit open owns the tab until the agent joins the active directory.
|
||||
// From then on it follows the normal server archive lifecycle. Detail/cache
|
||||
// hydration never decides whether the user's target is allowed to stay open.
|
||||
const foreignAgentIds = normalizeStringSet(snapshot.foreignAgentIds ?? []);
|
||||
// Explicit opens survive absent directory entries, but not known foreign ownership.
|
||||
// Once an agent joins this workspace's active directory, normal archive rules apply.
|
||||
const pinnedAgentIds = new Set(
|
||||
[...(state.pinnedAgentIds ?? [])].filter(
|
||||
(agentId) => !snapshot.agentsHydrated || !activeAgentIds.has(agentId),
|
||||
),
|
||||
[...(state.pinnedAgentIds ?? [])].filter((agentId) => {
|
||||
if (!snapshot.agentsHydrated) {
|
||||
return true;
|
||||
}
|
||||
return !activeAgentIds.has(agentId) && !foreignAgentIds.has(agentId);
|
||||
}),
|
||||
);
|
||||
const standaloneTerminalIds = normalizeStringSet(snapshot.standaloneTerminalIds);
|
||||
const knownTerminalIds = snapshot.knownTerminalIds
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { useSessionStore, type Agent } from "@/stores/session-store";
|
||||
import { createWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
|
||||
import {
|
||||
buildWorkspaceTabSnapshot,
|
||||
createWorkspaceAgentVisibilitySelector,
|
||||
@@ -63,6 +64,118 @@ function makeAgent(input: {
|
||||
const WORKSPACE_ID = "ws-1";
|
||||
|
||||
describe("workspace agent visibility", () => {
|
||||
it("removes a restored pinned tab from an unrelated workspace once its owner is known", async () => {
|
||||
const values = new Map<string, string>();
|
||||
const storage = {
|
||||
getItem: async (key: string) => values.get(key) ?? null,
|
||||
setItem: async (key: string, value: string) => {
|
||||
values.set(key, value);
|
||||
},
|
||||
removeItem: async (key: string) => {
|
||||
values.delete(key);
|
||||
},
|
||||
};
|
||||
const original = createWorkspaceLayoutStore(undefined, storage);
|
||||
await original.persist.rehydrate();
|
||||
const agent = makeAgent({ id: "restored-agent", cwd: "/project-a", workspaceId: "ws-a" });
|
||||
for (const workspaceId of ["ws-a", "ws-b"]) {
|
||||
original.getState().openTab({
|
||||
workspaceKey: `srv:${workspaceId}`,
|
||||
target: { kind: "agent", agentId: agent.id },
|
||||
intent: "reveal",
|
||||
pin: true,
|
||||
});
|
||||
}
|
||||
const restored = createWorkspaceLayoutStore(undefined, storage);
|
||||
await restored.persist.rehydrate();
|
||||
const sessionAgents = new Map([[agent.id, agent]]);
|
||||
const beforeHydration = buildWorkspaceTabSnapshot({
|
||||
agentVisibility: deriveWorkspaceAgentVisibility({ sessionAgents, workspaceId: "ws-b" }),
|
||||
agentsHydrated: false,
|
||||
terminalsHydrated: true,
|
||||
knownTerminalIds: [],
|
||||
standaloneTerminalIds: [],
|
||||
hasActivePendingTerminalCreate: false,
|
||||
hasActivePendingDraftCreate: false,
|
||||
});
|
||||
restored.getState().reconcileTabs("srv:ws-b", beforeHydration);
|
||||
expect(restored.getState().pinnedAgentIdsByWorkspace["srv:ws-b"]).toEqual(new Set([agent.id]));
|
||||
for (const workspaceId of ["ws-a", "ws-b"]) {
|
||||
restored.getState().reconcileTabs(
|
||||
`srv:${workspaceId}`,
|
||||
buildWorkspaceTabSnapshot({
|
||||
agentVisibility: deriveWorkspaceAgentVisibility({ sessionAgents, workspaceId }),
|
||||
agentsHydrated: true,
|
||||
terminalsHydrated: true,
|
||||
knownTerminalIds: [],
|
||||
standaloneTerminalIds: [],
|
||||
hasActivePendingTerminalCreate: false,
|
||||
hasActivePendingDraftCreate: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
expect(
|
||||
restored
|
||||
.getState()
|
||||
.getWorkspaceTabs("srv:ws-a")
|
||||
.filter((tab) => tab.target.kind === "agent")
|
||||
.map((tab) => tab.target),
|
||||
).toEqual([{ kind: "agent", agentId: agent.id }]);
|
||||
expect(
|
||||
restored
|
||||
.getState()
|
||||
.getWorkspaceTabs("srv:ws-b")
|
||||
.filter((tab) => tab.target.kind === "agent"),
|
||||
).toEqual([]);
|
||||
expect(restored.getState().pinnedAgentIdsByWorkspace["srv:ws-b"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses directory ownership before historical details when identifying foreign agents", () => {
|
||||
const own = makeAgent({ id: "own", cwd: "/repo", workspaceId: WORKSPACE_ID });
|
||||
const foreign = makeAgent({ id: "foreign", cwd: "/repo", workspaceId: "ws-2" });
|
||||
const unknown = makeAgent({ id: "unknown", cwd: "/repo" });
|
||||
const result = deriveWorkspaceAgentVisibility({
|
||||
workspaceId: WORKSPACE_ID,
|
||||
sessionAgents: new Map([
|
||||
[own.id, own],
|
||||
[foreign.id, foreign],
|
||||
[unknown.id, unknown],
|
||||
]),
|
||||
agentDetails: new Map([
|
||||
[own.id, { ...own, workspaceId: "ws-2" }],
|
||||
[foreign.id, { ...foreign, workspaceId: WORKSPACE_ID }],
|
||||
[unknown.id, { ...unknown, workspaceId: "ws-2" }],
|
||||
]),
|
||||
});
|
||||
|
||||
expect(result.activeAgentIds).toEqual(new Set([own.id]));
|
||||
expect(result.foreignAgentIds).toEqual(new Set([foreign.id]));
|
||||
});
|
||||
|
||||
it("recognizes foreign historical ownership without making archived or unknown agents foreign", () => {
|
||||
const archived = makeAgent({
|
||||
id: "archived",
|
||||
cwd: "/repo",
|
||||
workspaceId: WORKSPACE_ID,
|
||||
archivedAt: new Date("2026-03-04T00:01:00.000Z"),
|
||||
});
|
||||
const foreign = { ...archived, id: "foreign", workspaceId: "ws-2" };
|
||||
const unknown = { ...archived, id: "unknown", workspaceId: undefined };
|
||||
const result = deriveWorkspaceAgentVisibility({
|
||||
workspaceId: WORKSPACE_ID,
|
||||
sessionAgents: new Map(),
|
||||
agentDetails: new Map([
|
||||
[archived.id, archived],
|
||||
[foreign.id, foreign],
|
||||
[unknown.id, unknown],
|
||||
]),
|
||||
});
|
||||
|
||||
expect(result.activeAgentIds).toEqual(new Set());
|
||||
expect(result.foreignAgentIds).toEqual(new Set([foreign.id]));
|
||||
});
|
||||
|
||||
it("keeps subagents active while excluding them from auto-open", () => {
|
||||
const parent = makeAgent({
|
||||
id: "parent-agent",
|
||||
@@ -86,6 +199,7 @@ describe("workspace agent visibility", () => {
|
||||
|
||||
expect(result.activeAgentIds).toEqual(new Set(["parent-agent", "child-agent"]));
|
||||
expect(result.autoOpenAgentIds).toEqual(new Set(["parent-agent"]));
|
||||
expect(result.foreignAgentIds).toEqual(new Set());
|
||||
});
|
||||
|
||||
it("excludes archived subagents from active and auto-open", () => {
|
||||
@@ -295,6 +409,7 @@ describe("workspace agent visibility", () => {
|
||||
});
|
||||
|
||||
expect(result.activeAgentIds).toEqual(new Set<string>());
|
||||
expect(result.foreignAgentIds).toEqual(new Set(["other-ws-agent"]));
|
||||
});
|
||||
|
||||
it("excludes agents without a workspaceId", () => {
|
||||
@@ -308,12 +423,14 @@ describe("workspace agent visibility", () => {
|
||||
});
|
||||
|
||||
expect(result.activeAgentIds).toEqual(new Set<string>());
|
||||
expect(result.foreignAgentIds).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"]),
|
||||
foreignAgentIds: new Set(["foreign-agent"]),
|
||||
};
|
||||
|
||||
expect(
|
||||
@@ -331,6 +448,7 @@ describe("workspace agent visibility", () => {
|
||||
terminalsHydrated: true,
|
||||
activeAgentIds: agentVisibility.activeAgentIds,
|
||||
autoOpenAgentIds: agentVisibility.autoOpenAgentIds,
|
||||
foreignAgentIds: agentVisibility.foreignAgentIds,
|
||||
knownTerminalIds: ["terminal-1", "script-terminal"],
|
||||
standaloneTerminalIds: ["terminal-1"],
|
||||
hasActivePendingTerminalCreate: false,
|
||||
@@ -469,6 +587,24 @@ describe("workspace agent visibility", () => {
|
||||
expect(after.activeAgentIds).toEqual(new Set([child.id]));
|
||||
expect(after.autoOpenAgentIds).toEqual(new Set([child.id]));
|
||||
});
|
||||
|
||||
it("recomputes when only a previously unknown agent's foreign ownership becomes known", () => {
|
||||
seedSession(new Map([[parent.id, parent]]));
|
||||
const select = createWorkspaceAgentVisibilitySelector({
|
||||
serverId: SERVER_ID,
|
||||
workspaceId: WORKSPACE_ID,
|
||||
});
|
||||
const before = select(useSessionStore.getState());
|
||||
useSessionStore
|
||||
.getState()
|
||||
.setAgentDetails(SERVER_ID, new Map([[otherWorkspace.id, otherWorkspace]]));
|
||||
const after = select(useSessionStore.getState());
|
||||
|
||||
expect(after).not.toBe(before);
|
||||
expect(after.activeAgentIds).toEqual(before.activeAgentIds);
|
||||
expect(after.autoOpenAgentIds).toEqual(before.autoOpenAgentIds);
|
||||
expect(after.foreignAgentIds).toEqual(new Set([otherWorkspace.id]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("workspaceAgentVisibilityEqual", () => {
|
||||
@@ -476,10 +612,12 @@ describe("workspace agent visibility", () => {
|
||||
const a = {
|
||||
activeAgentIds: new Set(["a", "b"]),
|
||||
autoOpenAgentIds: new Set(["a"]),
|
||||
foreignAgentIds: new Set(["c"]),
|
||||
};
|
||||
const b = {
|
||||
activeAgentIds: new Set(["a", "b"]),
|
||||
autoOpenAgentIds: new Set(["a"]),
|
||||
foreignAgentIds: new Set(["c"]),
|
||||
};
|
||||
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
|
||||
});
|
||||
@@ -488,10 +626,12 @@ describe("workspace agent visibility", () => {
|
||||
const a = {
|
||||
activeAgentIds: new Set(["a"]),
|
||||
autoOpenAgentIds: new Set(["a"]),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
const b = {
|
||||
activeAgentIds: new Set(["b"]),
|
||||
autoOpenAgentIds: new Set(["a"]),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
|
||||
});
|
||||
@@ -500,10 +640,12 @@ describe("workspace agent visibility", () => {
|
||||
const a = {
|
||||
activeAgentIds: new Set(["a", "b"]),
|
||||
autoOpenAgentIds: new Set(["a"]),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
const b = {
|
||||
activeAgentIds: new Set(["a", "b"]),
|
||||
autoOpenAgentIds: new Set(["b"]),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
expect(workspaceAgentVisibilityEqual(a, b)).toBe(false);
|
||||
});
|
||||
@@ -512,10 +654,12 @@ describe("workspace agent visibility", () => {
|
||||
const a = {
|
||||
activeAgentIds: new Set<string>(),
|
||||
autoOpenAgentIds: new Set<string>(),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
const b = {
|
||||
activeAgentIds: new Set<string>(),
|
||||
autoOpenAgentIds: new Set<string>(),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
expect(workspaceAgentVisibilityEqual(a, b)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -6,10 +6,7 @@ import { normalizeWorkspaceOpaqueId } from "@/utils/workspace-identity";
|
||||
export interface WorkspaceAgentVisibility {
|
||||
activeAgentIds: Set<string>;
|
||||
autoOpenAgentIds: Set<string>;
|
||||
}
|
||||
|
||||
function agentBelongsToWorkspace(agent: Agent, workspaceId: string): boolean {
|
||||
return normalizeWorkspaceOpaqueId(agent.workspaceId) === workspaceId;
|
||||
foreignAgentIds: Set<string>;
|
||||
}
|
||||
|
||||
export function deriveWorkspaceAgentVisibility(input: {
|
||||
@@ -19,17 +16,23 @@ export function deriveWorkspaceAgentVisibility(input: {
|
||||
}): WorkspaceAgentVisibility {
|
||||
const { sessionAgents, agentDetails } = input;
|
||||
const workspaceId = normalizeWorkspaceOpaqueId(input.workspaceId);
|
||||
if ((!sessionAgents && !agentDetails) || !workspaceId) {
|
||||
if (!workspaceId) {
|
||||
return {
|
||||
activeAgentIds: new Set<string>(),
|
||||
autoOpenAgentIds: new Set<string>(),
|
||||
foreignAgentIds: new Set<string>(),
|
||||
};
|
||||
}
|
||||
|
||||
const activeAgentIds = new Set<string>();
|
||||
const autoOpenAgentIds = new Set<string>();
|
||||
const foreignAgentIds = new Set<string>();
|
||||
for (const agent of sessionAgents?.values() ?? []) {
|
||||
if (!agentBelongsToWorkspace(agent, workspaceId)) {
|
||||
const ownerId = normalizeWorkspaceOpaqueId(agent.workspaceId);
|
||||
if (ownerId !== workspaceId) {
|
||||
if (ownerId) {
|
||||
foreignAgentIds.add(agent.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!agent.archivedAt) {
|
||||
@@ -42,7 +45,16 @@ export function deriveWorkspaceAgentVisibility(input: {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { activeAgentIds, autoOpenAgentIds };
|
||||
for (const agent of agentDetails?.values() ?? []) {
|
||||
if (sessionAgents?.has(agent.id)) {
|
||||
continue;
|
||||
}
|
||||
const ownerId = normalizeWorkspaceOpaqueId(agent.workspaceId);
|
||||
if (ownerId && ownerId !== workspaceId) {
|
||||
foreignAgentIds.add(agent.id);
|
||||
}
|
||||
}
|
||||
return { activeAgentIds, autoOpenAgentIds, foreignAgentIds };
|
||||
}
|
||||
|
||||
interface AgentMapsSnapshot {
|
||||
@@ -99,6 +111,7 @@ export function buildWorkspaceTabSnapshot(input: {
|
||||
terminalsHydrated: input.terminalsHydrated,
|
||||
activeAgentIds: input.agentVisibility.activeAgentIds,
|
||||
autoOpenAgentIds: input.agentVisibility.autoOpenAgentIds,
|
||||
foreignAgentIds: input.agentVisibility.foreignAgentIds,
|
||||
knownTerminalIds: input.knownTerminalIds,
|
||||
standaloneTerminalIds: input.standaloneTerminalIds,
|
||||
hasActivePendingTerminalCreate: input.hasActivePendingTerminalCreate,
|
||||
@@ -113,7 +126,8 @@ export function workspaceAgentVisibilityEqual(
|
||||
return (
|
||||
a === b ||
|
||||
(setsEqual(a.activeAgentIds, b.activeAgentIds) &&
|
||||
setsEqual(a.autoOpenAgentIds, b.autoOpenAgentIds))
|
||||
setsEqual(a.autoOpenAgentIds, b.autoOpenAgentIds) &&
|
||||
setsEqual(a.foreignAgentIds, b.foreignAgentIds))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user