Skip to content
Merged
10 changes: 9 additions & 1 deletion src/browser/features/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ import {
} from "@/browser/utils/chatEditing";

import { type ThinkingLevel } from "@/common/types/thinking";
import { normalizeRuntimeEnablement } from "@/common/types/runtime";
import { normalizeRuntimeEnablement, RUNTIME_MODE } from "@/common/types/runtime";
import {
type MuxMessageMetadata,
type ReviewNoteDataForDisplay,
Expand Down Expand Up @@ -936,6 +936,13 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
// Disable send while Coder presets are loading (user could bypass preset validation)
const coderPresetsLoading =
coderState.enabled && !coderState.coderConfig?.existingWorkspace && coderState.loadingPresets;
// #6033: the trunk branch stays empty until the branch list loads, and the backend refuses
// every runtime except local without one, so a creation send must wait for the list.
const creationBranchesLoading =
variant === "creation" &&
props.kind !== "scratch" &&
!creationState.branchesLoaded &&
creationState.selectedRuntime.mode !== RUNTIME_MODE.LOCAL;
Comment thread
ThomasK33 marked this conversation as resolved.
const isProcessingAttachments = processingAttachmentCount > 0;
const hasSendableDraft = hasTypedText || hasImages || hasReviews;
// Workspace sends/edits mutate history relative to the visible transcript, so they wait for
Expand All @@ -958,6 +965,7 @@ const ChatInputInner: React.FC<ChatInputProps> = (props) => {
!sendInFlightBlocksInput &&
!isProcessingAttachments &&
!coderPresetsLoading &&
!creationBranchesLoading &&
Comment thread
ThomasK33 marked this conversation as resolved.
!transcriptBlocksSend &&
!editPreconditionInvalidated;
const runningGoalActive =
Expand Down
37 changes: 19 additions & 18 deletions src/browser/features/ChatInput/useCreationWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,32 +355,33 @@ export function useCreationWorkspace({
let mounted = true;
setBranchesLoaded(false);
setRuntimeAvailabilityState({ status: "loading" });
const doLoad = async () => {
// Both requests run at once and settle independently. Send waits for the branch list (#6033),
// so it must not also wait for the runtime probe (Docker, Podman, devcontainer CLI), which can
// take several seconds after the branches are ready.
const loadBranches = async () => {
try {
// Use allSettled so failures are independent - branches can load even if availability fails
const [branchResult, availabilityResult] = await Promise.allSettled([
api.projects.listBranches({ projectPath }),
api.projects.runtimeAvailability({ projectPath }),
]);
const result = await api.projects.listBranches({ projectPath });
if (!mounted) return;
if (branchResult.status === "fulfilled") {
setBranches(branchResult.value.branches);
setRecommendedTrunk(branchResult.value.recommendedTrunk);
} else {
console.error("Failed to load branches:", branchResult.reason);
}
if (availabilityResult.status === "fulfilled") {
setRuntimeAvailabilityState({ status: "loaded", data: availabilityResult.value });
} else {
setRuntimeAvailabilityState({ status: "failed" });
}
setBranches(result.branches);
setRecommendedTrunk(result.recommendedTrunk);
} catch (error) {
console.error("Failed to load branches:", error);
} finally {
if (mounted) {
setBranchesLoaded(true);
Comment thread
ThomasK33 marked this conversation as resolved.
}
}
};
void doLoad();
const loadRuntimeAvailability = async () => {
try {
const data = await api.projects.runtimeAvailability({ projectPath });
if (mounted) setRuntimeAvailabilityState({ status: "loaded", data });
} catch {
if (mounted) setRuntimeAvailabilityState({ status: "failed" });
}
};
void loadBranches();
void loadRuntimeAvailability();
return () => {
mounted = false;
};
Expand Down
Loading
Loading