Skip to content

[TASK] Refactor background-job liveness monitoring onto the Supervisor/BackgroundJob contract #720

Description

@edenreich

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

  1. 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).
  2. Fix the A2A_QueryTask guard as its first consumer - keyed by task id, sourced from the supervisor (the original bug).
  3. 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.
  4. 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

  1. Submit a long-running A2A task via A2A_SubmitTask.
  2. Call A2A_QueryTask for that task while it is polling.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Task.

    Projects

    Status
    QA

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions