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 @@ -9,6 +9,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.a2aproject.sdk.server.events.EventQueue;
import org.a2aproject.sdk.server.events.QueueClosedEvent;
import org.a2aproject.sdk.spec.A2AError;
import org.a2aproject.sdk.spec.Artifact;
import org.a2aproject.sdk.spec.Event;
Expand Down Expand Up @@ -155,6 +156,15 @@ private void updateStatus(TaskState state, @Nullable Message message, boolean is
.status(new TaskStatus(state, message, null))
.build();
eventQueue.enqueueEvent(event);

// If the caller forced finality on a non-terminal (interrupted) state, the
// event's own isFinal() is derived from the state and is false, so the stream
// would otherwise stay open (interrupted states are intentionally non-terminal;
// see #756). Signal explicit closure so the stream completes after this event
// is delivered, letting the client resume via a new request.
if (isFinal && !state.isFinal()) {
eventQueue.enqueueEvent(new QueueClosedEvent(taskId));
}
}

/**
Expand Down Expand Up @@ -569,7 +579,6 @@ public void addTask(Task task) {
* .taskId(context.getTaskId())
* .contextId(context.getContextId())
* .status(new TaskStatus(TaskState.WORKING))
* .isFinal(false)
* .build();
* emitter.emitEvent(event);
* }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.a2aproject.sdk.server.agentexecution.RequestContext;
import org.a2aproject.sdk.server.events.EventQueue;
import org.a2aproject.sdk.server.events.QueueClosedEvent;
import org.a2aproject.sdk.server.events.EventQueueItem;
import org.a2aproject.sdk.server.events.EventQueueUtil;
import org.a2aproject.sdk.server.events.InMemoryQueueManager;
Expand Down Expand Up @@ -187,13 +188,13 @@ public void testRequiresInputWithMessage() throws Exception {
@Test
public void testRequiresInputWithFinalTrue() throws Exception {
agentEmitter.requiresInput(true);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, null);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, null, true);
}

@Test
public void testRequiresInputWithMessageAndFinalTrue() throws Exception {
agentEmitter.requiresInput(SAMPLE_MESSAGE, true);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, SAMPLE_MESSAGE);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, SAMPLE_MESSAGE, true);
}

@Test
Expand All @@ -211,13 +212,13 @@ public void testRequiresAuthWithMessage() throws Exception {
@Test
public void testRequiresAuthWithFinalTrue() throws Exception {
agentEmitter.requiresAuth(true);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, null);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, null, true);
}

@Test
public void testRequiresAuthWithMessageAndFinalTrue() throws Exception {
agentEmitter.requiresAuth(SAMPLE_MESSAGE, true);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, SAMPLE_MESSAGE);
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, SAMPLE_MESSAGE, true);
}

@Test
Expand Down Expand Up @@ -486,6 +487,10 @@ public void sendMessageWithMismatchedContextIdThrows() {
}

private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, TaskState state, Message statusMessage) throws Exception {
return checkTaskStatusUpdateEventOnQueue(isFinal, state, statusMessage, false);
}

private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal, TaskState state, Message statusMessage, boolean expectStreamClose) throws Exception {
// Wait up to 5 seconds for event (async MainEventBusProcessor needs time to distribute)
EventQueueItem item = eventQueue.dequeueEventItem(WAIT_MILLI_SECONDS);
assertNotNull(item);
Expand All @@ -501,6 +506,12 @@ private TaskStatusUpdateEvent checkTaskStatusUpdateEventOnQueue(boolean isFinal,
assertEquals(state, tsue.status().state());
assertEquals(statusMessage, tsue.status().message());

if (expectStreamClose) {
EventQueueItem closeItem = eventQueue.dequeueEventItem(WAIT_MILLI_SECONDS);
assertNotNull(closeItem);
assertInstanceOf(QueueClosedEvent.class, closeItem.getEvent());
}

// Check no additional events (still use 0 timeout for this check)
assertNull(eventQueue.dequeueEventItem(0));

Expand Down
Loading