fix(chat-input): dismiss open question prompt when sending a message (#1740)

Sending a message while a question prompt was open left the prompt
lingering, blocked the send, or collided with the still-blocked agent
turn. Two root causes:

useSessionActivity treated pending permissions as idle but not pending
questions, so the send button became Stop during a question and Enter
queued/collided instead of sending. handleSubmit also never dismissed
the open question, stranding the session in a half-answered state.

The send path now dismisses open questions for the session subtree
(optimistic local clear so the card vanishes instantly, plus a formal
question.reject) and queues the message. The queued-message auto-send
hook then delivers it as the next turn once the rejected turn winds
down and the session returns to idle. Queueing avoids aborting the
turn, which surfaced an unwanted "running turn was stopped" notice.

Regression tests cover the no-op, subtree dismissal (root + subagent
child), and QuestionNotFoundError paths.
This commit is contained in:
Tom Rochette
2026-06-25 10:55:35 +03:00
committed by GitHub
parent 4a068fca86
commit 9a2012c94e
4 changed files with 188 additions and 5 deletions
+9 -5
View File
@@ -1,6 +1,6 @@
import React from 'react';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { useSessionStatus, useSessionMessages, useSessionPermissions } from '@/sync/sync-context';
import { useSessionStatus, useSessionMessages, useSessionPermissions, useSessionQuestions } from '@/sync/sync-context';
// Mirrors OpenCode SessionStatus: busy|retry|idle.
export type SessionActivityPhase = 'idle' | 'busy' | 'retry';
@@ -23,18 +23,22 @@ const IDLE_RESULT: SessionActivityResult = {
* Determines if a session is actively working.
* Checks session_status and, only when status is missing, falls back to the
* trailing assistant message when its completion update has not landed yet.
* Returns idle when permissions are pending (permission indicator takes priority).
* Returns idle when permissions or questions are pending (the permission /
* question indicator takes priority, and the send button must stay available so
* the user can supersede the prompt with a new message).
*/
export function useSessionActivity(sessionId: string | null | undefined, directory?: string): SessionActivityResult {
const status = useSessionStatus(sessionId ?? '', directory);
const messages = useSessionMessages(sessionId ?? '', directory);
const permissions = useSessionPermissions(sessionId ?? '', directory);
const questions = useSessionQuestions(sessionId ?? '', directory);
return React.useMemo<SessionActivityResult>(() => {
if (!sessionId) return IDLE_RESULT;
// Permissions pending → idle (permission indicator takes priority)
if (permissions.length > 0) return IDLE_RESULT;
// Permissions or questions pending → idle (the blocking indicator takes
// priority and the send button must remain a send, not a stop).
if (permissions.length > 0 || questions.length > 0) return IDLE_RESULT;
const phase: SessionActivityPhase = (status?.type ?? 'idle') as SessionActivityPhase;
@@ -61,7 +65,7 @@ export function useSessionActivity(sessionId: string | null | undefined, directo
isBusy: phase === 'busy' || (!statusWorking && hasPendingAssistant),
isCooldown: false,
};
}, [sessionId, status, messages, permissions]);
}, [sessionId, status, messages, permissions, questions]);
}
export function useCurrentSessionActivity(): SessionActivityResult {