fix(server): close event stream when requiresInput/requiresAuth force isFinal#980
Conversation
… isFinal AgentEmitter#requiresInput(message, isFinal=true) and requiresAuth(message, isFinal=true) set the internal terminalStateReached flag but never completed the stream: the emitted TaskStatusUpdateEvent#isFinal() is derived from the (non-terminal) task state, so EventConsumer kept the SSE / Flow.Publisher open. When the caller forces finality on a non-terminal (interrupted) state, enqueue a QueueClosedEvent so the stream completes after the status event is delivered, letting the client resume via a new request. Interrupted states still stay open by default (a2aproject#756). Also removes a stale .isFinal(false) call from the emitEvent Javadoc; the builder no longer exposes it. Fixes a2aproject#975
There was a problem hiding this comment.
Code Review
This pull request introduces explicit stream closure handling in AgentEmitter when finality is forced on a non-terminal state by enqueuing a QueueClosedEvent. This ensures the stream completes and allows clients to resume via a new request. The corresponding unit tests in AgentEmitterTest have been updated to verify that QueueClosedEvent is correctly emitted under these conditions. I have no feedback to provide as the implementation is correct and well-tested.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces the emission of a QueueClosedEvent in AgentEmitter when a final status is forced on a non-terminal (interrupted) task state, ensuring the event stream is explicitly closed so clients can resume via a new request. The corresponding unit tests in AgentEmitterTest have been updated to verify this behavior. I have no feedback to provide as there are no review comments.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Fixes #975.
Problem
AgentEmitter#requiresInput(message, isFinal=true)(andrequiresAuth(..., isFinal=true)) set the internalterminalStateReachedflag but never completed the stream. The emittedTaskStatusUpdateEvent#isFinal()is derived from the task state (status.state().isFinal()), which isfalsefor the interruptedINPUT_REQUIRED/AUTH_REQUIREDstates, soEventConsumerkept the SSE /Flow.Publisheropen. TheisFinalargument therefore only blocked further updates; it could not end the stream, and connections an agent asked to release stayed open.Fix
When the caller forces finality on a non-terminal state, enqueue the existing
QueueClosedEventafter the status event:EventConsumeralready treatsQueueClosedEventas a stream-terminating event, so the status event is delivered and then the stream completes, letting the client resume via a new request. This uses the SDK's own close path and touches neither the event record nor the proto. Interrupted states still stay open by default (#756); only an explicitisFinal=truecloses them. Terminal states are unaffected (their event'sisFinal()is alreadytrue, and the!state.isFinal()guard skips them).Also removes a stale
.isFinal(false)call from theemitEventJavadoc, since the builder no longer exposes it.Tests
QueueClosedEventvia a backward-compatibleexpectStreamCloseoverload of the existing helper.server-commonsuite 412/412 pass; with the fix reverted, the four forced-final tests fail (confirming they exercise the behavior).Note on approach
This implements the "wire
isFinalto actually close the stream" option. If you'd rather treat interrupted streams as always staying open and haveisFinalonrequiresInput/requiresAuthdeprecated/documented instead, I'm happy to switch to that.