a2aproject / a2aproject/a2a-python

cancel() and producer-failure write a terminal state to the store but not to active subscriber streams

Ouverte
#1,175 5 commentaires 0 réactions 1 personne assignée Réclamée par @rohityan Voir sur GitHub
component: server status:awaiting response
Langage dominant
Python
Étoiles
2.1k
Forks
496
Merge moyen
4 j 17 h
PR mergées (30 j)
12

Description

Follow-up from the review of #1172 (raised by @astrogilda).

### What happens

`ActiveTask.cancel()` now closes a task out to a terminal `CANCELED` state when
the executor's `cancel()` is cleanup-only or the task was parked in a
non-terminal state (e.g. `input-required`). That terminal state is written
directly to the task store via `save_task_event`, because by that point both
the agent and subscriber event queues are already closed.

The consequence is that the terminal state reaches the **store** but not an
**active subscriber stream**. A client that was streaming the task
(`SubscribeToTask` / `message/stream`) receives the initial `Task` in `WORKING`
and then the stream ends; it never sees a `CANCELED` event and has to re-read
the task to learn the outcome. The push path does not fire either, since
`_update_task_state` only notifies on `PushNotificationEvent`.

### Why it was left out of #1172

At the point of the direct store write, both queues are already closed, so a
store write is the only option left. The same pattern already exists for the
producer-failure path (`active_task.py`, the `FAILED` write in
`_run_producer`'s `except Exception` arm), under a comment that says so. #1172
kept scope to the authorization fix (#1159) and the "cancel leaves an actionable
terminal state" fix (#1170); this stream-visibility gap is orthogonal.

### Proposed follow-up

Give a streaming or push-subscribed client a way to observe the terminal state
without polling. Options to weigh:

- Emit a synthetic terminal `TaskStatusUpdateEvent` to `_event_queue_subscribers`
before the queues are torn down, so an active subscriber sees the transition.
- Route the terminal store write through the push path so `push_sender` fires.

Both the `cancel()` CANCELED write and the producer-failure `FAILED` write share
this gap and should get the same treatment.

### Repro

Subscribe to a live task whose executor's `cancel()` is cleanup-only, cancel it,
and collect the events the subscriber receives: the stream yields the initial
`WORKING` task and ends, while `task_store.get` afterwards returns `CANCELED`.

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.