fix(server): rename worktree branches from prompt + attachments

Replace top-level `attachments` and `nameContext` on `create_paseo_worktree_request` with `firstAgentContext: { prompt?, attachments? }`, the single signal driving auto-rename. Drop the `worktreeSlug || …` short-circuits so a random placeholder slug no longer blocks renaming, drop the attachment-as-PR fallback in favor of explicit `githubPrNumber`, and reshape the MCP `create_worktree` tool around a `target` discriminated union (`branch-off` / `checkout-branch` / `checkout-pr`) with concise field descriptions so callers stop misreading `prompt` as a name field.
This commit is contained in:
Mohamed Boudra
2026-05-02 12:12:44 +07:00
parent 8e9134d255
commit 2f177dde7a
19 changed files with 312 additions and 534 deletions
@@ -14,11 +14,7 @@ import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
import { encodeImages } from "@/utils/encode-images";
import { toErrorMessage } from "@/utils/error-messages";
import { splitComposerAttachmentsForSubmit } from "@/components/composer-attachments";
import type {
CreateAgentRequestOptions,
CreatePaseoWorktreeInput,
DaemonClient,
} from "@server/client/daemon-client";
import type { CreateAgentRequestOptions, DaemonClient } from "@server/client/daemon-client";
import { projectIconPlaceholderLabelFromDisplayName } from "@/utils/project-display-name";
import { requireWorkspaceExecutionAuthority } from "@/utils/workspace-execution";
import { navigateToPreparedWorkspaceTab } from "@/utils/workspace-navigation";
@@ -73,24 +69,19 @@ function buildChatDraftComposerArgs({
};
}
type WorkspaceCreationAttachments = NonNullable<CreatePaseoWorktreeInput["attachments"]>;
async function callWorkspaceCreation({
creationMethod,
connectedClient,
input,
wirePayload,
}: {
creationMethod: "create_worktree" | "open_project";
connectedClient: DaemonClient;
input: { cwd: string };
wirePayload: { attachments: WorkspaceCreationAttachments };
}) {
if (creationMethod === "create_worktree") {
return connectedClient.createPaseoWorktree({
cwd: input.cwd,
worktreeSlug: createNameId(),
...(wirePayload.attachments.length > 0 ? { attachments: wirePayload.attachments } : {}),
});
}
return connectedClient.openProject(input.cwd);
@@ -228,12 +219,10 @@ export function WorkspaceSetupDialog() {
}
const connectedClient = withConnectedClient();
const wirePayload = splitComposerAttachmentsForSubmit(input.attachments);
const payload = await callWorkspaceCreation({
creationMethod: pendingWorkspaceSetup.creationMethod,
connectedClient,
input,
wirePayload,
});
if (payload.error || !payload.workspace) {
@@ -699,6 +699,7 @@ describe("NewWorkspaceScreen picker payload", () => {
await flush();
expect(mockClient.createPaseoWorktree).toHaveBeenCalledTimes(1);
expect(firstCreateWorktreeCall().firstAgentContext?.prompt).toBe("please review this change");
expect(mockClient.createAgent).not.toHaveBeenCalled();
expect(saveDraftInputMock).toHaveBeenCalledWith({
draftKey: "draft:server:draft-new-workspace",
@@ -876,7 +877,7 @@ describe("NewWorkspaceScreen picker payload", () => {
await flush();
const call = firstCreateWorktreeCall();
const attachments = call.attachments ?? [];
const attachments = call.firstAgentContext?.attachments ?? [];
expect(attachments).toHaveLength(1);
expect(attachments[0]).toMatchObject({ type: "github_pr", number: 202 });
});
@@ -907,8 +908,11 @@ describe("NewWorkspaceScreen picker payload", () => {
await flush();
const call = firstCreateWorktreeCall();
expect(call.attachments).toHaveLength(1);
expect(call.attachments?.[0]).toMatchObject({ type: "github_pr", number: 202 });
expect(call.firstAgentContext?.attachments).toHaveLength(1);
expect(call.firstAgentContext?.attachments?.[0]).toMatchObject({
type: "github_pr",
number: 202,
});
});
it("omits PR rows from the picker when GitHub features are disabled", async () => {
@@ -293,6 +293,7 @@ interface CreateChatAgentInput {
composerState: ReturnType<typeof useAgentInputDraft>["composerState"];
ensureWorkspace: (input: {
cwd: string;
prompt: string;
attachments: AgentAttachment[];
}) => Promise<ReturnType<typeof normalizeWorkspaceDescriptor>>;
serverId: string;
@@ -310,7 +311,11 @@ async function runCreateChatAgent(input: CreateChatAgentInput): Promise<void> {
throw new Error("Select a model");
}
const { attachments: reviewAttachments } = splitComposerAttachmentsForSubmit(attachments);
const ensuredWorkspace = await ensureWorkspace({ cwd, attachments: reviewAttachments });
const ensuredWorkspace = await ensureWorkspace({
cwd,
prompt: text,
attachments: reviewAttachments,
});
submitWorkspaceDraft({
serverId,
draftKey,
@@ -588,13 +593,22 @@ export function NewWorkspaceScreen({
}, []);
const buildCreateWorktreeInput = useCallback(
(input: { cwd: string; attachments: AgentAttachment[] }) => {
(input: { cwd: string; prompt: string; attachments: AgentAttachment[] }) => {
const checkoutRequest = pickerItemToCheckoutRequest(selectedItem);
const trimmedPrompt = input.prompt.trim();
const hasFirstAgentContext = trimmedPrompt.length > 0 || input.attachments.length > 0;
return {
cwd: input.cwd,
worktreeSlug: createNameId(),
...(input.attachments.length > 0 ? { attachments: input.attachments } : {}),
...(hasFirstAgentContext
? {
firstAgentContext: {
...(trimmedPrompt ? { prompt: trimmedPrompt } : {}),
...(input.attachments.length > 0 ? { attachments: input.attachments } : {}),
},
}
: {}),
...checkoutRequest,
};
},
@@ -602,7 +616,7 @@ export function NewWorkspaceScreen({
);
const ensureWorkspace = useCallback(
async (input: { cwd: string; attachments: AgentAttachment[] }) => {
async (input: { cwd: string; prompt: string; attachments: AgentAttachment[] }) => {
if (createdWorkspace) {
return createdWorkspace;
}