Skip to content

Commit 75b1410

Browse files
authored
Merge branch 'main' into dependabot/maven/testing-282f29c024
2 parents 142b5f2 + 0998916 commit 75b1410

5 files changed

Lines changed: 116 additions & 36 deletions

File tree

.github/workflows/run-tck.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
path: a2a-tck
5454
ref: ${{ env.TCK_VERSION }}
5555
- name: Set up Python
56-
uses: actions/setup-python@v6
56+
uses: actions/setup-python@v7
5757
with:
5858
python-version-file: "a2a-tck/pyproject.toml"
5959
- name: Install uv and Python dependencies
@@ -149,7 +149,7 @@ jobs:
149149
path: a2a-tck
150150
ref: ${{ env.TCK_VERSION_0_3 }}
151151
- name: Set up Python
152-
uses: actions/setup-python@v6
152+
uses: actions/setup-python@v7
153153
with:
154154
python-version-file: "a2a-tck/pyproject.toml"
155155
- name: Install uv and Python dependencies

server-common/src/main/java/org/a2aproject/sdk/server/requesthandlers/DefaultRequestHandler.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ public Task onGetTask(TaskQueryParams params, ServerCallContext context) throws
312312
* Limits the history of a task to the most recent N messages.
313313
*
314314
* @param task the task to limit
315-
* @param historyLength the maximum number of recent messages to keep (0 or negative = unlimited)
315+
* @param historyLength the maximum number of recent messages to keep ({@code null} = no limit, 0 = empty history)
316316
* @return the task with limited history, or the original task if no limiting needed
317317
*/
318318
private static Task limitTaskHistory(Task task, @Nullable Integer historyLength) {
@@ -736,6 +736,10 @@ public void cancel() {
736736
@Override
737737
public void onNext(StreamingEventKind item) {
738738
LOGGER.debug("onNext: {} for task {}", item.getClass().getSimpleName(), taskId.get());
739+
if (item instanceof Task task) {
740+
Integer historyLength = params.configuration() != null ? params.configuration().historyLength() : null;
741+
item = limitTaskHistory(task, historyLength);
742+
}
739743
subscriber.onNext(item);
740744
}
741745

server-common/src/main/java/org/a2aproject/sdk/server/tasks/AgentEmitter.java

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -115,28 +115,14 @@ public AgentEmitter(RequestContext context, EventQueue eventQueue) {
115115
this.contextId = context.getContextId();
116116
}
117117

118-
private void updateStatus(TaskState taskState) {
119-
updateStatus(taskState, null, taskState.isFinal());
120-
}
121-
122118
/**
123119
* Updates the task status to the given state with an optional message.
124120
*
125121
* @param taskState the new task state
126122
* @param message optional message to include with the status update
127123
*/
128124
public void updateStatus(TaskState taskState, @Nullable Message message) {
129-
updateStatus(taskState, message, taskState.isFinal());
130-
}
131-
132-
/**
133-
* Updates the task status to the given state with an optional message and finality flag.
134-
*
135-
* @param state the new task state
136-
* @param message optional message to include with the status update
137-
* @param isFinal whether this is a final status (prevents further updates)
138-
*/
139-
private void updateStatus(TaskState state, @Nullable Message message, boolean isFinal) {
125+
boolean isFinal = taskState.isFinal();
140126
// Check terminal state first (fail fast)
141127
if (terminalStateReached.get()) {
142128
throw new IllegalStateException("Cannot update task status - terminal state already reached");
@@ -152,7 +138,7 @@ private void updateStatus(TaskState state, @Nullable Message message, boolean is
152138
TaskStatusUpdateEvent event = TaskStatusUpdateEvent.builder()
153139
.taskId(taskId)
154140
.contextId(contextId)
155-
.status(new TaskStatus(state, message, null))
141+
.status(new TaskStatus(taskState, message, null))
156142
.build();
157143
eventQueue.enqueueEvent(event);
158144
}
@@ -371,7 +357,7 @@ public void reject(@Nullable Message message) {
371357
* Marks the task as INPUT_REQUIRED, indicating the agent needs user input to continue.
372358
*/
373359
public void requiresInput() {
374-
requiresInput(null, false);
360+
requiresInput(null);
375361
}
376362

377363
/**
@@ -380,33 +366,39 @@ public void requiresInput() {
380366
* @param message optional message to include
381367
*/
382368
public void requiresInput(@Nullable Message message) {
383-
requiresInput(message, false);
369+
updateStatus(TaskState.TASK_STATE_INPUT_REQUIRED, message);
384370
}
385371

386372
/**
387-
* Marks the task as INPUT_REQUIRED with a finality flag.
373+
* Marks the task as INPUT_REQUIRED.
388374
*
389-
* @param isFinal whether this is a final status (prevents further updates)
375+
* @param isFinal ignored — interrupted states are not final per the A2A specification.
376+
* Use {@link #requiresInput()} instead.
377+
* @deprecated The {@code isFinal} parameter has no effect. Use {@link #requiresInput()} instead.
390378
*/
379+
@Deprecated(forRemoval = true)
391380
public void requiresInput(boolean isFinal) {
392-
requiresInput(null, isFinal);
381+
requiresInput();
393382
}
394383

395384
/**
396-
* Marks the task as INPUT_REQUIRED with an optional message and finality flag.
385+
* Marks the task as INPUT_REQUIRED with an optional message.
397386
*
398387
* @param message optional message to include
399-
* @param isFinal whether this is a final status (prevents further updates)
388+
* @param isFinal ignored — interrupted states are not final per the A2A specification.
389+
* Use {@link #requiresInput(Message)} instead.
390+
* @deprecated The {@code isFinal} parameter has no effect. Use {@link #requiresInput(Message)} instead.
400391
*/
392+
@Deprecated(forRemoval = true)
401393
public void requiresInput(@Nullable Message message, boolean isFinal) {
402-
updateStatus(TaskState.TASK_STATE_INPUT_REQUIRED, message, isFinal);
394+
requiresInput(message);
403395
}
404396

405397
/**
406398
* Marks the task as AUTH_REQUIRED, indicating the agent needs authentication to continue.
407399
*/
408400
public void requiresAuth() {
409-
requiresAuth(null, false);
401+
requiresAuth(null);
410402
}
411403

412404
/**
@@ -415,26 +407,32 @@ public void requiresAuth() {
415407
* @param message optional message to include
416408
*/
417409
public void requiresAuth(@Nullable Message message) {
418-
requiresAuth(message, false);
410+
updateStatus(TaskState.TASK_STATE_AUTH_REQUIRED, message);
419411
}
420412

421413
/**
422-
* Marks the task as AUTH_REQUIRED with a finality flag.
414+
* Marks the task as AUTH_REQUIRED.
423415
*
424-
* @param isFinal whether this is a final status (prevents further updates)
416+
* @param isFinal ignored — interrupted states are not final per the A2A specification.
417+
* Use {@link #requiresAuth()} instead.
418+
* @deprecated The {@code isFinal} parameter has no effect. Use {@link #requiresAuth()} instead.
425419
*/
420+
@Deprecated(forRemoval = true)
426421
public void requiresAuth(boolean isFinal) {
427-
requiresAuth(null, isFinal);
422+
requiresAuth();
428423
}
429424

430425
/**
431-
* Marks the task as AUTH_REQUIRED with an optional message and finality flag.
426+
* Marks the task as AUTH_REQUIRED with an optional message.
432427
*
433428
* @param message optional message to include
434-
* @param isFinal whether this is a final status (prevents further updates)
429+
* @param isFinal ignored — interrupted states are not final per the A2A specification.
430+
* Use {@link #requiresAuth(Message)} instead.
431+
* @deprecated The {@code isFinal} parameter has no effect. Use {@link #requiresAuth(Message)} instead.
435432
*/
433+
@Deprecated(forRemoval = true)
436434
public void requiresAuth(@Nullable Message message, boolean isFinal) {
437-
updateStatus(TaskState.TASK_STATE_AUTH_REQUIRED, message, isFinal);
435+
requiresAuth(message);
438436
}
439437

440438
/**
@@ -569,7 +567,6 @@ public void addTask(Task task) {
569567
* .taskId(context.getTaskId())
570568
* .contextId(context.getContextId())
571569
* .status(new TaskStatus(TaskState.WORKING))
572-
* .isFinal(false)
573570
* .build();
574571
* emitter.emitEvent(event);
575572
* }

server-common/src/test/java/org/a2aproject/sdk/server/tasks/AgentEmitterTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,14 @@ public void testRequiresInputWithMessage() throws Exception {
184184
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, SAMPLE_MESSAGE);
185185
}
186186

187+
@SuppressWarnings("deprecation")
187188
@Test
188189
public void testRequiresInputWithFinalTrue() throws Exception {
189190
agentEmitter.requiresInput(true);
190191
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_INPUT_REQUIRED, null);
191192
}
192193

194+
@SuppressWarnings("deprecation")
193195
@Test
194196
public void testRequiresInputWithMessageAndFinalTrue() throws Exception {
195197
agentEmitter.requiresInput(SAMPLE_MESSAGE, true);
@@ -208,12 +210,14 @@ public void testRequiresAuthWithMessage() throws Exception {
208210
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, SAMPLE_MESSAGE);
209211
}
210212

213+
@SuppressWarnings("deprecation")
211214
@Test
212215
public void testRequiresAuthWithFinalTrue() throws Exception {
213216
agentEmitter.requiresAuth(true);
214217
checkTaskStatusUpdateEventOnQueue(false, TaskState.TASK_STATE_AUTH_REQUIRED, null);
215218
}
216219

220+
@SuppressWarnings("deprecation")
217221
@Test
218222
public void testRequiresAuthWithMessageAndFinalTrue() throws Exception {
219223
agentEmitter.requiresAuth(SAMPLE_MESSAGE, true);

tests/server-common/src/test/java/org/a2aproject/sdk/server/apps/common/AbstractA2AServerTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,4 +3193,79 @@ public void testSendMessageWithHistoryLengthZero() throws Exception {
31933193
}
31943194
}
31953195

3196+
@Test
3197+
@Timeout(value = 30, unit = TimeUnit.SECONDS)
3198+
public void testSendStreamingMessageWithHistoryLengthZero() throws Exception {
3199+
AtomicReference<String> taskIdRef = new AtomicReference<>();
3200+
3201+
try {
3202+
Message initialMessage = Message.builder(MESSAGE)
3203+
.parts(new TextPart("input-required:Trigger INPUT_REQUIRED"))
3204+
.build();
3205+
3206+
CountDownLatch initialLatch = new CountDownLatch(1);
3207+
getClient().sendMessage(initialMessage, List.of((event, agentCard) -> {
3208+
if (event instanceof TaskEvent te) {
3209+
taskIdRef.set(te.getTask().id());
3210+
initialLatch.countDown();
3211+
} else if (event instanceof TaskUpdateEvent tue) {
3212+
taskIdRef.set(tue.getTask().id());
3213+
if (tue.getTask().status().state() == TaskState.TASK_STATE_INPUT_REQUIRED) {
3214+
initialLatch.countDown();
3215+
}
3216+
}
3217+
}), error -> {
3218+
if (!isStreamClosedError(error)) {
3219+
initialLatch.countDown();
3220+
}
3221+
});
3222+
3223+
assertTrue(initialLatch.await(15, TimeUnit.SECONDS), "Initial streaming sendMessage should complete");
3224+
String taskId = taskIdRef.get();
3225+
assertNotNull(taskId, "Should have captured task ID");
3226+
3227+
Message followUp = Message.builder(MESSAGE)
3228+
.taskId(taskId)
3229+
.parts(new TextPart("input-required:User input"))
3230+
.build();
3231+
3232+
MessageSendParams params = MessageSendParams.builder()
3233+
.message(followUp)
3234+
.configuration(MessageSendConfiguration.builder()
3235+
.historyLength(0)
3236+
.build())
3237+
.build();
3238+
3239+
CountDownLatch followUpLatch = new CountDownLatch(1);
3240+
AtomicReference<Task> resultTaskRef = new AtomicReference<>();
3241+
getClient().sendMessage(params, List.of((BiConsumer<ClientEvent, AgentCard>) (event, agentCard) -> {
3242+
if (event instanceof TaskEvent te) {
3243+
resultTaskRef.set(te.getTask());
3244+
followUpLatch.countDown();
3245+
} else if (event instanceof TaskUpdateEvent tue) {
3246+
resultTaskRef.set(tue.getTask());
3247+
if (tue.getTask().status().state() == TaskState.TASK_STATE_COMPLETED) {
3248+
followUpLatch.countDown();
3249+
}
3250+
}
3251+
}), error -> {
3252+
if (!isStreamClosedError(error)) {
3253+
followUpLatch.countDown();
3254+
}
3255+
}, null);
3256+
3257+
assertTrue(followUpLatch.await(15, TimeUnit.SECONDS), "Follow-up streaming sendMessage should complete");
3258+
Task resultTask = resultTaskRef.get();
3259+
assertNotNull(resultTask, "Should have received a Task response");
3260+
assertTrue(resultTask.history() == null || resultTask.history().isEmpty(),
3261+
"historyLength=0 should return no history, but got " +
3262+
(resultTask.history() != null ? resultTask.history().size() : 0) + " messages");
3263+
} finally {
3264+
String taskId = taskIdRef.get();
3265+
if (taskId != null) {
3266+
deleteTaskInTaskStore(taskId);
3267+
}
3268+
}
3269+
}
3270+
31963271
}

0 commit comments

Comments
 (0)