- Generate and rename temporary `t3code/*` worktree branches on first user turn in `ProviderCommandReactor` - Remove websocket/client branch-generation RPCs and related contract/API surface - Update Codex text-generation prompt to include attachment metadata only when attachments exist, with expanded tests
Showing the changes to apps/web/src (2 of 12 files). Show all
Unified · Split
| @@ -247,43 +247,6 @@ function buildTemporaryWorktreeBranchName(message: string): string { · 246 unmodified lines | |||
| 247 | return `${WORKTREE_BRANCH_PREFIX}/${hashBranchSeed(seed)}`; | 247 | return `${WORKTREE_BRANCH_PREFIX}/${hashBranchSeed(seed)}`; |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | function imageReferenceForBranchNaming(image: ComposerImageAttachment): string { | ||
| 251 | const maybePath = (image.file as File & { path?: unknown }).path; | ||
| 252 | if (typeof maybePath === "string" && maybePath.trim().length > 0) { | ||
| 253 | return maybePath.trim(); | ||
| 254 | } | ||
| 255 | return image.name; | ||
| 256 | } | ||
| 257 | |||
| 258 | function buildBranchNameGenerationMessage( | ||
| 259 | text: string, | ||
| 260 | images: ReadonlyArray<ComposerImageAttachment>, | ||
| 261 | ): string { | ||
| 262 | const trimmedText = text.trim(); | ||
| 263 | const imageReferences = Array.from( | ||
| 264 | new Set( | ||
| 265 | images | ||
| 266 | .map((image) => imageReferenceForBranchNaming(image).trim()) | ||
| 267 | .filter((reference) => reference.length > 0), | ||
| 268 | ), | ||
| 269 | ); | ||
| 270 | const attachmentsSection = | ||
| 271 | imageReferences.length > 0 | ||
| 272 | ? `Attached images:\n${imageReferences.map((reference) => `- ${reference}`).join("\n")}` | ||
| 273 | : ""; | ||
| 274 | |||
| 275 | if (trimmedText.length > 0 && attachmentsSection.length > 0) { | ||
| 276 | return `${trimmedText}\n\n${attachmentsSection}`; | ||
| 277 | } | ||
| 278 | if (trimmedText.length > 0) { | ||
| 279 | return trimmedText; | ||
| 280 | } | ||
| 281 | if (attachmentsSection.length > 0) { | ||
| 282 | return attachmentsSection; | ||
| 283 | } | ||
| 284 | return "New worktree change"; | ||
| 285 | } | ||
| 286 | |||
| 287 | const VscodeEntryIcon = memo(function VscodeEntryIcon(props: { | 250 | const VscodeEntryIcon = memo(function VscodeEntryIcon(props: { |
| 288 | pathValue: string; | 251 | pathValue: string; |
| 289 | kind: "file" | "directory"; | 252 | kind: "file" | "directory"; |
| @@ -1530,7 +1493,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 1240 unmodified lines | |||
| 1530 | const composerImagesSnapshot = [...composerImages]; | 1493 | const composerImagesSnapshot = [...composerImages]; |
| 1531 | const messageIdForSend = newMessageId(); | 1494 | const messageIdForSend = newMessageId(); |
| 1532 | const messageCreatedAt = new Date().toISOString(); | 1495 | const messageCreatedAt = new Date().toISOString(); |
| 1533 | const branchNameMessage = buildBranchNameGenerationMessage(trimmed, composerImagesSnapshot); | ||
| 1534 | const turnAttachmentsPromise = Promise.all( | 1496 | const turnAttachmentsPromise = Promise.all( |
| 1535 | composerImagesSnapshot.map( | 1497 | composerImagesSnapshot.map( |
| 1536 | async ( | 1498 | async ( |
| @@ -1578,7 +1540,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 41 unmodified lines | |||
| 1578 | setComposerHighlightedItemId(null); | 1540 | setComposerHighlightedItemId(null); |
| 1579 | 1541 | ||
| 1580 | let attemptedTurnStart = false; | 1542 | let attemptedTurnStart = false; |
| 1581 | let pendingWorktreeBranchRename: Promise<void> | null = null; | ||
| 1582 | try { | 1543 | try { |
| 1583 | // On first message: lock in branch + create worktree if needed. | 1544 | // On first message: lock in branch + create worktree if needed. |
| 1584 | if (baseBranchForWorktree) { | 1545 | if (baseBranchForWorktree) { |
| @@ -1604,36 +1565,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 19 unmodified lines | |||
| 1604 | branch: result.worktree.branch, | 1565 | branch: result.worktree.branch, |
| 1605 | worktreePath: result.worktree.path, | 1566 | worktreePath: result.worktree.path, |
| 1606 | }); | 1567 | }); |
| 1607 | pendingWorktreeBranchRename = (async () => { | ||
| 1608 | const turnAttachments = await turnAttachmentsPromise; | ||
| 1609 | const renamed = await api.git.generateAndRenameBranch({ | ||
| 1610 | cwd: result.worktree.path, | ||
| 1611 | oldBranch: result.worktree.branch, | ||
| 1612 | message: branchNameMessage, | ||
| 1613 | attachments: turnAttachments, | ||
| 1614 | }); | ||
| 1615 | if (!renamed.branch || renamed.branch === result.worktree.branch) { | ||
| 1616 | return; | ||
| 1617 | } | ||
| 1618 | await api.orchestration.dispatchCommand({ | ||
| 1619 | type: "thread.meta.update", | ||
| 1620 | commandId: newCommandId(), | ||
| 1621 | threadId: threadIdForSend, | ||
| 1622 | branch: renamed.branch, | ||
| 1623 | worktreePath: result.worktree.path, | ||
| 1624 | }); | ||
| 1625 | dispatch({ | ||
| 1626 | type: "SET_THREAD_BRANCH", | ||
| 1627 | threadId: threadIdForSend, | ||
| 1628 | branch: renamed.branch, | ||
| 1629 | worktreePath: result.worktree.path, | ||
| 1630 | }); | ||
| 1631 | })().catch((error) => { | ||
| 1632 | console.warn("Failed to rename generated worktree branch", { | ||
| 1633 | threadId: threadIdForSend, | ||
| 1634 | error: error instanceof Error ? error.message : String(error), | ||
| 1635 | }); | ||
| 1636 | }); | ||
| 1637 | const setupScript = setupProjectScript(activeProject.scripts); | 1568 | const setupScript = setupProjectScript(activeProject.scripts); |
| 1638 | if (setupScript) { | 1569 | if (setupScript) { |
| 1639 | await runProjectScript(setupScript, { | 1570 | await runProjectScript(setupScript, { |
| @@ -1683,9 +1614,6 @@ export default function ChatView({ threadId }: ChatViewProps) { · 43 unmodified lines | |||
| 1683 | sandboxMode, | 1614 | sandboxMode, |
| 1684 | createdAt: messageCreatedAt, | 1615 | createdAt: messageCreatedAt, |
| 1685 | }); | 1616 | }); |
| 1686 | if (pendingWorktreeBranchRename) { | ||
| 1687 | void pendingWorktreeBranchRename; | ||
| 1688 | } | ||
| 1689 | } catch (err) { | 1617 | } catch (err) { |
| 1690 | if ( | 1618 | if ( |
| 1691 | !attemptedTurnStart && | 1619 | !attemptedTurnStart && |
| @@ -164,9 +164,6 @@ export function createWsNativeApi(): NativeApi { · 163 unmodified lines | |||
| 164 | runStackedAction: (input) => transport.request(WS_METHODS.gitRunStackedAction, input), | 164 | runStackedAction: (input) => transport.request(WS_METHODS.gitRunStackedAction, input), |
| 165 | listBranches: (input) => transport.request(WS_METHODS.gitListBranches, input), | 165 | listBranches: (input) => transport.request(WS_METHODS.gitListBranches, input), |
| 166 | createWorktree: (input) => transport.request(WS_METHODS.gitCreateWorktree, input), | 166 | createWorktree: (input) => transport.request(WS_METHODS.gitCreateWorktree, input), |
| 167 | generateBranchName: (input) => transport.request(WS_METHODS.gitGenerateBranchName, input), | ||
| 168 | generateAndRenameBranch: (input) => | ||
| 169 | transport.request(WS_METHODS.gitGenerateAndRenameBranch, input), | ||
| 170 | renameBranch: (input) => transport.request(WS_METHODS.gitRenameBranch, input), | 167 | renameBranch: (input) => transport.request(WS_METHODS.gitRenameBranch, input), |
| 171 | removeWorktree: (input) => transport.request(WS_METHODS.gitRemoveWorktree, input), | 168 | removeWorktree: (input) => transport.request(WS_METHODS.gitRemoveWorktree, input), |
| 172 | createBranch: (input) => transport.request(WS_METHODS.gitCreateBranch, input), | 169 | createBranch: (input) => transport.request(WS_METHODS.gitCreateBranch, input), |