fix: prevent premature Quick Prompt completion from setup events

The session emits completion signals (agent-activity:completed,
message-complete, status:idle) during initialization before any
response content arrives. These early events were incorrectly
transitioning the popup to 'complete' phase, causing the
Nothing → In Progress → Nothing cycling pattern.

Track whether response content has been received via a ref and
only transition to 'complete' when hasContentRef is true. This
ensures setup/init completion events are ignored and the popup
stays in streaming until actual content has arrived and a
terminal event follows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-04-13 14:14:25 +02:00
co-authored by Copilot
parent 872476b2e3
commit 6ef44c6689
@@ -33,6 +33,9 @@ export function QuickPromptApp() {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const sessionIdRef = useRef<string | null>(null); const sessionIdRef = useRef<string | null>(null);
// Track whether we've received any response content (non-thinking) so we
// don't prematurely treat setup/init events as the final completion signal.
const hasContentRef = useRef(false);
const api = window.quickPromptApi; const api = window.quickPromptApi;
// Load capabilities on mount // Load capabilities on mount
@@ -70,6 +73,7 @@ export function QuickPromptApp() {
if (event.messageKind === 'thinking') { if (event.messageKind === 'thinking') {
setResponse((prev) => ({ ...prev, thinkingContent: prev.thinkingContent + event.contentDelta! })); setResponse((prev) => ({ ...prev, thinkingContent: prev.thinkingContent + event.contentDelta! }));
} else { } else {
hasContentRef.current = true;
setResponse((prev) => ({ setResponse((prev) => ({
...prev, ...prev,
content: prev.content + event.contentDelta!, content: prev.content + event.contentDelta!,
@@ -77,12 +81,17 @@ export function QuickPromptApp() {
})); }));
} }
setPhase('streaming'); setPhase('streaming');
} else if (event.kind === 'message-complete') { } else if (
setPhase('complete'); event.kind === 'message-complete' ||
} else if (event.kind === 'status' && event.status === 'idle') { (event.kind === 'status' && event.status === 'idle') ||
setPhase((prev) => (prev === 'idle' || prev === 'complete' ? prev : 'complete')); (event.kind === 'agent-activity' && event.activityType === 'completed')
} else if (event.kind === 'agent-activity' && event.activityType === 'completed') { ) {
setPhase((prev) => (prev === 'idle' || prev === 'complete' ? prev : 'complete')); // Only transition to "complete" once we've received actual response
// content. The session may emit completion events from initialisation
// or setup runs before the real content-generating turn starts.
if (hasContentRef.current) {
setPhase('complete');
}
} else if (event.kind === 'error') { } else if (event.kind === 'error') {
setErrorMessage(event.error ?? 'An unexpected error occurred.'); setErrorMessage(event.error ?? 'An unexpected error occurred.');
setPhase('error'); setPhase('error');
@@ -96,6 +105,7 @@ export function QuickPromptApp() {
setResponse({ content: '', thinkingContent: '', authorName: '' }); setResponse({ content: '', thinkingContent: '', authorName: '' });
setErrorMessage(undefined); setErrorMessage(undefined);
sessionIdRef.current = null; sessionIdRef.current = null;
hasContentRef.current = false;
// Refresh capabilities in case models changed // Refresh capabilities in case models changed
api.getCapabilities().then((caps) => { api.getCapabilities().then((caps) => {
setCapabilities(caps); setCapabilities(caps);
@@ -110,6 +120,7 @@ export function QuickPromptApp() {
setPhase('streaming'); setPhase('streaming');
setResponse({ content: '', thinkingContent: '', authorName: '' }); setResponse({ content: '', thinkingContent: '', authorName: '' });
setErrorMessage(undefined); setErrorMessage(undefined);
hasContentRef.current = false;
try { try {
const result = await api.send({ const result = await api.send({