From 9457bb6f4053b6ab36e7eabcae0a2902176635c8 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 9 Sep 2026 18:54:56 +0200 Subject: [PATCH] fix(app): preserve submitted draft ownership during workspace creation --- .../new-workspace-navigation-guard.spec.ts | 228 ++---------------- .../helpers/new-workspace-navigation.ts | 197 +++++++++++++++ .../app/e2e/support/helpers/new-workspace.ts | 6 + .../app/src/screens/new-workspace-screen.tsx | 17 +- .../new-workspace/background-handoff.test.ts | 15 +- .../new-workspace/background-handoff.ts | 5 +- 6 files changed, 244 insertions(+), 224 deletions(-) create mode 100644 packages/app/e2e/support/helpers/new-workspace-navigation.ts diff --git a/packages/app/e2e/browser/new-workspace-navigation-guard.spec.ts b/packages/app/e2e/browser/new-workspace-navigation-guard.spec.ts index 02079f2a5d..69becc94ab 100644 --- a/packages/app/e2e/browser/new-workspace-navigation-guard.spec.ts +++ b/packages/app/e2e/browser/new-workspace-navigation-guard.spec.ts @@ -1,216 +1,22 @@ -import { buildHostWorkspaceRoute } from "@/utils/host-routes"; -import { expect, test } from "../support/fixtures"; -import { gotoAppShell } from "../support/helpers/app"; -import { - archiveLocalWorkspaceFromDaemon, - archiveWorkspaceFromDaemon, - connectNewWorkspaceDaemonClient, - countWorkspaceAgents, - countWorkspaceTerminals, - delayBrowserWorkspaceCreatedResponse, - openNewWorkspaceComposer, - openProjectViaDaemon, - waitForCreatedWorkspace, -} from "../support/helpers/new-workspace"; -import { createTempGitRepo } from "../support/helpers/workspace"; -import { expectAppRoute } from "../support/helpers/route-assertions"; -import { getServerId } from "../support/helpers/server-id"; -import { - expectTerminalOutputContains, - expectWorkspaceOpensWithTerminalTab, - fillTerminalPrompt, - seedTerminalProfiles, - selectLaunchOption, - submitTerminalLaunch, - type TerminalProfile, -} from "../support/helpers/new-workspace-launch"; -import { - expectWorkspaceHeader, - switchWorkspaceViaSidebar, - waitForSidebarHydration, -} from "../support/helpers/workspace-ui"; +import { test } from "../support/fixtures"; +import { verifyDelayedWorkspaceCreation } from "../support/helpers/new-workspace-navigation"; -const PROMPT = "Hello from the navigation guard"; +test.describe("Delayed workspace creation", () => { + test.describe.configure({ timeout: 120_000 }); -const NAVIGATION_SETTLE_MS = 5_000; + for (const launch of ["chat", "terminal", "empty"] as const) { + test(`${launch}: keeps the workspace chosen during creation`, async ({ page }) => { + await verifyDelayedWorkspaceCreation(page, launch, "leave"); + }); + test(`worktree ${launch}: keeps the workspace chosen during creation`, async ({ page }) => { + await verifyDelayedWorkspaceCreation(page, launch, "leave", "worktree"); + }); + test(`${launch}: opens the created workspace when the user stays`, async ({ page }) => { + await verifyDelayedWorkspaceCreation(page, launch, "stay"); + }); + } -// The prompt is substituted in as `$0`, so it reaches the process exactly as typed. `sleep` holds -// the terminal open past the daemon polling this test does before it attaches — a process that has -// already exited has no output left to render. -const TERMINAL_PROFILE: TerminalProfile = { - id: "e2e-nav-guard-echo", - name: "Nav Guard Echo", - command: "/bin/sh", - args: ["-c", 'echo captured: "$0"; sleep 120', "{{{prompt}}}"], -}; - -test.describe("New workspace navigation guard", () => { - let client: Awaited>; - const localWorkspaceIds = new Set(); - const createdWorktreeDirectories = new Set(); - - test.describe.configure({ timeout: 240_000 }); - - test.beforeEach(async () => { - client = await connectNewWorkspaceDaemonClient(); - }); - - test.afterEach(async () => { - if (client) { - for (const workspaceDirectory of createdWorktreeDirectories) { - await archiveWorkspaceFromDaemon(client, workspaceDirectory).catch(() => undefined); - } - for (const workspaceId of localWorkspaceIds) { - await archiveLocalWorkspaceFromDaemon(client, workspaceId).catch(() => undefined); - } - } - createdWorktreeDirectories.clear(); - localWorkspaceIds.clear(); - await client?.close().catch(() => undefined); - }); - - test("stays put when creation resolves after the user moved on, and still starts the agent", async ({ - page, - }) => { - const serverId = getServerId(); - const tempRepo = await createTempGitRepo("new-workspace-nav-guard-"); - const createDelay = await delayBrowserWorkspaceCreatedResponse(page); - - try { - const openedProject = await openProjectViaDaemon(client, tempRepo.path); - localWorkspaceIds.add(openedProject.workspaceId); - const knownWorkspaceIds = new Set( - (await client.fetchWorkspaces()).entries.map((entry) => entry.id), - ); - - await gotoAppShell(page); - await waitForSidebarHydration(page); - await switchWorkspaceViaSidebar({ - page, - serverId, - workspaceId: openedProject.workspaceId, - }); - await expectWorkspaceHeader(page, { - title: openedProject.workspaceName, - subtitle: openedProject.projectDisplayName, - }); - - await openNewWorkspaceComposer(page, { - projectKey: openedProject.projectKey, - projectDisplayName: openedProject.projectDisplayName, - }); - - const composer = page.getByRole("textbox", { name: "Message agent..." }); - await expect(composer).toBeVisible({ timeout: 30_000 }); - await composer.fill(PROMPT); - await page.getByTestId("message-input-root").getByRole("button", { name: "Create" }).click(); - - // The daemon is still running `git worktree add`; leave before it answers. - await createDelay.waitForCreateRequest(); - const originalRoute = buildHostWorkspaceRoute(serverId, openedProject.workspaceId); - await switchWorkspaceViaSidebar({ - page, - serverId, - workspaceId: openedProject.workspaceId, - }); - createDelay.release(); - - // Wait for the agent the background path is responsible for creating, then prove the app - // never followed it. Polling the daemon rather than the URL keeps this ordering explicit. - const createdWorkspace = await waitForCreatedWorkspace(client, knownWorkspaceIds); - createdWorktreeDirectories.add(createdWorkspace.workspaceDirectory); - - await expect - .poll(() => countWorkspaceAgents(client, createdWorkspace.id), { timeout: 60_000 }) - .toBe(1); - await expectAppRoute(page, originalRoute); - - // Opening the workspace shows one real agent tab — not an orphaned draft tab, which would - // create a second agent on mount. - await switchWorkspaceViaSidebar({ page, serverId, workspaceId: createdWorkspace.id }); - const deckEntry = page - .getByTestId(`workspace-deck-entry-${serverId}:${createdWorkspace.id}`) - .filter({ visible: true }); - await expect(deckEntry.locator('[data-testid^="workspace-tab-agent_"]')).toHaveCount(1, { - timeout: 30_000, - }); - await expect(deckEntry.locator('[data-testid^="workspace-tab-draft_"]')).toHaveCount(0); - await expect(deckEntry.getByText(PROMPT).first()).toBeVisible({ timeout: 30_000 }); - expect(await countWorkspaceAgents(client, createdWorkspace.id)).toBe(1); - } finally { - createDelay.release(); - await tempRepo.cleanup(); - } - }); - - test("stays put when a terminal launch resolves after the user moved on, and still spawns the terminal", async ({ - page, - }) => { - const serverId = getServerId(); - const tempRepo = await createTempGitRepo("new-workspace-nav-guard-terminal-"); - const profileSeed = await seedTerminalProfiles([TERMINAL_PROFILE]); - const createDelay = await delayBrowserWorkspaceCreatedResponse(page); - - try { - const openedProject = await openProjectViaDaemon(client, tempRepo.path); - localWorkspaceIds.add(openedProject.workspaceId); - const knownWorkspaceIds = new Set( - (await client.fetchWorkspaces()).entries.map((entry) => entry.id), - ); - - await gotoAppShell(page); - await waitForSidebarHydration(page); - await switchWorkspaceViaSidebar({ - page, - serverId, - workspaceId: openedProject.workspaceId, - }); - await expectWorkspaceHeader(page, { - title: openedProject.workspaceName, - subtitle: openedProject.projectDisplayName, - }); - - await openNewWorkspaceComposer(page, { - projectKey: openedProject.projectKey, - projectDisplayName: openedProject.projectDisplayName, - }); - await selectLaunchOption(page, TERMINAL_PROFILE.id); - await fillTerminalPrompt(page, PROMPT); - await submitTerminalLaunch(page); - - await createDelay.waitForCreateRequest(); - const originalRoute = buildHostWorkspaceRoute(serverId, openedProject.workspaceId); - await switchWorkspaceViaSidebar({ - page, - serverId, - workspaceId: openedProject.workspaceId, - }); - createDelay.release(); - - const createdWorkspace = await waitForCreatedWorkspace(client, knownWorkspaceIds); - createdWorktreeDirectories.add(createdWorkspace.workspaceDirectory); - - // Wait for the terminal, not just the workspace. The launch path spawns the terminal and - // then navigates, so asserting the route on the workspace alone would race ahead of the - // navigation this test exists to catch and pass either way. - await expect - .poll(() => countWorkspaceTerminals(client, createdWorkspace.id), { timeout: 60_000 }) - .toBe(1); - // "Nothing navigated" is a negative, so it needs a window rather than a single poll, which - // would pass on its first sample. The daemon lists the terminal before the browser has its - // create response back, and the unguarded code navigates a couple of seconds after that. - await page.waitForTimeout(NAVIGATION_SETTLE_MS); - await expectAppRoute(page, originalRoute); - - // The terminal spawned with the prompt even though nothing navigated, and its tab opens on - // its own when the workspace is finally visited. - await switchWorkspaceViaSidebar({ page, serverId, workspaceId: createdWorkspace.id }); - await expectWorkspaceOpensWithTerminalTab(page); - await expectTerminalOutputContains(page, `captured: ${PROMPT}`); - } finally { - createDelay.release(); - await profileSeed.restore(); - await tempRepo.cleanup(); - } + test("preserves a newer draft opened while creation is pending", async ({ page }) => { + await verifyDelayedWorkspaceCreation(page, "chat", "new-draft"); }); }); diff --git a/packages/app/e2e/support/helpers/new-workspace-navigation.ts b/packages/app/e2e/support/helpers/new-workspace-navigation.ts new file mode 100644 index 0000000000..6e6220c581 --- /dev/null +++ b/packages/app/e2e/support/helpers/new-workspace-navigation.ts @@ -0,0 +1,197 @@ +import { expect, test, type Page } from "../fixtures"; +import { readFile } from "node:fs/promises"; +import { buildHostWorkspaceRoute } from "@/utils/host-routes"; +import { gotoAppShell } from "./app"; +import { scrollTimelineToOldestLoadedEdge } from "./timeline-pagination"; +import { + archiveLocalWorkspaceFromDaemon, + archiveWorkspaceFromDaemon, + connectNewWorkspaceDaemonClient, + countWorkspaceAgents, + countWorkspaceTerminals, + delayBrowserWorkspaceCreatedResponse, + expectNewWorkspaceDraft, + fillNewWorkspaceDraft, + openNewWorkspaceComposer, + openProjectViaDaemon, + selectWorkspaceIsolation, + submitNewWorkspaceEmpty, + submitNewWorkspacePrompt, + waitForCreatedWorkspace, +} from "./new-workspace"; +import { createTempGitRepo } from "./workspace"; +import { expectAppRoute } from "./route-assertions"; +import { getServerId } from "./server-id"; +import { + expectTerminalOutputContains, + expectWorkspaceOpensWithTerminalTab, + fillTerminalPrompt, + seedTerminalProfiles, + selectLaunchOption, + submitTerminalLaunch, + type TerminalProfile, +} from "./new-workspace-launch"; +import { switchWorkspaceViaSidebar, waitForSidebarHydration } from "./workspace-ui"; +import { dropFileOnComposer, expectAttachmentPill } from "./composer"; + +const PROMPT = "Hello from the navigation guard"; +const NEXT_PROMPT = "Keep this newer draft"; +const CONTEXT = { + name: "context.json", + mimeType: "application/json", + buffer: Buffer.from('{"preserve":"attachment"}'), +}; +const PROFILE: TerminalProfile = { + id: "e2e-nav-guard-echo", + name: "Nav Guard Echo", + command: "/bin/sh", + args: ["-c", 'echo captured: "$0"; sleep 120', "{{{prompt}}}"], +}; + +export async function verifyDelayedWorkspaceCreation( + page: Page, + launch: "chat" | "terminal" | "empty", + destination: "leave" | "stay" | "new-draft", + isolation: "local" | "worktree" = "local", +): Promise { + const client = await connectNewWorkspaceDaemonClient(); + const repo = await createTempGitRepo("workspace-focus-"); + const profileSeed = await seedTerminalProfiles([PROFILE]); + const delay = await delayBrowserWorkspaceCreatedResponse(page); + const serverId = getServerId(); + let localWorkspaceId: string | undefined; + let createdDirectory: string | undefined; + let newDraftRoute = ""; + + try { + const project = await openProjectViaDaemon(client, repo.path); + localWorkspaceId = project.workspaceId; + const knownIds = new Set((await client.fetchWorkspaces()).entries.map((entry) => entry.id)); + await gotoAppShell(page); + await waitForSidebarHydration(page); + await switchWorkspaceViaSidebar({ page, serverId, workspaceId: project.workspaceId }); + + await test.step("Submit workspace creation with the requested content", async () => { + await openNewWorkspaceComposer(page, project); + await selectWorkspaceIsolation(page, isolation); + if (launch === "chat") { + await dropFileOnComposer(page, CONTEXT); + await expectAttachmentPill(page, "composer-file-attachment-pill"); + await submitNewWorkspacePrompt(page, PROMPT); + } else if (launch === "terminal") { + await selectLaunchOption(page, PROFILE.id); + await fillTerminalPrompt(page, PROMPT); + await submitTerminalLaunch(page); + } else { + await submitNewWorkspaceEmpty(page); + } + await delay.waitForCreateRequest(); + }); + + await test.step("Choose where to work while the creation response is held", async () => { + if (destination !== "stay") { + await switchWorkspaceViaSidebar({ page, serverId, workspaceId: project.workspaceId }); + } + if (destination === "new-draft") { + await openNewWorkspaceComposer(page, project); + await fillNewWorkspaceDraft(page, NEXT_PROMPT); + const url = new URL(page.url()); + newDraftRoute = url.pathname + url.search; + } + // Recording pacing only; the response is held deterministically above. + if (process.env.E2E_RECORD_VIDEO === "1") { + await page.mouse.move(1100, 80); + await page.waitForTimeout(1500); + } + delay.release(); + }); + + const created = await waitForCreatedWorkspace(client, knownIds); + createdDirectory = created.workspaceDirectory; + expect(created.workspaceKind).toBe(isolation === "worktree" ? "worktree" : "local_checkout"); + await test.step("Completion creates exactly the requested resource and preserves focus", async () => { + await expect + .poll(() => countWorkspaceAgents(client, created.id)) + .toBe(launch === "chat" ? 1 : 0); + await expect + .poll(() => countWorkspaceTerminals(client, created.id)) + .toBe(launch === "terminal" ? 1 : 0); + // A negative navigation assertion needs an observation window after completion. + await page.waitForTimeout(5000); + const expectedRoute = + destination === "new-draft" + ? newDraftRoute + : buildHostWorkspaceRoute( + serverId, + destination === "stay" ? created.id : project.workspaceId, + ); + await expectAppRoute(page, expectedRoute); + if (destination === "new-draft") await expectNewWorkspaceDraft(page, NEXT_PROMPT); + if (launch === "chat") { + expect(delay.agentRequests).toHaveLength(1); + const upload = delay.agentRequests[0]?.attachments?.find( + (item) => item.type === "uploaded_file", + ); + if (!upload) throw new Error("The create request did not contain the submitted file"); + expect(await readFile(upload.path)).toEqual(CONTEXT.buffer); + expect(delay.agentRequests[0]).toMatchObject({ + workspaceId: created.id, + initialPrompt: PROMPT, + config: { + provider: "mock", + model: "ten-second-stream", + modeId: "load-test", + thinkingOptionId: "low", + cwd: created.workspaceDirectory, + }, + attachments: [ + expect.objectContaining({ + fileName: CONTEXT.name, + mimeType: CONTEXT.mimeType, + size: CONTEXT.buffer.length, + }), + ], + }); + } + }); + + await test.step("Visit the new workspace and return without submitting a second draft", async () => { + await switchWorkspaceViaSidebar({ page, serverId, workspaceId: created.id }); + if (launch === "chat") { + await scrollTimelineToOldestLoadedEdge(page); + await expect(page.getByText(PROMPT, { exact: true }).first()).toBeVisible(); + } else if (launch === "terminal") { + await expectWorkspaceOpensWithTerminalTab(page); + await expectTerminalOutputContains(page, `captured: ${PROMPT}`); + } + await switchWorkspaceViaSidebar({ page, serverId, workspaceId: project.workspaceId }); + await switchWorkspaceViaSidebar({ page, serverId, workspaceId: created.id }); + await expect + .poll(() => countWorkspaceAgents(client, created.id)) + .toBe(launch === "chat" ? 1 : 0); + await expect + .poll(() => countWorkspaceTerminals(client, created.id)) + .toBe(launch === "terminal" ? 1 : 0); + if (launch === "chat") { + expect(delay.agentRequests).toHaveLength(1); + await openNewWorkspaceComposer(page, project); + await expectNewWorkspaceDraft(page, destination === "new-draft" ? NEXT_PROMPT : ""); + } + if (process.env.E2E_RECORD_VIDEO === "1") { + await page.mouse.move(1100, 80); + await page.waitForTimeout(1500); + } + }); + } catch (error) { + await page.screenshot({ path: test.info().outputPath("before-cleanup.png") }); + console.error(error); + throw error; + } finally { + delay.release(); + if (createdDirectory) await archiveWorkspaceFromDaemon(client, createdDirectory); + if (localWorkspaceId) await archiveLocalWorkspaceFromDaemon(client, localWorkspaceId); + await profileSeed.restore(); + await client.close(); + await repo.cleanup(); + } +} diff --git a/packages/app/e2e/support/helpers/new-workspace.ts b/packages/app/e2e/support/helpers/new-workspace.ts index 0b453fb554..5cebc309b1 100644 --- a/packages/app/e2e/support/helpers/new-workspace.ts +++ b/packages/app/e2e/support/helpers/new-workspace.ts @@ -1,4 +1,5 @@ import { expect, type BrowserContext, type Page } from "@playwright/test"; +import type { CreateAgentRequestMessage } from "@getpaseo/protocol/messages"; import type { DaemonClient as InternalDaemonClient } from "@getpaseo/client/internal/daemon-client"; import { decodeWorkspaceIdFromPathSegment } from "@/utils/host-routes"; import { connectDaemonClient } from "./daemon-client-loader"; @@ -626,6 +627,7 @@ export async function delayBrowserAgentCreatedStatus( } export interface WorkspaceCreatedDelayControl { + agentRequests: readonly CreateAgentRequestMessage[]; release(): void; waitForCreateRequest(): Promise; } @@ -638,6 +640,7 @@ export async function delayBrowserWorkspaceCreatedResponse( page: Page, ): Promise { const daemonPortPattern = daemonWsRoutePattern(); + const agentRequests: CreateAgentRequestMessage[] = []; const createRequestIds = new Set(); const delayedForwards: Array<() => void> = []; let releaseRequested = false; @@ -651,6 +654,8 @@ export async function delayBrowserWorkspaceCreatedResponse( ws.onMessage((message) => { const sessionMessage = getSessionMessage(message); + if (sessionMessage?.type === "create_agent_request") + agentRequests.push(sessionMessage as CreateAgentRequestMessage); if (sessionMessage?.type === "workspace.create.request") { const requestId = getStringField(sessionMessage, "requestId"); if (requestId) { @@ -680,6 +685,7 @@ export async function delayBrowserWorkspaceCreatedResponse( }); return { + agentRequests, release() { releaseRequested = true; for (const forward of delayedForwards.splice(0)) { diff --git a/packages/app/src/screens/new-workspace-screen.tsx b/packages/app/src/screens/new-workspace-screen.tsx index e06e77bfab..d9413359e9 100644 --- a/packages/app/src/screens/new-workspace-screen.tsx +++ b/packages/app/src/screens/new-workspace-screen.tsx @@ -57,6 +57,7 @@ import { import { normalizeWorkspaceDescriptor, type WorkspaceDescriptor } from "@/stores/session-store"; import { useWorkspace } from "@/stores/session-store-hooks"; import { buildNewWorkspaceDraftKey, generateDraftId } from "@/stores/draft-keys"; +import { useDraftStore } from "@/stores/draft-store"; import { useOpenAddProject } from "@/hooks/use-open-add-project"; import { isActiveCreateFlowForDraft, useCreateFlowStore } from "@/stores/create-flow-store"; import { @@ -761,6 +762,7 @@ function normalizeBranchDetails( type SubmitOutcome = "navigated" | "background"; interface SubmitDraftInput { + draftVersionAtSubmit: number | undefined; serverId: string; draftKey: string; clearDraft: (lifecycle: "sent" | "abandoned") => void; @@ -941,6 +943,7 @@ function buildComposerInitialValues(input: { async function runCreateChatAgent(input: CreateChatAgentInput): Promise { const { payload, composerState, ensureWorkspace, serverId, clearDraft } = input; + const draftVersionAtSubmit = useDraftStore.getState().drafts[input.draftKey]?.version; const { text, attachments, cwd } = payload; if (!composerState) { throw new Error(input.labels.composerStateRequired); @@ -969,6 +972,7 @@ async function runCreateChatAgent(input: CreateChatAgentInput): Promise { - const connection = connectionRef.current; - if (!connection.client || !connection.isConnected) { + const connectedClient = getHostRuntimeStore().getClient(selectedServerId); + if (!connectedClient?.isConnected) { throw new Error(t("newWorkspace.errors.hostDisconnected")); } - return connection.client; - }, [t]); + return connectedClient; + }, [selectedServerId, t]); const clientReady = isConnected && Boolean(client); const hasSelectedSourceDirectory = selectedSourceDirectory !== null; diff --git a/packages/app/src/screens/new-workspace/background-handoff.test.ts b/packages/app/src/screens/new-workspace/background-handoff.test.ts index 953036c88a..8f2ebb7936 100644 --- a/packages/app/src/screens/new-workspace/background-handoff.test.ts +++ b/packages/app/src/screens/new-workspace/background-handoff.test.ts @@ -19,11 +19,16 @@ function draftText(): string | undefined { return useDraftStore.getState().drafts[DRAFT_KEY]?.input.text; } -function run(createAgent: () => Promise) { +function run( + createAgent: () => Promise, + draftVersionAtSubmit = useDraftStore.getState().drafts[DRAFT_KEY]?.version, +) { return createWorkspaceAgentInBackground({ draftId: DRAFT_ID, draftKey: DRAFT_KEY, - clearDraft: (lifecycle) => useDraftStore.getState().clearDraftInput({ draftKey: DRAFT_KEY, lifecycle }), + draftVersionAtSubmit, + clearDraft: (lifecycle) => + useDraftStore.getState().clearDraftInput({ draftKey: DRAFT_KEY, lifecycle }), draftContextScopeKey: SCOPE_KEY, createAgent, }); @@ -87,4 +92,10 @@ describe("createWorkspaceAgentInBackground", () => { expect(draftText()).toBe("a different idea"); }); + it("keeps a newer draft written while workspace creation was pending", async () => { + const submittedVersion = useDraftStore.getState().drafts[DRAFT_KEY]?.version; + saveDraft("a newer workspace idea"); + await run(async () => undefined, submittedVersion); + expect(draftText()).toBe("a newer workspace idea"); + }); }); diff --git a/packages/app/src/screens/new-workspace/background-handoff.ts b/packages/app/src/screens/new-workspace/background-handoff.ts index 33e99c156f..606fd7915a 100644 --- a/packages/app/src/screens/new-workspace/background-handoff.ts +++ b/packages/app/src/screens/new-workspace/background-handoff.ts @@ -5,6 +5,7 @@ import { useWorkspaceDraftSubmissionStore } from "@/stores/workspace-draft-submi export interface CreateWorkspaceAgentInBackgroundInput { draftId: string; draftKey: string; + draftVersionAtSubmit: number | undefined; clearDraft: (lifecycle: "sent" | "abandoned") => void; draftContextScopeKey: string | null; createAgent: () => Promise; @@ -23,8 +24,6 @@ export interface CreateWorkspaceAgentInBackgroundInput { export async function createWorkspaceAgentInBackground( input: CreateWorkspaceAgentInBackgroundInput, ): Promise { - const draftVersionAtSubmit = useDraftStore.getState().drafts[input.draftKey]?.version ?? null; - await input.createAgent(); useWorkspaceDraftSubmissionStore.getState().clearDraftSetup({ draftId: input.draftId }); @@ -37,7 +36,7 @@ export async function createWorkspaceAgentInBackground( // clearing would destroy the text needed to retry. And only if nothing else has written to the // draft since: the New workspace draft key is shared, so the user may have reopened the screen // and started typing something new while this was in flight. - if (useDraftStore.getState().drafts[input.draftKey]?.version === draftVersionAtSubmit) { + if (useDraftStore.getState().drafts[input.draftKey]?.version === input.draftVersionAtSubmit) { input.clearDraft("sent"); } }