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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user