Key workspace status and ownership by ID, not directory

Multiple workspaces can share one checkout directory. Agent and terminal
status, sidebar tabs, and ownership now attribute strictly by workspace ID,
so a running or blocked agent in one workspace no longer shows up on its
same-directory siblings.

A one-time startup migration backfills workspace IDs onto legacy agents, and
every agent and terminal is created with one thereafter. Directory lookups
remain only for genuinely folder-derived git facts (branch, diff) and the
archive-by-path adapter.
This commit is contained in:
Mohamed Boudra
2026-06-16 13:37:58 +07:00
parent ba9f7740a8
commit 9966e49329
102 changed files with 3222 additions and 1932 deletions
@@ -1,10 +1,10 @@
import { randomUUID } from "node:crypto";
import { test, expect, type Page } from "./fixtures";
import { seedWorkspace } from "./helpers/seed-client";
import { seedWorkspace, type SeedDaemonClient } from "./helpers/seed-client";
import { createIdleAgent, expectWorkspaceTabVisible } from "./helpers/archive-tab";
import { waitForWorkspaceTabsVisible } from "./helpers/workspace-tabs";
import { buildHostAgentDetailRoute } from "@/utils/host-routes";
import { captureWsSessionFrames, renameModalInput, renameModalSubmit } from "./helpers/rename";
import { renameModalInput, renameModalSubmit } from "./helpers/rename";
import { getServerId } from "./helpers/server-id";
async function openAgentInWorkspace(page: Page, agent: { id: string; workspaceId: string }) {
@@ -17,8 +17,13 @@ async function openAgentInWorkspace(page: Page, agent: { id: string; workspaceId
await expectWorkspaceTabVisible(page, agent.id);
}
async function fetchAgentTitle(client: SeedDaemonClient, agentId: string): Promise<string | null> {
const result = await client.fetchAgents({ scope: "active" });
return result.entries.find((entry) => entry.agent.id === agentId)?.agent.title ?? null;
}
test.describe("Workspace agent tab rename", () => {
test("right-click rename sends update_agent_request and updates the tab label", async ({
test("right-click rename persists the agent title and updates the tab label", async ({
page,
}) => {
test.setTimeout(120_000);
@@ -32,12 +37,6 @@ test.describe("Workspace agent tab rename", () => {
title: initialTitle,
});
const updateFrames = captureWsSessionFrames(page, "update_agent_request", (inner) => ({
agentId: String(inner.agentId ?? ""),
name: String(inner.name ?? ""),
requestId: String(inner.requestId ?? ""),
}));
await openAgentInWorkspace(page, agent);
const tab = page.getByTestId(`workspace-tab-agent_${agent.id}`).first();
@@ -62,12 +61,7 @@ test.describe("Workspace agent tab rename", () => {
await expect(input).toHaveCount(0, { timeout: 15_000 });
await expect(tab).toContainText(renamed, { timeout: 15_000 });
expect(updateFrames.length).toBeGreaterThan(0);
const lastFrame = updateFrames.at(-1)!;
expect(lastFrame.agentId).toBe(agent.id);
expect(lastFrame.name).toBe(renamed);
expect(lastFrame.requestId.length).toBeGreaterThan(0);
await expect.poll(() => fetchAgentTitle(workspace.client, agent.id)).toBe(renamed);
} finally {
await workspace.cleanup();
}