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 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;
// Load capabilities on mount
@@ -70,6 +73,7 @@ export function QuickPromptApp() {
if (event.messageKind === 'thinking') {
setResponse((prev) => ({ ...prev, thinkingContent: prev.thinkingContent + event.contentDelta! }));
} else {
hasContentRef.current = true;
setResponse((prev) => ({
...prev,
content: prev.content + event.contentDelta!,
@@ -77,12 +81,17 @@ export function QuickPromptApp() {
}));
}
setPhase('streaming');
} else if (event.kind === 'message-complete') {
setPhase('complete');
} else if (event.kind === 'status' && event.status === 'idle') {
setPhase((prev) => (prev === 'idle' || prev === 'complete' ? prev : 'complete'));
} else if (event.kind === 'agent-activity' && event.activityType === 'completed') {
setPhase((prev) => (prev === 'idle' || prev === 'complete' ? prev : 'complete'));
} else if (
event.kind === 'message-complete' ||
(event.kind === 'status' && event.status === 'idle') ||
(event.kind === 'agent-activity' && event.activityType === 'completed')
) {
// 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') {
setErrorMessage(event.error ?? 'An unexpected error occurred.');
setPhase('error');
@@ -96,6 +105,7 @@ export function QuickPromptApp() {
setResponse({ content: '', thinkingContent: '', authorName: '' });
setErrorMessage(undefined);
sessionIdRef.current = null;
hasContentRef.current = false;
// Refresh capabilities in case models changed
api.getCapabilities().then((caps) => {
setCapabilities(caps);
@@ -110,6 +120,7 @@ export function QuickPromptApp() {
setPhase('streaming');
setResponse({ content: '', thinkingContent: '', authorName: '' });
setErrorMessage(undefined);
hasContentRef.current = false;
try {
const result = await api.send({