Update workspace model ownership flows

This commit is contained in:
Mohamed Boudra
2026-06-15 22:02:18 +07:00
parent af0437e6df
commit a4d4d42873
23 changed files with 1100 additions and 154 deletions
+25 -13
View File
@@ -1676,23 +1676,35 @@ export const ArchiveWorkspaceRequestSchema = z.object({
});
// Create a new workspace record. Unlike open_project, this never deduplicates by
// directory: it always produces a fresh workspace. The backing directory is a
// choice — an existing local checkout/directory, or a newly created worktree.
// directory: it always produces a fresh workspace. The source discriminates
// between an existing local directory and a newly created paseo worktree.
export const WorkspaceCreateRequestSchema = z.object({
type: z.literal("workspace.create.request"),
backing: z.enum(["local", "worktree"]),
// local: path of the existing checkout/directory to back the workspace.
cwd: z.string().optional(),
// worktree: the project whose repo the worktree is cut from. local may also
// pass projectId, but the directory governs placement there.
projectId: z.string().optional(),
// worktree only: branch is the new/checked-out branch (slug); baseBranch is
// the branch to cut from.
branch: z.string().optional(),
baseBranch: z.string().optional(),
requestId: z.string(),
// Optional user-set title applied to the created workspace.
title: z.string().optional(),
requestId: z.string(),
// Optional prompt context for workspace-level name/branch generation.
firstAgentContext: FirstAgentContextSchema.optional(),
source: z.discriminatedUnion("kind", [
z.object({
kind: z.literal("directory"),
// Path of the existing checkout/directory to back the workspace.
path: z.string(),
projectId: z.string().optional(),
}),
z.object({
kind: z.literal("worktree"),
// The project whose repo the worktree is cut from.
cwd: z.string().optional(),
projectId: z.string().optional(),
action: z.enum(["branch-off", "checkout"]).optional(),
// Target branch name for checkout, or new branch name for branch-off.
refName: z.string().min(1).optional(),
baseBranch: z.string().optional(),
githubPrNumber: z.number().int().positive().optional(),
worktreeSlug: z.string().optional(),
}),
]),
});
export const WorkspaceClearAttentionRequestSchema = z.object({
@@ -4,6 +4,7 @@ import {
RecentProviderSessionDescriptorPayloadSchema,
SessionInboundMessageSchema,
SessionOutboundMessageSchema,
WorkspaceCreateRequestSchema,
WorkspaceDescriptorPayloadSchema,
WorkspaceScriptPayloadSchema,
} from "./messages.js";
@@ -866,4 +867,42 @@ describe("workspace message schemas", () => {
const checkout = result.data.payload.entries[0]?.project.checkout;
expect(checkout?.worktreeRoot).toBe("C:\\repo");
});
test("workspace.create.request rejects old flat backing shape and accepts new source envelope", () => {
// Old flat shape with backing enum must be rejected.
const oldFlat = WorkspaceCreateRequestSchema.safeParse({
type: "workspace.create.request",
requestId: "req-old",
backing: "worktree",
cwd: "/tmp/repo",
branch: "feat/my-feature",
});
expect(oldFlat.success).toBe(false);
// New envelope shape with source discriminated union must be accepted.
const newWorktree = WorkspaceCreateRequestSchema.parse({
type: "workspace.create.request",
requestId: "req-worktree",
source: {
kind: "worktree",
cwd: "/tmp/repo",
action: "checkout",
refName: "feat/my-feature",
},
});
expect(newWorktree.type).toBe("workspace.create.request");
expect(newWorktree.source.kind).toBe("worktree");
// Directory source must also be accepted.
const newDirectory = WorkspaceCreateRequestSchema.parse({
type: "workspace.create.request",
requestId: "req-dir",
source: {
kind: "directory",
path: "/tmp/repo",
},
});
expect(newDirectory.type).toBe("workspace.create.request");
expect(newDirectory.source.kind).toBe("directory");
});
});