Summary
Reframed from the original bug report (kept below): the broken A2A_QueryTask guard turned out to be one symptom of fragmented background-job liveness monitoring. Since #693 all three background job kinds - A2A tasks, background shells, subagents - implement the uniform BackgroundJob contract and run under the job supervisor, but the readers/controllers still consulted different sources:
| Consumer |
A2A |
Shell |
Subagent |
Status bar / /tasks list |
supervisor |
supervisor |
supervisor |
HasPending() |
supervisor |
ShellTracker |
SubagentTracker |
A2A_QueryTask polling guard |
tracker, wrong key (silent no-op) |
none |
none |
/tasks Cancel |
works |
A2A-only path, silently fails |
silently fails |
Scope
- Add a uniform per-id liveness query to the shared contract:
Supervisor.IsRunning(id), exposed as BackgroundTaskRegistry.IsJobRunning(id) and consumed by tools via a narrow JobLivenessReporter projection (mirroring JobSubmitter/JobStopper).
- Fix the
A2A_QueryTask guard as its first consumer - keyed by task id, sourced from the supervisor (the original bug).
- Add
JobMeta.HoldsSession so each job declares whether it keeps a headless session alive; Supervisor.HasPending() reads it and the registry delegates - shells and subagents stop consulting their trackers for liveness, and the interactive-pane carve-out is declared by the job itself instead of a tracker-side mode filter.
- Fix
/tasks Cancel for shells and subagents: the confirm gate arms for running non-A2A rows and cancellation dispatches by kind - A2A keeps CancelBackgroundTask (remote cancel + tracker cleanup; a2aJob.Wind is deliberately a local no-op), shells/subagents wind their supervised job with WindJob(id, WindStop).
Follow-up from #693.
Original bug report
A2A_QueryTask passes an agent URL where a task ID is expected. In internal/agent/tools/a2a_query_task.go the "is this task still being polled" guard calls IsPolling(agentURL) / GetPollingState(agentURL), but A2ATaskTracker.IsPolling/GetPollingState are keyed by task ID (taskIndex), so the lookup never matches and the guard is a silent no-op.
Steps to Reproduce
- Submit a long-running A2A task via
A2A_SubmitTask.
- Call
A2A_QueryTask for that task while it is polling.
- Inspect the "polling active" field of the result.
Expected Behavior
A2A_QueryTask reports the task as actively polling while it is in flight.
Actual Behavior
The polling-active guard never matches (agent URL is passed where a task ID is expected), so it always reports not-polling regardless of the task's real state.
Summary
Reframed from the original bug report (kept below): the broken
A2A_QueryTaskguard turned out to be one symptom of fragmented background-job liveness monitoring. Since #693 all three background job kinds - A2A tasks, background shells, subagents - implement the uniformBackgroundJobcontract and run under the job supervisor, but the readers/controllers still consulted different sources:/taskslistHasPending()ShellTrackerSubagentTrackerA2A_QueryTaskpolling guard/tasksCancelScope
Supervisor.IsRunning(id), exposed asBackgroundTaskRegistry.IsJobRunning(id)and consumed by tools via a narrowJobLivenessReporterprojection (mirroringJobSubmitter/JobStopper).A2A_QueryTaskguard as its first consumer - keyed by task id, sourced from the supervisor (the original bug).JobMeta.HoldsSessionso each job declares whether it keeps a headless session alive;Supervisor.HasPending()reads it and the registry delegates - shells and subagents stop consulting their trackers for liveness, and the interactive-pane carve-out is declared by the job itself instead of a tracker-side mode filter./tasksCancel for shells and subagents: the confirm gate arms for running non-A2A rows and cancellation dispatches by kind - A2A keepsCancelBackgroundTask(remote cancel + tracker cleanup;a2aJob.Windis deliberately a local no-op), shells/subagents wind their supervised job withWindJob(id, WindStop).Follow-up from #693.
Original bug report
A2A_QueryTaskpasses an agent URL where a task ID is expected. Ininternal/agent/tools/a2a_query_task.gothe "is this task still being polled" guard callsIsPolling(agentURL)/GetPollingState(agentURL), butA2ATaskTracker.IsPolling/GetPollingStateare keyed by task ID (taskIndex), so the lookup never matches and the guard is a silent no-op.Steps to Reproduce
A2A_SubmitTask.A2A_QueryTaskfor that task while it is polling.Expected Behavior
A2A_QueryTaskreports the task as actively polling while it is in flight.Actual Behavior
The polling-active guard never matches (agent URL is passed where a task ID is expected), so it always reports not-polling regardless of the task's real state.