Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export function createV2StreamingCoordinator(config: V2StreamingConfig): V2Strea
});
},

onSessionStatusChanged: status => {
onSessionStatusChanged: (status, _eventSessionId) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Session status from child sessions can still prematurely end root streaming

This callback ignores eventSessionId and treats any idle as stream completion. The same subagent/child-session behavior called out in run-session.ts applies here too, so a child idle can flip isStreaming to false and fire onStreamComplete before the root session is actually done.

if (status.type === 'idle') {
store.setState({ isStreaming: false });
onStreamComplete?.();
Expand Down
2 changes: 1 addition & 1 deletion src/components/cloud-agent-next/useCloudAgentStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export function useCloudAgentStream({
}
},

onSessionStatusChanged: status => {
onSessionStatusChanged: (status, _eventSessionId) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: idle from non-root sessions can mark UI streaming complete too early

eventSessionId is ignored, so any session status event (including child/subagent sessions) drives setIsStreaming and onComplete. This can cause premature completion state and UI transitions while the primary session is still running.

setSessionStatus(status);

// Handle streaming state based on status
Expand Down
10 changes: 8 additions & 2 deletions src/lib/cloud-agent-next/processor/event-processor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,10 @@ describe('createEventProcessor', () => {
})
);

expect(callbacks.onSessionStatusChanged).toHaveBeenCalledWith({ type: 'busy' });
expect(callbacks.onSessionStatusChanged).toHaveBeenCalledWith(
{ type: 'busy' },
'session-123'
);
expect(callbacks.onStreamingChanged).toHaveBeenCalledWith(true);
});

Expand All @@ -468,7 +471,10 @@ describe('createEventProcessor', () => {
})
);

expect(callbacks.onSessionStatusChanged).toHaveBeenLastCalledWith({ type: 'idle' });
expect(callbacks.onSessionStatusChanged).toHaveBeenLastCalledWith(
{ type: 'idle' },
'session-123'
);
expect(callbacks.onStreamingChanged).toHaveBeenLastCalledWith(false);
});

Expand Down
4 changes: 2 additions & 2 deletions src/lib/cloud-agent-next/processor/event-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ export function createEventProcessor(config: EventProcessorConfig = {}): EventPr
* Handle session.status events.
*/
function handleSessionStatus(data: EventSessionStatus['properties']): void {
const { status } = data;
const { sessionID, status } = data;

callbacks.onSessionStatusChanged?.(status);
callbacks.onSessionStatusChanged?.(status, sessionID);

// Update streaming state based on status
if (status.type === 'idle') {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/cloud-agent-next/processor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export type EventProcessorCallbacks = {
parentSessionId: string | null
) => void;

/** Called when session status changes (idle/busy/retry) */
onSessionStatusChanged?: (status: SessionStatus) => void;
/** Called when session status changes (idle/busy/retry). sessionID identifies which session changed. */
onSessionStatusChanged?: (status: SessionStatus, sessionID: string) => void;

/** Called when a new session is created (session.created event) */
onSessionCreated?: (sessionInfo: Session) => void;
Expand Down
7 changes: 5 additions & 2 deletions src/lib/cloud-agent-next/run-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,11 @@ export async function runSessionToCompletion(input: RunSessionInput): Promise<Ru
errorMessage = errData?.data?.message ?? 'Assistant message failed.';
}
},
onSessionStatusChanged: status => {
if (status.type === 'idle') resolveOnce();
onSessionStatusChanged: (status, eventSessionId) => {
// Only resolve when the *root* session goes idle. Subagent (child)
// sessions also emit idle events; resolving on those would cause
// the orchestrator to return a partial result prematurely.
if (status.type === 'idle' && eventSessionId === sessionId) resolveOnce();
},
onError: error => {
hasError = true;
Expand Down