Skip to content

feat(api): GET /api/tasks — operator visibility into A2A turns (#339) - #342

Open
mabry1985 wants to merge 1 commit into
mainfrom
feat/api-tasks-endpoint
Open

feat(api): GET /api/tasks — operator visibility into A2A turns (#339)#342
mabry1985 wants to merge 1 commit into
mainfrom
feat/api-tasks-endpoint

Conversation

@mabry1985

Copy link
Copy Markdown
Member

Fixes #339.

Why

Scheduled jobs have /api/scheduler/jobs; subagent runs have /api/agents. The turns themselves had no operator surface. During the #337 runaway, /api/agents and /api/delegations both returned empty while ~2,000 turns were executing — they track subagent runs and delegations, not A2A tasks. Working out what was actually running meant reading a2a-tasks.db over ssh with sqlite3.

What

GET /api/tasks?limit=&state=&context_id={tasks, count, counts_by_state, db, available}.

Two design choices taken directly from the incident:

  • Reads the sqlite file the SDK writes, not DatabaseTaskStore.list. The store's list filters by an owner resolved from a ServerCallContext the operator API has no natural value for; an owner mismatch would return an empty list — precisely the "everything looks idle while it isn't" failure this endpoint exists to prevent. A forensics surface must not be able to quietly return nothing.
  • counts_by_state always covers the whole store, not just the page. The runaway was legible in a single number (2,030 tasks in TASK_STATE_WORKING); a paginated list of the newest 100 would have concealed it.

Robustness, on the principle that this is what you reach for when things are already broken:

  • missing or corrupt store → available: false, never a 500
  • rows with unparseable status are reported as UNKNOWN, not silently dropped
  • state accepts working or TASK_STATE_WORKING
  • limit clamped to 1..500

Tests

tests/test_operator_tasks.py (8) covers ordering, the whole-store histogram under paging, both state spellings, context filtering, missing/corrupt store degradation, malformed-row reporting, and limit clamping. Plus 2 route tests for filter pass-through and the unwired default.

Full suite: 1743 passed. ruff check + format clean. Committed OpenAPI + API reference regenerated (check_openapi_spec.py and gen_api_docs.py --check both green).

Not included

The issue also suggests surfacing this in the console alongside scheduler jobs, and pairing it with a cancel affordance. This PR is the headless endpoint only — the console panel is a separate piece of work. Worth noting the cancel affordance is now straightforwardly buildable: cancel does genuinely stop a turn (verified under #338, regression test in #341), so a list + cancel button would have let the runaway be stopped through the API instead of by restarting the runtime.

🤖 Generated with Claude Code

Scheduled jobs had `/api/scheduler/jobs` and subagent runs had `/api/agents`,
but the turns themselves — what the agent is working on right now — had no
operator surface. During the #337 runaway, `/api/agents` and `/api/delegations`
both read empty while ~2,000 turns were executing, because they track subagent
runs and delegations rather than A2A tasks; establishing what was actually
running meant reading the sqlite store over ssh.

Two choices taken from that incident:

- Read the sqlite file the SDK writes rather than `DatabaseTaskStore.list`,
  whose owner filter (resolved from a ServerCallContext the operator API has no
  natural value for) could silently return an empty list — the exact failure
  this endpoint exists to prevent.
- Always return `counts_by_state` for the whole store, not just the page. The
  runaway was legible in one number (2,030 tasks WORKING); a page of the newest
  100 would have hidden it.

Degrades to `available: False` on a missing or corrupt store instead of 500ing,
since this is the surface you reach for when things are already broken, and
reports unparseable rows as UNKNOWN rather than dropping them.

Committed OpenAPI + API reference regenerated (CI-gated).

Console panel is NOT included — this is the headless surface only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@mabry1985, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d56fb36-fe44-410e-b665-cd0e1f97ec3b

📥 Commits

Reviewing files that changed from the base of the PR and between 55e0566 and f745180.

📒 Files selected for processing (8)
  • a2a_stores.py
  • docs/public/openapi.json
  • docs/reference/api-endpoints.md
  • operator_api/routes.py
  • operator_api/tasks.py
  • server/app.py
  • tests/test_operator_api_routes.py
  • tests/test_operator_tasks.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add operator visibility into in-flight A2A tasks (GET /api/tasks)

1 participant