diff --git a/apps/web/components/task/chat/use-chat-input-container.test.ts b/apps/web/components/task/chat/use-chat-input-container.test.ts index 3e160f47f3..62b2d36092 100644 --- a/apps/web/components/task/chat/use-chat-input-container.test.ts +++ b/apps/web/components/task/chat/use-chat-input-container.test.ts @@ -33,9 +33,25 @@ function renderInputState(overrides: Partial { - it("keeps the editor enabled while setup is still blocking submit", () => { + it("disables the editor while the session is still STARTING", () => { + // The editor must stay uneditable until the agent reaches RUNNING — if + // the user can press Cmd+Enter mid-startup, the backend rejects with + // "Failed to send message to agent" because the agent process isn't + // ready yet. This is the regression from earlier rounds where the e2e + // quick-chat suite kept failing on race conditions. const { result } = renderInputState({ isStarting: true }); - expect(result.current.isDisabled).toBe(false); + expect(result.current.isDisabled).toBe(true); + expect(result.current.submitDisabled).toBe(true); + expect(result.current.submitDisabledReason).toBeUndefined(); + }); + + it("surfaces the setup tooltip only while a container/sandbox is preparing", () => { + const { result } = renderInputState({ + isStarting: true, + isPreparingEnvironment: true, + }); + + expect(result.current.submitDisabledReason).toBe("The agent is still being set up."); }); }); diff --git a/apps/web/components/task/chat/use-chat-input-container.ts b/apps/web/components/task/chat/use-chat-input-container.ts index 598d0f8bff..1b8b9d7c46 100644 --- a/apps/web/components/task/chat/use-chat-input-container.ts +++ b/apps/web/components/task/chat/use-chat-input-container.ts @@ -102,18 +102,23 @@ function computeDerivedState(params: { isAgentBusy: boolean; hasAgentCommands: boolean; }) { + // STARTING is included so the editor itself stays uneditable until the + // session reaches RUNNING. Without this, the e2e suite's wait-for- + // contenteditable would fire mid-STARTING, the test would press Cmd+Enter, + // and the backend would reject with "Failed to send message to agent" + // before the agent process is ready to receive prompts. const isDisabled = + params.isStarting || params.isMoving || params.isSending || params.isFailed || params.needsRecovery || params.executorUnavailable; - // Only an active container/sandbox prepare blocks the submit and surfaces - // the "agent still being set up" tooltip. The brief STARTING transition - // every session passes through (including local quick-chat) intentionally - // does NOT block — the e2e suite presses Cmd+Enter as soon as the editor - // becomes editable, and a STARTING-only block silently dropped messages. - const submitDisabled = params.isPreparingEnvironment || isDisabled; + const submitDisabled = isDisabled; + // The "agent still being set up" tooltip is only meaningful while a + // container/sandbox is actively bootstrapping. The brief STARTING + // transition for local quick-chat sessions doesn't deserve its own + // tooltip — the editor is disabled, that's the signal. const submitDisabledReason = params.isPreparingEnvironment ? "The agent is still being set up." : undefined;