diff --git a/apps/web/components/task/chat/chat-input-area.tsx b/apps/web/components/task/chat/chat-input-area.tsx index 55dad36d95..cdb0576901 100644 --- a/apps/web/components/task/chat/chat-input-area.tsx +++ b/apps/web/components/task/chat/chat-input-area.tsx @@ -413,6 +413,7 @@ export function ChatInputArea({ onPlanModeChange={handlePlanModeChange} isAgentBusy={isAgentBusy} isStarting={panelState.isStarting} + isPreparingEnvironment={panelState.isPreparingEnvironment} isMoving={isMoving} isSending={isSending} onCancel={handleCancelTurn} diff --git a/apps/web/components/task/chat/chat-input-container.tsx b/apps/web/components/task/chat/chat-input-container.tsx index e62974f6bc..306a3a5385 100644 --- a/apps/web/components/task/chat/chat-input-container.tsx +++ b/apps/web/components/task/chat/chat-input-container.tsx @@ -57,6 +57,11 @@ type ChatInputContainerProps = { onPlanModeChange: (enabled: boolean) => void; isAgentBusy: boolean; isStarting: boolean; + /** True only while a containerized executor is bootstrapping (Docker + * prepare, Sprites sandbox spin-up). Distinct from the brief STARTING + * state every session — including local quick-chat — passes through; + * see useSessionState. */ + isPreparingEnvironment?: boolean; isMoving?: boolean; isSending: boolean; onCancel: () => void; @@ -292,6 +297,7 @@ export const ChatInputContainer = forwardRef 0 @@ -129,26 +142,11 @@ function computeDerivedState(params: { } export function useChatInputContainer(params: UseChatInputContainerParams) { - const { - ref, - sessionId, - isSending, - isStarting, - isMoving, - isFailed, - needsRecovery, - executorUnavailable, - isAgentBusy, - hasAgentCommands, - placeholder, - contextItems, - pendingClarification, - onClarificationResolved, - pendingCommentsByFile, - showRequestChangesTooltip, - onRequestChangesTooltipDismiss, - onSubmit, - } = params; + const { ref, sessionId, isSending, isStarting, isPreparingEnvironment, isMoving } = params; + const { isFailed, needsRecovery, executorUnavailable, isAgentBusy, hasAgentCommands } = params; + const { placeholder, contextItems, pendingClarification, onClarificationResolved } = params; + const { pendingCommentsByFile, showRequestChangesTooltip } = params; + const { onRequestChangesTooltipDismiss, onSubmit } = params; const [isInputFocused, setIsInputFocused] = useState(false); const [showNewSessionDialog, setShowNewSessionDialog] = useState(false); @@ -202,6 +200,7 @@ export function useChatInputContainer(params: UseChatInputContainerParams) { const derived = computeDerivedState({ isStarting, + isPreparingEnvironment, isMoving, isSending, isFailed, diff --git a/apps/web/hooks/domains/session/use-session-state.ts b/apps/web/hooks/domains/session/use-session-state.ts index bc4bb48d5a..458764f33f 100644 --- a/apps/web/hooks/domains/session/use-session-state.ts +++ b/apps/web/hooks/domains/session/use-session-state.ts @@ -48,5 +48,11 @@ export function useSessionState(sessionId: string | null) { ...flags, isStarting: flags.isStarting || isPreparingEnvironment, isWorking: flags.isWorking || isPreparingEnvironment, + // Exposed separately so consumers that gate on "is the executor still + // bootstrapping" (e.g. the chat input "agent is still being set up" + // tooltip) can distinguish a brief STARTING transition — which every + // session passes through, including local quick-chat — from an active + // Docker / Sprites prepare-environment phase. + isPreparingEnvironment, }; }